feat: implement coupon management and receive flow

This commit is contained in:
2026-01-13 18:19:29 +08:00
parent 9b06f768ab
commit 4f315cc2db
18 changed files with 1787 additions and 246 deletions

102
docs/coupon_plan.md Normal file
View File

@@ -0,0 +1,102 @@
# 优惠券功能规划(先规划,后执行)
## 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) ServiceReceive/ListAvailable/ListUserCoupons 过期处理。
3) HTTP用户端 + CreatorCenter 管理端接口。
4) 前端:结算页优惠券选择 + 领券入口。
5) 补测试 + 回归订单流程。