报告生成流程优化
统一 generate-theory-report 云函数、各报告 slides 结构规范化、积分扣减异步化
报告生成流程优化
记录日期:2026-04-04
1. 问题背景
知页「自我」里理论报告原链路经独立详情页、生成过程不可观测,且额度校验在云函数与订阅语义上易混淆。目标:入口更短、生成步骤对用户可见、积分预检简单明确;权威细节仍以 docs/流程文档/报告生成流程.md 为准。
2. 已完成的优化(按日期)
2026-04-04 本次迭代
2.1 免费账号 PLAN_LIMITS.free = 0 与文档不符
根因:cloudbase/functions/ 下 4 个旧版 aiQuota 副本(shared/、record-token-usage/、check-subscription/、apple-iap-verify/)中 PLAN_LIMITS.free = 0,与产品文档「免费版每日 10K token」不符。其他 generate-*-report/ 目录已是 free: 10000,仅这 4 处遗漏。
修复:将 4 处 free: 0 改为 free: 10000,重新部署相关云函数。
验证:record-token-usage 日志显示 plan: free, remaining: 10000,免费账号生成报告通过。
2.2 generate-theory-report user_id 解析失败
根因:CloudBase JS SDK invoke 将参数展开到 event 顶层(event.user_id、event.kind、event.prompt),event.data 仅含 { plans, insights, wishes };而云函数旧解析取 event.data 作为 body,导致 user_id 始终为 null,所有生成请求被拦截返回 Missing or invalid user_id。
修复:cloudbase/functions/generate-theory-report/index.js 中 body 解析改为 event 本身即 body(Event SDK 模式),event.data 为其中的 data 字段。
2.3 checkTokenUsage / recordTokenUsage 扁平响应兼容
根因:CloudBase JS SDK 在某些环境下对 invoke 返回 result.data 可能只有内层 quota 对象({ allowed, remaining, used_today })而没有 success 包裹。checkTokenUsage 原逻辑只识别 result.success && result.data 分支,扁平体落到默认返回 allowed: false,导致免费账号(明明有 10K 额度)被误判为「积分不足」。
修复:在 zhiyuxing-app/src/lib/token-usage.ts 增加 parseFlatCheckPayload / parseFlatRecordPayload,在判断 success 之前优先识别顶层 { allowed: boolean, remaining: number } 形状;数值兼容字符串与 camelCase。
2.4 useTheoryReportGeneration 积分门限改用 checkTokenUsage
根因:startGeneration 原来依赖 fetchAiPointsBalance(走 invoke 的裸 data 解析),与神秘人 Agent 等其他入口用的 checkTokenUsage(走 invokeFunctionSafe)逻辑分叉,维护成本高且易不一致。
修复:useTheoryReportGeneration 的 startGeneration 改用 checkTokenUsage;去掉对 fetchAiPointsBalance 的依赖;文案直接用 quota.message。
2.5 Playwright E2E 验收用例
新增:playwright/test-self-theory-report-e2e-20260404.js
链路:登录 → 知(浮动栏) → 自我 → 打开 Ikigai 卡 → 立即生成 → 等待「正在生成中」出现并最终消失
分支检测:
- 分支 A(积分充足):loading 出现 → 等待完成 → 报告出现在列表
- 分支 B(free/积分不足):loading 不出现(同步被拦截)→ 弹窗或 UI 无新报告
helpers.js 的 navigateToTab 修复:Web 上顶栏 <a> 与浮动栏 <div> 均含 role="tab",对 zhi tab 取 .last() 跳过不可见链接。
运行:
cd playwright && node test-self-theory-report-e2e-20260404.js
2026-04-03 首版落地
2.6 入口冗长、生成过程黑盒
方案:去掉 /zhi/theory/[id];卡片 → TheoryIntroModal →「立即生成」;同屏「我的报告」子 Tab + 列表内 job 卡片(五步:校验积分 → 拉计划 → 拉感悟与愿望 → 提交生成 → 等待落库);失败可重试。
已落地:TheoryIntroModal + useTheoryReportGeneration(五步进度)+ 约 3s 轮询 reports、优先 report_type 判定新行。
2.7 报告链路额度与扣减职责不清
方案(短期):generate-theory-report 只做 LLM + 写 reports,云侧不校验不扣减;App 生成前 checkTokenUsage(allowed: true 则放行);成功后异步 record-token-usage(action: record,reason: theory_report)更新积分。
神秘人 Agent:经工具调同一云函数时,当前不做积分预检、不做扣减、不做上限限制。
已落地:无客户端预估 token 表;预检仅为 allowed: true;云端统一理论函数不嵌配额逻辑;App 异步记账;Agent 路径不参与积分校验(本轮不做)。
3. 仍须关注的风险(非本轮闭项)
- 轮询最长 5 min 后 job prune,云若更晚落库会出现「进行中消失、报告迟来」
baselineReportIds+report_type判定新行需与云写入type严格一致- 余额展示可在
invalidateAiPointsDisplay后刷新;异步record失败不撤销已落库的reports - 神秘人若需与 App 同口径额度,再单独立项
4. 关键文件变更(2026-04-04)
| 文件 | 变更 |
|---|---|
cloudbase/functions/shared/aiQuota.js |
free: 0 → 10000 |
cloudbase/functions/record-token-usage/aiQuota.js |
free: 0 → 10000 |
cloudbase/functions/check-subscription/aiQuota.js |
free: 0 → 10000 |
cloudbase/functions/apple-iap-verify/aiQuota.js |
free: 0 → 10000 |
cloudbase/functions/generate-theory-report/index.js |
body 解析:Event SDK 模式下 event 本身即 body |
zhiyuxing-app/src/lib/token-usage.ts |
parseFlatCheckPayload / parseFlatRecordPayload 扁平响应兼容 |
zhiyuxing-app/src/hooks/useTheoryReportGeneration.ts |
startGeneration 改用 checkTokenUsage |
playwright/helpers.js |
navigateToTab zhi tab 改为 .last() |
playwright/test-self-theory-report-e2e-20260404.js |
新增 E2E 用例(分支 A/B 检测) |
5. 小结
本次完成:免费版 10K token 修复(PLAN_LIMITS.free)、generate-theory-report user_id 解析修复、checkTokenUsage/recordTokenUsage 扁平兼容、生成前门限与 Agent 路径对齐、playwright E2E 端到端验收(含分支 A: 报告生成落库 / 分支 B: 积分不足正确拦截)。
后续:轮询/prune 体验优化;云侧 timeout 与 type 命名一致性;神秘人若需与 App 同口径额度再立项。