zmlkit
로그인

업데이트

릴리스별 변경 사항 — changelog에서 바로.

  1. 按 App 管理多语言协议与支持邮箱

    • 后台:App 列表新增「协议与支持」,支持 12 语言独立编辑、模板草稿生成、预览、保存发布、链接复制与撤下;切换语言保留草稿,关闭前提示未保存修改。
    • 公开链接:/<slug>/privacy、/<slug>/terms 选择默认语言并回退英文;/<slug>/privacy/en 等语言后缀准确显示已发布版本,支持 embed,缺失/禁用返回 404。
    • 数据与权限:共享 Zod 契约、App/类型/语言唯一键、平台 App 管理权限及审计;PostgreSQL/SQLite 迁移与 MySQL schema,公开正文按文本安全渲染。
    • 生成方式:复用内置 12 语言模板,替换运营主体、App 名和邮箱;生成后需编辑并显式发布,不自动翻译自定义正文。

  2. 回流通用后台入口并补齐 RevenueCat 配置

    • 后台:按当前 App 权限选择首页,兼容旧 App 管理页入口;保留各页面独立鉴权,支持 App 运营、客服与自定义权限角色。
    • RevenueCat:移动端 SDK 包装与 Rootory 相同,无需覆盖;补齐两组 App 的服务端 API Key 示例,兼容同 App 的旧 READ_KEY 别名,保持 API_KEY 优先和 App 隔离。
    • 文档与验证:增加密钥用途、Webhook、商店映射与回流边界说明;增加角色入口及配置隔离回归测试。Rootory Plus 规则、实际密钥及专用订阅表不回流。

  3. 提取 Rootory 的通用账号能力与 Workers 适配

    • Apple:修复 Workers 的 PEM 签名兼容;Web/原生登录保存按 App/用户/Apple subject 绑定的加密授权;注销事务同时记录可重试撤销任务,旧客户端兼容,保留 MFA 和推荐码。
    • 头像:流式限制上传请求,CAS 发布新头像引用,提交后清理旧文件;上传/更新/删除失败由持久化任务补偿,支持多 App 隔离和并发领取。
    • 邮件:验证邮箱、密码重置、新设备提醒支持 12 种语言;复用账号 locale,签名 OAuth state 传语言,移动端携带语言,邮件落地表单同步语言。
    • 邮件落地页:复用同一验证请求的结果,避免 Strict Mode 或语言字典更新重复消费一次性令牌;浏览器验证语言传递、验证成功、注销和会话失效,E2E 使用独立构建目录并禁发真实邮件。
    • Cloudflare:独立 webpack 构建、依赖 tracing、静态 Argon2 WASM、请求级真实 pg.Pool 与 Hyperdrive;阻止失配的 WASM 补丁继续部署,普通 Node 构建保持默认流程。
    • 迁移与运维:PostgreSQL 0024 / SQLite 0019 新增账号清理任务表;CRON_SECRET 保护的清理端点、可配置任务保留天数与操作文档。
    • 验证:覆盖真实 workerd 签名/验签、跨运行时加密与旧 PHC、SQLite/PostgreSQL 的注销回滚/头像冲突/清理重试,执行模板构建、类型和契约检查。

  4. 精简模板官网,突出一套模板与三端开发

    • 内容:首屏统一为“一套模板,三端开发”,突出 50%+ 开发效率目标与比较范围;英文默认,支持中文切换,元数据同步更新。
    • 布局:居中标题、三端并列入口、六项内置能力、获取模板与三条 FAQ;移除大图、流程探索、源码树和复杂演示控制,详细内容转向文档入口。
    • 交互:保留三端设备预览、键盘切换、轻量过渡、授权弹窗与咨询复制/下载;维持购买配置、备案和旧首页切换。
    • 验证:更新浏览器回归,覆盖中英文、四种屏宽、预览不被裁切、文档入口、授权流程与减少动画;沿用模板构建、类型与契约检查。

  5. 新增 zmlkit 模板销售介绍官网

    • 视觉:暖白、炭黑与橙色的响应式官网,原创玻璃模块主视觉、本地优化 WebP、悬浮细节和滚动入场,尊重减少动画偏好。
    • 交互:三端预览、品牌色与项目名编辑、AI 媒体类型、计费流程与多 App 架构探索;源码目录展开、命令复制、FAQ、授权弹窗和咨询清单复制/下载。
    • 销售:服务端验证公开购买网址和销售邮箱;未配置时提供授权了解流程,不编造售价、评价或付款结果。源码售价与示例 SaaS 套餐分离,介绍模式的 /pricing 跳转授权说明,SEO 不发布示例套餐报价。
    • 边界:销售页代码隔离于 apps/zmlkit-site;保留文档、控制台与原有 JSON block 首页(TEMPLATE_MARKETING_MODE=app 可恢复),沿用已配置备案信息。未修改认证、计费、数据库或依赖。
    • 验证:新增公开配置边界测试和浏览器交互/响应式回归,覆盖中英文、复制、下载、键盘弹窗、四种屏宽及减少动画。

  6. 提取 rootory 分支的公共路由修复

    • 路由边界:将共享发票 DTO 映射移出 Route Handler;发票抬头映射和微信小程序 provider 常量改为模块私有,避免路由导出辅助成员及跨路由导入。
    • 组织邀请:邀请接受页迁入公共 console 路由组,复用主题、国际化与根布局;保留 /orgs/accept 地址和未登录时的登录提示。
    • 范围:选择性提取 rootory 21bbbe6 的 8 个源码文件,保留 main 的计费与依赖安全修复;业务功能及需要通用化的部署、Apple、RevenueCat 能力另行处理。

  7. 修复 main 的依赖安全告警

    • 运行与工具依赖:Next.js/eslint-config-next 升至 16.3.8 修复分支,更新 sharp/mysql2,以及 Expo、XML、URI、模式扩展、Wrangler/OpenNext 与测试工具链中的受影响依赖;Expo 保持 SDK 56 补丁级对齐。
    • 无上游修复版:braces 增加 parser/AST 深度上限,node-forge 严格验证嵌套 DigestAlgorithm;冻结安装应用补丁,CI 先执行漏洞回归,再审计。仅精确豁免这两个已本地修复的 GHSA,原始版本审计仍显示 2 high。
    • 兼容性:验证 image-size 2 的 Metro 解析、xcode 的 uuid 11 v4、query-string 的 ESM decoder 桥接及 Drizzle/esbuild transform;撤掉历史 image-size CVE 例外。
    • 记录:新增完整基线公告与修复报告 docs/dependency-audit-2026-10-11.md,并更新安全清单的当前依据。

  8. 回收 photorevive 分支的模板公共改进

    • Waffo Pancake:升级 SDK 0.25.0,环境/Store/商品 env 覆盖,首次支付与续费分流,状态事件不发积分,退款按支付周期定位;保留丢 webhook 查单,修正税费与金额校验并脱敏错误。
    • 数据库回归:移植通用 PostgreSQL 计费回放并纳入 CI,修复过期事件微秒时间认领;补充订阅、退款、Kie 最终确认与事务回滚测试。
    • 异步 AI:Kie 任务签名回调、认证复查、失败晚到成功恢复及延迟最终退款;保留 main 的持久派发、积分事务、限频退避与 SSRF 防护。
    • 通用扩展:来源图/驱动音频规格、按模型成本与安全音频时长计费(使用已修复漏洞的 music-metadata 11.16.0);补齐 progress/radio-group/textarea 基元、可配置主色前景、头像失败回退、部署排除规则与 TSX 测试支持。
    • 范围:以 photorevive fc3c29c 为审查来源选择性移植,未带入产品页面、照片业务、品牌、商户硬编码或依赖降级;默认模型目录保持原有产品范围。

  9. 首页集成官网链接与 FAQ 扩充

    • 集成链接:Works with 调整为 Waffo Pancake、Creem、Stripe、RevenueCat、OpenRouter、

    Cloudflare R2,并全部链接到已验证的官方网站;外链在新标签页安全打开。

    • 内容模型:logos block 向后兼容原字符串,同时支持 {label, href} 内容对象,链接具备

    hover 与键盘焦点状态;共享渲染器不硬编码品牌 URL。

    • FAQ 扩充:新增 6 条全端、支付、品牌化、失败恢复、扩展和部署问答,总数增至 8 条;

    提供英文和简体中文,其余语言缺失词条自动回退英文。


  10. 首页展示 Waffo Pancake 集成

    • 营销内容:在首页 logos block 首位加入 Waffo Pancake;沿用 JSON 内容驱动与英文结构

    回退,所有 12 种语言页面同步生效,未改动共享营销渲染器。


  11. Waffo 丢失回调对账补单

    • 根因确认:本地订单与 Waffo succeeded payment 正确关联,但测试 Store 未配置 webhook,

    因而没有生成 order.completed 投递、支付事件或积分流水。

    • 主动对账:为 Waffo provider 实现 queryOrder,按服务端订单号查询 GraphQL payments;

    丢失 webhook 时由既有 billing reconcile 幂等补为 paid,并执行相同金额校验与积分发放。

    • 回归与运维:新增 succeeded/unpaid 查询归一化测试,并明确本地测试也必须配置可达的

    HTTPS webhook;主动对账只作为回调丢失兜底。


  12. Waffo Pancake 设为默认支付方式

    • 默认优先级:将 Waffo 调整为支付注册表中首个 Web provider;Checkout 未显式指定

    provider 时优先使用 Waffo,Billing 页面同步默认选中 Waffo。

    • 安全回退:Waffo 未配置或被 App 支付开关禁用时,继续按 Stripe、Creem、PayPal、

    WeChat Pay、Alipay 的顺序选择下一家可用支付商,不影响显式 provider 调用。

    • 回归与文档:增加 provider 优先级测试,并同步 API 与 Waffo 接入说明。

  13. Waffo Pancake SDK 测试支付全链路接入

    • 官方 SDK 与最小密钥面:安装 @waffo/pancake-ts@0.19.0,仅以服务端

    WAFFO_MERCHANT_ID / WAFFO_PRIVATE_KEY 初始化;API Key 自身区分 test/prod,私钥支持 raw base64、PEM 与 \\n 形式,未新增 webhook secret 或客户端变量。

    • 统一支付架构接入:在 provider registry、共享 Zod DTO、管理端 feature toggle 与

    ProviderRefs 注册 waffo;复用既有 /api/billing/checkout 与 /api/billing/webhooks/waffo/:appSlug,保留 session 鉴权、App 隔离、限流、原始 body 验签、 事件幂等、金额/币种核对和统一积分发放。

    • 测试商品与事件归一化:在唯一 Store Rekindle 中创建并映射 app_a 的 6 个 test 产品;

    Checkout 使用服务端 priceSnapshot 与 orderNo/planCode/appId/userId metadata;覆盖一次性 支付、订阅激活/续费/状态/取消和退款事件,SDK RSA-SHA256 验签并保留防重放时间窗。

    • 真实闭环验证:通过项目端点创建 USD 4.99 Starter Pack,使用官方测试卡完成支付;公网

    webhook 返回 200,签名 payload 为 order.completed / mode=test,本地订单变为 paid、实收 499 cents,并且仅产生 1 条 500-credit grant 流水。临时 tunnel webhook 验收后已移除。

    • 文档与密钥卫生:新增 Waffo 配置、产品、回调、测试与生产切换手册,同步 API/部署/env

    示例;浏览器下载到仓库目录的重复私钥副本已移至 macOS 废纸篓,Git 仅保留无密钥代码。


  14. 依赖安全公告增量修复与无补丁风险窄域隔离

    • 可修复公告清零:更新 workspace overrides 与锁文件,将 fast-uri、两条

    brace-expansion 版本线、js-yaml、nanoid 提升至公告修复版本;原始 pnpm audit --prod 的 9 high / 1 moderate 降至 2 high / 1 moderate。

    • 无补丁风险显式接受:剩余两个 high 均来自 Expo/Metro 构建链的 image-size 无限循环

    公告,上游暂无修复版本且当前只解析版本库内受信任图片;CI 仅精确忽略 CVE-2025-71329/CVE-2025-71330,不使用会掩盖未来公告的宽泛忽略,并在安全清单记录移除条件。

    • 门禁保持:除上述两个已评估 CVE 外,pnpm audit --prod --audit-level=high 继续阻断

    high/critical;既有 Expo/xcode uuid moderate 保持跟踪。

    • Expo 补丁对齐:同步 Expo SDK 56 当前要求的 7 个兼容补丁版本,expo install --check

    恢复通过;全仓 511 passed(另 2 条 PostgreSQL 专用测试按设计 skip)、Mobile/API/Web typecheck、Web lint、Prettier、冻结安装及生产构建均通过。


  15. Codex Security 扫描修复:SSRF、深链、令牌与支付边界

    • Codex Security:通过 Codex 插件目录安装并启用 OpenAI Curated Codex Security 0.1.18;扫描产物加入忽略规则,避免本地安全报告误入版本库。
    • 出站 SSRF 防护:AI 结果 HEAD/下载复用 DNS pin 安全请求;每个重定向重新校验协议、主机和全部解析地址,拒绝内网/元数据/DNS rebinding,并在流式读取期间执行 50MB 上限。
    • 认证回跳与凭证作用域:OAuth/邮件自定义 scheme 改为 APP_<SLUG>_AUTH_REDIRECT_SCHEMES 精确白名单,Web 回跳精确同源且 callback 二次校验;账户写入、设备/绑定和账单管理统一只接受 session,API Key 保持数据面权限。
    • 一次性令牌与支付回跳:邮箱验证、重置、Magic Link、短信码以条件更新原子核销;Checkout 成功/取消地址在占券建单前限制为 WEB_BASE_URL 同源。
    • 复扫追加加固:开发用验证/重置/Magic Link token 仅允许 development 返回,Web 与 Mobile 生产 API URL 强制 HTTPS;诺诺发票回调密钥改为缺失即拒绝;优惠券预占先锁 coupon 行再检查单用户次数;公网支付/AI/发票 Webhook 在解析前流式限制 1MB。
    • 回归覆盖:新增 SSRF URL、OAuth 深链白名单与 Checkout 回跳测试,并同步安全清单、API、存储、Universal Links 和 env 示例。

  16. AI 派发可靠性 M2:持久化意图、模糊结果恢复、PostgreSQL 真并发

    • 持久化派发意图:generation 新增 dispatch_state(pending/sending/accepted/ambiguous)与

    dispatch_started_at,附 PostgreSQL/SQLite 迁移并同步 MySQL schema;worker 原子认领时先落 sending 再调用厂商,进程崩溃或重复入队也不会二次创建。

    • 模糊结果不误退/不重发:Kie/Replicate/Fal 将连接中断、408/5xx、成功响应缺 task id 标为

    ambiguous,保留积分 hold 等待回调或模型硬超时;429 仍走有限退避,明确 4xx/业务失败才立即 failed+退款。厂商已接单后的本地 task id/结果落库继续仅重试本地步骤。

    • 签名回调补绑:三家创建 webhook URL 携带本地 generationId;验签后的 webhook 在

    (app,provider,generationId,running,external_task_id IS NULL) 条件下原子补绑厂商 task id,恢复 “厂商已接单但创建响应丢失”的任务;正常 task id 路径和并发回调保持幂等。

    • 真实 PostgreSQL 竞态门禁:CI 新增 PostgreSQL 16 service,迁移后并发验证 10 积分下

    7+7 只能一笔成功且余额不为负、5+5 两笔恰好提交一次,并核对 consume 流水数量;普通 Vitest 环境安全跳过,专用 test:postgres job 强制执行。

    • 文档/验证:同步数据库设计、AI 执行流程、状态机和测试手册;本地 Web 394 passed

    (另 2 条 PostgreSQL 专用用例按设计 skip)、core-domain 39/39、api-contract 56/56, Web/mobile typecheck、Web lint/生产构建、Prettier、SQLite 迁移实跑与双 dialect migration drift 检查均通过;PostgreSQL 两条真并发由新增 CI service 门禁执行。


  17. Web React Hooks lint 零告警与 Edge Middleware 兼容性复核

    • Lint 零告警:清理 Web 端 14 条 React Hooks warning;模型切换时的控件默认值重置改到

    用户选择事件,避免 Effect 级联更新;邮件验证的空 token 状态改为初始化派生,并补齐翻译函数 依赖;初始数据请求及 localStorage、cookie、UA、PWA、客服等浏览器状态同步增加精确规则说明。

    • 组件精简:SupportWidget 移除仅用于区分第三方气泡的冗余 provider state;下载页移除已失效的

    react/no-danger disable,保留服务端生成 QR SVG 的既有可信边界。

    • 部署兼容性决策:不迁移到 Next.js proxy.ts。当前 middleware.ts 明确要求 Edge runtime,

    而 proxy.ts 强制 Node runtime,现有 OpenNext/Cloudflare Workers 目标不支持;构建 deprecation 提示属于已知兼容性取舍,待部署适配器支持后再迁移。

    • 验证:Web lint 0 warning / 0 error、Web 386/386、core-domain 39/39、

    api-contract 56/56;Web typecheck/生产构建、mobile tsc 与全仓 Prettier 均通过。


  18. AI 资金与任务完整性里程碑:参数计费、并发积分、单次派发

    • 模型级参数规范化:@zmlkit/core-domain 新增由 AI_MODELS 目录驱动的严格规范器;校验

    media/model 一致性、枚举 options、时长 min/max/step、参考图片/音频数量与 URL、音色及动态 toggle,拒绝未知参数和客户端 tier;当前图片固定 n=1。计费和 Provider 只读取规范化参数, 封堵 durationSec<=0 / n<=0 零积分生成,并把省略 model 的请求解析成持久化的具体目录模型。

    • 积分并发与事务完整性:generation、FIFO hold、consume 流水及 credit_txn_no 改为同一

    事务;grant 扣减加入 (app,user,id,remaining) CAS,冲突时回滚并重试整个事务,避免并发请求 重复消费同一余额。hold/退款金额增加正整数防御,失败状态与补偿退款也同事务提交;并发成功 已抢先结算时,迟到失败路径不会误退款。

    • 供应商派发去重:queued→running 先原子认领,Kie/Replicate/Fal 创建调用仅执行一次;

    模糊超时或本地落库失败不再重调供应商。成功响应后的 task id/结果 URL 本地持久化独立退避 重试;同步结果的待转存 URL 写入 result_meta,watcher/客户端轮询/cron 只重试本地转存。

    • 防御纵深与文档:三家 Provider 再校验目录 provider/media;ai-capabilities.md 与

    state-machines.md 同步规范化计费、CAS hold、单次派发和原子退款契约。

    • 测试:新增参数攻击、模型错配、积分 CAS 竞败、generation+hold 事务失败、供应商已接单但

    本地落库连续失败、成功/失败并发结算等故障注入覆盖;core-domain 39/39、Web 386/386、api-contract 56/56;Web typecheck/生产构建、mobile tsc、Web lint 与全仓 Prettier 门禁通过(lint 保留 14 条既有 React hooks warning,0 error)。


  19. 安全自动化里程碑:跨 App 黑盒、Mobile Vitest、CI 密钥扫描

    • 跨 App 黑盒矩阵:新增 app-isolation.spec.ts,在自包含 SQLite/真实 HTTP 环境用同一邮箱

    分别注册 app_a/app_b,钉死 user id 独立、A 的 profile 写入不影响 B、A bearer 在 B 下 GET/PATCH 均 401 AUTH_TOKEN_INVALID;Playwright 从 7 增至 8/8。

    • Mobile 测试从占位变可执行:接入 Vitest Node 环境与 @/ alias;4 个测试文件、10/10

    覆盖 API client 自动注入 X-App-Id/bearer、公开认证请求、401 清 token,SecureStore session 固定 key,推荐码深链清洗/暂存,以及国内隐私政策版本闸门。根 pnpm -r test 与 mobile CI 都实际运行,不再 echo "no tests yet"。

    • Secret scanning 门禁:CI 新增独立 Gitleaks 8.30.0 容器 job,同时扫当前 checkout 与本次

    push/PR 新增 commit range,启用 --redact;不采用需组织 license 的 Action。配置仅排除 .git、依赖与生成缓存,源码/文档/public 仍扫描;两处明确的空 env/合成 token fixture 用 行内 allow 注释,当前树本地扫描 0 findings。

    • 安全清单结案:security-checklist.md 勾选 Mobile SecureStore、token 绑定 app_id、跨 App

    黑盒矩阵与 CI 密钥扫描;新增 mobile-testing.md,同步 E2E、发布清单、一致性报告和文档索引。

    • 验证:pnpm -r test 478/478、Playwright 8/8、Web 生产构建、mobile typecheck/

    lint/Expo alignment、api-contract 56/56、Prettier 与 Gitleaks 当前树扫描全部通过。


  20. 稳定性里程碑:依赖安全、CI 恢复、pnpm 10 与 Expo 对齐

    • 生产依赖安全:Next.js 从 16.2.9 升至安全补丁线(锁定解析 16.2.12),sharp 升至

    0.35;PostCSS、fast-uri、brace-expansion、xmldom 增加精确且保持消费者 major 兼容的 override。pnpm audit --prod 从 18(12 high / 6 moderate)降至 1 moderate;剩余项为 Expo→xcode→uuid@7 的原生构建链漏洞,线上不可达且跨 major override 会破坏 API,记录在 security-checklist.md §15 等待上游修复。CI 新增 --audit-level=high 阻断门禁。

    • pnpm 单一真源:仓库 packageManager 与三处 GitHub Actions 统一到 pnpm 10.34.5;

    overrides / React Native audio patch 只保留在 pnpm-workspace.yaml,重新生成锁文件,消除 package.json/workspace 双写与 ignored-settings 告警,并确认 patch hash 仍进入 lockfile。

    • 共享契约恢复全绿:状态机测试同步最新设计——订单本地过期后允许迟到渠道成功

    expired→paid,订阅支持试用首扣失败/取消及 past_due 取消;56 项 api-contract 测试通过。

    • Web 质量门禁:修完 ESLint 的 9 个 error(render purity 的有意时钟读取加窄域说明、

    未使用 import、邮件 JSX 转义、JPush payload 以精确类型替代测试 any);保留的 React Compiler 建议仍为 warning,不阻断构建。

    • 格式化基线恢复:Prettier 精确锁定 3.9.1,并统一修复仓库既有格式 backlog;.agents、

    生成物及按契约保持双引号的 Web 扩展词典继续排除,CI 的 format:check 恢复为可执行门禁且 不改写机器产物。

    • Expo 兼容性:按 expo install --check 对齐 SDK 56 的 Expo core、router、updates、

    notifications、document/image picker 等 14 个依赖;CI 新增对齐检查。mobile lint 改为直接 调用已安装 ESLint,避免 Expo CLI 二次解析包管理器版本。

    • 文档纠偏:account-security.md 移除“移动端 2FA UI 未实现”的陈旧 TODO;

    consistency-report.md 增稳定性增量核对并明确 MySQL 运行时、移动端测试、跨 App 黑盒矩阵 仍是后续独立里程碑,未虚报完成。


  21. 移动端提取公用 UI 基础组件(components/ui)+ 下游「公开面」文档化

    • 新增 mobile/src/components/ui/(全部接 theme,供模板屏幕与 apps/<slug>/ 下游共用):Button(primary/outline/danger 变体 + loading 态)、TextField(label/error/secureToggle 眼睛按钮/multiline 自动 textarea)、Card(可选 onPress)、EmptyState(emoji+标题+正文+动作)、LoadingView、ErrorView(配 friendlyError + 重试)。
    • 模板屏幕自我消费(组件先被模板用过才算稳定 API):credits / feedback / change-password / bind-phone / edit-profile / create / billing / forgot-password / reset-password / magic-link-request / magic-link 的手搓按钮与输入框全部换成 Button/TextField(消掉 5+ 份 borderRadius:10~12, padding:13~15 微妙不一致的重复样式);notifications / ai-history / gallery / billing 的 loading 与空态换 LoadingView/EmptyState;MfaCodeForm 内部同步迁移;顺带把 magic-link / reset-password 等处硬编码 #dc2626 归一到 colors.danger。有意不动的:onboarding 按钮(每页 accent 色的定制设计)、AuthScreen 内部(登录关键路径、自身已是共享组件)、profile 的紧凑内联小按钮、chat 发送圆钮。
    • 文档化下游「公开面」:docs/build-new-app.md §4 与 mobile/src/apps/README.md 列出 apps/<slug>/ 可直接依赖的稳定导出(@/api/client、@/components/ui、@/theme/useTheme、@/i18n/useT、@/lib/errors、@/auth/store 等),并明确「自定义屏幕先用 ui 拼、别手写按钮/输入框样式」。
    • 验证:mobile typecheck + lint(0 errors,触及文件零新警告)、web 生产构建(dummy env)全绿。

  22. 中文区价格按 App Store 官方换算与美区统一

    • app_cn_demo 目录(core-domain plans)从「¥9.9 入门包 + ¥18 月卡」两条示例,重构为与 app_a 美区产品线 1:1 对齐的六条:入门/标准/专业积分包 + 会员月卡/年卡/终身,积分数与美区完全一致。
    • 价格采用 App Store 美区→中区官方自动换算价位(iOS IAP 会得到的同一换算):$4.99→¥30、$9.99→¥68、$29.99→¥198、$99.99→¥648、$149.99→¥998;月卡/年卡沿用国内「限时买断」模型(微信/支付宝无自动续费),并按 app_a 组合模型补齐每周期积分(月卡 1,500/30 天、年卡 20,000/365 天、终身 5,000)。
    • 会员与积分体系设计.md 示例价格同步更新。顺手修一个既有失败测试:ai-models.test.ts 的 LEGAL_MEDIA 补 'audio'(此前加音频模型时未同步,包内测试一直红)。
    • 验证:core-domain vitest 36、web typecheck + vitest 376 全绿。

  23. 三端审计整改:聊天历史端点 + 社交注册推荐归因 + 计费幂等单测 + Cookie 同意门 + CSP

    • P0-1 泄露密钥核验:git 历史中 docs/memo202607.md 的 OpenRouter key 实测仍有效(auth/key 返回 200)——需站长去 OpenRouter 吊销换新、2FA 备份码重生成(模板侧无法代办,07-18 已记录但未执行)。
    • P0-2 pnpm 配置向前兼容:pnpm.overrides / patchedDependencies 镜像进 pnpm-workspace.yaml(pnpm 10+ 只读该文件并忽略 package.json 的 pnpm 字段;当前 packageManager 钉 9.4.0 只读 package.json——两处同步维护,升 pnpm 10 后删 package.json 段)。9.4.0 下验证 install 无变化、RN patch 仍应用。
    • 聊天历史端点(api-contract §5 中唯一「文档有、实现无」的端点;此前对话写库但永远读不回):新增 GET /api/ai/chats 与 GET /api/ai/chats/:id/messages(app_id+本人双重限定、ULID 游标分页、parts 拍平为 content),DTO 同步 docs/contracts/dto.ts 与 @zmlkit/api-contract;ChatClient 加会话侧栏(新会话/切换加载历史/首条消息后刷新列表);api-usage.md 登记两端点。
    • 社交注册推荐归因(coupons-referrals §5 TODO 结案;此前仅邮箱/手机注册归因,微信/OAuth/Apple——两个客户端的主注册路径——静默丢失):wechatFindOrCreateUser / oauthFindOrCreateUser 新建用户分支统一调 attachReferralOnSignup;Web OAuth 的 ?ref= 经签名 state 走 provider 往返(微信 cookie-state 同样携带);AppleLoginRequest DTO 补 referralCode;小程序 onLaunch 捕获分享链接 ?ref= 暂存、首次登录透传后清除;RN 深链捕获(Linking.useURL → SecureStore)后随邮箱/手机/微信/Apple/OAuth 全部登录透传,成功即清。
    • 计费钱路单测(此前履约/幂等零测试):webhook.test.ts 8 例钉死 claim 协议(新事件领取/已处理重复/在途拒绝/陈旧接管 CAS 胜负/释放后重领/释放异常不抛);grant.test.ts 10 例钉死履约(目录积分入账+txn_no、重复 paid no-op、金额/币种不符审计+拒绝履约、乐观锁竞败零发放、canceled→paid 非法转移拒绝、trial 零积分建 trialing 订阅、续费按周期入账、续费重复去重、陈旧周期跳过)。
    • Analytics Cookie 同意门(analytics.md §3 占位结案,GDPR/ePrivacy):hasAnalyticsConsent() 接真实 Cookie(zmlkit_analytics_consent,一年);新增 <ConsentBanner/>(两 root layout 挂载、12 语言词条,仅当「需同意 + 未选择 + 当前区确有已配 provider」时展示;接受即刻 initAnalytics(),拒绝仅记录)。NEXT_PUBLIC_ANALYTICS_CONSENT=implied 跳横幅(视为同意),默认 required 安全默认;env 示例两处登记。
    • CSP 响应头(security-checklist §10 最后一项):next.config.ts 补零破坏子集 frame-ancestors 'none'; object-src 'none'; base-uri 'self'(不含 script-src——严格 nonce 版按 §14 留观单独立项,绝不误伤营销页第三方埋点);§10 勾选、§14 留观段更新。
    • 顺带:miniapp README 之外,consistency-report.md 等文档陈旧项留待下轮。验证:web typecheck + vitest 376(358+18)+ 生产构建(CI 同款 dummy env)、mobile typecheck、miniapp typecheck 全绿。

  24. 新增 Linux 服务器 Docker 自托管分步部署教程

    • 新增 docs/deploy-docker-linux.md:从零(装 Docker/防火墙/DNS)到生产可用的完整分步教程——生产 compose 编排(web+postgres+redis,端口只绑 127.0.0.1)、env 最小可启动集、迁移用 --target build 工具镜像跑(runner 无 tsx/drizzle,服务器不装 Node)、Nginx TLS 反代(X-Forwarded-For 供限流取 IP、SSE 关缓冲、50x 兜底页、certbot)、宿主机 crontab 触发 cron 路由(AI 对账走进程内循环不需外部 cron)、webhook/小程序合法域名注册、升级发布流程(先迁移后换容器、小内存离线构建、多副本须配 REDIS_URL)、pg_dump 备份与日志轮转、常见问题表。
    • docs/README.md 索引与 deployment.md §2.3 加指引链接(§2.3 保持要点摘要)。

  25. 三端体检整改:机密移出仓库 + 生成物不入库 + CI 补 mobile/miniapp 门禁 + /api/content 缓存限流

    • 仓库卫生(P0):docs/memo202607.md(私人备忘,含 OpenRouter key 与 2FA 备份码)移出 git 跟踪并 gitignore(docs/memo*.md)——注意:该 key 已进 git 历史,需去 OpenRouter 吊销换新,备份码作废重生成;还原 web/.env.example 遗留的 Duolingo 测试链接与 next-env.d.ts 构建 churn。
    • 生成物不入库:web/src/generated/(docs.json/updates.json,由 gen-content.mjs 从 CHANGELOG 与 web/content/docs 生成)移出跟踪 + gitignore,与 miniapp version.ts 同策略;web typecheck 脚本前置生成器,新克隆直接 typecheck 不再缺文件(pnpm 不跑 pre 钩子,显式链式)。
    • CI 三端门禁:新增 mobile job(pnpm typecheck + expo lint;原生构建留 EAS)与 miniapp job(独立 npm 工程:npm ci + typecheck + build:weapp,defaults.working-directory),此前 CI 只保护 web+packages。
    • /api/content 缓存 + 限流:公开无鉴权端点,三端首页每次 show 直打 DB。加进程内 60s 微缓存(键 appId|type|locale,上限 200 条过期清扫;不用 CDN s-maxage——多 App 共享部署须按 X-App-Id Vary,Cloudflare 等对自定义头 Vary 不生效,会串 App)+ IP 限流 120/分钟(复用分布式限流)。
    • 体检其余结论记录:营销页 <img> → next/image 待评估(Workers image optimizer 兼容);百望发票/微信 mediaCheck/席位计费同步等 TODO 均有文档留档;依赖仅落后 patch/minor。验证:web typecheck + vitest 358 + 生产构建全过。

  26. 小程序对齐移动端:生成页模型/参数/参考图 + 模板提示词直填 + 埋点 + 会员状态

    • 盘点结论:小程序已覆盖登录/三 Tab/生成/chat/支付/积分/通知/设置等主干;本次补齐四项差距,其余(onboarding、生物锁、评分引导、桌面小组件、声音克隆录音、找回密码)平台不适用或已定走 Web/App,记录在 miniapp/README。
    • 生成页进阶能力:小程序不能运行时引 @zmlkit/core-domain(独立 npm 工程,import type only),新增 web GET /api/ai/models(鉴权,按已配置 Provider 过滤返回 AI_MODELS 目录,与 /api/ai/providers 同款);生成页据 spec 渲染模型/画面比例/画质/分辨率/时长/开关/音色选择器 + 参考图上传(Taro.chooseImage → /api/ai/uploads,图片=参考图列表、视频=首帧/尾帧槽位),params 契约与 web/RN 完全一致;目录拉取失败优雅降级为纯提示词(服务端选默认模型)。声音克隆模型(referenceAudio.required)过滤不展示。tsconfig 补 core-domain 类型映射。
    • 模板提示词直填:首页模板卡带 CMS 正文(样式提示词),点击经 lib/content.ts 的 pending-prompt 模块变量交接(不走 URL,提示词长)预填生成页,对齐 RN "use this template"。
    • 埋点:lib/track.ts(队列 + 15s/满 20 条批量 POST /api/analytics/events,onAppHide 兜底 flush,静默失败),app 入口 initAnalytics;事件名与 RN 相同:plans 页 paywall_open、支付确认 purchase_success、生成成功 generate_success。
    • 会员状态:积分页顶部新增会员卡片(来自 /api/user/me membership:终身/续费/有效期文案),权益在上、钱包在下,对齐 RN billing 页。
    • miniapp README 陈旧段落更新(生成/支付/chat 早已落地)。验证:web typecheck+build、miniapp typecheck+build:weapp 全过。

  27. AI 厂商限频感知重试:429 不再秒败退款,退避重新入队

    • Provider 层(kie/fal/replicate)建任务遇 HTTP 429(Kie 还含 in-body code 429)时在结果上标 rateLimited,与普通失败区分。
    • job runner 对限频不退款不快速重试(原 0.5s/1s×3 对限频无效):任务回 queued、result_meta.rateLimitRequeues 计数,指数退避后重新入队(env AI_RATE_LIMIT_RETRY_MS 默认 30s 起步翻倍、上限 5 分钟;最多 AI_RATE_LIMIT_MAX_REQUEUES 默认 5 次,超限才 failed+退款,failReason=rate_limited,走 failAndRefund 带用户通知)。
    • 加固:queued→running 改原子认领(条件 UPDATE + 影响行数判定,重复入队不会向厂商重复提交);reconcile tick 与 cron 对账新增 stalled-queued 清扫(退避定时器随进程死掉时,>5 分钟未动的 queued 行被捞回重新入队)。
    • 单测:run-job 限频路径 4 例(回队不退款/预算耗尽转失败/普通失败不受影响/退避曲线) + kie provider 响应映射 5 例;全部 358 例通过。
    • 文档:state-machines §4(新增 running→queued 转移)、ai-capabilities §5、env/web.env.example、improvement-roadmap P2-6(含"多账号 key 池明确不做"的决策记录:ToS 风险+复杂度,扩容走厂商提额或模型目录路由到第二供应商)。

  28. 品牌 wordmark 集中管理:抽 BRAND_TITLE 常量(显示值仍为 zmlkit)

    • 新增 src/lib/brand.ts:BRAND_TITLE(默认 zmlkit,可用 NEXT_PUBLIC_BRAND_NAME env 覆盖) + BRAND_TAGLINE + BRAND_FULL_TITLE。
    • 全站显示点改为引用常量:两个 root layout 的 title/applicationName/openGraph/JSON-LD、控制台侧栏 brand、营销页头/页脚 wordmark 与版权、PWA manifest、opengraph-image、以及 blog/docs/legal/pricing/updates/download 各页的 OG/tab 标题后缀与 JSON-LD name。
    • i18n 里嵌入品牌的 prose(mkt.footer / mkt.apiDocsSubtitle,12 语言)改用 {brand} 占位,渲染时替换。apidocs.note 的 @zmlkit/api-contract 是包名,保持不动。
    • 显示值默认仍是 zmlkit;买家改一处 env(或 brand.ts)即可整站换名。@zmlkit/* 包名与 [zmlkit] 日志前缀是代码层标识,刻意不动。
    • 验证:以 NEXT_PUBLIC_BRAND_NAME=Acme 生产构建 + 起 server,首页 title/页脚/manifest/OG 全部变 Acme 且首页无残留 zmlkit wordmark;typecheck + 349 单测通过。
  29. App 下载页 /download:PC 扫码 / 手机直点,iOS/安卓/鸿蒙 env 门控

    1. 设备自适应(DownloadPanels.tsx,client):一套数据两种呈现——PC/平板每平台一张卡 + 服务端生成的 SVG 二维码;手机把访客自己的平台置顶为整宽主按钮、其余压缩为次级链接、不显示二维码。SSR 默认桌面,hydration 后按 UA 精修,页面保持 ISR 可缓存。
    2. 三平台 env 门控(DOWNLOAD_IOS_URL/ANDROID_URL/HARMONY_URL):某平台配了才出现,三者全空则整页 404、页脚入口与 sitemap 一并隐藏。遵守 PRD §4。
    3. 鸿蒙的诚实:依 harmonyos-support.md,纯血鸿蒙 NEXT(.hap) 未实现,鸿蒙卡通常指向同一 .apk 并标注「HarmonyOS 4.x 及以前」,不假装存在 .hap 包。
    4. 二维码:server/qrcode.ts 用 qrcode 库 toString(type:svg) 产出内联 SVG(高分屏清晰、无额外请求、白 quiet-zone 任何背景可扫)。
    5. 真实验证:生产构建起 server,桌面 /download 与 /zh/download 均 200 且渲染 3 个二维码 + 全部链接 + 鸿蒙副文案;未配 env 时 404 且页脚链接消失。i18n download.* 补 12 语言;新增 docs/app-download.md,README/screens-spec/env.example 登记。typecheck + 349 单测通过。
  30. PWA 可安装:manifest 补 192/512 图标 + 极简 SW + 营销站/控制台双入口

    1. 补齐可安装三条件:manifest 加 192/512 图标(新增 app/icon-192.png、icon-512.png 动态路由,共用 pwa/icon-mark.tsx,声明 any+maskable);两个 root layout 补 metadata.manifest(此前根本没链进 head,manifest 形同虚设);新增 public/sw.js 由 PwaProvider 注册。
    2. 极简 SW,故意不缓存:所有请求原样走网络,SW 内不存业务数据(避免给二开项目留陈旧缓存地雷);唯一缓存 offline.html 且只对页面导航兜底。dev 不注册(避免与 Turbopack 热重载冲突)。
    3. 双入口 + 自隐藏:InstallButton 挂营销站页脚与控制台侧边栏;拿不到安装路径就渲染 null,绝无死按钮。iOS Safari 弹「分享→添加到主屏幕」引导;iOS 上的 Chrome/Firefox 直接隐藏。标签走 props(营销树无 I18nProvider)。
    4. 真实验证:生产构建起 server,确认 manifest/sw.js(fetch handler)/两个图标(image/png)/offline.html 全部 200,且 <link rel="manifest"> 进了营销与控制台两棵树的 head。
    5. i18n pwa.* 补 12 语言(修掉 Hindi/en 等 6 处值内裸双引号);文档新增 docs/pwa.md,README/screens-spec 登记。
  31. UTM 获客归因:首触 utm_source 落库 signup_source + 管理后台来源列/筛选

    1. 激活休眠列:app_user.signup_source 在 database-design.md 里早已预留("获客渠道 utm")但全库无读写。本次接通,零 schema 变更、零迁移。
    2. 捕获(middleware.ts):访客带 ?utm_source 落地且无归因 cookie 时,utm_source/medium/campaign 三元组写 zml_utm cookie(30 天/Lax/httpOnly);首触优先不覆盖。清洗函数去空白/引号/尖括号/控制符、截 64 字符,cookie 解码时再清洗一遍防篡改。
    3. 落库:密码注册与 OAuth 首次建号读 cookie 写 signup_source;OAuth 建号顺手补记此前缺失的 signup_ip。手机号/微信/魔法链接维持现状(本就不采 IP),移动端无 cookie 自然为空。
    4. 展示:/admin/users 新增「来源」列 + src 模糊筛选;用户详情副标题带来源与注册 IP。col.source 补 12 语言。
    5. 真实验证抓到一个 bug:encodeURIComponent + Next cookies.set 自身编码 = 双重编码,注册端解不出——改为输出裸 JSON 交给 Next 编码,全链路(落地→cookie→二次访问不覆盖→注册→sqlite 查到 signup_source='google'→管理页筛选含/排除)在隔离环境跑通。
    6. 文档:analytics.md 新增 §1.5 UTM 获客归因;database-design.md 列注释指路。单测 9 例(清洗/编解码/Cookie 头解析)。

  32. 复核(第 4 轮):e2e 覆盖佣金门控/会员卡 + e2e 独立端口修隔离缺陷 + 三份文档去陈旧

    1. 新增 web/e2e/referral.spec.ts(2 用例):① 全新用户在 /settings/referral 看得到邀请卡但看不到「佣金」区域(服务端 isAffiliate 门控,此前只有人工验证);② /settings/billing 会员卡免费态正常渲染。跑通于一次性 sqlite 隔离环境。
    2. 修 e2e 隔离缺陷(跑新 spec 时暴露):playwright 与开发 dev server 共用 3000 端口 + reuseExistingServer,实测复用了另一个衍生项目的 dev server(app_a 未注册 → 注册页 500);若复用的是本项目 dev server 则会静默打到开发数据库,违反 setup-env.ts 的隔离承诺。改为 e2e 专属端口 3947(webServer 命令显式 next dev -p)。冷缓存(删 .next)全量 4 spec 验证通过。
    3. 文档去陈旧:improvement-roadmap.md P2-5 还写着「affiliate 本轮不做」→ 补 2026-07 已落地条目;consistency-report.md 增 §7 增量核对(3 新表/2 新权限/派生原则/env-only 费率/e2e);docs/README.md 索引修正「零新表」表述(membership_grant 是唯一落库来源)并补 coupons-referrals §3 佣金描述。
  33. 复核(第 3 轮):真实渲染验证两个新管理页 + 修模板既有的 2 个 i18n 缺键

    本轮以「实际跑起来」为主,不再只靠 typecheck/build:

    1. 真实渲染两个新管理页(此前只做过构建验证——而 /admin/users/[id] 正是在真实打开时才炸的)。起 dev server + 造真实管理员会话 + 灌数据后 curl:/admin/memberships、/admin/commissions(含日期/状态筛选参数)、/admin/settings、/settings/referral、/settings/billing 全部 200、无运行时错误;带数据渲染确认 tier 徽标、终身标识、勾选框路径均正常。

    → 多币种修复在真实 HTML 上得到确认:页面同时出现 100.00 USD、500.00 CNY、1200 JPY(零小数币种未被 ÷100),而旧 bug 的产物 600.00 搜不到。

    1. 实测「后台是否赠送积分」开关(此前从未验证,而这是明确需求):开 → 邀请人得 100 积分;关 → 0 积分,但邀请关系仍归属(佣金链路不受影响)。4 项全 PASS。
    2. 实测 sqlite「零依赖试用」路径:新加的 3 张表此前从未在 sqlite 上跑过。从零建库跑迁移 + 对三表增删查,epoch-ms 时间戳往返为 Date、终身 NULL 正确 → 模板首次上手路径完好。
    3. /api/user/me 走真实 HTTP 验证 membership 契约(移动端强依赖):非会员形态与 8 个字段齐全。

    修复(模板既有 bug,非本次新增):

    • security.exportFailed、common.networkRetry 两个 i18n 键在字典里根本不存在 → 数据导出失败、magic-link 失败时,用户看到的是键名本身(security.exportFailed)而不是错误提示,恰好都发生在最糟的时机。已补齐 12 语言。
    • 顺带建立静态校验手法:扫描全库 t()/tr() 的字面量键 × 合并字典 → 851 个引用、0 缺失。

    过程记录(避免下次重蹈): 先尝试用「扫描渲染 HTML 找裸键」来查缺键,结果每个页面都报同一份巨表、连 /settings/billing 都"泄漏" btn.newApp —— 原因是 i18n 字典被序列化进 RSC payload 当数据,正则把它当成了渲染文本。该检查是假阳性,已弃用并改为上面的静态比对。

    验证: web typecheck + build、340 例单测(36 文件)全绿;所有测试数据与临时脚本已清理(佣金表剩 0 行)。

    日期: 2026-07-15


  34. 复核(第 2 轮):补上承诺未做的「佣金比例后台只读表」+ 修 3 处文档失真

    发现并修复:

    1. 【承诺未兑现】佣金比例的后台只读展示压根没建。 决策时选的是「env 定义 + 后台只读(守铁律)」,我给出的预览里明确写着「/admin/settings → 只读展示,标注来源 env」——结果只在代码注释里提了,页面上没有。缺了它,管理员想知道当前比例只能上服务器翻环境变量,这个方案的一半价值就没了。

    → /admin/settings 新增「佣金比例」只读表:App / 会员等级 / 比例 / 计提范围 / 环境变量名(直接告诉运维改哪个 key),未配置时显示空状态。已用脚本对真实 env 验证渲染结果正确(含 default 排最后并显示为「其他套餐(无等级)」)。

    1. 【文档失真】api-usage.md 的 /api/user/me 响应示例已过期 —— 仍写着 subscription:null 却没有 membership,而这是「已实现接口的调用文档」,会直接误导二开者。

    → 补全响应示例 + 说明(派生非落库、非会员形态、lifetime 时 until 为 null)。

    1. 【文档缺失】新增的 3 个状态机没进 state-machines.md(该文档的职责就是「各表枚举值 + 状态机」)。

    → 新增 §6:membership_grant.status、affiliate.status、commission.status 的取值、转移图与关键约束(pending→settled 用 CAS;settled→reversed 允许——钱已付出,账必须能看出作废;reversed 为终态;幂等靠 (app_id,order_no);部分退款不自动冲销)。并在 §1 枚举总表补 3 行。特别注明:会员本身没有状态机(实时派生、不落库)。

    1. 【文档缺失】screens-spec.md 未登记新页面 → 补 /admin/memberships、/admin/commissions(含所需权限)、/settings/referral 佣金面板、/settings/billing 会员卡、移动端会员卡与 VIP 徽标、/admin/settings 的比例只读表。

    验证: web typecheck + build、340 例单测(36 文件)全绿;只读比例表用真实 env 跑脚本验证渲染。

    日期: 2026-07-15


  35. 复核会员/积分/佣金三套体系:修 2 个钱的 bug + 补移动端会员到期日

    发现并修复:

    1. 【钱算错·已用数据库证明】多币种佣金被混加。 affiliateTotals/listCommissions 按 (status, currency) 分组后却把不同币种加进同一个数,再用「第一个遇到的币种」当标签。实测:$100 + ¥500 → 显示 600.00 CNY。zmlkit 明确支持海外 USD + 国内 CNY(app_cn_demo),可触发。

    → 汇总改为按币种分列(CurrencyTotal[],纯函数 foldCurrencyTotals),UI 渲染 100.00 USD · 500.00 CNY,永不相加;每位好友的收益同样按币种分列。 → 顺带修 formatMoney 对零小数币种(JPY/KRW 等)不再 ÷100(120 JPY 而非 1.20)。

    1. 【钱算错·语义 bug】first_purchase 会给续费计佣。 原实现按「有没有更早的佣金记录」判断。若被邀请人在推广者被授权之前就买过(无佣金记录),其后续续费会被误判为「首购」并计佣——既违反字面语义,也与文档「授权前订单不补算」自相矛盾。

    → 改为按「有没有更早的 paid 订单」判断。同时保留退款释放名额的行为(退款订单转 refunded,不再算作更早的已付订单)。

    1. 【遗漏】移动端看不到会员到期日。 需求明确要求「网页端和移动端都能看到会员状态、会员到期日」,但移动端只做了 Me tab 的 VIP 徽标,billing 屏仍只有积分。

    → 移动端 billing 顶部新增会员卡(tier 徽标 + 终身/续费于/有效至 + 开通时间),数据取自 user.membership(/api/user/me 已下发,无需新接口)。

    验证: 每条修复都先证明再修、修后复验:

    • 多币种:写脚本对真实库造 USD+CNY 两笔 → 复现 600.00 CNY → 修复 → 新增 9 个纯函数单测钉死(含「不同币种不相加」「零小数币种」)。
    • first_purchase:对真实库跑 3 个场景全 PASS——「授权前购买 → 续费不计佣」(原 bug)、「退款释放名额仍有效」(防止修复引入回归)、「首单计佣、第二单不计」。
    • 全量:web typecheck + build、340 例单测(36 文件)、mobile tsc + lint(0 error)。

    已知行为(非 bug,记录备查):

    • 邀请积分金额在注册时快照进 referral 行(既有设计)。故后台把积分开关关掉期间注册的用户,即使之后重新打开,其首购也不会补发积分。
    • 部分退款不自动冲销佣金(与积分处理一致:refundOrder 对部分退款只记审计、需人工判断),已写入文档。
    • MEMBERSHIP_REQUIRED 未加移动端本地化文案,与其孪生的 CREDITS_INSUFFICIENT 现状一致(走服务端 message 兜底)。

    日期: 2026-07-15


  36. 分享赚佣金:授权制佣金体系(与邀请积分正交)+ 管理端账本

    先确认的三个岔路(决策记录):

    1. 佣金比例存哪 —— 与 PRD §4 铁律「配置全部走 env、后台设置页只读」正面冲突。你选守铁律:比例按 APP_<SLUG>_COMMISSION_RATE_<TIER> 走 env,后台只读。
    2. 出金模型 —— 你选只记账、后台手工标记已结算(不做提现/余额/收款账户,避免 PII 与合规负担)。
    3. 计提规则 —— 「仅首购」与「续费也计」被同时勾选,互斥;不在钱的规则上替你猜,改为 env 开关 APP_<SLUG>_COMMISSION_ON=first_purchase|all_orders(默认首购,与既有 REFERRAL_REWARD_ON 同套路),两种都能要。

    分层: 邀请积分(人人可得、站内价值)与佣金(授权制、真钱)正交并存,同一人可两者都拿。

    • env 配置(apps.ts):COMMISSION_RATE_<TIER>/_DEFAULT 百分比 → 内部 basis points 整数运算(无浮点漂移,>100% 夹到 100%);COMMISSION_ON。任一比例>0 即启用计划,但仍需逐人授权。
    • 新表 affiliate(授权:status/note/authorized_by/revoked_*,唯一 (app_id,user_id))与 commission(账本:base_amount/rate_bps/amount/status,唯一 (app_id,order_no)),三方言 + pg/sqlite 迁移。
    • commissions/service.ts:accrueCommission(基数=实付金额;比例按套餐 tier,无 tier 用 DEFAULT;首购/全单范围;$0 试用不计;未授权不计;自邀防护)、commissionAmount 纯函数(四舍五入 + 封顶实收)、reverseCommissionForOrder(退款冲销,含已结算行)、settleCommissions(CAS 只动 pending)。
    • 计提挂载 grant.ts 三处(web 首购 / RevenueCat / 续费)+ reclaimRefund 冲销;全部 fire-and-forget,佣金故障绝不阻断履约。
    • 退款释放「首购名额」:冲销行不占位,否则推广者会因对方退款永久失去该用户的计佣机会。
    • 管理端 /admin/commissions(新页 + HandCoins 图标):按推广者邮箱/状态/App/日期区间筛选,三档汇总,勾选批量标记已结算;结算按每行所属 App 逐一复核权限(平台管理员跨 App 视图下防越权)。
    • 授权入口 /admin/users/[id] →「佣金推广权限」卡;无 env 比例时提示「授权也只会得到 0」。
    • 推广者视图 /settings/referral:待结算/已结算/好友数 + 每位好友的注册时间、是否购买、购买的会员类型、购买日期、为我赚了多少。邮箱仍脱敏(沿用既有隐私立场)。
    • 邀请积分后台开关:feature_toggles.referralCredits kill-switch(默认开,只有显式 false 关闭;env 定金额、后台定启用);关闭后邀请关系仍归属,佣金与邀请图谱照常。/admin/settings → 增长。
    • 新权限 app.commission.manage(独立于 orders.read——看谁该拿钱是更小的圈子);新审计动作 3 个。
    • 文档:coupons-referrals.md 新增 §3(分层对比、env、8 条计提规则、出金、界面、验收清单)、database-design/rbac-matrix/env 示例同步。

    验证: web typecheck + build;单测 35 文件 331 例全绿(含新增 5 例佣金金额数学:四舍五入、封顶实收、零基数/零比例、1 分订单不产生幻影佣金)。并对真实数据库跑通完整链路(造邀请人/被邀请人/订单,5 项全 PASS 后清理):未授权不计佣 → 授权后按 tier 比例计佣(999×20%=200 分)→ webhook 重投不重复 → 首购范围忽略第二笔 → 退款转 reversed。

    ⚠️ 升级动作: 需执行迁移(affiliate/commission 两表)+ 重跑 pnpm --filter web db:seed(新权限码,否则佣金入口静默不显示)。

    日期: 2026-07-15


  37. 安装 expo-clipboard;模板风格提示词支持一键复制

    背景: 上一条里我为「复制提示词」按钮考虑过 expo-clipboard,但因其为原生模块(需重做 dev build)而放弃,改用 selectable 文本。现按需求装上——且它与既有的邀请分享(profile.tsx → /api/user/referral → 系统 Share 面板,docs/coupons-referrals.md)同属一条产品线,后续「复制邀请码/邀请链接」可直接复用。

    • mobile/package.json 新增 expo-clipboard@~56.0.4(经 npx expo install 由 SDK 56 自动对齐,非手填版本号)。
    • template.tsx:风格提示词卡片右上角加「复制」按钮,点击后闪现「已复制」1.5s。定时器用 ref 持有并在 unmount 时清理——否则闪现期间离开该屏会对已卸载组件 setState。
    • i18n:mobile 12 语言新增 common.copy / common.copied。

    注意: expo-clipboard 是原生模块,需重新 dev build 后生效(可与 d5ad8a0 的 expo-system-ui 合并在同一次构建里)。

    验证: mobile tsc + expo lint(0 error,16 个既有警告数量未变)。

    日期: 2026-07-15


  38. 移动端模板携带风格提示词,「使用该模板」自动填充

    约定: 模板内容的 BODY = 该模板的风格提示词(即产生封面图那个效果的提示词)。用 body 而非 prompt: 标签,因为提示词是含逗号/冒号的长自由文本,而 tag 是短 key:value 芯片,塞进去会被解析破坏且后台难编辑。ContentItem.body 本就存在且 /api/content 已返回,只是模板一直没用它。

    • content/actions.ts:actionGo(action, prompt?) 把提示词透传给 create-* 目的地(chat/credits/gallery 无提示词,忽略)。
    • create.tsx:读取 prompt 参数作为提示词输入框初值(仅播种,用户可自由改写)。/create 是每次 push 新建的栈屏(非 tab),故每次进入都会重新初始化。
    • template.tsx:详情页展示风格提示词(selectable 可手动选中复制),CTA 携带提示词跳转。
    • 首页 TemplateVM 增加 prompt,CMS 模板取 c.body,内置模板各自内置英文风格提示词(提示词是模型输入而非 UI 文案,故不做 i18n)。
    • 顺带修正内置演示数据的不一致:内置模板原先全部硬编码跳 create-image,导致「音乐片段」也跳图片生成;现各自带 action,音乐 → create-audio,提示词与目的地一致。
    • 后台补齐可编辑入口(关键):banner|template 类型走 isHome 分支,原本根本不渲染 body 输入框——约定将无法填写。现在模板类型在首页卡片字段组内新增「风格提示词」文本框(含占位与说明),banner 不显示。
    • i18n:mobile 12 语言加 tpl.stylePrompt;web 12 语言加 3 个 form.stylePrompt* 键。

    验证: web typecheck + build、mobile tsc + expo lint(0 error)通过。实跑数据链路:向本地库插入一条带 body 的 type=template 内容,curl /api/content?type=template 确认 body 原样返回移动端(BODY REACHES MOBILE: True),随后删除验证数据并确认接口不再返回。

    日期: 2026-07-15


  39. 管理端手工赋予会员(月/年/终身/自定义起止)+ 会员派生单测

    设计取舍(修正上一条的「零新表」): 「付费会员从订单派生、不落库」这条原则对付费依然成立,但它的前提是「有一条支付记录可派生」。管理员赠送的会员根本没有支付——伪装成假订单会污染营收统计(paidAmount 进报表),伪装成假订阅会让用户账单页渲染「取消订阅」按钮并调用一个不认识它的 provider。因此新增 membership_grant 表专门承载无支付的权益:付费永远派生、绝不落库;赠予落库、绝不伪装成付费。

    • 新表 membership_grant(三方言 schema 一致 + postgres/sqlite 迁移;sqlite↔postgres 行类型对等断言自动覆盖):tier、starts_at、ends_at(NULL=终身)、status(active/revoked,撤销留痕不删行)、note/granted_by/revoked_by,索引 (app_id,user_id,status)、(app_id,status,ends_at)。
    • web/src/server/membership-grant.ts(新):grantMembership()(校验 tier 必须存在于该 App 阶梯,否则拒绝——否则授予了却没有 rank/label,永远满足不了任何 gate;校验 end>start)、revokeMembershipGrant()(幂等 CAS)、listUserGrants()、resolveGrantEnd()(月/年按自然月年推算,终身=null,自定义直取)。
    • deriveMembership() 新增第三来源 grants:仅 status='active' 且已到 starts_at(支持未来生效)且未过 ends_at 才计入;赠予 renews=false。与付费叠加而非互斥(按既有 rank/终身/到期晚的取胜规则),补偿 30 天不会缩短用户既有付费会员。/admin/memberships 名册同步纳入赠予会员。
    • 管理端 /admin/users/[id] 新增「赋予会员」卡(与「调整积分」并列——同属「不收钱发价值」):tier 选择、月度/年度/终身/自定义起止、备注,附赠予历史与撤销;server actions 全程审计(新增 user.membership.grant / user.membership.revoke)。
    • 新权限 app.membership.grant(APP_MEMBERSHIP_GRANT),默认给 app_admin,app_support 注释预留;rbac-matrix 文档同步。
    • web/src/server/membership.test.ts(新,26 例):钉住派生的微妙规则——终身、国内月卡叠加、canceled 期末前有效、past_due 宽限、撤销/未来生效/过期赠予、rank 取胜、meetsTier、resolveGrantEnd。已做变异测试验证有效性(破坏「未来生效不激活」与「月卡叠加」各被对应用例精准杀死)。
    • 文档:设计文档新增 §7「管理端赋予会员」并修正 §2 的取舍表述;database-design 补表结构。

    ⚠️ 升级动作(必读): 本次新增权限码,而 hasPermission 读的是 DB 的 role_permission 表 → 必须重跑 pnpm --filter web db:seed(幂等:权限 upsert、角色权限 onConflictDoNothing),否则无人持有 app.membership.grant,入口不显示。同时需执行数据库迁移(新增 membership_grant 表)。

    验证: web typecheck + pnpm --filter web build 通过;web 单测 34 文件 326 例全绿(含新增 26 例);core-domain/api-contract 无新增失败(既有失败 4 例与本次无关)。

    日期: 2026-07-15


  40. 会员(entitlement)体系:与积分正交的权益层 + 三端展示 + 管理端名册

    背景: 模板原本只有积分层(credit_ledger FIFO + 过期、AI 预扣/退回),subscription 实质只是「按周期发积分」的发放器(credits_per_cycle),没有会员等级(tier)概念,也没有终身。这对 AI 生成类够用,但记单词/学习类要的是「买了会员就解锁功能」,与消耗量无关。

    核心取舍: 新增会员层与积分层正交,但不新增表——会员状态从既有 subscription + payment_order 实时派生(模板有 pg/mysql/sqlite 三份 schema,零迁移才好合并 upstream;且支付回调只管写对订单,会员永远是其函数,不会漂移;退款置 refunded 后会员自动失效)。

    • docs/会员与积分体系设计.md:重写为 zmlkit 设计规范(原 Q&A 稿仅供参考)——双层模型、派生规则、三种周期表达、各渠道生效、权益校验、三端展示、加 App 配方、验收清单;docs/README.md 索引与 core-domain.md 互链。
    • core-domain/plans.ts:Plan 新增 tier?(授予的会员等级,不填=纯积分包)与 membershipDays?(仅 one_time 会员卡:null=终身,N=买断 N 天且重复购买顺延);新增 MembershipTier 阶梯(label/rank/color)+ getMembershipTiers/getTierMeta/tierRank/planGrantsMembership/isLifetimePlan;样例:app_a 的 pro 月/年加 tier 并新增 pro-lifetime(会员+积分),新增 app_edu_demo(纯会员、credits: 0,记单词类参考),app_cn_demo 新增 vip-cn-monthpass(国内微信/支付宝无自动续费 → 卖 30 天月卡)。
    • plans.test.ts:credits>0 不变量放宽为「金额>0 且(有积分或有 tier)」以容纳纯会员套餐;新增 one_time 会员卡 membershipDays 不变量。
    • api-contract:新增 MembershipStatus DTO 并接入 MeResponse;新增错误码 MEMBERSHIP_REQUIRED(402)——CREDITS_INSUFFICIENT 的权益层孪生,让客户端区分「买积分」与「开会员」两种付费墙。
    • web/src/server/membership.ts(新):纯函数 deriveMembership()(订阅窗口 / 终身卡 / 限时卡叠加 / canceled 期末前仍有效 / past_due 宽限 / 多会员按 rank 取胜)+ getMembership() + 权益校验 meetsTier() / requireMembership()。
    • web/src/server/membership-roster.ts(新):管理端跨用户名册,复用同一派生纯函数(候选集受付费用户约束)。
    • /api/user/me 返回 membership;settings/billing 顶部新增会员状态卡(tier 徽标 + 终身/续费于/有效至 + 开通时间),积分卡下移,层次改为「权益 → 钱包 → 套餐 → 流水」。
    • 移动端:auth/store 的 MeUser 加 membership(从 /api/user/me 顶层折叠进 user),Me tab 昵称右侧渲染 VIP 徽标(★ Pro/★ VIP,色取自 tier meta)。
    • 管理端 /admin/memberships(新页 + Crown 图标导航,权限沿用 APP_ORDERS_READ):按 App(平台管理员)/会员等级筛选、按到期日排序(最快/最晚到期、最新开通),7 天内到期标红、终身高亮、按 tier 统计人数。
    • i18n:extra.ts 12 语言各新增 14 个会员键。

    验证: web typecheck + pnpm --filter web build(/admin/memberships 路由已生成)、mobile tsc 均通过;core-domain/api-contract 测试无新增失败(既有失败 ai-models.test.ts LEGAL_MEDIA ×1、state-machines.test.ts ×3 与本次无关,stash 复验确认)。

    日期: 2026-07-15