docs: 添加会员卡编辑功能设计文档

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
richarjiang
2026-04-07 09:35:28 +08:00
parent 0ca93ec97e
commit f7f18f5178

View File

@@ -0,0 +1,186 @@
# 会员卡编辑功能设计
**日期:** 2026-04-07
**状态:** 已批准
---
## 1. 概述
在管理后台会员列表中,点击会员 item 时弹出的详情弹窗增加"编辑"能力。管理员可编辑指定用户的卡种、卡有效期、剩余次数,也可清空/解除会员卡。
**前置约束:**
- 一个用户只能拥有一张会员卡
- 支持清空卡(解除会员资格)
---
## 2. 交互设计
### 2.1 弹窗结构
```
┌─────────────────────────────────┐
│ 会员详情 [×] │
├──────────┬──────────────────────┤
│ 详情 │ 编辑 │ ← Tab 切换
├──────────┴──────────────────────┤
│ 头像 / 昵称 / OpenID / 手机号 │
│ ───────────────────────────── │
│ 会员卡信息区(根据 Tab 显示) │
│ 预约统计(总/完成/取消) │
│ ───────────────────────────── │
│ [关闭] │
└─────────────────────────────────┘
```
### 2.2 无卡用户交互
- 详情 Tab 显示"暂无会员卡"提示
- 显示"去开卡"按钮,点击切换到编辑 Tab
- 编辑 Tab 中卡种 picker 默认选中列表第一项,有效期自动计算
### 2.3 状态说明
| 场景 | Tab 初始显示 | 编辑表单状态 |
|------|-------------|-------------|
| 有卡用户 | 详情 Tab | 预填充当前数据 |
| 无卡用户 | 详情 Tab提示无卡 | 空白表单,卡种默认选中第一项 |
---
## 3. 表单字段
| 字段 | 组件 | 条件 | 说明 |
|------|------|------|------|
| 卡种 | picker | 必填 | 从 `CardType` 表读取,按 sortOrder 排序 |
| 剩余次数 | input (number) | TIMES / TRIAL 类型 | 默认填充卡种的 totalTimes |
| 生效日期 | date picker | 必填 | 默认当天 |
| 有效期至 | date picker | 必填 | 自动计算,支持手动调整 |
### 3.1 卡种切换逻辑
切换卡种时:
1. `remainingTimes` 自动填充新卡种的 `totalTimes`TIMES/TRIAL 类型)
2. `expireDate` 按新卡种 `durationDays` 重新计算起始日期
3. 编辑器内手动修改过 `expireDate` 时,不再自动覆盖
---
## 4. API 设计
### 4.1 获取用户会员卡
```
GET /admin/members/:userId/membership
```
**响应:**
```json
{
"userId": "uuid",
"membership": {
"id": "uuid",
"cardTypeId": "uuid",
"remainingTimes": 10,
"startDate": "2026-01-01",
"expireDate": "2026-04-01",
"status": "ACTIVE",
"cardType": { ... }
} | null
}
```
### 4.2 创建或更新会员卡
```
PUT /admin/members/:userId/membership
```
**请求体:**
```json
{
"cardTypeId": "uuid",
"remainingTimes": 10,
"startDate": "2026-04-07",
"expireDate": "2026-07-07"
}
```
**业务逻辑:**
- 若该用户已有 membership → 更新
- 若该用户无 membership → 创建新的
- `status` 由后端根据 expireDate 和 remainingTimes 自动计算
### 4.3 解除会员卡
```
DELETE /admin/members/:userId/membership
```
**业务逻辑:**
- 将 membership 的 `status` 标记为 `EXPIRED`(软删除,便于审计)
- 不物理删除记录
---
## 5. 数据模型
### 5.1 会员卡状态自动计算规则
```
if (expireDate < now) → EXPIRED
else if (remainingTimes === 0) → USED_UP
else → ACTIVE
```
### 5.2 变更日志
通过现有的 `BookingStatusHistory` 表记录操作tbd: 是否需要独立 `MembershipChangeLog` 表,待实现时确认)。
---
## 6. 错误处理
| 场景 | 处理 |
|------|------|
| 卡种不存在 | 后端返回 404前端提示"卡种不存在" |
| 有效期早于生效日期 | 前端表单校验失败,提示"有效期不能早于生效日期" |
| 次数为负数 | 前端表单校验失败,提示"次数不能为负数" |
| 网络错误 | Toast 提示"网络错误,请重试" |
| 清空卡确认 | 弹出确认对话框,提示"确定要解除该用户的会员卡吗?" |
---
## 7. 涉及改动
### 后端
- `packages/server/src/membership/membership.controller.ts` — 新增三个 admin 接口
- `packages/server/src/membership/membership.service.ts` — 新增 `getUserMembership` / `updateUserMembership` / `deleteUserMembership`
- `packages/server/src/user/user.controller.ts` — 无改动(列表接口保持不变)
### 前端
- `packages/app/src/stores/admin.ts` — 新增三个 store action
- `packages/app/src/pages/admin/members.vue` — 将详情弹窗改造为 Tab 模式,新增编辑表单
---
## 8. UI 细节
### 8.1 Tab 切换
- 详情/编辑 Tab 横向排列,激活态有下划线指示
- 无卡用户点击"去开卡"后,自动切到编辑 Tab
### 8.2 编辑表单提交
- 保存成功后 Toast 提示"保存成功"
- 自动切回详情 Tab 并刷新数据
- 保存按钮点击后置灰,防止重复提交
### 8.3 清空卡
- 需二次确认
- 成功后弹窗关闭,列表自动刷新