Files
Genarrative/docs/【项目基线】当前产品与工程约束-2026-05-15.md
T
kdletters 9476ff7648
Project CI / Frontend tests (push) Successful in 4m5s
Project CI / Repository checks (push) Successful in 4m23s
Project CI / Backend tests (push) Successful in 5m1s
Project CI / Native shell tests (push) Failing after 10m54s
迁移仓库工具资源到 Codex 并固化执行边界
将 .hermes 工具资源迁移至 .codex

同步代码、文档与 RAG 路径引用

补充任务范围、验收与时间盒执行规则
2026-08-27 13:55:25 +08:00

19 KiB
Raw Blame History

当前产品与工程约束

状态更新:2026-08-25。当前主站只描述图片画布编辑器、编辑器项目/素材、账号与平台公共能力;旧创作模板、旧公开作品详情和专属运行态不属于现役入口。

更新时间:2026-08-25

项目定位

Genarrative / 陶泥儿当前主站聚焦图片画布创作、编辑器项目与素材管理,以及账号、钱包、公共设置、后台管理和小程序外壳。AI Game Creator 独立 App 按其最新 Runtime 与客户端合同运行,不复用旧模板入口。

当前主要能力:

  • 图片画布编辑器、编辑器项目与公开精选素材读取。
  • 账号、短信 / 密码 / 微信登录、个人资料、钱包、充值、邀请码、反馈、法律信息和后台管理。
  • AI Game Creator 的 DirectProject 工作流与资源/UI workflow(详见现行技术方案)。

产品命名

  • 产品展示名:陶泥儿。
  • 消费单位:泥点。
  • 公开账号标识:陶泥号。
  • 创作侧称谓:陶泥儿主。
  • 产品 IP / 产品形象:统一指 public/branding/taonier-product-ip.png,即陶罐中探出的橙色耳朵形象;后续左上角品牌区、产品形象引用和品牌视觉说明默认使用该形象,AGC 项目素材按其专门资源合同处理。

不要把旧“百梦”等历史命名重新写入新 UI 或新文档。

当前入口

  • 本地开发默认 Web 端口为 3000API 为 8082BgFilter worker 为 8083SpacetimeDB 为 3101,后台 Web 为 3102;实际端口以 .app/dev-stack.json 和启动日志为准,Linux 用户端口范围可能整体漂移。
  • 后台入口使用当前 dev-stack 中的 Web / admin 端口(默认 http://127.0.0.1:3000/admin/http://127.0.0.1:3102),不得把默认值当作运行时事实。
  • 后台前端工程:apps/admin-web
  • 小程序 WebView 外壳:miniprogram/
  • 法律文本仍保存在 media/files/user_agreement.mdmedia/files/privacy_policy.mdmedia/files/disclaimer.md,由前端法律弹窗读取。

桌面端一级入口固定为 创作 / 项目 / 我的,对应 /creation/project/profile;图片画布为 /editor/canvas?projectid=<id>。移动端底部 dock 只保留 我的,根入口默认展示“我的”并保持该 Tab 选中;移动端直达创作、项目或画布时显示桌面端提示,不挂载工具页面。

账户与登录

  1. 主站登录弹窗必须稳定展示 短信登录密码登录 两个核心入口;GET /api/auth/login-options 只能补充微信等环境相关入口,不能决定是否隐藏短信或密码登录。
  2. login-options 为空、失败、只返回 phone 或只返回 password 时,前端仍要同时展示验证码登录页签和密码登录页签;短信能力真实可用性由发送验证码接口返回结果表达。
  3. 登录弹窗继续复用现有独立 modal 和页签结构,不在页面中新增功能说明类文案,也不把邀请码输入放回登录面板。
  4. 微信小程序 web-view 外壳默认不预登录,首次进入直接打开 H5,并保持与 Web 端一致的未登录状态;只有 H5 触发 openLoginModal / requireAuth 等受保护入口时,才跳转小程序原生授权态。
  5. 小程序内需要登录时不展示 H5 登录弹窗,也不走手输手机号 / 短信验证码流程;统一先通过 wx.login 获取微信登录 code 并调用 /api/auth/wechat/miniprogram-login 完成快捷登录。若该接口返回 created=true,或返回用户昵称仍是手机号、公开陶泥号、“微信旅人”等默认展示值,才展示原生 input type="nickname" 补充微信昵称并再次调用 /api/auth/wechat/miniprogram-login 写入 displayName。若后端返回 pending_bind_phone,再通过原生 button open-type="getPhoneNumber" 获取微信手机号授权并调用 /api/auth/wechat/bind-phone 换取系统登录态。
  6. 小程序外壳注入到 H5 URL 的 clientTypeclientRuntimeminiProgramEnv 是宿主上下文,H5 内部 pushState / 阶段导航必须跨页面保留,避免登录和充值误判为普通浏览器;首点时微信 JS bridge 可能尚未就绪,前端还需用 MicroMessenger + miniProgram User-Agent 作为小程序识别兜底。
  7. 小程序 web-view 页必须启用好友分享与朋友圈分享,分享目标固定回到 pages/web-view/index,不把 H5 当前 URL 作为不受控启动参数传回小程序页。
  8. 小程序 web-view 外壳运行时通过 wx.getAccountInfoSync().miniProgram.envVersion 自动识别版本:线上版 release 使用 www.genarrative.world,体验版 trial 与开发版 develop 使用 dev.genarrative.world;传给后端的 x-mini-program-env 分别为 releasetrialdev
  9. 账号信息面板只展示 账号信息 标题;绑定手机号和绑定微信以紧凑模块展示当前绑定状态,已绑定手机号展示完整手机号,已绑定微信优先展示微信平台实际返回并由后端保存的 wechatDisplayName。小程序 jscode2session 不能直接返回微信昵称或个人微信号,只能稳定拿到当前小程序维度的 openid,并在满足微信开放平台条件时拿到 unionid;小程序昵称来自快捷登录后按需展示的原生 input type="nickname" 提交的 displayName。后端下发 wechatAccount 作为绑定账号标识,前端在没有真实昵称时展示微信账号尾号,不展示裸“已绑定”。换绑入口放在对应模块右上角,退出登录和退出全部设备固定放在面板内容最底部。直接打开账号信息时,外层弹窗只负责定位与可访问语义,保持无背景、无边框并允许内层阴影自然溢出;背景、边框、圆角、内容裁切和阴影统一由内层账号卡片承载,避免直角外壳在四角露出不透明底色或把圆角阴影裁成矩形。
  10. H5 登录态从未登录变为已登录,或从已登录变为未登录后,必须刷新当前页面一次,确保编辑器项目、个人缓存和私有 query 都按新身份重新初始化;普通 access token 续期、账号资料更新和同一登录态内的设置变化不得触发整页刷新。
  11. 同一账号允许多端同时在线。新增登录和单设备退出只影响对应 refresh session,不得提升账号级 tokenVersion 让其它设备的 access token 失效;只有“退出全部设备”、修改密码、重置密码等明确安全动作才吊销全端 refresh session 并提升 tokenVersion
  12. 手机号认证只支持中国大陆号码:验证码、密码登录、绑定、换绑和重置密码请求统一提交 purePhoneNumber 与可选 countryCode,省略国家码时默认 86,旧 phone 字段不再接受;显式国家码必须为无加号的 86,其他值返回“仅支持中国大陆手机号(+86)”。主站输入框保留 autocomplete="tel" 与浏览器默认电话号码回填能力,认证 service 把浏览器可能回填的 +86 1xxxxxxxxxx86 1xxxxxxxxxx 拆成 { countryCode: "86", purePhoneNumber: "1xxxxxxxxxx" } 后提交。微信小程序手机号授权必须使用微信真实返回的 countryCode + purePhoneNumber,不得套用普通请求的缺省国家码。后端分别验证国家码和纯号码后再生成 E.164 存储;前端校验只提供即时反馈,inputMode="numeric" 也只提示软键盘布局。

账户与充值

  1. 主站、图片画板和 AI Game Creator 统一使用 packages/shared 的依赖注入式钱包 Zustand Store;主站与 Tauri 客户端只保留各自的 URL、认证和重试 transport adapter。主站顶部、图片画板顶部和“我的”统计必须消费同一份 ProfileMudPointBalance 快照,泥点总额固定取 totalPoints,不得混用 dashboard 的 walletBalance;只有充值中心响应缺少 mudPointBalance 时,Store 才可按 owner 保存同一响应的 legacy walletBalance 作为纯总额兜底。较新的 legacy-only 响应必须原子清除旧 mudPointBalance,充值弹窗、空账单、个人中心统计卡与图片画板顶部可读取该总额,但不得据此伪造分桶明细。切换或退出账号必须立即清空快照并拒绝旧账号在途响应;消费端在 owner 绑定 effect 生效前也必须按当前用户 ID 同步屏蔽 owner 不匹配的快照,旧账号请求不得阻塞新账号首次读取。普通充值中心 GET 与会应用余额的异步操作必须在发起时捕获钱包 owner 生命周期、invalidation 版本和 operation sequence;回包只能结算不晚于该版本的刷新,过期快照不得覆盖余额或中止更新的终态刷新。生成、退款、充值、兑换码等余额可能变化事件只通知 Store 合并刷新。external generation 在 worker 领取后才预扣泥点,因此主站必须在任一项目的 active task 轮询期间持续推动钱包合并刷新,并在 completed / failed 任一终态再刷新以覆盖成功结算或失败退款;任务列表首次 bootstrap 的 active / terminal 任一读取瞬时失败时必须有界退避重试,任一分支成功结果都应立即保留,成功取得全局 active ID 后再交给常规轮询,不能因 terminal 分支失败而清空 active ID、停止钱包通知。公共泥点资产入口收起态展示“泥点图标 + 泥点总额 | 充值”;桌面端通过 hover / focus 展开,移动端通过点击展开。展开态只展示不限时泥点、每日免费泥点及后端返回的每日重置额度,并提供“使用详情”入口;余额都以后端充值中心 read model 为准,前端不得自行相减推算。
  2. 账户充值弹窗标题统一为“购买更多泥点”,当前版本只展示泥点商品,不展示会员页签、会员商品、购买会员或升级会员入口。底层会员数据与周期刷新能力继续保留用于存量兼容和结算,会员周期限时泥点不在当前版本前台展示。
  3. 泥点默认商品固定为四档:60 泥点 / ¥6180 + 90 泥点 / ¥18300 + 150 泥点 / ¥30680 + 340 泥点 / ¥6860 档不加赠,后三档首次购买各加赠基础泥点的 50%;实际展示、下单校验和支付确认仍以后端返回的充值商品配置为准。
  4. 首充加赠资格按泥点商品档位独立计算。用户买过 points_180 后,只影响 points_180 的首充展示和结算,其它未购买档位仍保留各自首充加赠资格。
  5. 前端不得用 hasPointsRecharged 统一隐藏所有泥点档位首充权益;该字段只表示账号是否发生过任一泥点充值。
  6. 充值支付渠道只允许由设备平台隔离层解析为 wechat_mpwechat_mp_virtualwechat_jsapiwechat_h5wechat_native;生产真实支付不得默认落到 mock,缺失或未知 paymentChannel 必须拒绝。
  7. 小程序 WebView 充值使用 wechat_mp_virtual 调起小程序虚拟支付;微信内浏览器使用 wechat_jsapi 调起微信支付 JSAPI;普通 Web 使用 wechat_native 二维码支付,避免因移动 UA、触控能力或窄屏误入 wechat_h5。只有微信通知或查单确认 SUCCESS 后才刷新余额或会员状态。一次充值从下单、宿主 / JSAPI / H5 / Native 调起到查单确认与 watch 必须始终携带同一个 ownerUserId + account lifecycle revisionpending / confirming order、二维码、提交状态、错误结果、成功回调以及清 token、重新登录等认证副作用在每次写入前都必须校验该令牌,账号切换或卸载后旧链路不得再影响新账号。
  8. 后端必须按 access JWT 中的最小设备快照拦截真实微信充值路径,不能只依赖前端隐藏入口或请求体传入的 paymentChannel。前端下单入口还必须使用同步 operation token 防止同一 React 提交周期内重复创建订单;充值下单、邀请码兑换和奖励码兑换必须绑定同一个账号生命周期 AbortSignal,切号或卸载时先中止旧 signal,禁止 POST 的 401、503 或网络重试重新读取新账号 Token。共享 access token refresh 还必须按账号认证代际与发起时 token 快照隔离:旧代际成功回包不得发布 token,旧代际失败不得清理新账号 token,新账号不得复用旧账号的在途 refresh Promise。账号切换时,充值、账单、邀请码中心、邀请码输入、弹窗和在途读取结果都必须按账号生命周期整体失效。
  9. 后台“充值商品”页继续维护泥点和会员商品配置,保存后影响新的充值中心快照、下单和支付确认;历史订单保留下单时快照。会员商品配置保留不表示当前版本开放公开购买或升级入口。

唯一后端路线

当前后端唯一有效路线:

server-rs + Axum + SpacetimeDB

职责边界:

  • api-serverHTTP / SSE / BFF 门面和外部副作用编排。
  • spacetime-moduleSpacetimeDB 表、reducer、procedure、事务 adapter 和 row mapper。
  • spacetime-client:后端访问 SpacetimeDB 的 typed facade。
  • module-*:纯领域模型、命令、应用规则、领域事件和领域错误。
  • platform-*:OSS、LLM、认证、语音等外部平台能力。
  • shared-contracts / packages/shared:前后端 DTO、公开契约和跨页面复用的无业务真相 TypeScript 代码;packages/shared 可承载共享 UI 组件与纯工具,但不承载领域规则、后端副作用或正式状态。
  • 前端:表现、交互、临时 UI 状态和后端结果渲染。

明确废弃:

  • server-node、Express、PostgreSQL 正式后端路线。
  • Go 服务端试验路线。
  • maincloud / Maincloud / MAINCLOUD 脚本、环境变量、测试和文档口径。
  • 除 CI/CD 脚本内部受控用法外,人工命令中的 spacetime --root-dir
  • 前端绕过后端投影或后端 API 自行承接正式业务真相。

UI 与交互约束

  1. 移动端优先,同时保证桌面端可用。
  2. UI 面板不要默认写功能说明、规则解释、操作教程类文案。
  3. 点击按钮弹出独立面板时,必须弹出 dialog / drawer / modal,不要在当前面板下方展开内容。
  4. 优先复用现有系统、页面、组件和弹层,不因一次需求新建平行系统。
  5. 游戏式页面要防止文字、按钮、HUD、底部 dock、输入法和画布互相遮挡。
  6. 平台根壳已处理移动端输入法聚焦:输入法弹出时保持画布稳定高度,只记录键盘状态、隐藏底部 dock 并补齐浅色暴露背景,不再全局上移平台壳;业务组件不要重复注册全局键盘适配。
  7. 主站入口已锁定移动端页面级缩放;单个游戏页面不要再重复实现整页缩放锁定。
  8. 图像输入通用 UI 统一走 src/components/common/CreativeImageInputPanel.tsx。外层页面持有业务状态,组件只承担上传卡、预览、参考图缩略图、AI 重绘开关、错误展示和提交按钮。
  9. 现役项目、素材等列表的筛选必须打开独立 dialog / drawer / modal;筛选结果为空时显示空状态,不把筛选内容展开在当前列表下方。
  10. 移动端“我的”页顶部品牌行承载扫码和设置入口,正文按参考图顺序组织为头像 / 昵称 / 陶泥号、三张统计卡、五项常用功能宫格、通用设置入口和法律信息;media/profile/ 中的陶泥素材作为该页图形资产。常用功能宫格固定承载泥点充值、邀请好友、兑换码、玩家社区、反馈与建议;当前只展示四项常驻入口时必须按四列铺满整行,不保留五列网格导致左对齐空位。页面不再提供会员购买 / 升级横幅、每日任务卡片或任务中心入口,也不提供独立存档按钮入口,不在底部保留旧的填邀请码次级入口;主题设置、账号与安全只作为通用设置弹窗下一级入口,不在“我的”页外层单独占行。填邀请码只由邀请链接 query 或其它明确引导打开独立弹窗,不作为“我的”页常驻按钮。
  11. 每日免费泥点由后端独立余额桶承载,基础额度固定为 20,按北京时间每日 00:00 重置。跨业务日退款时,原消费中的每日免费泥点部分叠加到退款当日每日免费桶,当日余额允许超过 20;到下一业务日仍统一失效并重置为 20。主站不得把已隐藏的每日任务入口或 daily_task_reward 文案继续当作每日免费泥点入口。
  12. “我的”页泥点余额、累计游玩、已玩游戏三张统计卡只展示各自标签和值,三个统计 icon 使用小尺寸普通 UI 档位,内容不换行,不在统计区底部展示“更新于”时间;移动端昵称、常用功能和法律信息也应保持 10px14px 的普通 UI 字号区间,避免展示级字号挤压内容。
  13. 移动端“我的”页需要兼容窄屏:头像 / 昵称 / 陶泥号、三张统计卡、五项常用功能和法律信息都必须能在底部固定 TabBar 上方完整滚动露出,不得与底部 dock、刘海 safe-area 或相邻 UI 元素遮挡重叠。
  14. 编辑器画布、生成状态和异步操作的即时反馈必须在复杂或高噪声背景上保持可读:使用高亮文字、深色描边、强阴影或小面积半透明底,不只依赖颜色本身表达状态。
  15. 平台亮色 UI 配色以陶泥儿主视觉为准:暖白 / 米杏底、陶土橙主按钮、深棕正文与浅杏边框;新增界面优先复用 packages/shared/src/theme.css--platform-* 主题变量和平台字体栈、apps/admin-web/src/styles/admin.css 的同系色值,不再引入粉红、蓝绿等独立主色方案;具体全局字体选择器保留在消费端原有层叠位置,避免共享样式导入改变既有视觉。

文案与编码

  1. 代码需要有必要的中文注释,注释解释业务意图或复杂边界,不写空泛重复注释。
  2. 不要擅自把中文文案、剧情、注释或文档改成英文。
  3. 看到中文乱码时,先确认真实编码,不要沿用乱码文本,也不要用英文替换。
  4. PowerShell 5.1 读取或写入文本时必须显式使用 UTF-8。
  5. 非必要不要整文件重写,尤其是包含中文的文件;优先局部补丁。
  6. 修改中文文件后优先执行 npm run check:encoding

Agent 与协作规则

  • Issue tracker 是自托管 Gitea。可用 Gitea UI/API 或 tea CLI;不要用 GitHub gh 或 GitLab glab
  • 默认 triage labelsneeds-triageneeds-infoready-for-agentready-for-humanwontfix
  • CONTEXT.md 是当前领域语言入口;架构决策以本文档和 docs/project-memory/shared-memory/decision-log.md 的最新稳定摘要为准。
  • .codex/ 只保存仓库级 Codex 工具资源,例如 skills、plugins、hooks 和配置模板;团队共享记忆、计划和 TODO 统一放在 docs/project-memory/,不提交个人 Codex 配置、会话、密钥、Token 或本地私密路径。
  • 每次工程修改都应同步更新本目录当前文档;如果产生长期有效知识,再同步 docs/project-memory/shared-memory/

当前文档策略

本目录只保留少量当前文档。旧审计、阶段计划、修复流水账和历史 PRD 中仍有效的编码级口径已融合;没有融合的内容视为历史参考,不再作为实现依据。