feat(portal-shell): 学生域全页面迁移与规范合规修复
- 学生域 32 页全量迁移(含作答/自动保存/提交/诊断) - 补齐 4 个 MSW mock 缺口,修 diagnostic case 名 - 修 4 处 Tailwind 任意值;新增共享组件与路由
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* portal-shell 适配:trackEvent/EventName 在客户端微前端不存在,
|
||||
* 服务端埋点由 BFF 负责;此处保留为 no-op 占位以保持类型契约。
|
||||
*/
|
||||
type EventName = string;
|
||||
const trackEvent = async (_event: {
|
||||
event: EventName;
|
||||
userId: string;
|
||||
targetType?: string;
|
||||
properties?: Record<string, unknown>;
|
||||
}): Promise<void> => {
|
||||
/* no-op: portal-shell client-side stub */
|
||||
};
|
||||
/**
|
||||
* portal-shell 适配:data-access/recordAiEvent 在客户端微前端不存在,
|
||||
* 由 BFF 维护事件存储;此处保留为 no-op 占位。
|
||||
*/
|
||||
const recordAiEvent = (_event: {
|
||||
userId: string;
|
||||
capability: string;
|
||||
success: boolean;
|
||||
durationMs: number;
|
||||
timestamp: number;
|
||||
errorMessage?: string;
|
||||
}): void => {
|
||||
/* no-op: portal-shell client-side stub */
|
||||
};
|
||||
|
||||
export type AiUsageEvent = {
|
||||
userId: string;
|
||||
capability:
|
||||
| "chat"
|
||||
| "similar_question"
|
||||
| "grading_assist"
|
||||
| "lesson_content"
|
||||
| "question_variant"
|
||||
| "weakness_analysis"
|
||||
| "child_summary"
|
||||
| "study_path"
|
||||
| "explain_error";
|
||||
providerId?: string;
|
||||
model?: string;
|
||||
success: boolean;
|
||||
durationMs: number;
|
||||
tokenUsage?: number;
|
||||
errorMessage?: string;
|
||||
};
|
||||
|
||||
const AI_EVENT_MAP: Record<AiUsageEvent["capability"], EventName> = {
|
||||
chat: "ai.chat",
|
||||
similar_question: "ai.similar_question",
|
||||
grading_assist: "ai.grading_assist",
|
||||
lesson_content: "ai.lesson_content",
|
||||
question_variant: "ai.question_variant",
|
||||
weakness_analysis: "ai.weakness_analysis",
|
||||
child_summary: "ai.child_summary",
|
||||
study_path: "ai.study_path",
|
||||
explain_error: "ai.explain_error",
|
||||
};
|
||||
|
||||
/**
|
||||
* AI 使用埋点
|
||||
*
|
||||
* 记录每次 AI 调用的元数据,用于监控、成本分析与异常排查。
|
||||
* 同时写入 data-access 层的内存事件存储(供管理员仪表盘聚合查询)。
|
||||
* 非阻塞,失败不影响主流程。
|
||||
*/
|
||||
export const trackAiUsage = (event: AiUsageEvent): void => {
|
||||
const eventName = AI_EVENT_MAP[event.capability];
|
||||
|
||||
// 写入 data-access 层(供 getAiUsageStats 聚合)
|
||||
recordAiEvent({
|
||||
userId: event.userId,
|
||||
capability: event.capability,
|
||||
success: event.success,
|
||||
durationMs: event.durationMs,
|
||||
timestamp: Date.now(),
|
||||
errorMessage: event.errorMessage,
|
||||
});
|
||||
|
||||
// 写入全局 trackEvent(供外部监控系统)
|
||||
void trackEvent({
|
||||
event: eventName,
|
||||
userId: event.userId,
|
||||
targetType: event.capability,
|
||||
properties: {
|
||||
providerId: event.providerId,
|
||||
model: event.model,
|
||||
success: event.success,
|
||||
durationMs: event.durationMs,
|
||||
tokenUsage: event.tokenUsage,
|
||||
errorMessage: event.errorMessage,
|
||||
},
|
||||
}).catch(() => {
|
||||
// 静默失败:埋点不应影响业务流程
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 测量 AI 调用耗时并自动埋点
|
||||
*/
|
||||
export const withAiTracking = async <T>(
|
||||
userId: string,
|
||||
capability: AiUsageEvent["capability"],
|
||||
providerId: string | undefined,
|
||||
fn: () => Promise<{ result: T; model?: string; tokenUsage?: number }>,
|
||||
): Promise<T> => {
|
||||
const start = Date.now();
|
||||
try {
|
||||
const { result, model, tokenUsage } = await fn();
|
||||
trackAiUsage({
|
||||
userId,
|
||||
capability,
|
||||
providerId,
|
||||
model,
|
||||
success: true,
|
||||
durationMs: Date.now() - start,
|
||||
tokenUsage,
|
||||
});
|
||||
return result;
|
||||
} catch (error) {
|
||||
trackAiUsage({
|
||||
userId,
|
||||
capability,
|
||||
providerId,
|
||||
success: false,
|
||||
durationMs: Date.now() - start,
|
||||
errorMessage: error instanceof Error ? error.message : String(error),
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user