feat: add invite code validity controls
- 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
This commit is contained in:
@@ -0,0 +1,271 @@
|
||||
# 邀请码有效期与后台二次确认实施计划
|
||||
|
||||
> **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` 时的实时校验,适合增加“时间窗”而不是“禁用删除”。
|
||||
- 后台已存在兑换码、任务配置等可写操作;本次要求把所有后台操作统一加二次确认,包括新增、编辑、禁用、删除等写入口。
|
||||
|
||||
---
|
||||
|
||||
## 设计原则
|
||||
|
||||
1. **邀请码不做软删除**:保留历史记录和邀请链路。
|
||||
2. **有效期由时间窗推导**:
|
||||
- `starts_at` 为空表示立即生效。
|
||||
- `expires_at` 为空表示长期有效。
|
||||
3. **前台只拒绝新绑定**:已绑定关系不回溯修改。
|
||||
4. **后台写操作统一确认**:所有会触发 POST / PATCH / DELETE 的管理动作,在真正提交前必须弹出二次确认。
|
||||
5. **尽量少改接口语义**:优先在现有 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.rs`
|
||||
- `ProfileInviteCode` 表结构新增开始/截止字段
|
||||
- 邀请码 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 构建/测试脚本验证
|
||||
- 如涉及生成绑定,先确认生成产物无漏字段
|
||||
|
||||
---
|
||||
|
||||
## 风险与权衡
|
||||
|
||||
1. **时间字段格式不统一**
|
||||
- 风险:前后端对时间单位理解不一致。
|
||||
- 处理:在契约层明确是 ISO 字符串还是微秒整数,并全链路统一。
|
||||
|
||||
2. **后台“所有操作”范围过大**
|
||||
- 风险:遗漏某些写入口。
|
||||
- 处理:先枚举现有写 API,再做统一确认封装。
|
||||
|
||||
3. **邀请码过期后历史链接解释成本**
|
||||
- 风险:用户误以为历史邀请码失效影响已绑定关系。
|
||||
- 处理:文案明确“仅影响新填写,不影响已绑定记录”。
|
||||
|
||||
4. **契约与生成绑定联动较多**
|
||||
- 风险:字段变更后生成文件数量较多。
|
||||
- 处理:先改源契约与服务端,再统一重生成 bindings。
|
||||
|
||||
---
|
||||
|
||||
## 待确认问题
|
||||
|
||||
1. `starts_at` / `expires_at` 在接口里要返回 **ISO 字符串** 还是 **微秒整数**?
|
||||
2. 后台二次确认是否统一用一个全局弹窗组件,还是页面级本地实现?
|
||||
3. 邀请码列表是否需要直接展示“状态标签”还是只展示时间字段由前端推导?
|
||||
4. 现有后台所有写操作里,是否还要覆盖调试类接口,还是仅覆盖业务管理接口?
|
||||
|
||||
---
|
||||
|
||||
## 建议执行顺序
|
||||
|
||||
1. 先确认时间字段格式与确认弹窗范围。
|
||||
2. 再改服务端契约与迁移。
|
||||
3. 再改 redeem 校验与后台接口。
|
||||
4. 最后统一改 admin-web 的二次确认与表单展示。
|
||||
|
||||
---
|
||||
|
||||
**结论:** 这是一个适合分阶段落地的改动,建议先做“邀请码时间窗 + 后台统一二次确认”的基础能力,再补交互细节。
|
||||
Reference in New Issue
Block a user