diff --git a/src/app/(dashboard)/teacher/diagnostic/page.tsx b/src/app/(dashboard)/teacher/diagnostic/page.tsx index eb52628..8babc1d 100644 --- a/src/app/(dashboard)/teacher/diagnostic/page.tsx +++ b/src/app/(dashboard)/teacher/diagnostic/page.tsx @@ -8,12 +8,9 @@ import { ReportList } from "@/modules/diagnostic/components/report-list" import { DiagnosticServiceProvider, } from "@/modules/diagnostic/services/diagnostic-service-context" -import { defaultDiagnosticService } from "@/modules/diagnostic/services/default-diagnostic-service" -import { createMonitoredDiagnosticService } from "@/modules/diagnostic/services/monitored-diagnostic-service" import { DiagnosticMonitorProvider, } from "@/modules/diagnostic/services/diagnostic-monitor-context" -import { noopDiagnosticMonitor } from "@/modules/diagnostic/services/diagnostic-monitor" import type { DiagnosticReportType, DiagnosticReportStatus } from "@/modules/diagnostic/types" export const dynamic = "force-dynamic" @@ -64,11 +61,8 @@ export default async function TeacherDiagnosticPage({ // v2-P2-2: 移除客户端 filter,DataScope 过滤已在 data-access 层完成 // class_members scope 的学生已通过 filters.studentId 在 data-access 层过滤 - // v2-P2-7: 包装默认服务以添加监控埋点(默认 no-op,生产环境可注入真实实现) - const monitoredService = createMonitoredDiagnosticService( - defaultDiagnosticService, - noopDiagnosticMonitor, - ) + // v2-P2-7: service 组装移至 DiagnosticServiceProvider(客户端), + // 避免 server component 创建含函数的对象传给 client component。 return (
@@ -78,8 +72,8 @@ export default async function TeacherDiagnosticPage({ {t("title.teacherReportListDesc")}

- - + + diff --git a/src/modules/diagnostic/services/default-diagnostic-service.ts b/src/modules/diagnostic/services/default-diagnostic-service.ts index 9496160..7ef8f0a 100644 --- a/src/modules/diagnostic/services/default-diagnostic-service.ts +++ b/src/modules/diagnostic/services/default-diagnostic-service.ts @@ -1,5 +1,3 @@ -"use client" - import type { ActionState } from "@/shared/types/action-state" import { diff --git a/src/modules/diagnostic/services/diagnostic-monitor-context.tsx b/src/modules/diagnostic/services/diagnostic-monitor-context.tsx index 3e91e08..2d3e761 100644 --- a/src/modules/diagnostic/services/diagnostic-monitor-context.tsx +++ b/src/modules/diagnostic/services/diagnostic-monitor-context.tsx @@ -10,13 +10,22 @@ * 默认值为 noopDiagnosticMonitor(不发送任何事件), * 确保未注入 Provider 时业务流程不受影响。 * + * 注意:monitor prop 为可选,Server Component 不应传递含函数的对象。 + * 若需注入真实监控实现,请在 Client Component 内部使用。 + * * 用法: * ```tsx - * - * + * // Server Component(不传 monitor) + * + * * * * + * + * // Client Component(可注入真实实现) + * + * ... + * * ``` */ @@ -32,8 +41,12 @@ const DiagnosticMonitorContext = createContext( ) interface DiagnosticMonitorProviderProps { - /** 监控实现(默认使用 noop,生产环境注入真实实现) */ - monitor: DiagnosticMonitor + /** + * 监控实现(可选)。 + * 未传时使用 noopDiagnosticMonitor。 + * Server Component 不要传此 prop(含函数无法跨边界传递)。 + */ + monitor?: DiagnosticMonitor children: ReactNode } @@ -41,8 +54,9 @@ export function DiagnosticMonitorProvider({ monitor, children, }: DiagnosticMonitorProviderProps): ReactNode { + const value = monitor ?? noopDiagnosticMonitor return ( - + {children} ) diff --git a/src/modules/diagnostic/services/diagnostic-service-context.tsx b/src/modules/diagnostic/services/diagnostic-service-context.tsx index 6af750d..f9720d4 100644 --- a/src/modules/diagnostic/services/diagnostic-service-context.tsx +++ b/src/modules/diagnostic/services/diagnostic-service-context.tsx @@ -1,8 +1,12 @@ "use client" -import { createContext, useContext, type ReactNode } from "react" +import { createContext, useContext, useMemo, type ReactNode } from "react" import type { DiagnosticService } from "./diagnostic-service" +import { defaultDiagnosticService } from "./default-diagnostic-service" +import { createMonitoredDiagnosticService } from "./monitored-diagnostic-service" +import { noopDiagnosticMonitor } from "./diagnostic-monitor" +import { useDiagnosticMonitor } from "./diagnostic-monitor-context" /** * v2-P1-4: 诊断模块服务 Context。 @@ -10,13 +14,14 @@ import type { DiagnosticService } from "./diagnostic-service" * 组件通过 useDiagnosticService() 获取服务实现, * 而非直接 import actions,实现依赖反转。 * - * 默认由 DefaultDiagnosticServiceProvider 注入真实实现(调用 Server Actions); + * 默认由 DiagnosticServiceProvider 在客户端组装默认实现(调用 Server Actions); * 测试时可注入 mock 实现以隔离组件测试。 */ const DiagnosticServiceContext = createContext(null) interface DiagnosticServiceProviderProps { - service: DiagnosticService + /** 可选自定义服务实现,未传则使用 defaultDiagnosticService + 监控包装 */ + service?: DiagnosticService children: ReactNode } @@ -24,8 +29,18 @@ export function DiagnosticServiceProvider({ service, children, }: DiagnosticServiceProviderProps): ReactNode { + const monitor = useDiagnosticMonitor() + const value = useMemo(() => { + if (service) return service + // 客户端组装:默认 service + 监控埋点包装 + return createMonitoredDiagnosticService( + defaultDiagnosticService, + monitor ?? noopDiagnosticMonitor, + ) + }, [service, monitor]) + return ( - + {children} ) diff --git a/src/modules/diagnostic/services/monitored-diagnostic-service.ts b/src/modules/diagnostic/services/monitored-diagnostic-service.ts index 381b94e..a036457 100644 --- a/src/modules/diagnostic/services/monitored-diagnostic-service.ts +++ b/src/modules/diagnostic/services/monitored-diagnostic-service.ts @@ -1,5 +1,3 @@ -"use client" - /** * v2-P2-7: 带监控埋点的诊断服务工厂。 *