# 优惠券功能规划（先规划，后执行）

## 1. 目标与范围

- 支持租户侧创建/管理优惠券模板。
- 支持用户领取/选择/使用优惠券，订单创建时自动校验与核销。
- 优惠券状态可追踪：未使用/已使用/已过期。
- 保持多租户隔离，避免跨租户误用。

## 2. 当前现状

- 数据表已存在：`coupons`、`user_coupons`、`orders.coupon_id`。
- 服务层已有部分能力：`Coupon.ListUserCoupons`、`Coupon.Validate`、`Coupon.MarkUsed`。
- 用户侧 API 已有：`GET /t/:tenantCode/v1/me/coupons`。
- 前端已有「我的优惠券」页面，但暂无领取/选择流程。

## 3. 领域规则（V1）

- `total_quantity = 0` 表示不限量；>0 表示最多可领取次数。
- 每个用户对同一优惠券默认只允许领取一次（可通过规则调整）。
- 过期判断以 `end_at` 为准：`end_at < now` 则视为过期。
- 使用时：订单金额需满足 `min_order_amount`；折扣券需遵守 `max_discount`。

## 4. 枚举与类型规范（必做）

- 新增枚举类型并统一使用（避免硬编码字符串）：
  - `consts.CouponType`：`fix_amount` / `discount`
  - `consts.UserCouponStatus`：`unused` / `used` / `expired`
- `backend/database/.transform.yaml` 映射：
  - `coupons.type -> consts.CouponType`
  - `user_coupons.status -> consts.UserCouponStatus`
- 运行 `atomctl gen enum` + `atomctl gen model`，保持生成文件一致性。

## 5. 接口规划（按模块）

### 5.1 用户侧（UserCenter）

- `GET /t/:tenantCode/v1/me/coupons?status=unused|used|expired`
  - 已有，补齐过期自动标记逻辑。
- `GET /t/:tenantCode/v1/me/coupons/available?amount=<int64>`
  - 返回「当前订单金额可用」的优惠券列表（用于结算页选择）。
- `POST /t/:tenantCode/v1/me/coupons/receive`
  - 领取优惠券（参数：`coupon_id`）。

### 5.2 租户侧（CreatorCenter）

- `POST /t/:tenantCode/v1/creator/coupons`
  - 创建优惠券模板。
- `GET /t/:tenantCode/v1/creator/coupons`
  - 分页查询模板列表（支持状态、有效期、类型过滤）。
- `GET /t/:tenantCode/v1/creator/coupons/:id<int>`
  - 查看模板详情。
- `PUT /t/:tenantCode/v1/creator/coupons/:id<int>`
  - 更新模板（仅未开始或未领取时允许修改核心字段）。
- `POST /t/:tenantCode/v1/creator/coupons/:id<int>/grant`
  - 定向发放给用户（参数：`user_ids` 批量）。

## 6. 服务层设计（关键逻辑）

- `Coupon.Create/Update/List/Get`：模板管理。
- `Coupon.Receive`：
  - 校验有效期与库存（`total_quantity`）。
  - 校验用户是否已领取。
  - 事务内写入 `user_coupons`。
- `Coupon.ListUserCoupons`：
  - join `coupons` 读取模板信息。
  - 对过期券自动标记 `expired`（可更新 DB）。
- `Coupon.ListAvailable`：
  - `status = unused`
  - `start_at <= now <= end_at`
  - `min_order_amount <= amount`
- `Coupon.Validate` / `MarkUsed`：
  - 已有，改用枚举类型 + 统一错误码语义。

## 7. 并发与一致性

- 领取优惠券时在事务中锁定 `coupons` 行或使用条件更新，防止超发：
  - `total_quantity > 0` 时，基于 `COUNT(user_coupons)` 判断库存。
  - 若性能成为瓶颈，再考虑新增 `claimed_quantity` 字段（V2 优化）。

## 8. 前端联动

- 结算页新增「选择优惠券」抽屉（参考 `docs/design/portal/PAGE_ORDER.md`）。
- 用户中心「优惠券」页保持与后端状态一致（unused/used/expired）。
- 订单创建前调用 `available` 接口，展示可用券。

## 9. 测试规划

- Service 单测：
  - 领取成功/重复领取/超库存/过期不可领取。
  - Validate：金额不足、折扣封顶、跨租户禁止。
  - MarkUsed：幂等/重复使用失败。
- Order 相关：
  - 使用优惠券创建订单成功并核销。

## 10. 实施顺序（建议）

1) 枚举 + transform + gen（保证类型安全）。
2) Service：Receive/ListAvailable/ListUserCoupons 过期处理。
3) HTTP：用户端 + CreatorCenter 管理端接口。
4) 前端：结算页优惠券选择 + 领券入口。
5) 补测试 + 回归订单流程。
