This commit is contained in:
2026-04-29 20:56:59 +08:00
parent fb6f455530
commit 730f485f48
200 changed files with 9881 additions and 2221 deletions

View File

@@ -45,7 +45,7 @@
修复:
1.`map_password_entry_error(...)` 中补充 `InvalidPublicUserCode`
2. 返回中文错误文案 `叙世号格式不正确`
2. 返回中文错误文案 `陶泥号格式不正确`
### 3.3 `module-custom-world` 的 `Display` 分支未覆盖新字段错误

View File

@@ -1,8 +1,8 @@
# 资产操作叙世币消耗接入方案
# 资产操作陶泥币消耗接入方案
## 背景
当前叙世币钱包余额、充值流水与邀请奖励已经收口到 `server-rs/crates/spacetime-module/src/runtime/profile.rs`。资产图片生成和作品发布由 Axum API 调用外部模型或写入业务状态SpacetimeDB reducer/procedure 不能直接执行外部网络生成,因此计费需要拆成两层:
当前陶泥币钱包余额、充值流水与邀请奖励已经收口到 `server-rs/crates/spacetime-module/src/runtime/profile.rs`。资产图片生成和作品发布由 Axum API 调用外部模型或写入业务状态SpacetimeDB reducer/procedure 不能直接执行外部网络生成,因此计费需要拆成两层:
- SpacetimeDB 负责钱包余额和流水的原子变更。
- Axum 资产操作服务负责在执行业务资产操作前扣费,并在生成、持久化或发布失败时补偿退款。
@@ -24,13 +24,13 @@
暂不接入以下入口:
- 旧资产工坊角色主形象/动作生成接口:当前仍使用 `asset-tool` 作为兼容归属,无法确认真实用户。
- 手动上传封面:不调用外部生成模型,不消耗叙世币。
- 手动上传封面:不调用外部生成模型,不消耗陶泥币。
- 自定义世界草稿自动补图链路:属于后台补全流程,避免一次用户操作触发多笔不可预期扣费。
- 文本实体、NPC 生成:本次需求聚焦图片资产和发布资产操作,首期只覆盖可明确归属的入口。
## 计费规则
- 每次可计费资产操作消耗 `1`叙世币。
- 每次可计费资产操作消耗 `1`陶泥币。
- 图片生成和作品发布都按资产操作计费;余额不足时禁止继续执行。
- 在调用外部图片生成或发布 mutation 前预扣,余额不足时直接返回业务错误,不继续调用后续资产操作。
- 如果图片生成、远程下载、OSS 写入、资产记录确认或发布 mutation 失败,资产操作服务自动发起同额退款。

View File

@@ -34,7 +34,7 @@ Stage 1 已把 Rust 鉴权快照同步到 SpacetimeDB 的 `auth_store_snapshot`
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `user_id` | `String` | 主键。 |
| `public_user_code` | `String` | 公开叙世号。 |
| `public_user_code` | `String` | 公开陶泥号。 |
| `username` | `String` | 当前账号用户名。 |
| `display_name` | `String` | 展示名。 |
| `phone_number_masked` | `Option<String>` | 脱敏手机号。 |

View File

@@ -37,7 +37,7 @@
- 当前场景的核心任务描述。
- 文本会作为游戏中首次进入某个场景生成章节任务的关键上下文。
- 必须结合场景描述、场景入口钩子、出场角色与 3 幕事件,说明玩家首次进入该场景时要完成什么。
- 世界档案的场景详情页必须直接展示该字段,便于创作者确认每个场景的默认章节任务。
- 世界档案的场景详情页必须直接展示该字段,便于陶泥主确认每个场景的默认章节任务。
### Landmark 生成源字段

View File

@@ -6,33 +6,33 @@
本轮在“我的”页面的“会员充值”入口落地账户充值弹窗,包含两个页签:
1. `叙世币充值`
1. `陶泥币充值`
2. `会员卡充值`
前端只负责展示与发起购买,套餐、价格、赠送规则、会员权益、生效时间、钱包余额与交易流水统一由 `server-rs` 后端返回。当前没有真实支付网关,本轮采用服务端模拟支付成功:创建订单后立即写入余额或会员状态,并返回最新账户中心快照。后续接入真实支付时,只替换订单支付状态推进,不改前端套餐与账户快照 contract。
## 2. 产品规则
### 2.1 叙世币充值套餐
### 2.1 陶泥币充值套餐
| productId | 叙世币 | 金额分 | 徽标 | 说明 |
| productId | 陶泥币 | 金额分 | 徽标 | 说明 |
| --- | ---: | ---: | --- | --- |
| `points_60` | 60 | 600 | 首充双倍 | 首充送60叙世币 |
| `points_180` | 180 | 1800 | 首充双倍 | 首充送180叙世币 |
| `points_300` | 300 | 3000 | 首充双倍 | 首充送300叙世币 |
| `points_680` | 680 | 6800 | 首充双倍 | 首充送680叙世币 |
| `points_1280` | 1280 | 12800 | 首充双倍 | 首充送1280叙世币 |
| `points_3280` | 3280 | 32800 | 首充双倍 | 首充送3280叙世币 |
| `points_60` | 60 | 600 | 首充双倍 | 首充送60陶泥币 |
| `points_180` | 180 | 1800 | 首充双倍 | 首充送180陶泥币 |
| `points_300` | 300 | 3000 | 首充双倍 | 首充送300陶泥币 |
| `points_680` | 680 | 6800 | 首充双倍 | 首充送680陶泥币 |
| `points_1280` | 1280 | 12800 | 首充双倍 | 首充送1280陶泥币 |
| `points_3280` | 3280 | 32800 | 首充双倍 | 首充送3280陶泥币 |
叙世币充值固定为 `¥6 / ¥18 / ¥30 / ¥68 / ¥128 / ¥328` 六个档位。全部档位参与首充双倍:用户历史上没有 `points_recharge` 流水时,本次购买到账叙世币为基础叙世币与等额赠送叙世币之和;已有充值流水后只到账基础叙世币。实际到账叙世币写入交易流水,余额以 SpacetimeDB projection 为准。
陶泥币充值固定为 `¥6 / ¥18 / ¥30 / ¥68 / ¥128 / ¥328` 六个档位。全部档位参与首充双倍:用户历史上没有 `points_recharge` 流水时,本次购买到账陶泥币为基础陶泥币与等额赠送陶泥币之和;已有充值流水后只到账基础陶泥币。实际到账陶泥币写入交易流水,余额以 SpacetimeDB projection 为准。
### 2.2 会员卡套餐
| productId | 类型 | 天数 | 金额分 | 权益 |
| --- | --- | ---: | ---: | --- |
| `member_month` | 月卡 | 30 | 2800 | 免叙世币回合数100每日签到加成0% |
| `member_season` | 季卡 | 90 | 7800 | 免叙世币回合数100每日签到加成100% |
| `member_year` | 年卡 | 365 | 24800 | 免叙世币回合数100每日签到加成210% |
| `member_month` | 月卡 | 30 | 2800 | 免陶泥币回合数100每日签到加成0% |
| `member_season` | 季卡 | 90 | 7800 | 免陶泥币回合数100每日签到加成100% |
| `member_year` | 年卡 | 365 | 24800 | 免陶泥币回合数100每日签到加成210% |
购买会员时,如果当前会员仍有效,则从当前到期时间顺延;如果已过期或从未购买,则从当前服务端时间开始计算。状态只区分 `普通` 与已生效会员,前端不自行推断。
@@ -42,8 +42,8 @@
需要 Bearer JWT。返回
1. 当前叙世币余额、会员状态、到期时间
2. 叙世币套餐与会员套餐
1. 当前陶泥币余额、会员状态、到期时间
2. 陶泥币套餐与会员套餐
3. 会员权益表
4. 最近订单摘要
@@ -64,7 +64,7 @@
1. 校验 `productId`
2. 后端创建已支付订单
3. 叙世币套餐写入钱包余额与流水
3. 陶泥币套餐写入钱包余额与流水
4. 会员套餐写入会员状态
5. 返回最新账户中心快照与订单摘要
@@ -74,15 +74,15 @@
1. “我的”页会员充值按钮打开独立弹窗,不在当前面板下方展开。
2. 弹窗顶部标题为 `账户充值`,右上角关闭。
3. 默认打开 `叙世币充值`,可切换到 `会员卡充值`
3. 默认打开 `陶泥币充值`,可切换到 `会员卡充值`
4. 点击套餐后调用下单接口,按钮进入处理中状态,成功后刷新 `profileDashboard`
5. 弹窗内不写大段说明文案,只保留必要金额、叙世币、会员权益和状态反馈。
5. 弹窗内不写大段说明文案,只保留必要金额、陶泥币、会员权益和状态反馈。
6. 会员卡充值区以套餐卡片优先展示周期、价格和处理状态;移动端单列,桌面端三列,权益表允许横向滚动,避免小屏挤压。
## 5. 验收
1. 普通用户打开弹窗能看到叙世币与会员套餐。
2. 叙世币购买后余额增加,流水来源为 `points_recharge`
3. 首充赠送只在首次叙世币充值时生效。
1. 普通用户打开弹窗能看到陶泥币与会员套餐。
2. 陶泥币购买后余额增加,流水来源为 `points_recharge`
3. 首充赠送只在首次陶泥币充值时生效。
4. 会员购买后会员状态与到期时间立即更新。
5. 移动端弹窗单列可滚动,桌面端接近参考图卡片网格。

View File

@@ -0,0 +1,89 @@
# “我的”资料卡昵称与头像编辑落地说明
日期:`2026-04-29`
## 1. 背景
本次迭代基于 `docs/prd/MY_TAB_PROFILE_IDENTITY_CARD_PRD_2026-04-16.md` 落地,但交互口径有两处收敛:
1. 昵称编辑不进入账号安全弹窗,点击昵称后的编辑按钮直接打开独立轻弹窗。
2. 头像编辑不进入通用资料抽屉,点击头像先选择本地图片,校验通过后进入头像裁剪弹窗。
资料卡仍保持清爽,不展示规则说明型长文案。
## 2. 前端交互
### 2.1 陶泥号复制
1. 点击“我的”页陶泥号后的复制按钮后,按钮文案临时切换为 `已复制`
2. 复制失败时临时切换为 `复制失败`
3. 状态自动恢复为 `复制`
### 2.2 昵称修改
1. 点击昵称右侧编辑按钮打开独立弹窗。
2. 弹窗内只提供昵称输入、取消、保存。
3. 前端先做长度与字符校验:
- `2-20` 个字符。
- 允许中文、英文、数字、下划线。
- 不允许纯空白。
4. 保存调用 `PATCH /api/profile/me`,成功后即时回写 `AuthUiContext.user`
### 2.3 头像上传与裁剪
1. 点击头像触发文件选择。
2. 前端先审核文件:
- MIME 类型仅允许 `image/jpeg``image/png``image/webp`
- 单文件不超过 `5MB`
3. 校验通过后读取为图片,打开裁剪弹窗。
4. 裁剪工具使用正方形裁剪框,支持拖动裁剪区域与缩放图片。
5. 保存时前端输出 `256x256` 的 PNG data URL调用 `PATCH /api/profile/me` 保存为账号头像。
6. 成功后资料卡头像立即展示新图。
## 3. 后端契约
### `PATCH /api/profile/me`
请求:
```json
{
"displayName": "新昵称",
"avatarDataUrl": "data:image/png;base64,..."
}
```
两个字段均可选,但至少提供一个有效字段。
响应:
```json
{
"user": {
"id": "user_00000001",
"publicUserCode": "SY-00000001",
"username": "phone_xxx",
"displayName": "新昵称",
"avatarUrl": "data:image/png;base64,...",
"phoneNumberMasked": "138****8000",
"loginMethod": "phone",
"bindingStatus": "active",
"wechatBound": false
}
}
```
## 4. 存储边界
当前头像先作为裁剪后的 `256x256` data URL 写入认证快照,保证账号资料可立即持久化和恢复。后续若接入 OSS 头像对象,应保持前端裁剪输出不变,只把后端 `avatarUrl` 从 data URL 替换为私有读代理 URL。
SpacetimeDB 正式表 `user_account` 需要增加 `avatar_url: Option<String>`,并在认证快照导入/导出、迁移导入兼容中对齐。
## 5. 验收
1. 创作页已发布作品分享按钮点击后显示 `已复制`
2. “我的”页陶泥号复制按钮点击后显示 `已复制`
3. “我的”页不展示 `手机号``正常` 标签。
4. 昵称编辑成功后,资料卡与顶部账号入口同步新昵称。
5. 非法头像文件不会进入裁剪流程。
6. 裁剪保存成功后,资料卡头像展示裁剪后的图片。

View File

@@ -1,20 +1,20 @@
# 我的 Tab 邀请与玩家社区首期落地方案
更新时间:`2026-04-25`
更新时间:`2026-04-29`
## 目标
在现有“我的”Tab 常用功能区落地三个轻量入口:
1. `邀请好友`:弹出面板展示当前账号绑定的邀请码。
2. `填邀请码`:弹出面板填写邀请码,成功后邀请者与被邀请者各获得 `30` 叙世币。
3. `玩家社区`:弹出面板展示微信群与 QQ 群二维码占位图,后续替换为正式图片。
2. `填邀请码`:弹出面板填写邀请码,成功后邀请者与被邀请者各获得 `30` 陶泥币。
3. `玩家社区`:弹出面板展示微信群与 QQ 群正式二维码图片。
## 后端边界
- 邀请码、邀请关系与奖励发放全部存入 `server-rs/crates/spacetime-module`
- Axum 只做鉴权、参数转发与响应映射,不在 API 层自行计算奖励。
- 前端只读取后端状态与调用提交接口,不做本地加叙世币。
- 前端只读取后端状态与调用提交接口,不做本地加陶泥币。
- 钱包余额继续复用 `profile_dashboard_state.wallet_balance`
- 奖励流水继续复用 `profile_wallet_ledger`,新增来源类型:
- `invite_inviter_reward`
@@ -43,7 +43,7 @@
- 每个用户拥有一个稳定邀请码,首次进入邀请中心时自动生成。
- 用户不能填写自己的邀请码。
- 用户最多填写一个邀请码,成功后不可修改。
- 被邀请者绑定成功后获得 `30` 叙世币。
- 被邀请者绑定成功后获得 `30` 陶泥币。
- 邀请者每天最多获得 `10` 次邀请奖励,超过后关系仍可绑定,被邀请者仍获得奖励,邀请者当次不再加分。
- 每次奖励都写入钱包流水,钱包余额以后端返回为准。
@@ -69,13 +69,13 @@
- `server-rs/crates/spacetime-module` 已新增邀请码与邀请关系表,邀请中心读取和填码绑定均通过 SpacetimeDB procedure 执行。
- `server-rs/crates/api-server` 已挂接 `/api/runtime/profile/referrals/*``/api/profile/referrals/*` 两组路由。
- 前端“我的”Tab 三个快捷入口均打开独立弹窗,玩家社区使用空白二维码占位
- 复制邀请会复制邀请码和邀请链接;填码成功后刷新个人看板叙世币。
- 前端“我的”Tab 三个快捷入口均打开独立弹窗,玩家社区使用 `media/social-media-group/wechat.png``media/social-media-group/qq.png` 两张正式二维码图片
- 复制邀请会复制邀请码和邀请链接;填码成功后刷新个人看板陶泥币。
## 前端交互
- 三个入口继续放在“我的”Tab 常用功能区,不新增页面。
- `邀请好友` 弹窗展示邀请码、复制按钮、邀请链接。
- `填邀请码` 弹窗在未绑定时展示输入框;已绑定时展示短状态。
- `玩家社区` 弹窗展示两个紧凑二维码占位区
- `玩家社区` 弹窗展示两个紧凑二维码图片区,保留微信群与 QQ 群短标签
- 弹窗文案只保留必要标签和短提示,不放长规则说明。

View File

@@ -1,6 +1,6 @@
# 密码登录入口历史落地设计
> 2026-04-25 更新:当前产品策略已调整为“不开放密码注册”。新用户必须通过手机号验证码注册/登录,密码登录只面向已经登录后设置过密码的手机号账号。`POST /api/auth/entry` 只接受 `phone + password`,不支持邮箱、用户名或叙世号登录,也不承担自动建号能力。本文原有“密码自动建号”内容仅作为历史背景保留,当前落地以本更新和 [PASSWORD_LOGIN_CHANGE_RESET_DESIGN_2026-04-24.md](./PASSWORD_LOGIN_CHANGE_RESET_DESIGN_2026-04-24.md) 为准。
> 2026-04-25 更新:当前产品策略已调整为“不开放密码注册”。新用户必须通过手机号验证码注册/登录,密码登录只面向已经登录后设置过密码的手机号账号。`POST /api/auth/entry` 只接受 `phone + password`,不支持邮箱、用户名或陶泥号登录,也不承担自动建号能力。本文原有“密码自动建号”内容仅作为历史背景保留,当前落地以本更新和 [PASSWORD_LOGIN_CHANGE_RESET_DESIGN_2026-04-24.md](./PASSWORD_LOGIN_CHANGE_RESET_DESIGN_2026-04-24.md) 为准。
>
> 2026-04-28 更新:为开发期本地/测试服联调新增服务端环境变量 `GENARRATIVE_DEV_PASSWORD_ENTRY_AUTO_REGISTER_ENABLED`,默认 `false`。仅当该变量显式为 `true` 时,`POST /api/auth/entry` 可对未知手机号用本次密码直接创建账号并登录;默认关闭时仍严格保持未知手机号返回 `401` 的生产语义。该开关不得用于生产环境,也不新增任何前端规则说明文案。
@@ -17,7 +17,7 @@
1. `api-server` 对外只暴露 `phone + password` 的最小接口。
2. `module-auth` 只负责已存在手机号账号的密码校验。
3. 密码入口不创建账号,不接收邮箱、用户名或叙世号。
3. 密码入口不创建账号,不接收邮箱、用户名或陶泥号。
4. 登录成功后与 JWT、refresh cookie 的衔接方式。
## 1.1 当前冻结结论
@@ -239,7 +239,7 @@
1. 未知手机号密码登录返回 `401`,且不创建账号。
2. 已登录手机号账号设置密码后可用 `phone + password` 登录。
3. 同手机号错误密码返回 `401`
4. 邮箱、用户名或叙世号作为密码登录标识返回 `400`
4. 邮箱、用户名或陶泥号作为密码登录标识返回 `400`
5. 登录成功时返回 access token。
6. 登录成功时写回 refresh cookie。
7. `GENARRATIVE_DEV_PASSWORD_ENTRY_AUTO_REGISTER_ENABLED` 默认关闭时行为不变。

View File

@@ -19,7 +19,7 @@
沿用现有 `POST /api/auth/entry`
1. 请求字段固定为 `phone``password`,前端只提交手机号。
2. 后端只按标准手机号归一化后查找账号,不兼容邮箱、用户名、叙世号或历史开发游客标识。
2. 后端只按标准手机号归一化后查找账号,不兼容邮箱、用户名、陶泥号或历史开发游客标识。
3. 手机号不存在时返回 `401`,不创建账号。
4. 手机号存在但未设置密码时返回 `401`
5. 校验成功后签发 access token并写入 refresh cookie。

View File

@@ -0,0 +1,23 @@
# 陶泥产品命名替换落地说明
## 背景
本轮将产品中文展示名从“叙世”调整为“陶泥”,并同步调整平台内三类对外称谓:
- `叙世币` 对外展示为 `陶泥币`
- `叙世号` 对外展示为 `陶泥号`
- `创作者` 对外展示为 `陶泥主`
## 落地边界
1. 前端页面、弹窗、测试断言和后端返回给用户的中文错误文案统一使用新称谓。
2. SpacetimeDB 表字段、Rust/TypeScript contract 字段、流水来源枚举、`points_*` 商品 ID、`public_user_code` 字段名继续保持不变,避免引入数据库迁移和历史数据兼容风险。
3. 公开编号现有 `SY-XXXXXXXX` 格式本轮不迁移,只调整用户可见标签为“陶泥号”;编号格式如需改为新前缀,应另起迁移方案并同步老用户兼容策略。
4. 历史日志、构建产物、第三方依赖和生成绑定不参与本轮文本替换。
## 验收点
1. 首页、登录绑定页、我的页和搜索结果不再展示旧产品名。
2. 钱包、充值、邀请、兑换码、资产计费和拼图道具确认文案统一展示“陶泥币”。
3. 账号公开标识相关错误和搜索空状态统一展示“陶泥号”。
4. 创作相关可见默认称谓使用“陶泥主”。

View File

@@ -2,9 +2,9 @@
## 1. 目标
本轮在现有“我的”资料与钱包 projection 上新增兑换码能力。用户兑换成功后直接增加叙世币余额,写入 `profile_wallet_ledger`,并同步刷新 `profile_dashboard_state.wallet_balance`
本轮在现有“我的”资料与钱包 projection 上新增兑换码能力。用户兑换成功后直接增加陶泥币余额,写入 `profile_wallet_ledger`,并同步刷新 `profile_dashboard_state.wallet_balance`
管理侧本轮只提供后端 API不新增管理后台页面。私有兑换码创建时支持内部 `userId` 与公开叙世号两类输入,后端创建阶段统一解析成内部 `userId` 存储。
管理侧本轮只提供后端 API不新增管理后台页面。私有兑换码创建时支持内部 `userId` 与公开陶泥号两类输入,后端创建阶段统一解析成内部 `userId` 存储。
## 2. 兑换码类型
@@ -26,7 +26,7 @@
| --- | --- | --- |
| `code` | `String` | 主键,标准化后的兑换码。 |
| `mode` | `RuntimeProfileRedeemCodeMode` | 兑换码模式。 |
| `reward_points` | `u64` | 单次到账叙世币。 |
| `reward_points` | `u64` | 单次到账陶泥币。 |
| `max_uses` | `u32` | 公共码为单用户上限,唯一码/私有码为全局上限。 |
| `global_used_count` | `u32` | 全局已使用次数。公共码也记录总使用次数,但不参与公共码上限判断。 |
| `enabled` | `bool` | 是否启用。 |
@@ -42,7 +42,7 @@
| `usage_id` | `String` | 主键,格式 `redeem:{code}:{user_id}:{micros}:{sequence}`。 |
| `code` | `String` | 兑换码。 |
| `user_id` | `String` | 兑换用户。 |
| `amount_granted` | `u64` | 到账叙世币。 |
| `amount_granted` | `u64` | 到账陶泥币。 |
| `created_at` | `Timestamp` | 兑换时间。 |
索引:`code``user_id``(code, user_id)`
@@ -121,7 +121,7 @@
“我的”页头像右侧入口由 `会员充值` 改为 `兑换码`。点击打开独立模态窗口,窗口内只保留输入框、兑换按钮和后端返回提示,不展示兑换规则说明。
成功后展示 `已到账 X 叙世币`,并刷新 profile dashboard。失败后直接展示后端 `message`
成功后展示 `已到账 X 陶泥币`,并刷新 profile dashboard。失败后直接展示后端 `message`
## 8. 测试矩阵

View File

@@ -2,7 +2,7 @@
## 1. 背景
当前前端展示的“叙世号”由前端基于 `AuthUser.id` 临时拼装:
当前前端展示的“陶泥号”由前端基于 `AuthUser.id` 临时拼装:
- 前缀固定为 `SY-`
-`user.id``username` 去除非字母数字字符后的末 8 位
@@ -174,7 +174,7 @@
1. `id` 返回内部 ID 仅供当前工程内部跳转与资源读取使用,不在 UI 上直接暴露为文案
2. 不返回手机号、登录方式、绑定状态、tokenVersion 等敏感字段
3. 未命中返回 `404`
4. `by-id` 仅接受内部 `user_XXXXXXXX` 这类用户 ID用于工程内跳转、运营排查或已有资源引用不替代公开叙世号主搜索语义
4. `by-id` 仅接受内部 `user_XXXXXXXX` 这类用户 ID用于工程内跳转、运营排查或已有资源引用不替代公开陶泥号主搜索语义
## 5.2 广场作品公开编号搜索
@@ -251,7 +251,7 @@
## 7.1 账号展示
当前首页资料卡和桌面顶部都展示前端拼装叙世号,改为:
当前首页资料卡和桌面顶部都展示前端拼装陶泥号,改为:
1. 直接展示 `authUi.user.publicUserCode`
2. 复制按钮复制后端返回值
@@ -262,7 +262,7 @@
广场作品卡和详情页增加:
1. 作品号 `CW-XXXXXXXX`
2. 作者叙世`SY-XXXXXXXX`
2. 作者陶泥`SY-XXXXXXXX`
展示要求:
@@ -284,7 +284,7 @@
用户搜索命中后的最小行为:
1. 打开独立用户搜索结果面板或对话框
2. 展示头像字母、显示名、叙世
2. 展示头像字母、显示名、陶泥
3. 提供“查看该作者作品”入口
作品搜索命中后的行为:
@@ -325,7 +325,7 @@
## 11. 当前落地说明
1. 首页叙世号展示已优先读取后端 `publicUserCode`,原本基于 `AuthUser.id/username` 的前端拼装仅保留为兼容兜底,避免老会话未刷新时界面直接空白。
1. 首页陶泥号展示已优先读取后端 `publicUserCode`,原本基于 `AuthUser.id/username` 的前端拼装仅保留为兼容兜底,避免老会话未刷新时界面直接空白。
2. 用户公开搜索与广场作品公开搜索均已改为调用后端匿名接口,前端只负责输入、展示与跳转,不再自行决定最终编号格式。
3. 自定义世界发布链路已改为从认证服务读取真实 `public_user_code` 写入作品真相与广场读模型,不再从内部 `user_id` 临时反推 `SY-XXXXXXXX`
4. 当前作品号 `public_work_code` 仍采用基于 `profile_id` 的稳定 fallback 方案生成 `CW-XXXXXXXX`;若后续补独立计数表,需要在不改变读写接口的前提下替换生成来源。

View File

@@ -201,7 +201,7 @@ Rust DTO 只承载对前端公开的 HTTP contract不直接泄露 `module-puz
1. 每次生成 2 张候选图。
2. 候选图通过 `api-server` 写入 OSS兼容展示路径统一为 `/generated-puzzle-assets/...`,禁止再落到仓库 `public/` 目录。
3. Axum 把候选图 URL、assetId、prompt snapshot 回写到 Spacetime session draft。
4. 创作者在结果页选择其中 1 张作为正式图。
4. 陶泥主在结果页选择其中 1 张作为正式图。
这样可以保证:
@@ -211,7 +211,7 @@ Rust DTO 只承载对前端公开的 HTTP contract不直接泄露 `module-puz
### 6.1 发布前编辑真相补充
结果页允许创作者在发布前直接编辑:
结果页允许陶泥主在发布前直接编辑:
1. `关卡名`
2. `摘要`

View File

@@ -0,0 +1,58 @@
# 拼图填表式创作流程改造 2026-04-29
## 背景
拼图创作入口不再使用 Agent 对话收集题材锚点。新流程只让玩家填写两个字段:拼图标题、画面描述。画面描述支持上传参考图。玩家确认后直接进入草稿生成进度页,后续草稿生成、首图生成、正式图选择、结果页编辑和发布沿用现有后端编排。
## 入口表单
1. 拼图标题为必填字段,保存到 `seedText`,同时作为 `levelName` 的优先来源。
2. 画面描述为必填字段,保存到 `pictureDescription`,同时作为 `summary` 和首图生成 prompt 的优先来源;支持多行文本,后端解析不得截断首行之后的内容。
3. 参考图为可选字段,保存到 `referenceImageSrc`。表单支持本地图片上传为 Data URL草稿首图生成时直接传入现有拼图图生图接口。
4. 表单确认后前端先创建拼图 session再立即执行 `compile_puzzle_draft`,并传入 `promptText = pictureDescription``referenceImageSrc`
5. 表单提交 payload 需要在前端创作流程中暂存,生成进度页失败重试时必须继续携带同一份画面描述与参考图。
6. 入口不再展示拼图 Agent 聊天气泡、快捷补齐或多锚点卡片;新建拼图时必须清空旧 session只有从当前生成进度页返回表单时保留本轮内容。
## 锚点映射
拼图模式锚点收口为两个玩家输入源:
| 新字段 | 落地字段 | 说明 |
| --- | --- | --- |
| 拼图标题 | `themePromise.value``levelName``creatorIntent.themePromise` | 作为题材承诺与关卡名称的真相源 |
| 画面描述 | `visualSubject.value``summary`、首图 `promptText` | 作为画面主体与生图 prompt 的真相源 |
兼容旧结构时仍保留 `visualMood``compositionHooks``tagsAndForbidden` 字段,但它们不再由 Agent 问答收集:
1. `visualMood` 固定标记为系统推断,值为“清晰、适合拼图切块”。
2. `compositionHooks` 固定标记为系统推断,值为“主体轮廓、色块分区、局部细节”。
3. `tagsAndForbidden` 根据拼图标题和画面描述生成 3 到 6 个题材标签;禁忌只保留通用图像约束,不写入 UI。
生成进度页的“当前拼图信息”只展示玩家输入锚点:拼图标题、画面描述。题材标签仅作为草稿结果页内容展示,不在进度页混入旧五锚点结构。
## 后端编译
1. `CreatePuzzleAgentSessionRequest` 新增 `pictureDescription``referenceImageSrc`,但不改 SpacetimeDB 表结构。
2. api-server 创建 session 时把标题和画面描述合成 `seedText` 传入 SpacetimeDBSpacetimeDB reducer 只做确定性锚点生成,不接触图片或外部服务。
3. `compile_puzzle_draft_with_initial_cover` 新增首图 prompt 和参考图参数。若前端传入画面描述,则首图生成直接使用这段文本;若传入参考图,则走现有 DashScope 图生图链路。
4. 图片生成仍在 api-server 内完成,遵守 SpacetimeDB reducer 不做网络 I/O 的约束。
## 结果页
拼图草稿结果页不再区分 Tab合并为一个可滚动列表页内容顺序固定为
1. 关卡名称。
2. 画面预览。
3. 画面描述。
4. 重新生成画面按钮。
5. 题材标签。
画面描述区域不再展示候选图实际 prompt 或“请生成一张适合……”之类内部提示词模块。参考图入口保留在画面描述编辑区域内,便于重新生成时继续带入。结果页编辑画面描述时必须同步更新 `summary`,确保自动保存、作品测试、发布和重新生成画面使用同一份描述。
## 验收
1. 从拼图创作入口只能看到标题、画面描述和参考图上传,不出现 Agent 聊天输入、补齐设定、锚点问答。
2. 点击确认后进入拼图草稿生成进度页,并自动完成草稿编译、首图生成、正式图选择。
3. 首图生成请求使用玩家画面描述作为 prompt上传参考图时走图生图。
4. 结果页为单列表,顺序符合上文要求,不展示 Tab 和内部实际 prompt。
5. 发布、作品测试、自动保存标题、画面描述和标签仍可用。

View File

@@ -0,0 +1,42 @@
# 拼图图片生成与运行时 9:16 对齐 2026-04-29
## 背景
拼图生成图和运行时画面需要统一为竖屏游戏口径。此前链路里存在两类不一致:
1. 旧方案按 `1:1` 正方形生成与承载。
2. 上一轮误按 `16:9` 横版对齐,和本轮竖屏玩法目标相反。
本次统一为 `9:16` 竖屏尺寸,确保生成图、结果页预览、发布正式图、历史素材缩略和实际游戏棋盘使用同一画面比例。
## 落地结论
### 1. 图片生成
1. 拼图生成图固定使用 `720*1280`
2. 文生图和参考图生图共用同一个尺寸常量,禁止一条链路仍生成正方形或横版图。
3. 拼图图片提示词明确写入 `9:16 竖屏画布`,并继续保留 `3x3 或 4x4 拼图切块`、主体清晰、层次明确、无文字水印等约束。
4. 图片生成仍由 `api-server` 执行。SpacetimeDB reducer 只负责 session、draft、candidate、work profile 的确定性落库,不做网络 I/O。
### 2. 结果页与素材选择
1. 画面预览容器使用 `aspect-[9/16]`
2. 发布弹窗正式图使用 `aspect-[9/16]`
3. 历史拼图素材卡片缩略图使用 `aspect-[9/16]`
4. 图片显示继续使用 `object-cover`,兼容历史正方形或横版素材,但新生成素材的真相比例为 `9:16`
### 3. 运行时棋盘
1. `PuzzleRuntimeShell` 继续作为唯一运行时承载组件,不新增页面。
2. 棋盘根容器使用 `aspect-[9/16]`并显式设置行列网格3x3 / 4x4 都在竖屏舞台内切片。
3. 棋盘最大宽度按可用视口高度反推,避免桌面端竖屏棋盘被宽容器撑出首屏。
4. 单格不设置固定最小高度,避免移动端竖屏棋盘被单格高度撑破。
5. 拼图片背景切片仍按 `board.cols * 100%``board.rows * 100%` 计算,比例由棋盘容器统一决定。
## 验收
1. 点击拼图草稿生成或重新生成画面时,后端请求 DashScope 的 `size``720*1280`
2. 结果页画面预览、发布弹窗正式图、历史素材缩略图均为 `9:16`
3. 进入拼图运行时后,棋盘整体为 `9:16` 竖屏,不再是正方形或横版。
4. 移动端和桌面端运行时棋盘不被单格最小高度撑出首屏,顶部标题、底部状态与棋盘不重叠。
5. 旧正方形或横版素材仍能被 `object-cover` 展示和游玩,不阻断历史作品。

View File

@@ -4,7 +4,7 @@
拼图结果页此前存在两个串联问题:
1. 创作者在结果页修改 `关卡名`、新增标签、删除标签,只会改前端本地 `editState`,不会立即写回拼图作品 profile。
1. 陶泥主在结果页修改 `关卡名`、新增标签、删除标签,只会改前端本地 `editState`,不会立即写回拼图作品 profile。
2. 发布弹窗同时混用了旧 session 内的 `publishReady` 与前端本地编辑态,导致标签已经在界面里补够,但发布校验仍然盯着旧草稿里的标签数量,用户无法通过发布检验。
这会直接破坏拼图创作主链的可用性:用户明明已经在结果页补齐正式标签,却因为没有自动保存、也没有按当前编辑态重算门槛而卡在发布前。

View File

@@ -0,0 +1,45 @@
# 拼图运行态 `run_json` 计时字段兼容修复 2026-04-29
## 背景
作品详情页点击“启动”时Rust API 通过 SpacetimeDB `start_puzzle_run` procedure 拿到字符串化的 `run_json`,再在 `spacetime-client` 映射层反序列化为拼图运行态快照。
本次线上报错为:
```text
puzzle run run_json 非法: missing field `started_at_ms`
```
说明主云 procedure 已成功返回快照,但返回的 JSON 仍可能是旧字段集,没有带上后续限时与排行榜迭代新增的计时字段。
## 根因
`PuzzleRuntimeLevelSnapshot` 在早期 PRD 中只包含关卡基础信息、棋盘和状态。后续版本新增:
1. `started_at_ms`
2. `cleared_at_ms`
3. `elapsed_ms`
4. `time_limit_ms`
5. `remaining_ms`
6. `paused_accumulated_ms`
7. `pause_started_at_ms`
8. `freeze_accumulated_ms`
9. `freeze_started_at_ms`
10. `freeze_until_ms`
11. `leaderboard_entries`
其中部分字段已经有 `serde(default)`,但 `started_at_ms``cleared_at_ms``elapsed_ms``leaderboard_entries` 仍按必填字段解析。只要主云旧模块或历史快照缺少这些字段API facade 就会在映射层失败,导致详情页启动中断。
## 修复口径
本次只做后端兼容,不改表结构,不改前端表现:
1. `module-puzzle` 的运行态快照新增字段统一允许缺省。
2. 旧 JSON 缺 `started_at_ms` 时,用当前毫秒时间作为兼容起点,保证前端倒计时不会从 `0` 时间戳开始。
3. 旧棋盘缺 `all_tiles_resolved` 时按 `false` 处理。
4. 旧 run / level 缺 `leaderboard_entries` 时按空榜单处理。
5. `spacetime-client` 增加回归测试,确保 `run_json` 缺新增计时字段仍能启动。
## 经验结论
`procedure -> run_json/items_json -> client record` 这类链路只要返回字符串化聚合快照,新增字段就必须默认具备向后兼容能力。平台入口级操作不应因为单个新增字段缺失直接 500能安全补默认值的字段应在服务端契约层统一兜底。

View File

@@ -0,0 +1,108 @@
# 拼图运行时限时与道具系统设计 2026-04-29
## 背景
拼图运行时从纯粹的无压解谜升级为限时关卡,需要同时补齐三类体验:
1. 不同难度有明确倒计时,超时即失败。
2. 底部固定 3 个轻量道具:提示、查看原图、冻结时间。
3. 道具使用必须经过确认弹窗并消耗 `1` 陶泥币,确认弹窗期间暂停关卡计时。
本设计只处理拼图运行时,不改拼图创作链、发布链和广场推荐链。
## 运行态字段
`PuzzleRuntimeLevelSnapshot` 增加以下字段:
1. `timeLimitMs`:当前关卡限时。
2. `remainingMs`:后端或本地运行态计算出的剩余时间。
3. `pausedAccumulatedMs`:已累计暂停时长。
4. `pauseStartedAtMs`:当前是否处于暂停中;有值表示暂停开始时间。
5. `freezeUntilMs`:冻结时间道具生效截止时间;冻结期间倒计时不减少。
`status` 增加 `failed`。当 `remainingMs <= 0` 且关卡尚未通关时,状态进入 `failed`,后续交换、拖动、排行榜提交都拒绝。
## 难度限时
第一版按网格规模定义限时:
1. `3x3``180000ms`
2. `4x4``300000ms`
后续若扩展更多难度,只能通过同一个难度解析函数扩展,不允许在 UI 里写死另一套时间。
## 计时规则
有效消耗时间计算:
```text
effectiveElapsedMs = nowMs - startedAtMs - pausedAccumulatedMs - activeFreezeElapsedMs
remainingMs = max(0, timeLimitMs - effectiveElapsedMs)
```
其中:
1. 弹窗打开、设置面板打开、查看原图覆盖打开时,运行态需要暂停。
2. 冻结时间生效时,画面播放冻结特效,并展示冻结剩余时长。
3. 通关时 `elapsedMs` 使用有效消耗时间,不把确认弹窗、查看原图和冻结时间计入成绩。
4. 失败后保留棋盘,不弹通关结算。
5. 正式后端 run 的前端倒计时归零时,需要主动刷新一次 `getPuzzleRun`,让 SpacetimeDB 侧把 `failed` 状态写回快照,避免只停留在本地视觉失败。
## 道具规则
### 提示
提示道具只演示,不替玩家移动。
演示对象选择:
1. 优先选当前棋盘中拼块数量最多、且尚未完全处于正确位置的合并块。
2. 若没有合并块,选择一个不在正确格子的单块。
3. 演示从当前所在格移动到该块锚点的正确格,结束后回到原位。
### 查看原图
查看原图是开关按钮:
1. 打开后把原图以半透明方式覆盖在拼图棋盘上。
2. 覆盖期间暂停倒计时;确认弹窗关闭到覆盖层显示之间不得恢复计时,正式后端 run 也需要保持 `pauseStartedAtMs`
3. 再次点击关闭覆盖并恢复计时。
### 冻结时间
冻结时间确认后:
1. 播放冻结视觉特效。
2. 显示冻结剩余时长。
3. 第一版冻结 `10000ms`
## 计费规则
每次确认使用道具消耗 `1` 陶泥币。
正式后端运行态复用现有资产操作钱包预扣链路,新增道具 `asset_kind`
1. `puzzle_prop_hint`
2. `puzzle_prop_preview`
3. `puzzle_prop_freeze_time`
本地调试 run 没有真实用户钱包,不伪造扣费,只保留同样的确认交互与运行态效果。
若扣费或道具过程失败,确认弹窗保持打开并继续暂停倒计时,在弹窗内展示失败原因;只有成功确认后才关闭弹窗并播放对应反馈。
## UI 规则
1. 底部只放 3 个道具按钮,不写规则说明文案。
2. 点击道具弹出独立确认窗口,不在底栏下方展开。
3. 确认窗口打开期间暂停计时。
4. 按钮使用图标和短标签;不可用时降低透明度。
5. 失败状态使用简洁弹窗展示,可返回或重新开始,不与通关结算混用。
## 画布表现修正
本轮同步修正合并块视觉:
1. 合并块之间不再使用额外 `p-1` 缝隙,拼图块需要贴合。
2. 单块和大块使用同一套边界描边宽度与颜色。
3. 外轮廓和凹入转角都需要圆角化。
4. 新合并产生时,在新大块中心播放一次简洁闪光,不显示文字提示。

View File

@@ -4,6 +4,13 @@
## 文档列表
- [RPG_HOME_CUSTOM_WORLD_LIBRARY_TIMEOUT_FIX_2026-04-29.md](./RPG_HOME_CUSTOM_WORLD_LIBRARY_TIMEOUT_FIX_2026-04-29.md):记录首页 `custom-world-library` 首屏列表 SpacetimeDB procedure 超时的根因,冻结列表读模型轻量化与 procedure 等待窗口配置化的修复口径。
- [PRODUCT_NAMING_TAONI_RENAME_2026-04-29.md](./PRODUCT_NAMING_TAONI_RENAME_2026-04-29.md):记录本轮产品中文名调整为“陶泥”,以及陶泥币、陶泥号、陶泥主三类对外称谓替换的落地边界。
- [PUZZLE_FORM_CREATION_FLOW_2026-04-29.md](./PUZZLE_FORM_CREATION_FLOW_2026-04-29.md):记录拼图创作入口从 Agent 对话改为标题与画面描述填表、参考图直达首图生成,以及结果页合并为单列表的落地边界。
- [PUZZLE_IMAGE_AND_RUNTIME_9_16_ALIGNMENT_2026-04-29.md](./PUZZLE_IMAGE_AND_RUNTIME_9_16_ALIGNMENT_2026-04-29.md):记录拼图生成图片、结果页预览、历史素材缩略和运行时棋盘统一为 9:16 竖屏的落地边界。
- [RPG_NPC_BATTLE_ENTRY_QUEST_AND_TARGET_FIX_2026-04-29.md](./RPG_NPC_BATTLE_ENTRY_QUEST_AND_TARGET_FIX_2026-04-29.md):记录 NPC 进入战斗时不再自动补章节任务、pending 委托不被误接取,以及战斗目标缺少 encounter 时仍可渲染的修复边界。
- [RPG_RUNTIME_PANEL_CLOSE_BUTTON_FIX_2026-04-29.md](./RPG_RUNTIME_PANEL_CLOSE_BUTTON_FIX_2026-04-29.md):记录 RPG 运行态历史手写弹窗右上关闭按钮点击失效的统一修复边界,收口像素风关闭按钮的事件传播、层级和点击面积。
- [RPG_RUNTIME_PARTY_INVENTORY_PANEL_UI_SIMPLIFICATION_2026-04-29.md](./RPG_RUNTIME_PARTY_INVENTORY_PANEL_UI_SIMPLIFICATION_2026-04-29.md):记录 RPG 运行态队伍面板删除成员列表上方任务信息、背包面板删除顶部旅程回顾的展示边界,保持辅助面板首屏聚焦成员与物品。
- [RPG_PROMPT_FRONTEND_REMOVAL_AND_SERVER_RS_MIGRATION_2026-04-28.md](./RPG_PROMPT_FRONTEND_REMOVAL_AND_SERVER_RS_MIGRATION_2026-04-28.md):冻结 RPG 提示词禁止存在前端的边界,明确前端只保留 API client角色私聊/NPC 对话/剧情续写等 prompt 统一收口到 `server-rs`
- [RPG_CREATION_RESULT_VIEW_BACKEND_TRUTH_MIGRATION_2026-04-28.md](./RPG_CREATION_RESULT_VIEW_BACKEND_TRUTH_MIGRATION_2026-04-28.md):冻结 RPG 创作结果页保存、Agent session/result preview 真相优先级和结果页入口裁决迁移到后端 result-view 的落地边界。
- [RPG_CREATION_PROFILE_GENERATION_BACKEND_MIGRATION_2026-04-28.md](./RPG_CREATION_PROFILE_GENERATION_BACKEND_MIGRATION_2026-04-28.md):记录 RPG 创作 profile 生成移除非浏览器 legacy AI 回退,统一通过 `server-rs``/api/runtime/custom-world/profile` 生成世界底稿。

View File

@@ -0,0 +1,50 @@
# RPG 首页自定义世界库超时修复
日期:`2026-04-29`
## 1. 问题
首页进入时会并发读取:
1. `GET /api/runtime/custom-world-library`
2. `GET /api/runtime/custom-world-gallery`
3. 个人看板、浏览历史、存档等私有数据
其中 `custom-world-library` 通过 `api-server -> spacetime-client -> list_custom_world_profiles procedure` 读取当前用户作品。旧实现把每个作品的完整 `profile_payload_json` 一并返回给首页列表,而首页卡片只需要标题、摘要、封面、状态和计数字段。用户作品较多或 Maincloud 连接抖动时,这个 procedure 容易超过 `spacetime-client` 固定 `10s` 等待窗口,最终由 Axum 映射成 `502 Bad Gateway`,前端控制台显示 `SpacetimeDB procedure 调用超时`
## 2. 修复口径
本轮不改表结构,不新增前端展示规则,只收窄首屏读模型负载:
1. `list_custom_world_profiles` 仍保持旧 procedure 名称和返回 envelope避免本轮重新生成 bindings。
2. 列表返回的 `profile_payload_json` 改为轻量摘要 JSON只包含首页卡片和标签兜底需要的少量字段。
3. 单条详情、发布、下架、编辑继续使用完整 profile snapshot确保进入详情或结果页时仍有完整世界数据。
4. `spacetime-client` 的 procedure 等待窗口从硬编码 `10s` 改为可配置Maincloud 默认使用更宽的窗口吸收连接冷启动与短时抖动。
5. Axum 的 `GET /api/runtime/custom-world-library` 首屏接口改走已有 `custom-world/works` 轻量读模型,并在用户点击详情/编辑时再调用 owner-only detail 接口取完整 profile避免 Maincloud wasm 尚未发布轻量 profile procedure 时首页继续命中重 procedure。
## 3. 轻量 profile JSON 字段
列表轻量 profile 只保留:
1. `id`
2. `name`
3. `subtitle`
4. `summary`
5. `themeMode`
6. `cover.imageSrc`
7. `majorFactions`
8. `coreConflicts`
9. `playableNpcs`
10. `storyNpcs`
11. `landmarks`
这些字段足够支撑首页卡片的封面、标签、数量和基本文案。服务端列表兜底允许把 `majorFactions``coreConflicts``playableNpcs``storyNpcs``landmarks` 返回为空数组,并依赖 entry 顶层的计数字段、封面和主题兜底展示。需要完整 profile 的操作必须走 detail 或 mutation 回包,不能依赖列表接口搬大 JSON。
## 4. 验收
1. 首页进入不再因为 `custom-world-library` 首屏列表超时直接报 502。
2. `cargo check -p spacetime-module` 通过。
3. `cargo check -p spacetime-client` 通过。
4. `cargo check -p api-server` 通过。
5. `npm run check:encoding` 通过。
6. 修改后按项目约束使用 `npm run api-server:maincloud` 重启后端。

View File

@@ -0,0 +1,26 @@
# RPG NPC 战斗入口任务与目标显示修复记录2026-04-29
## 背景
运行态从 NPC 交互进入战斗后,出现两个连带问题:
1. 玩家没有确认领取任务,但界面表现为突然多了一个任务。
2. 对面的 NPC 进入战斗后从画布上消失。
## 根因
1. `project_story_engine_after_action` 会在动作后自动补齐当前场景章节任务。这个规则适合“进入/探索场景”的开章节点,但不适合 `npc_fight / npc_spar` 战斗入口;否则玩家点击战斗也会像被系统强行塞入任务。
2. `resolve_npc_battle_entry_action` 进入战斗时会清空 `currentEncounter`,并改由 `sceneHostileNpcs` 承接敌方渲染。若进入战斗前已有 `sceneHostileNpcs` 但条目缺少 `encounter`,画布层会因为没有 NPC 形象上下文而跳过渲染。
## 落地边界
1. `npc_fight / npc_spar` 只负责进入战斗,不创建章节任务,不接取 NPC pending quest。
2. 场景章节任务仍保留在真正的场景进入、观察、推进节点自动创建。
3. 战斗入口必须保证每一个 `sceneHostileNpcs` 条目都带有可渲染的 `encounter`;若旧数据缺失,使用进入战斗前的当前 NPC encounter 兜底。
4. 前端画布也要兜底渲染缺少 `encounter` 的战斗目标,避免服务端旧快照或迁移数据导致目标直接不可见。
## 验证点
1. `npc_fight` 带 pending quest story 时,不写入 `quests`,不增加 `runtimeStats.questsAccepted`
2. `npc_fight` 时若已有敌方列表缺少 `encounter`,服务端会给战斗目标补齐进入战斗前的 NPC encounter。
3. 画布层在 `sceneCombatants[].encounter` 缺失时仍显示敌方名称和血条。

View File

@@ -0,0 +1,26 @@
# RPG 运行态面板右上关闭按钮修复2026-04-29
## 背景
RPG 运行态里仍有一批历史手写弹窗,没有统一迁入 `UnifiedModal`。这些弹窗的右上关闭按钮分别散落在角色详情、队伍、背包、地图、NPC 交易、任务日志和奖励面板里,按钮尺寸、层级、点击事件传播和无障碍标识不一致。
用户反馈多个 RPG 模板游戏内面板右上角关闭按钮点击无效。排查后,本次先按最小风险方式修复关闭交互边界,不重构业务面板结构。
## 落地方案
1. 新增 `PixelCloseButton` 作为 RPG 像素风面板右上关闭按钮的统一组件。
2. 组件内部统一处理:
- `event.preventDefault()`
- `event.stopPropagation()`
- 稳定 `z-index`
- 固定移动端友好的点击面积;
- `aria-label``title`
3. RPG 游戏内旧弹窗的右上关闭按钮统一替换为 `PixelCloseButton`
4. 保留各面板原本的关闭回调和业务状态清理逻辑,不改变任务、奖励、交易、地图、角色详情等业务行为。
## 验收
1. 点击游戏内面板右上关闭按钮时,只触发该按钮的关闭回调,不被父层遮罩或面板点击处理吞掉。
2. 队伍、背包、地图、角色详情、角色聊天、NPC 交易 / 赠礼 / 招募、任务日志、任务详情、奖励详情等面板的右上关闭按钮可稳定关闭。
3. 关闭按钮具备可检索的无障碍名称,后续可用自动化测试直接定位。
4. 编码检查、定向测试和类型检查通过。

View File

@@ -0,0 +1,19 @@
# RPG 运行态队伍 / 背包面板信息精简2026-04-29
## 背景
运行态队伍与背包都属于冒险过程中的辅助弹出面板,移动端优先要求是快速查看成员状态、物品格子与工坊操作。当前队伍面板在成员列表上方额外展示活跃任务,背包面板在格子上方额外展示旅程回顾,会把首屏焦点从“队伍成员 / 背包物品”推开。
## 落地边界
1. 队伍面板删除成员列表上方的任务信息模块。
2. 背包面板删除物品格子上方的旅程回顾模块。
3. 不删除任务系统、旅程回顾数据或冒险页里的任务提示,只调整这两个辅助面板的展示入口。
4. 父级不再向这两个面板传入已经不展示的字段,避免保留无效 UI 契约。
## 验收
1. 打开队伍面板后,顶部直接进入“队伍成员”列表。
2. 打开背包面板后,顶部直接进入物品格子。
3. 任务状态仍由冒险主面板和任务弹层承担。
4. `continueGameDigest` 数据仍保留在运行态状态中,后续可在更合适的独立入口展示。

View File

@@ -2,7 +2,7 @@
## 背景
世界创作结果页已经提供“作品测试”入口,但测试运行时此前缺少与“幕预览”一致的显式退出按钮。创作者进入测试后只能依赖浏览器返回、刷新或其他间接链路离开,不符合独立运行时面板的交互语义。
世界创作结果页已经提供“作品测试”入口,但测试运行时此前缺少与“幕预览”一致的显式退出按钮。陶泥主进入测试后只能依赖浏览器返回、刷新或其他间接链路离开,不符合独立运行时面板的交互语义。
## 本次约束

View File

@@ -2,7 +2,7 @@
## 背景
幕预览和测试作品用于创作者检查玩法表现,不能被当作玩家正式游玩记录。若这类运行时复用正式 RPG 壳、story action 或 snapshot 接口,必须在进入个人存档页、游玩统计、作品游玩历史前被过滤。
幕预览和测试作品用于陶泥主检查玩法表现,不能被当作玩家正式游玩记录。若这类运行时复用正式 RPG 壳、story action 或 snapshot 接口,必须在进入个人存档页、游玩统计、作品游玩历史前被过滤。
## 落地约束

View File

@@ -10,7 +10,7 @@
1. 草稿层可以承载 `scene chapter / scene act`
2. 后端可以把 `scene_chapter` 编译成正式蓝图
3. 创作者可以在现有场景编辑弹层里看到并编辑多幕配置
3. 陶泥主可以在现有场景编辑弹层里看到并编辑多幕配置
4. 编辑后的幕信息可以正确写回 `sceneChapterBlueprints`
5. 运行时共享层先具备读取幕背景、主角色、相遇 NPC 池的基础能力
6. 当前幕主角色的负好感 `5` 轮聊天限制先形成首个可运行闭环
@@ -60,7 +60,7 @@
前端已完成第一批接入:
1. `scene_chapter` 不再作为独立 Tab / 独立卡片暴露给创作者
1. `scene_chapter` 不再作为独立 Tab / 独立卡片暴露给陶泥主
2. 多幕配置已内嵌到 `CustomWorldEntityEditorModal.tsx``LandmarkEditor`
3. 单幕编辑已从文本表单切成“背景大图预览 + 3 个角色槽位”的轻量交互
4. “幕标题 / 幕摘要 / 幕目标 / 过渡钩子”已从场景手工编辑区移除,继续留在草稿生成与编译层
@@ -88,7 +88,7 @@
7. 幕预览运行时已补 custom world NPC 的视觉兜底链路,优先使用 `visual / imageSrc` 渲染,避免角色形象或动画空白
8. 当前幕小预览已调整为左侧玩家、右侧敌对/相遇角色的构图NPC 站位采用一前两后
前排主角色与玩家角色保持同一 y 轴后排两个角色改为同一列、x 轴对齐并上下分布,且后排整体 y 轴中点与前排主角色一致
9. 新增幕默认只带 1 个主角色,后续槽位由创作者按需补充
9. 新增幕默认只带 1 个主角色,后续槽位由陶泥主按需补充
10. 小预览里的名字已移动到角色头顶,角色渲染不再带方形底板,避免遮挡场景背景
11. 幕预览复用真实游戏壳时隐藏左上角角色等级徽标,退出入口固定在上方画面区域底部居中,并使用“结束预览”作为操作文案
12. 创作侧场景列表封面、多幕配置卡片、配置背景弹层统一读取同一张场景显示图;在任一幕保存背景时同步回全部幕背景字段和场景兼容图,避免同一场景在不同层级出现不同预览图

View File

@@ -409,7 +409,7 @@ Access-Control-Allow-Credentials: true
职责:
- 面向创作者、运营、内部编辑器
- 面向陶泥主、运营、内部编辑器
- 必须鉴权
- 必须审计
- 不建议对公网完全开放
@@ -469,7 +469,7 @@ flowchart TD
当出现这些需求时,再进入下一阶段:
- 多人同时在线
-创作者协作
-陶泥主协作
- 图片/视频生成任务变多
- 需要账号体系、存档、云同步
- 需要审计和版本回滚

View File

@@ -206,7 +206,7 @@
1. 密码登录仍由 `user_account.password_hash` 承担
2. 本轮不引入 `password` provider identity
3. 密码登录只接受已绑定手机号的账号,不支持邮箱、用户名或叙世号作为登录身份
3. 密码登录只接受已绑定手机号的账号,不支持邮箱、用户名或陶泥号作为登录身份
4. 密码登录不创建账号,新账号只由手机号验证码登录创建
### 9.2 `POST /api/auth/phone/login`

View File

@@ -13,6 +13,8 @@ SpacetimeDB reducer 必须保持确定性,不能访问文件系统和网络。
procedure 不再访问 HTTP 文件桥,也不接收部署机本地文件路径。这样可以避开 SpacetimeDB 对 private/special-purpose 地址的 HTTP 访问限制,并避免把 private 表内容通过临时 HTTP 服务转发。
SpacetimeDB Wasm 运行环境不支持 `std::time::SystemTime::now()`procedure 或 reducer 内需要当前时间时必须使用 `ctx.timestamp`。如果共享 crate 同时服务前端/本地纯逻辑与 SpacetimeDB 模块,应提供 `*_at(now_ms)` 或显式时间参数版本SpacetimeDB 模块只调用注入时间的函数,避免发布后在 maincloud 触发 `time not implemented on this platform` panic。
`spacetime login show --token` 输出的是 CLI 登录 token不是 HTTP `/v1/database/.../call` 所需的数据库连接 token。导入脚本如果没有显式传 `--token`,会自动调用 `POST /v1/identity` 获取 Web API token迁移时不要把 CLI token 传给 `--token`
## 接口
@@ -132,6 +134,10 @@ node scripts/spacetime-revoke-migration-operator.mjs \
4. 导出成功后执行清库发布新 wasm。
5. 新 wasm 发布成功后,把第 3 步导出的 JSON 自动导入回灌。
SpacetimeDB 2.1 对 schema 冲突的报错文案可能不再包含 `schema conflict`,而是直接提示 `manual migration``default value annotation``--delete-data`。发布脚本必须把这些文案同样识别为可迁移冲突,否则会停在原始失败而不进入导出回灌流程。
新增字段优先采用低风险热升级策略:旧字段顺序保持不变,新字段追加到表尾,并用 `#[default(...)]` 提供旧行默认值。只有仍无法通过发布器检查时,才执行清库发布与 JSON 回灌。
任一阶段失败都会中止流程,并保留已经导出的迁移 JSON。非 schema 冲突的发布失败不会进入迁移流程。
```bash
@@ -253,7 +259,9 @@ node scripts/spacetime-export-migration-json.mjs \
- 自定义世界:`custom_world_profile``custom_world_session``custom_world_agent_session``custom_world_agent_message``custom_world_agent_operation``custom_world_draft_card``custom_world_gallery_entry`
- 资产索引:`asset_object``asset_entity_binding`
- 拼图:`puzzle_agent_session``puzzle_agent_message``puzzle_work_profile``puzzle_runtime_run`
- 大鱼:`big_fish_creation_session``big_fish_agent_message``big_fish_asset_slot``big_fish_runtime_run`
- 大鱼:`big_fish_creation_session``big_fish_agent_message``big_fish_asset_slot`
`big_fish_runtime_run` 当前运行态已由前端本地运行服务承接,不再加入迁移白名单;但 maincloud 旧库仍可能存在该表。为避免热升级被 “Removing the table big_fish_runtime_run requires a manual migration” 阻断,模块发布期可以保留兼容空壳表,后续确认旧数据可丢弃后再走正式删除表迁移。
后续新增 SpacetimeDB 表时,必须同步把表加入迁移白名单与本文档。

View File

@@ -20,16 +20,16 @@ spacetime sql <db> "SELECT * FROM custom_world_gallery_entry"
## 总览
| 领域 | 表 |
| --- | --- |
| 认证 | `auth_store_snapshot`, `user_account`, `auth_identity`, `refresh_session` |
| 领域 | 表 |
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 认证 | `auth_store_snapshot`, `user_account`, `auth_identity`, `refresh_session` |
| 运行时档案 | `runtime_setting`, `runtime_snapshot`, `user_browse_history`, `profile_dashboard_state`, `profile_wallet_ledger`, `profile_redeem_code`, `profile_redeem_code_usage`, `profile_played_world`, `profile_save_archive` |
| RPG 运行时 | `story_session`, `story_event`, `npc_state`, `inventory_slot`, `battle_state`, `treasure_record`, `quest_record`, `quest_log`, `player_progression`, `chapter_progression` |
| 世界创作 | `custom_world_profile`, `custom_world_session`, `custom_world_agent_session`, `custom_world_agent_message`, `custom_world_agent_operation`, `custom_world_draft_card`, `custom_world_gallery_entry` |
| 拼图 | `puzzle_agent_session`, `puzzle_agent_message`, `puzzle_work_profile`, `puzzle_runtime_run` |
| 大鱼吃小鱼 | `big_fish_creation_session`, `big_fish_agent_message`, `big_fish_asset_slot`, `big_fish_runtime_run` |
| 资产 | `asset_object`, `asset_entity_binding` |
| AI 任务 | `ai_task`, `ai_task_stage`, `ai_text_chunk`, `ai_result_reference` |
| RPG 运行时 | `story_session`, `story_event`, `npc_state`, `inventory_slot`, `battle_state`, `treasure_record`, `quest_record`, `quest_log`, `player_progression`, `chapter_progression` |
| 世界创作 | `custom_world_profile`, `custom_world_session`, `custom_world_agent_session`, `custom_world_agent_message`, `custom_world_agent_operation`, `custom_world_draft_card`, `custom_world_gallery_entry` |
| 拼图 | `puzzle_agent_session`, `puzzle_agent_message`, `puzzle_work_profile`, `puzzle_runtime_run` |
| 大鱼吃小鱼 | `big_fish_creation_session`, `big_fish_agent_message`, `big_fish_asset_slot`, `big_fish_runtime_run` |
| 资产 | `asset_object`, `asset_entity_binding` |
| AI 任务 | `ai_task`, `ai_task_stage`, `ai_text_chunk`, `ai_result_reference` |
## 认证表
@@ -46,8 +46,8 @@ SELECT * FROM auth_store_snapshot WHERE snapshot_id = 'default';
### `user_account`
- 作用:用户账号主表,保存用户名、公开叙世号、手机号掩码、登录方式、密码登录开关和 token 版本。
- 结构:`user_id PK: String`, `public_user_code: String`, `username: String`, `display_name: String`, `phone_number_masked: Option<String>`, `phone_number_e164: Option<String>`, `login_method: String`, `binding_status: String`, `wechat_bound: bool`, `password_hash: Option<String>`, `password_login_enabled: bool`, `token_version: u64`
- 作用:用户账号主表,保存用户名、公开陶泥号、手机号掩码、登录方式、密码登录开关和 token 版本。
- 结构:`user_id PK: String`, `public_user_code: String`, `username: String`, `display_name: String`, `avatar_url: Option<String>`, `phone_number_masked: Option<String>`, `phone_number_e164: Option<String>`, `login_method: String`, `binding_status: String`, `wechat_bound: bool`, `password_hash: Option<String>`, `password_login_enabled: bool`, `token_version: u64`
- 索引:`username`, `public_user_code`
```sql
@@ -135,7 +135,7 @@ SELECT * FROM profile_wallet_ledger WHERE user_id = '<user_id>' ORDER BY created
### `profile_redeem_code`
- 作用:运营发放的叙世币兑换码,支持公共码、唯一码和私有码。
- 作用:运营发放的陶泥币兑换码,支持公共码、唯一码和私有码。
- 结构:`code PK: String`, `mode: RuntimeProfileRedeemCodeMode`, `reward_points: u64`, `max_uses: u32`, `global_used_count: u32`, `enabled: bool`, `allowed_user_ids: Vec<String>`, `created_by: String`, `created_at: Timestamp`, `updated_at: Timestamp`
- 索引:主键 `code`

View File

@@ -39,7 +39,7 @@
- 发布
3. 完整复制外部 TXT 模式的运行机制:
- 玩家游玩会话
- 创作者测试/读档会话
- 陶泥主测试/读档会话
- 流式动作执行
- 文本模式显示
- 历史记录
@@ -99,7 +99,7 @@
- 属性面板
5. 双会话机制:
- 玩家游玩会话
- 创作者测试/读档会话
- 陶泥主测试/读档会话
6. 流式动作接口与事件协议:
- `start`
- `raw_text`
@@ -551,7 +551,7 @@ TXT 模式后续必须完整落地双会话机制:
1. 玩家游玩会话
- 对应外部 `POST /api/optical/games/session/create`
- 用于正式游玩
2. 创作者测试/读档会话
2. 陶泥主测试/读档会话
- 对应外部 `POST /api/visual/session/create`
- 用于测试体验与加载指定存档