- Add invite code starts/expires fields across contracts, API, Spacetime bindings, and admin UI - Enforce pending/expired invite code redemption behavior and expose admin status - Add admin write-operation confirmation guard and documentation - Add invite code contract/runtime tests
9.4 KiB
9.4 KiB
邀请码有效期与后台二次确认实施计划
For Hermes: 按 plan 模式,仅输出并保存实施计划,不直接改业务代码。
Goal: 为邀请码新增开始日期与截止日期,并让后台所有会修改数据的操作在提交前增加二次确认,降低误操作风险。
Architecture:
邀请码仍作为“用户稳定邀请身份码”保留,不做停用删除;在数据层增加 starts_at / expires_at,前台填写邀请码时按时间窗校验,后台列表与编辑页展示状态。后台所有写操作统一先弹二次确认,再真正调用 API,避免对兑换码、邀请码、任务配置等管理动作误触发。
Tech Stack: Rust / SpacetimeDB / Axum / shared-contracts / TS + React 的 admin-web。
当前上下文
- 邀请码当前只有
user_id、invite_code、metadata_json、created_at、updated_at,没有状态字段。 - 目前后台存在邀请码管理入口,但没有停用能力,也没有有效期概念。
- 邀请码用于
redeem_profile_referral_invite_code时的实时校验,适合增加“时间窗”而不是“禁用删除”。 - 后台已存在兑换码、任务配置等可写操作;本次要求把所有后台操作统一加二次确认,包括新增、编辑、禁用、删除等写入口。
设计原则
- 邀请码不做软删除:保留历史记录和邀请链路。
- 有效期由时间窗推导:
starts_at为空表示立即生效。expires_at为空表示长期有效。
- 前台只拒绝新绑定:已绑定关系不回溯修改。
- 后台写操作统一确认:所有会触发 POST / PATCH / DELETE 的管理动作,在真正提交前必须弹出二次确认。
- 尽量少改接口语义:优先在现有 admin upsert/list 体系内扩展字段,而不是新增一套并行 API。
方案概要
邀请码时间窗
新增字段:
starts_at: Option<Timestamp>expires_at: Option<Timestamp>
校验规则:
- 当前时间
< starts_at:返回“邀请码未生效” - 当前时间
>= expires_at:返回“邀请码已过期” - 其他情况允许填写
建议把状态展示为:
- 未生效
- 有效
- 已过期
- 长期有效(两个字段都为空或仅无截止)
后台二次确认
对 admin-web 所有管理动作统一加确认弹窗/对话框,至少覆盖:
- 兑换码新增/更新
- 兑换码停用
- 邀请码新增/更新
- 任务配置新增/更新
- 任务配置停用
- 其他后续新增的后台写操作
确认文案要求:
- 显示对象标识(如 code / inviteCode / taskId)
- 显示操作类型(新增 / 更新 / 停用)
- 明确提醒“该操作会立即影响线上数据”
- 允许取消返回,不调用 API
预期修改文件
1. 服务端领域与契约
server-rs/crates/spacetime-module/src/runtime/profile.rsProfileInviteCode表结构新增开始/截止字段- 邀请码 upsert 逻辑写入时间窗
- 邀请码 redeem 逻辑增加时间窗校验
- 邀请中心快照补充时间窗/状态
server-rs/crates/spacetime-module/src/migration.rs- 兼容旧表数据,给旧邀请码补默认空值
server-rs/crates/shared-contracts/src/runtime*.rs或对应生成/手写契约文件AdminUpsertProfileInviteCodeRequest扩展字段ProfileInviteCodeAdminResponse扩展字段- 如需要,增加时间窗相关状态枚举或派生字段
server-rs/crates/spacetime-client/src/module_bindings/*- 重新生成 bindings
- mapper 补齐新字段
2. API Server
server-rs/crates/api-server/src/runtime_profile.rs- 接收/转发邀请码时间窗参数
- 返回新增字段给后台
- 如需要,调整校验错误文案
server-rs/crates/api-server/src/app.rs- 若有新路由或错误码需挂接,在此统一登记
3. Admin Web
apps/admin-web/src/api/adminApiTypes.ts- 增加邀请码时间窗字段
- 如有需要,增加后台操作请求结构字段
apps/admin-web/src/api/adminApiClient.ts- 透传新的请求/响应字段
apps/admin-web/src/app/adminRoutes.ts- 不一定需要改,但如果新增独立页面/子面板,需要在此登记
apps/admin-web/src/styles/admin.css- 确认弹窗与时间窗展示样式
apps/admin-web/src/**实际管理页面组件- 邀请码编辑表单
- 邀请码列表状态展示
- 所有写操作前的二次确认弹窗
4. 文档
docs/technical/或docs/design/- 补一份邀请码时间窗与后台确认交互说明
- 如现有文档已经覆盖后台管理规范,则优先补充现有文档,不重复造新说明页
分步实施计划
Task 1: 明确数据模型与契约扩展
Objective: 定义邀请码开始/截止日期字段及其在响应中的展示方式。
要点:
- 确认字段名采用
starts_at/expires_at,避免与现有字段语义冲突。 - 确认时间类型统一用
Timestamp/ 毫秒微秒整数转换策略。 - 明确返回给后台的字段是否需要附带派生状态(如
status)。
产出:
- 契约字段定义
- 状态枚举/派生规则
Task 2: 更新 SpacetimeDB 表与迁移
Objective: 让邀请码表可保存有效期,并兼容旧数据。
要点:
- 修改
ProfileInviteCode表结构。 - 更新迁移逻辑,旧记录默认无开始/截止。
- 检查是否需要补充索引或查询辅助字段。
验证:
- 旧数据能正常读取。
- 新数据能写入开始/截止。
Task 3: 实现邀请填写时的时间窗校验
Objective: 在邀请码被填写时正确拒绝未生效或已过期的邀请码。
要点:
- 在
redeem_profile_referral_invite_code_record内增加开始/截止校验。 - 保持“自己的邀请码不能填”“邀请码不存在”等原有错误优先级清晰。
- 保留历史绑定关系不受影响。
验证:
- 未到开始时间时返回明确错误。
- 超过截止时间时返回明确错误。
- 正常区间可绑定成功。
Task 4: 扩展后台邀请码管理接口
Objective: 让后台可以创建/修改邀请码时间窗,并在列表中查看状态。
要点:
- 扩展
AdminUpsertProfileInviteCodeRequest。 - 扩展
ProfileInviteCodeAdminResponse。 api-server接口负责接收新字段并转发。- 列表接口返回可读时间字段与状态。
验证:
- 后台表单提交后,返回结果包含时间窗信息。
- 列表页能看到状态与时间。
Task 5: 给后台所有写操作加二次确认
Objective: 统一拦截所有后台写动作,避免误点直接生效。
覆盖范围建议:
- 邀请码新增/更新
- 兑换码新增/更新/停用
- 任务配置新增/更新/停用
- 后续新增的管理写操作
实现要求:
- 在真正调用 API 之前弹出确认框。
- 确认框需要展示对象名、操作类型、影响范围。
- 取消后不发送请求。
- 尽量抽象出通用确认组件/通用 action 包装函数,避免每个页面重复写。
验证:
- 点击“保存”不会直接提交,需先确认。
- 点击“取消”不会发请求。
- 所有后台写入口行为一致。
Task 6: 补充文档与交互说明
Objective: 把新规则写进项目文档,避免后续实现偏差。
要点:
- 记录邀请码时间窗语义。
- 记录后台二次确认规范。
- 说明哪些动作属于“必须确认”的写操作。
测试与验证
服务端
- 邀请码时间窗单测 / 集成测试
- 邀请码 redeem 流程回归测试
- 旧数据兼容测试
API / 前端
- 管理后台列表展示正确
- 表单提交能回传新字段
- 二次确认取消后不请求接口
- 二次确认确认后正常提交
推荐验证命令
- 视项目现有脚本执行对应后端测试
- 前端按 admin-web 构建/测试脚本验证
- 如涉及生成绑定,先确认生成产物无漏字段
风险与权衡
-
时间字段格式不统一
- 风险:前后端对时间单位理解不一致。
- 处理:在契约层明确是 ISO 字符串还是微秒整数,并全链路统一。
-
后台“所有操作”范围过大
- 风险:遗漏某些写入口。
- 处理:先枚举现有写 API,再做统一确认封装。
-
邀请码过期后历史链接解释成本
- 风险:用户误以为历史邀请码失效影响已绑定关系。
- 处理:文案明确“仅影响新填写,不影响已绑定记录”。
-
契约与生成绑定联动较多
- 风险:字段变更后生成文件数量较多。
- 处理:先改源契约与服务端,再统一重生成 bindings。
待确认问题
starts_at/expires_at在接口里要返回 ISO 字符串 还是 微秒整数?- 后台二次确认是否统一用一个全局弹窗组件,还是页面级本地实现?
- 邀请码列表是否需要直接展示“状态标签”还是只展示时间字段由前端推导?
- 现有后台所有写操作里,是否还要覆盖调试类接口,还是仅覆盖业务管理接口?
建议执行顺序
- 先确认时间字段格式与确认弹窗范围。
- 再改服务端契约与迁移。
- 再改 redeem 校验与后台接口。
- 最后统一改 admin-web 的二次确认与表单展示。
结论: 这是一个适合分阶段落地的改动,建议先做“邀请码时间窗 + 后台统一二次确认”的基础能力,再补交互细节。