# 优惠券功能规划(先规划,后执行) ## 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=` - 返回「当前订单金额可用」的优惠券列表(用于结算页选择)。 - `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` - 查看模板详情。 - `PUT /t/:tenantCode/v1/creator/coupons/:id` - 更新模板(仅未开始或未领取时允许修改核心字段)。 - `POST /t/:tenantCode/v1/creator/coupons/:id/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) 补测试 + 回归订单流程。