feat(diagnostic): refactor services with monitor and context providers

- Update default-diagnostic-service.ts

- Update diagnostic-monitor-context.tsx

- Update diagnostic-service-context.tsx

- Update monitored-diagnostic-service.ts

- Update teacher diagnostic page
This commit is contained in:
SpecialX
2026-07-04 10:22:48 +08:00
parent eee0145274
commit b63d116b6c
5 changed files with 42 additions and 23 deletions

View File

@@ -8,12 +8,9 @@ import { ReportList } from "@/modules/diagnostic/components/report-list"
import { import {
DiagnosticServiceProvider, DiagnosticServiceProvider,
} from "@/modules/diagnostic/services/diagnostic-service-context" } 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 { import {
DiagnosticMonitorProvider, DiagnosticMonitorProvider,
} from "@/modules/diagnostic/services/diagnostic-monitor-context" } from "@/modules/diagnostic/services/diagnostic-monitor-context"
import { noopDiagnosticMonitor } from "@/modules/diagnostic/services/diagnostic-monitor"
import type { DiagnosticReportType, DiagnosticReportStatus } from "@/modules/diagnostic/types" import type { DiagnosticReportType, DiagnosticReportStatus } from "@/modules/diagnostic/types"
export const dynamic = "force-dynamic" export const dynamic = "force-dynamic"
@@ -64,11 +61,8 @@ export default async function TeacherDiagnosticPage({
// v2-P2-2: 移除客户端 filterDataScope 过滤已在 data-access 层完成 // v2-P2-2: 移除客户端 filterDataScope 过滤已在 data-access 层完成
// class_members scope 的学生已通过 filters.studentId 在 data-access 层过滤 // class_members scope 的学生已通过 filters.studentId 在 data-access 层过滤
// v2-P2-7: 包装默认服务以添加监控埋点(默认 no-op生产环境可注入真实实现 // v2-P2-7: service 组装移至 DiagnosticServiceProvider客户端
const monitoredService = createMonitoredDiagnosticService( // 避免 server component 创建含函数的对象传给 client component。
defaultDiagnosticService,
noopDiagnosticMonitor,
)
return ( return (
<div className="h-full flex-1 flex-col space-y-8 p-8 md:flex"> <div className="h-full flex-1 flex-col space-y-8 p-8 md:flex">
@@ -78,8 +72,8 @@ export default async function TeacherDiagnosticPage({
{t("title.teacherReportListDesc")} {t("title.teacherReportListDesc")}
</p> </p>
</div> </div>
<DiagnosticMonitorProvider monitor={noopDiagnosticMonitor}> <DiagnosticMonitorProvider>
<DiagnosticServiceProvider service={monitoredService}> <DiagnosticServiceProvider>
<ReportList reports={reports.reports} /> <ReportList reports={reports.reports} />
</DiagnosticServiceProvider> </DiagnosticServiceProvider>
</DiagnosticMonitorProvider> </DiagnosticMonitorProvider>

View File

@@ -1,5 +1,3 @@
"use client"
import type { ActionState } from "@/shared/types/action-state" import type { ActionState } from "@/shared/types/action-state"
import { import {

View File

@@ -10,13 +10,22 @@
* 默认值为 noopDiagnosticMonitor不发送任何事件 * 默认值为 noopDiagnosticMonitor不发送任何事件
* 确保未注入 Provider 时业务流程不受影响。 * 确保未注入 Provider 时业务流程不受影响。
* *
* 注意monitor prop 为可选Server Component 不应传递含函数的对象。
* 若需注入真实监控实现,请在 Client Component 内部使用。
*
* 用法: * 用法:
* ```tsx * ```tsx
* <DiagnosticMonitorProvider monitor={postHogMonitor}> * // Server Component不传 monitor
* <DiagnosticServiceProvider service={defaultDiagnosticService}> * <DiagnosticMonitorProvider>
* <DiagnosticServiceProvider>
* <ReportList /> * <ReportList />
* </DiagnosticServiceProvider> * </DiagnosticServiceProvider>
* </DiagnosticMonitorProvider> * </DiagnosticMonitorProvider>
*
* // Client Component可注入真实实现
* <DiagnosticMonitorProvider monitor={postHogMonitor}>
* ...
* </DiagnosticMonitorProvider>
* ``` * ```
*/ */
@@ -32,8 +41,12 @@ const DiagnosticMonitorContext = createContext<DiagnosticMonitor>(
) )
interface DiagnosticMonitorProviderProps { interface DiagnosticMonitorProviderProps {
/** 监控实现(默认使用 noop生产环境注入真实实现 */ /**
monitor: DiagnosticMonitor * 监控实现(可选)。
* 未传时使用 noopDiagnosticMonitor。
* Server Component 不要传此 prop含函数无法跨边界传递
*/
monitor?: DiagnosticMonitor
children: ReactNode children: ReactNode
} }
@@ -41,8 +54,9 @@ export function DiagnosticMonitorProvider({
monitor, monitor,
children, children,
}: DiagnosticMonitorProviderProps): ReactNode { }: DiagnosticMonitorProviderProps): ReactNode {
const value = monitor ?? noopDiagnosticMonitor
return ( return (
<DiagnosticMonitorContext.Provider value={monitor}> <DiagnosticMonitorContext.Provider value={value}>
{children} {children}
</DiagnosticMonitorContext.Provider> </DiagnosticMonitorContext.Provider>
) )

View File

@@ -1,8 +1,12 @@
"use client" "use client"
import { createContext, useContext, type ReactNode } from "react" import { createContext, useContext, useMemo, type ReactNode } from "react"
import type { DiagnosticService } from "./diagnostic-service" 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。 * v2-P1-4: 诊断模块服务 Context。
@@ -10,13 +14,14 @@ import type { DiagnosticService } from "./diagnostic-service"
* 组件通过 useDiagnosticService() 获取服务实现, * 组件通过 useDiagnosticService() 获取服务实现,
* 而非直接 import actions实现依赖反转。 * 而非直接 import actions实现依赖反转。
* *
* 默认由 DefaultDiagnosticServiceProvider 注入真实实现(调用 Server Actions * 默认由 DiagnosticServiceProvider 在客户端组装默认实现(调用 Server Actions
* 测试时可注入 mock 实现以隔离组件测试。 * 测试时可注入 mock 实现以隔离组件测试。
*/ */
const DiagnosticServiceContext = createContext<DiagnosticService | null>(null) const DiagnosticServiceContext = createContext<DiagnosticService | null>(null)
interface DiagnosticServiceProviderProps { interface DiagnosticServiceProviderProps {
service: DiagnosticService /** 可选自定义服务实现,未传则使用 defaultDiagnosticService + 监控包装 */
service?: DiagnosticService
children: ReactNode children: ReactNode
} }
@@ -24,8 +29,18 @@ export function DiagnosticServiceProvider({
service, service,
children, children,
}: DiagnosticServiceProviderProps): ReactNode { }: DiagnosticServiceProviderProps): ReactNode {
const monitor = useDiagnosticMonitor()
const value = useMemo<DiagnosticService>(() => {
if (service) return service
// 客户端组装:默认 service + 监控埋点包装
return createMonitoredDiagnosticService(
defaultDiagnosticService,
monitor ?? noopDiagnosticMonitor,
)
}, [service, monitor])
return ( return (
<DiagnosticServiceContext.Provider value={service}> <DiagnosticServiceContext.Provider value={value}>
{children} {children}
</DiagnosticServiceContext.Provider> </DiagnosticServiceContext.Provider>
) )

View File

@@ -1,5 +1,3 @@
"use client"
/** /**
* v2-P2-7: 带监控埋点的诊断服务工厂。 * v2-P2-7: 带监控埋点的诊断服务工厂。
* *