报告生成流程优化

统一 generate-theory-report 云函数、各报告 slides 结构规范化、积分扣减异步化

by 于成季 ·

报告生成流程优化

记录日期: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_idevent.kindevent.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)逻辑分叉,维护成本高且易不一致。

修复useTheoryReportGenerationstartGeneration 改用 checkTokenUsage;去掉对 fetchAiPointsBalance 的依赖;文案直接用 quota.message

2.5 Playwright E2E 验收用例

新增playwright/test-self-theory-report-e2e-20260404.js

链路:登录 → 知(浮动栏) → 自我 → 打开 Ikigai 卡 → 立即生成 → 等待「正在生成中」出现并最终消失

分支检测:

  • 分支 A(积分充足):loading 出现 → 等待完成 → 报告出现在列表
  • 分支 B(free/积分不足):loading 不出现(同步被拦截)→ 弹窗或 UI 无新报告

helpers.jsnavigateToTab 修复: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 生成前 checkTokenUsageallowed: true 则放行);成功后异步 record-token-usageaction: 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 同口径额度再立项。

← 所有文章