Files
mp-pilates/docs/superpowers/specs/2026-04-07-member-card-edit-design.md
richarjiang f7f18f5178 docs: 添加会员卡编辑功能设计文档
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 09:35:28 +08:00

5.1 KiB
Raw Blame History

会员卡编辑功能设计

日期: 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

响应:

{
  "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

请求体:

{
  "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 清空卡

  • 需二次确认
  • 成功后弹窗关闭,列表自动刷新