From 031e8a8175c942c5b93429cda2e34854a46f1f7d Mon Sep 17 00:00:00 2001 From: SpecialX <47072643+wangxiner55@users.noreply.github.com> Date: Tue, 7 Jul 2026 20:13:22 +0800 Subject: [PATCH] refactor: split 5 oversized files into domain-specific subfiles (0 violations) --- scripts/arch-scan/arch.db | Bin 3465216 -> 3477504 bytes src/modules/messaging/actions-bulk.ts | 105 + src/modules/messaging/actions-core.ts | 375 +++ src/modules/messaging/actions-group.ts | 114 + src/modules/messaging/actions-reports.ts | 201 ++ src/modules/messaging/actions-templates.ts | 220 ++ src/modules/messaging/actions.ts | 1007 +------- .../questions/data-access-cross-module.ts | 170 ++ .../questions/data-access-import-export.ts | 121 + src/modules/questions/data-access-options.ts | 88 + .../questions/data-access-questions.ts | 469 ++++ src/modules/questions/data-access.ts | 865 +------ src/modules/textbooks/data-access-chapters.ts | 244 ++ .../textbooks/data-access-cross-module.ts | 152 ++ .../textbooks/data-access-knowledge-points.ts | 132 + .../textbooks/data-access-prerequisites.ts | 64 + .../textbooks/data-access-textbooks.ts | 291 +++ .../textbooks/data-access-verification.ts | 62 + src/modules/textbooks/data-access.ts | 919 +------ src/shared/db/schema.ts | 2275 +---------------- src/shared/db/schema/_helpers.ts | 6 + src/shared/db/schema/academic.ts | 51 + src/shared/db/schema/ai-providers.ts | 35 + src/shared/db/schema/announcements.ts | 56 + src/shared/db/schema/attendance.ts | 177 ++ src/shared/db/schema/audit.ts | 116 + src/shared/db/schema/classes.ts | 159 ++ src/shared/db/schema/course-plans.ts | 90 + src/shared/db/schema/elective-courses.ts | 73 + src/shared/db/schema/error-book.ts | 95 + src/shared/db/schema/exams.ts | 136 + src/shared/db/schema/files.ts | 28 + src/shared/db/schema/formative-assessment.ts | 137 + src/shared/db/schema/grade-records.ts | 189 ++ src/shared/db/schema/homework.ts | 139 + src/shared/db/schema/knowledge-points.ts | 87 + src/shared/db/schema/learning-diagnostic.ts | 46 + src/shared/db/schema/lesson-preparation.ts | 157 ++ src/shared/db/schema/messaging.ts | 123 + src/shared/db/schema/notifications.ts | 89 + src/shared/db/schema/practice.ts | 110 + src/shared/db/schema/questions.ts | 74 + src/shared/db/schema/rbac.ts | 45 + src/shared/db/schema/scheduling.ts | 56 + src/shared/db/schema/school.ts | 91 + src/shared/db/schema/standards.ts | 44 + src/shared/db/schema/system-settings.ts | 29 + src/shared/db/schema/users.ts | 152 ++ .../lib/cache/invalidation-map-academic.ts | 248 ++ .../lib/cache/invalidation-map-admin.ts | 349 +++ .../lib/cache/invalidation-map-classes.ts | 156 ++ .../lib/cache/invalidation-map-grades.ts | 166 ++ .../invalidation-map-lesson-preparation.ts | 150 ++ .../lib/cache/invalidation-map-messaging.ts | 136 + src/shared/lib/cache/invalidation-map.ts | 1171 +-------- 55 files changed, 6748 insertions(+), 6092 deletions(-) create mode 100644 src/modules/messaging/actions-bulk.ts create mode 100644 src/modules/messaging/actions-core.ts create mode 100644 src/modules/messaging/actions-group.ts create mode 100644 src/modules/messaging/actions-reports.ts create mode 100644 src/modules/messaging/actions-templates.ts create mode 100644 src/modules/questions/data-access-cross-module.ts create mode 100644 src/modules/questions/data-access-import-export.ts create mode 100644 src/modules/questions/data-access-options.ts create mode 100644 src/modules/questions/data-access-questions.ts create mode 100644 src/modules/textbooks/data-access-chapters.ts create mode 100644 src/modules/textbooks/data-access-cross-module.ts create mode 100644 src/modules/textbooks/data-access-knowledge-points.ts create mode 100644 src/modules/textbooks/data-access-prerequisites.ts create mode 100644 src/modules/textbooks/data-access-textbooks.ts create mode 100644 src/modules/textbooks/data-access-verification.ts create mode 100644 src/shared/db/schema/_helpers.ts create mode 100644 src/shared/db/schema/academic.ts create mode 100644 src/shared/db/schema/ai-providers.ts create mode 100644 src/shared/db/schema/announcements.ts create mode 100644 src/shared/db/schema/attendance.ts create mode 100644 src/shared/db/schema/audit.ts create mode 100644 src/shared/db/schema/classes.ts create mode 100644 src/shared/db/schema/course-plans.ts create mode 100644 src/shared/db/schema/elective-courses.ts create mode 100644 src/shared/db/schema/error-book.ts create mode 100644 src/shared/db/schema/exams.ts create mode 100644 src/shared/db/schema/files.ts create mode 100644 src/shared/db/schema/formative-assessment.ts create mode 100644 src/shared/db/schema/grade-records.ts create mode 100644 src/shared/db/schema/homework.ts create mode 100644 src/shared/db/schema/knowledge-points.ts create mode 100644 src/shared/db/schema/learning-diagnostic.ts create mode 100644 src/shared/db/schema/lesson-preparation.ts create mode 100644 src/shared/db/schema/messaging.ts create mode 100644 src/shared/db/schema/notifications.ts create mode 100644 src/shared/db/schema/practice.ts create mode 100644 src/shared/db/schema/questions.ts create mode 100644 src/shared/db/schema/rbac.ts create mode 100644 src/shared/db/schema/scheduling.ts create mode 100644 src/shared/db/schema/school.ts create mode 100644 src/shared/db/schema/standards.ts create mode 100644 src/shared/db/schema/system-settings.ts create mode 100644 src/shared/db/schema/users.ts create mode 100644 src/shared/lib/cache/invalidation-map-academic.ts create mode 100644 src/shared/lib/cache/invalidation-map-admin.ts create mode 100644 src/shared/lib/cache/invalidation-map-classes.ts create mode 100644 src/shared/lib/cache/invalidation-map-grades.ts create mode 100644 src/shared/lib/cache/invalidation-map-lesson-preparation.ts create mode 100644 src/shared/lib/cache/invalidation-map-messaging.ts diff --git a/scripts/arch-scan/arch.db b/scripts/arch-scan/arch.db index c61b832261bf621a31d072be48a16f6665acdfc8..6a4972622a41d195f13d90ac54bde4b90802689e 100644 GIT binary patch delta 513219 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�z~O~c7;3lNN#EauC*KXTvIzu_juvRDU-XR4qR?S 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Ξ*5aEqc`_@W!g3lb-i1rb-ANESq#twp** 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

32dqb3$>QA4lf!H!`#s=uFbA8 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²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 NM&dQZ#k5(*{vR-uI_&@e delta 507077 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≺7p6I7aDns46WV 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

oL!Xh7o4@1|KT5?M53E;b(CFX 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%0DR_Z}KXd|hje!yj0u*p^Xk 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�g>sP0ep0#?$xW=fWM~7vX@gNzi>n`L9w1 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

qOL^sBQSmu9^6$^PW);Mb(X}5S7pWyZ>wd5&s+h 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 Y6FV2e1vedsvKIMxWrQI9J%;%I2bVIOfB*mh diff --git a/src/modules/messaging/actions-bulk.ts b/src/modules/messaging/actions-bulk.ts new file mode 100644 index 0000000..e8fa5c9 --- /dev/null +++ b/src/modules/messaging/actions-bulk.ts @@ -0,0 +1,105 @@ +"use server" + +import { invalidateFor } from "@/shared/lib/cache" +import { PermissionDeniedError, requirePermission } from "@/shared/lib/auth-guard" +import { trackEvent } from "@/shared/lib/track-event" +import { Permissions } from "@/shared/types/permissions" +import type { ActionState } from "@/shared/types/action-state" + +import { BulkMessageIdsSchema } from "./schema" +import { + bulkMarkMessagesAsRead, + bulkDeleteMessages, + bulkToggleMessagesStar, +} from "./data-access" + +// --------------------------------------------------------------------------- +// P2-1: 批量操作 Server Actions +// --------------------------------------------------------------------------- + +export async function bulkMarkMessagesAsReadAction( + ids: string[] +): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_READ) + + const parsed = BulkMessageIdsSchema.safeParse({ ids }) + if (!parsed.success) { + return { success: false, message: "Invalid message IDs", errors: parsed.error.flatten().fieldErrors } + } + + const updated = await bulkMarkMessagesAsRead(parsed.data.ids, ctx.userId) + await invalidateFor("messaging.bulkMarkRead") + + void trackEvent({ + event: "message.marked_read", + userId: ctx.userId, + targetType: "message", + properties: { count: updated, bulk: true }, + }) + + return { success: true, message: "Messages marked as read", data: { updated } } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +export async function bulkDeleteMessagesAction( + ids: string[] +): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_DELETE) + + const parsed = BulkMessageIdsSchema.safeParse({ ids }) + if (!parsed.success) { + return { success: false, message: "Invalid message IDs", errors: parsed.error.flatten().fieldErrors } + } + + await bulkDeleteMessages(parsed.data.ids, ctx.userId) + await invalidateFor("messaging.bulkDelete") + + void trackEvent({ + event: "message.deleted", + userId: ctx.userId, + targetType: "message", + properties: { count: parsed.data.ids.length, bulk: true }, + }) + + return { success: true, message: "Messages deleted" } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +export async function bulkToggleMessagesStarAction( + ids: string[] +): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_READ) + + const parsed = BulkMessageIdsSchema.safeParse({ ids }) + if (!parsed.success) { + return { success: false, message: "Invalid message IDs", errors: parsed.error.flatten().fieldErrors } + } + + await bulkToggleMessagesStar(parsed.data.ids, ctx.userId) + await invalidateFor("messaging.bulkToggleStar") + + void trackEvent({ + event: "message.star_toggled", + userId: ctx.userId, + targetType: "message", + properties: { count: parsed.data.ids.length, bulk: true }, + }) + + return { success: true, message: "Star toggled" } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} diff --git a/src/modules/messaging/actions-core.ts b/src/modules/messaging/actions-core.ts new file mode 100644 index 0000000..eaa8b20 --- /dev/null +++ b/src/modules/messaging/actions-core.ts @@ -0,0 +1,375 @@ +"use server" + +import { invalidateFor } from "@/shared/lib/cache" +import { getTranslations } from "next-intl/server" +import { PermissionDeniedError, requirePermission } from "@/shared/lib/auth-guard" +import { trackEvent } from "@/shared/lib/track-event" +import { Permissions } from "@/shared/types/permissions" +import type { AuthContext } from "@/shared/types/permissions" +import type { ActionState } from "@/shared/types/action-state" +import { sendNotification } from "@/modules/notifications/dispatcher" + +import { + SendMessageSchema, + MessageIdSchema, +} from "./schema" +import { + getMessages, + getMessageById, + getMessageThread, + createMessage, + markMessageAsRead, + deleteMessage, + getRecipients, + getUnreadMessageCount, + toggleMessageStar, + isReceiverAllowed, + recallMessage, + isEitherUserBlocked, + getMessageAttachments, +} from "./data-access" +import type { Message, MessageType, RecipientOption, MessageAttachment } from "./types" + +export async function sendMessageAction( + prevState: ActionState | null, + formData: FormData +): Promise> { + let ctx: AuthContext | null = null + let parsedInput: { + receiverId: string + subject: string | null + content: string + parentMessageId: string | null + } | null = null + try { + ctx = await requirePermission(Permissions.MESSAGE_SEND) + + const parsed = SendMessageSchema.safeParse({ + receiverId: formData.get("receiverId"), + subject: formData.get("subject") || undefined, + content: formData.get("content"), + parentMessageId: formData.get("parentMessageId") || undefined, + }) + + if (!parsed.success) { + return { success: false, message: "Invalid form data", errors: parsed.error.flatten().fieldErrors } + } + + const input = parsed.data + parsedInput = input + if (input.receiverId === ctx.userId) { + return { success: false, message: "Cannot send a message to yourself" } + } + + // P0-1: Server Action 二次校验收件人是否在 sender 的 DataScope 允许范围内 + // 防止绕过前端 Select 直接构造 FormData 提交任意 receiverId 越权发送 + const allowed = await isReceiverAllowed(ctx.userId, input.receiverId, ctx.dataScope) + if (!allowed) { + return { success: false, message: "Recipient not allowed" } + } + + // P2-5: 屏蔽校验 —— 发送方或接收方任一屏蔽对方,则禁止发送 + const blocked = await isEitherUserBlocked(ctx.userId, input.receiverId) + if (blocked) { + return { success: false, message: "Cannot send message due to block" } + } + + // P0-6: 校验 parentMessageId 属于当前用户参与的会话 + // 防止将回复消息注入到他人会话线程 + if (input.parentMessageId) { + const parent = await getMessageById(input.parentMessageId, ctx.userId) + if (!parent) { + return { success: false, message: "Parent message not accessible" } + } + } + + const id = await createMessage({ + senderId: ctx.userId, + receiverId: input.receiverId, + subject: input.subject, + content: input.content, + parentMessageId: input.parentMessageId, + }) + + // Notify the receiver about the new message via the notifications dispatcher. + // This respects user notification preferences (SMS/WeChat/Email/In-App). + const t = await getTranslations("messages") + const notifyTitle = input.subject + ? t("notification.messageTitle", { subject: input.subject }) + : t("notification.messageTitleNoSubject") + await sendNotification({ + userId: input.receiverId, + type: "info", + title: notifyTitle, + content: input.content.slice(0, 200), + actionUrl: `/messages/${id}`, + metadata: { messageType: "message", messageId: id }, + }) + + await invalidateFor("messaging.send", { id }) + + void trackEvent({ + event: "message.sent", + userId: ctx.userId, + targetId: id, + targetType: "message", + properties: { receiverId: input.receiverId, hasSubject: !!input.subject }, + }) + + return { success: true, message: "Message sent", data: id } + } catch (e) { + // P1-9: 失败埋点(权限拒绝除外,避免噪音) + if (ctx && parsedInput && !(e instanceof PermissionDeniedError)) { + void trackEvent({ + event: "message.send_failed", + userId: ctx.userId, + targetType: "message", + properties: { + receiverId: parsedInput.receiverId, + reason: e instanceof Error ? e.message : "unknown", + }, + }) + } + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +export async function markMessageAsReadAction(messageId: string): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_READ) + + const parsed = MessageIdSchema.safeParse({ messageId }) + if (!parsed.success) { + return { success: false, message: "Invalid message id", errors: parsed.error.flatten().fieldErrors } + } + + await markMessageAsRead(parsed.data.messageId, ctx.userId) + await invalidateFor("messaging.markRead", { id: parsed.data.messageId }) + + void trackEvent({ + event: "message.marked_read", + userId: ctx.userId, + targetId: parsed.data.messageId, + targetType: "message", + }) + + return { success: true, message: "Marked as read" } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +export async function deleteMessageAction(messageId: string): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_DELETE) + + const parsed = MessageIdSchema.safeParse({ messageId }) + if (!parsed.success) { + return { success: false, message: "Invalid message id", errors: parsed.error.flatten().fieldErrors } + } + + await deleteMessage(parsed.data.messageId, ctx.userId) + await invalidateFor("messaging.delete", { id: parsed.data.messageId }) + + void trackEvent({ + event: "message.deleted", + userId: ctx.userId, + targetId: parsed.data.messageId, + targetType: "message", + }) + + return { success: true, message: "Message deleted" } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +export async function getMessagesAction( + params: { + type: MessageType + page?: number + pageSize?: number + keyword?: string + starredOnly?: boolean + /** P2-11: 按发件人筛选 */ + senderId?: string + /** P2-11: 按日期范围筛选 */ + dateFrom?: string + dateTo?: string + /** P2-11: 按已读状态筛选 */ + isRead?: boolean + } +): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_READ) + const result = await getMessages({ userId: ctx.userId, ...params }) + return { success: true, data: result } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +/** + * P1-3: 获取消息线程(根消息 + 回复链)。 + * + * 替代直接调用 data-access.getMessageThread,统一通过 Server Action 入口 + * 做权限校验。返回的 Message[] 已按时间倒序(与 data-access 一致), + * UI 层可按需 reverse 为正序展示。 + * + * 注:原 getMessageDetailAction 已删除(P0-4 死代码,详情数据由 + * getMessageDetailPageData 编排函数 + RSC 直接获取,组件通过 props 接收)。 + */ +export async function getMessageThreadAction(messageId: string): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_READ) + + const parsed = MessageIdSchema.safeParse({ messageId }) + if (!parsed.success) { + return { success: false, message: "Invalid message id", errors: parsed.error.flatten().fieldErrors } + } + + // data-access.getMessageThread 内部会校验当前用户对根消息的访问权 + const thread = await getMessageThread(parsed.data.messageId, ctx.userId) + return { success: true, data: thread } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +export async function getRecipientsAction(): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_SEND) + const recipients = await getRecipients(ctx.userId, ctx.dataScope) + return { success: true, data: recipients } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +export async function getUnreadMessageCountAction(): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_READ) + const count = await getUnreadMessageCount(ctx.userId) + return { success: true, data: count } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +// --------------------------------------------------------------------------- +// V2-P2-13c: 消息星标 Server Actions +// --------------------------------------------------------------------------- + +export async function toggleMessageStarAction(messageId: string): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_READ) + + const parsed = MessageIdSchema.safeParse({ messageId }) + if (!parsed.success) { + return { success: false, message: "Invalid message id", errors: parsed.error.flatten().fieldErrors } + } + + await toggleMessageStar(parsed.data.messageId, ctx.userId) + await invalidateFor("messaging.toggleStar", { id: parsed.data.messageId }) + + void trackEvent({ + event: "message.star_toggled", + userId: ctx.userId, + targetId: parsed.data.messageId, + targetType: "message", + }) + + return { success: true, message: "Star toggled" } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +// --------------------------------------------------------------------------- +// P2-4: 消息撤回 Server Action(2 分钟窗口内仅发送方可撤回) +// --------------------------------------------------------------------------- + +export async function recallMessageAction( + messageId: string +): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_SEND) + + const parsed = MessageIdSchema.safeParse({ messageId }) + if (!parsed.success) { + return { success: false, message: "Invalid message id", errors: parsed.error.flatten().fieldErrors } + } + + const result = await recallMessage(parsed.data.messageId, ctx.userId) + + if (result === "not_found") { + return { success: false, message: "Message not found or not owned" } + } + if (result === "already_recalled") { + return { success: false, message: "Message already recalled" } + } + if (result === "expired") { + return { success: false, message: "Recall window expired" } + } + + await invalidateFor("messaging.recall", { id: parsed.data.messageId }) + + void trackEvent({ + event: "message.recalled", + userId: ctx.userId, + targetId: parsed.data.messageId, + targetType: "message", + }) + + return { success: true, message: "Message recalled" } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +// --------------------------------------------------------------------------- +// P2-3: 消息附件 Server Actions +// --------------------------------------------------------------------------- + +/** + * P2-3: 获取指定消息的附件列表。 + * + * 校验当前用户对消息有访问权(必须是发送方或接收方),防止越权访问附件。 + * 附件存储复用 files 模块的 fileAttachments 表(targetType="message")。 + */ +export async function getMessageAttachmentsAction( + messageId: string +): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_READ) + // 校验访问权:必须是消息的发送方或接收方 + const msg = await getMessageById(messageId, ctx.userId) + if (!msg) { + return { success: false, message: "Message not found" } + } + const attachments = await getMessageAttachments(messageId) + return { success: true, data: attachments } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} diff --git a/src/modules/messaging/actions-group.ts b/src/modules/messaging/actions-group.ts new file mode 100644 index 0000000..fc04b2d --- /dev/null +++ b/src/modules/messaging/actions-group.ts @@ -0,0 +1,114 @@ +"use server" + +import { invalidateFor } from "@/shared/lib/cache" +import { getTranslations } from "next-intl/server" +import { PermissionDeniedError, requirePermission } from "@/shared/lib/auth-guard" +import { trackEvent } from "@/shared/lib/track-event" +import { Permissions } from "@/shared/types/permissions" +import type { AuthContext } from "@/shared/types/permissions" +import type { ActionState } from "@/shared/types/action-state" +import { sendNotification } from "@/modules/notifications/dispatcher" + +import { SendGroupMessageSchema } from "./schema" +import { sendGroupMessage } from "./data-access" + +// --------------------------------------------------------------------------- +// P2-2: 群组消息 Server Action(教师→全班家长,fan-out on write) +// --------------------------------------------------------------------------- + +export async function sendGroupMessageAction( + prevState: ActionState<{ recipientCount: number }> | null, + formData: FormData +): Promise> { + let ctx: AuthContext | null = null + let parsedInput: { classId: string; subject: string | null; content: string } | null = null + try { + ctx = await requirePermission(Permissions.MESSAGE_SEND) + + const parsed = SendGroupMessageSchema.safeParse({ + classId: formData.get("classId"), + subject: formData.get("subject") || undefined, + content: formData.get("content"), + }) + + if (!parsed.success) { + return { success: false, message: "Invalid form data", errors: parsed.error.flatten().fieldErrors } + } + + const input = parsed.data + parsedInput = input + + // P2-2: 校验当前教师是否有权向该班级群发(必须在其 class_taught 范围内) + const allowed = ctx.dataScope.type === "class_taught" + && ctx.dataScope.classIds.includes(input.classId) + if (!allowed) { + return { success: false, message: "Not allowed to send group message to this class" } + } + + const result = await sendGroupMessage({ + senderId: ctx.userId, + classId: input.classId, + subject: input.subject, + content: input.content, + }) + + if (result.recipientIds.length === 0) { + return { success: false, message: "No recipients found in this class" } + } + + // 通知每个家长 + const t = await getTranslations("messages") + const notifyTitle = input.subject + ? t("notification.messageTitle", { subject: input.subject }) + : t("notification.messageTitleNoSubject") + const notifyContent = input.content.slice(0, 200) + + await Promise.all( + result.recipientIds.map((parentId) => + sendNotification({ + userId: parentId, + type: "info", + title: notifyTitle, + content: notifyContent, + actionUrl: "/messages", + metadata: { messageType: "group_message", groupMessageId: result.groupMessageId }, + }) + ) + ) + + await invalidateFor("messaging.sendGroup") + + void trackEvent({ + event: "message.group_sent", + userId: ctx.userId, + targetType: "message", + properties: { + classId: input.classId, + recipientCount: result.recipientIds.length, + groupMessageId: result.groupMessageId, + }, + }) + + return { + success: true, + message: "Group message sent", + data: { recipientCount: result.recipientIds.length }, + } + } catch (e) { + // P1-9: 失败埋点(权限拒绝除外) + if (ctx && parsedInput && !(e instanceof PermissionDeniedError)) { + void trackEvent({ + event: "message.group_send_failed", + userId: ctx.userId, + targetType: "message", + properties: { + classId: parsedInput.classId, + reason: e instanceof Error ? e.message : "unknown", + }, + }) + } + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} diff --git a/src/modules/messaging/actions-reports.ts b/src/modules/messaging/actions-reports.ts new file mode 100644 index 0000000..09c1b40 --- /dev/null +++ b/src/modules/messaging/actions-reports.ts @@ -0,0 +1,201 @@ +"use server" + +import { invalidateFor } from "@/shared/lib/cache" +import { PermissionDeniedError, requirePermission } from "@/shared/lib/auth-guard" +import { trackEvent } from "@/shared/lib/track-event" +import { Permissions } from "@/shared/types/permissions" +import type { AuthContext } from "@/shared/types/permissions" +import type { ActionState } from "@/shared/types/action-state" + +import { + ReportMessageSchema, + BlockUserSchema, +} from "./schema" +import { + getMessageById, + reportMessage, + hasUserReportedMessage, + blockUser, + unblockUser, + getUserBlocks, +} from "./data-access" +import type { UserBlock } from "./types" + +// --------------------------------------------------------------------------- +// P2-5: 消息举报 + 用户屏蔽 Server Actions +// --------------------------------------------------------------------------- + +/** + * P2-5: 举报消息。 + * + * - 校验消息存在且当前用户为接收方(只能举报收到的消息) + * - 防重复:同一用户对同一消息只能举报一次 + */ +export async function reportMessageAction( + prevState: ActionState | null, + formData: FormData +): Promise> { + let ctx: AuthContext | null = null + let parsedInput: { messageId: string; reason: string; description?: string } | null = null + try { + ctx = await requirePermission(Permissions.MESSAGE_READ) + + const parsed = ReportMessageSchema.safeParse({ + messageId: formData.get("messageId"), + reason: formData.get("reason"), + description: formData.get("description") || undefined, + }) + + if (!parsed.success) { + return { success: false, message: "Invalid form data", errors: parsed.error.flatten().fieldErrors } + } + + const input = parsed.data + parsedInput = input + + // 校验消息存在且当前用户为接收方(防越权举报) + const msg = await getMessageById(input.messageId, ctx.userId) + if (!msg) { + return { success: false, message: "Message not found" } + } + // 举报人必须是接收方(不能举报自己发的消息) + if (msg.senderId === ctx.userId) { + return { success: false, message: "Cannot report your own message" } + } + + const result = await reportMessage({ + reporterId: ctx.userId, + messageId: input.messageId, + reportedUserId: msg.senderId, + reason: input.reason as "spam" | "harassment" | "inappropriate" | "other", + description: input.description ?? null, + }) + + if (result === "already_reported") { + return { success: false, message: "Already reported this message" } + } + + void trackEvent({ + event: "message.reported", + userId: ctx.userId, + targetId: input.messageId, + targetType: "message", + properties: { reason: input.reason }, + }) + + return { success: true, message: "Message reported", data: null } + } catch (e) { + if (ctx && parsedInput && !(e instanceof PermissionDeniedError)) { + void trackEvent({ + event: "message.report_failed", + userId: ctx.userId, + targetId: parsedInput.messageId, + targetType: "message", + properties: { reason: parsedInput.reason }, + }) + } + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +/** + * P2-5: 检查当前用户是否已举报某条消息(用于 UI 禁用状态)。 + */ +export async function checkReportedAction(messageId: string): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_READ) + const reported = await hasUserReportedMessage(ctx.userId, messageId) + return { success: true, data: reported } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +/** + * P2-5: 屏蔽用户。 + */ +export async function blockUserAction( + prevState: ActionState | null, + formData: FormData +): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_SEND) + + const parsed = BlockUserSchema.safeParse({ + blockedId: formData.get("blockedId"), + }) + + if (!parsed.success) { + return { success: false, message: "Invalid form data", errors: parsed.error.flatten().fieldErrors } + } + + const input = parsed.data + + const result = await blockUser(ctx.userId, input.blockedId) + + if (result === "self_block") { + return { success: false, message: "Cannot block yourself" } + } + if (result === "already_blocked") { + return { success: false, message: "User already blocked" } + } + + await invalidateFor("messaging.block", { id: input.blockedId }) + + void trackEvent({ + event: "message.user_blocked", + userId: ctx.userId, + targetId: input.blockedId, + targetType: "user", + }) + + return { success: true, message: "User blocked", data: null } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +/** + * P2-5: 取消屏蔽用户。 + */ +export async function unblockUserAction(blockedId: string): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_SEND) + await unblockUser(ctx.userId, blockedId) + await invalidateFor("messaging.unblock", { id: blockedId }) + + void trackEvent({ + event: "message.user_unblocked", + userId: ctx.userId, + targetId: blockedId, + targetType: "user", + }) + + return { success: true, message: "User unblocked", data: null } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +/** + * P2-5: 获取当前用户的屏蔽列表。 + */ +export async function getUserBlocksAction(): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_SEND) + const blocks = await getUserBlocks(ctx.userId) + return { success: true, data: blocks } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} diff --git a/src/modules/messaging/actions-templates.ts b/src/modules/messaging/actions-templates.ts new file mode 100644 index 0000000..bfebd58 --- /dev/null +++ b/src/modules/messaging/actions-templates.ts @@ -0,0 +1,220 @@ +"use server" + +import { invalidateFor } from "@/shared/lib/cache" +import { PermissionDeniedError, requirePermission } from "@/shared/lib/auth-guard" +import { trackEvent } from "@/shared/lib/track-event" +import { Permissions } from "@/shared/types/permissions" +import type { ActionState } from "@/shared/types/action-state" + +import { + MessageDraftSchema, + MessageTemplateSchema, + MessageIdSchema, +} from "./schema" +import { + getMessageDrafts, + createMessageDraft, + updateMessageDraft, + deleteMessageDraft, + getMessageTemplates, + createMessageTemplate, + updateMessageTemplate, + deleteMessageTemplate, +} from "./data-access" +import type { MessageDraft, MessageTemplate } from "./types" + +// --------------------------------------------------------------------------- +// V2-P2-13c: 消息草稿 Server Actions +// --------------------------------------------------------------------------- + +export async function getMessageDraftsAction(): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_SEND) + const drafts = await getMessageDrafts(ctx.userId) + return { success: true, data: drafts } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +export async function saveMessageDraftAction( + prevState: ActionState | null, + formData: FormData +): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_SEND) + + const parsed = MessageDraftSchema.safeParse({ + draftId: formData.get("draftId") ?? undefined, + receiverId: formData.get("receiverId") ?? undefined, + subject: formData.get("subject") ?? undefined, + content: formData.get("content") ?? undefined, + parentMessageId: formData.get("parentMessageId") ?? undefined, + }) + + if (!parsed.success) { + return { success: false, message: "Invalid form data", errors: parsed.error.flatten().fieldErrors } + } + + const input = parsed.data + // P2-10: 从 FormData 读取 deviceId 和 expectedVersion(可选) + const deviceId = (formData.get("deviceId") as string | null) ?? null + const expectedVersionRaw = formData.get("expectedVersion") + const expectedVersion = expectedVersionRaw ? Number(expectedVersionRaw) : undefined + + const draftData = { + receiverId: input.receiverId || null, + subject: input.subject || null, + content: input.content || null, + parentMessageId: input.parentMessageId || null, + deviceId, + expectedVersion, + } + + if (input.draftId) { + // P2-10: updateMessageDraft 现在返回 "ok" | "not_found" | "conflict" + const result = await updateMessageDraft(input.draftId, ctx.userId, draftData) + if (result === "conflict") { + // P2-10: 版本冲突,客户端应拉取最新版本 + return { success: false, message: "Draft version conflict" } + } + if (result === "not_found") { + // P2-10: 草稿不存在(可能已在其他设备删除),重新创建 + const id = await createMessageDraft({ + userId: ctx.userId, + receiverId: draftData.receiverId, + subject: draftData.subject, + content: draftData.content, + parentMessageId: draftData.parentMessageId, + deviceId: draftData.deviceId, + }) + return { success: true, message: "Draft recreated", data: id } + } + return { success: true, message: "Draft saved", data: input.draftId } + } + + const id = await createMessageDraft({ + userId: ctx.userId, + receiverId: draftData.receiverId, + subject: draftData.subject, + content: draftData.content, + parentMessageId: draftData.parentMessageId, + deviceId: draftData.deviceId, + }) + + return { success: true, message: "Draft created", data: id } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +export async function deleteMessageDraftAction(draftId: string): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_SEND) + + await deleteMessageDraft(draftId, ctx.userId) + await invalidateFor("messaging.draft.delete") + + return { success: true, message: "Draft deleted" } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +// --------------------------------------------------------------------------- +// P2-6: 快捷回复模板 Server Actions +// --------------------------------------------------------------------------- + +export async function getMessageTemplatesAction(): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_SEND) + const templates = await getMessageTemplates(ctx.userId) + return { success: true, data: templates } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +export async function saveMessageTemplateAction( + prevState: ActionState | null, + formData: FormData +): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_SEND) + + const parsed = MessageTemplateSchema.safeParse({ + templateId: formData.get("templateId") ?? undefined, + title: formData.get("title"), + content: formData.get("content"), + sortOrder: formData.get("sortOrder") !== null + ? Number(formData.get("sortOrder")) + : undefined, + }) + + if (!parsed.success) { + return { success: false, message: "Invalid form data", errors: parsed.error.flatten().fieldErrors } + } + + const input = parsed.data + + if (input.templateId) { + await updateMessageTemplate(input.templateId, ctx.userId, { + title: input.title, + content: input.content, + sortOrder: input.sortOrder, + }) + await invalidateFor("messaging.template.save", { id: input.templateId }) + return { success: true, message: "Template updated", data: input.templateId } + } + + const id = await createMessageTemplate({ + userId: ctx.userId, + title: input.title, + content: input.content, + sortOrder: input.sortOrder, + }) + + await invalidateFor("messaging.template.save", { id }) + + void trackEvent({ + event: "message.template_created", + userId: ctx.userId, + targetId: id, + targetType: "message", + }) + + return { success: true, message: "Template created", data: id } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} + +export async function deleteMessageTemplateAction(templateId: string): Promise> { + try { + const ctx = await requirePermission(Permissions.MESSAGE_SEND) + + const parsed = MessageIdSchema.safeParse({ messageId: templateId }) + if (!parsed.success) { + return { success: false, message: "Invalid template id", errors: parsed.error.flatten().fieldErrors } + } + + await deleteMessageTemplate(parsed.data.messageId, ctx.userId) + await invalidateFor("messaging.template.delete", { id: parsed.data.messageId }) + + return { success: true, message: "Template deleted" } + } catch (e) { + if (e instanceof PermissionDeniedError) return { success: false, message: e.message } + if (e instanceof Error) return { success: false, message: e.message } + return { success: false, message: "Unexpected error" } + } +} diff --git a/src/modules/messaging/actions.ts b/src/modules/messaging/actions.ts index bbb9f54..d936e79 100644 --- a/src/modules/messaging/actions.ts +++ b/src/modules/messaging/actions.ts @@ -1,972 +1,47 @@ "use server" -import { invalidateFor } from "@/shared/lib/cache" -import { getTranslations } from "next-intl/server" -import { PermissionDeniedError, requirePermission } from "@/shared/lib/auth-guard" -import { trackEvent } from "@/shared/lib/track-event" -import { Permissions } from "@/shared/types/permissions" -import type { AuthContext } from "@/shared/types/permissions" -import type { ActionState } from "@/shared/types/action-state" -import { sendNotification } from "@/modules/notifications/dispatcher" - -import { - SendMessageSchema, - MessageIdSchema, - MessageDraftSchema, - BulkMessageIdsSchema, - MessageTemplateSchema, - SendGroupMessageSchema, - ReportMessageSchema, - BlockUserSchema, -} from "./schema" -import { - getMessages, - getMessageById, - getMessageThread, - createMessage, - markMessageAsRead, - deleteMessage, - getRecipients, - getUnreadMessageCount, - toggleMessageStar, - getMessageDrafts, - createMessageDraft, - updateMessageDraft, - deleteMessageDraft, - isReceiverAllowed, - bulkMarkMessagesAsRead, - bulkDeleteMessages, - bulkToggleMessagesStar, - recallMessage, - getMessageTemplates, - createMessageTemplate, - updateMessageTemplate, - deleteMessageTemplate, - sendGroupMessage, - reportMessage, - hasUserReportedMessage, - blockUser, - unblockUser, - isEitherUserBlocked, - getUserBlocks, - getMessageAttachments, -} from "./data-access" -import type { Message, MessageType, MessageDraft, MessageTemplate, RecipientOption, UserBlock, MessageAttachment } from "./types" - -export async function sendMessageAction( - prevState: ActionState | null, - formData: FormData -): Promise> { - let ctx: AuthContext | null = null - let parsedInput: { - receiverId: string - subject: string | null - content: string - parentMessageId: string | null - } | null = null - try { - ctx = await requirePermission(Permissions.MESSAGE_SEND) - - const parsed = SendMessageSchema.safeParse({ - receiverId: formData.get("receiverId"), - subject: formData.get("subject") || undefined, - content: formData.get("content"), - parentMessageId: formData.get("parentMessageId") || undefined, - }) - - if (!parsed.success) { - return { success: false, message: "Invalid form data", errors: parsed.error.flatten().fieldErrors } - } - - const input = parsed.data - parsedInput = input - if (input.receiverId === ctx.userId) { - return { success: false, message: "Cannot send a message to yourself" } - } - - // P0-1: Server Action 二次校验收件人是否在 sender 的 DataScope 允许范围内 - // 防止绕过前端 Select 直接构造 FormData 提交任意 receiverId 越权发送 - const allowed = await isReceiverAllowed(ctx.userId, input.receiverId, ctx.dataScope) - if (!allowed) { - return { success: false, message: "Recipient not allowed" } - } - - // P2-5: 屏蔽校验 —— 发送方或接收方任一屏蔽对方,则禁止发送 - const blocked = await isEitherUserBlocked(ctx.userId, input.receiverId) - if (blocked) { - return { success: false, message: "Cannot send message due to block" } - } - - // P0-6: 校验 parentMessageId 属于当前用户参与的会话 - // 防止将回复消息注入到他人会话线程 - if (input.parentMessageId) { - const parent = await getMessageById(input.parentMessageId, ctx.userId) - if (!parent) { - return { success: false, message: "Parent message not accessible" } - } - } - - const id = await createMessage({ - senderId: ctx.userId, - receiverId: input.receiverId, - subject: input.subject, - content: input.content, - parentMessageId: input.parentMessageId, - }) - - // Notify the receiver about the new message via the notifications dispatcher. - // This respects user notification preferences (SMS/WeChat/Email/In-App). - const t = await getTranslations("messages") - const notifyTitle = input.subject - ? t("notification.messageTitle", { subject: input.subject }) - : t("notification.messageTitleNoSubject") - await sendNotification({ - userId: input.receiverId, - type: "info", - title: notifyTitle, - content: input.content.slice(0, 200), - actionUrl: `/messages/${id}`, - metadata: { messageType: "message", messageId: id }, - }) - - await invalidateFor("messaging.send", { id }) - - void trackEvent({ - event: "message.sent", - userId: ctx.userId, - targetId: id, - targetType: "message", - properties: { receiverId: input.receiverId, hasSubject: !!input.subject }, - }) - - return { success: true, message: "Message sent", data: id } - } catch (e) { - // P1-9: 失败埋点(权限拒绝除外,避免噪音) - if (ctx && parsedInput && !(e instanceof PermissionDeniedError)) { - void trackEvent({ - event: "message.send_failed", - userId: ctx.userId, - targetType: "message", - properties: { - receiverId: parsedInput.receiverId, - reason: e instanceof Error ? e.message : "unknown", - }, - }) - } - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -export async function markMessageAsReadAction(messageId: string): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_READ) - - const parsed = MessageIdSchema.safeParse({ messageId }) - if (!parsed.success) { - return { success: false, message: "Invalid message id", errors: parsed.error.flatten().fieldErrors } - } - - await markMessageAsRead(parsed.data.messageId, ctx.userId) - await invalidateFor("messaging.markRead", { id: parsed.data.messageId }) - - void trackEvent({ - event: "message.marked_read", - userId: ctx.userId, - targetId: parsed.data.messageId, - targetType: "message", - }) - - return { success: true, message: "Marked as read" } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -export async function deleteMessageAction(messageId: string): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_DELETE) - - const parsed = MessageIdSchema.safeParse({ messageId }) - if (!parsed.success) { - return { success: false, message: "Invalid message id", errors: parsed.error.flatten().fieldErrors } - } - - await deleteMessage(parsed.data.messageId, ctx.userId) - await invalidateFor("messaging.delete", { id: parsed.data.messageId }) - - void trackEvent({ - event: "message.deleted", - userId: ctx.userId, - targetId: parsed.data.messageId, - targetType: "message", - }) - - return { success: true, message: "Message deleted" } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -export async function getMessagesAction( - params: { - type: MessageType - page?: number - pageSize?: number - keyword?: string - starredOnly?: boolean - /** P2-11: 按发件人筛选 */ - senderId?: string - /** P2-11: 按日期范围筛选 */ - dateFrom?: string - dateTo?: string - /** P2-11: 按已读状态筛选 */ - isRead?: boolean - } -): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_READ) - const result = await getMessages({ userId: ctx.userId, ...params }) - return { success: true, data: result } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - /** - * P1-3: 获取消息线程(根消息 + 回复链)。 + * 私信 Server Actions(barrel 重新导出) * - * 替代直接调用 data-access.getMessageThread,统一通过 Server Action 入口 - * 做权限校验。返回的 Message[] 已按时间倒序(与 data-access 一致), - * UI 层可按需 reverse 为正序展示。 - * - * 注:原 getMessageDetailAction 已删除(P0-4 死代码,详情数据由 - * getMessageDetailPageData 编排函数 + RSC 直接获取,组件通过 props 接收)。 + * 原 actions.ts 973 行接近 1000 行硬上限,拆分为 5 个按职责划分的子文件 + * (与 data-access 拆分对齐)。本文件仅作为公共 API 入口,保持向后兼容。 + * 通知相关 Server Actions 已迁移至 @/modules/notifications/actions。 */ -export async function getMessageThreadAction(messageId: string): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_READ) - const parsed = MessageIdSchema.safeParse({ messageId }) - if (!parsed.success) { - return { success: false, message: "Invalid message id", errors: parsed.error.flatten().fieldErrors } - } - - // data-access.getMessageThread 内部会校验当前用户对根消息的访问权 - const thread = await getMessageThread(parsed.data.messageId, ctx.userId) - return { success: true, data: thread } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -export async function getRecipientsAction(): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_SEND) - const recipients = await getRecipients(ctx.userId, ctx.dataScope) - return { success: true, data: recipients } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -export async function getUnreadMessageCountAction(): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_READ) - const count = await getUnreadMessageCount(ctx.userId) - return { success: true, data: count } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -// --------------------------------------------------------------------------- -// V2-P2-13c: 消息星标 Server Actions -// --------------------------------------------------------------------------- - -export async function toggleMessageStarAction(messageId: string): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_READ) - - const parsed = MessageIdSchema.safeParse({ messageId }) - if (!parsed.success) { - return { success: false, message: "Invalid message id", errors: parsed.error.flatten().fieldErrors } - } - - await toggleMessageStar(parsed.data.messageId, ctx.userId) - await invalidateFor("messaging.toggleStar", { id: parsed.data.messageId }) - - void trackEvent({ - event: "message.star_toggled", - userId: ctx.userId, - targetId: parsed.data.messageId, - targetType: "message", - }) - - return { success: true, message: "Star toggled" } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -// --------------------------------------------------------------------------- -// V2-P2-13c: 消息草稿 Server Actions -// --------------------------------------------------------------------------- - -export async function getMessageDraftsAction(): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_SEND) - const drafts = await getMessageDrafts(ctx.userId) - return { success: true, data: drafts } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -export async function saveMessageDraftAction( - prevState: ActionState | null, - formData: FormData -): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_SEND) - - const parsed = MessageDraftSchema.safeParse({ - draftId: formData.get("draftId") ?? undefined, - receiverId: formData.get("receiverId") ?? undefined, - subject: formData.get("subject") ?? undefined, - content: formData.get("content") ?? undefined, - parentMessageId: formData.get("parentMessageId") ?? undefined, - }) - - if (!parsed.success) { - return { success: false, message: "Invalid form data", errors: parsed.error.flatten().fieldErrors } - } - - const input = parsed.data - // P2-10: 从 FormData 读取 deviceId 和 expectedVersion(可选) - const deviceId = (formData.get("deviceId") as string | null) ?? null - const expectedVersionRaw = formData.get("expectedVersion") - const expectedVersion = expectedVersionRaw ? Number(expectedVersionRaw) : undefined - - const draftData = { - receiverId: input.receiverId || null, - subject: input.subject || null, - content: input.content || null, - parentMessageId: input.parentMessageId || null, - deviceId, - expectedVersion, - } - - if (input.draftId) { - // P2-10: updateMessageDraft 现在返回 "ok" | "not_found" | "conflict" - const result = await updateMessageDraft(input.draftId, ctx.userId, draftData) - if (result === "conflict") { - // P2-10: 版本冲突,客户端应拉取最新版本 - return { success: false, message: "Draft version conflict" } - } - if (result === "not_found") { - // P2-10: 草稿不存在(可能已在其他设备删除),重新创建 - const id = await createMessageDraft({ - userId: ctx.userId, - receiverId: draftData.receiverId, - subject: draftData.subject, - content: draftData.content, - parentMessageId: draftData.parentMessageId, - deviceId: draftData.deviceId, - }) - return { success: true, message: "Draft recreated", data: id } - } - return { success: true, message: "Draft saved", data: input.draftId } - } - - const id = await createMessageDraft({ - userId: ctx.userId, - receiverId: draftData.receiverId, - subject: draftData.subject, - content: draftData.content, - parentMessageId: draftData.parentMessageId, - deviceId: draftData.deviceId, - }) - - return { success: true, message: "Draft created", data: id } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -export async function deleteMessageDraftAction(draftId: string): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_SEND) - - await deleteMessageDraft(draftId, ctx.userId) - await invalidateFor("messaging.draft.delete") - - return { success: true, message: "Draft deleted" } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -// --------------------------------------------------------------------------- -// P2-1: 批量操作 Server Actions -// --------------------------------------------------------------------------- - -export async function bulkMarkMessagesAsReadAction( - ids: string[] -): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_READ) - - const parsed = BulkMessageIdsSchema.safeParse({ ids }) - if (!parsed.success) { - return { success: false, message: "Invalid message IDs", errors: parsed.error.flatten().fieldErrors } - } - - const updated = await bulkMarkMessagesAsRead(parsed.data.ids, ctx.userId) - await invalidateFor("messaging.bulkMarkRead") - - void trackEvent({ - event: "message.marked_read", - userId: ctx.userId, - targetType: "message", - properties: { count: updated, bulk: true }, - }) - - return { success: true, message: "Messages marked as read", data: { updated } } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -export async function bulkDeleteMessagesAction( - ids: string[] -): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_DELETE) - - const parsed = BulkMessageIdsSchema.safeParse({ ids }) - if (!parsed.success) { - return { success: false, message: "Invalid message IDs", errors: parsed.error.flatten().fieldErrors } - } - - await bulkDeleteMessages(parsed.data.ids, ctx.userId) - await invalidateFor("messaging.bulkDelete") - - void trackEvent({ - event: "message.deleted", - userId: ctx.userId, - targetType: "message", - properties: { count: parsed.data.ids.length, bulk: true }, - }) - - return { success: true, message: "Messages deleted" } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -export async function bulkToggleMessagesStarAction( - ids: string[] -): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_READ) - - const parsed = BulkMessageIdsSchema.safeParse({ ids }) - if (!parsed.success) { - return { success: false, message: "Invalid message IDs", errors: parsed.error.flatten().fieldErrors } - } - - await bulkToggleMessagesStar(parsed.data.ids, ctx.userId) - await invalidateFor("messaging.bulkToggleStar") - - void trackEvent({ - event: "message.star_toggled", - userId: ctx.userId, - targetType: "message", - properties: { count: parsed.data.ids.length, bulk: true }, - }) - - return { success: true, message: "Star toggled" } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -// --------------------------------------------------------------------------- -// P2-4: 消息撤回 Server Action(2 分钟窗口内仅发送方可撤回) -// --------------------------------------------------------------------------- - -export async function recallMessageAction( - messageId: string -): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_SEND) - - const parsed = MessageIdSchema.safeParse({ messageId }) - if (!parsed.success) { - return { success: false, message: "Invalid message id", errors: parsed.error.flatten().fieldErrors } - } - - const result = await recallMessage(parsed.data.messageId, ctx.userId) - - if (result === "not_found") { - return { success: false, message: "Message not found or not owned" } - } - if (result === "already_recalled") { - return { success: false, message: "Message already recalled" } - } - if (result === "expired") { - return { success: false, message: "Recall window expired" } - } - - await invalidateFor("messaging.recall", { id: parsed.data.messageId }) - - void trackEvent({ - event: "message.recalled", - userId: ctx.userId, - targetId: parsed.data.messageId, - targetType: "message", - }) - - return { success: true, message: "Message recalled" } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -// --------------------------------------------------------------------------- -// P2-2: 群组消息 Server Action(教师→全班家长,fan-out on write) -// --------------------------------------------------------------------------- - -export async function sendGroupMessageAction( - prevState: ActionState<{ recipientCount: number }> | null, - formData: FormData -): Promise> { - let ctx: AuthContext | null = null - let parsedInput: { classId: string; subject: string | null; content: string } | null = null - try { - ctx = await requirePermission(Permissions.MESSAGE_SEND) - - const parsed = SendGroupMessageSchema.safeParse({ - classId: formData.get("classId"), - subject: formData.get("subject") || undefined, - content: formData.get("content"), - }) - - if (!parsed.success) { - return { success: false, message: "Invalid form data", errors: parsed.error.flatten().fieldErrors } - } - - const input = parsed.data - parsedInput = input - - // P2-2: 校验当前教师是否有权向该班级群发(必须在其 class_taught 范围内) - const allowed = ctx.dataScope.type === "class_taught" - && ctx.dataScope.classIds.includes(input.classId) - if (!allowed) { - return { success: false, message: "Not allowed to send group message to this class" } - } - - const result = await sendGroupMessage({ - senderId: ctx.userId, - classId: input.classId, - subject: input.subject, - content: input.content, - }) - - if (result.recipientIds.length === 0) { - return { success: false, message: "No recipients found in this class" } - } - - // 通知每个家长 - const t = await getTranslations("messages") - const notifyTitle = input.subject - ? t("notification.messageTitle", { subject: input.subject }) - : t("notification.messageTitleNoSubject") - const notifyContent = input.content.slice(0, 200) - - await Promise.all( - result.recipientIds.map((parentId) => - sendNotification({ - userId: parentId, - type: "info", - title: notifyTitle, - content: notifyContent, - actionUrl: "/messages", - metadata: { messageType: "group_message", groupMessageId: result.groupMessageId }, - }) - ) - ) - - await invalidateFor("messaging.sendGroup") - - void trackEvent({ - event: "message.group_sent", - userId: ctx.userId, - targetType: "message", - properties: { - classId: input.classId, - recipientCount: result.recipientIds.length, - groupMessageId: result.groupMessageId, - }, - }) - - return { - success: true, - message: "Group message sent", - data: { recipientCount: result.recipientIds.length }, - } - } catch (e) { - // P1-9: 失败埋点(权限拒绝除外) - if (ctx && parsedInput && !(e instanceof PermissionDeniedError)) { - void trackEvent({ - event: "message.group_send_failed", - userId: ctx.userId, - targetType: "message", - properties: { - classId: parsedInput.classId, - reason: e instanceof Error ? e.message : "unknown", - }, - }) - } - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -// --------------------------------------------------------------------------- -// P2-6: 快捷回复模板 Server Actions -// --------------------------------------------------------------------------- - -export async function getMessageTemplatesAction(): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_SEND) - const templates = await getMessageTemplates(ctx.userId) - return { success: true, data: templates } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -export async function saveMessageTemplateAction( - prevState: ActionState | null, - formData: FormData -): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_SEND) - - const parsed = MessageTemplateSchema.safeParse({ - templateId: formData.get("templateId") ?? undefined, - title: formData.get("title"), - content: formData.get("content"), - sortOrder: formData.get("sortOrder") !== null - ? Number(formData.get("sortOrder")) - : undefined, - }) - - if (!parsed.success) { - return { success: false, message: "Invalid form data", errors: parsed.error.flatten().fieldErrors } - } - - const input = parsed.data - - if (input.templateId) { - await updateMessageTemplate(input.templateId, ctx.userId, { - title: input.title, - content: input.content, - sortOrder: input.sortOrder, - }) - await invalidateFor("messaging.template.save", { id: input.templateId }) - return { success: true, message: "Template updated", data: input.templateId } - } - - const id = await createMessageTemplate({ - userId: ctx.userId, - title: input.title, - content: input.content, - sortOrder: input.sortOrder, - }) - - await invalidateFor("messaging.template.save", { id }) - - void trackEvent({ - event: "message.template_created", - userId: ctx.userId, - targetId: id, - targetType: "message", - }) - - return { success: true, message: "Template created", data: id } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -export async function deleteMessageTemplateAction(templateId: string): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_SEND) - - const parsed = MessageIdSchema.safeParse({ messageId: templateId }) - if (!parsed.success) { - return { success: false, message: "Invalid template id", errors: parsed.error.flatten().fieldErrors } - } - - await deleteMessageTemplate(parsed.data.messageId, ctx.userId) - await invalidateFor("messaging.template.delete", { id: parsed.data.messageId }) - - return { success: true, message: "Template deleted" } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -// --------------------------------------------------------------------------- -// 通知相关 Server Actions(getNotificationsAction / markNotificationAsReadAction / -// markAllNotificationsAsReadAction / getUnreadNotificationCountAction / -// getNotificationPreferencesAction / updateNotificationPreferencesAction) -// 已迁移至 @/modules/notifications/actions。请直接从 notifications 模块导入。 -// --------------------------------------------------------------------------- - -// --------------------------------------------------------------------------- -// P2-5: 消息举报 + 用户屏蔽 Server Actions -// --------------------------------------------------------------------------- - -/** - * P2-5: 举报消息。 - * - * - 校验消息存在且当前用户为接收方(只能举报收到的消息) - * - 防重复:同一用户对同一消息只能举报一次 - */ -export async function reportMessageAction( - prevState: ActionState | null, - formData: FormData -): Promise> { - let ctx: AuthContext | null = null - let parsedInput: { messageId: string; reason: string; description?: string } | null = null - try { - ctx = await requirePermission(Permissions.MESSAGE_READ) - - const parsed = ReportMessageSchema.safeParse({ - messageId: formData.get("messageId"), - reason: formData.get("reason"), - description: formData.get("description") || undefined, - }) - - if (!parsed.success) { - return { success: false, message: "Invalid form data", errors: parsed.error.flatten().fieldErrors } - } - - const input = parsed.data - parsedInput = input - - // 校验消息存在且当前用户为接收方(防越权举报) - const msg = await getMessageById(input.messageId, ctx.userId) - if (!msg) { - return { success: false, message: "Message not found" } - } - // 举报人必须是接收方(不能举报自己发的消息) - if (msg.senderId === ctx.userId) { - return { success: false, message: "Cannot report your own message" } - } - - const result = await reportMessage({ - reporterId: ctx.userId, - messageId: input.messageId, - reportedUserId: msg.senderId, - reason: input.reason as "spam" | "harassment" | "inappropriate" | "other", - description: input.description ?? null, - }) - - if (result === "already_reported") { - return { success: false, message: "Already reported this message" } - } - - void trackEvent({ - event: "message.reported", - userId: ctx.userId, - targetId: input.messageId, - targetType: "message", - properties: { reason: input.reason }, - }) - - return { success: true, message: "Message reported", data: null } - } catch (e) { - if (ctx && parsedInput && !(e instanceof PermissionDeniedError)) { - void trackEvent({ - event: "message.report_failed", - userId: ctx.userId, - targetId: parsedInput.messageId, - targetType: "message", - properties: { reason: parsedInput.reason }, - }) - } - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -/** - * P2-5: 检查当前用户是否已举报某条消息(用于 UI 禁用状态)。 - */ -export async function checkReportedAction(messageId: string): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_READ) - const reported = await hasUserReportedMessage(ctx.userId, messageId) - return { success: true, data: reported } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -/** - * P2-5: 屏蔽用户。 - */ -export async function blockUserAction( - prevState: ActionState | null, - formData: FormData -): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_SEND) - - const parsed = BlockUserSchema.safeParse({ - blockedId: formData.get("blockedId"), - }) - - if (!parsed.success) { - return { success: false, message: "Invalid form data", errors: parsed.error.flatten().fieldErrors } - } - - const input = parsed.data - - const result = await blockUser(ctx.userId, input.blockedId) - - if (result === "self_block") { - return { success: false, message: "Cannot block yourself" } - } - if (result === "already_blocked") { - return { success: false, message: "User already blocked" } - } - - await invalidateFor("messaging.block", { id: input.blockedId }) - - void trackEvent({ - event: "message.user_blocked", - userId: ctx.userId, - targetId: input.blockedId, - targetType: "user", - }) - - return { success: true, message: "User blocked", data: null } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -/** - * P2-5: 取消屏蔽用户。 - */ -export async function unblockUserAction(blockedId: string): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_SEND) - await unblockUser(ctx.userId, blockedId) - await invalidateFor("messaging.unblock", { id: blockedId }) - - void trackEvent({ - event: "message.user_unblocked", - userId: ctx.userId, - targetId: blockedId, - targetType: "user", - }) - - return { success: true, message: "User unblocked", data: null } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -/** - * P2-5: 获取当前用户的屏蔽列表。 - */ -export async function getUserBlocksAction(): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_SEND) - const blocks = await getUserBlocks(ctx.userId) - return { success: true, data: blocks } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} - -// --------------------------------------------------------------------------- -// P2-3: 消息附件 Server Actions -// --------------------------------------------------------------------------- - -/** - * P2-3: 获取指定消息的附件列表。 - * - * 校验当前用户对消息有访问权(必须是发送方或接收方),防止越权访问附件。 - * 附件存储复用 files 模块的 fileAttachments 表(targetType="message")。 - */ -export async function getMessageAttachmentsAction( - messageId: string -): Promise> { - try { - const ctx = await requirePermission(Permissions.MESSAGE_READ) - // 校验访问权:必须是消息的发送方或接收方 - const msg = await getMessageById(messageId, ctx.userId) - if (!msg) { - return { success: false, message: "Message not found" } - } - const attachments = await getMessageAttachments(messageId) - return { success: true, data: attachments } - } catch (e) { - if (e instanceof PermissionDeniedError) return { success: false, message: e.message } - if (e instanceof Error) return { success: false, message: e.message } - return { success: false, message: "Unexpected error" } - } -} +export { + sendMessageAction, + markMessageAsReadAction, + deleteMessageAction, + getMessagesAction, + getMessageThreadAction, + getRecipientsAction, + getUnreadMessageCountAction, + toggleMessageStarAction, + recallMessageAction, + getMessageAttachmentsAction, +} from "./actions-core" + +export { + bulkMarkMessagesAsReadAction, + bulkDeleteMessagesAction, + bulkToggleMessagesStarAction, +} from "./actions-bulk" + +export { sendGroupMessageAction } from "./actions-group" + +export { + getMessageDraftsAction, + saveMessageDraftAction, + deleteMessageDraftAction, + getMessageTemplatesAction, + saveMessageTemplateAction, + deleteMessageTemplateAction, +} from "./actions-templates" + +export { + reportMessageAction, + checkReportedAction, + blockUserAction, + unblockUserAction, + getUserBlocksAction, +} from "./actions-reports" diff --git a/src/modules/questions/data-access-cross-module.ts b/src/modules/questions/data-access-cross-module.ts new file mode 100644 index 0000000..b782c24 --- /dev/null +++ b/src/modules/questions/data-access-cross-module.ts @@ -0,0 +1,170 @@ +import 'server-only'; + +import { db } from "@/shared/db"; +import { questions, questionsToKnowledgePoints } from "@/shared/db/schema"; +import { count, inArray } from "drizzle-orm"; +import { cacheFn } from "@/shared/lib/cache"; +import { getKnowledgePointNamesByIds } from "@/modules/textbooks/data-access"; + +// --------------------------------------------------------------------------- +// Cross-module query interfaces — read-only access for other modules +// --------------------------------------------------------------------------- + +export type QuestionKnowledgePoint = { + questionId: string + knowledgePointId: string + knowledgePointName: string +} + +/** Returns knowledge points associated with the given question ids. */ +export const getKnowledgePointsForQuestionsRaw = async ( + questionIds: string[] +): Promise> => { + const result = new Map() + const uniqueIds = Array.from(new Set(questionIds.filter((v): v is string => typeof v === "string" && v.length > 0))) + if (uniqueIds.length === 0) return result + + // 1. 查询 questionId -> knowledgePointId 关联(questions 模块自有表) + const rows = await db + .select({ + questionId: questionsToKnowledgePoints.questionId, + knowledgePointId: questionsToKnowledgePoints.knowledgePointId, + }) + .from(questionsToKnowledgePoints) + .where(inArray(questionsToKnowledgePoints.questionId, uniqueIds)) + + if (rows.length === 0) return result + + // 2. 跨模块批量解析知识点名称,避免直接 JOIN knowledgePoints 表 + const kpIds = Array.from(new Set(rows.map((r) => r.knowledgePointId))) + const kpNameMap = await getKnowledgePointNamesByIds(kpIds) + + // 3. 组装结果 + for (const r of rows) { + const list = result.get(r.questionId) ?? [] + list.push({ + questionId: r.questionId, + knowledgePointId: r.knowledgePointId, + knowledgePointName: kpNameMap.get(r.knowledgePointId) ?? "", + }) + result.set(r.questionId, list) + } + return result +} +export const getKnowledgePointsForQuestions = cacheFn(getKnowledgePointsForQuestionsRaw, { + tags: ["questions"], + ttl: 300, + keyParts: ["questions", "getKnowledgePointsForQuestions"], +}) + +/** + * 跨模块接口:获取题目内容与类型(供 error-book 模块提取正确答案使用)。 + * + * error-book 模块在采集错题时需要从题目内容中提取正确答案(correctAnswer), + * 此接口返回题目的 content 和 type,由 error-book 模块调用 + * `homework/lib/question-content-utils.ts` 中的纯函数进行提取。 + */ +export type QuestionContentForErrorCollection = { + content: unknown + type: string +} + +export const getQuestionsContentForErrorCollectionRaw = async ( + questionIds: string[], +): Promise> => { + const result = new Map() + const uniqueIds = Array.from(new Set(questionIds.filter((v): v is string => typeof v === "string" && v.length > 0))) + if (uniqueIds.length === 0) return result + + const rows = await db + .select({ + id: questions.id, + content: questions.content, + type: questions.type, + }) + .from(questions) + .where(inArray(questions.id, uniqueIds)) + + for (const r of rows) { + result.set(r.id, { content: r.content, type: r.type }) + } + return result +} +export const getQuestionsContentForErrorCollection = cacheFn( + getQuestionsContentForErrorCollectionRaw, + { + tags: ["questions"], + ttl: 300, + keyParts: ["questions", "getQuestionsContentForErrorCollection"], + }, +) + +/** + * 按 ID 批量获取题目类型映射。 + * + * exams 等模块在组卷/成绩录入时需要题目 type 字段,此接口封装对 questions 表的查询, + * 避免跨模块直接 JOIN questions 表(违反三层架构)。 + * + * 返回 Map,未找到的 ID 不会出现在 Map 中。 + */ +export const getQuestionTypeMapByIdsRaw = async ( + questionIds: string[], +): Promise> => { + const result = new Map() + const uniqueIds = Array.from(new Set(questionIds.filter((v): v is string => typeof v === "string" && v.length > 0))) + if (uniqueIds.length === 0) return result + + const rows = await db + .select({ id: questions.id, type: questions.type }) + .from(questions) + .where(inArray(questions.id, uniqueIds)) + + for (const r of rows) { + result.set(r.id, r.type) + } + return result +} +export const getQuestionTypeMapByIds = cacheFn(getQuestionTypeMapByIdsRaw, { + tags: ["questions"], + ttl: 300, + keyParts: ["questions", "getQuestionTypeMapByIds"], +}) + +// --------------------------------------------------------------------------- +// Cross-module batch interfaces — question count per knowledge point +// --------------------------------------------------------------------------- + +/** + * 跨模块接口:按知识点 ID 批量获取关联题目数。 + * + * 供 textbooks 模块知识图谱使用,避免直接查询 questionsToKnowledgePoints 表。 + * + * @param knowledgePointIds 知识点 ID 列表 + * @returns Map + */ +export const getQuestionCountByKpIdsRaw = async ( + knowledgePointIds: string[] +): Promise> => { + const result = new Map() + const uniqueIds = Array.from(new Set(knowledgePointIds.filter((v): v is string => typeof v === "string" && v.length > 0))) + if (uniqueIds.length === 0) return result + + const rows = await db + .select({ + knowledgePointId: questionsToKnowledgePoints.knowledgePointId, + count: count(), + }) + .from(questionsToKnowledgePoints) + .where(inArray(questionsToKnowledgePoints.knowledgePointId, uniqueIds)) + .groupBy(questionsToKnowledgePoints.knowledgePointId) + + for (const r of rows) { + result.set(r.knowledgePointId, Number(r.count)) + } + return result +} +export const getQuestionCountByKpIds = cacheFn(getQuestionCountByKpIdsRaw, { + tags: ["questions"], + ttl: 300, + keyParts: ["questions", "getQuestionCountByKpIds"], +}) diff --git a/src/modules/questions/data-access-import-export.ts b/src/modules/questions/data-access-import-export.ts new file mode 100644 index 0000000..a8b72f8 --- /dev/null +++ b/src/modules/questions/data-access-import-export.ts @@ -0,0 +1,121 @@ +import 'server-only'; + +import { db } from "@/shared/db"; +import { questions } from "@/shared/db/schema"; +import { and, desc, eq, inArray, sql, type SQL } from "drizzle-orm"; +import { cacheFn } from "@/shared/lib/cache"; +import type { QuestionType } from "./types"; +import { insertQuestionWithRelations } from "./data-access-questions"; + +// --------------------------------------------------------------------------- +// 导入/导出接口 +// --------------------------------------------------------------------------- + +/** 导出题目格式(JSON) */ +export interface QuestionExportItem { + id: string + type: QuestionType + difficulty: number + content: unknown + knowledgePoints: { id: string; name: string }[] + createdAt: Date + updatedAt: Date +} + +/** + * 导出题目为结构化 JSON 格式。 + * + * 支持按 IDs 导出指定题目,或导出全部题目(受 pageSize 限制)。 + * 遵循权限范围:非 all 范围只能导出自己创建的题目。 + */ +export async function exportQuestionsRaw( + questionIds?: string[], + canExportAll = true, + authorId?: string +): Promise { + const conditions: SQL[] = [] + + if (questionIds && questionIds.length > 0) { + conditions.push(inArray(questions.id, questionIds)) + } + + if (!canExportAll && authorId) { + conditions.push(eq(questions.authorId, authorId)) + } + + conditions.push(sql`${questions.parentId} IS NULL`) + + const whereClause = conditions.length > 0 ? and(...conditions) : undefined + + const rows = await db.query.questions.findMany({ + where: whereClause, + // P3-8: LIMIT 从 1000 调低至 100,避免单次导出拉取过多记录 + limit: 100, + orderBy: [desc(questions.createdAt)], + with: { + knowledgePoints: { + with: { + knowledgePoint: true, + }, + }, + }, + }) + + return rows.map((row) => ({ + id: row.id, + type: row.type, + difficulty: row.difficulty ?? 1, + content: row.content, + knowledgePoints: (row.knowledgePoints ?? []).map((rel) => ({ + id: rel.knowledgePoint.id, + name: rel.knowledgePoint.name, + })), + createdAt: row.createdAt, + updatedAt: row.updatedAt, + })) +} +export const exportQuestions = cacheFn(exportQuestionsRaw, { + tags: ["questions"], + ttl: 60, + keyParts: ["questions", "exportQuestions"], +}) + +/** 导入题目的单条输入 */ +export interface QuestionImportItem { + type: QuestionType + difficulty: number + content: unknown + knowledgePointIds?: string[] +} + +/** + * 批量导入题目。 + * + * 单事务保证原子性,任一题目导入失败则全部回滚。 + * 返回创建的题目 ID 列表。 + */ +export async function importQuestions( + items: QuestionImportItem[], + authorId: string +): Promise { + if (items.length === 0) return [] + + return await db.transaction(async (tx) => { + const createdIds: string[] = [] + for (const item of items) { + const id = await insertQuestionWithRelations( + tx, + { + content: item.content, + type: item.type, + difficulty: item.difficulty, + knowledgePointIds: item.knowledgePointIds, + }, + authorId, + null + ) + createdIds.push(id) + } + return createdIds + }) +} diff --git a/src/modules/questions/data-access-options.ts b/src/modules/questions/data-access-options.ts new file mode 100644 index 0000000..5639591 --- /dev/null +++ b/src/modules/questions/data-access-options.ts @@ -0,0 +1,88 @@ +import 'server-only'; + +import { cacheFn } from "@/shared/lib/cache"; +import { + getChaptersByTextbookId as getChaptersByTextbookIdFromTextbooks, + getKnowledgePointOptions as getKnowledgePointOptionsFromTextbooks, + getKnowledgePointsByChapterId as getKnowledgePointsByChapterIdFromTextbooks, + getTextbooks as getTextbooksFromTextbooks, +} from "@/modules/textbooks/data-access"; +import type { ChapterOption, KnowledgePointOption, TextbookOption } from "./types"; + +export async function getKnowledgePointOptionsRaw(): Promise { + // Delegate to textbooks module data-access to avoid direct queries on + // textbooks/chapters/knowledgePoints tables (owned by textbooks module). + return await getKnowledgePointOptionsFromTextbooks() +} +export const getKnowledgePointOptions = cacheFn(getKnowledgePointOptionsRaw, { + tags: ["questions"], + ttl: 600, + keyParts: ["questions", "getKnowledgePointOptions"], +}) + +/** + * 获取教材选项列表(级联筛选第一级)。 + * + * 委托 textbooks 模块 data-access 获取教材列表,避免直接查询 textbooks 表。 + */ +export async function getTextbookOptionsRaw(): Promise { + const textbooks = await getTextbooksFromTextbooks() + return textbooks.map((tb) => ({ + id: tb.id, + title: tb.title, + subject: tb.subject, + grade: tb.grade, + })) +} +export const getTextbookOptions = cacheFn(getTextbookOptionsRaw, { + tags: ["questions"], + ttl: 600, + keyParts: ["questions", "getTextbookOptions"], +}) + +/** + * 获取指定教材下的章节选项列表(级联筛选第二级)。 + * + * 委托 textbooks 模块 data-access 获取章节树,展平为选项列表。 + */ +export async function getChapterOptionsRaw(textbookId: string): Promise { + const chapters = await getChaptersByTextbookIdFromTextbooks(textbookId) + const options: ChapterOption[] = [] + + function flatten(chs: typeof chapters, depth = 0): void { + for (const ch of chs) { + options.push({ + id: ch.id, + title: ch.title, + parentId: ch.parentId ?? null, + depth, + }) + if (ch.children && ch.children.length > 0) { + flatten(ch.children, depth + 1) + } + } + } + + flatten(chapters) + return options +} +export const getChapterOptions = cacheFn(getChapterOptionsRaw, { + tags: ["questions"], + ttl: 600, + keyParts: ["questions", "getChapterOptions"], +}) + +/** + * 获取指定章节下的知识点选项列表(级联筛选第三级)。 + * + * 委托 textbooks 模块 data-access 获取知识点列表。 + */ +export async function getKnowledgePointOptionsByChapterRaw(chapterId: string): Promise<{ id: string; name: string }[]> { + const kps = await getKnowledgePointsByChapterIdFromTextbooks(chapterId) + return kps.map((kp) => ({ id: kp.id, name: kp.name })) +} +export const getKnowledgePointOptionsByChapter = cacheFn(getKnowledgePointOptionsByChapterRaw, { + tags: ["questions"], + ttl: 600, + keyParts: ["questions", "getKnowledgePointOptionsByChapter"], +}) diff --git a/src/modules/questions/data-access-questions.ts b/src/modules/questions/data-access-questions.ts new file mode 100644 index 0000000..1deedce --- /dev/null +++ b/src/modules/questions/data-access-questions.ts @@ -0,0 +1,469 @@ +import 'server-only'; + +import { db } from "@/shared/db"; +import { questions, questionsToKnowledgePoints } from "@/shared/db/schema"; +import { and, count, desc, eq, inArray, sql, type SQL } from "drizzle-orm"; +import { cacheFn } from "@/shared/lib/cache"; +import { createId } from "@paralleldrive/cuid2"; +import { + getKnowledgePointsByChapterId as getKnowledgePointsByChapterIdFromTextbooks, + getKnowledgePointsByTextbookId as getKnowledgePointsByTextbookIdFromTextbooks, +} from "@/modules/textbooks/data-access"; +import type { CreateQuestionInput } from "./schema"; +import type { Question, QuestionType } from "./types"; + +type Tx = Parameters[0]>[0] + +/** + * F-02: 将原始查询字符串转换为 MySQL FULLTEXT BOOLEAN MODE 查询格式。 + * 按空白拆分,转义 BOOLEAN MODE 特殊字符,每个词加 + 前缀和 * 后缀(前缀匹配)。 + * 例如 "math question" → "+math* +question*" + */ +function toBooleanModeQuery(q: string): string { + const words = q.trim().split(/\s+/).filter(Boolean) + if (words.length === 0) return "" + const escapeWord = (w: string): string => `+${w.replace(/[+\-<>()~*"']/g, " ").trim()}*` + return words.map(escapeWord).filter((w) => w !== "+*").join(" ") +} + +export type UpdateQuestionInput = { + type: QuestionType + difficulty: number + content: unknown + knowledgePointIds?: string[] +} + +export type GetQuestionsParams = { + q?: string; + page?: number; + pageSize?: number; + ids?: string[]; + knowledgePointId?: string; + textbookId?: string; + chapterId?: string; + type?: QuestionType; + difficulty?: number; +}; + +/** + * 分页查询题目列表,支持按 ID 集合、关键词全文检索、题目类型/难度、 + * 知识点/章节/教材级联筛选。 + * + * 级联筛选通过 textbooks 模块 data-access 解析知识点 ID 集合,避免直接查询 + * textbooks/chapters/knowledgePoints 表。子题(parentId 非 null)默认不返回, + * 除非通过 `ids` 显式指定。 + * + * @returns 包含 data 与 meta(page/pageSize/total/totalPages)的对象 + */ +export const getQuestionsRaw = async ({ + q, + page = 1, + pageSize = 50, + ids, + knowledgePointId, + textbookId, + chapterId, + type, + difficulty, +}: GetQuestionsParams = {}) => { + const safePage = typeof page === "number" && page >= 1 ? page : 1 + const safePageSize = typeof pageSize === "number" && pageSize > 0 ? pageSize : 50 + const offset = (safePage - 1) * safePageSize; + + const conditions: SQL[] = []; + + if (ids && ids.length > 0) { + conditions.push(inArray(questions.id, ids)); + } + + if (q && q.trim().length > 0) { + // F-02: 使用 FULLTEXT 索引(content_text 生成列)+ MATCH AGAINST 替代 LIKE '%xxx%' 全表扫描 + const booleanQuery = toBooleanModeQuery(q.trim()) + if (booleanQuery) { + conditions.push( + sql`MATCH(${questions.contentText}) AGAINST(${booleanQuery} IN BOOLEAN MODE)` + ) + } + } + + if (type) { + conditions.push(eq(questions.type, type)); + } + + if (difficulty) { + conditions.push(eq(questions.difficulty, difficulty)); + } + + // 级联筛选:通过 textbooks data-access 获取知识点 ID 集合, + // 再按知识点过滤题目。避免直接查询 textbooks/chapters 表。 + if (knowledgePointId) { + conditions.push( + inArray( + questions.id, + db + .select({ questionId: questionsToKnowledgePoints.questionId }) + .from(questionsToKnowledgePoints) + .where(eq(questionsToKnowledgePoints.knowledgePointId, knowledgePointId)) + ) + ); + } else if (chapterId) { + // 按章节筛选:获取该章节下所有知识点 ID + const kps = await getKnowledgePointsByChapterIdFromTextbooks(chapterId) + const kpIds = kps.map((kp) => kp.id) + if (kpIds.length > 0) { + conditions.push( + inArray( + questions.id, + db + .select({ questionId: questionsToKnowledgePoints.questionId }) + .from(questionsToKnowledgePoints) + .where(inArray(questionsToKnowledgePoints.knowledgePointId, kpIds)) + ) + ); + } else { + // 章节下无知识点,返回空结果 + conditions.push(sql`1 = 0`) + } + } else if (textbookId) { + // 按教材筛选:获取该教材下所有知识点 ID + const kps = await getKnowledgePointsByTextbookIdFromTextbooks(textbookId) + const kpIds = kps.map((kp) => kp.id) + if (kpIds.length > 0) { + conditions.push( + inArray( + questions.id, + db + .select({ questionId: questionsToKnowledgePoints.questionId }) + .from(questionsToKnowledgePoints) + .where(inArray(questionsToKnowledgePoints.knowledgePointId, kpIds)) + ) + ); + } else { + conditions.push(sql`1 = 0`) + } + } + + if (!ids || ids.length === 0) { + conditions.push(sql`${questions.parentId} IS NULL`) + } + + const whereClause = conditions.length > 0 ? and(...conditions) : undefined; + + const [totalResult] = await db + .select({ count: count() }) + .from(questions) + .where(whereClause); + + const total = Number(totalResult?.count ?? 0); + + const rows = await db.query.questions.findMany({ + where: whereClause, + limit: safePageSize, + offset: offset, + orderBy: [desc(questions.createdAt)], + with: { + knowledgePoints: { + with: { + knowledgePoint: true, + }, + }, + author: { + columns: { + id: true, + name: true, + image: true, + }, + }, + children: true, + }, + }); + + return { + data: rows.map((row) => { + const knowledgePoints = + row.knowledgePoints?.map((rel) => rel.knowledgePoint) ?? []; + + const author = row.author + ? { + id: row.author.id, + name: row.author.name, + image: row.author.image, + } + : null; + + const mapped: Question = { + id: row.id, + content: row.content, + type: row.type, + difficulty: row.difficulty ?? 1, + createdAt: row.createdAt, + updatedAt: row.updatedAt, + author, + knowledgePoints: knowledgePoints.map((kp) => ({ id: kp.id, name: kp.name })), + childrenCount: row.children?.length ?? 0, + }; + + return mapped; + }), + meta: { + page: safePage, + pageSize: safePageSize, + total, + totalPages: Math.ceil(total / safePageSize), + }, + }; +}; +export const getQuestions = cacheFn(getQuestionsRaw, { + tags: ["questions"], + ttl: 300, + keyParts: ["questions", "getQuestions"], +}) + +export type QuestionsDashboardStats = { + questionCount: number +} + +/** + * 获取题目仪表盘统计(题目总数)。 + * + * @returns 包含 questionCount 的统计对象 + */ +export const getQuestionsDashboardStatsRaw = async (): Promise => { + const [row] = await db.select({ value: count() }).from(questions) + return { questionCount: Number(row?.value ?? 0) } +} +export const getQuestionsDashboardStats = cacheFn(getQuestionsDashboardStatsRaw, { + tags: ["questions"], + ttl: 300, + keyParts: ["questions", "getQuestionsDashboardStats"], +}) + +export async function insertQuestionWithRelations( + tx: Tx, + input: CreateQuestionInput, + authorId: string, + parentId: string | null = null +): Promise { + const newQuestionId = createId(); + + await tx.insert(questions).values({ + id: newQuestionId, + content: input.content, + type: input.type, + difficulty: input.difficulty, + authorId: authorId, + parentId: parentId, + }); + + if (input.knowledgePointIds && input.knowledgePointIds.length > 0) { + await tx.insert(questionsToKnowledgePoints).values( + input.knowledgePointIds.map((kpId) => ({ + questionId: newQuestionId, + knowledgePointId: kpId, + })) + ); + } + + if (input.subQuestions && input.subQuestions.length > 0) { + for (const subQ of input.subQuestions) { + await insertQuestionWithRelations(tx, subQ, authorId, newQuestionId); + } + } + + return newQuestionId; +} + +/** + * 创建题目(含子题与知识点关联),在单事务内递归插入。 + * + * 递归遍历 `input.subQuestions` 创建子题,parentId 指向父题 ID。 + * 任一子题插入失败则整个事务回滚。 + * + * @param input - 题目输入(content/type/difficulty/knowledgePointIds/subQuestions) + * @param authorId - 创建者用户 ID + * @returns 新创建的根题目 ID + */ +export async function createQuestionWithRelations( + input: CreateQuestionInput, + authorId: string +): Promise { + return await db.transaction(async (tx) => { + return await insertQuestionWithRelations(tx, input, authorId, null); + }); +} + +/** + * 按 ID 更新题目(content/type/difficulty)并替换知识点关联。 + * + * 在单事务内:1) 更新题目字段;2) 删除原 questionsToKnowledgePoints 关联; + * 3) 插入新关联。权限范围由 `canEditAll` 控制——非 all 范围只能更新自己创建的题目。 + * + * @param id - 待更新的题目 ID + * @param input - 更新输入(type/difficulty/content/knowledgePointIds) + * @param canEditAll - 是否有全部数据范围权限 + * @param authorId - 当前用户 ID(用于权限过滤) + */ +export async function updateQuestionById( + id: string, + input: UpdateQuestionInput, + canEditAll: boolean, + authorId: string +): Promise { + await db.transaction(async (tx) => { + await tx + .update(questions) + .set({ + type: input.type, + difficulty: input.difficulty, + content: input.content, + updatedAt: new Date(), + }) + .where( + canEditAll + ? eq(questions.id, id) + : and(eq(questions.id, id), eq(questions.authorId, authorId)) + ); + + await tx + .delete(questionsToKnowledgePoints) + .where(eq(questionsToKnowledgePoints.questionId, id)); + + if (input.knowledgePointIds && input.knowledgePointIds.length > 0) { + await tx.insert(questionsToKnowledgePoints).values( + input.knowledgePointIds.map((kpId) => ({ + questionId: id, + knowledgePointId: kpId, + })) + ); + } + }); +} + +/** + * 批量收集给定根题目 ID 的所有后代(含自身)ID。 + * + * 使用 BFS 逐层 `inArray` 查询替代原先每节点逐条 SELECT 的 N+1 模式: + * 每层只发 1 条 SQL,整体复杂度从 O(N) 降至 O(depth)。 + * `visited` 跨调用共享以避免循环引用造成的无限递归。 + */ +async function collectDescendantQuestionIds( + tx: Tx, + rootIds: string[], + visited: Set = new Set(), +): Promise { + const queue: string[] = [] + for (const id of rootIds) { + if (!visited.has(id)) { + visited.add(id) + queue.push(id) + } + } + + while (queue.length > 0) { + const currentLevel = queue.splice(0, queue.length) + const children = await tx + .select({ id: questions.id }) + .from(questions) + .where(inArray(questions.parentId, currentLevel)); + + for (const child of children) { + if (!visited.has(child.id)) { + visited.add(child.id) + queue.push(child.id) + } + } + } + + return Array.from(visited) +} + +async function deleteQuestionRecursive( + tx: Tx, + questionId: string, + visited: Set = new Set(), +): Promise { + // 收集所有后代 ID(含自身),单条 inArray 批量删除, + // 替代原先每层每子节点逐条 SELECT + DELETE 的 N+1 模式。 + // 环检测由 collectDescendantQuestionIds 内部的 visited Set 保证。 + const allIds = await collectDescendantQuestionIds(tx, [questionId], visited) + if (allIds.length === 0) return + await tx.delete(questions).where(inArray(questions.id, allIds)) +} + +/** + * 按 ID 递归删除题目(含所有子题)。 + * + * 在事务内:1) 查询题目并校验存在性;2) 校验权限(非 all 范围只能删除自己创建的题目); + * 3) 通过 BFS 收集所有后代 ID 后单条 `inArray` 批量删除。 + * + * @param questionId - 待删除的根题目 ID + * @param canDeleteAll - 是否有全部数据范围权限 + * @param authorId - 当前用户 ID(用于权限过滤) + * @throws 当题目不存在或无权删除时抛出 Error + */ +export async function deleteQuestionByIdRecursive( + questionId: string, + canDeleteAll: boolean, + authorId: string +): Promise { + await db.transaction(async (tx) => { + const q = await tx.query.questions.findFirst({ + where: eq(questions.id, questionId), + }); + + if (!q) { + throw new Error("Question not found"); + } + + if (!canDeleteAll && q.authorId !== authorId) { + throw new Error("Unauthorized"); + } + + await deleteQuestionRecursive(tx, questionId); + }); +} + +/** + * 批量删除题目(级联删除子题)。 + * + * 遵循权限范围:非 all 范围只能删除自己创建的题目。 + * 单事务保证原子性,任一题目删除失败则全部回滚。 + * + * @param questionIds - 待删除的题目 ID 列表 + * @param canDeleteAll - 是否有全部数据范围权限 + * @param authorId - 当前用户 ID(用于权限过滤) + * @returns 实际删除的题目数量 + */ +export async function deleteQuestionsBatch( + questionIds: string[], + canDeleteAll: boolean, + authorId: string +): Promise { + if (questionIds.length === 0) return 0 + + const uniqueIds = Array.from(new Set(questionIds)) + return await db.transaction(async (tx) => { + // 权限过滤:非 all 范围只能删除自己创建的题目 + const whereClause = canDeleteAll + ? inArray(questions.id, uniqueIds) + : and(inArray(questions.id, uniqueIds), eq(questions.authorId, authorId)) + + const targetRows = await tx + .select({ id: questions.id }) + .from(questions) + .where(whereClause) + + const targetIds = targetRows.map((r) => r.id) + if (targetIds.length === 0) return 0 + + // 一次性收集所有 targetIds 的后代(含自身),单条 inArray 删除, + // 替代原先循环调用 deleteQuestionRecursive 产生的 N×depth 查询。 + const visited = new Set() + const allIds = await collectDescendantQuestionIds(tx, targetIds, visited) + if (allIds.length > 0) { + await tx.delete(questions).where(inArray(questions.id, allIds)) + } + + return targetIds.length + }) +} diff --git a/src/modules/questions/data-access.ts b/src/modules/questions/data-access.ts index 42612f0..9403e3b 100644 --- a/src/modules/questions/data-access.ts +++ b/src/modules/questions/data-access.ts @@ -1,827 +1,48 @@ -import 'server-only'; +// Barrel export for questions data-access. +// 业务方仍通过 `@/modules/questions/data-access` 导入,子文件按职责拆分。 -import { db } from "@/shared/db"; -import { questions, questionsToKnowledgePoints } from "@/shared/db/schema"; -import { and, count, desc, eq, inArray, sql, type SQL } from "drizzle-orm"; -import { cacheFn } from "@/shared/lib/cache"; -import { createId } from "@paralleldrive/cuid2"; -import { - getChaptersByTextbookId as getChaptersByTextbookIdFromTextbooks, - getKnowledgePointOptions as getKnowledgePointOptionsFromTextbooks, - getKnowledgePointsByChapterId as getKnowledgePointsByChapterIdFromTextbooks, - getKnowledgePointsByTextbookId as getKnowledgePointsByTextbookIdFromTextbooks, - getTextbooks as getTextbooksFromTextbooks, - getKnowledgePointNamesByIds, -} from "@/modules/textbooks/data-access"; -import type { CreateQuestionInput } from "./schema"; -import type { ChapterOption, KnowledgePointOption, Question, QuestionType, TextbookOption } from "./types"; +export { + getQuestionsRaw, + getQuestions, + getQuestionsDashboardStatsRaw, + getQuestionsDashboardStats, + createQuestionWithRelations, + updateQuestionById, + deleteQuestionByIdRecursive, + deleteQuestionsBatch, + type UpdateQuestionInput, + type GetQuestionsParams, + type QuestionsDashboardStats, +} from "./data-access-questions"; -type Tx = Parameters[0]>[0] +export { + getKnowledgePointOptionsRaw, + getKnowledgePointOptions, + getTextbookOptionsRaw, + getTextbookOptions, + getChapterOptionsRaw, + getChapterOptions, + getKnowledgePointOptionsByChapterRaw, + getKnowledgePointOptionsByChapter, +} from "./data-access-options"; -/** - * F-02: 将原始查询字符串转换为 MySQL FULLTEXT BOOLEAN MODE 查询格式。 - * 按空白拆分,转义 BOOLEAN MODE 特殊字符,每个词加 + 前缀和 * 后缀(前缀匹配)。 - * 例如 "math question" → "+math* +question*" - */ -function toBooleanModeQuery(q: string): string { - const words = q.trim().split(/\s+/).filter(Boolean) - if (words.length === 0) return "" - const escapeWord = (w: string): string => `+${w.replace(/[+\-<>()~*"']/g, " ").trim()}*` - return words.map(escapeWord).filter((w) => w !== "+*").join(" ") -} - -export type UpdateQuestionInput = { - type: QuestionType - difficulty: number - content: unknown - knowledgePointIds?: string[] -} - -export type GetQuestionsParams = { - q?: string; - page?: number; - pageSize?: number; - ids?: string[]; - knowledgePointId?: string; - textbookId?: string; - chapterId?: string; - type?: QuestionType; - difficulty?: number; -}; - -/** - * 分页查询题目列表,支持按 ID 集合、关键词全文检索、题目类型/难度、 - * 知识点/章节/教材级联筛选。 - * - * 级联筛选通过 textbooks 模块 data-access 解析知识点 ID 集合,避免直接查询 - * textbooks/chapters/knowledgePoints 表。子题(parentId 非 null)默认不返回, - * 除非通过 `ids` 显式指定。 - * - * @returns 包含 data 与 meta(page/pageSize/total/totalPages)的对象 - */ -export const getQuestionsRaw = async ({ - q, - page = 1, - pageSize = 50, - ids, - knowledgePointId, - textbookId, - chapterId, - type, - difficulty, -}: GetQuestionsParams = {}) => { - const safePage = typeof page === "number" && page >= 1 ? page : 1 - const safePageSize = typeof pageSize === "number" && pageSize > 0 ? pageSize : 50 - const offset = (safePage - 1) * safePageSize; - - const conditions: SQL[] = []; - - if (ids && ids.length > 0) { - conditions.push(inArray(questions.id, ids)); - } - - if (q && q.trim().length > 0) { - // F-02: 使用 FULLTEXT 索引(content_text 生成列)+ MATCH AGAINST 替代 LIKE '%xxx%' 全表扫描 - const booleanQuery = toBooleanModeQuery(q.trim()) - if (booleanQuery) { - conditions.push( - sql`MATCH(${questions.contentText}) AGAINST(${booleanQuery} IN BOOLEAN MODE)` - ) - } - } - - if (type) { - conditions.push(eq(questions.type, type)); - } - - if (difficulty) { - conditions.push(eq(questions.difficulty, difficulty)); - } - - // 级联筛选:通过 textbooks data-access 获取知识点 ID 集合, - // 再按知识点过滤题目。避免直接查询 textbooks/chapters 表。 - if (knowledgePointId) { - conditions.push( - inArray( - questions.id, - db - .select({ questionId: questionsToKnowledgePoints.questionId }) - .from(questionsToKnowledgePoints) - .where(eq(questionsToKnowledgePoints.knowledgePointId, knowledgePointId)) - ) - ); - } else if (chapterId) { - // 按章节筛选:获取该章节下所有知识点 ID - const kps = await getKnowledgePointsByChapterIdFromTextbooks(chapterId) - const kpIds = kps.map((kp) => kp.id) - if (kpIds.length > 0) { - conditions.push( - inArray( - questions.id, - db - .select({ questionId: questionsToKnowledgePoints.questionId }) - .from(questionsToKnowledgePoints) - .where(inArray(questionsToKnowledgePoints.knowledgePointId, kpIds)) - ) - ); - } else { - // 章节下无知识点,返回空结果 - conditions.push(sql`1 = 0`) - } - } else if (textbookId) { - // 按教材筛选:获取该教材下所有知识点 ID - const kps = await getKnowledgePointsByTextbookIdFromTextbooks(textbookId) - const kpIds = kps.map((kp) => kp.id) - if (kpIds.length > 0) { - conditions.push( - inArray( - questions.id, - db - .select({ questionId: questionsToKnowledgePoints.questionId }) - .from(questionsToKnowledgePoints) - .where(inArray(questionsToKnowledgePoints.knowledgePointId, kpIds)) - ) - ); - } else { - conditions.push(sql`1 = 0`) - } - } - - if (!ids || ids.length === 0) { - conditions.push(sql`${questions.parentId} IS NULL`) - } - - const whereClause = conditions.length > 0 ? and(...conditions) : undefined; - - const [totalResult] = await db - .select({ count: count() }) - .from(questions) - .where(whereClause); - - const total = Number(totalResult?.count ?? 0); - - const rows = await db.query.questions.findMany({ - where: whereClause, - limit: safePageSize, - offset: offset, - orderBy: [desc(questions.createdAt)], - with: { - knowledgePoints: { - with: { - knowledgePoint: true, - }, - }, - author: { - columns: { - id: true, - name: true, - image: true, - }, - }, - children: true, - }, - }); - - return { - data: rows.map((row) => { - const knowledgePoints = - row.knowledgePoints?.map((rel) => rel.knowledgePoint) ?? []; - - const author = row.author - ? { - id: row.author.id, - name: row.author.name, - image: row.author.image, - } - : null; - - const mapped: Question = { - id: row.id, - content: row.content, - type: row.type, - difficulty: row.difficulty ?? 1, - createdAt: row.createdAt, - updatedAt: row.updatedAt, - author, - knowledgePoints: knowledgePoints.map((kp) => ({ id: kp.id, name: kp.name })), - childrenCount: row.children?.length ?? 0, - }; - - return mapped; - }), - meta: { - page: safePage, - pageSize: safePageSize, - total, - totalPages: Math.ceil(total / safePageSize), - }, - }; -}; -export const getQuestions = cacheFn(getQuestionsRaw, { - tags: ["questions"], - ttl: 300, - keyParts: ["questions", "getQuestions"], -}) - -export type QuestionsDashboardStats = { - questionCount: number -} - -/** - * 获取题目仪表盘统计(题目总数)。 - * - * @returns 包含 questionCount 的统计对象 - */ -export const getQuestionsDashboardStatsRaw = async (): Promise => { - const [row] = await db.select({ value: count() }).from(questions) - return { questionCount: Number(row?.value ?? 0) } -} -export const getQuestionsDashboardStats = cacheFn(getQuestionsDashboardStatsRaw, { - tags: ["questions"], - ttl: 300, - keyParts: ["questions", "getQuestionsDashboardStats"], -}) - -async function insertQuestionWithRelations( - tx: Tx, - input: CreateQuestionInput, - authorId: string, - parentId: string | null = null -): Promise { - const newQuestionId = createId(); - - await tx.insert(questions).values({ - id: newQuestionId, - content: input.content, - type: input.type, - difficulty: input.difficulty, - authorId: authorId, - parentId: parentId, - }); - - if (input.knowledgePointIds && input.knowledgePointIds.length > 0) { - await tx.insert(questionsToKnowledgePoints).values( - input.knowledgePointIds.map((kpId) => ({ - questionId: newQuestionId, - knowledgePointId: kpId, - })) - ); - } - - if (input.subQuestions && input.subQuestions.length > 0) { - for (const subQ of input.subQuestions) { - await insertQuestionWithRelations(tx, subQ, authorId, newQuestionId); - } - } - - return newQuestionId; -} - -/** - * 创建题目(含子题与知识点关联),在单事务内递归插入。 - * - * 递归遍历 `input.subQuestions` 创建子题,parentId 指向父题 ID。 - * 任一子题插入失败则整个事务回滚。 - * - * @param input - 题目输入(content/type/difficulty/knowledgePointIds/subQuestions) - * @param authorId - 创建者用户 ID - * @returns 新创建的根题目 ID - */ -export async function createQuestionWithRelations( - input: CreateQuestionInput, - authorId: string -): Promise { - return await db.transaction(async (tx) => { - return await insertQuestionWithRelations(tx, input, authorId, null); - }); -} - -/** - * 按 ID 更新题目(content/type/difficulty)并替换知识点关联。 - * - * 在单事务内:1) 更新题目字段;2) 删除原 questionsToKnowledgePoints 关联; - * 3) 插入新关联。权限范围由 `canEditAll` 控制——非 all 范围只能更新自己创建的题目。 - * - * @param id - 待更新的题目 ID - * @param input - 更新输入(type/difficulty/content/knowledgePointIds) - * @param canEditAll - 是否有全部数据范围权限 - * @param authorId - 当前用户 ID(用于权限过滤) - */ -export async function updateQuestionById( - id: string, - input: UpdateQuestionInput, - canEditAll: boolean, - authorId: string -): Promise { - await db.transaction(async (tx) => { - await tx - .update(questions) - .set({ - type: input.type, - difficulty: input.difficulty, - content: input.content, - updatedAt: new Date(), - }) - .where( - canEditAll - ? eq(questions.id, id) - : and(eq(questions.id, id), eq(questions.authorId, authorId)) - ); - - await tx - .delete(questionsToKnowledgePoints) - .where(eq(questionsToKnowledgePoints.questionId, id)); - - if (input.knowledgePointIds && input.knowledgePointIds.length > 0) { - await tx.insert(questionsToKnowledgePoints).values( - input.knowledgePointIds.map((kpId) => ({ - questionId: id, - knowledgePointId: kpId, - })) - ); - } - }); -} - -/** - * 批量收集给定根题目 ID 的所有后代(含自身)ID。 - * - * 使用 BFS 逐层 `inArray` 查询替代原先每节点逐条 SELECT 的 N+1 模式: - * 每层只发 1 条 SQL,整体复杂度从 O(N) 降至 O(depth)。 - * `visited` 跨调用共享以避免循环引用造成的无限递归。 - */ -async function collectDescendantQuestionIds( - tx: Tx, - rootIds: string[], - visited: Set = new Set(), -): Promise { - const queue: string[] = [] - for (const id of rootIds) { - if (!visited.has(id)) { - visited.add(id) - queue.push(id) - } - } - - while (queue.length > 0) { - const currentLevel = queue.splice(0, queue.length) - const children = await tx - .select({ id: questions.id }) - .from(questions) - .where(inArray(questions.parentId, currentLevel)); - - for (const child of children) { - if (!visited.has(child.id)) { - visited.add(child.id) - queue.push(child.id) - } - } - } - - return Array.from(visited) -} - -async function deleteQuestionRecursive( - tx: Tx, - questionId: string, - visited: Set = new Set(), -): Promise { - // 收集所有后代 ID(含自身),单条 inArray 批量删除, - // 替代原先每层每子节点逐条 SELECT + DELETE 的 N+1 模式。 - // 环检测由 collectDescendantQuestionIds 内部的 visited Set 保证。 - const allIds = await collectDescendantQuestionIds(tx, [questionId], visited) - if (allIds.length === 0) return - await tx.delete(questions).where(inArray(questions.id, allIds)) -} - -/** - * 按 ID 递归删除题目(含所有子题)。 - * - * 在事务内:1) 查询题目并校验存在性;2) 校验权限(非 all 范围只能删除自己创建的题目); - * 3) 通过 BFS 收集所有后代 ID 后单条 `inArray` 批量删除。 - * - * @param questionId - 待删除的根题目 ID - * @param canDeleteAll - 是否有全部数据范围权限 - * @param authorId - 当前用户 ID(用于权限过滤) - * @throws 当题目不存在或无权删除时抛出 Error - */ -export async function deleteQuestionByIdRecursive( - questionId: string, - canDeleteAll: boolean, - authorId: string -): Promise { - await db.transaction(async (tx) => { - const q = await tx.query.questions.findFirst({ - where: eq(questions.id, questionId), - }); - - if (!q) { - throw new Error("Question not found"); - } - - if (!canDeleteAll && q.authorId !== authorId) { - throw new Error("Unauthorized"); - } - - await deleteQuestionRecursive(tx, questionId); - }); -} - -/** - * 批量删除题目(级联删除子题)。 - * - * 遵循权限范围:非 all 范围只能删除自己创建的题目。 - * 单事务保证原子性,任一题目删除失败则全部回滚。 - * - * @param questionIds - 待删除的题目 ID 列表 - * @param canDeleteAll - 是否有全部数据范围权限 - * @param authorId - 当前用户 ID(用于权限过滤) - * @returns 实际删除的题目数量 - */ -export async function deleteQuestionsBatch( - questionIds: string[], - canDeleteAll: boolean, - authorId: string -): Promise { - if (questionIds.length === 0) return 0 - - const uniqueIds = Array.from(new Set(questionIds)) - return await db.transaction(async (tx) => { - // 权限过滤:非 all 范围只能删除自己创建的题目 - const whereClause = canDeleteAll - ? inArray(questions.id, uniqueIds) - : and(inArray(questions.id, uniqueIds), eq(questions.authorId, authorId)) - - const targetRows = await tx - .select({ id: questions.id }) - .from(questions) - .where(whereClause) - - const targetIds = targetRows.map((r) => r.id) - if (targetIds.length === 0) return 0 - - // 一次性收集所有 targetIds 的后代(含自身),单条 inArray 删除, - // 替代原先循环调用 deleteQuestionRecursive 产生的 N×depth 查询。 - const visited = new Set() - const allIds = await collectDescendantQuestionIds(tx, targetIds, visited) - if (allIds.length > 0) { - await tx.delete(questions).where(inArray(questions.id, allIds)) - } - - return targetIds.length - }) -} - -export async function getKnowledgePointOptionsRaw(): Promise { - // Delegate to textbooks module data-access to avoid direct queries on - // textbooks/chapters/knowledgePoints tables (owned by textbooks module). - return await getKnowledgePointOptionsFromTextbooks() -} -export const getKnowledgePointOptions = cacheFn(getKnowledgePointOptionsRaw, { - tags: ["questions"], - ttl: 600, - keyParts: ["questions", "getKnowledgePointOptions"], -}) - -/** - * 获取教材选项列表(级联筛选第一级)。 - * - * 委托 textbooks 模块 data-access 获取教材列表,避免直接查询 textbooks 表。 - */ -export async function getTextbookOptionsRaw(): Promise { - const textbooks = await getTextbooksFromTextbooks() - return textbooks.map((tb) => ({ - id: tb.id, - title: tb.title, - subject: tb.subject, - grade: tb.grade, - })) -} -export const getTextbookOptions = cacheFn(getTextbookOptionsRaw, { - tags: ["questions"], - ttl: 600, - keyParts: ["questions", "getTextbookOptions"], -}) - -/** - * 获取指定教材下的章节选项列表(级联筛选第二级)。 - * - * 委托 textbooks 模块 data-access 获取章节树,展平为选项列表。 - */ -export async function getChapterOptionsRaw(textbookId: string): Promise { - const chapters = await getChaptersByTextbookIdFromTextbooks(textbookId) - const options: ChapterOption[] = [] - - function flatten(chs: typeof chapters, depth = 0): void { - for (const ch of chs) { - options.push({ - id: ch.id, - title: ch.title, - parentId: ch.parentId ?? null, - depth, - }) - if (ch.children && ch.children.length > 0) { - flatten(ch.children, depth + 1) - } - } - } - - flatten(chapters) - return options -} -export const getChapterOptions = cacheFn(getChapterOptionsRaw, { - tags: ["questions"], - ttl: 600, - keyParts: ["questions", "getChapterOptions"], -}) - -/** - * 获取指定章节下的知识点选项列表(级联筛选第三级)。 - * - * 委托 textbooks 模块 data-access 获取知识点列表。 - */ -export async function getKnowledgePointOptionsByChapterRaw(chapterId: string): Promise<{ id: string; name: string }[]> { - const kps = await getKnowledgePointsByChapterIdFromTextbooks(chapterId) - return kps.map((kp) => ({ id: kp.id, name: kp.name })) -} -export const getKnowledgePointOptionsByChapter = cacheFn(getKnowledgePointOptionsByChapterRaw, { - tags: ["questions"], - ttl: 600, - keyParts: ["questions", "getKnowledgePointOptionsByChapter"], -}) - -// --------------------------------------------------------------------------- -// Cross-module query interfaces — read-only access for other modules -// --------------------------------------------------------------------------- - -export type QuestionKnowledgePoint = { - questionId: string - knowledgePointId: string - knowledgePointName: string -} - -/** Returns knowledge points associated with the given question ids. */ -export const getKnowledgePointsForQuestionsRaw = async ( - questionIds: string[] -): Promise> => { - const result = new Map() - const uniqueIds = Array.from(new Set(questionIds.filter((v): v is string => typeof v === "string" && v.length > 0))) - if (uniqueIds.length === 0) return result - - // 1. 查询 questionId -> knowledgePointId 关联(questions 模块自有表) - const rows = await db - .select({ - questionId: questionsToKnowledgePoints.questionId, - knowledgePointId: questionsToKnowledgePoints.knowledgePointId, - }) - .from(questionsToKnowledgePoints) - .where(inArray(questionsToKnowledgePoints.questionId, uniqueIds)) - - if (rows.length === 0) return result - - // 2. 跨模块批量解析知识点名称,避免直接 JOIN knowledgePoints 表 - const kpIds = Array.from(new Set(rows.map((r) => r.knowledgePointId))) - const kpNameMap = await getKnowledgePointNamesByIds(kpIds) - - // 3. 组装结果 - for (const r of rows) { - const list = result.get(r.questionId) ?? [] - list.push({ - questionId: r.questionId, - knowledgePointId: r.knowledgePointId, - knowledgePointName: kpNameMap.get(r.knowledgePointId) ?? "", - }) - result.set(r.questionId, list) - } - return result -} -export const getKnowledgePointsForQuestions = cacheFn(getKnowledgePointsForQuestionsRaw, { - tags: ["questions"], - ttl: 300, - keyParts: ["questions", "getKnowledgePointsForQuestions"], -}) - -/** - * 跨模块接口:获取题目内容与类型(供 error-book 模块提取正确答案使用)。 - * - * error-book 模块在采集错题时需要从题目内容中提取正确答案(correctAnswer), - * 此接口返回题目的 content 和 type,由 error-book 模块调用 - * `homework/lib/question-content-utils.ts` 中的纯函数进行提取。 - */ -export type QuestionContentForErrorCollection = { - content: unknown - type: string -} - -export const getQuestionsContentForErrorCollectionRaw = async ( - questionIds: string[], -): Promise> => { - const result = new Map() - const uniqueIds = Array.from(new Set(questionIds.filter((v): v is string => typeof v === "string" && v.length > 0))) - if (uniqueIds.length === 0) return result - - const rows = await db - .select({ - id: questions.id, - content: questions.content, - type: questions.type, - }) - .from(questions) - .where(inArray(questions.id, uniqueIds)) - - for (const r of rows) { - result.set(r.id, { content: r.content, type: r.type }) - } - return result -} -export const getQuestionsContentForErrorCollection = cacheFn( +export { + getKnowledgePointsForQuestionsRaw, + getKnowledgePointsForQuestions, getQuestionsContentForErrorCollectionRaw, - { - tags: ["questions"], - ttl: 300, - keyParts: ["questions", "getQuestionsContentForErrorCollection"], - }, -) + getQuestionsContentForErrorCollection, + getQuestionTypeMapByIdsRaw, + getQuestionTypeMapByIds, + getQuestionCountByKpIdsRaw, + getQuestionCountByKpIds, + type QuestionKnowledgePoint, + type QuestionContentForErrorCollection, +} from "./data-access-cross-module"; -/** - * 按 ID 批量获取题目类型映射。 - * - * exams 等模块在组卷/成绩录入时需要题目 type 字段,此接口封装对 questions 表的查询, - * 避免跨模块直接 JOIN questions 表(违反三层架构)。 - * - * 返回 Map,未找到的 ID 不会出现在 Map 中。 - */ -export const getQuestionTypeMapByIdsRaw = async ( - questionIds: string[], -): Promise> => { - const result = new Map() - const uniqueIds = Array.from(new Set(questionIds.filter((v): v is string => typeof v === "string" && v.length > 0))) - if (uniqueIds.length === 0) return result - - const rows = await db - .select({ id: questions.id, type: questions.type }) - .from(questions) - .where(inArray(questions.id, uniqueIds)) - - for (const r of rows) { - result.set(r.id, r.type) - } - return result -} -export const getQuestionTypeMapByIds = cacheFn(getQuestionTypeMapByIdsRaw, { - tags: ["questions"], - ttl: 300, - keyParts: ["questions", "getQuestionTypeMapByIds"], -}) - -// --------------------------------------------------------------------------- -// 导入/导出接口 -// --------------------------------------------------------------------------- - -/** 导出题目格式(JSON) */ -export interface QuestionExportItem { - id: string - type: QuestionType - difficulty: number - content: unknown - knowledgePoints: { id: string; name: string }[] - createdAt: Date - updatedAt: Date -} - -/** - * 导出题目为结构化 JSON 格式。 - * - * 支持按 IDs 导出指定题目,或导出全部题目(受 pageSize 限制)。 - * 遵循权限范围:非 all 范围只能导出自己创建的题目。 - */ -export async function exportQuestionsRaw( - questionIds?: string[], - canExportAll = true, - authorId?: string -): Promise { - const conditions: SQL[] = [] - - if (questionIds && questionIds.length > 0) { - conditions.push(inArray(questions.id, questionIds)) - } - - if (!canExportAll && authorId) { - conditions.push(eq(questions.authorId, authorId)) - } - - conditions.push(sql`${questions.parentId} IS NULL`) - - const whereClause = conditions.length > 0 ? and(...conditions) : undefined - - const rows = await db.query.questions.findMany({ - where: whereClause, - // P3-8: LIMIT 从 1000 调低至 100,避免单次导出拉取过多记录 - limit: 100, - orderBy: [desc(questions.createdAt)], - with: { - knowledgePoints: { - with: { - knowledgePoint: true, - }, - }, - }, - }) - - return rows.map((row) => ({ - id: row.id, - type: row.type, - difficulty: row.difficulty ?? 1, - content: row.content, - knowledgePoints: (row.knowledgePoints ?? []).map((rel) => ({ - id: rel.knowledgePoint.id, - name: rel.knowledgePoint.name, - })), - createdAt: row.createdAt, - updatedAt: row.updatedAt, - })) -} -export const exportQuestions = cacheFn(exportQuestionsRaw, { - tags: ["questions"], - ttl: 60, - keyParts: ["questions", "exportQuestions"], -}) - -/** 导入题目的单条输入 */ -export interface QuestionImportItem { - type: QuestionType - difficulty: number - content: unknown - knowledgePointIds?: string[] -} - -/** - * 批量导入题目。 - * - * 单事务保证原子性,任一题目导入失败则全部回滚。 - * 返回创建的题目 ID 列表。 - */ -export async function importQuestions( - items: QuestionImportItem[], - authorId: string -): Promise { - if (items.length === 0) return [] - - return await db.transaction(async (tx) => { - const createdIds: string[] = [] - for (const item of items) { - const id = await insertQuestionWithRelations( - tx, - { - content: item.content, - type: item.type, - difficulty: item.difficulty, - knowledgePointIds: item.knowledgePointIds, - }, - authorId, - null - ) - createdIds.push(id) - } - return createdIds - }) -} - -// --------------------------------------------------------------------------- -// Cross-module batch interfaces — question count per knowledge point -// --------------------------------------------------------------------------- - -/** - * 跨模块接口:按知识点 ID 批量获取关联题目数。 - * - * 供 textbooks 模块知识图谱使用,避免直接查询 questionsToKnowledgePoints 表。 - * - * @param knowledgePointIds 知识点 ID 列表 - * @returns Map - */ -export const getQuestionCountByKpIdsRaw = async ( - knowledgePointIds: string[] -): Promise> => { - const result = new Map() - const uniqueIds = Array.from(new Set(knowledgePointIds.filter((v): v is string => typeof v === "string" && v.length > 0))) - if (uniqueIds.length === 0) return result - - const rows = await db - .select({ - knowledgePointId: questionsToKnowledgePoints.knowledgePointId, - count: count(), - }) - .from(questionsToKnowledgePoints) - .where(inArray(questionsToKnowledgePoints.knowledgePointId, uniqueIds)) - .groupBy(questionsToKnowledgePoints.knowledgePointId) - - for (const r of rows) { - result.set(r.knowledgePointId, Number(r.count)) - } - return result -} -export const getQuestionCountByKpIds = cacheFn(getQuestionCountByKpIdsRaw, { - tags: ["questions"], - ttl: 300, - keyParts: ["questions", "getQuestionCountByKpIds"], -}) +export { + exportQuestionsRaw, + exportQuestions, + importQuestions, + type QuestionExportItem, + type QuestionImportItem, +} from "./data-access-import-export"; diff --git a/src/modules/textbooks/data-access-chapters.ts b/src/modules/textbooks/data-access-chapters.ts new file mode 100644 index 0000000..5588334 --- /dev/null +++ b/src/modules/textbooks/data-access-chapters.ts @@ -0,0 +1,244 @@ +import "server-only" + +import { and, asc, eq, inArray, isNull, or } from "drizzle-orm" +import { createId } from "@paralleldrive/cuid2" + +import { cacheFn } from "@/shared/lib/cache" +import { db } from "@/shared/db" +import { chapters, knowledgePoints, knowledgePointPrerequisites } from "@/shared/db/schema" +import { NotFoundError } from "@/shared/lib/action-utils" +import type { Chapter } from "./types" +import type { + CreateChapterInput, + UpdateChapterContentInput, +} from "./schema" +import { buildChapterTree } from "./utils" + +export const getChaptersByTextbookIdRaw = async (textbookId: string): Promise => { + const rows = await db + .select({ + id: chapters.id, + textbookId: chapters.textbookId, + title: chapters.title, + order: chapters.order, + parentId: chapters.parentId, + content: chapters.content, + createdAt: chapters.createdAt, + updatedAt: chapters.updatedAt, + }) + .from(chapters) + .where(eq(chapters.textbookId, textbookId)) + .orderBy(asc(chapters.order), asc(chapters.title)) + + return buildChapterTree( + rows.map((r) => ({ + id: r.id, + textbookId: r.textbookId, + title: r.title, + order: r.order, + parentId: r.parentId, + content: r.content ?? null, + createdAt: r.createdAt, + updatedAt: r.updatedAt, + })) + ) +} +export const getChaptersByTextbookId = cacheFn(getChaptersByTextbookIdRaw, { + tags: ["textbooks"], + ttl: 300, + keyParts: ["textbooks", "getChaptersByTextbookId"], +}) + +/** + * 在指定教材下创建章节(content 初始为空字符串,children 为空数组)。 + * + * @param data - 章节输入(textbookId/title/order?/parentId?) + * @returns 新创建的章节对象 + */ +export async function createChapter(data: CreateChapterInput): Promise { + const id = createId() + const now = new Date() + + const row = { + id, + textbookId: data.textbookId, + title: data.title.trim(), + order: data.order ?? 0, + parentId: data.parentId ?? null, + content: "", + createdAt: now, + updatedAt: now, + } + + await db.insert(chapters).values(row) + + return { + id: row.id, + textbookId: row.textbookId, + title: row.title, + order: row.order, + parentId: row.parentId, + content: row.content, + createdAt: row.createdAt, + updatedAt: row.updatedAt, + children: [], + } +} + +/** + * 更新章节正文 content 字段,并返回更新后的章节对象。 + * + * @param data - 更新输入(chapterId + content) + * @returns 更新后的章节对象(children 为空数组) + * @throws {NotFoundError} 当章节不存在时抛出 + */ +export async function updateChapterContent(data: UpdateChapterContentInput): Promise { + await db.update(chapters).set({ content: data.content }).where(eq(chapters.id, data.chapterId)) + + const [row] = await db + .select({ + id: chapters.id, + textbookId: chapters.textbookId, + title: chapters.title, + order: chapters.order, + parentId: chapters.parentId, + content: chapters.content, + createdAt: chapters.createdAt, + updatedAt: chapters.updatedAt, + }) + .from(chapters) + .where(eq(chapters.id, data.chapterId)) + .limit(1) + + if (!row) throw new NotFoundError("章节") + + return { + id: row.id, + textbookId: row.textbookId, + title: row.title, + order: row.order, + parentId: row.parentId, + content: row.content ?? null, + createdAt: row.createdAt, + updatedAt: row.updatedAt, + children: [], + } +} + +/** + * 删除章节及其所有后代章节,并清理后代知识点及其前置依赖记录。 + * + * 通过本地构建 childrenByParent 索引 + 栈遍历收集所有后代 ID, + * 在事务内:1) 清理 knowledgePointPrerequisites 表中引用被删知识点的孤儿记录; + * 2) 删除知识点;3) 删除章节。 + * + * @param id - 待删除的章节 ID + */ +export async function deleteChapter(id: string): Promise { + const [target] = await db + .select({ id: chapters.id, textbookId: chapters.textbookId }) + .from(chapters) + .where(eq(chapters.id, id)) + .limit(1) + + if (!target) return + + const all = await db + .select({ id: chapters.id, parentId: chapters.parentId }) + .from(chapters) + .where(eq(chapters.textbookId, target.textbookId)) + + const childrenByParent = new Map() + for (const ch of all) { + if (!ch.parentId) continue + const arr = childrenByParent.get(ch.parentId) ?? [] + arr.push(ch.id) + childrenByParent.set(ch.parentId, arr) + } + + const idsToDelete: string[] = [] + const stack: string[] = [id] + const seen = new Set() + while (stack.length) { + const cur = stack.pop() + if (!cur) break + if (seen.has(cur)) continue + seen.add(cur) + idsToDelete.push(cur) + const kids = childrenByParent.get(cur) + if (kids) stack.push(...kids) + } + + // P0 数据完整性修复:先查询被删章节下的所有知识点 ID,用于清理孤儿前置依赖记录 + const kpsInChapters = await db + .select({ id: knowledgePoints.id }) + .from(knowledgePoints) + .where(inArray(knowledgePoints.chapterId, idsToDelete)) + const kpIds = kpsInChapters.map((k) => k.id) + + await db.transaction(async (tx) => { + // P0 修复:清理 knowledgePointPrerequisites 表中引用被删知识点的孤儿记录 + // 包括作为 knowledgePointId 和作为 prerequisiteKpId 的记录 + if (kpIds.length > 0) { + await tx + .delete(knowledgePointPrerequisites) + .where( + or( + inArray(knowledgePointPrerequisites.knowledgePointId, kpIds), + inArray(knowledgePointPrerequisites.prerequisiteKpId, kpIds), + ), + ) + } + await tx.delete(knowledgePoints).where(inArray(knowledgePoints.chapterId, idsToDelete)) + await tx.delete(chapters).where(inArray(chapters.id, idsToDelete)) + }) +} + +/** + * 调整章节在同级中的位置(可同时切换 parentId),单事务内批量更新 order。 + * + * 流程:1) 读取目标章节;2) 查询同 parent 下兄弟;3) 移除目标后插入到 newIndex; + * 4) 在事务内并行 UPDATE 变更行(order 或 parentId)。 + * + * @param chapterId - 待移动的章节 ID + * @param newIndex - 目标位置(0-based) + * @param parentId - 目标父章节 ID(顶层为 null) + * @throws 当章节不存在时抛出 NotFoundError + */ +export async function reorderChapters(chapterId: string, newIndex: number, parentId: string | null): Promise { + const [target] = await db.select().from(chapters).where(eq(chapters.id, chapterId)).limit(1) + if (!target) throw new NotFoundError("章节") + + const siblings = await db + .select() + .from(chapters) + .where( + and( + eq(chapters.textbookId, target.textbookId), + parentId ? eq(chapters.parentId, parentId) : isNull(chapters.parentId) + ) + ) + .orderBy(asc(chapters.order)) + + const currentSiblings = siblings.filter((c) => c.id !== chapterId) + currentSiblings.splice(newIndex, 0, target) + + await db.transaction(async (tx) => { + // 批量并行 UPDATE 替代原先串行 await 循环,单事务保证原子性。 + // 仅对需要变更的行发起 UPDATE(order 变化或 parentId 变化)。 + await Promise.all( + currentSiblings.map((ch, i) => { + if (ch.order !== i || (ch.id === chapterId && ch.parentId !== parentId)) { + return tx + .update(chapters) + .set({ + order: i, + parentId: ch.id === chapterId ? parentId : ch.parentId + }) + .where(eq(chapters.id, ch.id)) + } + return Promise.resolve() + }) + ) + }) +} diff --git a/src/modules/textbooks/data-access-cross-module.ts b/src/modules/textbooks/data-access-cross-module.ts new file mode 100644 index 0000000..6fa3506 --- /dev/null +++ b/src/modules/textbooks/data-access-cross-module.ts @@ -0,0 +1,152 @@ +import "server-only" + +import { asc, eq, inArray } from "drizzle-orm" + +import { cacheFn } from "@/shared/lib/cache" +import { db } from "@/shared/db" +import { chapters, knowledgePoints, textbooks } from "@/shared/db/schema" + +/** F-05: 列表查询默认上限,防止无界查询拉爆内存 */ +const DEFAULT_LIMIT = 1000 + +// --------------------------------------------------------------------------- +// Cross-module query interfaces — read-only access for other modules +// --------------------------------------------------------------------------- + +export type KnowledgePointOption = { + id: string + name: string + chapterId: string | null + chapterTitle: string | null + textbookId: string | null + textbookTitle: string | null + subject: string | null + grade: string | null +} + +/** + * 获取所有知识点选项(含章节和教材信息)。 + * 供 questions 模块跨模块调用使用,避免直接查询 textbooks/chapters/knowledgePoints 表。 + */ +export const getKnowledgePointOptionsRaw = async (): Promise => { + const rows = await db + .select({ + id: knowledgePoints.id, + name: knowledgePoints.name, + chapterId: chapters.id, + chapterTitle: chapters.title, + textbookId: textbooks.id, + textbookTitle: textbooks.title, + subject: textbooks.subject, + grade: textbooks.grade, + }) + .from(knowledgePoints) + .leftJoin(chapters, eq(chapters.id, knowledgePoints.chapterId)) + .leftJoin(textbooks, eq(textbooks.id, chapters.textbookId)) + .orderBy( + asc(textbooks.title), + asc(chapters.order), + asc(chapters.title), + asc(knowledgePoints.order), + asc(knowledgePoints.name) + ) + .limit(DEFAULT_LIMIT) + + return rows.map((row) => ({ + id: row.id, + name: row.name, + chapterId: row.chapterId ?? null, + chapterTitle: row.chapterTitle ?? null, + textbookId: row.textbookId ?? null, + textbookTitle: row.textbookTitle ?? null, + subject: row.subject ?? null, + grade: row.grade ?? null, + })) +} +export const getKnowledgePointOptions = cacheFn(getKnowledgePointOptionsRaw, { + tags: ["textbooks"], + ttl: 300, + keyParts: ["textbooks", "getKnowledgePointOptions"], +}) + +// --------------------------------------------------------------------------- +// Cross-module batch title/name resolvers +// --------------------------------------------------------------------------- + +/** + * 跨模块接口:按教材 ID 批量获取教材标题。 + * + * 供 lesson-preparation 等模块解析课案关联的教材名称,避免直接查询 textbooks 表。 + */ +export const getTextbookTitlesByIdsRaw = async ( + textbookIds: string[] +): Promise> => { + const result = new Map() + const uniqueIds = Array.from(new Set(textbookIds.filter((v): v is string => typeof v === "string" && v.length > 0))) + if (uniqueIds.length === 0) return result + + const rows = await db + .select({ id: textbooks.id, title: textbooks.title }) + .from(textbooks) + .where(inArray(textbooks.id, uniqueIds)) + + for (const r of rows) result.set(r.id, r.title) + return result +} +export const getTextbookTitlesByIds = cacheFn(getTextbookTitlesByIdsRaw, { + tags: ["textbooks"], + ttl: 300, + keyParts: ["textbooks", "getTextbookTitlesByIds"], +}) + +/** + * 跨模块接口:按章节 ID 批量获取章节标题。 + * + * 供 lesson-preparation 等模块解析课案关联的章节名称,避免直接查询 chapters 表。 + */ +export const getChapterTitlesByIdsRaw = async ( + chapterIds: string[] +): Promise> => { + const result = new Map() + const uniqueIds = Array.from(new Set(chapterIds.filter((v): v is string => typeof v === "string" && v.length > 0))) + if (uniqueIds.length === 0) return result + + const rows = await db + .select({ id: chapters.id, title: chapters.title }) + .from(chapters) + .where(inArray(chapters.id, uniqueIds)) + + for (const r of rows) result.set(r.id, r.title) + return result +} +export const getChapterTitlesByIds = cacheFn(getChapterTitlesByIdsRaw, { + tags: ["textbooks"], + ttl: 300, + keyParts: ["textbooks", "getChapterTitlesByIds"], +}) + +/** + * 跨模块接口:按知识点 ID 批量获取知识点名称。 + * + * 供 questions 模块解析题目关联的知识点名称,避免直接查询 knowledgePoints 表。 + */ +export const getKnowledgePointNamesByIdsRaw = async ( + knowledgePointIds: string[] +): Promise> => { + const result = new Map() + const uniqueIds = Array.from(new Set(knowledgePointIds.filter((v): v is string => typeof v === "string" && v.length > 0))) + if (uniqueIds.length === 0) return result + + const rows = await db + .select({ id: knowledgePoints.id, name: knowledgePoints.name }) + .from(knowledgePoints) + .where(inArray(knowledgePoints.id, uniqueIds)) + + for (const r of rows) result.set(r.id, r.name) + return result +} +export const getKnowledgePointNamesByIds = cacheFn(getKnowledgePointNamesByIdsRaw, { + tags: ["textbooks"], + ttl: 300, + keyParts: ["textbooks", "getKnowledgePointNamesByIds"], +}) diff --git a/src/modules/textbooks/data-access-knowledge-points.ts b/src/modules/textbooks/data-access-knowledge-points.ts new file mode 100644 index 0000000..c824676 --- /dev/null +++ b/src/modules/textbooks/data-access-knowledge-points.ts @@ -0,0 +1,132 @@ +import "server-only" + +import { asc, eq } from "drizzle-orm" +import { createId } from "@paralleldrive/cuid2" + +import { cacheFn } from "@/shared/lib/cache" +import { db } from "@/shared/db" +import { chapters, knowledgePoints } from "@/shared/db/schema" +import type { KnowledgePoint } from "./types" +import type { + CreateKnowledgePointInput, + UpdateKnowledgePointInput, +} from "./schema" + +/** + * 获取指定章节下的知识点列表,按 order 与 name 升序。 + * + * @param chapterId - 章节 ID + * @returns 知识点数组 + */ +export const getKnowledgePointsByChapterIdRaw = async (chapterId: string): Promise => { + const rows = await db + .select({ + id: knowledgePoints.id, + name: knowledgePoints.name, + description: knowledgePoints.description, + parentId: knowledgePoints.parentId, + chapterId: knowledgePoints.chapterId, + level: knowledgePoints.level, + order: knowledgePoints.order, + }) + .from(knowledgePoints) + .where(eq(knowledgePoints.chapterId, chapterId)) + .orderBy(asc(knowledgePoints.order), asc(knowledgePoints.name)) + + return rows.map((r) => ({ + id: r.id, + name: r.name, + description: r.description ?? null, + parentId: r.parentId ?? null, + chapterId: r.chapterId ?? undefined, + level: r.level ?? 0, + order: r.order ?? 0, + })) +} +export const getKnowledgePointsByChapterId = cacheFn(getKnowledgePointsByChapterIdRaw, { + tags: ["textbooks"], + ttl: 300, + keyParts: ["textbooks", "getKnowledgePointsByChapterId"], +}) + +/** + * 获取指定教材下的全部知识点(通过 chapters 表 JOIN 过滤),按章节 order、 + * 知识点 order、知识点 name 升序。 + * + * @param textbookId - 教材 ID + * @returns 知识点数组 + */ +export const getKnowledgePointsByTextbookIdRaw = async (textbookId: string): Promise => { + const rows = await db + .select({ + id: knowledgePoints.id, + name: knowledgePoints.name, + description: knowledgePoints.description, + parentId: knowledgePoints.parentId, + chapterId: knowledgePoints.chapterId, + level: knowledgePoints.level, + order: knowledgePoints.order, + }) + .from(knowledgePoints) + .innerJoin(chapters, eq(chapters.id, knowledgePoints.chapterId)) + .where(eq(chapters.textbookId, textbookId)) + .orderBy(asc(chapters.order), asc(knowledgePoints.order), asc(knowledgePoints.name)) + + return rows.map((r) => ({ + id: r.id, + name: r.name, + description: r.description ?? null, + parentId: r.parentId ?? null, + chapterId: r.chapterId ?? undefined, + level: r.level ?? 0, + order: r.order ?? 0, + })) +} +export const getKnowledgePointsByTextbookId = cacheFn(getKnowledgePointsByTextbookIdRaw, { + tags: ["textbooks"], + ttl: 300, + keyParts: ["textbooks", "getKnowledgePointsByTextbookId"], +}) + +/** + * 创建知识点(level/order 默认 0,需调用方按需更新)。 + * + * @param data - 知识点输入(name/description?/anchorText?/chapterId/parentId?) + */ +export async function createKnowledgePoint(data: CreateKnowledgePointInput): Promise { + await db.insert(knowledgePoints).values({ + id: createId(), + name: data.name, + description: data.description, + anchorText: data.anchorText, + chapterId: data.chapterId, + parentId: data.parentId, + level: 0, // Default level + order: 0, // Default order + }) +} + +/** + * 按 ID 更新知识点(name/description/anchorText),不动 level/order/parentId。 + * + * @param data - 知识点更新输入(id/name/description?/anchorText?) + */ +export async function updateKnowledgePoint(data: UpdateKnowledgePointInput): Promise { + await db + .update(knowledgePoints) + .set({ + name: data.name, + description: data.description, + anchorText: data.anchorText, + }) + .where(eq(knowledgePoints.id, data.id)) +} + +/** + * 按 ID 删除知识点。 + * + * @param id - 待删除的知识点 ID + */ +export async function deleteKnowledgePoint(id: string): Promise { + await db.delete(knowledgePoints).where(eq(knowledgePoints.id, id)) +} diff --git a/src/modules/textbooks/data-access-prerequisites.ts b/src/modules/textbooks/data-access-prerequisites.ts new file mode 100644 index 0000000..f456069 --- /dev/null +++ b/src/modules/textbooks/data-access-prerequisites.ts @@ -0,0 +1,64 @@ +import "server-only" + +import { and, eq } from "drizzle-orm" + +import { cacheFn } from "@/shared/lib/cache" +import { db } from "@/shared/db" +import { chapters, knowledgePoints, knowledgePointPrerequisites } from "@/shared/db/schema" +import type { + CreatePrerequisiteInput, + DeletePrerequisiteInput, +} from "./schema" + +// ===== Prerequisite CRUD ===== + +/** + * 创建知识点前置依赖关系(knowledgePointId → prerequisiteKpId)。 + * + * @param data - 前置依赖输入(knowledgePointId/prerequisiteKpId) + */ +export async function createPrerequisite(data: CreatePrerequisiteInput): Promise { + await db.insert(knowledgePointPrerequisites).values({ + knowledgePointId: data.knowledgePointId, + prerequisiteKpId: data.prerequisiteKpId, + }) +} + +/** + * 删除知识点前置依赖关系(按 knowledgePointId + prerequisiteKpId 联合主键)。 + * + * @param data - 待删除的前置依赖输入(knowledgePointId/prerequisiteKpId) + */ +export async function deletePrerequisite(data: DeletePrerequisiteInput): Promise { + await db + .delete(knowledgePointPrerequisites) + .where(and( + eq(knowledgePointPrerequisites.knowledgePointId, data.knowledgePointId), + eq(knowledgePointPrerequisites.prerequisiteKpId, data.prerequisiteKpId), + )) +} + +/** + * 获取教材下所有知识点的前置依赖边列表。 + * 用于循环检测。 + */ +export async function getPrerequisiteEdgesForTextbookRaw( + textbookId: string, +): Promise> { + const rows = await db + .select({ + knowledgePointId: knowledgePointPrerequisites.knowledgePointId, + prerequisiteKpId: knowledgePointPrerequisites.prerequisiteKpId, + }) + .from(knowledgePointPrerequisites) + .innerJoin(knowledgePoints, eq(knowledgePoints.id, knowledgePointPrerequisites.knowledgePointId)) + .innerJoin(chapters, eq(chapters.id, knowledgePoints.chapterId)) + .where(eq(chapters.textbookId, textbookId)) + + return rows.map((r): [string, string] => [r.knowledgePointId, r.prerequisiteKpId]) +} +export const getPrerequisiteEdgesForTextbook = cacheFn(getPrerequisiteEdgesForTextbookRaw, { + tags: ["textbooks"], + ttl: 300, + keyParts: ["textbooks", "getPrerequisiteEdgesForTextbook"], +}) diff --git a/src/modules/textbooks/data-access-textbooks.ts b/src/modules/textbooks/data-access-textbooks.ts new file mode 100644 index 0000000..8d741d0 --- /dev/null +++ b/src/modules/textbooks/data-access-textbooks.ts @@ -0,0 +1,291 @@ +import "server-only" + +import { and, asc, count, eq, like, or, sql, type SQL } from "drizzle-orm" +import { createId } from "@paralleldrive/cuid2" + +import { cacheFn } from "@/shared/lib/cache" +import { db } from "@/shared/db" +import { chapters, textbooks } from "@/shared/db/schema" +import { escapeLikePattern, NotFoundError } from "@/shared/lib/action-utils" +import type { Textbook } from "./types" +import type { + CreateTextbookInput, + UpdateTextbookInput, +} from "./schema" +import { normalizeOptional } from "./utils" + +/** F-05: 列表查询默认上限,防止无界查询拉爆内存 */ +const DEFAULT_LIMIT = 1000 + +/** + * 数据范围过滤参数。 + * 学生端应传入 grade 按年级过滤;教师/admin 端不传则返回全量。 + */ +export interface TextbookQueryScope { + /** 按年级过滤(学生端传入学生所在年级) */ + grade?: string +} + +export const getTextbooksRaw = async (query?: string, subject?: string, grade?: string): Promise => { + const conditions: SQL[] = [] + + const q = query?.trim() + if (q) { + // F-02: 前缀匹配可走索引,避免 %xxx% 全表扫描 + const needle = `${escapeLikePattern(q)}%` + const nameCond = or( + like(textbooks.title, needle), + like(textbooks.subject, needle), + like(textbooks.grade, needle), + like(textbooks.publisher, needle) + ) + if (nameCond) conditions.push(nameCond) + } + + const s = subject?.trim() + if (s && s !== "all") conditions.push(eq(textbooks.subject, s)) + + const g = grade?.trim() + if (g && g !== "all") conditions.push(eq(textbooks.grade, g)) + + const rows = await db + .select({ + id: textbooks.id, + title: textbooks.title, + subject: textbooks.subject, + grade: textbooks.grade, + publisher: textbooks.publisher, + createdAt: textbooks.createdAt, + updatedAt: textbooks.updatedAt, + chaptersCount: sql`COUNT(${chapters.id})`, + }) + .from(textbooks) + .leftJoin(chapters, eq(chapters.textbookId, textbooks.id)) + .where(conditions.length ? and(...conditions) : undefined) + .groupBy(textbooks.id, textbooks.title, textbooks.subject, textbooks.grade, textbooks.publisher, textbooks.createdAt, textbooks.updatedAt) + .orderBy(asc(textbooks.title), asc(textbooks.subject), asc(textbooks.grade)) + .limit(DEFAULT_LIMIT) + + return rows.map((r) => ({ + id: r.id, + title: r.title, + subject: r.subject, + grade: r.grade, + publisher: r.publisher, + createdAt: r.createdAt, + updatedAt: r.updatedAt, + _count: { chapters: Number(r.chaptersCount ?? 0) }, + })) +} +export const getTextbooks = cacheFn(getTextbooksRaw, { + tags: ["textbooks"], + ttl: 300, + keyParts: ["textbooks", "getTextbooks"], +}) + +export const getTextbookByIdRaw = async (id: string): Promise => { + const [row] = await db + .select({ + id: textbooks.id, + title: textbooks.title, + subject: textbooks.subject, + grade: textbooks.grade, + publisher: textbooks.publisher, + createdAt: textbooks.createdAt, + updatedAt: textbooks.updatedAt, + chaptersCount: sql`COUNT(${chapters.id})`, + }) + .from(textbooks) + .leftJoin(chapters, eq(chapters.textbookId, textbooks.id)) + .where(eq(textbooks.id, id)) + .groupBy(textbooks.id, textbooks.title, textbooks.subject, textbooks.grade, textbooks.publisher, textbooks.createdAt, textbooks.updatedAt) + .limit(1) + + if (!row) return undefined + + return { + id: row.id, + title: row.title, + subject: row.subject, + grade: row.grade, + publisher: row.publisher, + createdAt: row.createdAt, + updatedAt: row.updatedAt, + _count: { chapters: Number(row.chaptersCount ?? 0) }, + } +} +export const getTextbookById = cacheFn(getTextbookByIdRaw, { + tags: ["textbooks"], + ttl: 300, + keyParts: ["textbooks", "getTextbookById"], +}) + +/** + * 创建教材。 + * + * @param data - 教材输入(title/subject/grade/publisher) + * @returns 新创建的教材对象(chaptersCount 为 0) + */ +export async function createTextbook(data: CreateTextbookInput): Promise { + const id = createId() + const now = new Date() + + const row = { + id, + title: data.title.trim(), + subject: data.subject.trim(), + grade: normalizeOptional(data.grade), + publisher: normalizeOptional(data.publisher), + createdAt: now, + updatedAt: now, + } + + await db.insert(textbooks).values(row) + + return { + ...row, + _count: { chapters: 0 }, + } +} + +/** + * 更新教材字段(title/subject/grade/publisher)。 + * + * @param data - 更新输入(必须含 id) + * @returns 更新后的教材对象 + * @throws {NotFoundError} 当教材不存在时抛出 + */ +export async function updateTextbook(data: UpdateTextbookInput): Promise { + await db + .update(textbooks) + .set({ + title: data.title.trim(), + subject: data.subject.trim(), + grade: normalizeOptional(data.grade), + publisher: normalizeOptional(data.publisher), + }) + .where(eq(textbooks.id, data.id)) + + const updated = await getTextbookById(data.id) + if (!updated) throw new NotFoundError("教材") + return updated +} + +/** + * 删除教材(DB 级联删除关联章节,但章节下的知识点需应用层显式清理)。 + * + * @param id - 教材 ID + */ +export async function deleteTextbook(id: string): Promise { + await db.delete(textbooks).where(eq(textbooks.id, id)) +} + +export type TextbooksDashboardStats = { + textbookCount: number + chapterCount: number +} + +/** + * 获取教材仪表盘统计(教材总数与章节总数)。 + * + * @returns 包含 textbookCount / chapterCount 的统计对象 + */ +export const getTextbooksDashboardStatsRaw = async (): Promise => { + const [textbookCountRow, chapterCountRow] = await Promise.all([ + db.select({ value: count() }).from(textbooks), + db.select({ value: count() }).from(chapters), + ]) + return { + textbookCount: Number(textbookCountRow[0]?.value ?? 0), + chapterCount: Number(chapterCountRow[0]?.value ?? 0), + } +} +export const getTextbooksDashboardStats = cacheFn(getTextbooksDashboardStatsRaw, { + tags: ["textbooks"], + ttl: 300, + keyParts: ["textbooks", "getTextbooksDashboardStats"], +}) + +// --------------------------------------------------------------------------- +// 带 scope 的查询(P1-1 数据范围过滤) +// --------------------------------------------------------------------------- + +/** + * 按数据范围获取教材列表。 + * + * 学生端应传入 `scope.grade` 按年级过滤,避免跨年级越权读取。 + */ +export const getTextbooksWithScopeRaw = async ( + query?: string, + subject?: string, + grade?: string, + scope?: TextbookQueryScope +): Promise => { + const conditions: SQL[] = [] + + const q = query?.trim() + if (q) { + // F-02: 前缀匹配可走索引,避免 %xxx% 全表扫描 + const needle = `${escapeLikePattern(q)}%` + const nameCond = or( + like(textbooks.title, needle), + like(textbooks.subject, needle), + like(textbooks.grade, needle), + like(textbooks.publisher, needle) + ) + if (nameCond) conditions.push(nameCond) + } + + const s = subject?.trim() + if (s && s !== "all") conditions.push(eq(textbooks.subject, s)) + + // URL 参数 grade(用户筛选) + const g = grade?.trim() + if (g && g !== "all") conditions.push(eq(textbooks.grade, g)) + + // scope.grade(数据范围过滤,学生端强制按年级过滤) + const scopeGrade = scope?.grade?.trim() + if (scopeGrade) conditions.push(eq(textbooks.grade, scopeGrade)) + + const rows = await db + .select({ + id: textbooks.id, + title: textbooks.title, + subject: textbooks.subject, + grade: textbooks.grade, + publisher: textbooks.publisher, + createdAt: textbooks.createdAt, + updatedAt: textbooks.updatedAt, + chaptersCount: sql`COUNT(${chapters.id})`, + }) + .from(textbooks) + .leftJoin(chapters, eq(chapters.textbookId, textbooks.id)) + .where(conditions.length ? and(...conditions) : undefined) + .groupBy( + textbooks.id, + textbooks.title, + textbooks.subject, + textbooks.grade, + textbooks.publisher, + textbooks.createdAt, + textbooks.updatedAt + ) + .orderBy(asc(textbooks.title), asc(textbooks.subject), asc(textbooks.grade)) + .limit(DEFAULT_LIMIT) + + return rows.map((r) => ({ + id: r.id, + title: r.title, + subject: r.subject, + grade: r.grade, + publisher: r.publisher, + createdAt: r.createdAt, + updatedAt: r.updatedAt, + _count: { chapters: Number(r.chaptersCount ?? 0) }, + })) +} +export const getTextbooksWithScope = cacheFn(getTextbooksWithScopeRaw, { + tags: ["textbooks"], + ttl: 300, + keyParts: ["textbooks", "getTextbooksWithScope"], +}) diff --git a/src/modules/textbooks/data-access-verification.ts b/src/modules/textbooks/data-access-verification.ts new file mode 100644 index 0000000..dc07ef9 --- /dev/null +++ b/src/modules/textbooks/data-access-verification.ts @@ -0,0 +1,62 @@ +import "server-only" + +import { eq } from "drizzle-orm" + +import { cacheFn } from "@/shared/lib/cache" +import { db } from "@/shared/db" +import { chapters, knowledgePoints } from "@/shared/db/schema" + +// --------------------------------------------------------------------------- +// 资源归属校验(P0-4) +// --------------------------------------------------------------------------- + +/** + * 校验章节是否属于指定教材。 + * + * 用于 Server Action 二次校验,防止越权操作其他教材的章节。 + * + * @returns true 表示归属一致 + */ +export async function verifyChapterBelongsToTextbookRaw( + chapterId: string, + textbookId: string +): Promise { + const [row] = await db + .select({ textbookId: chapters.textbookId }) + .from(chapters) + .where(eq(chapters.id, chapterId)) + .limit(1) + + if (!row) return false + return row.textbookId === textbookId +} +export const verifyChapterBelongsToTextbook = cacheFn(verifyChapterBelongsToTextbookRaw, { + tags: ["textbooks"], + ttl: 60, + keyParts: ["textbooks", "verifyChapterBelongsToTextbook"], +}) + +/** + * 校验知识点是否属于指定教材(通过 chapter → textbook 关联)。 + * + * 用于 Server Action 二次校验,防止越权操作其他教材的知识点。 + */ +export async function verifyKnowledgePointBelongsToTextbookRaw( + kpId: string, + textbookId: string +): Promise { + const [row] = await db + .select({ textbookId: chapters.textbookId }) + .from(knowledgePoints) + .innerJoin(chapters, eq(chapters.id, knowledgePoints.chapterId)) + .where(eq(knowledgePoints.id, kpId)) + .limit(1) + + if (!row) return false + return row.textbookId === textbookId +} +export const verifyKnowledgePointBelongsToTextbook = cacheFn(verifyKnowledgePointBelongsToTextbookRaw, { + tags: ["textbooks"], + ttl: 60, + keyParts: ["textbooks", "verifyKnowledgePointBelongsToTextbook"], +}) diff --git a/src/modules/textbooks/data-access.ts b/src/modules/textbooks/data-access.ts index e8b1ae2..1a6542c 100644 --- a/src/modules/textbooks/data-access.ts +++ b/src/modules/textbooks/data-access.ts @@ -1,906 +1,17 @@ import "server-only" -import { and, asc, count, eq, inArray, like, or, sql, isNull, type SQL } from "drizzle-orm" -import { createId } from "@paralleldrive/cuid2" - -import { cacheFn } from "@/shared/lib/cache" -import { db } from "@/shared/db" -import { chapters, knowledgePoints, knowledgePointPrerequisites, textbooks } from "@/shared/db/schema" -import { escapeLikePattern, NotFoundError } from "@/shared/lib/action-utils" -import type { - Chapter, - KnowledgePoint, - Textbook, -} from "./types" -import type { - CreateChapterInput, - CreateKnowledgePointInput, - CreatePrerequisiteInput, - CreateTextbookInput, - DeletePrerequisiteInput, - UpdateChapterContentInput, - UpdateKnowledgePointInput, - UpdateTextbookInput, -} from "./schema" -import { - buildChapterTree, - findChapterById, - normalizeOptional, - sortChapters, -} from "./utils" - -export { buildChapterTree, findChapterById, normalizeOptional, sortChapters } - -/** F-05: 列表查询默认上限,防止无界查询拉爆内存 */ -const DEFAULT_LIMIT = 1000 - -/** - * 数据范围过滤参数。 - * 学生端应传入 grade 按年级过滤;教师/admin 端不传则返回全量。 - */ -export interface TextbookQueryScope { - /** 按年级过滤(学生端传入学生所在年级) */ - grade?: string -} - -export const getTextbooksRaw = async (query?: string, subject?: string, grade?: string): Promise => { - const conditions: SQL[] = [] - - const q = query?.trim() - if (q) { - // F-02: 前缀匹配可走索引,避免 %xxx% 全表扫描 - const needle = `${escapeLikePattern(q)}%` - const nameCond = or( - like(textbooks.title, needle), - like(textbooks.subject, needle), - like(textbooks.grade, needle), - like(textbooks.publisher, needle) - ) - if (nameCond) conditions.push(nameCond) - } - - const s = subject?.trim() - if (s && s !== "all") conditions.push(eq(textbooks.subject, s)) - - const g = grade?.trim() - if (g && g !== "all") conditions.push(eq(textbooks.grade, g)) - - const rows = await db - .select({ - id: textbooks.id, - title: textbooks.title, - subject: textbooks.subject, - grade: textbooks.grade, - publisher: textbooks.publisher, - createdAt: textbooks.createdAt, - updatedAt: textbooks.updatedAt, - chaptersCount: sql`COUNT(${chapters.id})`, - }) - .from(textbooks) - .leftJoin(chapters, eq(chapters.textbookId, textbooks.id)) - .where(conditions.length ? and(...conditions) : undefined) - .groupBy(textbooks.id, textbooks.title, textbooks.subject, textbooks.grade, textbooks.publisher, textbooks.createdAt, textbooks.updatedAt) - .orderBy(asc(textbooks.title), asc(textbooks.subject), asc(textbooks.grade)) - .limit(DEFAULT_LIMIT) - - return rows.map((r) => ({ - id: r.id, - title: r.title, - subject: r.subject, - grade: r.grade, - publisher: r.publisher, - createdAt: r.createdAt, - updatedAt: r.updatedAt, - _count: { chapters: Number(r.chaptersCount ?? 0) }, - })) -} -export const getTextbooks = cacheFn(getTextbooksRaw, { - tags: ["textbooks"], - ttl: 300, - keyParts: ["textbooks", "getTextbooks"], -}) - -export const getTextbookByIdRaw = async (id: string): Promise => { - const [row] = await db - .select({ - id: textbooks.id, - title: textbooks.title, - subject: textbooks.subject, - grade: textbooks.grade, - publisher: textbooks.publisher, - createdAt: textbooks.createdAt, - updatedAt: textbooks.updatedAt, - chaptersCount: sql`COUNT(${chapters.id})`, - }) - .from(textbooks) - .leftJoin(chapters, eq(chapters.textbookId, textbooks.id)) - .where(eq(textbooks.id, id)) - .groupBy(textbooks.id, textbooks.title, textbooks.subject, textbooks.grade, textbooks.publisher, textbooks.createdAt, textbooks.updatedAt) - .limit(1) - - if (!row) return undefined - - return { - id: row.id, - title: row.title, - subject: row.subject, - grade: row.grade, - publisher: row.publisher, - createdAt: row.createdAt, - updatedAt: row.updatedAt, - _count: { chapters: Number(row.chaptersCount ?? 0) }, - } -} -export const getTextbookById = cacheFn(getTextbookByIdRaw, { - tags: ["textbooks"], - ttl: 300, - keyParts: ["textbooks", "getTextbookById"], -}) - -export const getChaptersByTextbookIdRaw = async (textbookId: string): Promise => { - const rows = await db - .select({ - id: chapters.id, - textbookId: chapters.textbookId, - title: chapters.title, - order: chapters.order, - parentId: chapters.parentId, - content: chapters.content, - createdAt: chapters.createdAt, - updatedAt: chapters.updatedAt, - }) - .from(chapters) - .where(eq(chapters.textbookId, textbookId)) - .orderBy(asc(chapters.order), asc(chapters.title)) - - return buildChapterTree( - rows.map((r) => ({ - id: r.id, - textbookId: r.textbookId, - title: r.title, - order: r.order, - parentId: r.parentId, - content: r.content ?? null, - createdAt: r.createdAt, - updatedAt: r.updatedAt, - })) - ) -} -export const getChaptersByTextbookId = cacheFn(getChaptersByTextbookIdRaw, { - tags: ["textbooks"], - ttl: 300, - keyParts: ["textbooks", "getChaptersByTextbookId"], -}) - -/** - * 创建教材。 - * - * @param data - 教材输入(title/subject/grade/publisher) - * @returns 新创建的教材对象(chaptersCount 为 0) - */ -export async function createTextbook(data: CreateTextbookInput): Promise { - const id = createId() - const now = new Date() - - const row = { - id, - title: data.title.trim(), - subject: data.subject.trim(), - grade: normalizeOptional(data.grade), - publisher: normalizeOptional(data.publisher), - createdAt: now, - updatedAt: now, - } - - await db.insert(textbooks).values(row) - - return { - ...row, - _count: { chapters: 0 }, - } -} - -/** - * 更新教材字段(title/subject/grade/publisher)。 - * - * @param data - 更新输入(必须含 id) - * @returns 更新后的教材对象 - * @throws {NotFoundError} 当教材不存在时抛出 - */ -export async function updateTextbook(data: UpdateTextbookInput): Promise { - await db - .update(textbooks) - .set({ - title: data.title.trim(), - subject: data.subject.trim(), - grade: normalizeOptional(data.grade), - publisher: normalizeOptional(data.publisher), - }) - .where(eq(textbooks.id, data.id)) - - const updated = await getTextbookById(data.id) - if (!updated) throw new NotFoundError("教材") - return updated -} - -/** - * 删除教材(DB 级联删除关联章节,但章节下的知识点需应用层显式清理)。 - * - * @param id - 教材 ID - */ -export async function deleteTextbook(id: string): Promise { - await db.delete(textbooks).where(eq(textbooks.id, id)) -} - -/** - * 在指定教材下创建章节(content 初始为空字符串,children 为空数组)。 - * - * @param data - 章节输入(textbookId/title/order?/parentId?) - * @returns 新创建的章节对象 - */ -export async function createChapter(data: CreateChapterInput): Promise { - const id = createId() - const now = new Date() - - const row = { - id, - textbookId: data.textbookId, - title: data.title.trim(), - order: data.order ?? 0, - parentId: data.parentId ?? null, - content: "", - createdAt: now, - updatedAt: now, - } - - await db.insert(chapters).values(row) - - return { - id: row.id, - textbookId: row.textbookId, - title: row.title, - order: row.order, - parentId: row.parentId, - content: row.content, - createdAt: row.createdAt, - updatedAt: row.updatedAt, - children: [], - } -} - -/** - * 更新章节正文 content 字段,并返回更新后的章节对象。 - * - * @param data - 更新输入(chapterId + content) - * @returns 更新后的章节对象(children 为空数组) - * @throws {NotFoundError} 当章节不存在时抛出 - */ -export async function updateChapterContent(data: UpdateChapterContentInput): Promise { - await db.update(chapters).set({ content: data.content }).where(eq(chapters.id, data.chapterId)) - - const [row] = await db - .select({ - id: chapters.id, - textbookId: chapters.textbookId, - title: chapters.title, - order: chapters.order, - parentId: chapters.parentId, - content: chapters.content, - createdAt: chapters.createdAt, - updatedAt: chapters.updatedAt, - }) - .from(chapters) - .where(eq(chapters.id, data.chapterId)) - .limit(1) - - if (!row) throw new NotFoundError("章节") - - return { - id: row.id, - textbookId: row.textbookId, - title: row.title, - order: row.order, - parentId: row.parentId, - content: row.content ?? null, - createdAt: row.createdAt, - updatedAt: row.updatedAt, - children: [], - } -} - -/** - * 删除章节及其所有后代章节,并清理后代知识点及其前置依赖记录。 - * - * 通过本地构建 childrenByParent 索引 + 栈遍历收集所有后代 ID, - * 在事务内:1) 清理 knowledgePointPrerequisites 表中引用被删知识点的孤儿记录; - * 2) 删除知识点;3) 删除章节。 - * - * @param id - 待删除的章节 ID - */ -export async function deleteChapter(id: string): Promise { - const [target] = await db - .select({ id: chapters.id, textbookId: chapters.textbookId }) - .from(chapters) - .where(eq(chapters.id, id)) - .limit(1) - - if (!target) return - - const all = await db - .select({ id: chapters.id, parentId: chapters.parentId }) - .from(chapters) - .where(eq(chapters.textbookId, target.textbookId)) - - const childrenByParent = new Map() - for (const ch of all) { - if (!ch.parentId) continue - const arr = childrenByParent.get(ch.parentId) ?? [] - arr.push(ch.id) - childrenByParent.set(ch.parentId, arr) - } - - const idsToDelete: string[] = [] - const stack: string[] = [id] - const seen = new Set() - while (stack.length) { - const cur = stack.pop() - if (!cur) break - if (seen.has(cur)) continue - seen.add(cur) - idsToDelete.push(cur) - const kids = childrenByParent.get(cur) - if (kids) stack.push(...kids) - } - - // P0 数据完整性修复:先查询被删章节下的所有知识点 ID,用于清理孤儿前置依赖记录 - const kpsInChapters = await db - .select({ id: knowledgePoints.id }) - .from(knowledgePoints) - .where(inArray(knowledgePoints.chapterId, idsToDelete)) - const kpIds = kpsInChapters.map((k) => k.id) - - await db.transaction(async (tx) => { - // P0 修复:清理 knowledgePointPrerequisites 表中引用被删知识点的孤儿记录 - // 包括作为 knowledgePointId 和作为 prerequisiteKpId 的记录 - if (kpIds.length > 0) { - await tx - .delete(knowledgePointPrerequisites) - .where( - or( - inArray(knowledgePointPrerequisites.knowledgePointId, kpIds), - inArray(knowledgePointPrerequisites.prerequisiteKpId, kpIds), - ), - ) - } - await tx.delete(knowledgePoints).where(inArray(knowledgePoints.chapterId, idsToDelete)) - await tx.delete(chapters).where(inArray(chapters.id, idsToDelete)) - }) -} - -/** - * 获取指定章节下的知识点列表,按 order 与 name 升序。 - * - * @param chapterId - 章节 ID - * @returns 知识点数组 - */ -export const getKnowledgePointsByChapterIdRaw = async (chapterId: string): Promise => { - const rows = await db - .select({ - id: knowledgePoints.id, - name: knowledgePoints.name, - description: knowledgePoints.description, - parentId: knowledgePoints.parentId, - chapterId: knowledgePoints.chapterId, - level: knowledgePoints.level, - order: knowledgePoints.order, - }) - .from(knowledgePoints) - .where(eq(knowledgePoints.chapterId, chapterId)) - .orderBy(asc(knowledgePoints.order), asc(knowledgePoints.name)) - - return rows.map((r) => ({ - id: r.id, - name: r.name, - description: r.description ?? null, - parentId: r.parentId ?? null, - chapterId: r.chapterId ?? undefined, - level: r.level ?? 0, - order: r.order ?? 0, - })) -} -export const getKnowledgePointsByChapterId = cacheFn(getKnowledgePointsByChapterIdRaw, { - tags: ["textbooks"], - ttl: 300, - keyParts: ["textbooks", "getKnowledgePointsByChapterId"], -}) - -/** - * 获取指定教材下的全部知识点(通过 chapters 表 JOIN 过滤),按章节 order、 - * 知识点 order、知识点 name 升序。 - * - * @param textbookId - 教材 ID - * @returns 知识点数组 - */ -export const getKnowledgePointsByTextbookIdRaw = async (textbookId: string): Promise => { - const rows = await db - .select({ - id: knowledgePoints.id, - name: knowledgePoints.name, - description: knowledgePoints.description, - parentId: knowledgePoints.parentId, - chapterId: knowledgePoints.chapterId, - level: knowledgePoints.level, - order: knowledgePoints.order, - }) - .from(knowledgePoints) - .innerJoin(chapters, eq(chapters.id, knowledgePoints.chapterId)) - .where(eq(chapters.textbookId, textbookId)) - .orderBy(asc(chapters.order), asc(knowledgePoints.order), asc(knowledgePoints.name)) - - return rows.map((r) => ({ - id: r.id, - name: r.name, - description: r.description ?? null, - parentId: r.parentId ?? null, - chapterId: r.chapterId ?? undefined, - level: r.level ?? 0, - order: r.order ?? 0, - })) -} -export const getKnowledgePointsByTextbookId = cacheFn(getKnowledgePointsByTextbookIdRaw, { - tags: ["textbooks"], - ttl: 300, - keyParts: ["textbooks", "getKnowledgePointsByTextbookId"], -}) - -/** - * 创建知识点(level/order 默认 0,需调用方按需更新)。 - * - * @param data - 知识点输入(name/description?/anchorText?/chapterId/parentId?) - */ -export async function createKnowledgePoint(data: CreateKnowledgePointInput): Promise { - await db.insert(knowledgePoints).values({ - id: createId(), - name: data.name, - description: data.description, - anchorText: data.anchorText, - chapterId: data.chapterId, - parentId: data.parentId, - level: 0, // Default level - order: 0, // Default order - }) -} - -/** - * 按 ID 更新知识点(name/description/anchorText),不动 level/order/parentId。 - * - * @param data - 知识点更新输入(id/name/description?/anchorText?) - */ -export async function updateKnowledgePoint(data: UpdateKnowledgePointInput): Promise { - await db - .update(knowledgePoints) - .set({ - name: data.name, - description: data.description, - anchorText: data.anchorText, - }) - .where(eq(knowledgePoints.id, data.id)) -} - -/** - * 按 ID 删除知识点。 - * - * @param id - 待删除的知识点 ID - */ -export async function deleteKnowledgePoint(id: string): Promise { - await db.delete(knowledgePoints).where(eq(knowledgePoints.id, id)) -} - -/** - * 调整章节在同级中的位置(可同时切换 parentId),单事务内批量更新 order。 - * - * 流程:1) 读取目标章节;2) 查询同 parent 下兄弟;3) 移除目标后插入到 newIndex; - * 4) 在事务内并行 UPDATE 变更行(order 或 parentId)。 - * - * @param chapterId - 待移动的章节 ID - * @param newIndex - 目标位置(0-based) - * @param parentId - 目标父章节 ID(顶层为 null) - * @throws 当章节不存在时抛出 NotFoundError - */ -export async function reorderChapters(chapterId: string, newIndex: number, parentId: string | null): Promise { - const [target] = await db.select().from(chapters).where(eq(chapters.id, chapterId)).limit(1) - if (!target) throw new NotFoundError("章节") - - const siblings = await db - .select() - .from(chapters) - .where( - and( - eq(chapters.textbookId, target.textbookId), - parentId ? eq(chapters.parentId, parentId) : isNull(chapters.parentId) - ) - ) - .orderBy(asc(chapters.order)) - - const currentSiblings = siblings.filter((c) => c.id !== chapterId) - currentSiblings.splice(newIndex, 0, target) - - await db.transaction(async (tx) => { - // 批量并行 UPDATE 替代原先串行 await 循环,单事务保证原子性。 - // 仅对需要变更的行发起 UPDATE(order 变化或 parentId 变化)。 - await Promise.all( - currentSiblings.map((ch, i) => { - if (ch.order !== i || (ch.id === chapterId && ch.parentId !== parentId)) { - return tx - .update(chapters) - .set({ - order: i, - parentId: ch.id === chapterId ? parentId : ch.parentId - }) - .where(eq(chapters.id, ch.id)) - } - return Promise.resolve() - }) - ) - }) -} - -export type TextbooksDashboardStats = { - textbookCount: number - chapterCount: number -} - -/** - * 获取教材仪表盘统计(教材总数与章节总数)。 - * - * @returns 包含 textbookCount / chapterCount 的统计对象 - */ -export const getTextbooksDashboardStatsRaw = async (): Promise => { - const [textbookCountRow, chapterCountRow] = await Promise.all([ - db.select({ value: count() }).from(textbooks), - db.select({ value: count() }).from(chapters), - ]) - return { - textbookCount: Number(textbookCountRow[0]?.value ?? 0), - chapterCount: Number(chapterCountRow[0]?.value ?? 0), - } -} -export const getTextbooksDashboardStats = cacheFn(getTextbooksDashboardStatsRaw, { - tags: ["textbooks"], - ttl: 300, - keyParts: ["textbooks", "getTextbooksDashboardStats"], -}) - -// --------------------------------------------------------------------------- -// 资源归属校验(P0-4) -// --------------------------------------------------------------------------- - -/** - * 校验章节是否属于指定教材。 - * - * 用于 Server Action 二次校验,防止越权操作其他教材的章节。 - * - * @returns true 表示归属一致 - */ -export async function verifyChapterBelongsToTextbookRaw( - chapterId: string, - textbookId: string -): Promise { - const [row] = await db - .select({ textbookId: chapters.textbookId }) - .from(chapters) - .where(eq(chapters.id, chapterId)) - .limit(1) - - if (!row) return false - return row.textbookId === textbookId -} -export const verifyChapterBelongsToTextbook = cacheFn(verifyChapterBelongsToTextbookRaw, { - tags: ["textbooks"], - ttl: 60, - keyParts: ["textbooks", "verifyChapterBelongsToTextbook"], -}) - -/** - * 校验知识点是否属于指定教材(通过 chapter → textbook 关联)。 - * - * 用于 Server Action 二次校验,防止越权操作其他教材的知识点。 - */ -export async function verifyKnowledgePointBelongsToTextbookRaw( - kpId: string, - textbookId: string -): Promise { - const [row] = await db - .select({ textbookId: chapters.textbookId }) - .from(knowledgePoints) - .innerJoin(chapters, eq(chapters.id, knowledgePoints.chapterId)) - .where(eq(knowledgePoints.id, kpId)) - .limit(1) - - if (!row) return false - return row.textbookId === textbookId -} -export const verifyKnowledgePointBelongsToTextbook = cacheFn(verifyKnowledgePointBelongsToTextbookRaw, { - tags: ["textbooks"], - ttl: 60, - keyParts: ["textbooks", "verifyKnowledgePointBelongsToTextbook"], -}) - -// --------------------------------------------------------------------------- -// 带 scope 的查询(P1-1 数据范围过滤) -// --------------------------------------------------------------------------- - -/** - * 按数据范围获取教材列表。 - * - * 学生端应传入 `scope.grade` 按年级过滤,避免跨年级越权读取。 - */ -export const getTextbooksWithScopeRaw = async ( - query?: string, - subject?: string, - grade?: string, - scope?: TextbookQueryScope -): Promise => { - const conditions: SQL[] = [] - - const q = query?.trim() - if (q) { - // F-02: 前缀匹配可走索引,避免 %xxx% 全表扫描 - const needle = `${escapeLikePattern(q)}%` - const nameCond = or( - like(textbooks.title, needle), - like(textbooks.subject, needle), - like(textbooks.grade, needle), - like(textbooks.publisher, needle) - ) - if (nameCond) conditions.push(nameCond) - } - - const s = subject?.trim() - if (s && s !== "all") conditions.push(eq(textbooks.subject, s)) - - // URL 参数 grade(用户筛选) - const g = grade?.trim() - if (g && g !== "all") conditions.push(eq(textbooks.grade, g)) - - // scope.grade(数据范围过滤,学生端强制按年级过滤) - const scopeGrade = scope?.grade?.trim() - if (scopeGrade) conditions.push(eq(textbooks.grade, scopeGrade)) - - const rows = await db - .select({ - id: textbooks.id, - title: textbooks.title, - subject: textbooks.subject, - grade: textbooks.grade, - publisher: textbooks.publisher, - createdAt: textbooks.createdAt, - updatedAt: textbooks.updatedAt, - chaptersCount: sql`COUNT(${chapters.id})`, - }) - .from(textbooks) - .leftJoin(chapters, eq(chapters.textbookId, textbooks.id)) - .where(conditions.length ? and(...conditions) : undefined) - .groupBy( - textbooks.id, - textbooks.title, - textbooks.subject, - textbooks.grade, - textbooks.publisher, - textbooks.createdAt, - textbooks.updatedAt - ) - .orderBy(asc(textbooks.title), asc(textbooks.subject), asc(textbooks.grade)) - .limit(DEFAULT_LIMIT) - - return rows.map((r) => ({ - id: r.id, - title: r.title, - subject: r.subject, - grade: r.grade, - publisher: r.publisher, - createdAt: r.createdAt, - updatedAt: r.updatedAt, - _count: { chapters: Number(r.chaptersCount ?? 0) }, - })) -} -export const getTextbooksWithScope = cacheFn(getTextbooksWithScopeRaw, { - tags: ["textbooks"], - ttl: 300, - keyParts: ["textbooks", "getTextbooksWithScope"], -}) - -// --------------------------------------------------------------------------- -// Cross-module query interfaces — read-only access for other modules -// --------------------------------------------------------------------------- - -export type KnowledgePointOption = { - id: string - name: string - chapterId: string | null - chapterTitle: string | null - textbookId: string | null - textbookTitle: string | null - subject: string | null - grade: string | null -} - -/** - * 获取所有知识点选项(含章节和教材信息)。 - * 供 questions 模块跨模块调用使用,避免直接查询 textbooks/chapters/knowledgePoints 表。 - */ -export const getKnowledgePointOptionsRaw = async (): Promise => { - const rows = await db - .select({ - id: knowledgePoints.id, - name: knowledgePoints.name, - chapterId: chapters.id, - chapterTitle: chapters.title, - textbookId: textbooks.id, - textbookTitle: textbooks.title, - subject: textbooks.subject, - grade: textbooks.grade, - }) - .from(knowledgePoints) - .leftJoin(chapters, eq(chapters.id, knowledgePoints.chapterId)) - .leftJoin(textbooks, eq(textbooks.id, chapters.textbookId)) - .orderBy( - asc(textbooks.title), - asc(chapters.order), - asc(chapters.title), - asc(knowledgePoints.order), - asc(knowledgePoints.name) - ) - .limit(DEFAULT_LIMIT) - - return rows.map((row) => ({ - id: row.id, - name: row.name, - chapterId: row.chapterId ?? null, - chapterTitle: row.chapterTitle ?? null, - textbookId: row.textbookId ?? null, - textbookTitle: row.textbookTitle ?? null, - subject: row.subject ?? null, - grade: row.grade ?? null, - })) -} -export const getKnowledgePointOptions = cacheFn(getKnowledgePointOptionsRaw, { - tags: ["textbooks"], - ttl: 300, - keyParts: ["textbooks", "getKnowledgePointOptions"], -}) - -// ===== Prerequisite CRUD ===== - -/** - * 创建知识点前置依赖关系(knowledgePointId → prerequisiteKpId)。 - * - * @param data - 前置依赖输入(knowledgePointId/prerequisiteKpId) - */ -export async function createPrerequisite(data: CreatePrerequisiteInput): Promise { - await db.insert(knowledgePointPrerequisites).values({ - knowledgePointId: data.knowledgePointId, - prerequisiteKpId: data.prerequisiteKpId, - }) -} - -/** - * 删除知识点前置依赖关系(按 knowledgePointId + prerequisiteKpId 联合主键)。 - * - * @param data - 待删除的前置依赖输入(knowledgePointId/prerequisiteKpId) - */ -export async function deletePrerequisite(data: DeletePrerequisiteInput): Promise { - await db - .delete(knowledgePointPrerequisites) - .where(and( - eq(knowledgePointPrerequisites.knowledgePointId, data.knowledgePointId), - eq(knowledgePointPrerequisites.prerequisiteKpId, data.prerequisiteKpId), - )) -} - -/** - * 获取教材下所有知识点的前置依赖边列表。 - * 用于循环检测。 - */ -export async function getPrerequisiteEdgesForTextbookRaw( - textbookId: string, -): Promise> { - const rows = await db - .select({ - knowledgePointId: knowledgePointPrerequisites.knowledgePointId, - prerequisiteKpId: knowledgePointPrerequisites.prerequisiteKpId, - }) - .from(knowledgePointPrerequisites) - .innerJoin(knowledgePoints, eq(knowledgePoints.id, knowledgePointPrerequisites.knowledgePointId)) - .innerJoin(chapters, eq(chapters.id, knowledgePoints.chapterId)) - .where(eq(chapters.textbookId, textbookId)) - - return rows.map((r): [string, string] => [r.knowledgePointId, r.prerequisiteKpId]) -} -export const getPrerequisiteEdgesForTextbook = cacheFn(getPrerequisiteEdgesForTextbookRaw, { - tags: ["textbooks"], - ttl: 300, - keyParts: ["textbooks", "getPrerequisiteEdgesForTextbook"], -}) - -// --------------------------------------------------------------------------- -// Cross-module batch title/name resolvers -// --------------------------------------------------------------------------- - -/** - * 跨模块接口:按教材 ID 批量获取教材标题。 - * - * 供 lesson-preparation 等模块解析课案关联的教材名称,避免直接查询 textbooks 表。 - */ -export const getTextbookTitlesByIdsRaw = async ( - textbookIds: string[] -): Promise> => { - const result = new Map() - const uniqueIds = Array.from(new Set(textbookIds.filter((v): v is string => typeof v === "string" && v.length > 0))) - if (uniqueIds.length === 0) return result - - const rows = await db - .select({ id: textbooks.id, title: textbooks.title }) - .from(textbooks) - .where(inArray(textbooks.id, uniqueIds)) - - for (const r of rows) result.set(r.id, r.title) - return result -} -export const getTextbookTitlesByIds = cacheFn(getTextbookTitlesByIdsRaw, { - tags: ["textbooks"], - ttl: 300, - keyParts: ["textbooks", "getTextbookTitlesByIds"], -}) - -/** - * 跨模块接口:按章节 ID 批量获取章节标题。 - * - * 供 lesson-preparation 等模块解析课案关联的章节名称,避免直接查询 chapters 表。 - */ -export const getChapterTitlesByIdsRaw = async ( - chapterIds: string[] -): Promise> => { - const result = new Map() - const uniqueIds = Array.from(new Set(chapterIds.filter((v): v is string => typeof v === "string" && v.length > 0))) - if (uniqueIds.length === 0) return result - - const rows = await db - .select({ id: chapters.id, title: chapters.title }) - .from(chapters) - .where(inArray(chapters.id, uniqueIds)) - - for (const r of rows) result.set(r.id, r.title) - return result -} -export const getChapterTitlesByIds = cacheFn(getChapterTitlesByIdsRaw, { - tags: ["textbooks"], - ttl: 300, - keyParts: ["textbooks", "getChapterTitlesByIds"], -}) - -/** - * 跨模块接口:按知识点 ID 批量获取知识点名称。 - * - * 供 questions 模块解析题目关联的知识点名称,避免直接查询 knowledgePoints 表。 - */ -export const getKnowledgePointNamesByIdsRaw = async ( - knowledgePointIds: string[] -): Promise> => { - const result = new Map() - const uniqueIds = Array.from(new Set(knowledgePointIds.filter((v): v is string => typeof v === "string" && v.length > 0))) - if (uniqueIds.length === 0) return result - - const rows = await db - .select({ id: knowledgePoints.id, name: knowledgePoints.name }) - .from(knowledgePoints) - .where(inArray(knowledgePoints.id, uniqueIds)) - - for (const r of rows) result.set(r.id, r.name) - return result -} -export const getKnowledgePointNamesByIds = cacheFn(getKnowledgePointNamesByIdsRaw, { - tags: ["textbooks"], - ttl: 300, - keyParts: ["textbooks", "getKnowledgePointNamesByIds"], -}) +// 重新导出 utils 中的工具函数,保持向后兼容 +export { buildChapterTree, findChapterById, normalizeOptional, sortChapters } from "./utils" + +// 教材 CRUD + 查询 + scope + 仪表盘统计 +export * from "./data-access-textbooks" +// 章节 CRUD + reorder +export * from "./data-access-chapters" +// 知识点 CRUD + 查询 +export * from "./data-access-knowledge-points" +// 资源归属校验 +export * from "./data-access-verification" +// 前置依赖 CRUD +export * from "./data-access-prerequisites" +// 跨模块批量解析接口 +export * from "./data-access-cross-module" diff --git a/src/shared/db/schema.ts b/src/shared/db/schema.ts index 703d50f..8f1a09f 100644 --- a/src/shared/db/schema.ts +++ b/src/shared/db/schema.ts @@ -1,2244 +1,31 @@ -import { - mysqlTable, - varchar, - text, - timestamp, - int, - primaryKey, - index, - uniqueIndex, - json, - mysqlEnum, - boolean, - foreignKey, - date, - datetime, - decimal, - bigint -} from "drizzle-orm/mysql-core"; -import { sql } from "drizzle-orm"; -import { createId } from "@paralleldrive/cuid2"; -import type { AdapterAccountType } from "next-auth/adapters"; - -// --- Helper for ID generation (CUID2) --- -const id = (name: string) => varchar(name, { length: 128 }).notNull().$defaultFn(() => createId()); - -// --- 1. Users & Auth (Auth.js v5 Standard + RBAC) --- - -export const users = mysqlTable("users", { - id: id("id").primaryKey(), - name: varchar("name", { length: 255 }), - email: varchar("email", { length: 255 }).notNull().unique(), - emailVerified: timestamp("emailVerified", { mode: "date" }), - image: varchar("image", { length: 255 }), - // Credentials Auth (Optional) - password: varchar("password", { length: 255 }), - - phone: varchar("phone", { length: 30 }), - address: varchar("address", { length: 255 }), - gender: varchar("gender", { length: 20 }), - age: int("age"), - gradeId: varchar("grade_id", { length: 128 }), - departmentId: varchar("department_id", { length: 128 }), - onboardedAt: timestamp("onboarded_at", { mode: "date" }), - - // 未成年人信息保护 - birthDate: date("birth_date"), - guardianName: varchar("guardian_name", { length: 255 }), - guardianPhone: varchar("guardian_phone", { length: 20 }), - guardianRelation: varchar("guardian_relation", { length: 50 }), - consentAcceptedAt: datetime("consent_accepted_at"), - - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - emailIdx: index("email_idx").on(table.email), - gradeIdIdx: index("users_grade_id_idx").on(table.gradeId), - departmentIdIdx: index("users_department_id_idx").on(table.departmentId), -})); - -// Auth.js: Accounts (OAuth providers) -export const accounts = mysqlTable("accounts", { - userId: varchar("userId", { length: 128 }) - .notNull() - .references(() => users.id, { onDelete: "cascade" }), - type: varchar("type", { length: 255 }).$type().notNull(), - provider: varchar("provider", { length: 255 }).notNull(), - providerAccountId: varchar("providerAccountId", { length: 255 }).notNull(), - refresh_token: text("refresh_token"), - access_token: text("access_token"), - expires_at: int("expires_at"), - token_type: varchar("token_type", { length: 255 }), - scope: varchar("scope", { length: 255 }), - id_token: text("id_token"), - session_state: varchar("session_state", { length: 255 }), - }, - (account) => ({ - compoundKey: primaryKey({ - columns: [account.provider, account.providerAccountId], - }), - userIdIdx: index("account_userId_idx").on(account.userId), - }) -); - -/** - * @deprecated audit-P1-9:此表在 JWT 策略下从未被写入数据。 - * - * NextAuth 配置为 `session: { strategy: "jwt" }`(见 `src/auth.ts`), - * 所有 session 信息存储在 JWT cookie 中,不写入此表。 - * `revokeAllOtherSessionsAction` 已改为 no-op(返回 0 并记录日志)。 - * `getUsersDashboardStats` 已不再查询此表。 - * - * 此表定义暂时保留是为了: - * 1. 避免删除表定义后 drizzle-kit 生成 DROP TABLE 迁移误删生产数据 - * 2. 未来若切换为 `strategy: "database"` 或引入 JWT 黑名单可复用 - * - * 后续应在显式迁移中删除此表(同时删除 relations.ts 中的 sessionsRelations)。 - */ -// Auth.js: Sessions -export const sessions = mysqlTable("sessions", { - sessionToken: varchar("sessionToken", { length: 255 }).primaryKey(), - userId: varchar("userId", { length: 128 }) - .notNull() - .references(() => users.id, { onDelete: "cascade" }), - expires: timestamp("expires", { mode: "date" }).notNull(), -}, (table) => ({ - userIdIdx: index("session_userId_idx").on(table.userId), -})); - -// Auth.js: Verification Tokens -export const verificationTokens = mysqlTable("verificationTokens", { - identifier: varchar("identifier", { length: 255 }).notNull(), - token: varchar("token", { length: 255 }).notNull(), - expires: timestamp("expires", { mode: "date" }).notNull(), -}, (vt) => ({ - compoundKey: primaryKey({ columns: [vt.identifier, vt.token] }), -})); - -// --- Custom RBAC Extensions --- - -export const roles = mysqlTable("roles", { - id: id("id").primaryKey(), - name: varchar("name", { length: 50 }).notNull().unique(), // e.g., 'admin', 'teacher', 'student', 'grade_head' - description: varchar("description", { length: 255 }), - // Builtin roles (admin/teacher/student/parent/grade_head/teaching_head) are - // seeded with is_system = true and cannot be deleted. The `admin` role is - // additionally fully locked (no permission edits, no disable, no rename). - isSystem: boolean("is_system").default(false).notNull(), - // Disabled roles do not contribute permissions at login time. - isEnabled: boolean("is_enabled").default(true).notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}); - -// Many-to-Many: Users <-> Roles -// Solves: "A teacher can also be a Grade Head" -export const usersToRoles = mysqlTable("users_to_roles", { - userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - roleId: varchar("role_id", { length: 128 }).notNull().references(() => roles.id, { onDelete: "cascade" }), -}, (table) => ({ - pk: primaryKey({ columns: [table.userId, table.roleId] }), - userIdIdx: index("user_id_idx").on(table.userId), -})); - -// Role -> Permissions (fine-grained RBAC) -export const rolePermissions = mysqlTable("role_permissions", { - roleId: varchar("role_id", { length: 128 }).notNull().references(() => roles.id, { onDelete: "cascade" }), - permission: varchar("permission", { length: 100 }).notNull(), -}, (table) => ({ - pk: primaryKey({ columns: [table.roleId, table.permission] }), - roleIdIdx: index("role_permissions_role_idx").on(table.roleId), -})); - -// --- 2. Knowledge Points (Tree Structure) --- - -export const knowledgePoints = mysqlTable("knowledge_points", { - id: id("id").primaryKey(), - name: varchar("name", { length: 255 }).notNull(), - description: text("description"), - anchorText: varchar("anchor_text", { length: 255 }), - - // Tree Structure: Parent KP - parentId: varchar("parent_id", { length: 128 }), // Self-reference defined in relations - - chapterId: varchar("chapter_id", { length: 128 }), - - // Metadata for ordering or level - level: int("level").default(0), - order: int("order").default(0), - - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - parentIdIdx: index("parent_id_idx").on(table.parentId), - chapterIdIdx: index("kp_chapter_id_idx").on(table.chapterId), -})); - -// --- 知识点前置依赖(知识图谱) --- -export const knowledgePointPrerequisites = mysqlTable("knowledge_point_prerequisites", { - knowledgePointId: varchar("knowledge_point_id", { length: 128 }).notNull(), - prerequisiteKpId: varchar("prerequisite_kp_id", { length: 128 }).notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), -}, (table) => ({ - kpPairPk: primaryKey({ columns: [table.knowledgePointId, table.prerequisiteKpId], name: "kp_prereq_pk" }), - kpIdx: index("kp_prereq_kp_idx").on(table.knowledgePointId), - prereqIdx: index("kp_prereq_prereq_idx").on(table.prerequisiteKpId), - kpFk: foreignKey({ - columns: [table.knowledgePointId], - foreignColumns: [knowledgePoints.id], - name: "kp_prereq_kp_fk", - }).onDelete("cascade"), - prereqFk: foreignKey({ - columns: [table.prerequisiteKpId], - foreignColumns: [knowledgePoints.id], - name: "kp_prereq_prereq_fk", - }).onDelete("cascade"), -})); - -// --- 3. Question Bank (Core) --- - -export const questionTypeEnum = mysqlEnum("type", ["single_choice", "multiple_choice", "text", "judgment", "composite"]); - -export const questions = mysqlTable("questions", { - id: id("id").primaryKey(), - - // Content can be JSON to store rich text, images, etc. or just text. - // Using JSON for flexibility in a modern ed-tech app (e.g. SlateJS nodes). - content: json("content").notNull(), - - // P3-6: 题目内容全文检索文本镜像。 - // - STORED 生成列:每次 INSERT/UPDATE content 时由 MySQL 自动同步 - // - 使用 CAST(content AS CHAR) 把 JSON 序列化为纯文本以支持 FULLTEXT - // - 配合 questions_content_text_ft_idx FULLTEXT 索引(见对应 drizzle 迁移)实现 MATCH AGAINST 检索 - // drizzle-kit 无法生成 FULLTEXT 索引声明,迁移文件中需手动追加 CREATE FULLTEXT INDEX - contentText: text("content_text").generatedAlwaysAs( - sql`CAST(content AS CHAR)`, - { mode: "stored" }, - ), - - type: questionTypeEnum.notNull(), - difficulty: int("difficulty").default(1), // 1-5 - - // Self-reference for "Infinite Nesting" (Parent Question) - // e.g., A reading comprehension passage (Parent) -> 5 sub-questions (Children) - parentId: varchar("parent_id", { length: 128 }), - - authorId: varchar("author_id", { length: 128 }).notNull().references(() => users.id), - - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - parentIdIdx: index("parent_id_idx").on(table.parentId), // Critical for querying children - authorIdIdx: index("author_id_idx").on(table.authorId), - // P3-6: 题目按类型+难度筛选的高频查询索引 - typeDifficultyIdx: index("questions_type_difficulty_idx").on(table.type, table.difficulty), -})); - -// Many-to-Many: Questions <-> Knowledge Points -export const questionsToKnowledgePoints = mysqlTable("questions_to_knowledge_points", { - questionId: varchar("question_id", { length: 128 }).notNull(), - knowledgePointId: varchar("knowledge_point_id", { length: 128 }).notNull(), -}, (table) => ({ - pk: primaryKey({ columns: [table.questionId, table.knowledgePointId] }), - kpIdx: index("kp_idx").on(table.knowledgePointId), // For querying "All questions under this KP" - qFk: foreignKey({ - columns: [table.questionId], - foreignColumns: [questions.id], - name: "q_kp_qid_fk" - }).onDelete("cascade"), - kpFk: foreignKey({ - columns: [table.knowledgePointId], - foreignColumns: [knowledgePoints.id], - name: "q_kp_kpid_fk" - }).onDelete("cascade"), -})); - -// --- 4. Academic / Teaching Flow --- - -export const subjects = mysqlTable("subjects", { - id: id("id").primaryKey(), - name: varchar("name", { length: 100 }).notNull().unique(), - code: varchar("code", { length: 50 }).unique(), - order: int("order").default(0), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - nameIdx: index("subjects_name_idx").on(table.name), -})); - -export const textbooks = mysqlTable("textbooks", { - id: id("id").primaryKey(), - title: varchar("title", { length: 255 }).notNull(), - subject: varchar("subject", { length: 100 }).notNull(), - grade: varchar("grade", { length: 50 }), - publisher: varchar("publisher", { length: 100 }), - - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}); - -export const chapters = mysqlTable("chapters", { - id: id("id").primaryKey(), - textbookId: varchar("textbook_id", { length: 128 }).notNull().references(() => textbooks.id, { onDelete: "cascade" }), - title: varchar("title", { length: 255 }).notNull(), - order: int("order").default(0), - - // Chapters can also be nested - parentId: varchar("parent_id", { length: 128 }), - - content: text("content"), - - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - textbookIdx: index("textbook_idx").on(table.textbookId), - parentIdIdx: index("parent_id_idx").on(table.parentId), -})); - -// --- 5. School Management --- - -export const departments = mysqlTable("departments", { - id: id("id").primaryKey(), - name: varchar("name", { length: 255 }).notNull().unique(), - description: text("description"), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - nameIdx: index("departments_name_idx").on(table.name), -})) - -export const classrooms = mysqlTable("classrooms", { - id: id("id").primaryKey(), - name: varchar("name", { length: 255 }).notNull().unique(), - building: varchar("building", { length: 100 }), - floor: int("floor"), - capacity: int("capacity"), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - nameIdx: index("classrooms_name_idx").on(table.name), -})) - -export const academicYears = mysqlTable("academic_years", { - id: id("id").primaryKey(), - name: varchar("name", { length: 100 }).notNull().unique(), - startDate: timestamp("start_date", { mode: "date" }).notNull(), - endDate: timestamp("end_date", { mode: "date" }).notNull(), - isActive: boolean("is_active").default(false).notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - nameIdx: index("academic_years_name_idx").on(table.name), - activeIdx: index("academic_years_active_idx").on(table.isActive), -})) - -export const schools = mysqlTable("schools", { - id: id("id").primaryKey(), - name: varchar("name", { length: 255 }).notNull().unique(), - code: varchar("code", { length: 50 }).unique(), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - nameIdx: index("schools_name_idx").on(table.name), - codeIdx: index("schools_code_idx").on(table.code), -})) - -export const grades = mysqlTable("grades", { - id: id("id").primaryKey(), - schoolId: varchar("school_id", { length: 128 }).notNull(), - name: varchar("name", { length: 100 }).notNull(), - order: int("order").default(0).notNull(), - gradeHeadId: varchar("grade_head_id", { length: 128 }), - teachingHeadId: varchar("teaching_head_id", { length: 128 }), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - schoolIdx: index("grades_school_idx").on(table.schoolId), - schoolNameUnique: index("grades_school_name_uniq").on(table.schoolId, table.name), - gradeHeadIdx: index("grades_grade_head_idx").on(table.gradeHeadId), - teachingHeadIdx: index("grades_teaching_head_idx").on(table.teachingHeadId), - schoolFk: foreignKey({ - columns: [table.schoolId], - foreignColumns: [schools.id], - name: "g_s_fk", - }).onDelete("cascade"), - gradeHeadFk: foreignKey({ - columns: [table.gradeHeadId], - foreignColumns: [users.id], - name: "g_gh_fk", - }).onDelete("set null"), - teachingHeadFk: foreignKey({ - columns: [table.teachingHeadId], - foreignColumns: [users.id], - name: "g_th_fk", - }).onDelete("set null"), -})) - -// --- 6. Classes / Enrollment / Schedule --- - -export const classEnrollmentStatusEnum = mysqlEnum("class_enrollment_status", ["active", "inactive"]); - -export const classes = mysqlTable("classes", { - id: id("id").primaryKey(), - schoolName: varchar("school_name", { length: 255 }), - schoolId: varchar("school_id", { length: 128 }), - name: varchar("name", { length: 255 }).notNull(), - grade: varchar("grade", { length: 50 }).notNull(), - gradeId: varchar("grade_id", { length: 128 }), - homeroom: varchar("homeroom", { length: 50 }), - room: varchar("room", { length: 50 }), - invitationCode: varchar("invitation_code", { length: 6 }).unique(), - teacherId: varchar("teacher_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - teacherIdx: index("classes_teacher_idx").on(table.teacherId), - gradeIdx: index("classes_grade_idx").on(table.grade), - schoolIdx: index("classes_school_idx").on(table.schoolId), - gradeIdIdx: index("classes_grade_id_idx").on(table.gradeId), - schoolFk: foreignKey({ - columns: [table.schoolId], - foreignColumns: [schools.id], - name: "c_s_fk", - }).onDelete("set null"), - gradeFk: foreignKey({ - columns: [table.gradeId], - foreignColumns: [grades.id], - name: "c_g_fk", - }).onDelete("set null"), -})); - -export const classSubjectTeachers = mysqlTable("class_subject_teachers", { - classId: varchar("class_id", { length: 128 }).notNull(), - subjectId: varchar("subject_id", { length: 128 }).notNull(), - teacherId: varchar("teacher_id", { length: 128 }).references(() => users.id, { onDelete: "set null" }), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - pk: primaryKey({ columns: [table.classId, table.subjectId] }), - classIdx: index("class_subject_teachers_class_idx").on(table.classId), - teacherIdx: index("class_subject_teachers_teacher_idx").on(table.teacherId), - subjectIdIdx: index("class_subject_teachers_subject_id_idx").on(table.subjectId), - classFk: foreignKey({ - columns: [table.classId], - foreignColumns: [classes.id], - name: "cst_c_fk", - }).onDelete("cascade"), - subjectFk: foreignKey({ - columns: [table.subjectId], - foreignColumns: [subjects.id], - name: "cst_s_fk", - }).onDelete("cascade"), -})); - -/** - * 班级邀请码表(v3 新增,对标 Google Classroom / 钉钉教育 / 智学网)。 - * - * 设计要点: - * - 独立表而非挂在 classes 表上,支持有效期/次数/审计/多码并存 - * - 6 位字母数字(剔除歧义字符 0/O/1/I/L),空间 22^6 ≈ 1.13 亿 - * - status 枚举:active/disabled/expired/exhausted - * - max_uses NULL=无限;expires_at NULL=永久 - * - 软删除:revoke 时设置 status=disabled + revoked_at,不物理删除(审计需要) - * - * 与 classes.invitationCode 的关系: - * - 新表上线后,enrollStudentByInvitationCode / enrollTeacherByInvitationCode 优先查新表 - * - classes.invitationCode 保留作为 fallback,下个版本移除 - */ -export const classInvitationCodeStatusEnum = mysqlEnum("class_invitation_code_status", [ - "active", - "disabled", - "expired", - "exhausted", -]); - -export const classInvitationCodes = mysqlTable("class_invitation_codes", { - id: id("id").primaryKey(), - classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }), - code: varchar("code", { length: 8 }).notNull().unique(), - status: classInvitationCodeStatusEnum.default("active").notNull(), - maxUses: int("max_uses"), - usedCount: int("used_count").default(0).notNull(), - expiresAt: timestamp("expires_at"), - createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), - revokedAt: timestamp("revoked_at"), - revokedBy: varchar("revoked_by", { length: 128 }), - note: varchar("note", { length: 255 }), -}, (table) => ({ - codeIdx: uniqueIndex("class_invitation_codes_code_idx").on(table.code), - classIdx: index("class_invitation_codes_class_idx").on(table.classId), - statusExpiresIdx: index("class_invitation_codes_status_expires_idx").on(table.status, table.expiresAt), - classFk: foreignKey({ - columns: [table.classId], - foreignColumns: [classes.id], - name: "cic_c_fk", - }).onDelete("cascade"), -})); - -export const classEnrollments = mysqlTable("class_enrollments", { - classId: varchar("class_id", { length: 128 }).notNull(), - studentId: varchar("student_id", { length: 128 }).notNull(), - status: classEnrollmentStatusEnum.default("active").notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), -}, (table) => ({ - pk: primaryKey({ columns: [table.classId, table.studentId] }), - classIdx: index("class_enrollments_class_idx").on(table.classId), - studentIdx: index("class_enrollments_student_idx").on(table.studentId), - classFk: foreignKey({ - columns: [table.classId], - foreignColumns: [classes.id], - name: "ce_c_fk", - }).onDelete("cascade"), - studentFk: foreignKey({ - columns: [table.studentId], - foreignColumns: [users.id], - name: "ce_s_fk", - }).onDelete("cascade"), -})); - -export const classSchedule = mysqlTable("class_schedule", { - id: id("id").primaryKey(), - classId: varchar("class_id", { length: 128 }).notNull(), - weekday: int("weekday").notNull(), - startTime: varchar("start_time", { length: 5 }).notNull(), - endTime: varchar("end_time", { length: 5 }).notNull(), - course: varchar("course", { length: 255 }).notNull(), - location: varchar("location", { length: 100 }), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - classIdx: index("class_schedule_class_idx").on(table.classId), - classDayIdx: index("class_schedule_class_day_idx").on(table.classId, table.weekday), - classFk: foreignKey({ - columns: [table.classId], - foreignColumns: [classes.id], - name: "cs_c_fk", - }).onDelete("cascade"), -})); - -// --- 7. Exam Proctoring Config (考试监考配置) --- - -export const examModeEnum = mysqlEnum("exam_mode", ["homework", "timed", "proctored"]); -export const proctoringEventTypeEnum = mysqlEnum("event_type", [ - "tab_switch", "window_blur", "copy_attempt", "paste_attempt", - "right_click", "devtools_open", "fullscreen_exit", "idle_timeout" -]); - -export const exams = mysqlTable("exams", { - id: id("id").primaryKey(), - title: varchar("title", { length: 255 }).notNull(), - description: text("description"), - - /** - * Stores the hierarchical structure of the exam. - * Expected JSON Schema (3-level: section > group > question): - * type ExamStructure = Array< - * | { type: 'section', title: string, level?: number, children: ExamStructure } - * | { type: 'group', title: string, instruction?: string, children: ExamStructure } - * | { type: 'question', questionId: string, score: number } - * > - * - * Note: This is for UI presentation/ordering. - * Real relational integrity is maintained in 'exam_questions' table. - */ - structure: json("structure"), - - creatorId: varchar("creator_id", { length: 128 }).notNull().references(() => users.id), - - // Link to Subjects and Grades - subjectId: varchar("subject_id", { length: 128 }).references(() => subjects.id), - gradeId: varchar("grade_id", { length: 128 }).references(() => grades.id), - - startTime: timestamp("start_time"), - endTime: timestamp("end_time"), - - // P2: Online exam mode + proctoring settings - examMode: examModeEnum.default("homework"), - durationMinutes: int("duration_minutes"), - shuffleQuestions: boolean("shuffle_questions").default(false), - allowLateStart: boolean("allow_late_start").default(false), - lateStartGraceMinutes: int("late_start_grace_minutes").default(0), - antiCheatEnabled: boolean("anti_cheat_enabled").default(false), - - // Status: draft, published, ongoing, finished - status: varchar("status", { length: 50 }).default("draft"), - - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - subjectIdx: index("exams_subject_idx").on(table.subjectId), - gradeIdx: index("exams_grade_idx").on(table.gradeId), - // P3-6: 考试状态按时间排序、按创建者筛选的高频查询索引 - statusCreatedIdx: index("exams_status_created_idx").on(table.status, table.createdAt), - creatorIdx: index("exams_creator_idx").on(table.creatorId), -})); - -// Linking Exams to Questions (Many-to-Many often, or One-to-Many if specific to exam) -// Usually questions are reused, so Many-to-Many -export const examQuestions = mysqlTable("exam_questions", { - examId: varchar("exam_id", { length: 128 }).notNull().references(() => exams.id, { onDelete: "cascade" }), - questionId: varchar("question_id", { length: 128 }).notNull().references(() => questions.id, { onDelete: "cascade" }), - score: int("score").default(0), - order: int("order").default(0), -}, (table) => ({ - pk: primaryKey({ columns: [table.examId, table.questionId] }), -})); - -export const examSubmissions = mysqlTable("exam_submissions", { - id: id("id").primaryKey(), - examId: varchar("exam_id", { length: 128 }).notNull().references(() => exams.id, { onDelete: "cascade" }), - studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - - score: int("score"), // Total score - status: varchar("status", { length: 50 }).default("started"), // started, submitted, graded - - submittedAt: timestamp("submitted_at"), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - examStudentIdx: index("exam_student_idx").on(table.examId, table.studentId), - // P3-6: 提交记录按考试状态筛选、按提交时间排序的高频查询索引 - examStatusIdx: index("exam_submissions_exam_status_idx").on(table.examId, table.status), - submittedAtIdx: index("exam_submissions_submitted_at_idx").on(table.submittedAt), -})); - -export const submissionAnswers = mysqlTable("submission_answers", { - id: id("id").primaryKey(), - submissionId: varchar("submission_id", { length: 128 }).notNull().references(() => examSubmissions.id, { onDelete: "cascade" }), - questionId: varchar("question_id", { length: 128 }).notNull().references(() => questions.id), - - answerContent: json("answer_content"), // Student's answer - score: int("score"), // Score for this specific question - feedback: text("feedback"), // Teacher's feedback - - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - submissionIdx: index("submission_idx").on(table.submissionId), -})); - -export const homeworkAssignments = mysqlTable("homework_assignments", { - id: id("id").primaryKey(), - sourceExamId: varchar("source_exam_id", { length: 128 }), - title: varchar("title", { length: 255 }).notNull(), - description: text("description"), - structure: json("structure"), - status: varchar("status", { length: 50 }).default("draft"), - creatorId: varchar("creator_id", { length: 128 }).notNull(), - availableAt: timestamp("available_at"), - dueAt: timestamp("due_at"), - allowLate: boolean("allow_late").default(false).notNull(), - lateDueAt: timestamp("late_due_at"), - maxAttempts: int("max_attempts").default(1).notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - creatorIdx: index("hw_assignment_creator_idx").on(table.creatorId), - sourceExamIdx: index("hw_assignment_source_exam_idx").on(table.sourceExamId), - statusIdx: index("hw_assignment_status_idx").on(table.status), - sourceExamFk: foreignKey({ - columns: [table.sourceExamId], - foreignColumns: [exams.id], - name: "hw_asg_exam_fk", - }).onDelete("cascade"), - creatorFk: foreignKey({ - columns: [table.creatorId], - foreignColumns: [users.id], - name: "hw_asg_creator_fk", - }).onDelete("cascade"), -})); - -export const homeworkAssignmentQuestions = mysqlTable("homework_assignment_questions", { - assignmentId: varchar("assignment_id", { length: 128 }).notNull(), - questionId: varchar("question_id", { length: 128 }).notNull(), - score: int("score").default(0), - order: int("order").default(0), -}, (table) => ({ - pk: primaryKey({ columns: [table.assignmentId, table.questionId] }), - assignmentIdx: index("hw_assignment_questions_assignment_idx").on(table.assignmentId), - assignmentFk: foreignKey({ - columns: [table.assignmentId], - foreignColumns: [homeworkAssignments.id], - name: "hw_aq_a_fk", - }).onDelete("cascade"), - questionFk: foreignKey({ - columns: [table.questionId], - foreignColumns: [questions.id], - name: "hw_aq_q_fk", - }).onDelete("cascade"), -})); - -export const homeworkAssignmentTargets = mysqlTable("homework_assignment_targets", { - assignmentId: varchar("assignment_id", { length: 128 }).notNull(), - studentId: varchar("student_id", { length: 128 }).notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), -}, (table) => ({ - pk: primaryKey({ columns: [table.assignmentId, table.studentId] }), - assignmentIdx: index("hw_assignment_targets_assignment_idx").on(table.assignmentId), - studentIdx: index("hw_assignment_targets_student_idx").on(table.studentId), - assignmentFk: foreignKey({ - columns: [table.assignmentId], - foreignColumns: [homeworkAssignments.id], - name: "hw_at_a_fk", - }).onDelete("cascade"), - studentFk: foreignKey({ - columns: [table.studentId], - foreignColumns: [users.id], - name: "hw_at_s_fk", - }).onDelete("cascade"), -})); - -export const homeworkSubmissions = mysqlTable("homework_submissions", { - id: id("id").primaryKey(), - assignmentId: varchar("assignment_id", { length: 128 }).notNull(), - studentId: varchar("student_id", { length: 128 }).notNull(), - attemptNo: int("attempt_no").default(1).notNull(), - score: int("score"), - status: varchar("status", { length: 50 }).default("started"), - startedAt: timestamp("started_at").defaultNow().notNull(), - submittedAt: timestamp("submitted_at"), - isLate: boolean("is_late").default(false).notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - assignmentStudentIdx: index("hw_assignment_student_idx").on(table.assignmentId, table.studentId), - assignmentFk: foreignKey({ - columns: [table.assignmentId], - foreignColumns: [homeworkAssignments.id], - name: "hw_sub_a_fk", - }).onDelete("cascade"), - studentFk: foreignKey({ - columns: [table.studentId], - foreignColumns: [users.id], - name: "hw_sub_student_fk", - }).onDelete("cascade"), -})); - -export const homeworkAnswers = mysqlTable("homework_answers", { - id: id("id").primaryKey(), - submissionId: varchar("submission_id", { length: 128 }).notNull(), - questionId: varchar("question_id", { length: 128 }).notNull(), - answerContent: json("answer_content"), - score: int("score"), - feedback: text("feedback"), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - submissionIdx: index("hw_answer_submission_idx").on(table.submissionId), - submissionQuestionIdx: index("hw_answer_submission_question_idx").on(table.submissionId, table.questionId), - submissionFk: foreignKey({ - columns: [table.submissionId], - foreignColumns: [homeworkSubmissions.id], - name: "hw_ans_sub_fk", - }).onDelete("cascade"), - questionFk: foreignKey({ - columns: [table.questionId], - foreignColumns: [questions.id], - name: "hw_ans_q_fk", - }), -})); - -export const aiProviderVisibilityEnum = mysqlEnum("visibility", ["public", "private"]); - -export const aiProviders = mysqlTable("ai_providers", { - id: id("id").primaryKey(), - provider: mysqlEnum("provider", ["zhipu", "openai", "gemini", "custom", "ollama"]).notNull(), - baseUrl: varchar("base_url", { length: 512 }), - model: varchar("model", { length: 128 }).notNull(), - apiKeyEncrypted: text("api_key_encrypted").notNull(), - apiKeyLast4: varchar("api_key_last4", { length: 4 }), - isDefault: boolean("is_default").default(false).notNull(), - // V3: 可见性 — public 由管理员发布,全员可用;private 仅创建者可见 - visibility: aiProviderVisibilityEnum.default("private").notNull(), - createdBy: varchar("created_by", { length: 128 }), - updatedBy: varchar("updated_by", { length: 128 }), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - providerIdx: index("ai_provider_idx").on(table.provider), - defaultIdx: index("ai_provider_default_idx").on(table.isDefault), - visibilityIdx: index("ai_provider_visibility_idx").on(table.visibility), - createdByIdx: index("ai_provider_created_by_idx").on(table.createdBy), -})); - -// --- 8. Announcements --- - -export const announcementTypeEnum = mysqlEnum("type", ["school", "grade", "class"]); -export const announcementStatusEnum = mysqlEnum("status", ["draft", "published", "archived"]); - -export const announcements = mysqlTable("announcements", { - id: id("id").primaryKey(), - title: varchar("title", { length: 255 }).notNull(), - content: text("content").notNull(), - type: announcementTypeEnum.default("school").notNull(), - status: announcementStatusEnum.default("draft").notNull(), - targetGradeId: varchar("target_grade_id", { length: 128 }), - targetClassId: varchar("target_class_id", { length: 128 }), - authorId: varchar("author_id", { length: 128 }).notNull().references(() => users.id), - publishedAt: datetime("published_at", { mode: "date" }), - // V2-P2-13d: 公告置顶(置顶公告在列表中优先显示) - isPinned: boolean("is_pinned").default(false).notNull(), - createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), - updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - authorIdx: index("announcements_author_idx").on(table.authorId), - statusIdx: index("announcements_status_idx").on(table.status), - typeIdx: index("announcements_type_idx").on(table.type), - targetGradeIdx: index("announcements_target_grade_idx").on(table.targetGradeId), - targetClassIdx: index("announcements_target_class_idx").on(table.targetClassId), - // V2-P2-13d: 置顶索引 - statusPinnedIdx: index("announcements_status_pinned_idx").on(table.status, table.isPinned), -})); - -// --- 8b. Announcement Reads (公告已读回执) --- - -export const announcementReads = mysqlTable("announcement_reads", { - id: id("id").primaryKey(), - announcementId: varchar("announcement_id", { length: 128 }).notNull().references(() => announcements.id, { onDelete: "cascade" }), - userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - readAt: timestamp("read_at", { mode: "date" }).defaultNow().notNull(), -}, (table) => ({ - announcementIdx: index("announcement_reads_announcement_idx").on(table.announcementId), - userIdx: index("announcement_reads_user_idx").on(table.userId), - // 唯一约束:一个用户对一条公告只能有一条已读记录 - uniqueAnnouncementUser: uniqueIndex("announcement_reads_unique_idx").on(table.announcementId, table.userId), -})); - -// --- 9. Audit & Login Logs --- - -export const auditLogStatusEnum = mysqlEnum("status", ["success", "failure"]); - -export const auditLogs = mysqlTable("audit_logs", { - id: id("id").primaryKey(), - userId: varchar("user_id", { length: 128 }).notNull(), - userName: varchar("user_name", { length: 255 }).notNull(), - action: varchar("action", { length: 255 }).notNull(), - module: varchar("module", { length: 128 }).notNull(), - targetId: varchar("target_id", { length: 128 }), - targetType: varchar("target_type", { length: 128 }), - detail: text("detail"), - ipAddress: varchar("ip_address", { length: 45 }), - userAgent: varchar("user_agent", { length: 512 }), - status: auditLogStatusEnum.default("success").notNull(), - createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), -}, (table) => ({ - userIdIdx: index("audit_logs_user_id_idx").on(table.userId), - moduleIdx: index("audit_logs_module_idx").on(table.module), - actionIdx: index("audit_logs_action_idx").on(table.action), - statusIdx: index("audit_logs_status_idx").on(table.status), - createdAtIdx: index("audit_logs_created_at_idx").on(table.createdAt), -})); - -export const loginLogActionEnum = mysqlEnum("action", ["signin", "signout", "signup"]); -export const loginLogStatusEnum = mysqlEnum("status", ["success", "failure"]); - -export const loginLogs = mysqlTable("login_logs", { - id: id("id").primaryKey(), - userId: varchar("user_id", { length: 128 }), - userEmail: varchar("user_email", { length: 255 }).notNull(), - action: loginLogActionEnum.notNull(), - status: loginLogStatusEnum.default("success").notNull(), - ipAddress: varchar("ip_address", { length: 45 }), - userAgent: varchar("user_agent", { length: 512 }), - errorMessage: text("error_message"), - createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), -}, (table) => ({ - userIdIdx: index("login_logs_user_id_idx").on(table.userId), - userEmailIdx: index("login_logs_user_email_idx").on(table.userEmail), - actionIdx: index("login_logs_action_idx").on(table.action), - statusIdx: index("login_logs_status_idx").on(table.status), - createdAtIdx: index("login_logs_created_at_idx").on(table.createdAt), -})); - -// --- 9b. Invitation Codes (注册邀请码,audit-P2-3 新增) --- - -/** - * 注册邀请码表(audit-P2-3) - * - * 管理员批量生成,用于: - * - 预分配注册角色(student/teacher/parent/admin) - * - 可选绑定邮箱(限定指定用户使用) - * - 可选绑定班级(注册后自动加入班级) - * - 可选过期时间 - */ -export const invitationCodes = mysqlTable("invitation_codes", { - id: id("id").primaryKey(), - /** 邀请码明文(8-12 位大写字母+数字组合,unique) */ - code: varchar("code", { length: 64 }).notNull().unique(), - /** 可选:限定邮箱使用(未设置则任意邮箱可用) */ - email: varchar("email", { length: 255 }), - /** 注册后分配的角色(student/teacher/parent/admin) */ - role: varchar("role", { length: 50 }).notNull(), - /** 可选:注册后自动加入的班级 ID */ - classId: varchar("class_id", { length: 128 }), - /** 可选:管理员备注 */ - notes: varchar("notes", { length: 500 }), - createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), - /** 创建者(管理员 userId) */ - createdBy: varchar("created_by", { length: 128 }), - /** 可选:过期时间(未设置则永久有效) */ - expiresAt: timestamp("expires_at", { mode: "date" }), - /** 已使用者 userId(未使用为 null) */ - usedBy: varchar("used_by", { length: 128 }), - /** 使用时间(未使用为 null) */ - usedAt: timestamp("used_at", { mode: "date" }), -}, (table) => ({ - codeIdx: index("invitation_codes_code_idx").on(table.code), - emailIdx: index("invitation_codes_email_idx").on(table.email), - usedByIdx: index("invitation_codes_used_by_idx").on(table.usedBy), -})); - -// --- 10. Data Change Logs (数据变更日志) --- - -export const dataChangeLogActionEnum = mysqlEnum("action", ["create", "update", "delete"]); - -export const dataChangeLogs = mysqlTable("data_change_logs", { - id: varchar("id", { length: 128 }).primaryKey(), - tableName: varchar("table_name", { length: 128 }).notNull(), - recordId: varchar("record_id", { length: 128 }).notNull(), - action: dataChangeLogActionEnum.notNull(), - oldValue: text("old_value"), - newValue: text("new_value"), - changedBy: varchar("changed_by", { length: 128 }).notNull(), - changedByName: varchar("changed_by_name", { length: 255 }).notNull(), - ipAddress: varchar("ip_address", { length: 45 }), - createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), -}, (table) => ({ - tableNameIdx: index("data_change_logs_table_name_idx").on(table.tableName), - recordIdIdx: index("data_change_logs_record_id_idx").on(table.recordId), - actionIdx: index("data_change_logs_action_idx").on(table.action), - changedByIdx: index("data_change_logs_changed_by_idx").on(table.changedBy), - createdAtIdx: index("data_change_logs_created_at_idx").on(table.createdAt), -})); - -// Re-export old courses table if needed or deprecate it. -// Assuming we are replacing the old simple schema with this robust one. -// But if there were existing tables, we might keep them or comment them out. -// For this task, I will overwrite completely as this is a "System Architect" redesign. - -// --- 11. Grade Records (成绩录入) --- - -export const gradeRecordTypeEnum = mysqlEnum("type", ["exam", "quiz", "homework", "other"]); -export const gradeRecordSemesterEnum = mysqlEnum("semester", ["1", "2"]); - -export const gradeRecords = mysqlTable("grade_records", { - id: id("id").primaryKey(), - studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }), - subjectId: varchar("subject_id", { length: 128 }).notNull().references(() => subjects.id, { onDelete: "cascade" }), - examId: varchar("exam_id", { length: 128 }), - academicYearId: varchar("academic_year_id", { length: 128 }), - title: varchar("title", { length: 255 }).notNull(), - score: decimal("score", { precision: 6, scale: 2 }).notNull(), - fullScore: decimal("full_score", { precision: 6, scale: 2 }).default("100").notNull(), - type: gradeRecordTypeEnum.default("exam").notNull(), - semester: gradeRecordSemesterEnum.default("1").notNull(), - recordedBy: varchar("recorded_by", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - remark: text("remark"), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - studentIdx: index("grade_records_student_idx").on(table.studentId), - classIdx: index("grade_records_class_idx").on(table.classId), - subjectIdx: index("grade_records_subject_idx").on(table.subjectId), - examIdx: index("grade_records_exam_idx").on(table.examId), - classSubjectIdx: index("grade_records_class_subject_idx").on(table.classId, table.subjectId), - recordedByIdx: index("grade_records_recorded_by_idx").on(table.recordedBy), - classFk: foreignKey({ - columns: [table.classId], - foreignColumns: [classes.id], - name: "gr_c_fk", - }).onDelete("cascade"), - studentFk: foreignKey({ - columns: [table.studentId], - foreignColumns: [users.id], - name: "gr_s_fk", - }).onDelete("cascade"), - subjectFk: foreignKey({ - columns: [table.subjectId], - foreignColumns: [subjects.id], - name: "gr_sub_fk", - }).onDelete("cascade"), - recordedByFk: foreignKey({ - columns: [table.recordedBy], - foreignColumns: [users.id], - name: "gr_rb_fk", - }).onDelete("cascade"), -})); - -// --- 11.1 Grade Record Answers (成绩每题得分) --- - -export const gradeRecordAnswers = mysqlTable("grade_record_answers", { - id: id("id").primaryKey(), - gradeRecordId: varchar("grade_record_id", { length: 128 }).notNull().references(() => gradeRecords.id, { onDelete: "cascade" }), - questionId: varchar("question_id", { length: 128 }).notNull().references(() => questions.id, { onDelete: "cascade" }), - score: decimal("score", { precision: 6, scale: 2 }).notNull(), - fullScore: decimal("full_score", { precision: 6, scale: 2 }).notNull(), - feedback: text("feedback"), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - gradeRecordIdx: index("grade_record_answers_record_idx").on(table.gradeRecordId), - questionIdx: index("grade_record_answers_question_idx").on(table.questionId), - gradeRecordFk: foreignKey({ - columns: [table.gradeRecordId], - foreignColumns: [gradeRecords.id], - name: "gra_gr_fk", - }).onDelete("cascade"), - questionFk: foreignKey({ - columns: [table.questionId], - foreignColumns: [questions.id], - name: "gra_q_fk", - }).onDelete("cascade"), -})); - -// --- 12. File Attachments (文件附件) --- - -export const fileAttachments = mysqlTable("file_attachments", { - id: varchar("id", { length: 128 }).primaryKey(), - filename: varchar("filename", { length: 255 }).notNull(), - originalName: varchar("original_name", { length: 255 }).notNull(), - mimeType: varchar("mime_type", { length: 128 }).notNull(), - size: bigint("size", { mode: "number" }).notNull(), - storagePath: varchar("storage_path", { length: 512 }).notNull(), - url: varchar("url", { length: 512 }), - uploaderId: varchar("uploader_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - targetType: varchar("target_type", { length: 128 }), - targetId: varchar("target_id", { length: 128 }), - createdAt: timestamp("created_at").defaultNow().notNull(), -}, (table) => ({ - uploaderIdx: index("file_attachments_uploader_idx").on(table.uploaderId), - targetIdx: index("file_attachments_target_idx").on(table.targetType, table.targetId), - createdAtIdx: index("file_attachments_created_at_idx").on(table.createdAt), -})); - -// --- 13. Course Plans (课程计划) --- - -export const coursePlanStatusEnum = mysqlEnum("status", ["planning", "active", "completed", "paused"]); -export const coursePlanSemesterEnum = mysqlEnum("semester", ["1", "2"]); - -export const coursePlans = mysqlTable("course_plans", { - id: id("id").primaryKey(), - classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }), - subjectId: varchar("subject_id", { length: 128 }).notNull().references(() => subjects.id, { onDelete: "cascade" }), - teacherId: varchar("teacher_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - academicYearId: varchar("academic_year_id", { length: 128 }), - semester: coursePlanSemesterEnum.default("1").notNull(), - totalHours: int("total_hours").default(0).notNull(), - completedHours: int("completed_hours").default(0).notNull(), - weeklyHours: int("weekly_hours").default(0).notNull(), - startDate: date("start_date"), - endDate: date("end_date"), - syllabus: text("syllabus"), - objectives: text("objectives"), - status: coursePlanStatusEnum.default("planning").notNull(), - createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - classIdx: index("course_plans_class_idx").on(table.classId), - teacherIdx: index("course_plans_teacher_idx").on(table.teacherId), - subjectIdx: index("course_plans_subject_idx").on(table.subjectId), - statusIdx: index("course_plans_status_idx").on(table.status), - classSubjectIdx: index("course_plans_class_subject_idx").on(table.classId, table.subjectId), - classFk: foreignKey({ - columns: [table.classId], - foreignColumns: [classes.id], - name: "cp_c_fk", - }).onDelete("cascade"), - subjectFk: foreignKey({ - columns: [table.subjectId], - foreignColumns: [subjects.id], - name: "cp_s_fk", - }).onDelete("cascade"), - teacherFk: foreignKey({ - columns: [table.teacherId], - foreignColumns: [users.id], - name: "cp_t_fk", - }).onDelete("cascade"), - createdByFk: foreignKey({ - columns: [table.createdBy], - foreignColumns: [users.id], - name: "cp_cb_fk", - }).onDelete("cascade"), -})); - -export const coursePlanItems = mysqlTable("course_plan_items", { - id: id("id").primaryKey(), - planId: varchar("plan_id", { length: 128 }).notNull().references(() => coursePlans.id, { onDelete: "cascade" }), - week: int("week").notNull(), - topic: varchar("topic", { length: 255 }).notNull(), - content: text("content"), - hours: int("hours").default(2).notNull(), - textbookChapter: varchar("textbook_chapter", { length: 255 }), - notes: text("notes"), - isCompleted: boolean("is_completed").default(false).notNull(), - completedAt: date("completed_at"), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - planIdx: index("course_plan_items_plan_idx").on(table.planId), - planWeekIdx: index("course_plan_items_plan_week_idx").on(table.planId, table.week), - planFk: foreignKey({ - columns: [table.planId], - foreignColumns: [coursePlans.id], - name: "cpi_p_fk", - }).onDelete("cascade"), -})); - -// --- 14. Messages (站内消息) --- - -export const messages = mysqlTable("messages", { - id: id("id").primaryKey(), - senderId: varchar("sender_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - receiverId: varchar("receiver_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - subject: varchar("subject", { length: 255 }), - content: text("content").notNull(), - isRead: boolean("is_read").default(false).notNull(), - readAt: timestamp("read_at", { mode: "date" }), - parentMessageId: varchar("parent_message_id", { length: 128 }), // 回复链 - // 软删除:发送方/接收方各自独立删除,互不影响 - senderDeletedAt: timestamp("sender_deleted_at", { mode: "date" }), - receiverDeletedAt: timestamp("receiver_deleted_at", { mode: "date" }), - // V2-P2-13c: 消息星标(接收方可标记重要消息) - isStarred: boolean("is_starred").default(false).notNull(), - // P2-4: 消息撤回(发送方在 2 分钟窗口内可撤回;非空即表示已撤回) - recalledAt: timestamp("recalled_at", { mode: "date" }), - // P2-2: 群组消息 ID(fan-out on write:教师群发时每条消息共享同一 groupMessageId) - groupMessageId: varchar("group_message_id", { length: 128 }), - createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), -}, (table) => ({ - senderIdx: index("messages_sender_idx").on(table.senderId), - receiverIdx: index("messages_receiver_idx").on(table.receiverId), - isReadIdx: index("messages_is_read_idx").on(table.isRead), - parentIdx: index("messages_parent_idx").on(table.parentMessageId), - receiverReadIdx: index("messages_receiver_read_idx").on(table.receiverId, table.isRead), - // V2-P2-13c: 星标索引 - receiverStarredIdx: index("messages_receiver_starred_idx").on(table.receiverId, table.isStarred), - // P2-2: 群组消息索引(按 groupMessageId 聚合查询) - groupMessageIdx: index("messages_group_message_idx").on(table.groupMessageId), -})); - -// --- 14b. Message Drafts (消息草稿) --- - -export const messageDrafts = mysqlTable("message_drafts", { - id: id("id").primaryKey(), - userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - receiverId: varchar("receiver_id", { length: 128 }).references(() => users.id, { onDelete: "cascade" }), - subject: varchar("subject", { length: 255 }), - content: text("content"), - parentMessageId: varchar("parent_message_id", { length: 128 }), - // P2-10: 跨设备草稿同步 - 设备 ID(标识来源设备) - deviceId: varchar("device_id", { length: 128 }), - // P2-10: 跨设备草稿同步 - 版本号(乐观锁,冲突检测) - version: int("version").default(1).notNull(), - updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().onUpdateNow().notNull(), - createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), -}, (table) => ({ - userIdx: index("message_drafts_user_idx").on(table.userId), - userUpdatedIdx: index("message_drafts_user_updated_idx").on(table.userId, table.updatedAt), - // P2-10: 支持按 userId + parentMessageId 查询最新版本草稿 - userParentIdx: index("message_drafts_user_parent_idx").on(table.userId, table.parentMessageId), -})); - -// --- 14c. Message Templates (快捷回复模板, P2-6) --- - -export const messageTemplates = mysqlTable("message_templates", { - id: id("id").primaryKey(), - userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - title: varchar("title", { length: 100 }).notNull(), - content: text("content").notNull(), - // 排序权重(越小越靠前),默认 0 - sortOrder: int("sort_order").default(0).notNull(), - updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().onUpdateNow().notNull(), - createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), -}, (table) => ({ - userIdx: index("message_templates_user_idx").on(table.userId), - userSortIdx: index("message_templates_user_sort_idx").on(table.userId, table.sortOrder), -})); - -// --- 14d. Message Reports (消息举报, P2-5) --- - -export const messageReports = mysqlTable("message_reports", { - id: id("id").primaryKey(), - // 举报人 - reporterId: varchar("reporter_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - // 被举报消息 ID - messageId: varchar("message_id", { length: 128 }).notNull().references(() => messages.id, { onDelete: "cascade" }), - // 被举报人(冗余,便于按被举报人查询) - reportedUserId: varchar("reported_user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - // 举报理由分类:spam / harassment / inappropriate / other - reason: varchar("reason", { length: 50 }).notNull(), - // 详细描述(可选) - description: text("description"), - // 举报状态:pending / reviewed / dismissed / actioned - status: varchar("status", { length: 20 }).notNull().default("pending"), - createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), - updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - reporterIdx: index("message_reports_reporter_idx").on(table.reporterId), - messageIdx: index("message_reports_message_idx").on(table.messageId), - reportedUserIdx: index("message_reports_reported_user_idx").on(table.reportedUserId), - statusIdx: index("message_reports_status_idx").on(table.status), -})); - -// --- 14e. User Blocks (用户屏蔽, P2-5) --- - -export const userBlocks = mysqlTable("user_blocks", { - id: id("id").primaryKey(), - // 屏蔽发起人 - blockerId: varchar("blocker_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - // 被屏蔽人 - blockedId: varchar("blocked_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), -}, (table) => ({ - // 同一对 (blocker, blocked) 只能存在一条记录 - blockerBlockedUnique: uniqueIndex("user_blocks_blocker_blocked_uniq").on(table.blockerId, table.blockedId), - blockerIdx: index("user_blocks_blocker_idx").on(table.blockerId), -})); - -// --- 15. Message Notifications (消息通知) --- - -export const messageNotifications = mysqlTable("message_notifications", { - id: id("id").primaryKey(), - userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - type: varchar("type", { length: 128 }).notNull(), // "message", "announcement", "homework", "grade", "diagnostic" - title: varchar("title", { length: 255 }).notNull(), - content: text("content"), - link: varchar("link", { length: 512 }), - isRead: boolean("is_read").default(false).notNull(), - // V2-P2-13b: 通知优先级(low/normal/high/urgent),默认 normal - priority: varchar("priority", { length: 16 }).default("normal").notNull(), - // V2-P2-13b: 通知归档标记,归档后不在默认列表显示 - isArchived: boolean("is_archived").default(false).notNull(), - createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), -}, (table) => ({ - userIdx: index("message_notifications_user_idx").on(table.userId), - isReadIdx: index("message_notifications_is_read_idx").on(table.isRead), - userReadIdx: index("message_notifications_user_read_idx").on(table.userId, table.isRead), - createdAtIdx: index("message_notifications_created_at_idx").on(table.createdAt), - // V2-P2-13b: 新增优先级和归档索引 - priorityIdx: index("message_notifications_priority_idx").on(table.priority), - userArchivedIdx: index("message_notifications_user_archived_idx").on(table.userId, table.isArchived), -})); - -// --- 17. Notification Logs (通知发送日志) --- - -export const notificationLogs = mysqlTable("notification_logs", { - id: id("id").primaryKey(), - // 关联用户(通知接收人) - userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - // 通知标题(冗余存储,便于查询) - title: varchar("title", { length: 255 }).notNull(), - // 发送渠道: in_app | email | sms | wechat - channel: varchar("channel", { length: 32 }).notNull(), - // 发送状态: success | failure - status: varchar("status", { length: 16 }).notNull(), - // 渠道返回的消息 ID(用于追踪) - messageId: varchar("message_id", { length: 255 }), - // 失败时的错误信息 - error: text("error"), - // 发送时间 - sentAt: timestamp("sent_at", { mode: "date" }).defaultNow().notNull(), -}, (table) => ({ - userIdx: index("notification_logs_user_idx").on(table.userId), - channelIdx: index("notification_logs_channel_idx").on(table.channel), - statusIdx: index("notification_logs_status_idx").on(table.status), - sentAtIdx: index("notification_logs_sent_at_idx").on(table.sentAt), -})); - -// --- 16. Notification Preferences (通知偏好) --- - -export const notificationPreferences = mysqlTable("notification_preferences", { - id: varchar("id", { length: 128 }).primaryKey(), - userId: varchar("user_id", { length: 128 }).notNull().unique().references(() => users.id, { onDelete: "cascade" }), - emailEnabled: boolean("email_enabled").default(false).notNull(), - smsEnabled: boolean("sms_enabled").default(false).notNull(), - pushEnabled: boolean("push_enabled").default(true).notNull(), - homeworkNotifications: boolean("homework_notifications").default(true).notNull(), - gradeNotifications: boolean("grade_notifications").default(true).notNull(), - announcementNotifications: boolean("announcement_notifications").default(true).notNull(), - messageNotifications: boolean("message_notifications").default(true).notNull(), - attendanceNotifications: boolean("attendance_notifications").default(true).notNull(), - // 免打扰时段(格式 "HH:mm",如 "22:00") - quietHoursEnabled: boolean("quiet_hours_enabled").default(false).notNull(), - quietHoursStart: varchar("quiet_hours_start", { length: 5 }), - quietHoursEnd: varchar("quiet_hours_end", { length: 5 }), - createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), - updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - userIdIdx: index("notification_preferences_user_idx").on(table.userId), - userFk: foreignKey({ - columns: [table.userId], - foreignColumns: [users.id], - name: "np_u_fk", - }).onDelete("cascade"), -})); - -// --- 17. Parent-Student Relations (家长-子女关联) --- - -export const parentStudentRelations = mysqlTable("parent_student_relations", { - id: varchar("id", { length: 128 }).primaryKey().$defaultFn(() => createId()), - parentId: varchar("parent_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - relation: varchar("relation", { length: 50 }), // 父亲/母亲/其他 - createdAt: timestamp("created_at").defaultNow().notNull(), -}, (table) => ({ - parentIdx: index("parent_student_relations_parent_idx").on(table.parentId), - studentIdx: index("parent_student_relations_student_idx").on(table.studentId), - parentFk: foreignKey({ - columns: [table.parentId], - foreignColumns: [users.id], - name: "psr_p_fk", - }).onDelete("cascade"), - studentFk: foreignKey({ - columns: [table.studentId], - foreignColumns: [users.id], - name: "psr_s_fk", - }).onDelete("cascade"), -})); - -// --- 18. Attendance (考勤管理) --- - -export const attendanceStatusEnum = mysqlEnum("status", ["present", "absent", "late", "early_leave", "excused", "school_activity"]); - -/** - * L-6 节次考勤:节次枚举。 - * - morning_reading: 早读 - * - morning: 上午 - * - afternoon: 下午 - * - evening: 晚自习 - * - full_day: 全日(默认值,向后兼容:null 也表示全日) - */ -export const attendancePeriodEnum = mysqlEnum("period", ["morning_reading", "morning", "afternoon", "evening", "full_day"]); - -export const attendanceRecords = mysqlTable("attendance_records", { - id: id("id").primaryKey(), - studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }), - scheduleId: varchar("schedule_id", { length: 128 }), - date: date("date").notNull(), - status: attendanceStatusEnum.notNull(), - // L-6:节次考勤字段。null 表示全日记录(向后兼容已有数据)。 - period: attendancePeriodEnum, - remark: text("remark"), - reason: varchar("reason", { length: 255 }), - recordedBy: varchar("recorded_by", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - studentIdx: index("attendance_records_student_idx").on(table.studentId), - classIdx: index("attendance_records_class_idx").on(table.classId), - dateIdx: index("attendance_records_date_idx").on(table.date), - classDateIdx: index("attendance_records_class_date_idx").on(table.classId, table.date), - studentDateIdx: index("attendance_records_student_date_idx").on(table.studentId, table.date), - scheduleIdx: index("attendance_records_schedule_idx").on(table.scheduleId), - recordedByIdx: index("attendance_records_recorded_by_idx").on(table.recordedBy), - // L-6:班级 + 日期 + 节次复合索引,支持按节次查询班级当日点名 - classDatePeriodIdx: index("attendance_records_class_date_period_idx").on(table.classId, table.date, table.period), - classFk: foreignKey({ - columns: [table.classId], - foreignColumns: [classes.id], - name: "ar_c_fk", - }).onDelete("cascade"), - studentFk: foreignKey({ - columns: [table.studentId], - foreignColumns: [users.id], - name: "ar_s_fk", - }).onDelete("cascade"), - recordedByFk: foreignKey({ - columns: [table.recordedBy], - foreignColumns: [users.id], - name: "ar_rb_fk", - }).onDelete("cascade"), -})); - -export const attendanceRules = mysqlTable("attendance_rules", { - id: id("id").primaryKey(), - classId: varchar("class_id", { length: 128 }).references(() => classes.id, { onDelete: "cascade" }), - lateThresholdMinutes: int("late_threshold_minutes").default(15), - earlyLeaveThresholdMinutes: int("early_leave_threshold_minutes").default(15), - enableAutoMark: boolean("enable_auto_mark").default(false), - // L-3:出勤率阈值预警(教师端配置驱动) - attendanceRateThreshold: int("attendance_rate_threshold").default(90), - consecutiveAbsenceThreshold: int("consecutive_absence_threshold").default(3), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").onUpdateNow().notNull(), -}, (table) => ({ - classIdx: index("attendance_rules_class_idx").on(table.classId), - classFk: foreignKey({ - columns: [table.classId], - foreignColumns: [classes.id], - name: "atr_c_fk", - }).onDelete("cascade"), -})); - -// --- 18b. Leave Requests (L-5 在线请假流程) --- - -/** - * 请假类型。 - * - sick: 病假 - * - personal: 事假 - * - family: 家庭事务 - * - other: 其他 - */ -export const leaveTypeEnum = mysqlEnum("leave_type", [ - "sick", - "personal", - "family", - "other", -]); - -/** - * 请假审批状态机:pending → approved/rejected;approved/pending 可被 requester 取消为 cancelled。 - * - pending: 待审批 - * - approved: 已批准(自动同步考勤为 excused) - * - rejected: 已拒绝 - * - cancelled: 已撤销(申请人在审批前主动撤销) - */ -export const leaveStatusEnum = mysqlEnum("leave_status", [ - "pending", - "approved", - "rejected", - "cancelled", -]); - -export const leaveRequests = mysqlTable("leave_requests", { - id: id("id").primaryKey(), - /** 请假学生 ID */ - studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - /** 提交人 ID(家长代子女提交时为家长 userId;学生本人提交时等于 studentId) */ - requesterId: varchar("requester_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - /** 学生所属班级 ID(提交时快照,便于按班级筛选审批) */ - classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }), - leaveType: leaveTypeEnum.notNull(), - /** 起始日期(含) */ - startDate: date("start_date").notNull(), - /** 结束日期(含) */ - endDate: date("end_date").notNull(), - /** 请假事由 */ - reason: text("reason").notNull(), - status: leaveStatusEnum.default("pending").notNull(), - /** 审批人 ID(pending/cancelled 状态为 null) */ - reviewerId: varchar("reviewer_id", { length: 128 }).references(() => users.id, { onDelete: "set null" }), - /** 审批意见(拒绝时必填,批准时可选) */ - reviewComment: text("review_comment"), - reviewedAt: timestamp("reviewed_at"), - /** 是否已同步考勤记录(approved 后异步写入 attendance_records,置为 true) */ - attendanceSynced: boolean("attendance_synced").default(false).notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - studentIdx: index("leave_requests_student_idx").on(table.studentId), - requesterIdx: index("leave_requests_requester_idx").on(table.requesterId), - classIdx: index("leave_requests_class_idx").on(table.classId), - statusIdx: index("leave_requests_status_idx").on(table.status), - reviewerIdx: index("leave_requests_reviewer_idx").on(table.reviewerId), - /** 班级+状态查询索引(教师审批列表常用) */ - classStatusIdx: index("leave_requests_class_status_idx").on(table.classId, table.status), - /** 学生+状态查询索引(家长/学生列表常用) */ - studentStatusIdx: index("leave_requests_student_status_idx").on(table.studentId, table.status), - classFk: foreignKey({ - columns: [table.classId], - foreignColumns: [classes.id], - name: "lr_c_fk", - }).onDelete("cascade"), - studentFk: foreignKey({ - columns: [table.studentId], - foreignColumns: [users.id], - name: "lr_s_fk", - }).onDelete("cascade"), - requesterFk: foreignKey({ - columns: [table.requesterId], - foreignColumns: [users.id], - name: "lr_rq_fk", - }).onDelete("cascade"), - reviewerFk: foreignKey({ - columns: [table.reviewerId], - foreignColumns: [users.id], - name: "lr_rv_fk", - }).onDelete("set null"), -})); - -// --- 19. Password Security (密码安全策略) --- - -export const passwordSecurity = mysqlTable("password_security", { - id: varchar("id", { length: 128 }).primaryKey().$defaultFn(() => createId()), - userId: varchar("user_id", { length: 128 }).notNull().unique().references(() => users.id, { onDelete: "cascade" }), - failedLoginAttempts: int("failed_login_attempts").default(0).notNull(), - lockedUntil: timestamp("locked_until", { mode: "date" }), - passwordChangedAt: timestamp("password_changed_at").defaultNow().notNull(), - mustChangePassword: boolean("must_change_password").default(false).notNull(), - lastPasswordChange: timestamp("last_password_change", { mode: "date" }), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - userIdIdx: index("password_security_user_idx").on(table.userId), - userFk: foreignKey({ - columns: [table.userId], - foreignColumns: [users.id], - name: "ps_u_fk", - }).onDelete("cascade"), -})); - -// --- 20. Scheduling Rules & Schedule Changes (排课规则与调课) --- - -export const schedulingRules = mysqlTable("scheduling_rules", { - id: id("id").primaryKey(), - classId: varchar("class_id", { length: 128 }), // null=全局规则 - maxDailyHours: int("max_daily_hours").default(8), - maxContinuousHours: int("max_continuous_hours").default(2), - lunchBreakStart: varchar("lunch_break_start", { length: 10 }).default("12:00"), - lunchBreakEnd: varchar("lunch_break_end", { length: 10 }).default("13:00"), - morningStart: varchar("morning_start", { length: 10 }).default("08:00"), - afternoonEnd: varchar("afternoon_end", { length: 10 }).default("17:00"), - avoidBackToBack: boolean("avoid_back_to_back").default(false), - balancedSubjects: boolean("balanced_subjects").default(true), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - classIdx: index("scheduling_rules_class_idx").on(table.classId), -})); - -export const scheduleChangeStatusEnum = mysqlEnum("status", ["pending", "approved", "rejected", "completed"]); - -export const scheduleChanges = mysqlTable("schedule_changes", { - id: id("id").primaryKey(), - originalScheduleId: varchar("original_schedule_id", { length: 128 }), - classId: varchar("class_id", { length: 128 }).notNull(), - originalTeacherId: varchar("original_teacher_id", { length: 128 }), - substituteTeacherId: varchar("substitute_teacher_id", { length: 128 }), - originalDate: date("original_date"), - newDate: date("new_date"), - newStartTime: varchar("new_start_time", { length: 10 }), - newEndTime: varchar("new_end_time", { length: 10 }), - reason: text("reason"), - status: scheduleChangeStatusEnum.default("pending").notNull(), - requestedBy: varchar("requested_by", { length: 128 }).notNull(), - approvedBy: varchar("approved_by", { length: 128 }), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - classIdx: index("schedule_changes_class_idx").on(table.classId), - statusIdx: index("schedule_changes_status_idx").on(table.status), - requestedByIdx: index("schedule_changes_requested_by_idx").on(table.requestedBy), - originalScheduleIdx: index("schedule_changes_original_schedule_idx").on(table.originalScheduleId), -})); - -// --- 21. Elective Course Management (选课管理) --- - -export const electiveCourseStatusEnum = mysqlEnum("status", ["draft", "open", "closed", "cancelled"]); -export const electiveSelectionModeEnum = mysqlEnum("selection_mode", ["fcfs", "lottery"]); -export const courseSelectionStatusEnum = mysqlEnum("selection_status", ["selected", "enrolled", "waitlist", "dropped", "rejected"]); - -export const electiveCourses = mysqlTable("elective_courses", { - id: id("id").primaryKey(), - name: varchar("name", { length: 255 }).notNull(), - subjectId: varchar("subject_id", { length: 128 }).references(() => subjects.id, { onDelete: "set null" }), - teacherId: varchar("teacher_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - gradeId: varchar("grade_id", { length: 128 }).references(() => grades.id, { onDelete: "set null" }), - description: text("description"), - capacity: int("capacity").default(30).notNull(), - enrolledCount: int("enrolled_count").default(0).notNull(), - classroom: varchar("classroom", { length: 100 }), - schedule: varchar("schedule", { length: 255 }), - startDate: date("start_date"), - endDate: date("end_date"), - selectionStartAt: datetime("selection_start_at"), - selectionEndAt: datetime("selection_end_at"), - /** 退课截止时间(P2-4 新增):超过此时间学生不可退课;为 null 表示不限制 */ - dropDeadline: datetime("drop_deadline"), - status: electiveCourseStatusEnum.default("draft").notNull(), - selectionMode: electiveSelectionModeEnum.default("fcfs").notNull(), - credit: decimal("credit", { precision: 3, scale: 1 }).default("1.0"), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - teacherIdx: index("elective_courses_teacher_idx").on(table.teacherId), - subjectIdx: index("elective_courses_subject_idx").on(table.subjectId), - gradeIdx: index("elective_courses_grade_idx").on(table.gradeId), - statusIdx: index("elective_courses_status_idx").on(table.status), -})); - -export const courseSelections = mysqlTable("course_selections", { - id: id("id").primaryKey(), - courseId: varchar("course_id", { length: 128 }).notNull().references(() => electiveCourses.id, { onDelete: "cascade" }), - studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - status: courseSelectionStatusEnum.default("selected").notNull(), - priority: int("priority").default(1), - selectedAt: timestamp("selected_at").defaultNow().notNull(), - enrolledAt: timestamp("enrolled_at"), - droppedAt: timestamp("dropped_at"), - /** 退课理由(P2-4 新增):学生退课时可选填写的理由,便于学校运营分析 */ - dropReason: varchar("drop_reason", { length: 255 }), - lotteryRank: int("lottery_rank"), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - courseStudentPk: primaryKey({ columns: [table.courseId, table.studentId] }), - courseIdx: index("course_selections_course_idx").on(table.courseId), - studentIdx: index("course_selections_student_idx").on(table.studentId), - statusIdx: index("course_selections_status_idx").on(table.status), -})); - -// --- 22. Exam Proctoring Events (考试监考事件) --- - -export const examProctoringEvents = mysqlTable("exam_proctoring_events", { - id: id("id").primaryKey(), - submissionId: varchar("submission_id", { length: 128 }).notNull().references(() => examSubmissions.id, { onDelete: "cascade" }), - studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - examId: varchar("exam_id", { length: 128 }).notNull().references(() => exams.id, { onDelete: "cascade" }), - eventType: proctoringEventTypeEnum.notNull(), - eventDetail: text("event_detail"), - occurredAt: timestamp("occurred_at").defaultNow().notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), -}, (table) => ({ - submissionIdx: index("proctoring_submission_idx").on(table.submissionId), - studentIdx: index("proctoring_student_idx").on(table.studentId), - examIdx: index("proctoring_exam_idx").on(table.examId), - eventTypeIdx: index("proctoring_event_type_idx").on(table.eventType), -})); - -// --- 23. Learning Diagnostic (学情诊断报告) --- - -export const knowledgePointMastery = mysqlTable("knowledge_point_mastery", { - id: id("id").primaryKey(), - // 显式 foreignKey 见表参数(避免自动生成 FK 名过长 > 64 字符) - studentId: varchar("student_id", { length: 128 }).notNull(), - knowledgePointId: varchar("knowledge_point_id", { length: 128 }).notNull(), - masteryLevel: decimal("mastery_level", { precision: 5, scale: 2 }).default("0").notNull(), - totalQuestions: int("total_questions").default(0).notNull(), - correctQuestions: int("correct_questions").default(0).notNull(), - lastAssessedAt: timestamp("last_assessed_at").defaultNow().notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - studentKpPk: primaryKey({ columns: [table.studentId, table.knowledgePointId] }), - studentIdx: index("mastery_student_idx").on(table.studentId), - kpIdx: index("mastery_kp_idx").on(table.knowledgePointId), - studentFk: foreignKey({ - columns: [table.studentId], - foreignColumns: [users.id], - name: "kpm_student_fk", - }).onDelete("cascade"), - kpFk: foreignKey({ - columns: [table.knowledgePointId], - foreignColumns: [knowledgePoints.id], - name: "kpm_kp_fk", - }).onDelete("cascade"), -})); - -export const diagnosticReportStatusEnum = mysqlEnum("report_status", ["draft", "published", "archived"]); -export const diagnosticReportTypeEnum = mysqlEnum("report_type", ["individual", "class", "grade"]); - -export const learningDiagnosticReports = mysqlTable("learning_diagnostic_reports", { - id: id("id").primaryKey(), - studentId: varchar("student_id", { length: 128 }).references(() => users.id, { onDelete: "cascade" }), - generatedBy: varchar("generated_by", { length: 128 }).references(() => users.id, { onDelete: "set null" }), - // v4-P1-4: 班级报告关联的 classId,用于发布时批量通知全班学生 - classId: varchar("class_id", { length: 128 }).references(() => classes.id, { onDelete: "set null" }), - // v4-P2-3: 年级报告关联的 gradeId,用于年级级别诊断 - gradeId: varchar("grade_id", { length: 128 }).references(() => grades.id, { onDelete: "set null" }), - reportType: diagnosticReportTypeEnum.default("individual").notNull(), - period: varchar("period", { length: 50 }), - summary: text("summary"), - strengths: json("strengths"), - weaknesses: json("weaknesses"), - recommendations: json("recommendations"), - overallScore: decimal("overall_score", { precision: 5, scale: 2 }), - status: diagnosticReportStatusEnum.default("draft").notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - studentIdx: index("diagnostic_student_idx").on(table.studentId), - generatedByIdx: index("diagnostic_generated_by_idx").on(table.generatedBy), - statusIdx: index("diagnostic_status_idx").on(table.status), - reportTypeIdx: index("diagnostic_report_type_idx").on(table.reportType), - classIdx: index("diagnostic_class_idx").on(table.classId), - gradeIdx: index("diagnostic_grade_idx").on(table.gradeId), -})); - -// --- 24. Lesson Preparation (备课) --- - -export const lessonPlans = mysqlTable("lesson_plans", { - id: id("id").primaryKey(), - title: varchar("title", { length: 255 }).notNull(), - textbookId: varchar("textbook_id", { length: 128 }).references(() => textbooks.id), - chapterId: varchar("chapter_id", { length: 128 }).references(() => chapters.id), - coursePlanItemId: varchar("course_plan_item_id", { length: 128 }), - subjectId: varchar("subject_id", { length: 128 }).references(() => subjects.id), - gradeId: varchar("grade_id", { length: 128 }).references(() => grades.id), - templateId: varchar("template_id", { length: 128 }), - templateName: varchar("template_name", { length: 100 }), - content: json("content").notNull(), - status: varchar("status", { length: 50 }).default("draft").notNull(), - creatorId: varchar("creator_id", { length: 128 }).notNull().references(() => users.id), - lastSavedAt: timestamp("last_saved_at"), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - creatorIdx: index("lp_creator_idx").on(table.creatorId), - statusIdx: index("lp_status_idx").on(table.status), - textbookChapterIdx: index("lp_textbook_chapter_idx").on(table.textbookId, table.chapterId), - subjectGradeIdx: index("lp_subject_grade_idx").on(table.subjectId, table.gradeId), -})); - -export const lessonPlanVersions = mysqlTable("lesson_plan_versions", { - id: id("id").primaryKey(), - planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), - versionNo: int("version_no").notNull(), - label: varchar("label", { length: 100 }), - content: json("content").notNull(), - isAuto: boolean("is_auto").default(false).notNull(), - creatorId: varchar("creator_id", { length: 128 }).notNull().references(() => users.id), - createdAt: timestamp("created_at").defaultNow().notNull(), -}, (table) => ({ - planVersionIdx: index("lpv_plan_version_idx").on(table.planId, table.versionNo), - planCreatedIdx: index("lpv_plan_created_idx").on(table.planId, table.createdAt), -})); - -export const lessonPlanTemplates = mysqlTable("lesson_plan_templates", { - id: id("id").primaryKey(), - name: varchar("name", { length: 100 }).notNull(), - type: varchar("type", { length: 50 }).notNull(), - scope: varchar("scope", { length: 50 }).notNull(), - blocks: json("blocks").notNull(), - creatorId: varchar("creator_id", { length: 128 }).references(() => users.id), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - typeCreatorIdx: index("lpt_type_creator_idx").on(table.type, table.creatorId), -})); - -// M2 协同备课:Block 级评论表 -export const lessonPlanComments = mysqlTable("lesson_plan_comments", { - id: id("id").primaryKey(), - planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), - blockId: varchar("block_id", { length: 128 }).notNull(), - content: text("content").notNull(), - authorId: varchar("author_id", { length: 128 }).notNull().references(() => users.id), - resolved: boolean("resolved").default(false).notNull(), - parentCommentId: varchar("parent_comment_id", { length: 128 }), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - planBlockIdx: index("lpc_plan_block_idx").on(table.planId, table.blockId), - authorIdx: index("lpc_author_idx").on(table.authorId), -})); - -// M3 审核工作流:审核记录表 -export const lessonPlanReviewRecords = mysqlTable("lesson_plan_review_records", { - id: id("id").primaryKey(), - planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), - reviewerId: varchar("reviewer_id", { length: 128 }).notNull().references(() => users.id), - decision: varchar("decision", { length: 20 }).notNull(), // approved | rejected - comment: text("comment"), - previousStatus: varchar("previous_status", { length: 50 }).notNull(), - newStatus: varchar("new_status", { length: 50 }).notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), -}, (table) => ({ - planCreatedIdx: index("lprr_plan_created_idx").on(table.planId, table.createdAt), - reviewerIdx: index("lprr_reviewer_idx").on(table.reviewerId), -})); - -// M6 资源附件库:附件关联表 -export const lessonPlanAttachments = mysqlTable("lesson_plan_attachments", { - id: id("id").primaryKey(), - planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), - blockId: varchar("block_id", { length: 128 }), - fileId: varchar("file_id", { length: 128 }).notNull(), - displayName: varchar("display_name", { length: 255 }).notNull(), - attachmentType: varchar("attachment_type", { length: 50 }).default("reference").notNull(), // reference | material | supplementary - uploadedBy: varchar("uploaded_by", { length: 128 }).notNull().references(() => users.id), - createdAt: timestamp("created_at").defaultNow().notNull(), -}, (table) => ({ - planIdx: index("lpa_plan_idx").on(table.planId), - blockIdx: index("lpa_block_idx").on(table.blockId), - fileIdx: index("lpa_file_idx").on(table.fileId), -})); - -// M12 代课教师机制:代课教师映射表 -export const lessonPlanSubstitutes = mysqlTable("lesson_plan_substitutes", { - id: id("id").primaryKey(), - planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), - originalTeacherId: varchar("original_teacher_id", { length: 128 }).notNull().references(() => users.id), - substituteTeacherId: varchar("substitute_teacher_id", { length: 128 }).notNull().references(() => users.id), - startDate: date("start_date").notNull(), - endDate: date("end_date"), - reason: varchar("reason", { length: 255 }), - status: varchar("status", { length: 20 }).default("active").notNull(), // active | expired | cancelled - createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - planIdx: index("lps_plan_idx").on(table.planId), - originalTeacherIdx: index("lps_original_teacher_idx").on(table.originalTeacherId), - substituteTeacherIdx: index("lps_substitute_teacher_idx").on(table.substituteTeacherId), - dateRangeIdx: index("lps_date_range_idx").on(table.startDate, table.endDate), -})); - -// V5-7:课案-课时绑定表(将课案绑定到具体班级的某个日期/节次) -export const lessonPlanSchedules = mysqlTable("lesson_plan_schedules", { - id: id("id").primaryKey(), - planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), - classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }), - /** 排课日期 YYYY-MM-DD */ - scheduledDate: date("scheduled_date").notNull(), - /** 节次序号(1-12,对应学校节次安排) */ - period: int("period").notNull(), - /** 关联 class_schedule.id(如使用课表)*/ - classScheduleId: varchar("class_schedule_id", { length: 128 }), - /** 教学时长(分钟),默认 40 */ - durationMin: int("duration_min").default(40).notNull(), - createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - planIdx: index("lpsc_plan_idx").on(table.planId), - classDateIdx: index("lpsc_class_date_idx").on(table.classId, table.scheduledDate), - teacherDateIdx: index("lpsc_plan_date_idx").on(table.planId, table.scheduledDate), -})); - -// --- 25. System Settings (系统设置 - 键值对存储) --- - -export const systemSettings = mysqlTable("system_settings", { - id: id("id").primaryKey(), - /** 设置分组:school_info / security_policy / file_upload / notification_config */ - category: varchar("category", { length: 50 }).notNull(), - /** 设置键名,如 schoolName / passwordMinLength / maxFileSize */ - key: varchar("key", { length: 100 }).notNull(), - /** 设置值(JSON 字符串,支持字符串/数字/布尔/对象) */ - value: text("value").notNull(), - /** 值类型:string / number / boolean / json */ - valueType: varchar("value_type", { length: 20 }).default("string").notNull(), - updatedBy: varchar("updated_by", { length: 128 }), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - categoryKeyIdx: uniqueIndex("ss_category_key_idx").on(table.category, table.key), - categoryIdx: index("ss_category_idx").on(table.category), -})); - -// --- 26. Error Book (错题本) --- - -export const errorBookSourceTypeEnum = mysqlEnum("source_type", ["exam", "homework", "manual"]); -export const errorBookStatusEnum = mysqlEnum("error_status", ["new", "learning", "mastered", "archived"]); -export const errorBookReviewResultEnum = mysqlEnum("review_result", ["again", "hard", "good", "easy"]); - -/** - * 错题本条目 - 存储学生的错题记录。 - * 来源可以是考试提交、作业提交,或学生手动添加。 - * 采用简化版 SM-2 间隔重复算法调度复习。 - */ -export const errorBookItems = mysqlTable("error_book_items", { - id: id("id").primaryKey(), - studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - questionId: varchar("question_id", { length: 128 }).notNull().references(() => questions.id), - - // 来源 - sourceType: errorBookSourceTypeEnum.default("manual").notNull(), - /** exam_submission_id / homework_submission_id / null(手动添加) */ - sourceId: varchar("source_id", { length: 128 }), - - // 作答快照(冗余存储,避免源记录被删除后丢失上下文) - studentAnswer: json("student_answer"), - correctAnswer: json("correct_answer"), - - // 分类元数据(冗余,加速筛选) - subjectId: varchar("subject_id", { length: 128 }), - knowledgePointIds: json("knowledge_point_ids"), - - // 状态与掌握度 - status: errorBookStatusEnum.default("new").notNull(), - /** 0-5 掌握程度,0=未学,5=已掌握 */ - masteryLevel: int("mastery_level").default(0).notNull(), - - // SM-2 间隔重复参数 - nextReviewAt: timestamp("next_review_at", { mode: "date" }), - /** 复习间隔(天) */ - reviewInterval: int("review_interval").default(1).notNull(), - reviewCount: int("review_count").default(0).notNull(), - /** 连续答对次数(用于判断是否标记为已掌握) */ - correctStreak: int("correct_streak").default(0).notNull(), - - // 学生笔记与反思 - note: text("note"), - /** 错误原因标签(JSON 数组,如 ["粗心", "概念不清", "计算错误"]) */ - errorTags: json("error_tags"), - - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - studentIdx: index("eb_item_student_idx").on(table.studentId), - studentStatusIdx: index("eb_item_student_status_idx").on(table.studentId, table.status), - studentReviewIdx: index("eb_item_student_review_idx").on(table.studentId, table.nextReviewAt), - questionIdx: index("eb_item_question_idx").on(table.questionId), - subjectIdx: index("eb_item_subject_idx").on(table.subjectId), - sourceIdx: index("eb_item_source_idx").on(table.sourceType, table.sourceId), -})); - -/** - * 错题复习记录 - 每次复习的历史快照。 - * 用于追踪复习进度和算法调参。 - */ -export const errorBookReviews = mysqlTable("error_book_reviews", { - id: id("id").primaryKey(), - itemId: varchar("item_id", { length: 128 }).notNull().references(() => errorBookItems.id, { onDelete: "cascade" }), - studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - - /** 复习自评结果:again(重来) / hard(困难) / good(良好) / easy(简单) */ - result: errorBookReviewResultEnum.notNull(), - reviewedAt: timestamp("reviewed_at").defaultNow().notNull(), - - /** 本次复习后的新间隔(天) */ - newInterval: int("new_interval"), - newMasteryLevel: int("new_mastery_level"), - - createdAt: timestamp("created_at").defaultNow().notNull(), -}, (table) => ({ - itemIdx: index("eb_review_item_idx").on(table.itemId), - studentIdx: index("eb_review_student_idx").on(table.studentId), - studentReviewedIdx: index("eb_review_student_reviewed_idx").on(table.studentId, table.reviewedAt), -})); - -// --- 27. Grade Drafts (成绩录入草稿 - 服务端自动保存) --- - -/** - * 成绩录入草稿 - 用于跨设备恢复未保存的成绩。 - * v3-P2-10: 替代纯 localStorage 方案,支持换设备恢复。 - * 唯一键:userId + classId + subjectId + type,确保每位教师每组合只有一个草稿。 - */ -export const gradeDrafts = mysqlTable("grade_drafts", { - id: id("id").primaryKey(), - userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }), - subjectId: varchar("subject_id", { length: 128 }).notNull().references(() => subjects.id, { onDelete: "cascade" }), - type: varchar("type", { length: 20 }).notNull(), - /** 草稿内容:{ scores: Record, timestamp: number } */ - content: json("content").notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), - /** - * P3-6 协同录入锁机制: - * - lockedBy: 当前持有编辑锁的教师 ID(null 表示无锁) - * - lockedAt: 锁获取时间(用于过期判定,默认 5 分钟自动释放) - * - lockToken: 锁定令牌(前端续期时校验,防止伪造他人续期) - */ - lockedBy: varchar("locked_by", { length: 128 }), - lockedAt: timestamp("locked_at"), - lockToken: varchar("lock_token", { length: 64 }), -}, (table) => ({ - userDraftIdx: uniqueIndex("gd_user_class_subject_type_idx").on( - table.userId, - table.classId, - table.subjectId, - table.type - ), - userUpdatedIdx: index("gd_user_updated_idx").on(table.userId, table.updatedAt), - // P3-6: 班级-科目-类型粒度的锁查询索引(用于跨用户协同冲突检测) - classSubjectLockIdx: index("gd_class_subject_lock_idx").on( - table.classId, - table.subjectId, - table.type, - table.lockedBy - ), -})); - -/** - * P3-7 成绩申诉状态机枚举。 - * - pending: 学生已提交申诉,等待教师复核 - * - approved: 教师批准申诉(可能调整分数) - * - rejected: 教师驳回申诉(维持原分数) - * - withdrawn: 学生主动撤回申诉 - */ -export const gradeAppealStatusEnum = mysqlEnum("appeal_status", [ - "pending", - "approved", - "rejected", - "withdrawn", -]); - -/** - * 成绩申诉记录 - 学生对成绩提出异议,教师复核后修改并留痕。 - * - * P3-7: 满足合规性要求,成绩修改全程留痕。 - * 状态机:pending → approved/rejected/withdrawn - * 一条 grade_record 只允许有一条 pending 申诉,避免重复申诉。 - */ -export const gradeAppeals = mysqlTable("grade_appeals", { - id: id("id").primaryKey(), - gradeRecordId: varchar("grade_record_id", { length: 128 }).notNull().references(() => gradeRecords.id, { onDelete: "cascade" }), - studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - /** 申诉快照:申诉时的原始分数(审计留痕,防止后续修改后追溯困难) */ - originalScore: decimal("original_score", { precision: 6, scale: 2 }).notNull(), - /** 学生期望的分数(选填,便于教师判断诉求) */ - expectedScore: decimal("expected_score", { precision: 6, scale: 2 }), - /** 申诉理由 */ - reason: text("reason").notNull(), - status: gradeAppealStatusEnum.default("pending").notNull(), - /** 复核教师 ID(pending 状态为 null) */ - reviewerId: varchar("reviewer_id", { length: 128 }).references(() => users.id, { onDelete: "set null" }), - /** 教师复核意见 */ - reviewComment: text("review_comment"), - /** 审核后的调整分数(approved 且分数有变时填写) */ - adjustedScore: decimal("adjusted_score", { precision: 6, scale: 2 }), - reviewedAt: timestamp("reviewed_at"), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - gradeRecordIdx: index("ga_grade_record_idx").on(table.gradeRecordId), - studentIdx: index("ga_student_idx").on(table.studentId), - statusIdx: index("ga_status_idx").on(table.status), - reviewerIdx: index("ga_reviewer_idx").on(table.reviewerId), - /** pending 状态查询索引(应用层校验唯一性,避免多次申诉) */ - pendingLookupIdx: index("ga_pending_lookup_idx").on( - table.gradeRecordId, - table.status - ), -})); - -// --- 28. Adaptive Practice (专项练习闭环) --- - -/** - * 专项练习类型。 - * - error_variant: 错题变式练习(从错题本发起,生成变式题) - * - knowledge_point: 知识点专项练习(按知识点抽题) - * - weak_chapter: 薄弱章节练习(按掌握度自动推荐) - * - ai_recommended: AI 推荐练习(AI 根据学情综合推荐) - */ -export const practiceTypeEnum = mysqlEnum("practice_type", [ - "error_variant", - "knowledge_point", - "weak_chapter", - "ai_recommended", -]); - -export const practiceStatusEnum = mysqlEnum("practice_status", [ - "in_progress", - "completed", - "abandoned", -]); - -export const practiceAnswerStatusEnum = mysqlEnum("practice_answer_status", [ - "pending", - "answered", - "skipped", -]); - -/** - * 专项练习会话 - 学生发起的一次专项练习。 - * - * 记录练习类型、来源元数据(知识点/错题/章节)、状态和统计。 - * 练习题目存储在 practiceAnswers 表中。 - */ -export const practiceSessions = mysqlTable("practice_sessions", { - id: id("id").primaryKey(), - studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - subjectId: varchar("subject_id", { length: 128 }), - - practiceType: practiceTypeEnum.notNull(), - /** 来源元数据:{ knowledgePointIds?, errorBookItemIds?, chapterId?, difficulty?, sourceQuestionId? } */ - sourceMeta: json("source_meta"), - - status: practiceStatusEnum.default("in_progress").notNull(), - - totalQuestions: int("total_questions").default(0).notNull(), - answeredQuestions: int("answered_questions").default(0).notNull(), - correctCount: int("correct_count").default(0).notNull(), - - startedAt: timestamp("started_at", { mode: "date" }).defaultNow().notNull(), - completedAt: timestamp("completed_at", { mode: "date" }), - - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - studentIdx: index("ps_student_idx").on(table.studentId), - studentStatusIdx: index("ps_student_status_idx").on(table.studentId, table.status), - subjectIdx: index("ps_subject_idx").on(table.subjectId), -})); - -/** - * 专项练习答题记录 - 会话中每道题的作答情况。 - * - * 支持两种题目来源: - * 1. 题库中的题目(questionId 指向 questions 表) - * 2. AI 生成的变式题(variantContent 存储 JSON 内容,questionId 指向原题) - */ -export const practiceAnswers = mysqlTable("practice_answers", { - id: id("id").primaryKey(), - sessionId: varchar("session_id", { length: 128 }).notNull().references(() => practiceSessions.id, { onDelete: "cascade" }), - studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - - questionId: varchar("question_id", { length: 128 }).notNull().references(() => questions.id), - /** 变式题内容(AI 生成的变式题,未入库时存储在此字段) */ - variantContent: json("variant_content"), - /** 是否为变式题 */ - isVariant: boolean("is_variant").default(false).notNull(), - - orderIndex: int("order_index").notNull(), - status: practiceAnswerStatusEnum.default("pending").notNull(), - - studentAnswer: json("student_answer"), - isCorrect: boolean("is_correct"), - score: int("score"), - maxScore: int("max_score").default(1).notNull(), - - answeredAt: timestamp("answered_at", { mode: "date" }), - - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - sessionIdx: index("pa_session_idx").on(table.sessionId), - studentIdx: index("pa_student_idx").on(table.studentId), - questionIdx: index("pa_question_idx").on(table.questionId), - sessionOrderIdx: index("pa_session_order_idx").on(table.sessionId, table.orderIndex), -})); - -// --- 29. Standards (M1 课标对标体系) --- - -/** - * M1 课标库:支持国家标准/课标/自定义三层级。 - * 课案通过 lessonPlanStandards 关联表实现多对多关系。 - */ -export const standards = mysqlTable("standards", { - id: id("id").primaryKey(), - code: varchar("code", { length: 100 }).notNull(), - title: varchar("title", { length: 255 }).notNull(), - description: text("description"), - /** 层级:national(国家标准)/ curriculum(课标)/ custom(自定义) */ - level: varchar("level", { length: 20 }).notNull(), - parentId: varchar("parent_id", { length: 128 }), - subjectId: varchar("subject_id", { length: 128 }).references(() => subjects.id), - gradeId: varchar("grade_id", { length: 128 }).references(() => grades.id), - /** 学段:primary / junior_high / senior_high */ - stage: varchar("stage", { length: 20 }), - sortOrder: int("sort_order").default(0).notNull(), - isActive: boolean("is_active").default(true).notNull(), - createdBy: varchar("created_by", { length: 128 }).references(() => users.id), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - codeIdx: uniqueIndex("std_code_idx").on(table.code), - parentIdx: index("std_parent_idx").on(table.parentId), - subjectGradeIdx: index("std_subject_grade_idx").on(table.subjectId, table.gradeId), - levelIdx: index("std_level_idx").on(table.level), -})); - -/** M1 课案 ↔ 课标 多对多关联 */ -export const lessonPlanStandards = mysqlTable("lesson_plan_standards", { - id: id("id").primaryKey(), - planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), - standardId: varchar("standard_id", { length: 128 }).notNull().references(() => standards.id, { onDelete: "cascade" }), - /** 关联类型:primary(主要对标)/ related(相关对标) */ - relationType: varchar("relation_type", { length: 20 }).default("primary").notNull(), - createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id), - createdAt: timestamp("created_at").defaultNow().notNull(), -}, (table) => ({ - planStandardIdx: uniqueIndex("lps_plan_standard_idx").on(table.planId, table.standardId), - standardIdx: index("lps_standard_idx").on(table.standardId), -})); - -// --- 30. Formative Assessment (M5 形成性评价闭环) --- - -/** - * M5 互动组件:发布课案时嵌入的 poll/quiz。 - * 学生作答后结果回写到 lessonPlanFormativeResponses。 - */ -export const lessonPlanFormativeItems = mysqlTable("lesson_plan_formative_items", { - id: id("id").primaryKey(), - planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), - blockId: varchar("block_id", { length: 128 }).notNull(), - /** 互动类型:poll / quiz / exit_ticket */ - interactionType: varchar("interaction_type", { length: 20 }).notNull(), - /** 题目内容(JSON:题目/选项/正确答案) */ - payload: json("payload").notNull(), - /** 是否启用实时反馈 */ - instantFeedback: boolean("instant_feedback").default(false).notNull(), - orderIndex: int("order_index").default(0).notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), -}, (table) => ({ - planBlockIdx: index("lpfi_plan_block_idx").on(table.planId, table.blockId), -})); - -/** M5 学生作答记录 */ -export const lessonPlanFormativeResponses = mysqlTable("lesson_plan_formative_responses", { - id: id("id").primaryKey(), - // 显式 foreignKey 见表参数(避免自动生成 FK 名过长 > 64 字符) - itemId: varchar("item_id", { length: 128 }).notNull(), - studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), - classId: varchar("class_id", { length: 128 }), - /** 学生作答(JSON) */ - response: json("response").notNull(), - /** 是否正确(quiz 类型适用) */ - isCorrect: boolean("is_correct"), - /** 作答耗时(秒) */ - durationSec: int("duration_sec"), - createdAt: timestamp("created_at").defaultNow().notNull(), -}, (table) => ({ - itemIdx: index("lpfr_item_idx").on(table.itemId), - studentIdx: index("lpfr_student_idx").on(table.studentId), - classIdx: index("lpfr_class_idx").on(table.classId), - itemFk: foreignKey({ - columns: [table.itemId], - foreignColumns: [lessonPlanFormativeItems.id], - name: "lpfr_item_fk", - }).onDelete("cascade"), -})); - -// --- 31. Lesson Plan Analytics (M10 备课分析仪表盘) --- - -/** - * M10 备课分析快照:按日聚合的备课投入/质量统计。 - * 用于仪表盘展示"教师备课投入""模板使用率""课标覆盖热力图"。 - */ -export const lessonPlanAnalyticsDaily = mysqlTable("lesson_plan_analytics_daily", { - id: id("id").primaryKey(), - snapshotDate: date("snapshot_date").notNull(), - teacherId: varchar("teacher_id", { length: 128 }).notNull().references(() => users.id), - subjectId: varchar("subject_id", { length: 128 }), - gradeId: varchar("grade_id", { length: 128 }), - /** 当日新建课案数 */ - newPlansCount: int("new_plans_count").default(0).notNull(), - /** 当日编辑时长(分钟) */ - editDurationMin: int("edit_duration_min").default(0).notNull(), - /** 当日保存版本数 */ - savedVersionsCount: int("saved_versions_count").default(0).notNull(), - /** 当日提交审核数 */ - submittedCount: int("submitted_count").default(0).notNull(), - /** 当日发布数 */ - publishedCount: int("published_count").default(0).notNull(), - /** 关联的课标数量 */ - standardsLinked: int("standards_linked").default(0).notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), -}, (table) => ({ - dateTeacherIdx: uniqueIndex("lpad_date_teacher_idx").on(table.snapshotDate, table.teacherId), - dateIdx: index("lpad_date_idx").on(table.snapshotDate), - subjectGradeIdx: index("lpad_subject_grade_idx").on(table.subjectId, table.gradeId), -})); - -// --- 32. Lesson Plan AI Evaluation (M8 AI 评估) --- - -/** - * M8 AI 课案质量评估记录:保存 AI 对课案的质量评分与建议。 - */ -export const lessonPlanAiEvaluations = mysqlTable("lesson_plan_ai_evaluations", { - id: id("id").primaryKey(), - planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), - versionNo: int("version_no").notNull(), - /** 总体评分(0-100) */ - overallScore: int("overall_score"), - /** 维度评分(JSON:{ clarity, alignment, engagement, differentiation, assessment }) */ - dimensionScores: json("dimension_scores"), - /** AI 建议文本 */ - suggestions: text("suggestions"), - /** 推荐的课标 ID 列表(JSON) */ - recommendedStandardIds: json("recommended_standard_ids"), - evaluatedBy: varchar("evaluated_by", { length: 128 }).notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), -}, (table) => ({ - planVersionIdx: index("lpae_plan_version_idx").on(table.planId, table.versionNo), - planIdx: index("lpae_plan_idx").on(table.planId), -})); +// Barrel export for schema modules. +// 拆分自原 schema.ts(2245 行)→ 按业务域分组的多个子文件,每个 < 500 行。 +// 所有原 import 路径 `@/shared/db/schema` 仍然有效(文件优先于同名文件夹解析)。 + +export * from "./schema/users"; +export * from "./schema/rbac"; +export * from "./schema/knowledge-points"; +export * from "./schema/questions"; +export * from "./schema/academic"; +export * from "./schema/school"; +export * from "./schema/classes"; +export * from "./schema/exams"; +export * from "./schema/homework"; +export * from "./schema/ai-providers"; +export * from "./schema/announcements"; +export * from "./schema/audit"; +export * from "./schema/grade-records"; +export * from "./schema/files"; +export * from "./schema/course-plans"; +export * from "./schema/messaging"; +export * from "./schema/notifications"; +export * from "./schema/attendance"; +export * from "./schema/scheduling"; +export * from "./schema/elective-courses"; +export * from "./schema/learning-diagnostic"; +export * from "./schema/lesson-preparation"; +export * from "./schema/system-settings"; +export * from "./schema/error-book"; +export * from "./schema/practice"; +export * from "./schema/standards"; +export * from "./schema/formative-assessment"; diff --git a/src/shared/db/schema/_helpers.ts b/src/shared/db/schema/_helpers.ts new file mode 100644 index 0000000..1c01983 --- /dev/null +++ b/src/shared/db/schema/_helpers.ts @@ -0,0 +1,6 @@ +import { varchar } from "drizzle-orm/mysql-core"; +import { createId } from "@paralleldrive/cuid2"; + +// --- Helper for ID generation (CUID2) --- +export const id = (name: string) => + varchar(name, { length: 128 }).notNull().$defaultFn(() => createId()); diff --git a/src/shared/db/schema/academic.ts b/src/shared/db/schema/academic.ts new file mode 100644 index 0000000..4561f35 --- /dev/null +++ b/src/shared/db/schema/academic.ts @@ -0,0 +1,51 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; + +// --- 4. Academic / Teaching Flow --- + +export const subjects = mysqlTable("subjects", { + id: id("id").primaryKey(), + name: varchar("name", { length: 100 }).notNull().unique(), + code: varchar("code", { length: 50 }).unique(), + order: int("order").default(0), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + nameIdx: index("subjects_name_idx").on(table.name), +})); + +export const textbooks = mysqlTable("textbooks", { + id: id("id").primaryKey(), + title: varchar("title", { length: 255 }).notNull(), + subject: varchar("subject", { length: 100 }).notNull(), + grade: varchar("grade", { length: 50 }), + publisher: varchar("publisher", { length: 100 }), + + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}); + +export const chapters = mysqlTable("chapters", { + id: id("id").primaryKey(), + textbookId: varchar("textbook_id", { length: 128 }).notNull().references(() => textbooks.id, { onDelete: "cascade" }), + title: varchar("title", { length: 255 }).notNull(), + order: int("order").default(0), + + // Chapters can also be nested + parentId: varchar("parent_id", { length: 128 }), + + content: text("content"), + + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + textbookIdx: index("textbook_idx").on(table.textbookId), + parentIdIdx: index("parent_id_idx").on(table.parentId), +})); diff --git a/src/shared/db/schema/ai-providers.ts b/src/shared/db/schema/ai-providers.ts new file mode 100644 index 0000000..06d75a6 --- /dev/null +++ b/src/shared/db/schema/ai-providers.ts @@ -0,0 +1,35 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + index, + mysqlEnum, + boolean, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; + +// --- AI Providers --- + +export const aiProviderVisibilityEnum = mysqlEnum("visibility", ["public", "private"]); + +export const aiProviders = mysqlTable("ai_providers", { + id: id("id").primaryKey(), + provider: mysqlEnum("provider", ["zhipu", "openai", "gemini", "custom", "ollama"]).notNull(), + baseUrl: varchar("base_url", { length: 512 }), + model: varchar("model", { length: 128 }).notNull(), + apiKeyEncrypted: text("api_key_encrypted").notNull(), + apiKeyLast4: varchar("api_key_last4", { length: 4 }), + isDefault: boolean("is_default").default(false).notNull(), + // V3: 可见性 — public 由管理员发布,全员可用;private 仅创建者可见 + visibility: aiProviderVisibilityEnum.default("private").notNull(), + createdBy: varchar("created_by", { length: 128 }), + updatedBy: varchar("updated_by", { length: 128 }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + providerIdx: index("ai_provider_idx").on(table.provider), + defaultIdx: index("ai_provider_default_idx").on(table.isDefault), + visibilityIdx: index("ai_provider_visibility_idx").on(table.visibility), + createdByIdx: index("ai_provider_created_by_idx").on(table.createdBy), +})); diff --git a/src/shared/db/schema/announcements.ts b/src/shared/db/schema/announcements.ts new file mode 100644 index 0000000..006bf9f --- /dev/null +++ b/src/shared/db/schema/announcements.ts @@ -0,0 +1,56 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + index, + datetime, + mysqlEnum, + boolean, + uniqueIndex, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; + +// --- 8. Announcements --- + +export const announcementTypeEnum = mysqlEnum("type", ["school", "grade", "class"]); +export const announcementStatusEnum = mysqlEnum("status", ["draft", "published", "archived"]); + +export const announcements = mysqlTable("announcements", { + id: id("id").primaryKey(), + title: varchar("title", { length: 255 }).notNull(), + content: text("content").notNull(), + type: announcementTypeEnum.default("school").notNull(), + status: announcementStatusEnum.default("draft").notNull(), + targetGradeId: varchar("target_grade_id", { length: 128 }), + targetClassId: varchar("target_class_id", { length: 128 }), + authorId: varchar("author_id", { length: 128 }).notNull().references(() => users.id), + publishedAt: datetime("published_at", { mode: "date" }), + // V2-P2-13d: 公告置顶(置顶公告在列表中优先显示) + isPinned: boolean("is_pinned").default(false).notNull(), + createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), + updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + authorIdx: index("announcements_author_idx").on(table.authorId), + statusIdx: index("announcements_status_idx").on(table.status), + typeIdx: index("announcements_type_idx").on(table.type), + targetGradeIdx: index("announcements_target_grade_idx").on(table.targetGradeId), + targetClassIdx: index("announcements_target_class_idx").on(table.targetClassId), + // V2-P2-13d: 置顶索引 + statusPinnedIdx: index("announcements_status_pinned_idx").on(table.status, table.isPinned), +})); + +// --- 8b. Announcement Reads (公告已读回执) --- + +export const announcementReads = mysqlTable("announcement_reads", { + id: id("id").primaryKey(), + announcementId: varchar("announcement_id", { length: 128 }).notNull().references(() => announcements.id, { onDelete: "cascade" }), + userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + readAt: timestamp("read_at", { mode: "date" }).defaultNow().notNull(), +}, (table) => ({ + announcementIdx: index("announcement_reads_announcement_idx").on(table.announcementId), + userIdx: index("announcement_reads_user_idx").on(table.userId), + // 唯一约束:一个用户对一条公告只能有一条已读记录 + uniqueAnnouncementUser: uniqueIndex("announcement_reads_unique_idx").on(table.announcementId, table.userId), +})); diff --git a/src/shared/db/schema/attendance.ts b/src/shared/db/schema/attendance.ts new file mode 100644 index 0000000..18febe9 --- /dev/null +++ b/src/shared/db/schema/attendance.ts @@ -0,0 +1,177 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, + date, + foreignKey, + mysqlEnum, + boolean, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; +import { classes } from "./classes"; + +// --- 18. Attendance (考勤管理) --- + +export const attendanceStatusEnum = mysqlEnum("status", ["present", "absent", "late", "early_leave", "excused", "school_activity"]); + +/** + * L-6 节次考勤:节次枚举。 + * - morning_reading: 早读 + * - morning: 上午 + * - afternoon: 下午 + * - evening: 晚自习 + * - full_day: 全日(默认值,向后兼容:null 也表示全日) + */ +export const attendancePeriodEnum = mysqlEnum("period", ["morning_reading", "morning", "afternoon", "evening", "full_day"]); + +export const attendanceRecords = mysqlTable("attendance_records", { + id: id("id").primaryKey(), + studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }), + scheduleId: varchar("schedule_id", { length: 128 }), + date: date("date").notNull(), + status: attendanceStatusEnum.notNull(), + // L-6:节次考勤字段。null 表示全日记录(向后兼容已有数据)。 + period: attendancePeriodEnum, + remark: text("remark"), + reason: varchar("reason", { length: 255 }), + recordedBy: varchar("recorded_by", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + studentIdx: index("attendance_records_student_idx").on(table.studentId), + classIdx: index("attendance_records_class_idx").on(table.classId), + dateIdx: index("attendance_records_date_idx").on(table.date), + classDateIdx: index("attendance_records_class_date_idx").on(table.classId, table.date), + studentDateIdx: index("attendance_records_student_date_idx").on(table.studentId, table.date), + scheduleIdx: index("attendance_records_schedule_idx").on(table.scheduleId), + recordedByIdx: index("attendance_records_recorded_by_idx").on(table.recordedBy), + // L-6:班级 + 日期 + 节次复合索引,支持按节次查询班级当日点名 + classDatePeriodIdx: index("attendance_records_class_date_period_idx").on(table.classId, table.date, table.period), + classFk: foreignKey({ + columns: [table.classId], + foreignColumns: [classes.id], + name: "ar_c_fk", + }).onDelete("cascade"), + studentFk: foreignKey({ + columns: [table.studentId], + foreignColumns: [users.id], + name: "ar_s_fk", + }).onDelete("cascade"), + recordedByFk: foreignKey({ + columns: [table.recordedBy], + foreignColumns: [users.id], + name: "ar_rb_fk", + }).onDelete("cascade"), +})); + +export const attendanceRules = mysqlTable("attendance_rules", { + id: id("id").primaryKey(), + classId: varchar("class_id", { length: 128 }).references(() => classes.id, { onDelete: "cascade" }), + lateThresholdMinutes: int("late_threshold_minutes").default(15), + earlyLeaveThresholdMinutes: int("early_leave_threshold_minutes").default(15), + enableAutoMark: boolean("enable_auto_mark").default(false), + // L-3:出勤率阈值预警(教师端配置驱动) + attendanceRateThreshold: int("attendance_rate_threshold").default(90), + consecutiveAbsenceThreshold: int("consecutive_absence_threshold").default(3), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").onUpdateNow().notNull(), +}, (table) => ({ + classIdx: index("attendance_rules_class_idx").on(table.classId), + classFk: foreignKey({ + columns: [table.classId], + foreignColumns: [classes.id], + name: "atr_c_fk", + }).onDelete("cascade"), +})); + +// --- 18b. Leave Requests (L-5 在线请假流程) --- + +/** + * 请假类型。 + * - sick: 病假 + * - personal: 事假 + * - family: 家庭事务 + * - other: 其他 + */ +export const leaveTypeEnum = mysqlEnum("leave_type", [ + "sick", + "personal", + "family", + "other", +]); + +/** + * 请假审批状态机:pending → approved/rejected;approved/pending 可被 requester 取消为 cancelled。 + * - pending: 待审批 + * - approved: 已批准(自动同步考勤为 excused) + * - rejected: 已拒绝 + * - cancelled: 已撤销(申请人在审批前主动撤销) + */ +export const leaveStatusEnum = mysqlEnum("leave_status", [ + "pending", + "approved", + "rejected", + "cancelled", +]); + +export const leaveRequests = mysqlTable("leave_requests", { + id: id("id").primaryKey(), + /** 请假学生 ID */ + studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + /** 提交人 ID(家长代子女提交时为家长 userId;学生本人提交时等于 studentId) */ + requesterId: varchar("requester_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + /** 学生所属班级 ID(提交时快照,便于按班级筛选审批) */ + classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }), + leaveType: leaveTypeEnum.notNull(), + /** 起始日期(含) */ + startDate: date("start_date").notNull(), + /** 结束日期(含) */ + endDate: date("end_date").notNull(), + /** 请假事由 */ + reason: text("reason").notNull(), + status: leaveStatusEnum.default("pending").notNull(), + /** 审批人 ID(pending/cancelled 状态为 null) */ + reviewerId: varchar("reviewer_id", { length: 128 }).references(() => users.id, { onDelete: "set null" }), + /** 审批意见(拒绝时必填,批准时可选) */ + reviewComment: text("review_comment"), + reviewedAt: timestamp("reviewed_at"), + /** 是否已同步考勤记录(approved 后异步写入 attendance_records,置为 true) */ + attendanceSynced: boolean("attendance_synced").default(false).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + studentIdx: index("leave_requests_student_idx").on(table.studentId), + requesterIdx: index("leave_requests_requester_idx").on(table.requesterId), + classIdx: index("leave_requests_class_idx").on(table.classId), + statusIdx: index("leave_requests_status_idx").on(table.status), + reviewerIdx: index("leave_requests_reviewer_idx").on(table.reviewerId), + /** 班级+状态查询索引(教师审批列表常用) */ + classStatusIdx: index("leave_requests_class_status_idx").on(table.classId, table.status), + /** 学生+状态查询索引(家长/学生列表常用) */ + studentStatusIdx: index("leave_requests_student_status_idx").on(table.studentId, table.status), + classFk: foreignKey({ + columns: [table.classId], + foreignColumns: [classes.id], + name: "lr_c_fk", + }).onDelete("cascade"), + studentFk: foreignKey({ + columns: [table.studentId], + foreignColumns: [users.id], + name: "lr_s_fk", + }).onDelete("cascade"), + requesterFk: foreignKey({ + columns: [table.requesterId], + foreignColumns: [users.id], + name: "lr_rq_fk", + }).onDelete("cascade"), + reviewerFk: foreignKey({ + columns: [table.reviewerId], + foreignColumns: [users.id], + name: "lr_rv_fk", + }).onDelete("set null"), +})); diff --git a/src/shared/db/schema/audit.ts b/src/shared/db/schema/audit.ts new file mode 100644 index 0000000..c24ad25 --- /dev/null +++ b/src/shared/db/schema/audit.ts @@ -0,0 +1,116 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + index, + mysqlEnum, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; + +// --- 9. Audit & Login Logs --- + +export const auditLogStatusEnum = mysqlEnum("status", ["success", "failure"]); + +export const auditLogs = mysqlTable("audit_logs", { + id: id("id").primaryKey(), + userId: varchar("user_id", { length: 128 }).notNull(), + userName: varchar("user_name", { length: 255 }).notNull(), + action: varchar("action", { length: 255 }).notNull(), + module: varchar("module", { length: 128 }).notNull(), + targetId: varchar("target_id", { length: 128 }), + targetType: varchar("target_type", { length: 128 }), + detail: text("detail"), + ipAddress: varchar("ip_address", { length: 45 }), + userAgent: varchar("user_agent", { length: 512 }), + status: auditLogStatusEnum.default("success").notNull(), + createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), +}, (table) => ({ + userIdIdx: index("audit_logs_user_id_idx").on(table.userId), + moduleIdx: index("audit_logs_module_idx").on(table.module), + actionIdx: index("audit_logs_action_idx").on(table.action), + statusIdx: index("audit_logs_status_idx").on(table.status), + createdAtIdx: index("audit_logs_created_at_idx").on(table.createdAt), +})); + +export const loginLogActionEnum = mysqlEnum("action", ["signin", "signout", "signup"]); +export const loginLogStatusEnum = mysqlEnum("status", ["success", "failure"]); + +export const loginLogs = mysqlTable("login_logs", { + id: id("id").primaryKey(), + userId: varchar("user_id", { length: 128 }), + userEmail: varchar("user_email", { length: 255 }).notNull(), + action: loginLogActionEnum.notNull(), + status: loginLogStatusEnum.default("success").notNull(), + ipAddress: varchar("ip_address", { length: 45 }), + userAgent: varchar("user_agent", { length: 512 }), + errorMessage: text("error_message"), + createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), +}, (table) => ({ + userIdIdx: index("login_logs_user_id_idx").on(table.userId), + userEmailIdx: index("login_logs_user_email_idx").on(table.userEmail), + actionIdx: index("login_logs_action_idx").on(table.action), + statusIdx: index("login_logs_status_idx").on(table.status), + createdAtIdx: index("login_logs_created_at_idx").on(table.createdAt), +})); + +// --- 9b. Invitation Codes (注册邀请码,audit-P2-3 新增) --- + +/** + * 注册邀请码表(audit-P2-3) + * + * 管理员批量生成,用于: + * - 预分配注册角色(student/teacher/parent/admin) + * - 可选绑定邮箱(限定指定用户使用) + * - 可选绑定班级(注册后自动加入班级) + * - 可选过期时间 + */ +export const invitationCodes = mysqlTable("invitation_codes", { + id: id("id").primaryKey(), + /** 邀请码明文(8-12 位大写字母+数字组合,unique) */ + code: varchar("code", { length: 64 }).notNull().unique(), + /** 可选:限定邮箱使用(未设置则任意邮箱可用) */ + email: varchar("email", { length: 255 }), + /** 注册后分配的角色(student/teacher/parent/admin) */ + role: varchar("role", { length: 50 }).notNull(), + /** 可选:注册后自动加入的班级 ID */ + classId: varchar("class_id", { length: 128 }), + /** 可选:管理员备注 */ + notes: varchar("notes", { length: 500 }), + createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), + /** 创建者(管理员 userId) */ + createdBy: varchar("created_by", { length: 128 }), + /** 可选:过期时间(未设置则永久有效) */ + expiresAt: timestamp("expires_at", { mode: "date" }), + /** 已使用者 userId(未使用为 null) */ + usedBy: varchar("used_by", { length: 128 }), + /** 使用时间(未使用为 null) */ + usedAt: timestamp("used_at", { mode: "date" }), +}, (table) => ({ + codeIdx: index("invitation_codes_code_idx").on(table.code), + emailIdx: index("invitation_codes_email_idx").on(table.email), + usedByIdx: index("invitation_codes_used_by_idx").on(table.usedBy), +})); + +// --- 10. Data Change Logs (数据变更日志) --- + +export const dataChangeLogActionEnum = mysqlEnum("action", ["create", "update", "delete"]); + +export const dataChangeLogs = mysqlTable("data_change_logs", { + id: varchar("id", { length: 128 }).primaryKey(), + tableName: varchar("table_name", { length: 128 }).notNull(), + recordId: varchar("record_id", { length: 128 }).notNull(), + action: dataChangeLogActionEnum.notNull(), + oldValue: text("old_value"), + newValue: text("new_value"), + changedBy: varchar("changed_by", { length: 128 }).notNull(), + changedByName: varchar("changed_by_name", { length: 255 }).notNull(), + ipAddress: varchar("ip_address", { length: 45 }), + createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), +}, (table) => ({ + tableNameIdx: index("data_change_logs_table_name_idx").on(table.tableName), + recordIdIdx: index("data_change_logs_record_id_idx").on(table.recordId), + actionIdx: index("data_change_logs_action_idx").on(table.action), + changedByIdx: index("data_change_logs_changed_by_idx").on(table.changedBy), + createdAtIdx: index("data_change_logs_created_at_idx").on(table.createdAt), +})); diff --git a/src/shared/db/schema/classes.ts b/src/shared/db/schema/classes.ts new file mode 100644 index 0000000..99c931d --- /dev/null +++ b/src/shared/db/schema/classes.ts @@ -0,0 +1,159 @@ +import { + mysqlTable, + varchar, + timestamp, + int, + index, + mysqlEnum, + foreignKey, + primaryKey, + uniqueIndex, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; +import { schools, grades } from "./school"; +import { subjects } from "./academic"; + +// --- 6. Classes / Enrollment / Schedule --- + +export const classEnrollmentStatusEnum = mysqlEnum("class_enrollment_status", ["active", "inactive"]); + +export const classes = mysqlTable("classes", { + id: id("id").primaryKey(), + schoolName: varchar("school_name", { length: 255 }), + schoolId: varchar("school_id", { length: 128 }), + name: varchar("name", { length: 255 }).notNull(), + grade: varchar("grade", { length: 50 }).notNull(), + gradeId: varchar("grade_id", { length: 128 }), + homeroom: varchar("homeroom", { length: 50 }), + room: varchar("room", { length: 50 }), + invitationCode: varchar("invitation_code", { length: 6 }).unique(), + teacherId: varchar("teacher_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + teacherIdx: index("classes_teacher_idx").on(table.teacherId), + gradeIdx: index("classes_grade_idx").on(table.grade), + schoolIdx: index("classes_school_idx").on(table.schoolId), + gradeIdIdx: index("classes_grade_id_idx").on(table.gradeId), + schoolFk: foreignKey({ + columns: [table.schoolId], + foreignColumns: [schools.id], + name: "c_s_fk", + }).onDelete("set null"), + gradeFk: foreignKey({ + columns: [table.gradeId], + foreignColumns: [grades.id], + name: "c_g_fk", + }).onDelete("set null"), +})); + +export const classSubjectTeachers = mysqlTable("class_subject_teachers", { + classId: varchar("class_id", { length: 128 }).notNull(), + subjectId: varchar("subject_id", { length: 128 }).notNull(), + teacherId: varchar("teacher_id", { length: 128 }).references(() => users.id, { onDelete: "set null" }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + pk: primaryKey({ columns: [table.classId, table.subjectId] }), + classIdx: index("class_subject_teachers_class_idx").on(table.classId), + teacherIdx: index("class_subject_teachers_teacher_idx").on(table.teacherId), + subjectIdIdx: index("class_subject_teachers_subject_id_idx").on(table.subjectId), + classFk: foreignKey({ + columns: [table.classId], + foreignColumns: [classes.id], + name: "cst_c_fk", + }).onDelete("cascade"), + subjectFk: foreignKey({ + columns: [table.subjectId], + foreignColumns: [subjects.id], + name: "cst_s_fk", + }).onDelete("cascade"), +})); + +/** + * 班级邀请码表(v3 新增,对标 Google Classroom / 钉钉教育 / 智学网)。 + * + * 设计要点: + * - 独立表而非挂在 classes 表上,支持有效期/次数/审计/多码并存 + * - 6 位字母数字(剔除歧义字符 0/O/1/I/L),空间 22^6 ≈ 1.13 亿 + * - status 枚举:active/disabled/expired/exhausted + * - max_uses NULL=无限;expires_at NULL=永久 + * - 软删除:revoke 时设置 status=disabled + revoked_at,不物理删除(审计需要) + * + * 与 classes.invitationCode 的关系: + * - 新表上线后,enrollStudentByInvitationCode / enrollTeacherByInvitationCode 优先查新表 + * - classes.invitationCode 保留作为 fallback,下个版本移除 + */ +export const classInvitationCodeStatusEnum = mysqlEnum("class_invitation_code_status", [ + "active", + "disabled", + "expired", + "exhausted", +]); + +export const classInvitationCodes = mysqlTable("class_invitation_codes", { + id: id("id").primaryKey(), + classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }), + code: varchar("code", { length: 8 }).notNull().unique(), + status: classInvitationCodeStatusEnum.default("active").notNull(), + maxUses: int("max_uses"), + usedCount: int("used_count").default(0).notNull(), + expiresAt: timestamp("expires_at"), + createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), + revokedAt: timestamp("revoked_at"), + revokedBy: varchar("revoked_by", { length: 128 }), + note: varchar("note", { length: 255 }), +}, (table) => ({ + codeIdx: uniqueIndex("class_invitation_codes_code_idx").on(table.code), + classIdx: index("class_invitation_codes_class_idx").on(table.classId), + statusExpiresIdx: index("class_invitation_codes_status_expires_idx").on(table.status, table.expiresAt), + classFk: foreignKey({ + columns: [table.classId], + foreignColumns: [classes.id], + name: "cic_c_fk", + }).onDelete("cascade"), +})); + +export const classEnrollments = mysqlTable("class_enrollments", { + classId: varchar("class_id", { length: 128 }).notNull(), + studentId: varchar("student_id", { length: 128 }).notNull(), + status: classEnrollmentStatusEnum.default("active").notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), +}, (table) => ({ + pk: primaryKey({ columns: [table.classId, table.studentId] }), + classIdx: index("class_enrollments_class_idx").on(table.classId), + studentIdx: index("class_enrollments_student_idx").on(table.studentId), + classFk: foreignKey({ + columns: [table.classId], + foreignColumns: [classes.id], + name: "ce_c_fk", + }).onDelete("cascade"), + studentFk: foreignKey({ + columns: [table.studentId], + foreignColumns: [users.id], + name: "ce_s_fk", + }).onDelete("cascade"), +})); + +export const classSchedule = mysqlTable("class_schedule", { + id: id("id").primaryKey(), + classId: varchar("class_id", { length: 128 }).notNull(), + weekday: int("weekday").notNull(), + startTime: varchar("start_time", { length: 5 }).notNull(), + endTime: varchar("end_time", { length: 5 }).notNull(), + course: varchar("course", { length: 255 }).notNull(), + location: varchar("location", { length: 100 }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + classIdx: index("class_schedule_class_idx").on(table.classId), + classDayIdx: index("class_schedule_class_day_idx").on(table.classId, table.weekday), + classFk: foreignKey({ + columns: [table.classId], + foreignColumns: [classes.id], + name: "cs_c_fk", + }).onDelete("cascade"), +})); diff --git a/src/shared/db/schema/course-plans.ts b/src/shared/db/schema/course-plans.ts new file mode 100644 index 0000000..4e1dee6 --- /dev/null +++ b/src/shared/db/schema/course-plans.ts @@ -0,0 +1,90 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, + date, + foreignKey, + mysqlEnum, + boolean, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; +import { classes } from "./classes"; +import { subjects } from "./academic"; + +// --- 13. Course Plans (课程计划) --- + +export const coursePlanStatusEnum = mysqlEnum("status", ["planning", "active", "completed", "paused"]); +export const coursePlanSemesterEnum = mysqlEnum("semester", ["1", "2"]); + +export const coursePlans = mysqlTable("course_plans", { + id: id("id").primaryKey(), + classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }), + subjectId: varchar("subject_id", { length: 128 }).notNull().references(() => subjects.id, { onDelete: "cascade" }), + teacherId: varchar("teacher_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + academicYearId: varchar("academic_year_id", { length: 128 }), + semester: coursePlanSemesterEnum.default("1").notNull(), + totalHours: int("total_hours").default(0).notNull(), + completedHours: int("completed_hours").default(0).notNull(), + weeklyHours: int("weekly_hours").default(0).notNull(), + startDate: date("start_date"), + endDate: date("end_date"), + syllabus: text("syllabus"), + objectives: text("objectives"), + status: coursePlanStatusEnum.default("planning").notNull(), + createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + classIdx: index("course_plans_class_idx").on(table.classId), + teacherIdx: index("course_plans_teacher_idx").on(table.teacherId), + subjectIdx: index("course_plans_subject_idx").on(table.subjectId), + statusIdx: index("course_plans_status_idx").on(table.status), + classSubjectIdx: index("course_plans_class_subject_idx").on(table.classId, table.subjectId), + classFk: foreignKey({ + columns: [table.classId], + foreignColumns: [classes.id], + name: "cp_c_fk", + }).onDelete("cascade"), + subjectFk: foreignKey({ + columns: [table.subjectId], + foreignColumns: [subjects.id], + name: "cp_s_fk", + }).onDelete("cascade"), + teacherFk: foreignKey({ + columns: [table.teacherId], + foreignColumns: [users.id], + name: "cp_t_fk", + }).onDelete("cascade"), + createdByFk: foreignKey({ + columns: [table.createdBy], + foreignColumns: [users.id], + name: "cp_cb_fk", + }).onDelete("cascade"), +})); + +export const coursePlanItems = mysqlTable("course_plan_items", { + id: id("id").primaryKey(), + planId: varchar("plan_id", { length: 128 }).notNull().references(() => coursePlans.id, { onDelete: "cascade" }), + week: int("week").notNull(), + topic: varchar("topic", { length: 255 }).notNull(), + content: text("content"), + hours: int("hours").default(2).notNull(), + textbookChapter: varchar("textbook_chapter", { length: 255 }), + notes: text("notes"), + isCompleted: boolean("is_completed").default(false).notNull(), + completedAt: date("completed_at"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + planIdx: index("course_plan_items_plan_idx").on(table.planId), + planWeekIdx: index("course_plan_items_plan_week_idx").on(table.planId, table.week), + planFk: foreignKey({ + columns: [table.planId], + foreignColumns: [coursePlans.id], + name: "cpi_p_fk", + }).onDelete("cascade"), +})); diff --git a/src/shared/db/schema/elective-courses.ts b/src/shared/db/schema/elective-courses.ts new file mode 100644 index 0000000..cf293e9 --- /dev/null +++ b/src/shared/db/schema/elective-courses.ts @@ -0,0 +1,73 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, + date, + datetime, + decimal, + mysqlEnum, + primaryKey, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; +import { subjects } from "./academic"; +import { grades } from "./school"; + +// --- 21. Elective Course Management (选课管理) --- + +export const electiveCourseStatusEnum = mysqlEnum("status", ["draft", "open", "closed", "cancelled"]); +export const electiveSelectionModeEnum = mysqlEnum("selection_mode", ["fcfs", "lottery"]); +export const courseSelectionStatusEnum = mysqlEnum("selection_status", ["selected", "enrolled", "waitlist", "dropped", "rejected"]); + +export const electiveCourses = mysqlTable("elective_courses", { + id: id("id").primaryKey(), + name: varchar("name", { length: 255 }).notNull(), + subjectId: varchar("subject_id", { length: 128 }).references(() => subjects.id, { onDelete: "set null" }), + teacherId: varchar("teacher_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + gradeId: varchar("grade_id", { length: 128 }).references(() => grades.id, { onDelete: "set null" }), + description: text("description"), + capacity: int("capacity").default(30).notNull(), + enrolledCount: int("enrolled_count").default(0).notNull(), + classroom: varchar("classroom", { length: 100 }), + schedule: varchar("schedule", { length: 255 }), + startDate: date("start_date"), + endDate: date("end_date"), + selectionStartAt: datetime("selection_start_at"), + selectionEndAt: datetime("selection_end_at"), + /** 退课截止时间(P2-4 新增):超过此时间学生不可退课;为 null 表示不限制 */ + dropDeadline: datetime("drop_deadline"), + status: electiveCourseStatusEnum.default("draft").notNull(), + selectionMode: electiveSelectionModeEnum.default("fcfs").notNull(), + credit: decimal("credit", { precision: 3, scale: 1 }).default("1.0"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + teacherIdx: index("elective_courses_teacher_idx").on(table.teacherId), + subjectIdx: index("elective_courses_subject_idx").on(table.subjectId), + gradeIdx: index("elective_courses_grade_idx").on(table.gradeId), + statusIdx: index("elective_courses_status_idx").on(table.status), +})); + +export const courseSelections = mysqlTable("course_selections", { + id: id("id").primaryKey(), + courseId: varchar("course_id", { length: 128 }).notNull().references(() => electiveCourses.id, { onDelete: "cascade" }), + studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + status: courseSelectionStatusEnum.default("selected").notNull(), + priority: int("priority").default(1), + selectedAt: timestamp("selected_at").defaultNow().notNull(), + enrolledAt: timestamp("enrolled_at"), + droppedAt: timestamp("dropped_at"), + /** 退课理由(P2-4 新增):学生退课时可选填写的理由,便于学校运营分析 */ + dropReason: varchar("drop_reason", { length: 255 }), + lotteryRank: int("lottery_rank"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + courseStudentPk: primaryKey({ columns: [table.courseId, table.studentId] }), + courseIdx: index("course_selections_course_idx").on(table.courseId), + studentIdx: index("course_selections_student_idx").on(table.studentId), + statusIdx: index("course_selections_status_idx").on(table.status), +})); diff --git a/src/shared/db/schema/error-book.ts b/src/shared/db/schema/error-book.ts new file mode 100644 index 0000000..307b856 --- /dev/null +++ b/src/shared/db/schema/error-book.ts @@ -0,0 +1,95 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, + json, + mysqlEnum, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; +import { questions } from "./questions"; + +// --- 26. Error Book (错题本) --- + +export const errorBookSourceTypeEnum = mysqlEnum("source_type", ["exam", "homework", "manual"]); +export const errorBookStatusEnum = mysqlEnum("error_status", ["new", "learning", "mastered", "archived"]); +export const errorBookReviewResultEnum = mysqlEnum("review_result", ["again", "hard", "good", "easy"]); + +/** + * 错题本条目 - 存储学生的错题记录。 + * 来源可以是考试提交、作业提交,或学生手动添加。 + * 采用简化版 SM-2 间隔重复算法调度复习。 + */ +export const errorBookItems = mysqlTable("error_book_items", { + id: id("id").primaryKey(), + studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + questionId: varchar("question_id", { length: 128 }).notNull().references(() => questions.id), + + // 来源 + sourceType: errorBookSourceTypeEnum.default("manual").notNull(), + /** exam_submission_id / homework_submission_id / null(手动添加) */ + sourceId: varchar("source_id", { length: 128 }), + + // 作答快照(冗余存储,避免源记录被删除后丢失上下文) + studentAnswer: json("student_answer"), + correctAnswer: json("correct_answer"), + + // 分类元数据(冗余,加速筛选) + subjectId: varchar("subject_id", { length: 128 }), + knowledgePointIds: json("knowledge_point_ids"), + + // 状态与掌握度 + status: errorBookStatusEnum.default("new").notNull(), + /** 0-5 掌握程度,0=未学,5=已掌握 */ + masteryLevel: int("mastery_level").default(0).notNull(), + + // SM-2 间隔重复参数 + nextReviewAt: timestamp("next_review_at", { mode: "date" }), + /** 复习间隔(天) */ + reviewInterval: int("review_interval").default(1).notNull(), + reviewCount: int("review_count").default(0).notNull(), + /** 连续答对次数(用于判断是否标记为已掌握) */ + correctStreak: int("correct_streak").default(0).notNull(), + + // 学生笔记与反思 + note: text("note"), + /** 错误原因标签(JSON 数组,如 ["粗心", "概念不清", "计算错误"]) */ + errorTags: json("error_tags"), + + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + studentIdx: index("eb_item_student_idx").on(table.studentId), + studentStatusIdx: index("eb_item_student_status_idx").on(table.studentId, table.status), + studentReviewIdx: index("eb_item_student_review_idx").on(table.studentId, table.nextReviewAt), + questionIdx: index("eb_item_question_idx").on(table.questionId), + subjectIdx: index("eb_item_subject_idx").on(table.subjectId), + sourceIdx: index("eb_item_source_idx").on(table.sourceType, table.sourceId), +})); + +/** + * 错题复习记录 - 每次复习的历史快照。 + * 用于追踪复习进度和算法调参。 + */ +export const errorBookReviews = mysqlTable("error_book_reviews", { + id: id("id").primaryKey(), + itemId: varchar("item_id", { length: 128 }).notNull().references(() => errorBookItems.id, { onDelete: "cascade" }), + studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + + /** 复习自评结果:again(重来) / hard(困难) / good(良好) / easy(简单) */ + result: errorBookReviewResultEnum.notNull(), + reviewedAt: timestamp("reviewed_at").defaultNow().notNull(), + + /** 本次复习后的新间隔(天) */ + newInterval: int("new_interval"), + newMasteryLevel: int("new_mastery_level"), + + createdAt: timestamp("created_at").defaultNow().notNull(), +}, (table) => ({ + itemIdx: index("eb_review_item_idx").on(table.itemId), + studentIdx: index("eb_review_student_idx").on(table.studentId), + studentReviewedIdx: index("eb_review_student_reviewed_idx").on(table.studentId, table.reviewedAt), +})); diff --git a/src/shared/db/schema/exams.ts b/src/shared/db/schema/exams.ts new file mode 100644 index 0000000..647ff18 --- /dev/null +++ b/src/shared/db/schema/exams.ts @@ -0,0 +1,136 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, + json, + mysqlEnum, + boolean, + primaryKey, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; +import { subjects } from "./academic"; +import { grades } from "./school"; +import { questions } from "./questions"; + +// --- 7. Exam Proctoring Config (考试监考配置) --- + +export const examModeEnum = mysqlEnum("exam_mode", ["homework", "timed", "proctored"]); +export const proctoringEventTypeEnum = mysqlEnum("event_type", [ + "tab_switch", "window_blur", "copy_attempt", "paste_attempt", + "right_click", "devtools_open", "fullscreen_exit", "idle_timeout" +]); + +export const exams = mysqlTable("exams", { + id: id("id").primaryKey(), + title: varchar("title", { length: 255 }).notNull(), + description: text("description"), + + /** + * Stores the hierarchical structure of the exam. + * Expected JSON Schema (3-level: section > group > question): + * type ExamStructure = Array< + * | { type: 'section', title: string, level?: number, children: ExamStructure } + * | { type: 'group', title: string, instruction?: string, children: ExamStructure } + * | { type: 'question', questionId: string, score: number } + * > + * + * Note: This is for UI presentation/ordering. + * Real relational integrity is maintained in 'exam_questions' table. + */ + structure: json("structure"), + + creatorId: varchar("creator_id", { length: 128 }).notNull().references(() => users.id), + + // Link to Subjects and Grades + subjectId: varchar("subject_id", { length: 128 }).references(() => subjects.id), + gradeId: varchar("grade_id", { length: 128 }).references(() => grades.id), + + startTime: timestamp("start_time"), + endTime: timestamp("end_time"), + + // P2: Online exam mode + proctoring settings + examMode: examModeEnum.default("homework"), + durationMinutes: int("duration_minutes"), + shuffleQuestions: boolean("shuffle_questions").default(false), + allowLateStart: boolean("allow_late_start").default(false), + lateStartGraceMinutes: int("late_start_grace_minutes").default(0), + antiCheatEnabled: boolean("anti_cheat_enabled").default(false), + + // Status: draft, published, ongoing, finished + status: varchar("status", { length: 50 }).default("draft"), + + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + subjectIdx: index("exams_subject_idx").on(table.subjectId), + gradeIdx: index("exams_grade_idx").on(table.gradeId), + // P3-6: 考试状态按时间排序、按创建者筛选的高频查询索引 + statusCreatedIdx: index("exams_status_created_idx").on(table.status, table.createdAt), + creatorIdx: index("exams_creator_idx").on(table.creatorId), +})); + +// Linking Exams to Questions (Many-to-Many often, or One-to-Many if specific to exam) +// Usually questions are reused, so Many-to-Many +export const examQuestions = mysqlTable("exam_questions", { + examId: varchar("exam_id", { length: 128 }).notNull().references(() => exams.id, { onDelete: "cascade" }), + questionId: varchar("question_id", { length: 128 }).notNull().references(() => questions.id, { onDelete: "cascade" }), + score: int("score").default(0), + order: int("order").default(0), +}, (table) => ({ + pk: primaryKey({ columns: [table.examId, table.questionId] }), +})); + +export const examSubmissions = mysqlTable("exam_submissions", { + id: id("id").primaryKey(), + examId: varchar("exam_id", { length: 128 }).notNull().references(() => exams.id, { onDelete: "cascade" }), + studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + + score: int("score"), // Total score + status: varchar("status", { length: 50 }).default("started"), // started, submitted, graded + + submittedAt: timestamp("submitted_at"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + examStudentIdx: index("exam_student_idx").on(table.examId, table.studentId), + // P3-6: 提交记录按考试状态筛选、按提交时间排序的高频查询索引 + examStatusIdx: index("exam_submissions_exam_status_idx").on(table.examId, table.status), + submittedAtIdx: index("exam_submissions_submitted_at_idx").on(table.submittedAt), +})); + +export const submissionAnswers = mysqlTable("submission_answers", { + id: id("id").primaryKey(), + submissionId: varchar("submission_id", { length: 128 }).notNull().references(() => examSubmissions.id, { onDelete: "cascade" }), + questionId: varchar("question_id", { length: 128 }).notNull().references(() => questions.id), + + answerContent: json("answer_content"), // Student's answer + score: int("score"), // Score for this specific question + feedback: text("feedback"), // Teacher's feedback + + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + submissionIdx: index("submission_idx").on(table.submissionId), +})); + +// --- 22. Exam Proctoring Events (考试监考事件) --- + +export const examProctoringEvents = mysqlTable("exam_proctoring_events", { + id: id("id").primaryKey(), + submissionId: varchar("submission_id", { length: 128 }).notNull().references(() => examSubmissions.id, { onDelete: "cascade" }), + studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + examId: varchar("exam_id", { length: 128 }).notNull().references(() => exams.id, { onDelete: "cascade" }), + eventType: proctoringEventTypeEnum.notNull(), + eventDetail: text("event_detail"), + occurredAt: timestamp("occurred_at").defaultNow().notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), +}, (table) => ({ + submissionIdx: index("proctoring_submission_idx").on(table.submissionId), + studentIdx: index("proctoring_student_idx").on(table.studentId), + examIdx: index("proctoring_exam_idx").on(table.examId), + eventTypeIdx: index("proctoring_event_type_idx").on(table.eventType), +})); diff --git a/src/shared/db/schema/files.ts b/src/shared/db/schema/files.ts new file mode 100644 index 0000000..a137d1d --- /dev/null +++ b/src/shared/db/schema/files.ts @@ -0,0 +1,28 @@ +import { + mysqlTable, + varchar, + timestamp, + index, + bigint, +} from "drizzle-orm/mysql-core"; +import { users } from "./users"; + +// --- 12. File Attachments (文件附件) --- + +export const fileAttachments = mysqlTable("file_attachments", { + id: varchar("id", { length: 128 }).primaryKey(), + filename: varchar("filename", { length: 255 }).notNull(), + originalName: varchar("original_name", { length: 255 }).notNull(), + mimeType: varchar("mime_type", { length: 128 }).notNull(), + size: bigint("size", { mode: "number" }).notNull(), + storagePath: varchar("storage_path", { length: 512 }).notNull(), + url: varchar("url", { length: 512 }), + uploaderId: varchar("uploader_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + targetType: varchar("target_type", { length: 128 }), + targetId: varchar("target_id", { length: 128 }), + createdAt: timestamp("created_at").defaultNow().notNull(), +}, (table) => ({ + uploaderIdx: index("file_attachments_uploader_idx").on(table.uploaderId), + targetIdx: index("file_attachments_target_idx").on(table.targetType, table.targetId), + createdAtIdx: index("file_attachments_created_at_idx").on(table.createdAt), +})); diff --git a/src/shared/db/schema/formative-assessment.ts b/src/shared/db/schema/formative-assessment.ts new file mode 100644 index 0000000..6b88e44 --- /dev/null +++ b/src/shared/db/schema/formative-assessment.ts @@ -0,0 +1,137 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, + json, + date, + boolean, + foreignKey, + uniqueIndex, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; +import { lessonPlans } from "./lesson-preparation"; +import { standards } from "./standards"; + +// --- 30. Formative Assessment (M5 形成性评价闭环) --- + +/** + * M5 互动组件:发布课案时嵌入的 poll/quiz。 + * 学生作答后结果回写到 lessonPlanFormativeResponses。 + */ +export const lessonPlanFormativeItems = mysqlTable("lesson_plan_formative_items", { + id: id("id").primaryKey(), + planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), + blockId: varchar("block_id", { length: 128 }).notNull(), + /** 互动类型:poll / quiz / exit_ticket */ + interactionType: varchar("interaction_type", { length: 20 }).notNull(), + /** 题目内容(JSON:题目/选项/正确答案) */ + payload: json("payload").notNull(), + /** 是否启用实时反馈 */ + instantFeedback: boolean("instant_feedback").default(false).notNull(), + orderIndex: int("order_index").default(0).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + planBlockIdx: index("lpfi_plan_block_idx").on(table.planId, table.blockId), +})); + +/** M5 学生作答记录 */ +export const lessonPlanFormativeResponses = mysqlTable("lesson_plan_formative_responses", { + id: id("id").primaryKey(), + // 显式 foreignKey 见表参数(避免自动生成 FK 名过长 > 64 字符) + itemId: varchar("item_id", { length: 128 }).notNull(), + studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + classId: varchar("class_id", { length: 128 }), + /** 学生作答(JSON) */ + response: json("response").notNull(), + /** 是否正确(quiz 类型适用) */ + isCorrect: boolean("is_correct"), + /** 作答耗时(秒) */ + durationSec: int("duration_sec"), + createdAt: timestamp("created_at").defaultNow().notNull(), +}, (table) => ({ + itemIdx: index("lpfr_item_idx").on(table.itemId), + studentIdx: index("lpfr_student_idx").on(table.studentId), + classIdx: index("lpfr_class_idx").on(table.classId), + itemFk: foreignKey({ + columns: [table.itemId], + foreignColumns: [lessonPlanFormativeItems.id], + name: "lpfr_item_fk", + }).onDelete("cascade"), +})); + +// --- 31. Lesson Plan Analytics (M10 备课分析仪表盘) --- + +/** + * M10 备课分析快照:按日聚合的备课投入/质量统计。 + * 用于仪表盘展示"教师备课投入""模板使用率""课标覆盖热力图"。 + */ +export const lessonPlanAnalyticsDaily = mysqlTable("lesson_plan_analytics_daily", { + id: id("id").primaryKey(), + snapshotDate: date("snapshot_date").notNull(), + teacherId: varchar("teacher_id", { length: 128 }).notNull().references(() => users.id), + subjectId: varchar("subject_id", { length: 128 }), + gradeId: varchar("grade_id", { length: 128 }), + /** 当日新建课案数 */ + newPlansCount: int("new_plans_count").default(0).notNull(), + /** 当日编辑时长(分钟) */ + editDurationMin: int("edit_duration_min").default(0).notNull(), + /** 当日保存版本数 */ + savedVersionsCount: int("saved_versions_count").default(0).notNull(), + /** 当日提交审核数 */ + submittedCount: int("submitted_count").default(0).notNull(), + /** 当日发布数 */ + publishedCount: int("published_count").default(0).notNull(), + /** 关联的课标数量 */ + standardsLinked: int("standards_linked").default(0).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), +}, (table) => ({ + dateTeacherIdx: uniqueIndex("lpad_date_teacher_idx").on(table.snapshotDate, table.teacherId), + dateIdx: index("lpad_date_idx").on(table.snapshotDate), + subjectGradeIdx: index("lpad_subject_grade_idx").on(table.subjectId, table.gradeId), +})); + +// --- 32. Lesson Plan AI Evaluation (M8 AI 评估) --- + +/** + * M8 AI 课案质量评估记录:保存 AI 对课案的质量评分与建议。 + */ +export const lessonPlanAiEvaluations = mysqlTable("lesson_plan_ai_evaluations", { + id: id("id").primaryKey(), + planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), + versionNo: int("version_no").notNull(), + /** 总体评分(0-100) */ + overallScore: int("overall_score"), + /** 维度评分(JSON:{ clarity, alignment, engagement, differentiation, assessment }) */ + dimensionScores: json("dimension_scores"), + /** AI 建议文本 */ + suggestions: text("suggestions"), + /** 推荐的课标 ID 列表(JSON) */ + recommendedStandardIds: json("recommended_standard_ids"), + evaluatedBy: varchar("evaluated_by", { length: 128 }).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), +}, (table) => ({ + planVersionIdx: index("lpae_plan_version_idx").on(table.planId, table.versionNo), + planIdx: index("lpae_plan_idx").on(table.planId), +})); + +// --- M1 课案 ↔ 课标 多对多关联 --- +// 注:lessonPlanStandards 同时引用 lessonPlans 和 standards,放在此处避免循环依赖。 + +/** M1 课案 ↔ 课标 多对多关联 */ +export const lessonPlanStandards = mysqlTable("lesson_plan_standards", { + id: id("id").primaryKey(), + planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), + standardId: varchar("standard_id", { length: 128 }).notNull().references(() => standards.id, { onDelete: "cascade" }), + /** 关联类型:primary(主要对标)/ related(相关对标) */ + relationType: varchar("relation_type", { length: 20 }).default("primary").notNull(), + createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id), + createdAt: timestamp("created_at").defaultNow().notNull(), +}, (table) => ({ + planStandardIdx: uniqueIndex("lps_plan_standard_idx").on(table.planId, table.standardId), + standardIdx: index("lps_standard_idx").on(table.standardId), +})); diff --git a/src/shared/db/schema/grade-records.ts b/src/shared/db/schema/grade-records.ts new file mode 100644 index 0000000..c4c9adf --- /dev/null +++ b/src/shared/db/schema/grade-records.ts @@ -0,0 +1,189 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + index, + decimal, + foreignKey, + mysqlEnum, + json, + uniqueIndex, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; +import { classes } from "./classes"; +import { subjects } from "./academic"; +import { questions } from "./questions"; + +// --- 11. Grade Records (成绩录入) --- + +export const gradeRecordTypeEnum = mysqlEnum("type", ["exam", "quiz", "homework", "other"]); +export const gradeRecordSemesterEnum = mysqlEnum("semester", ["1", "2"]); + +export const gradeRecords = mysqlTable("grade_records", { + id: id("id").primaryKey(), + studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }), + subjectId: varchar("subject_id", { length: 128 }).notNull().references(() => subjects.id, { onDelete: "cascade" }), + examId: varchar("exam_id", { length: 128 }), + academicYearId: varchar("academic_year_id", { length: 128 }), + title: varchar("title", { length: 255 }).notNull(), + score: decimal("score", { precision: 6, scale: 2 }).notNull(), + fullScore: decimal("full_score", { precision: 6, scale: 2 }).default("100").notNull(), + type: gradeRecordTypeEnum.default("exam").notNull(), + semester: gradeRecordSemesterEnum.default("1").notNull(), + recordedBy: varchar("recorded_by", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + remark: text("remark"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + studentIdx: index("grade_records_student_idx").on(table.studentId), + classIdx: index("grade_records_class_idx").on(table.classId), + subjectIdx: index("grade_records_subject_idx").on(table.subjectId), + examIdx: index("grade_records_exam_idx").on(table.examId), + classSubjectIdx: index("grade_records_class_subject_idx").on(table.classId, table.subjectId), + recordedByIdx: index("grade_records_recorded_by_idx").on(table.recordedBy), + classFk: foreignKey({ + columns: [table.classId], + foreignColumns: [classes.id], + name: "gr_c_fk", + }).onDelete("cascade"), + studentFk: foreignKey({ + columns: [table.studentId], + foreignColumns: [users.id], + name: "gr_s_fk", + }).onDelete("cascade"), + subjectFk: foreignKey({ + columns: [table.subjectId], + foreignColumns: [subjects.id], + name: "gr_sub_fk", + }).onDelete("cascade"), + recordedByFk: foreignKey({ + columns: [table.recordedBy], + foreignColumns: [users.id], + name: "gr_rb_fk", + }).onDelete("cascade"), +})); + +// --- 11.1 Grade Record Answers (成绩每题得分) --- + +export const gradeRecordAnswers = mysqlTable("grade_record_answers", { + id: id("id").primaryKey(), + gradeRecordId: varchar("grade_record_id", { length: 128 }).notNull().references(() => gradeRecords.id, { onDelete: "cascade" }), + questionId: varchar("question_id", { length: 128 }).notNull().references(() => questions.id, { onDelete: "cascade" }), + score: decimal("score", { precision: 6, scale: 2 }).notNull(), + fullScore: decimal("full_score", { precision: 6, scale: 2 }).notNull(), + feedback: text("feedback"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + gradeRecordIdx: index("grade_record_answers_record_idx").on(table.gradeRecordId), + questionIdx: index("grade_record_answers_question_idx").on(table.questionId), + gradeRecordFk: foreignKey({ + columns: [table.gradeRecordId], + foreignColumns: [gradeRecords.id], + name: "gra_gr_fk", + }).onDelete("cascade"), + questionFk: foreignKey({ + columns: [table.questionId], + foreignColumns: [questions.id], + name: "gra_q_fk", + }).onDelete("cascade"), +})); + +// --- 27. Grade Drafts (成绩录入草稿 - 服务端自动保存) --- + +/** + * 成绩录入草稿 - 用于跨设备恢复未保存的成绩。 + * v3-P2-10: 替代纯 localStorage 方案,支持换设备恢复。 + * 唯一键:userId + classId + subjectId + type,确保每位教师每组合只有一个草稿。 + */ +export const gradeDrafts = mysqlTable("grade_drafts", { + id: id("id").primaryKey(), + userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }), + subjectId: varchar("subject_id", { length: 128 }).notNull().references(() => subjects.id, { onDelete: "cascade" }), + type: varchar("type", { length: 20 }).notNull(), + /** 草稿内容:{ scores: Record, timestamp: number } */ + content: json("content").notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), + /** + * P3-6 协同录入锁机制: + * - lockedBy: 当前持有编辑锁的教师 ID(null 表示无锁) + * - lockedAt: 锁获取时间(用于过期判定,默认 5 分钟自动释放) + * - lockToken: 锁定令牌(前端续期时校验,防止伪造他人续期) + */ + lockedBy: varchar("locked_by", { length: 128 }), + lockedAt: timestamp("locked_at"), + lockToken: varchar("lock_token", { length: 64 }), +}, (table) => ({ + userDraftIdx: uniqueIndex("gd_user_class_subject_type_idx").on( + table.userId, + table.classId, + table.subjectId, + table.type + ), + userUpdatedIdx: index("gd_user_updated_idx").on(table.userId, table.updatedAt), + // P3-6: 班级-科目-类型粒度的锁查询索引(用于跨用户协同冲突检测) + classSubjectLockIdx: index("gd_class_subject_lock_idx").on( + table.classId, + table.subjectId, + table.type, + table.lockedBy + ), +})); + +/** + * P3-7 成绩申诉状态机枚举。 + * - pending: 学生已提交申诉,等待教师复核 + * - approved: 教师批准申诉(可能调整分数) + * - rejected: 教师驳回申诉(维持原分数) + * - withdrawn: 学生主动撤回申诉 + */ +export const gradeAppealStatusEnum = mysqlEnum("appeal_status", [ + "pending", + "approved", + "rejected", + "withdrawn", +]); + +/** + * 成绩申诉记录 - 学生对成绩提出异议,教师复核后修改并留痕。 + * + * P3-7: 满足合规性要求,成绩修改全程留痕。 + * 状态机:pending → approved/rejected/withdrawn + * 一条 grade_record 只允许有一条 pending 申诉,避免重复申诉。 + */ +export const gradeAppeals = mysqlTable("grade_appeals", { + id: id("id").primaryKey(), + gradeRecordId: varchar("grade_record_id", { length: 128 }).notNull().references(() => gradeRecords.id, { onDelete: "cascade" }), + studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + /** 申诉快照:申诉时的原始分数(审计留痕,防止后续修改后追溯困难) */ + originalScore: decimal("original_score", { precision: 6, scale: 2 }).notNull(), + /** 学生期望的分数(选填,便于教师判断诉求) */ + expectedScore: decimal("expected_score", { precision: 6, scale: 2 }), + /** 申诉理由 */ + reason: text("reason").notNull(), + status: gradeAppealStatusEnum.default("pending").notNull(), + /** 复核教师 ID(pending 状态为 null) */ + reviewerId: varchar("reviewer_id", { length: 128 }).references(() => users.id, { onDelete: "set null" }), + /** 教师复核意见 */ + reviewComment: text("review_comment"), + /** 审核后的调整分数(approved 且分数有变时填写) */ + adjustedScore: decimal("adjusted_score", { precision: 6, scale: 2 }), + reviewedAt: timestamp("reviewed_at"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + gradeRecordIdx: index("ga_grade_record_idx").on(table.gradeRecordId), + studentIdx: index("ga_student_idx").on(table.studentId), + statusIdx: index("ga_status_idx").on(table.status), + reviewerIdx: index("ga_reviewer_idx").on(table.reviewerId), + /** pending 状态查询索引(应用层校验唯一性,避免多次申诉) */ + pendingLookupIdx: index("ga_pending_lookup_idx").on( + table.gradeRecordId, + table.status + ), +})); diff --git a/src/shared/db/schema/homework.ts b/src/shared/db/schema/homework.ts new file mode 100644 index 0000000..71b7610 --- /dev/null +++ b/src/shared/db/schema/homework.ts @@ -0,0 +1,139 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, + json, + boolean, + foreignKey, + primaryKey, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; +import { exams } from "./exams"; +import { questions } from "./questions"; + +// --- Homework Assignments & Submissions --- + +export const homeworkAssignments = mysqlTable("homework_assignments", { + id: id("id").primaryKey(), + sourceExamId: varchar("source_exam_id", { length: 128 }), + title: varchar("title", { length: 255 }).notNull(), + description: text("description"), + structure: json("structure"), + status: varchar("status", { length: 50 }).default("draft"), + creatorId: varchar("creator_id", { length: 128 }).notNull(), + availableAt: timestamp("available_at"), + dueAt: timestamp("due_at"), + allowLate: boolean("allow_late").default(false).notNull(), + lateDueAt: timestamp("late_due_at"), + maxAttempts: int("max_attempts").default(1).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + creatorIdx: index("hw_assignment_creator_idx").on(table.creatorId), + sourceExamIdx: index("hw_assignment_source_exam_idx").on(table.sourceExamId), + statusIdx: index("hw_assignment_status_idx").on(table.status), + sourceExamFk: foreignKey({ + columns: [table.sourceExamId], + foreignColumns: [exams.id], + name: "hw_asg_exam_fk", + }).onDelete("cascade"), + creatorFk: foreignKey({ + columns: [table.creatorId], + foreignColumns: [users.id], + name: "hw_asg_creator_fk", + }).onDelete("cascade"), +})); + +export const homeworkAssignmentQuestions = mysqlTable("homework_assignment_questions", { + assignmentId: varchar("assignment_id", { length: 128 }).notNull(), + questionId: varchar("question_id", { length: 128 }).notNull(), + score: int("score").default(0), + order: int("order").default(0), +}, (table) => ({ + pk: primaryKey({ columns: [table.assignmentId, table.questionId] }), + assignmentIdx: index("hw_assignment_questions_assignment_idx").on(table.assignmentId), + assignmentFk: foreignKey({ + columns: [table.assignmentId], + foreignColumns: [homeworkAssignments.id], + name: "hw_aq_a_fk", + }).onDelete("cascade"), + questionFk: foreignKey({ + columns: [table.questionId], + foreignColumns: [questions.id], + name: "hw_aq_q_fk", + }).onDelete("cascade"), +})); + +export const homeworkAssignmentTargets = mysqlTable("homework_assignment_targets", { + assignmentId: varchar("assignment_id", { length: 128 }).notNull(), + studentId: varchar("student_id", { length: 128 }).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), +}, (table) => ({ + pk: primaryKey({ columns: [table.assignmentId, table.studentId] }), + assignmentIdx: index("hw_assignment_targets_assignment_idx").on(table.assignmentId), + studentIdx: index("hw_assignment_targets_student_idx").on(table.studentId), + assignmentFk: foreignKey({ + columns: [table.assignmentId], + foreignColumns: [homeworkAssignments.id], + name: "hw_at_a_fk", + }).onDelete("cascade"), + studentFk: foreignKey({ + columns: [table.studentId], + foreignColumns: [users.id], + name: "hw_at_s_fk", + }).onDelete("cascade"), +})); + +export const homeworkSubmissions = mysqlTable("homework_submissions", { + id: id("id").primaryKey(), + assignmentId: varchar("assignment_id", { length: 128 }).notNull(), + studentId: varchar("student_id", { length: 128 }).notNull(), + attemptNo: int("attempt_no").default(1).notNull(), + score: int("score"), + status: varchar("status", { length: 50 }).default("started"), + startedAt: timestamp("started_at").defaultNow().notNull(), + submittedAt: timestamp("submitted_at"), + isLate: boolean("is_late").default(false).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + assignmentStudentIdx: index("hw_assignment_student_idx").on(table.assignmentId, table.studentId), + assignmentFk: foreignKey({ + columns: [table.assignmentId], + foreignColumns: [homeworkAssignments.id], + name: "hw_sub_a_fk", + }).onDelete("cascade"), + studentFk: foreignKey({ + columns: [table.studentId], + foreignColumns: [users.id], + name: "hw_sub_student_fk", + }).onDelete("cascade"), +})); + +export const homeworkAnswers = mysqlTable("homework_answers", { + id: id("id").primaryKey(), + submissionId: varchar("submission_id", { length: 128 }).notNull(), + questionId: varchar("question_id", { length: 128 }).notNull(), + answerContent: json("answer_content"), + score: int("score"), + feedback: text("feedback"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + submissionIdx: index("hw_answer_submission_idx").on(table.submissionId), + submissionQuestionIdx: index("hw_answer_submission_question_idx").on(table.submissionId, table.questionId), + submissionFk: foreignKey({ + columns: [table.submissionId], + foreignColumns: [homeworkSubmissions.id], + name: "hw_ans_sub_fk", + }).onDelete("cascade"), + questionFk: foreignKey({ + columns: [table.questionId], + foreignColumns: [questions.id], + name: "hw_ans_q_fk", + }), +})); diff --git a/src/shared/db/schema/knowledge-points.ts b/src/shared/db/schema/knowledge-points.ts new file mode 100644 index 0000000..66f7b40 --- /dev/null +++ b/src/shared/db/schema/knowledge-points.ts @@ -0,0 +1,87 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, + primaryKey, + foreignKey, + decimal, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; + +// --- 2. Knowledge Points (Tree Structure) --- + +export const knowledgePoints = mysqlTable("knowledge_points", { + id: id("id").primaryKey(), + name: varchar("name", { length: 255 }).notNull(), + description: text("description"), + anchorText: varchar("anchor_text", { length: 255 }), + + // Tree Structure: Parent KP + parentId: varchar("parent_id", { length: 128 }), // Self-reference defined in relations + + chapterId: varchar("chapter_id", { length: 128 }), + + // Metadata for ordering or level + level: int("level").default(0), + order: int("order").default(0), + + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + parentIdIdx: index("parent_id_idx").on(table.parentId), + chapterIdIdx: index("kp_chapter_id_idx").on(table.chapterId), +})); + +// --- 知识点前置依赖(知识图谱) --- +export const knowledgePointPrerequisites = mysqlTable("knowledge_point_prerequisites", { + knowledgePointId: varchar("knowledge_point_id", { length: 128 }).notNull(), + prerequisiteKpId: varchar("prerequisite_kp_id", { length: 128 }).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), +}, (table) => ({ + kpPairPk: primaryKey({ columns: [table.knowledgePointId, table.prerequisiteKpId], name: "kp_prereq_pk" }), + kpIdx: index("kp_prereq_kp_idx").on(table.knowledgePointId), + prereqIdx: index("kp_prereq_prereq_idx").on(table.prerequisiteKpId), + kpFk: foreignKey({ + columns: [table.knowledgePointId], + foreignColumns: [knowledgePoints.id], + name: "kp_prereq_kp_fk", + }).onDelete("cascade"), + prereqFk: foreignKey({ + columns: [table.prerequisiteKpId], + foreignColumns: [knowledgePoints.id], + name: "kp_prereq_prereq_fk", + }).onDelete("cascade"), +})); + +// --- 23. Learning Diagnostic (学情诊断报告) - 知识点掌握度 --- + +export const knowledgePointMastery = mysqlTable("knowledge_point_mastery", { + id: id("id").primaryKey(), + // 显式 foreignKey 见表参数(避免自动生成 FK 名过长 > 64 字符) + studentId: varchar("student_id", { length: 128 }).notNull(), + knowledgePointId: varchar("knowledge_point_id", { length: 128 }).notNull(), + masteryLevel: decimal("mastery_level", { precision: 5, scale: 2 }).default("0").notNull(), + totalQuestions: int("total_questions").default(0).notNull(), + correctQuestions: int("correct_questions").default(0).notNull(), + lastAssessedAt: timestamp("last_assessed_at").defaultNow().notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + studentKpPk: primaryKey({ columns: [table.studentId, table.knowledgePointId] }), + studentIdx: index("mastery_student_idx").on(table.studentId), + kpIdx: index("mastery_kp_idx").on(table.knowledgePointId), + studentFk: foreignKey({ + columns: [table.studentId], + foreignColumns: [users.id], + name: "kpm_student_fk", + }).onDelete("cascade"), + kpFk: foreignKey({ + columns: [table.knowledgePointId], + foreignColumns: [knowledgePoints.id], + name: "kpm_kp_fk", + }).onDelete("cascade"), +})); diff --git a/src/shared/db/schema/learning-diagnostic.ts b/src/shared/db/schema/learning-diagnostic.ts new file mode 100644 index 0000000..36691b1 --- /dev/null +++ b/src/shared/db/schema/learning-diagnostic.ts @@ -0,0 +1,46 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + index, + json, + decimal, + mysqlEnum, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; +import { classes } from "./classes"; +import { grades } from "./school"; + +// --- 23. Learning Diagnostic (学情诊断报告) --- + +export const diagnosticReportStatusEnum = mysqlEnum("report_status", ["draft", "published", "archived"]); +export const diagnosticReportTypeEnum = mysqlEnum("report_type", ["individual", "class", "grade"]); + +export const learningDiagnosticReports = mysqlTable("learning_diagnostic_reports", { + id: id("id").primaryKey(), + studentId: varchar("student_id", { length: 128 }).references(() => users.id, { onDelete: "cascade" }), + generatedBy: varchar("generated_by", { length: 128 }).references(() => users.id, { onDelete: "set null" }), + // v4-P1-4: 班级报告关联的 classId,用于发布时批量通知全班学生 + classId: varchar("class_id", { length: 128 }).references(() => classes.id, { onDelete: "set null" }), + // v4-P2-3: 年级报告关联的 gradeId,用于年级级别诊断 + gradeId: varchar("grade_id", { length: 128 }).references(() => grades.id, { onDelete: "set null" }), + reportType: diagnosticReportTypeEnum.default("individual").notNull(), + period: varchar("period", { length: 50 }), + summary: text("summary"), + strengths: json("strengths"), + weaknesses: json("weaknesses"), + recommendations: json("recommendations"), + overallScore: decimal("overall_score", { precision: 5, scale: 2 }), + status: diagnosticReportStatusEnum.default("draft").notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + studentIdx: index("diagnostic_student_idx").on(table.studentId), + generatedByIdx: index("diagnostic_generated_by_idx").on(table.generatedBy), + statusIdx: index("diagnostic_status_idx").on(table.status), + reportTypeIdx: index("diagnostic_report_type_idx").on(table.reportType), + classIdx: index("diagnostic_class_idx").on(table.classId), + gradeIdx: index("diagnostic_grade_idx").on(table.gradeId), +})); diff --git a/src/shared/db/schema/lesson-preparation.ts b/src/shared/db/schema/lesson-preparation.ts new file mode 100644 index 0000000..4bd198c --- /dev/null +++ b/src/shared/db/schema/lesson-preparation.ts @@ -0,0 +1,157 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, + json, + date, + boolean, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; +import { textbooks, chapters, subjects } from "./academic"; +import { grades } from "./school"; +import { classes } from "./classes"; + +// --- 24. Lesson Preparation (备课) --- + +export const lessonPlans = mysqlTable("lesson_plans", { + id: id("id").primaryKey(), + title: varchar("title", { length: 255 }).notNull(), + textbookId: varchar("textbook_id", { length: 128 }).references(() => textbooks.id), + chapterId: varchar("chapter_id", { length: 128 }).references(() => chapters.id), + coursePlanItemId: varchar("course_plan_item_id", { length: 128 }), + subjectId: varchar("subject_id", { length: 128 }).references(() => subjects.id), + gradeId: varchar("grade_id", { length: 128 }).references(() => grades.id), + templateId: varchar("template_id", { length: 128 }), + templateName: varchar("template_name", { length: 100 }), + content: json("content").notNull(), + status: varchar("status", { length: 50 }).default("draft").notNull(), + creatorId: varchar("creator_id", { length: 128 }).notNull().references(() => users.id), + lastSavedAt: timestamp("last_saved_at"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + creatorIdx: index("lp_creator_idx").on(table.creatorId), + statusIdx: index("lp_status_idx").on(table.status), + textbookChapterIdx: index("lp_textbook_chapter_idx").on(table.textbookId, table.chapterId), + subjectGradeIdx: index("lp_subject_grade_idx").on(table.subjectId, table.gradeId), +})); + +export const lessonPlanVersions = mysqlTable("lesson_plan_versions", { + id: id("id").primaryKey(), + planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), + versionNo: int("version_no").notNull(), + label: varchar("label", { length: 100 }), + content: json("content").notNull(), + isAuto: boolean("is_auto").default(false).notNull(), + creatorId: varchar("creator_id", { length: 128 }).notNull().references(() => users.id), + createdAt: timestamp("created_at").defaultNow().notNull(), +}, (table) => ({ + planVersionIdx: index("lpv_plan_version_idx").on(table.planId, table.versionNo), + planCreatedIdx: index("lpv_plan_created_idx").on(table.planId, table.createdAt), +})); + +export const lessonPlanTemplates = mysqlTable("lesson_plan_templates", { + id: id("id").primaryKey(), + name: varchar("name", { length: 100 }).notNull(), + type: varchar("type", { length: 50 }).notNull(), + scope: varchar("scope", { length: 50 }).notNull(), + blocks: json("blocks").notNull(), + creatorId: varchar("creator_id", { length: 128 }).references(() => users.id), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + typeCreatorIdx: index("lpt_type_creator_idx").on(table.type, table.creatorId), +})); + +// M2 协同备课:Block 级评论表 +export const lessonPlanComments = mysqlTable("lesson_plan_comments", { + id: id("id").primaryKey(), + planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), + blockId: varchar("block_id", { length: 128 }).notNull(), + content: text("content").notNull(), + authorId: varchar("author_id", { length: 128 }).notNull().references(() => users.id), + resolved: boolean("resolved").default(false).notNull(), + parentCommentId: varchar("parent_comment_id", { length: 128 }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + planBlockIdx: index("lpc_plan_block_idx").on(table.planId, table.blockId), + authorIdx: index("lpc_author_idx").on(table.authorId), +})); + +// M3 审核工作流:审核记录表 +export const lessonPlanReviewRecords = mysqlTable("lesson_plan_review_records", { + id: id("id").primaryKey(), + planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), + reviewerId: varchar("reviewer_id", { length: 128 }).notNull().references(() => users.id), + decision: varchar("decision", { length: 20 }).notNull(), // approved | rejected + comment: text("comment"), + previousStatus: varchar("previous_status", { length: 50 }).notNull(), + newStatus: varchar("new_status", { length: 50 }).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), +}, (table) => ({ + planCreatedIdx: index("lprr_plan_created_idx").on(table.planId, table.createdAt), + reviewerIdx: index("lprr_reviewer_idx").on(table.reviewerId), +})); + +// M6 资源附件库:附件关联表 +export const lessonPlanAttachments = mysqlTable("lesson_plan_attachments", { + id: id("id").primaryKey(), + planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), + blockId: varchar("block_id", { length: 128 }), + fileId: varchar("file_id", { length: 128 }).notNull(), + displayName: varchar("display_name", { length: 255 }).notNull(), + attachmentType: varchar("attachment_type", { length: 50 }).default("reference").notNull(), // reference | material | supplementary + uploadedBy: varchar("uploaded_by", { length: 128 }).notNull().references(() => users.id), + createdAt: timestamp("created_at").defaultNow().notNull(), +}, (table) => ({ + planIdx: index("lpa_plan_idx").on(table.planId), + blockIdx: index("lpa_block_idx").on(table.blockId), + fileIdx: index("lpa_file_idx").on(table.fileId), +})); + +// M12 代课教师机制:代课教师映射表 +export const lessonPlanSubstitutes = mysqlTable("lesson_plan_substitutes", { + id: id("id").primaryKey(), + planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), + originalTeacherId: varchar("original_teacher_id", { length: 128 }).notNull().references(() => users.id), + substituteTeacherId: varchar("substitute_teacher_id", { length: 128 }).notNull().references(() => users.id), + startDate: date("start_date").notNull(), + endDate: date("end_date"), + reason: varchar("reason", { length: 255 }), + status: varchar("status", { length: 20 }).default("active").notNull(), // active | expired | cancelled + createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + planIdx: index("lps_plan_idx").on(table.planId), + originalTeacherIdx: index("lps_original_teacher_idx").on(table.originalTeacherId), + substituteTeacherIdx: index("lps_substitute_teacher_idx").on(table.substituteTeacherId), + dateRangeIdx: index("lps_date_range_idx").on(table.startDate, table.endDate), +})); + +// V5-7:课案-课时绑定表(将课案绑定到具体班级的某个日期/节次) +export const lessonPlanSchedules = mysqlTable("lesson_plan_schedules", { + id: id("id").primaryKey(), + planId: varchar("plan_id", { length: 128 }).notNull().references(() => lessonPlans.id, { onDelete: "cascade" }), + classId: varchar("class_id", { length: 128 }).notNull().references(() => classes.id, { onDelete: "cascade" }), + /** 排课日期 YYYY-MM-DD */ + scheduledDate: date("scheduled_date").notNull(), + /** 节次序号(1-12,对应学校节次安排) */ + period: int("period").notNull(), + /** 关联 class_schedule.id(如使用课表)*/ + classScheduleId: varchar("class_schedule_id", { length: 128 }), + /** 教学时长(分钟),默认 40 */ + durationMin: int("duration_min").default(40).notNull(), + createdBy: varchar("created_by", { length: 128 }).notNull().references(() => users.id), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + planIdx: index("lpsc_plan_idx").on(table.planId), + classDateIdx: index("lpsc_class_date_idx").on(table.classId, table.scheduledDate), + teacherDateIdx: index("lpsc_plan_date_idx").on(table.planId, table.scheduledDate), +})); diff --git a/src/shared/db/schema/messaging.ts b/src/shared/db/schema/messaging.ts new file mode 100644 index 0000000..7890a09 --- /dev/null +++ b/src/shared/db/schema/messaging.ts @@ -0,0 +1,123 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, + boolean, + uniqueIndex, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; + +// --- 14. Messages (站内消息) --- + +export const messages = mysqlTable("messages", { + id: id("id").primaryKey(), + senderId: varchar("sender_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + receiverId: varchar("receiver_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + subject: varchar("subject", { length: 255 }), + content: text("content").notNull(), + isRead: boolean("is_read").default(false).notNull(), + readAt: timestamp("read_at", { mode: "date" }), + parentMessageId: varchar("parent_message_id", { length: 128 }), // 回复链 + // 软删除:发送方/接收方各自独立删除,互不影响 + senderDeletedAt: timestamp("sender_deleted_at", { mode: "date" }), + receiverDeletedAt: timestamp("receiver_deleted_at", { mode: "date" }), + // V2-P2-13c: 消息星标(接收方可标记重要消息) + isStarred: boolean("is_starred").default(false).notNull(), + // P2-4: 消息撤回(发送方在 2 分钟窗口内可撤回;非空即表示已撤回) + recalledAt: timestamp("recalled_at", { mode: "date" }), + // P2-2: 群组消息 ID(fan-out on write:教师群发时每条消息共享同一 groupMessageId) + groupMessageId: varchar("group_message_id", { length: 128 }), + createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), +}, (table) => ({ + senderIdx: index("messages_sender_idx").on(table.senderId), + receiverIdx: index("messages_receiver_idx").on(table.receiverId), + isReadIdx: index("messages_is_read_idx").on(table.isRead), + parentIdx: index("messages_parent_idx").on(table.parentMessageId), + receiverReadIdx: index("messages_receiver_read_idx").on(table.receiverId, table.isRead), + // V2-P2-13c: 星标索引 + receiverStarredIdx: index("messages_receiver_starred_idx").on(table.receiverId, table.isStarred), + // P2-2: 群组消息索引(按 groupMessageId 聚合查询) + groupMessageIdx: index("messages_group_message_idx").on(table.groupMessageId), +})); + +// --- 14b. Message Drafts (消息草稿) --- + +export const messageDrafts = mysqlTable("message_drafts", { + id: id("id").primaryKey(), + userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + receiverId: varchar("receiver_id", { length: 128 }).references(() => users.id, { onDelete: "cascade" }), + subject: varchar("subject", { length: 255 }), + content: text("content"), + parentMessageId: varchar("parent_message_id", { length: 128 }), + // P2-10: 跨设备草稿同步 - 设备 ID(标识来源设备) + deviceId: varchar("device_id", { length: 128 }), + // P2-10: 跨设备草稿同步 - 版本号(乐观锁,冲突检测) + version: int("version").default(1).notNull(), + updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().onUpdateNow().notNull(), + createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), +}, (table) => ({ + userIdx: index("message_drafts_user_idx").on(table.userId), + userUpdatedIdx: index("message_drafts_user_updated_idx").on(table.userId, table.updatedAt), + // P2-10: 支持按 userId + parentMessageId 查询最新版本草稿 + userParentIdx: index("message_drafts_user_parent_idx").on(table.userId, table.parentMessageId), +})); + +// --- 14c. Message Templates (快捷回复模板, P2-6) --- + +export const messageTemplates = mysqlTable("message_templates", { + id: id("id").primaryKey(), + userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + title: varchar("title", { length: 100 }).notNull(), + content: text("content").notNull(), + // 排序权重(越小越靠前),默认 0 + sortOrder: int("sort_order").default(0).notNull(), + updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().onUpdateNow().notNull(), + createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), +}, (table) => ({ + userIdx: index("message_templates_user_idx").on(table.userId), + userSortIdx: index("message_templates_user_sort_idx").on(table.userId, table.sortOrder), +})); + +// --- 14d. Message Reports (消息举报, P2-5) --- + +export const messageReports = mysqlTable("message_reports", { + id: id("id").primaryKey(), + // 举报人 + reporterId: varchar("reporter_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + // 被举报消息 ID + messageId: varchar("message_id", { length: 128 }).notNull().references(() => messages.id, { onDelete: "cascade" }), + // 被举报人(冗余,便于按被举报人查询) + reportedUserId: varchar("reported_user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + // 举报理由分类:spam / harassment / inappropriate / other + reason: varchar("reason", { length: 50 }).notNull(), + // 详细描述(可选) + description: text("description"), + // 举报状态:pending / reviewed / dismissed / actioned + status: varchar("status", { length: 20 }).notNull().default("pending"), + createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), + updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + reporterIdx: index("message_reports_reporter_idx").on(table.reporterId), + messageIdx: index("message_reports_message_idx").on(table.messageId), + reportedUserIdx: index("message_reports_reported_user_idx").on(table.reportedUserId), + statusIdx: index("message_reports_status_idx").on(table.status), +})); + +// --- 14e. User Blocks (用户屏蔽, P2-5) --- + +export const userBlocks = mysqlTable("user_blocks", { + id: id("id").primaryKey(), + // 屏蔽发起人 + blockerId: varchar("blocker_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + // 被屏蔽人 + blockedId: varchar("blocked_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), +}, (table) => ({ + // 同一对 (blocker, blocked) 只能存在一条记录 + blockerBlockedUnique: uniqueIndex("user_blocks_blocker_blocked_uniq").on(table.blockerId, table.blockedId), + blockerIdx: index("user_blocks_blocker_idx").on(table.blockerId), +})); diff --git a/src/shared/db/schema/notifications.ts b/src/shared/db/schema/notifications.ts new file mode 100644 index 0000000..988da8e --- /dev/null +++ b/src/shared/db/schema/notifications.ts @@ -0,0 +1,89 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + index, + boolean, + foreignKey, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; + +// --- 15. Message Notifications (消息通知) --- + +export const messageNotifications = mysqlTable("message_notifications", { + id: id("id").primaryKey(), + userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + type: varchar("type", { length: 128 }).notNull(), // "message", "announcement", "homework", "grade", "diagnostic" + title: varchar("title", { length: 255 }).notNull(), + content: text("content"), + link: varchar("link", { length: 512 }), + isRead: boolean("is_read").default(false).notNull(), + // V2-P2-13b: 通知优先级(low/normal/high/urgent),默认 normal + priority: varchar("priority", { length: 16 }).default("normal").notNull(), + // V2-P2-13b: 通知归档标记,归档后不在默认列表显示 + isArchived: boolean("is_archived").default(false).notNull(), + createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), +}, (table) => ({ + userIdx: index("message_notifications_user_idx").on(table.userId), + isReadIdx: index("message_notifications_is_read_idx").on(table.isRead), + userReadIdx: index("message_notifications_user_read_idx").on(table.userId, table.isRead), + createdAtIdx: index("message_notifications_created_at_idx").on(table.createdAt), + // V2-P2-13b: 新增优先级和归档索引 + priorityIdx: index("message_notifications_priority_idx").on(table.priority), + userArchivedIdx: index("message_notifications_user_archived_idx").on(table.userId, table.isArchived), +})); + +// --- 17. Notification Logs (通知发送日志) --- + +export const notificationLogs = mysqlTable("notification_logs", { + id: id("id").primaryKey(), + // 关联用户(通知接收人) + userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + // 通知标题(冗余存储,便于查询) + title: varchar("title", { length: 255 }).notNull(), + // 发送渠道: in_app | email | sms | wechat + channel: varchar("channel", { length: 32 }).notNull(), + // 发送状态: success | failure + status: varchar("status", { length: 16 }).notNull(), + // 渠道返回的消息 ID(用于追踪) + messageId: varchar("message_id", { length: 255 }), + // 失败时的错误信息 + error: text("error"), + // 发送时间 + sentAt: timestamp("sent_at", { mode: "date" }).defaultNow().notNull(), +}, (table) => ({ + userIdx: index("notification_logs_user_idx").on(table.userId), + channelIdx: index("notification_logs_channel_idx").on(table.channel), + statusIdx: index("notification_logs_status_idx").on(table.status), + sentAtIdx: index("notification_logs_sent_at_idx").on(table.sentAt), +})); + +// --- 16. Notification Preferences (通知偏好) --- + +export const notificationPreferences = mysqlTable("notification_preferences", { + id: varchar("id", { length: 128 }).primaryKey(), + userId: varchar("user_id", { length: 128 }).notNull().unique().references(() => users.id, { onDelete: "cascade" }), + emailEnabled: boolean("email_enabled").default(false).notNull(), + smsEnabled: boolean("sms_enabled").default(false).notNull(), + pushEnabled: boolean("push_enabled").default(true).notNull(), + homeworkNotifications: boolean("homework_notifications").default(true).notNull(), + gradeNotifications: boolean("grade_notifications").default(true).notNull(), + announcementNotifications: boolean("announcement_notifications").default(true).notNull(), + messageNotifications: boolean("message_notifications").default(true).notNull(), + attendanceNotifications: boolean("attendance_notifications").default(true).notNull(), + // 免打扰时段(格式 "HH:mm",如 "22:00") + quietHoursEnabled: boolean("quiet_hours_enabled").default(false).notNull(), + quietHoursStart: varchar("quiet_hours_start", { length: 5 }), + quietHoursEnd: varchar("quiet_hours_end", { length: 5 }), + createdAt: timestamp("created_at", { mode: "date" }).defaultNow().notNull(), + updatedAt: timestamp("updated_at", { mode: "date" }).defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + userIdIdx: index("notification_preferences_user_idx").on(table.userId), + userFk: foreignKey({ + columns: [table.userId], + foreignColumns: [users.id], + name: "np_u_fk", + }).onDelete("cascade"), +})); diff --git a/src/shared/db/schema/practice.ts b/src/shared/db/schema/practice.ts new file mode 100644 index 0000000..05f6267 --- /dev/null +++ b/src/shared/db/schema/practice.ts @@ -0,0 +1,110 @@ +import { + mysqlTable, + varchar, + timestamp, + int, + index, + json, + boolean, + mysqlEnum, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; +import { questions } from "./questions"; + +// --- 28. Adaptive Practice (专项练习闭环) --- + +/** + * 专项练习类型。 + * - error_variant: 错题变式练习(从错题本发起,生成变式题) + * - knowledge_point: 知识点专项练习(按知识点抽题) + * - weak_chapter: 薄弱章节练习(按掌握度自动推荐) + * - ai_recommended: AI 推荐练习(AI 根据学情综合推荐) + */ +export const practiceTypeEnum = mysqlEnum("practice_type", [ + "error_variant", + "knowledge_point", + "weak_chapter", + "ai_recommended", +]); + +export const practiceStatusEnum = mysqlEnum("practice_status", [ + "in_progress", + "completed", + "abandoned", +]); + +export const practiceAnswerStatusEnum = mysqlEnum("practice_answer_status", [ + "pending", + "answered", + "skipped", +]); + +/** + * 专项练习会话 - 学生发起的一次专项练习。 + * + * 记录练习类型、来源元数据(知识点/错题/章节)、状态和统计。 + * 练习题目存储在 practiceAnswers 表中。 + */ +export const practiceSessions = mysqlTable("practice_sessions", { + id: id("id").primaryKey(), + studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + subjectId: varchar("subject_id", { length: 128 }), + + practiceType: practiceTypeEnum.notNull(), + /** 来源元数据:{ knowledgePointIds?, errorBookItemIds?, chapterId?, difficulty?, sourceQuestionId? } */ + sourceMeta: json("source_meta"), + + status: practiceStatusEnum.default("in_progress").notNull(), + + totalQuestions: int("total_questions").default(0).notNull(), + answeredQuestions: int("answered_questions").default(0).notNull(), + correctCount: int("correct_count").default(0).notNull(), + + startedAt: timestamp("started_at", { mode: "date" }).defaultNow().notNull(), + completedAt: timestamp("completed_at", { mode: "date" }), + + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + studentIdx: index("ps_student_idx").on(table.studentId), + studentStatusIdx: index("ps_student_status_idx").on(table.studentId, table.status), + subjectIdx: index("ps_subject_idx").on(table.subjectId), +})); + +/** + * 专项练习答题记录 - 会话中每道题的作答情况。 + * + * 支持两种题目来源: + * 1. 题库中的题目(questionId 指向 questions 表) + * 2. AI 生成的变式题(variantContent 存储 JSON 内容,questionId 指向原题) + */ +export const practiceAnswers = mysqlTable("practice_answers", { + id: id("id").primaryKey(), + sessionId: varchar("session_id", { length: 128 }).notNull().references(() => practiceSessions.id, { onDelete: "cascade" }), + studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + + questionId: varchar("question_id", { length: 128 }).notNull().references(() => questions.id), + /** 变式题内容(AI 生成的变式题,未入库时存储在此字段) */ + variantContent: json("variant_content"), + /** 是否为变式题 */ + isVariant: boolean("is_variant").default(false).notNull(), + + orderIndex: int("order_index").notNull(), + status: practiceAnswerStatusEnum.default("pending").notNull(), + + studentAnswer: json("student_answer"), + isCorrect: boolean("is_correct"), + score: int("score"), + maxScore: int("max_score").default(1).notNull(), + + answeredAt: timestamp("answered_at", { mode: "date" }), + + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + sessionIdx: index("pa_session_idx").on(table.sessionId), + studentIdx: index("pa_student_idx").on(table.studentId), + questionIdx: index("pa_question_idx").on(table.questionId), + sessionOrderIdx: index("pa_session_order_idx").on(table.sessionId, table.orderIndex), +})); diff --git a/src/shared/db/schema/questions.ts b/src/shared/db/schema/questions.ts new file mode 100644 index 0000000..88c5c98 --- /dev/null +++ b/src/shared/db/schema/questions.ts @@ -0,0 +1,74 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, + json, + mysqlEnum, + primaryKey, + foreignKey, +} from "drizzle-orm/mysql-core"; +import { sql } from "drizzle-orm"; +import { id } from "./_helpers"; +import { users } from "./users"; +import { knowledgePoints } from "./knowledge-points"; + +// --- 3. Question Bank (Core) --- + +export const questionTypeEnum = mysqlEnum("type", ["single_choice", "multiple_choice", "text", "judgment", "composite"]); + +export const questions = mysqlTable("questions", { + id: id("id").primaryKey(), + + // Content can be JSON to store rich text, images, etc. or just text. + // Using JSON for flexibility in a modern ed-tech app (e.g. SlateJS nodes). + content: json("content").notNull(), + + // P3-6: 题目内容全文检索文本镜像。 + // - STORED 生成列:每次 INSERT/UPDATE content 时由 MySQL 自动同步 + // - 使用 CAST(content AS CHAR) 把 JSON 序列化为纯文本以支持 FULLTEXT + // - 配合 questions_content_text_ft_idx FULLTEXT 索引(见对应 drizzle 迁移)实现 MATCH AGAINST 检索 + // drizzle-kit 无法生成 FULLTEXT 索引,迁移文件中需手动追加 CREATE FULLTEXT INDEX + contentText: text("content_text").generatedAlwaysAs( + sql`CAST(content AS CHAR)`, + { mode: "stored" }, + ), + + type: questionTypeEnum.notNull(), + difficulty: int("difficulty").default(1), // 1-5 + + // Self-reference for "Infinite Nesting" (Parent Question) + // e.g., A reading comprehension passage (Parent) -> 5 sub-questions (Children) + parentId: varchar("parent_id", { length: 128 }), + + authorId: varchar("author_id", { length: 128 }).notNull().references(() => users.id), + + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + parentIdIdx: index("parent_id_idx").on(table.parentId), // Critical for querying children + authorIdIdx: index("author_id_idx").on(table.authorId), + // P3-6: 题目按类型+难度筛选的高频查询索引 + typeDifficultyIdx: index("questions_type_difficulty_idx").on(table.type, table.difficulty), +})); + +// Many-to-Many: Questions <-> Knowledge Points +export const questionsToKnowledgePoints = mysqlTable("questions_to_knowledge_points", { + questionId: varchar("question_id", { length: 128 }).notNull(), + knowledgePointId: varchar("knowledge_point_id", { length: 128 }).notNull(), +}, (table) => ({ + pk: primaryKey({ columns: [table.questionId, table.knowledgePointId] }), + kpIdx: index("kp_idx").on(table.knowledgePointId), // For querying "All questions under this KP" + qFk: foreignKey({ + columns: [table.questionId], + foreignColumns: [questions.id], + name: "q_kp_qid_fk" + }).onDelete("cascade"), + kpFk: foreignKey({ + columns: [table.knowledgePointId], + foreignColumns: [knowledgePoints.id], + name: "q_kp_kpid_fk" + }).onDelete("cascade"), +})); diff --git a/src/shared/db/schema/rbac.ts b/src/shared/db/schema/rbac.ts new file mode 100644 index 0000000..9bc2a09 --- /dev/null +++ b/src/shared/db/schema/rbac.ts @@ -0,0 +1,45 @@ +import { + mysqlTable, + varchar, + boolean, + timestamp, + primaryKey, + index, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; + +// --- Custom RBAC Extensions --- + +export const roles = mysqlTable("roles", { + id: id("id").primaryKey(), + name: varchar("name", { length: 50 }).notNull().unique(), // e.g., 'admin', 'teacher', 'student', 'grade_head' + description: varchar("description", { length: 255 }), + // Builtin roles (admin/teacher/student/parent/grade_head/teaching_head) are + // seeded with is_system = true and cannot be deleted. The `admin` role is + // additionally fully locked (no permission edits, no disable, no rename). + isSystem: boolean("is_system").default(false).notNull(), + // Disabled roles do not contribute permissions at login time. + isEnabled: boolean("is_enabled").default(true).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}); + +// Many-to-Many: Users <-> Roles +// Solves: "A teacher can also be a Grade Head" +export const usersToRoles = mysqlTable("users_to_roles", { + userId: varchar("user_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + roleId: varchar("role_id", { length: 128 }).notNull().references(() => roles.id, { onDelete: "cascade" }), +}, (table) => ({ + pk: primaryKey({ columns: [table.userId, table.roleId] }), + userIdIdx: index("user_id_idx").on(table.userId), +})); + +// Role -> Permissions (fine-grained RBAC) +export const rolePermissions = mysqlTable("role_permissions", { + roleId: varchar("role_id", { length: 128 }).notNull().references(() => roles.id, { onDelete: "cascade" }), + permission: varchar("permission", { length: 100 }).notNull(), +}, (table) => ({ + pk: primaryKey({ columns: [table.roleId, table.permission] }), + roleIdIdx: index("role_permissions_role_idx").on(table.roleId), +})); diff --git a/src/shared/db/schema/scheduling.ts b/src/shared/db/schema/scheduling.ts new file mode 100644 index 0000000..de06c02 --- /dev/null +++ b/src/shared/db/schema/scheduling.ts @@ -0,0 +1,56 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, + date, + mysqlEnum, + boolean, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; + +// --- 20. Scheduling Rules & Schedule Changes (排课规则与调课) --- + +export const schedulingRules = mysqlTable("scheduling_rules", { + id: id("id").primaryKey(), + classId: varchar("class_id", { length: 128 }), // null=全局规则 + maxDailyHours: int("max_daily_hours").default(8), + maxContinuousHours: int("max_continuous_hours").default(2), + lunchBreakStart: varchar("lunch_break_start", { length: 10 }).default("12:00"), + lunchBreakEnd: varchar("lunch_break_end", { length: 10 }).default("13:00"), + morningStart: varchar("morning_start", { length: 10 }).default("08:00"), + afternoonEnd: varchar("afternoon_end", { length: 10 }).default("17:00"), + avoidBackToBack: boolean("avoid_back_to_back").default(false), + balancedSubjects: boolean("balanced_subjects").default(true), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + classIdx: index("scheduling_rules_class_idx").on(table.classId), +})); + +export const scheduleChangeStatusEnum = mysqlEnum("status", ["pending", "approved", "rejected", "completed"]); + +export const scheduleChanges = mysqlTable("schedule_changes", { + id: id("id").primaryKey(), + originalScheduleId: varchar("original_schedule_id", { length: 128 }), + classId: varchar("class_id", { length: 128 }).notNull(), + originalTeacherId: varchar("original_teacher_id", { length: 128 }), + substituteTeacherId: varchar("substitute_teacher_id", { length: 128 }), + originalDate: date("original_date"), + newDate: date("new_date"), + newStartTime: varchar("new_start_time", { length: 10 }), + newEndTime: varchar("new_end_time", { length: 10 }), + reason: text("reason"), + status: scheduleChangeStatusEnum.default("pending").notNull(), + requestedBy: varchar("requested_by", { length: 128 }).notNull(), + approvedBy: varchar("approved_by", { length: 128 }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + classIdx: index("schedule_changes_class_idx").on(table.classId), + statusIdx: index("schedule_changes_status_idx").on(table.status), + requestedByIdx: index("schedule_changes_requested_by_idx").on(table.requestedBy), + originalScheduleIdx: index("schedule_changes_original_schedule_idx").on(table.originalScheduleId), +})); diff --git a/src/shared/db/schema/school.ts b/src/shared/db/schema/school.ts new file mode 100644 index 0000000..d887e0d --- /dev/null +++ b/src/shared/db/schema/school.ts @@ -0,0 +1,91 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, + boolean, + foreignKey, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; + +// --- 5. School Management --- + +export const departments = mysqlTable("departments", { + id: id("id").primaryKey(), + name: varchar("name", { length: 255 }).notNull().unique(), + description: text("description"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + nameIdx: index("departments_name_idx").on(table.name), +})) + +export const classrooms = mysqlTable("classrooms", { + id: id("id").primaryKey(), + name: varchar("name", { length: 255 }).notNull().unique(), + building: varchar("building", { length: 100 }), + floor: int("floor"), + capacity: int("capacity"), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + nameIdx: index("classrooms_name_idx").on(table.name), +})) + +export const academicYears = mysqlTable("academic_years", { + id: id("id").primaryKey(), + name: varchar("name", { length: 100 }).notNull().unique(), + startDate: timestamp("start_date", { mode: "date" }).notNull(), + endDate: timestamp("end_date", { mode: "date" }).notNull(), + isActive: boolean("is_active").default(false).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + nameIdx: index("academic_years_name_idx").on(table.name), + activeIdx: index("academic_years_active_idx").on(table.isActive), +})) + +export const schools = mysqlTable("schools", { + id: id("id").primaryKey(), + name: varchar("name", { length: 255 }).notNull().unique(), + code: varchar("code", { length: 50 }).unique(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + nameIdx: index("schools_name_idx").on(table.name), + codeIdx: index("schools_code_idx").on(table.code), +})) + +export const grades = mysqlTable("grades", { + id: id("id").primaryKey(), + schoolId: varchar("school_id", { length: 128 }).notNull(), + name: varchar("name", { length: 100 }).notNull(), + order: int("order").default(0).notNull(), + gradeHeadId: varchar("grade_head_id", { length: 128 }), + teachingHeadId: varchar("teaching_head_id", { length: 128 }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + schoolIdx: index("grades_school_idx").on(table.schoolId), + schoolNameUnique: index("grades_school_name_uniq").on(table.schoolId, table.name), + gradeHeadIdx: index("grades_grade_head_idx").on(table.gradeHeadId), + teachingHeadIdx: index("grades_teaching_head_idx").on(table.teachingHeadId), + schoolFk: foreignKey({ + columns: [table.schoolId], + foreignColumns: [schools.id], + name: "g_s_fk", + }).onDelete("cascade"), + gradeHeadFk: foreignKey({ + columns: [table.gradeHeadId], + foreignColumns: [users.id], + name: "g_gh_fk", + }).onDelete("set null"), + teachingHeadFk: foreignKey({ + columns: [table.teachingHeadId], + foreignColumns: [users.id], + name: "g_th_fk", + }).onDelete("set null"), +})) diff --git a/src/shared/db/schema/standards.ts b/src/shared/db/schema/standards.ts new file mode 100644 index 0000000..6ff7849 --- /dev/null +++ b/src/shared/db/schema/standards.ts @@ -0,0 +1,44 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, + uniqueIndex, + boolean, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; +import { users } from "./users"; +import { subjects } from "./academic"; +import { grades } from "./school"; + +// --- 29. Standards (M1 课标对标体系) --- + +/** + * M1 课标库:支持国家标准/课标/自定义三层级。 + * 课案通过 lessonPlanStandards 关联表实现多对多关系。 + */ +export const standards = mysqlTable("standards", { + id: id("id").primaryKey(), + code: varchar("code", { length: 100 }).notNull(), + title: varchar("title", { length: 255 }).notNull(), + description: text("description"), + /** 层级:national(国家标准)/ curriculum(课标)/ custom(自定义) */ + level: varchar("level", { length: 20 }).notNull(), + parentId: varchar("parent_id", { length: 128 }), + subjectId: varchar("subject_id", { length: 128 }).references(() => subjects.id), + gradeId: varchar("grade_id", { length: 128 }).references(() => grades.id), + /** 学段:primary / junior_high / senior_high */ + stage: varchar("stage", { length: 20 }), + sortOrder: int("sort_order").default(0).notNull(), + isActive: boolean("is_active").default(true).notNull(), + createdBy: varchar("created_by", { length: 128 }).references(() => users.id), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + codeIdx: uniqueIndex("std_code_idx").on(table.code), + parentIdx: index("std_parent_idx").on(table.parentId), + subjectGradeIdx: index("std_subject_grade_idx").on(table.subjectId, table.gradeId), + levelIdx: index("std_level_idx").on(table.level), +})); diff --git a/src/shared/db/schema/system-settings.ts b/src/shared/db/schema/system-settings.ts new file mode 100644 index 0000000..8067bf9 --- /dev/null +++ b/src/shared/db/schema/system-settings.ts @@ -0,0 +1,29 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + index, + uniqueIndex, +} from "drizzle-orm/mysql-core"; +import { id } from "./_helpers"; + +// --- 25. System Settings (系统设置 - 键值对存储) --- + +export const systemSettings = mysqlTable("system_settings", { + id: id("id").primaryKey(), + /** 设置分组:school_info / security_policy / file_upload / notification_config */ + category: varchar("category", { length: 50 }).notNull(), + /** 设置键名,如 schoolName / passwordMinLength / maxFileSize */ + key: varchar("key", { length: 100 }).notNull(), + /** 设置值(JSON 字符串,支持字符串/数字/布尔/对象) */ + value: text("value").notNull(), + /** 值类型:string / number / boolean / json */ + valueType: varchar("value_type", { length: 20 }).default("string").notNull(), + updatedBy: varchar("updated_by", { length: 128 }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + categoryKeyIdx: uniqueIndex("ss_category_key_idx").on(table.category, table.key), + categoryIdx: index("ss_category_idx").on(table.category), +})); diff --git a/src/shared/db/schema/users.ts b/src/shared/db/schema/users.ts new file mode 100644 index 0000000..52d029a --- /dev/null +++ b/src/shared/db/schema/users.ts @@ -0,0 +1,152 @@ +import { + mysqlTable, + varchar, + text, + timestamp, + int, + index, + primaryKey, + date, + datetime, + boolean, + foreignKey, +} from "drizzle-orm/mysql-core"; +import { createId } from "@paralleldrive/cuid2"; +import type { AdapterAccountType } from "next-auth/adapters"; +import { id } from "./_helpers"; + +// --- 1. Users & Auth (Auth.js v5 Standard) --- + +export const users = mysqlTable("users", { + id: id("id").primaryKey(), + name: varchar("name", { length: 255 }), + email: varchar("email", { length: 255 }).notNull().unique(), + emailVerified: timestamp("emailVerified", { mode: "date" }), + image: varchar("image", { length: 255 }), + // Credentials Auth (Optional) + password: varchar("password", { length: 255 }), + + phone: varchar("phone", { length: 30 }), + address: varchar("address", { length: 255 }), + gender: varchar("gender", { length: 20 }), + age: int("age"), + gradeId: varchar("grade_id", { length: 128 }), + departmentId: varchar("department_id", { length: 128 }), + onboardedAt: timestamp("onboarded_at", { mode: "date" }), + + // 未成年人信息保护 + birthDate: date("birth_date"), + guardianName: varchar("guardian_name", { length: 255 }), + guardianPhone: varchar("guardian_phone", { length: 20 }), + guardianRelation: varchar("guardian_relation", { length: 50 }), + consentAcceptedAt: datetime("consent_accepted_at"), + + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + emailIdx: index("email_idx").on(table.email), + gradeIdIdx: index("users_grade_id_idx").on(table.gradeId), + departmentIdIdx: index("users_department_id_idx").on(table.departmentId), +})); + +// Auth.js: Accounts (OAuth providers) +export const accounts = mysqlTable("accounts", { + userId: varchar("userId", { length: 128 }) + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + type: varchar("type", { length: 255 }).$type().notNull(), + provider: varchar("provider", { length: 255 }).notNull(), + providerAccountId: varchar("providerAccountId", { length: 255 }).notNull(), + refresh_token: text("refresh_token"), + access_token: text("access_token"), + expires_at: int("expires_at"), + token_type: varchar("token_type", { length: 255 }), + scope: varchar("scope", { length: 255 }), + id_token: text("id_token"), + session_state: varchar("session_state", { length: 255 }), + }, + (account) => ({ + compoundKey: primaryKey({ + columns: [account.provider, account.providerAccountId], + }), + userIdIdx: index("account_userId_idx").on(account.userId), + }) +); + +/** + * @deprecated audit-P1-9:此表在 JWT 策略下从未被写入数据。 + * + * NextAuth 配置为 `session: { strategy: "jwt" }`(见 `src/auth.ts`), + * 所有 session 信息存储在 JWT cookie 中,不写入此表。 + * `revokeAllOtherSessionsAction` 已改为 no-op(返回 0 并记录日志)。 + * `getUsersDashboardStats` 已不再查询此表。 + * + * 此表定义暂时保留是为了: + * 1. 避免删除表定义后 drizzle-kit 生成 DROP TABLE 迁移误删生产数据 + * 2. 未来若切换为 `strategy: "database"` 或引入 JWT 黑名单可复用 + * + * 后续应在显式迁移中删除此表(同时删除 relations.ts 中的 sessionsRelations)。 + */ +// Auth.js: Sessions +export const sessions = mysqlTable("sessions", { + sessionToken: varchar("sessionToken", { length: 255 }).primaryKey(), + userId: varchar("userId", { length: 128 }) + .notNull() + .references(() => users.id, { onDelete: "cascade" }), + expires: timestamp("expires", { mode: "date" }).notNull(), +}, (table) => ({ + userIdIdx: index("session_userId_idx").on(table.userId), +})); + +// Auth.js: Verification Tokens +export const verificationTokens = mysqlTable("verificationTokens", { + identifier: varchar("identifier", { length: 255 }).notNull(), + token: varchar("token", { length: 255 }).notNull(), + expires: timestamp("expires", { mode: "date" }).notNull(), +}, (vt) => ({ + compoundKey: primaryKey({ columns: [vt.identifier, vt.token] }), +})); + +// --- 17. Parent-Student Relations (家长-子女关联) --- + +export const parentStudentRelations = mysqlTable("parent_student_relations", { + id: varchar("id", { length: 128 }).primaryKey().$defaultFn(() => createId()), + parentId: varchar("parent_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + studentId: varchar("student_id", { length: 128 }).notNull().references(() => users.id, { onDelete: "cascade" }), + relation: varchar("relation", { length: 50 }), // 父亲/母亲/其他 + createdAt: timestamp("created_at").defaultNow().notNull(), +}, (table) => ({ + parentIdx: index("parent_student_relations_parent_idx").on(table.parentId), + studentIdx: index("parent_student_relations_student_idx").on(table.studentId), + parentFk: foreignKey({ + columns: [table.parentId], + foreignColumns: [users.id], + name: "psr_p_fk", + }).onDelete("cascade"), + studentFk: foreignKey({ + columns: [table.studentId], + foreignColumns: [users.id], + name: "psr_s_fk", + }).onDelete("cascade"), +})); + +// --- 19. Password Security (密码安全策略) --- + +export const passwordSecurity = mysqlTable("password_security", { + id: varchar("id", { length: 128 }).primaryKey().$defaultFn(() => createId()), + userId: varchar("user_id", { length: 128 }).notNull().unique().references(() => users.id, { onDelete: "cascade" }), + failedLoginAttempts: int("failed_login_attempts").default(0).notNull(), + lockedUntil: timestamp("locked_until", { mode: "date" }), + passwordChangedAt: timestamp("password_changed_at").defaultNow().notNull(), + mustChangePassword: boolean("must_change_password").default(false).notNull(), + lastPasswordChange: timestamp("last_password_change", { mode: "date" }), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), +}, (table) => ({ + userIdIdx: index("password_security_user_idx").on(table.userId), + userFk: foreignKey({ + columns: [table.userId], + foreignColumns: [users.id], + name: "ps_u_fk", + }).onDelete("cascade"), +})); diff --git a/src/shared/lib/cache/invalidation-map-academic.ts b/src/shared/lib/cache/invalidation-map-academic.ts new file mode 100644 index 0000000..2e9dee6 --- /dev/null +++ b/src/shared/lib/cache/invalidation-map-academic.ts @@ -0,0 +1,248 @@ +import type { InvalidationRule } from "./types" + +/** + * course-plans / diagnostic / elective / error-book / exams 模块的失效规则。 + * + * 详见 `invalidation-map.ts` 中的聚合说明。 + */ +export const ACADEMIC_INVALIDATION_RULES = { + // ===== course-plans 模块 ===== + "course-plans.create": { + tags: ["course-plans"], + queryKeys: [["course-plans", "list"]], + paths: [ + "/admin/course-plans", + "/teacher/course-plans", + "/parent/course-plans", + "/student/course-plans", + ], + }, + "course-plans.update": { + tags: ["course-plans"], + queryKeys: [["course-plans", "detail"], ["course-plans", "list"]], + paths: [ + "/admin/course-plans", + "/teacher/course-plans", + "/parent/course-plans", + "/student/course-plans", + ], + }, + "course-plans.delete": { + tags: ["course-plans"], + queryKeys: [["course-plans", "list"]], + paths: [ + "/admin/course-plans", + "/teacher/course-plans", + "/parent/course-plans", + "/student/course-plans", + ], + }, + "course-plans.item.create": { + tags: ["course-plans"], + queryKeys: [["course-plans", "items"]], + paths: [ + "/admin/course-plans", + "/teacher/course-plans", + "/parent/course-plans", + "/student/course-plans", + ], + }, + "course-plans.item.update": { + tags: ["course-plans"], + queryKeys: [["course-plans", "items"]], + paths: [ + "/admin/course-plans", + "/teacher/course-plans", + "/parent/course-plans", + "/student/course-plans", + ], + }, + "course-plans.item.delete": { + tags: ["course-plans"], + queryKeys: [["course-plans", "items"]], + paths: [ + "/admin/course-plans", + "/teacher/course-plans", + "/parent/course-plans", + "/student/course-plans", + ], + }, + "course-plans.item.toggleCompleted": { + tags: ["course-plans"], + queryKeys: [["course-plans", "items"]], + paths: [ + "/admin/course-plans", + "/teacher/course-plans", + "/parent/course-plans", + "/student/course-plans", + ], + }, + "course-plans.item.reorder": { + tags: ["course-plans"], + queryKeys: [["course-plans", "items"]], + paths: [ + "/admin/course-plans", + "/teacher/course-plans", + "/parent/course-plans", + "/student/course-plans", + ], + }, + "course-plans.item.bulkToggle": { + tags: ["course-plans"], + queryKeys: [["course-plans", "items"]], + paths: [ + "/admin/course-plans", + "/teacher/course-plans", + "/parent/course-plans", + "/student/course-plans", + ], + }, + "course-plans.copy": { + tags: ["course-plans"], + queryKeys: [["course-plans", "list"]], + paths: [ + "/admin/course-plans", + "/teacher/course-plans", + "/parent/course-plans", + "/student/course-plans", + ], + }, + + // ===== diagnostic 模块 ===== + "diagnostic.generateStudentReport": { + tags: ["diagnostic"], + queryKeys: [["diagnostic", "list"]], + paths: ["/teacher/diagnostic"], + }, + "diagnostic.generateClassReport": { + tags: ["diagnostic"], + queryKeys: [["diagnostic", "list"]], + paths: ["/teacher/diagnostic"], + }, + "diagnostic.generateGradeReport": { + tags: ["diagnostic"], + queryKeys: [["diagnostic", "list"]], + paths: ["/teacher/diagnostic", "/admin/diagnostic"], + }, + "diagnostic.publish": { + tags: ["diagnostic"], + queryKeys: [["diagnostic", "list"]], + paths: ["/teacher/diagnostic"], + }, + "diagnostic.delete": { + tags: ["diagnostic"], + queryKeys: [["diagnostic", "list"]], + paths: ["/teacher/diagnostic"], + }, + + // ===== elective 模块 ===== + "elective.create": { + tags: ["elective"], + queryKeys: [["elective", "list"]], + paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], + }, + "elective.update": { + tags: ["elective"], + queryKeys: [["elective", "detail"], ["elective", "list"]], + paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], + }, + "elective.delete": { + tags: ["elective"], + queryKeys: [["elective", "list"]], + paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], + }, + "elective.openSelection": { + tags: ["elective"], + queryKeys: [["elective", "list"]], + paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], + }, + "elective.closeSelection": { + tags: ["elective"], + queryKeys: [["elective", "list"]], + paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], + }, + "elective.runLottery": { + tags: ["elective"], + queryKeys: [["elective", "list"]], + paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], + }, + "elective.selectCourse": { + tags: ["elective"], + queryKeys: [["elective", "selections"]], + paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], + }, + "elective.dropCourse": { + tags: ["elective"], + queryKeys: [["elective", "selections"]], + paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], + }, + + // ===== error-book 模块 ===== + "error-book.create": { + tags: ["error-book"], + queryKeys: [["error-book", "list"]], + paths: ["/student/error-book"], + }, + "error-book.update": { + tags: ["error-book"], + queryKeys: [["error-book", "detail"], ["error-book", "list"]], + paths: ["/student/error-book"], + }, + "error-book.review": { + tags: ["error-book"], + queryKeys: [["error-book", "detail"]], + paths: ["/student/error-book"], + }, + "error-book.delete": { + tags: ["error-book"], + queryKeys: [["error-book", "list"]], + paths: ["/student/error-book"], + }, + "error-book.archive": { + tags: ["error-book"], + queryKeys: [["error-book", "list"]], + paths: ["/student/error-book"], + }, + "error-book.collect": { + tags: ["error-book"], + queryKeys: [["error-book", "list"]], + paths: ["/student/error-book"], + }, + + // ===== exams 模块 ===== + "exams.create": { + tags: ["exams"], + queryKeys: [["exams", "list"]], + paths: ["/teacher/exams/all"], + }, + "exams.createAi": { + tags: ["exams"], + queryKeys: [["exams", "list"]], + paths: ["/teacher/exams/all"], + }, + "exams.regenerateAiQuestion": { + tags: ["exams"], + queryKeys: [["exams", "detail"]], + paths: [], + }, + "exams.update": { + tags: ["exams"], + queryKeys: [["exams", "detail"], ["exams", "list"]], + paths: ["/teacher/exams/all"], + }, + "exams.delete": { + tags: ["exams"], + queryKeys: [["exams", "list"]], + paths: ["/teacher/exams/all"], + }, + "exams.duplicate": { + tags: ["exams"], + queryKeys: [["exams", "list"]], + paths: ["/teacher/exams/all"], + }, + "exams.richContent.save": { + tags: ["exams"], + queryKeys: [["exams", "detail"]], + paths: ["/teacher/exams/all"], + }, +} as const satisfies Record diff --git a/src/shared/lib/cache/invalidation-map-admin.ts b/src/shared/lib/cache/invalidation-map-admin.ts new file mode 100644 index 0000000..4642f51 --- /dev/null +++ b/src/shared/lib/cache/invalidation-map-admin.ts @@ -0,0 +1,349 @@ +import type { InvalidationRule } from "./types" + +/** + * questions / rbac / scheduling / school / settings / standards / textbooks / users 模块的失效规则。 + * + * 详见 `invalidation-map.ts` 中的聚合说明。 + */ +export const ADMIN_INVALIDATION_RULES = { + // ===== questions 模块 ===== + "questions.create": { + tags: ["questions"], + queryKeys: [["questions", "list"]], + paths: ["/teacher/questions", "/admin/questions"], + }, + "questions.update": { + tags: ["questions"], + queryKeys: [["questions", "detail"], ["questions", "list"]], + paths: ["/teacher/questions", "/admin/questions"], + }, + "questions.delete": { + tags: ["questions"], + queryKeys: [["questions", "list"]], + paths: ["/teacher/questions", "/admin/questions"], + }, + "questions.deleteBatch": { + tags: ["questions"], + queryKeys: [["questions", "list"]], + paths: ["/teacher/questions", "/admin/questions"], + }, + "questions.import": { + tags: ["questions"], + queryKeys: [["questions", "list"]], + paths: ["/teacher/questions", "/admin/questions"], + }, + + // ===== rbac 模块 ===== + "rbac.role.create": { + tags: ["rbac"], + queryKeys: [["rbac", "roles"]], + paths: ["/admin/roles"], + }, + "rbac.role.update": { + tags: ["rbac"], + queryKeys: [["rbac", "roles"]], + paths: ["/admin/roles"], + }, + "rbac.role.delete": { + tags: ["rbac"], + queryKeys: [["rbac", "roles"]], + paths: ["/admin/roles"], + }, + "rbac.role.toggleEnabled": { + tags: ["rbac"], + queryKeys: [["rbac", "roles"]], + paths: ["/admin/roles"], + }, + "rbac.role.setPermissions": { + tags: ["rbac"], + queryKeys: [["rbac", "roles"]], + paths: ["/admin/roles"], + }, + "rbac.assignUserRoles": { + tags: ["rbac"], + queryKeys: [["rbac", "userRoles"]], + paths: ["/admin/users"], + }, + + // ===== scheduling 模块 ===== + "scheduling.saveRules": { + tags: ["scheduling"], + queryKeys: [["scheduling", "rules"]], + paths: ["/admin/scheduling/rules"], + }, + "scheduling.autoSchedule": { + tags: ["scheduling"], + queryKeys: [["scheduling", "list"]], + paths: ["/admin/scheduling/auto", "/teacher/classes/schedule"], + }, + "scheduling.applyAutoSchedule": { + tags: ["scheduling"], + queryKeys: [["scheduling", "changes"]], + paths: ["/teacher/schedule-changes", "/admin/scheduling/changes"], + }, + "scheduling.requestChange": { + tags: ["scheduling"], + queryKeys: [["scheduling", "changes"]], + paths: ["/admin/scheduling/changes", "/teacher/schedule-changes"], + }, + "scheduling.approveChange": { + tags: ["scheduling"], + queryKeys: [["scheduling", "changes"]], + paths: ["/admin/scheduling/changes", "/teacher/schedule-changes"], + }, + "scheduling.rejectChange": { + tags: ["scheduling"], + queryKeys: [["scheduling", "changes"]], + paths: ["/admin/scheduling/changes", "/teacher/schedule-changes"], + }, + + // ===== school 模块 ===== + "school.department.create": { + tags: ["school"], + queryKeys: [["school", "departments"]], + paths: ["/admin/school/departments"], + }, + "school.department.update": { + tags: ["school"], + queryKeys: [["school", "departments"]], + paths: ["/admin/school/departments"], + }, + "school.department.delete": { + tags: ["school"], + queryKeys: [["school", "departments"]], + paths: ["/admin/school/departments"], + }, + "school.academicYear.create": { + tags: ["school"], + queryKeys: [["school", "academicYears"]], + paths: ["/admin/school/academic-year"], + }, + "school.academicYear.update": { + tags: ["school"], + queryKeys: [["school", "academicYears"]], + paths: ["/admin/school/academic-year"], + }, + "school.academicYear.delete": { + tags: ["school"], + queryKeys: [["school", "academicYears"]], + paths: ["/admin/school/academic-year"], + }, + "school.school.create": { + tags: ["school"], + queryKeys: [["school", "schools"]], + paths: ["/admin/school/schools", "/admin/school/grades"], + }, + "school.school.update": { + tags: ["school"], + queryKeys: [["school", "schools"]], + paths: ["/admin/school/schools", "/admin/school/grades"], + }, + "school.school.delete": { + tags: ["school"], + queryKeys: [["school", "schools"]], + paths: ["/admin/school/schools", "/admin/school/grades"], + }, + "school.grade.create": { + tags: ["school"], + queryKeys: [["school", "grades"]], + paths: ["/admin/school/grades"], + }, + "school.grade.update": { + tags: ["school"], + queryKeys: [["school", "grades"]], + paths: ["/admin/school/grades"], + }, + "school.grade.delete": { + tags: ["school"], + queryKeys: [["school", "grades"]], + paths: ["/admin/school/grades"], + }, + "school.grade.promote": { + tags: ["school"], + queryKeys: [["school", "grades"]], + paths: ["/admin/school/grades"], + }, + + // ===== settings 模块 ===== + "settings.aiProvider.upsert": { + tags: ["settings"], + queryKeys: [["settings", "aiProviders"]], + paths: ["/admin/ai-settings", "/settings"], + }, + "settings.aiProvider.test": { + tags: ["settings"], + queryKeys: [["settings", "aiProviders"]], + paths: ["/admin/ai-settings", "/settings"], + }, + "settings.aiProvider.delete": { + tags: ["settings"], + queryKeys: [["settings", "aiProviders"]], + paths: ["/admin/ai-settings", "/settings"], + }, + "settings.avatar.update": { + tags: ["settings"], + queryKeys: [["settings", "avatar"]], + paths: ["/profile", "/settings"], + }, + "settings.avatar.remove": { + tags: ["settings"], + queryKeys: [["settings", "avatar"]], + paths: ["/profile", "/settings"], + }, + "settings.brand.save": { + tags: ["settings"], + queryKeys: [["settings", "brand"]], + paths: ["/admin/settings", "/(auth)/login", "/(auth)/register"], + }, + "settings.password.change": { + tags: ["settings"], + queryKeys: [["settings", "password"]], + paths: ["/settings"], + }, + "settings.security.setup2FA": { + tags: ["settings"], + queryKeys: [["settings", "security"]], + paths: ["/settings"], + }, + "settings.security.verify2FA": { + tags: ["settings"], + queryKeys: [["settings", "security"]], + paths: ["/settings"], + }, + "settings.security.disable2FA": { + tags: ["settings"], + queryKeys: [["settings", "security"]], + paths: ["/settings"], + }, + "settings.security.regenerateBackupCodes": { + tags: ["settings"], + queryKeys: [["settings", "security"]], + paths: ["/settings"], + }, + "settings.security.revokeSessions": { + tags: ["settings"], + queryKeys: [["settings", "security"]], + paths: ["/settings"], + }, + "settings.profile.update": { + tags: ["settings"], + queryKeys: [["settings", "profile"]], + paths: ["/settings"], + }, + "settings.notifications.updatePreferences": { + tags: ["settings"], + queryKeys: [["settings", "notificationPreferences"]], + paths: ["/settings"], + }, + "settings.systemSettings.save": { + tags: ["settings"], + queryKeys: [["settings", "system"]], + paths: ["/admin/settings"], + }, + + // ===== standards 模块 ===== + "standards.create": { + tags: ["standards"], + queryKeys: [["standards", "list"]], + paths: ["/admin/standards"], + }, + "standards.update": { + tags: ["standards"], + queryKeys: [["standards", "detail"], ["standards", "list"]], + paths: ["/admin/standards"], + }, + "standards.deactivate": { + tags: ["standards"], + queryKeys: [["standards", "list"]], + paths: ["/admin/standards"], + }, + "standards.linkPlan": { + tags: ["standards"], + queryKeys: [["standards", "planLinks"]], + paths: ["/teacher/lesson-plans"], + }, + "standards.unlinkPlan": { + tags: ["standards"], + queryKeys: [["standards", "planLinks"]], + paths: ["/teacher/lesson-plans"], + }, + + // ===== textbooks 模块 ===== + "textbooks.create": { + tags: ["textbooks"], + queryKeys: [["textbooks", "list"]], + paths: ["/teacher/textbooks"], + }, + "textbooks.update": { + tags: ["textbooks"], + queryKeys: [["textbooks", "detail"], ["textbooks", "list"]], + paths: ["/teacher/textbooks"], + }, + "textbooks.delete": { + tags: ["textbooks"], + queryKeys: [["textbooks", "list"]], + paths: ["/teacher/textbooks"], + }, + "textbooks.chapter.create": { + tags: ["textbooks"], + queryKeys: [["textbooks", "chapters"]], + paths: ["/teacher/textbooks"], + }, + "textbooks.chapter.update": { + tags: ["textbooks"], + queryKeys: [["textbooks", "chapters"]], + paths: ["/teacher/textbooks"], + }, + "textbooks.chapter.delete": { + tags: ["textbooks"], + queryKeys: [["textbooks", "chapters"]], + paths: ["/teacher/textbooks"], + }, + "textbooks.chapter.reorder": { + tags: ["textbooks"], + queryKeys: [["textbooks", "chapters"]], + paths: ["/teacher/textbooks"], + }, + "textbooks.knowledgePoint.create": { + tags: ["textbooks"], + queryKeys: [["textbooks", "knowledgePoints"]], + paths: ["/teacher/textbooks"], + }, + "textbooks.knowledgePoint.update": { + tags: ["textbooks"], + queryKeys: [["textbooks", "knowledgePoints"]], + paths: ["/teacher/textbooks"], + }, + "textbooks.knowledgePoint.delete": { + tags: ["textbooks"], + queryKeys: [["textbooks", "knowledgePoints"]], + paths: ["/teacher/textbooks"], + }, + "textbooks.prerequisite.create": { + tags: ["textbooks"], + queryKeys: [["textbooks", "prerequisites"]], + paths: ["/teacher/textbooks"], + }, + "textbooks.prerequisite.delete": { + tags: ["textbooks"], + queryKeys: [["textbooks", "prerequisites"]], + paths: ["/teacher/textbooks"], + }, + + // ===== users 模块 ===== + "users.update": { + tags: ["users"], + queryKeys: [["users", "detail"]], + paths: ["/profile", "/settings"], + }, + "users.import": { + tags: ["users"], + queryKeys: [["users", "list"]], + paths: ["/admin/users/import"], + }, + "users.delete": { + tags: ["users"], + queryKeys: [["users", "list"]], + paths: ["/admin/users"], + }, +} as const satisfies Record diff --git a/src/shared/lib/cache/invalidation-map-classes.ts b/src/shared/lib/cache/invalidation-map-classes.ts new file mode 100644 index 0000000..ecdd497 --- /dev/null +++ b/src/shared/lib/cache/invalidation-map-classes.ts @@ -0,0 +1,156 @@ +import type { InvalidationRule } from "./types" + +/** + * classes / i18n / adaptive-practice / announcements / attendance / audit 模块的失效规则。 + * + * 详见 `invalidation-map.ts` 中的聚合说明。 + */ +export const CLASSES_INVALIDATION_RULES = { + // ===== classes 模块 ===== + "classes.create": { + tags: ["classes", "classes:list"], + queryKeys: [["classes", "list"]], + paths: ["/teacher/classes/my", "/admin/classes"], + }, + "classes.update": { + tags: ["classes", "classes:detail", "classes:detail:{id}"], + queryKeys: [["classes", "detail"], ["classes", "list"]], + // paths 不含动态段:revalidatePath("/teacher/classes/my") 已能覆盖详情页([id] 路由) + paths: ["/teacher/classes/my", "/admin/classes"], + }, + "classes.delete": { + tags: ["classes", "classes:list", "classes:detail:{id}"], + queryKeys: [["classes", "list"], ["classes", "detail"]], + paths: ["/teacher/classes/my", "/admin/classes"], + }, + "classes.students.update": { + tags: ["classes:students:{classId}"], + queryKeys: [["classes", "students"]], + paths: ["/teacher/classes/my"], + }, + "classes.schedule.update": { + tags: ["classes:schedule:{classId}"], + queryKeys: [["classes", "schedule"]], + paths: ["/teacher/classes/schedule"], + }, + "classes.grade.update": { + tags: ["classes"], + queryKeys: [["classes", "list"]], + paths: [], + }, + "classes.invitation.create": { + tags: ["classes:invitations:{classId}"], + queryKeys: [["classes", "invitations"]], + paths: ["/teacher/classes/my/{classId}"], + }, + "classes.invitation.revoke": { + tags: ["classes:invitations:{classId}"], + queryKeys: [["classes", "invitations"]], + paths: ["/teacher/classes/my/{classId}"], + }, + + // ===== i18n 模块 ===== + "i18n.setLocale": { + tags: ["i18n"], + queryKeys: [["i18n"]], + paths: ["/"], + }, + + // ===== adaptive-practice 模块 ===== + "adaptive-practice.create": { + tags: ["adaptive-practice"], + queryKeys: [["adaptive-practice", "list"]], + paths: ["/student/practice"], + }, + "adaptive-practice.submit": { + tags: ["adaptive-practice"], + queryKeys: [["adaptive-practice", "detail"]], + paths: ["/student/practice"], + }, + "adaptive-practice.complete": { + tags: ["adaptive-practice"], + queryKeys: [["adaptive-practice", "list"], ["adaptive-practice", "detail"]], + paths: ["/student/practice"], + }, + "adaptive-practice.abandon": { + tags: ["adaptive-practice"], + queryKeys: [["adaptive-practice", "list"]], + paths: ["/student/practice"], + }, + + // ===== announcements 模块 ===== + "announcements.create": { + tags: ["announcements"], + queryKeys: [["announcements", "list"]], + paths: ["/admin/announcements", "/announcements"], + }, + "announcements.update": { + tags: ["announcements"], + queryKeys: [["announcements", "detail"], ["announcements", "list"]], + paths: ["/admin/announcements", "/announcements"], + }, + "announcements.delete": { + tags: ["announcements"], + queryKeys: [["announcements", "list"]], + paths: ["/admin/announcements", "/announcements"], + }, + "announcements.publish": { + tags: ["announcements"], + queryKeys: [["announcements", "list"]], + paths: ["/admin/announcements", "/announcements"], + }, + "announcements.archive": { + tags: ["announcements"], + queryKeys: [["announcements", "list"]], + paths: ["/admin/announcements", "/announcements"], + }, + "announcements.togglePin": { + tags: ["announcements"], + queryKeys: [["announcements", "list"]], + paths: ["/admin/announcements", "/announcements"], + }, + "announcements.markRead": { + tags: ["announcements"], + queryKeys: [["announcements", "unread"]], + paths: [], + }, + + // ===== attendance 模块 ===== + "attendance.record": { + tags: ["attendance"], + queryKeys: [["attendance", "list"]], + paths: ["/teacher/attendance"], + }, + "attendance.batchRecord": { + tags: ["attendance"], + queryKeys: [["attendance", "list"]], + paths: ["/teacher/attendance"], + }, + "attendance.update": { + tags: ["attendance"], + queryKeys: [["attendance", "detail"], ["attendance", "list"]], + paths: ["/teacher/attendance"], + }, + "attendance.delete": { + tags: ["attendance"], + queryKeys: [["attendance", "list"]], + paths: ["/teacher/attendance"], + }, + "attendance.saveRules": { + tags: ["attendance"], + queryKeys: [["attendance", "rules"]], + paths: ["/teacher/attendance"], + }, + + // ===== audit 模块 ===== + "audit.saveRetentionConfig": { + tags: ["audit"], + queryKeys: [["audit", "config"]], + paths: ["/admin/audit-logs/overview", "/admin/audit-logs"], + }, + "audit.purge": { + tags: ["audit"], + queryKeys: [["audit", "list"]], + paths: ["/admin/audit-logs/overview", "/admin/audit-logs"], + }, +} as const satisfies Record diff --git a/src/shared/lib/cache/invalidation-map-grades.ts b/src/shared/lib/cache/invalidation-map-grades.ts new file mode 100644 index 0000000..7800306 --- /dev/null +++ b/src/shared/lib/cache/invalidation-map-grades.ts @@ -0,0 +1,166 @@ +import type { InvalidationRule } from "./types" + +/** + * grades / homework / invitation-codes / leave-requests 模块的失效规则。 + * + * 详见 `invalidation-map.ts` 中的聚合说明。 + */ +export const GRADES_INVALIDATION_RULES = { + // ===== grades 模块 ===== + "grades.create": { + tags: ["grades"], + queryKeys: [["grades", "list"]], + paths: ["/teacher/grades"], + }, + "grades.batchCreate": { + tags: ["grades"], + queryKeys: [["grades", "list"]], + paths: ["/teacher/grades"], + }, + "grades.batchCreateByExam": { + tags: ["grades"], + queryKeys: [["grades", "list"]], + paths: ["/teacher/grades"], + }, + "grades.undoBatchCreate": { + tags: ["grades"], + queryKeys: [["grades", "list"]], + paths: ["/teacher/grades"], + }, + "grades.update": { + tags: ["grades"], + queryKeys: [["grades", "detail"], ["grades", "list"]], + paths: ["/teacher/grades"], + }, + "grades.delete": { + tags: ["grades"], + queryKeys: [["grades", "list"]], + paths: ["/teacher/grades"], + }, + "grades.bulkDelete": { + tags: ["grades"], + queryKeys: [["grades", "list"]], + paths: ["/teacher/grades"], + }, + "grades.import": { + tags: ["grades"], + queryKeys: [["grades", "list"]], + paths: ["/teacher/grades", "/teacher/grades/entry"], + }, + "grades.draft.save": { + tags: ["grades"], + queryKeys: [["grades", "draft"]], + paths: ["/teacher/grades"], + }, + "grades.draft.delete": { + tags: ["grades"], + queryKeys: [["grades", "draft"]], + paths: [], + }, + "grades.appeal.create": { + tags: ["grades"], + queryKeys: [["grades", "appeals"]], + paths: ["/student/grades"], + }, + "grades.appeal.withdraw": { + tags: ["grades"], + queryKeys: [["grades", "appeals"]], + paths: ["/student/grades"], + }, + "grades.appeal.review": { + tags: ["grades"], + queryKeys: [["grades", "appeals"]], + paths: ["/teacher/grades", "/student/grades"], + }, + "grades.lock.acquire": { + tags: ["grades"], + queryKeys: [["grades", "lock"]], + paths: [], + }, + "grades.lock.renew": { + tags: ["grades"], + queryKeys: [["grades", "lock"]], + paths: [], + }, + "grades.lock.release": { + tags: ["grades"], + queryKeys: [["grades", "lock"]], + paths: [], + }, + + // ===== homework 模块 ===== + "homework.create": { + tags: ["homework"], + queryKeys: [["homework", "list"]], + paths: ["/teacher/homework/assignments", "/teacher/homework/submissions"], + }, + "homework.startSubmission": { + tags: ["homework"], + queryKeys: [["homework", "submissions"]], + paths: ["/student/learning/assignments"], + }, + "homework.saveAnswer": { + tags: ["homework"], + queryKeys: [["homework", "submissions"]], + paths: [], + }, + "homework.submit": { + tags: ["homework"], + queryKeys: [["homework", "submissions"]], + paths: ["/teacher/homework/submissions", "/student/learning/assignments"], + }, + "homework.grade": { + tags: ["homework"], + queryKeys: [["homework", "submissions"]], + paths: ["/teacher/homework/submissions"], + }, + "homework.batchAutoGrade": { + tags: ["homework"], + queryKeys: [["homework", "submissions"]], + paths: ["/teacher/homework/submissions", "/teacher/homework/assignments"], + }, + "homework.deleteScan": { + tags: ["homework"], + queryKeys: [["homework", "scans"]], + paths: [], + }, + "homework.remind": { + tags: ["homework"], + queryKeys: [["homework", "list"]], + paths: [], + }, + + // ===== invitation-codes 模块 ===== + "invitation-codes.generate": { + tags: ["invitation-codes"], + queryKeys: [["invitation-codes", "list"]], + paths: ["/admin/invitation-codes"], + }, + "invitation-codes.delete": { + tags: ["invitation-codes"], + queryKeys: [["invitation-codes", "list"]], + paths: ["/admin/invitation-codes"], + }, + "invitation-codes.validate": { + tags: ["invitation-codes"], + queryKeys: [["invitation-codes"]], + paths: [], + }, + + // ===== leave-requests 模块 ===== + "leave-requests.create": { + tags: ["leave-requests"], + queryKeys: [["leave-requests", "list"]], + paths: ["/parent/leave", "/student/leave", "/teacher/leave"], + }, + "leave-requests.review": { + tags: ["leave-requests"], + queryKeys: [["leave-requests", "list"]], + paths: ["/parent/leave", "/student/leave", "/teacher/leave"], + }, + "leave-requests.cancel": { + tags: ["leave-requests"], + queryKeys: [["leave-requests", "list"]], + paths: ["/parent/leave", "/student/leave", "/teacher/leave"], + }, +} as const satisfies Record diff --git a/src/shared/lib/cache/invalidation-map-lesson-preparation.ts b/src/shared/lib/cache/invalidation-map-lesson-preparation.ts new file mode 100644 index 0000000..92b7d8d --- /dev/null +++ b/src/shared/lib/cache/invalidation-map-lesson-preparation.ts @@ -0,0 +1,150 @@ +import type { InvalidationRule } from "./types" + +/** + * lesson-preparation 模块的失效规则。 + * + * 详见 `invalidation-map.ts` 中的聚合说明。 + */ +export const LESSON_PREPARATION_INVALIDATION_RULES = { + // ===== lesson-preparation 模块 ===== + "lesson-preparation.create": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "list"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.update": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "detail"], ["lesson-preparation", "list"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.saveVersion": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "versions"]], + paths: [], + }, + "lesson-preparation.revertVersion": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "versions"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.delete": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "list"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.publish": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "detail"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.unpublish": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "detail"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.duplicate": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "list"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.template.save": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "templates"]], + paths: [], + }, + "lesson-preparation.template.delete": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "templates"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.formative.create": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "formative"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.formative.update": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "formative"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.formative.delete": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "formative"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.formative.submitResponse": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "formative"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.comment.create": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "comments"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.comment.update": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "comments"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.comment.toggleResolved": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "comments"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.comment.delete": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "comments"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.substitute.create": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "substitutes"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.substitute.updateStatus": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "substitutes"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.substitute.delete": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "substitutes"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.attachment.create": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "detail"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.review.submit": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "detail"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.review.review": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "detail"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.review.withdraw": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "detail"]], + paths: ["/teacher/lesson-plans"], + }, + "lesson-preparation.schedule.create": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "schedules"]], + paths: ["/teacher/lesson-plans", "/teacher/lesson-plans/calendar"], + }, + "lesson-preparation.schedule.delete": { + tags: ["lesson-preparation"], + queryKeys: [["lesson-preparation", "schedules"]], + paths: ["/teacher/lesson-plans", "/teacher/lesson-plans/calendar"], + }, + "lesson-preparation.publishHomework": { + tags: ["lesson-preparation", "homework"], + queryKeys: [["lesson-preparation", "list"], ["homework", "list"]], + paths: ["/teacher/lesson-plans", "/teacher/homework"], + }, +} as const satisfies Record diff --git a/src/shared/lib/cache/invalidation-map-messaging.ts b/src/shared/lib/cache/invalidation-map-messaging.ts new file mode 100644 index 0000000..e7ba4a9 --- /dev/null +++ b/src/shared/lib/cache/invalidation-map-messaging.ts @@ -0,0 +1,136 @@ +import type { InvalidationRule } from "./types" + +/** + * messaging / notifications / onboarding / proctoring 模块的失效规则。 + * + * 详见 `invalidation-map.ts` 中的聚合说明。 + */ +export const MESSAGING_INVALIDATION_RULES = { + // ===== messaging 模块 ===== + "messaging.send": { + tags: ["messaging"], + queryKeys: [["messaging", "list"]], + paths: ["/messages"], + }, + "messaging.markRead": { + tags: ["messaging"], + queryKeys: [["messaging", "detail"], ["messaging", "unread"]], + paths: ["/messages"], + }, + "messaging.delete": { + tags: ["messaging"], + queryKeys: [["messaging", "list"]], + paths: ["/messages"], + }, + "messaging.toggleStar": { + tags: ["messaging"], + queryKeys: [["messaging", "detail"]], + paths: ["/messages"], + }, + "messaging.draft.save": { + tags: ["messaging"], + queryKeys: [["messaging", "drafts"]], + paths: ["/messages/compose"], + }, + "messaging.draft.delete": { + tags: ["messaging"], + queryKeys: [["messaging", "drafts"]], + paths: ["/messages"], + }, + "messaging.bulkMarkRead": { + tags: ["messaging"], + queryKeys: [["messaging", "list"], ["messaging", "unread"]], + paths: ["/messages"], + }, + "messaging.bulkDelete": { + tags: ["messaging"], + queryKeys: [["messaging", "list"]], + paths: ["/messages"], + }, + "messaging.bulkToggleStar": { + tags: ["messaging"], + queryKeys: [["messaging", "list"]], + paths: ["/messages"], + }, + "messaging.recall": { + tags: ["messaging"], + queryKeys: [["messaging", "detail"]], + paths: ["/messages"], + }, + "messaging.sendGroup": { + tags: ["messaging"], + queryKeys: [["messaging", "list"]], + paths: ["/messages", "/messages/compose"], + }, + "messaging.template.save": { + tags: ["messaging"], + queryKeys: [["messaging", "templates"]], + paths: ["/messages"], + }, + "messaging.template.delete": { + tags: ["messaging"], + queryKeys: [["messaging", "templates"]], + paths: ["/messages"], + }, + "messaging.report": { + tags: ["messaging"], + queryKeys: [["messaging", "detail"]], + paths: ["/messages"], + }, + "messaging.block": { + tags: ["messaging"], + queryKeys: [["messaging", "blocks"]], + paths: ["/messages"], + }, + "messaging.unblock": { + tags: ["messaging"], + queryKeys: [["messaging", "blocks"]], + paths: ["/messages"], + }, + + // ===== notifications 模块 ===== + "notifications.send": { + tags: ["notifications"], + queryKeys: [["notifications", "list"]], + paths: [], + }, + "notifications.sendClass": { + tags: ["notifications"], + queryKeys: [["notifications", "list"]], + paths: [], + }, + "notifications.markRead": { + tags: ["notifications"], + queryKeys: [["notifications", "detail"], ["notifications", "unread"]], + paths: ["/messages"], + }, + "notifications.markAllRead": { + tags: ["notifications"], + queryKeys: [["notifications", "list"], ["notifications", "unread"]], + paths: ["/messages"], + }, + "notifications.archive": { + tags: ["notifications"], + queryKeys: [["notifications", "list"]], + paths: ["/messages"], + }, + "notifications.updatePreferences": { + tags: ["notifications"], + queryKeys: [["notifications", "preferences"]], + paths: ["/settings"], + }, + + // ===== onboarding 模块 ===== + "onboarding.complete": { + tags: ["onboarding"], + queryKeys: [["onboarding", "status"]], + paths: ["/onboarding"], + }, + + // ===== proctoring 模块 ===== + "proctoring.recordEvent": { + tags: ["proctoring"], + queryKeys: [["proctoring", "events"]], + paths: ["/teacher/exams"], + }, +} as const satisfies Record diff --git a/src/shared/lib/cache/invalidation-map.ts b/src/shared/lib/cache/invalidation-map.ts index 6bc019b..4e90be1 100644 --- a/src/shared/lib/cache/invalidation-map.ts +++ b/src/shared/lib/cache/invalidation-map.ts @@ -1,4 +1,10 @@ import type { InvalidationRule } from "./types" +import { ACADEMIC_INVALIDATION_RULES } from "./invalidation-map-academic" +import { ADMIN_INVALIDATION_RULES } from "./invalidation-map-admin" +import { CLASSES_INVALIDATION_RULES } from "./invalidation-map-classes" +import { GRADES_INVALIDATION_RULES } from "./invalidation-map-grades" +import { LESSON_PREPARATION_INVALIDATION_RULES } from "./invalidation-map-lesson-preparation" +import { MESSAGING_INVALIDATION_RULES } from "./invalidation-map-messaging" /** * 模板占位符填充。 @@ -31,1164 +37,17 @@ export function fillTemplate( * - tags:模块级粒度 `["{module}"]`(简化迁移,不强制资源级) * - queryKeys:`[["{module}"]]` 或 `[["{module}", "list"]]` * - paths:从原 revalidatePath 调用中提取;动态段(`[id]` 路由)用父路径覆盖 + * + * 拆分说明:规则按业务域分布在 `invalidation-map-*.ts` 子文件中, + * 此处通过 spread 聚合成单一 `INVALIDATION_MAP`,保留对外的稳定 API。 */ export const INVALIDATION_MAP = { - // ===== classes 模块 ===== - "classes.create": { - tags: ["classes", "classes:list"], - queryKeys: [["classes", "list"]], - paths: ["/teacher/classes/my", "/admin/classes"], - }, - "classes.update": { - tags: ["classes", "classes:detail", "classes:detail:{id}"], - queryKeys: [["classes", "detail"], ["classes", "list"]], - // paths 不含动态段:revalidatePath("/teacher/classes/my") 已能覆盖详情页([id] 路由) - paths: ["/teacher/classes/my", "/admin/classes"], - }, - "classes.delete": { - tags: ["classes", "classes:list", "classes:detail:{id}"], - queryKeys: [["classes", "list"], ["classes", "detail"]], - paths: ["/teacher/classes/my", "/admin/classes"], - }, - "classes.students.update": { - tags: ["classes:students:{classId}"], - queryKeys: [["classes", "students"]], - paths: ["/teacher/classes/my"], - }, - "classes.schedule.update": { - tags: ["classes:schedule:{classId}"], - queryKeys: [["classes", "schedule"]], - paths: ["/teacher/classes/schedule"], - }, - "classes.grade.update": { - tags: ["classes"], - queryKeys: [["classes", "list"]], - paths: [], - }, - "classes.invitation.create": { - tags: ["classes:invitations:{classId}"], - queryKeys: [["classes", "invitations"]], - paths: ["/teacher/classes/my/{classId}"], - }, - "classes.invitation.revoke": { - tags: ["classes:invitations:{classId}"], - queryKeys: [["classes", "invitations"]], - paths: ["/teacher/classes/my/{classId}"], - }, - - // ===== i18n 模块 ===== - "i18n.setLocale": { - tags: ["i18n"], - queryKeys: [["i18n"]], - paths: ["/"], - }, - - // ===== adaptive-practice 模块 ===== - "adaptive-practice.create": { - tags: ["adaptive-practice"], - queryKeys: [["adaptive-practice", "list"]], - paths: ["/student/practice"], - }, - "adaptive-practice.submit": { - tags: ["adaptive-practice"], - queryKeys: [["adaptive-practice", "detail"]], - paths: ["/student/practice"], - }, - "adaptive-practice.complete": { - tags: ["adaptive-practice"], - queryKeys: [["adaptive-practice", "list"], ["adaptive-practice", "detail"]], - paths: ["/student/practice"], - }, - "adaptive-practice.abandon": { - tags: ["adaptive-practice"], - queryKeys: [["adaptive-practice", "list"]], - paths: ["/student/practice"], - }, - - // ===== announcements 模块 ===== - "announcements.create": { - tags: ["announcements"], - queryKeys: [["announcements", "list"]], - paths: ["/admin/announcements", "/announcements"], - }, - "announcements.update": { - tags: ["announcements"], - queryKeys: [["announcements", "detail"], ["announcements", "list"]], - paths: ["/admin/announcements", "/announcements"], - }, - "announcements.delete": { - tags: ["announcements"], - queryKeys: [["announcements", "list"]], - paths: ["/admin/announcements", "/announcements"], - }, - "announcements.publish": { - tags: ["announcements"], - queryKeys: [["announcements", "list"]], - paths: ["/admin/announcements", "/announcements"], - }, - "announcements.archive": { - tags: ["announcements"], - queryKeys: [["announcements", "list"]], - paths: ["/admin/announcements", "/announcements"], - }, - "announcements.togglePin": { - tags: ["announcements"], - queryKeys: [["announcements", "list"]], - paths: ["/admin/announcements", "/announcements"], - }, - "announcements.markRead": { - tags: ["announcements"], - queryKeys: [["announcements", "unread"]], - paths: [], - }, - - // ===== attendance 模块 ===== - "attendance.record": { - tags: ["attendance"], - queryKeys: [["attendance", "list"]], - paths: ["/teacher/attendance"], - }, - "attendance.batchRecord": { - tags: ["attendance"], - queryKeys: [["attendance", "list"]], - paths: ["/teacher/attendance"], - }, - "attendance.update": { - tags: ["attendance"], - queryKeys: [["attendance", "detail"], ["attendance", "list"]], - paths: ["/teacher/attendance"], - }, - "attendance.delete": { - tags: ["attendance"], - queryKeys: [["attendance", "list"]], - paths: ["/teacher/attendance"], - }, - "attendance.saveRules": { - tags: ["attendance"], - queryKeys: [["attendance", "rules"]], - paths: ["/teacher/attendance"], - }, - - // ===== audit 模块 ===== - "audit.saveRetentionConfig": { - tags: ["audit"], - queryKeys: [["audit", "config"]], - paths: ["/admin/audit-logs/overview", "/admin/audit-logs"], - }, - "audit.purge": { - tags: ["audit"], - queryKeys: [["audit", "list"]], - paths: ["/admin/audit-logs/overview", "/admin/audit-logs"], - }, - - // ===== course-plans 模块 ===== - "course-plans.create": { - tags: ["course-plans"], - queryKeys: [["course-plans", "list"]], - paths: [ - "/admin/course-plans", - "/teacher/course-plans", - "/parent/course-plans", - "/student/course-plans", - ], - }, - "course-plans.update": { - tags: ["course-plans"], - queryKeys: [["course-plans", "detail"], ["course-plans", "list"]], - paths: [ - "/admin/course-plans", - "/teacher/course-plans", - "/parent/course-plans", - "/student/course-plans", - ], - }, - "course-plans.delete": { - tags: ["course-plans"], - queryKeys: [["course-plans", "list"]], - paths: [ - "/admin/course-plans", - "/teacher/course-plans", - "/parent/course-plans", - "/student/course-plans", - ], - }, - "course-plans.item.create": { - tags: ["course-plans"], - queryKeys: [["course-plans", "items"]], - paths: [ - "/admin/course-plans", - "/teacher/course-plans", - "/parent/course-plans", - "/student/course-plans", - ], - }, - "course-plans.item.update": { - tags: ["course-plans"], - queryKeys: [["course-plans", "items"]], - paths: [ - "/admin/course-plans", - "/teacher/course-plans", - "/parent/course-plans", - "/student/course-plans", - ], - }, - "course-plans.item.delete": { - tags: ["course-plans"], - queryKeys: [["course-plans", "items"]], - paths: [ - "/admin/course-plans", - "/teacher/course-plans", - "/parent/course-plans", - "/student/course-plans", - ], - }, - "course-plans.item.toggleCompleted": { - tags: ["course-plans"], - queryKeys: [["course-plans", "items"]], - paths: [ - "/admin/course-plans", - "/teacher/course-plans", - "/parent/course-plans", - "/student/course-plans", - ], - }, - "course-plans.item.reorder": { - tags: ["course-plans"], - queryKeys: [["course-plans", "items"]], - paths: [ - "/admin/course-plans", - "/teacher/course-plans", - "/parent/course-plans", - "/student/course-plans", - ], - }, - "course-plans.item.bulkToggle": { - tags: ["course-plans"], - queryKeys: [["course-plans", "items"]], - paths: [ - "/admin/course-plans", - "/teacher/course-plans", - "/parent/course-plans", - "/student/course-plans", - ], - }, - "course-plans.copy": { - tags: ["course-plans"], - queryKeys: [["course-plans", "list"]], - paths: [ - "/admin/course-plans", - "/teacher/course-plans", - "/parent/course-plans", - "/student/course-plans", - ], - }, - - // ===== diagnostic 模块 ===== - "diagnostic.generateStudentReport": { - tags: ["diagnostic"], - queryKeys: [["diagnostic", "list"]], - paths: ["/teacher/diagnostic"], - }, - "diagnostic.generateClassReport": { - tags: ["diagnostic"], - queryKeys: [["diagnostic", "list"]], - paths: ["/teacher/diagnostic"], - }, - "diagnostic.generateGradeReport": { - tags: ["diagnostic"], - queryKeys: [["diagnostic", "list"]], - paths: ["/teacher/diagnostic", "/admin/diagnostic"], - }, - "diagnostic.publish": { - tags: ["diagnostic"], - queryKeys: [["diagnostic", "list"]], - paths: ["/teacher/diagnostic"], - }, - "diagnostic.delete": { - tags: ["diagnostic"], - queryKeys: [["diagnostic", "list"]], - paths: ["/teacher/diagnostic"], - }, - - // ===== elective 模块 ===== - "elective.create": { - tags: ["elective"], - queryKeys: [["elective", "list"]], - paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], - }, - "elective.update": { - tags: ["elective"], - queryKeys: [["elective", "detail"], ["elective", "list"]], - paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], - }, - "elective.delete": { - tags: ["elective"], - queryKeys: [["elective", "list"]], - paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], - }, - "elective.openSelection": { - tags: ["elective"], - queryKeys: [["elective", "list"]], - paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], - }, - "elective.closeSelection": { - tags: ["elective"], - queryKeys: [["elective", "list"]], - paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], - }, - "elective.runLottery": { - tags: ["elective"], - queryKeys: [["elective", "list"]], - paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], - }, - "elective.selectCourse": { - tags: ["elective"], - queryKeys: [["elective", "selections"]], - paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], - }, - "elective.dropCourse": { - tags: ["elective"], - queryKeys: [["elective", "selections"]], - paths: ["/admin/elective", "/teacher/elective", "/student/elective", "/parent/elective"], - }, - - // ===== error-book 模块 ===== - "error-book.create": { - tags: ["error-book"], - queryKeys: [["error-book", "list"]], - paths: ["/student/error-book"], - }, - "error-book.update": { - tags: ["error-book"], - queryKeys: [["error-book", "detail"], ["error-book", "list"]], - paths: ["/student/error-book"], - }, - "error-book.review": { - tags: ["error-book"], - queryKeys: [["error-book", "detail"]], - paths: ["/student/error-book"], - }, - "error-book.delete": { - tags: ["error-book"], - queryKeys: [["error-book", "list"]], - paths: ["/student/error-book"], - }, - "error-book.archive": { - tags: ["error-book"], - queryKeys: [["error-book", "list"]], - paths: ["/student/error-book"], - }, - "error-book.collect": { - tags: ["error-book"], - queryKeys: [["error-book", "list"]], - paths: ["/student/error-book"], - }, - - // ===== exams 模块 ===== - "exams.create": { - tags: ["exams"], - queryKeys: [["exams", "list"]], - paths: ["/teacher/exams/all"], - }, - "exams.createAi": { - tags: ["exams"], - queryKeys: [["exams", "list"]], - paths: ["/teacher/exams/all"], - }, - "exams.regenerateAiQuestion": { - tags: ["exams"], - queryKeys: [["exams", "detail"]], - paths: [], - }, - "exams.update": { - tags: ["exams"], - queryKeys: [["exams", "detail"], ["exams", "list"]], - paths: ["/teacher/exams/all"], - }, - "exams.delete": { - tags: ["exams"], - queryKeys: [["exams", "list"]], - paths: ["/teacher/exams/all"], - }, - "exams.duplicate": { - tags: ["exams"], - queryKeys: [["exams", "list"]], - paths: ["/teacher/exams/all"], - }, - "exams.richContent.save": { - tags: ["exams"], - queryKeys: [["exams", "detail"]], - paths: ["/teacher/exams/all"], - }, - - // ===== grades 模块 ===== - "grades.create": { - tags: ["grades"], - queryKeys: [["grades", "list"]], - paths: ["/teacher/grades"], - }, - "grades.batchCreate": { - tags: ["grades"], - queryKeys: [["grades", "list"]], - paths: ["/teacher/grades"], - }, - "grades.batchCreateByExam": { - tags: ["grades"], - queryKeys: [["grades", "list"]], - paths: ["/teacher/grades"], - }, - "grades.undoBatchCreate": { - tags: ["grades"], - queryKeys: [["grades", "list"]], - paths: ["/teacher/grades"], - }, - "grades.update": { - tags: ["grades"], - queryKeys: [["grades", "detail"], ["grades", "list"]], - paths: ["/teacher/grades"], - }, - "grades.delete": { - tags: ["grades"], - queryKeys: [["grades", "list"]], - paths: ["/teacher/grades"], - }, - "grades.bulkDelete": { - tags: ["grades"], - queryKeys: [["grades", "list"]], - paths: ["/teacher/grades"], - }, - "grades.import": { - tags: ["grades"], - queryKeys: [["grades", "list"]], - paths: ["/teacher/grades", "/teacher/grades/entry"], - }, - "grades.draft.save": { - tags: ["grades"], - queryKeys: [["grades", "draft"]], - paths: ["/teacher/grades"], - }, - "grades.draft.delete": { - tags: ["grades"], - queryKeys: [["grades", "draft"]], - paths: [], - }, - "grades.appeal.create": { - tags: ["grades"], - queryKeys: [["grades", "appeals"]], - paths: ["/student/grades"], - }, - "grades.appeal.withdraw": { - tags: ["grades"], - queryKeys: [["grades", "appeals"]], - paths: ["/student/grades"], - }, - "grades.appeal.review": { - tags: ["grades"], - queryKeys: [["grades", "appeals"]], - paths: ["/teacher/grades", "/student/grades"], - }, - "grades.lock.acquire": { - tags: ["grades"], - queryKeys: [["grades", "lock"]], - paths: [], - }, - "grades.lock.renew": { - tags: ["grades"], - queryKeys: [["grades", "lock"]], - paths: [], - }, - "grades.lock.release": { - tags: ["grades"], - queryKeys: [["grades", "lock"]], - paths: [], - }, - - // ===== homework 模块 ===== - "homework.create": { - tags: ["homework"], - queryKeys: [["homework", "list"]], - paths: ["/teacher/homework/assignments", "/teacher/homework/submissions"], - }, - "homework.startSubmission": { - tags: ["homework"], - queryKeys: [["homework", "submissions"]], - paths: ["/student/learning/assignments"], - }, - "homework.saveAnswer": { - tags: ["homework"], - queryKeys: [["homework", "submissions"]], - paths: [], - }, - "homework.submit": { - tags: ["homework"], - queryKeys: [["homework", "submissions"]], - paths: ["/teacher/homework/submissions", "/student/learning/assignments"], - }, - "homework.grade": { - tags: ["homework"], - queryKeys: [["homework", "submissions"]], - paths: ["/teacher/homework/submissions"], - }, - "homework.batchAutoGrade": { - tags: ["homework"], - queryKeys: [["homework", "submissions"]], - paths: ["/teacher/homework/submissions", "/teacher/homework/assignments"], - }, - "homework.deleteScan": { - tags: ["homework"], - queryKeys: [["homework", "scans"]], - paths: [], - }, - "homework.remind": { - tags: ["homework"], - queryKeys: [["homework", "list"]], - paths: [], - }, - - // ===== invitation-codes 模块 ===== - "invitation-codes.generate": { - tags: ["invitation-codes"], - queryKeys: [["invitation-codes", "list"]], - paths: ["/admin/invitation-codes"], - }, - "invitation-codes.delete": { - tags: ["invitation-codes"], - queryKeys: [["invitation-codes", "list"]], - paths: ["/admin/invitation-codes"], - }, - "invitation-codes.validate": { - tags: ["invitation-codes"], - queryKeys: [["invitation-codes"]], - paths: [], - }, - - // ===== leave-requests 模块 ===== - "leave-requests.create": { - tags: ["leave-requests"], - queryKeys: [["leave-requests", "list"]], - paths: ["/parent/leave", "/student/leave", "/teacher/leave"], - }, - "leave-requests.review": { - tags: ["leave-requests"], - queryKeys: [["leave-requests", "list"]], - paths: ["/parent/leave", "/student/leave", "/teacher/leave"], - }, - "leave-requests.cancel": { - tags: ["leave-requests"], - queryKeys: [["leave-requests", "list"]], - paths: ["/parent/leave", "/student/leave", "/teacher/leave"], - }, - - // ===== lesson-preparation 模块 ===== - "lesson-preparation.create": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "list"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.update": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "detail"], ["lesson-preparation", "list"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.saveVersion": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "versions"]], - paths: [], - }, - "lesson-preparation.revertVersion": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "versions"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.delete": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "list"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.publish": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "detail"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.unpublish": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "detail"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.duplicate": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "list"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.template.save": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "templates"]], - paths: [], - }, - "lesson-preparation.template.delete": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "templates"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.formative.create": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "formative"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.formative.update": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "formative"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.formative.delete": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "formative"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.formative.submitResponse": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "formative"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.comment.create": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "comments"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.comment.update": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "comments"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.comment.toggleResolved": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "comments"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.comment.delete": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "comments"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.substitute.create": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "substitutes"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.substitute.updateStatus": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "substitutes"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.substitute.delete": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "substitutes"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.attachment.create": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "detail"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.review.submit": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "detail"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.review.review": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "detail"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.review.withdraw": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "detail"]], - paths: ["/teacher/lesson-plans"], - }, - "lesson-preparation.schedule.create": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "schedules"]], - paths: ["/teacher/lesson-plans", "/teacher/lesson-plans/calendar"], - }, - "lesson-preparation.schedule.delete": { - tags: ["lesson-preparation"], - queryKeys: [["lesson-preparation", "schedules"]], - paths: ["/teacher/lesson-plans", "/teacher/lesson-plans/calendar"], - }, - "lesson-preparation.publishHomework": { - tags: ["lesson-preparation", "homework"], - queryKeys: [["lesson-preparation", "list"], ["homework", "list"]], - paths: ["/teacher/lesson-plans", "/teacher/homework"], - }, - - // ===== messaging 模块 ===== - "messaging.send": { - tags: ["messaging"], - queryKeys: [["messaging", "list"]], - paths: ["/messages"], - }, - "messaging.markRead": { - tags: ["messaging"], - queryKeys: [["messaging", "detail"], ["messaging", "unread"]], - paths: ["/messages"], - }, - "messaging.delete": { - tags: ["messaging"], - queryKeys: [["messaging", "list"]], - paths: ["/messages"], - }, - "messaging.toggleStar": { - tags: ["messaging"], - queryKeys: [["messaging", "detail"]], - paths: ["/messages"], - }, - "messaging.draft.save": { - tags: ["messaging"], - queryKeys: [["messaging", "drafts"]], - paths: ["/messages/compose"], - }, - "messaging.draft.delete": { - tags: ["messaging"], - queryKeys: [["messaging", "drafts"]], - paths: ["/messages"], - }, - "messaging.bulkMarkRead": { - tags: ["messaging"], - queryKeys: [["messaging", "list"], ["messaging", "unread"]], - paths: ["/messages"], - }, - "messaging.bulkDelete": { - tags: ["messaging"], - queryKeys: [["messaging", "list"]], - paths: ["/messages"], - }, - "messaging.bulkToggleStar": { - tags: ["messaging"], - queryKeys: [["messaging", "list"]], - paths: ["/messages"], - }, - "messaging.recall": { - tags: ["messaging"], - queryKeys: [["messaging", "detail"]], - paths: ["/messages"], - }, - "messaging.sendGroup": { - tags: ["messaging"], - queryKeys: [["messaging", "list"]], - paths: ["/messages", "/messages/compose"], - }, - "messaging.template.save": { - tags: ["messaging"], - queryKeys: [["messaging", "templates"]], - paths: ["/messages"], - }, - "messaging.template.delete": { - tags: ["messaging"], - queryKeys: [["messaging", "templates"]], - paths: ["/messages"], - }, - "messaging.report": { - tags: ["messaging"], - queryKeys: [["messaging", "detail"]], - paths: ["/messages"], - }, - "messaging.block": { - tags: ["messaging"], - queryKeys: [["messaging", "blocks"]], - paths: ["/messages"], - }, - "messaging.unblock": { - tags: ["messaging"], - queryKeys: [["messaging", "blocks"]], - paths: ["/messages"], - }, - - // ===== notifications 模块 ===== - "notifications.send": { - tags: ["notifications"], - queryKeys: [["notifications", "list"]], - paths: [], - }, - "notifications.sendClass": { - tags: ["notifications"], - queryKeys: [["notifications", "list"]], - paths: [], - }, - "notifications.markRead": { - tags: ["notifications"], - queryKeys: [["notifications", "detail"], ["notifications", "unread"]], - paths: ["/messages"], - }, - "notifications.markAllRead": { - tags: ["notifications"], - queryKeys: [["notifications", "list"], ["notifications", "unread"]], - paths: ["/messages"], - }, - "notifications.archive": { - tags: ["notifications"], - queryKeys: [["notifications", "list"]], - paths: ["/messages"], - }, - "notifications.updatePreferences": { - tags: ["notifications"], - queryKeys: [["notifications", "preferences"]], - paths: ["/settings"], - }, - - // ===== onboarding 模块 ===== - "onboarding.complete": { - tags: ["onboarding"], - queryKeys: [["onboarding", "status"]], - paths: ["/onboarding"], - }, - - // ===== proctoring 模块 ===== - "proctoring.recordEvent": { - tags: ["proctoring"], - queryKeys: [["proctoring", "events"]], - paths: ["/teacher/exams"], - }, - - // ===== questions 模块 ===== - "questions.create": { - tags: ["questions"], - queryKeys: [["questions", "list"]], - paths: ["/teacher/questions", "/admin/questions"], - }, - "questions.update": { - tags: ["questions"], - queryKeys: [["questions", "detail"], ["questions", "list"]], - paths: ["/teacher/questions", "/admin/questions"], - }, - "questions.delete": { - tags: ["questions"], - queryKeys: [["questions", "list"]], - paths: ["/teacher/questions", "/admin/questions"], - }, - "questions.deleteBatch": { - tags: ["questions"], - queryKeys: [["questions", "list"]], - paths: ["/teacher/questions", "/admin/questions"], - }, - "questions.import": { - tags: ["questions"], - queryKeys: [["questions", "list"]], - paths: ["/teacher/questions", "/admin/questions"], - }, - - // ===== rbac 模块 ===== - "rbac.role.create": { - tags: ["rbac"], - queryKeys: [["rbac", "roles"]], - paths: ["/admin/roles"], - }, - "rbac.role.update": { - tags: ["rbac"], - queryKeys: [["rbac", "roles"]], - paths: ["/admin/roles"], - }, - "rbac.role.delete": { - tags: ["rbac"], - queryKeys: [["rbac", "roles"]], - paths: ["/admin/roles"], - }, - "rbac.role.toggleEnabled": { - tags: ["rbac"], - queryKeys: [["rbac", "roles"]], - paths: ["/admin/roles"], - }, - "rbac.role.setPermissions": { - tags: ["rbac"], - queryKeys: [["rbac", "roles"]], - paths: ["/admin/roles"], - }, - "rbac.assignUserRoles": { - tags: ["rbac"], - queryKeys: [["rbac", "userRoles"]], - paths: ["/admin/users"], - }, - - // ===== scheduling 模块 ===== - "scheduling.saveRules": { - tags: ["scheduling"], - queryKeys: [["scheduling", "rules"]], - paths: ["/admin/scheduling/rules"], - }, - "scheduling.autoSchedule": { - tags: ["scheduling"], - queryKeys: [["scheduling", "list"]], - paths: ["/admin/scheduling/auto", "/teacher/classes/schedule"], - }, - "scheduling.applyAutoSchedule": { - tags: ["scheduling"], - queryKeys: [["scheduling", "changes"]], - paths: ["/teacher/schedule-changes", "/admin/scheduling/changes"], - }, - "scheduling.requestChange": { - tags: ["scheduling"], - queryKeys: [["scheduling", "changes"]], - paths: ["/admin/scheduling/changes", "/teacher/schedule-changes"], - }, - "scheduling.approveChange": { - tags: ["scheduling"], - queryKeys: [["scheduling", "changes"]], - paths: ["/admin/scheduling/changes", "/teacher/schedule-changes"], - }, - "scheduling.rejectChange": { - tags: ["scheduling"], - queryKeys: [["scheduling", "changes"]], - paths: ["/admin/scheduling/changes", "/teacher/schedule-changes"], - }, - - // ===== school 模块 ===== - "school.department.create": { - tags: ["school"], - queryKeys: [["school", "departments"]], - paths: ["/admin/school/departments"], - }, - "school.department.update": { - tags: ["school"], - queryKeys: [["school", "departments"]], - paths: ["/admin/school/departments"], - }, - "school.department.delete": { - tags: ["school"], - queryKeys: [["school", "departments"]], - paths: ["/admin/school/departments"], - }, - "school.academicYear.create": { - tags: ["school"], - queryKeys: [["school", "academicYears"]], - paths: ["/admin/school/academic-year"], - }, - "school.academicYear.update": { - tags: ["school"], - queryKeys: [["school", "academicYears"]], - paths: ["/admin/school/academic-year"], - }, - "school.academicYear.delete": { - tags: ["school"], - queryKeys: [["school", "academicYears"]], - paths: ["/admin/school/academic-year"], - }, - "school.school.create": { - tags: ["school"], - queryKeys: [["school", "schools"]], - paths: ["/admin/school/schools", "/admin/school/grades"], - }, - "school.school.update": { - tags: ["school"], - queryKeys: [["school", "schools"]], - paths: ["/admin/school/schools", "/admin/school/grades"], - }, - "school.school.delete": { - tags: ["school"], - queryKeys: [["school", "schools"]], - paths: ["/admin/school/schools", "/admin/school/grades"], - }, - "school.grade.create": { - tags: ["school"], - queryKeys: [["school", "grades"]], - paths: ["/admin/school/grades"], - }, - "school.grade.update": { - tags: ["school"], - queryKeys: [["school", "grades"]], - paths: ["/admin/school/grades"], - }, - "school.grade.delete": { - tags: ["school"], - queryKeys: [["school", "grades"]], - paths: ["/admin/school/grades"], - }, - "school.grade.promote": { - tags: ["school"], - queryKeys: [["school", "grades"]], - paths: ["/admin/school/grades"], - }, - - // ===== settings 模块 ===== - "settings.aiProvider.upsert": { - tags: ["settings"], - queryKeys: [["settings", "aiProviders"]], - paths: ["/admin/ai-settings", "/settings"], - }, - "settings.aiProvider.test": { - tags: ["settings"], - queryKeys: [["settings", "aiProviders"]], - paths: ["/admin/ai-settings", "/settings"], - }, - "settings.aiProvider.delete": { - tags: ["settings"], - queryKeys: [["settings", "aiProviders"]], - paths: ["/admin/ai-settings", "/settings"], - }, - "settings.avatar.update": { - tags: ["settings"], - queryKeys: [["settings", "avatar"]], - paths: ["/profile", "/settings"], - }, - "settings.avatar.remove": { - tags: ["settings"], - queryKeys: [["settings", "avatar"]], - paths: ["/profile", "/settings"], - }, - "settings.brand.save": { - tags: ["settings"], - queryKeys: [["settings", "brand"]], - paths: ["/admin/settings", "/(auth)/login", "/(auth)/register"], - }, - "settings.password.change": { - tags: ["settings"], - queryKeys: [["settings", "password"]], - paths: ["/settings"], - }, - "settings.security.setup2FA": { - tags: ["settings"], - queryKeys: [["settings", "security"]], - paths: ["/settings"], - }, - "settings.security.verify2FA": { - tags: ["settings"], - queryKeys: [["settings", "security"]], - paths: ["/settings"], - }, - "settings.security.disable2FA": { - tags: ["settings"], - queryKeys: [["settings", "security"]], - paths: ["/settings"], - }, - "settings.security.regenerateBackupCodes": { - tags: ["settings"], - queryKeys: [["settings", "security"]], - paths: ["/settings"], - }, - "settings.security.revokeSessions": { - tags: ["settings"], - queryKeys: [["settings", "security"]], - paths: ["/settings"], - }, - "settings.profile.update": { - tags: ["settings"], - queryKeys: [["settings", "profile"]], - paths: ["/settings"], - }, - "settings.notifications.updatePreferences": { - tags: ["settings"], - queryKeys: [["settings", "notificationPreferences"]], - paths: ["/settings"], - }, - "settings.systemSettings.save": { - tags: ["settings"], - queryKeys: [["settings", "system"]], - paths: ["/admin/settings"], - }, - - // ===== standards 模块 ===== - "standards.create": { - tags: ["standards"], - queryKeys: [["standards", "list"]], - paths: ["/admin/standards"], - }, - "standards.update": { - tags: ["standards"], - queryKeys: [["standards", "detail"], ["standards", "list"]], - paths: ["/admin/standards"], - }, - "standards.deactivate": { - tags: ["standards"], - queryKeys: [["standards", "list"]], - paths: ["/admin/standards"], - }, - "standards.linkPlan": { - tags: ["standards"], - queryKeys: [["standards", "planLinks"]], - paths: ["/teacher/lesson-plans"], - }, - "standards.unlinkPlan": { - tags: ["standards"], - queryKeys: [["standards", "planLinks"]], - paths: ["/teacher/lesson-plans"], - }, - - // ===== textbooks 模块 ===== - "textbooks.create": { - tags: ["textbooks"], - queryKeys: [["textbooks", "list"]], - paths: ["/teacher/textbooks"], - }, - "textbooks.update": { - tags: ["textbooks"], - queryKeys: [["textbooks", "detail"], ["textbooks", "list"]], - paths: ["/teacher/textbooks"], - }, - "textbooks.delete": { - tags: ["textbooks"], - queryKeys: [["textbooks", "list"]], - paths: ["/teacher/textbooks"], - }, - "textbooks.chapter.create": { - tags: ["textbooks"], - queryKeys: [["textbooks", "chapters"]], - paths: ["/teacher/textbooks"], - }, - "textbooks.chapter.update": { - tags: ["textbooks"], - queryKeys: [["textbooks", "chapters"]], - paths: ["/teacher/textbooks"], - }, - "textbooks.chapter.delete": { - tags: ["textbooks"], - queryKeys: [["textbooks", "chapters"]], - paths: ["/teacher/textbooks"], - }, - "textbooks.chapter.reorder": { - tags: ["textbooks"], - queryKeys: [["textbooks", "chapters"]], - paths: ["/teacher/textbooks"], - }, - "textbooks.knowledgePoint.create": { - tags: ["textbooks"], - queryKeys: [["textbooks", "knowledgePoints"]], - paths: ["/teacher/textbooks"], - }, - "textbooks.knowledgePoint.update": { - tags: ["textbooks"], - queryKeys: [["textbooks", "knowledgePoints"]], - paths: ["/teacher/textbooks"], - }, - "textbooks.knowledgePoint.delete": { - tags: ["textbooks"], - queryKeys: [["textbooks", "knowledgePoints"]], - paths: ["/teacher/textbooks"], - }, - "textbooks.prerequisite.create": { - tags: ["textbooks"], - queryKeys: [["textbooks", "prerequisites"]], - paths: ["/teacher/textbooks"], - }, - "textbooks.prerequisite.delete": { - tags: ["textbooks"], - queryKeys: [["textbooks", "prerequisites"]], - paths: ["/teacher/textbooks"], - }, - - // ===== users 模块 ===== - "users.update": { - tags: ["users"], - queryKeys: [["users", "detail"]], - paths: ["/profile", "/settings"], - }, - "users.import": { - tags: ["users"], - queryKeys: [["users", "list"]], - paths: ["/admin/users/import"], - }, - "users.delete": { - tags: ["users"], - queryKeys: [["users", "list"]], - paths: ["/admin/users"], - }, + ...CLASSES_INVALIDATION_RULES, + ...ACADEMIC_INVALIDATION_RULES, + ...GRADES_INVALIDATION_RULES, + ...LESSON_PREPARATION_INVALIDATION_RULES, + ...MESSAGING_INVALIDATION_RULES, + ...ADMIN_INVALIDATION_RULES, } as const satisfies Record export type InvalidationActionId = keyof typeof INVALIDATION_MAP