加载中…
文档目录
优惠券系统

简介

本开发文档围绕 DouPHP 优惠券系统的完整生命周期展开,覆盖优惠券的创建、发放、使用、核销、状态流转、规则配置、并发控制与防刷机制,并提供 API 接入指南与数据统计分析思路。文档面向开发者,帮助快速理解并集成优惠券能力到商城、小程序或第三方系统中。

项目结构

优惠券功能以“模块”形式组织,包含核心领域服务、后台管理、前台展示、API 暴露以及数据模型与路由定义。关键路径如下:

  • 核心领域服务:_'/module/coupon/core/service/coupon
  • 后台管理:_'/module/coupon/admin/controller, service, model
  • 前台业务:_'/module/coupon/front/controller, service
  • API 层:_'/module/coupon/api/controller, route
  • 路由定义:admin/route/coupon.php, _'/module/coupon/api/route/coupon.php
  • 订单集成:_'\module/order/admin/service/order/OrderService.php(用于订单侧统计与展示)
graph TB
subgraph "后台管理"
AC["Admin Controller<br/>CouponController"]
ASvc["Admin Service<br/>CouponService"]
AModel["Models<br/>Coupon / CouponLog"]
end
subgraph "核心领域"
CSvc["Core Service<br/>CouponService"]
CElig["Eligibility<br/>CouponEligibility"]
end
subgraph "前台"
FCtl["Front Controller<br/>CouponController"]
FSvc["Front Service<br/>CouponService"]
end
subgraph "API"
ACtl["API Controller<br/>CouponController"]
AR["API Route<br/>coupon.php"]
end
subgraph "订单集成"
OSvc["Order Service<br/>OrderService"]
end
AC --> ASvc --> AModel
ACtl --> FSvc --> CSvc
ACtl --> CElig
FCtl --> FSvc
OSvc --> CSvc

核心组件

  • 核心领域服务
    • 优惠券查询与折扣计算:CouponService(列表、用户券、过期判断、折扣计算、状态判定、券码生成)
    • 资格判定:CouponEligibility(范围命中、首单限制、条件金额校验)
  • 后台管理
    • 控制器与服务:CouponController、CouponService(CRUD、分页、批量删除、审计日志)
    • 日志服务:CouponLogService(领取/使用记录筛选与分页)
    • 模型:Coupon、CouponLog(字段映射、排序、过滤作用域)
  • 前台业务
    • 控制器:CouponController(页面渲染)
    • 服务:CouponService(领取幂等写入、用户券列表构建)
  • API 层
    • 控制器:CouponController(index、claim)
    • 路由:coupon.php(GET coupon, POST coupon/claim, GET user/coupon/index)

架构总览

优惠券系统采用分层架构:

  • 表现层:后台控制器、前台控制器、API 控制器
  • 业务层:后台服务、前台服务、核心领域服务
  • 数据层:ORM 模型、数据库表(coupon、coupon_log、order_coupon 等)
sequenceDiagram
participant Client as "客户端"
participant API as "API 控制器"
participant Front as "前台服务"
participant Core as "核心服务"
participant Elig as "资格判定"
participant DB as "数据库"
Client->>API : POST /api/coupon/claim
API->>Front : claimCouponIfNew(userId, couponId, ip)
Front->>DB : 检查是否已领取(幂等)
DB-->>Front : 是否存在
alt 未领取
Front->>DB : 插入领取记录(coupon_log)
DB-->>Front : 成功
else 已领取
Front-->>API : 直接返回
end
API->>Core : getCouponList(userId)
Core->>DB : 查询有效券列表
DB-->>Core : 券集合
Core->>Elig : canUse(券, userId, cart)
Elig-->>Core : 是否可用
Core-->>API : 券列表及状态
API-->>Client : 响应

详细组件分析

优惠券类型与规则配置

  • 类型
    • 满减券:type=money,按面额 face_value 减免,需满足 condition 门槛
    • 折扣券:type=percent,按比例 face_value% 减免,可设置 max_discount 封顶
    • 无门槛券:condition=0,无需最低消费即可使用
  • 适用范围
    • scope_type=all/module/category/product
    • scope_value:JSON 数组,指定模块、分类或商品 ID 集合
  • 时间窗口
    • start_at/end_at:生效起止时间
  • 用户限制
    • is_first_order_only:仅首单可用(通过订单状态判断)
  • 其他
    • status:启用/禁用
    • sort:排序权重

状态流转与生命周期

  • 生命周期阶段
    • 待领取:未出现在 coupon_log
    • 已领取:coupon_log.claimed_at 存在
    • 已使用:coupon_log.used_at 存在
    • 已过期:coupon.end_at 早于当前时间
  • 状态判定逻辑
    • 优先判断 used_at,其次判断过期,再判断 claimed_at,最后为可领取
flowchart TD
Start(["开始"]) --> CheckUsed{"是否已使用?"}
CheckUsed --> |是| Used["状态: 已使用"]
CheckUsed --> |否| CheckExp{"是否已过期?"}
CheckExp --> |是| Expired["状态: 已过期"]
CheckExp --> |否| CheckClaimed{"是否已领取?"}
CheckClaimed --> |是| Got["状态: 已领取"]
CheckClaimed --> |否| Getable["状态: 可领取"]
Used --> End(["结束"])
Expired --> End
Got --> End
Getable --> End

折扣计算与使用流程

  • 折扣计算
    • 校验券有效性(状态、时间窗口、用户是否已领取)
    • 根据 type 计算:
      • percent:amount = item_amount * (face_value/100),受 max_discount 限制
      • money:amount = face_value
    • 更新 item_amount 为折后金额
  • 使用流程
    • 前端选择券 -> 提交订单 -> 调用 discount() -> 得到 amount 与折后金额 -> 落库 order_coupon
sequenceDiagram
participant FE as "前端"
participant API as "API/控制器"
participant Core as "核心服务"
participant DB as "数据库"
FE->>API : 提交订单(含 coupon_id, item_amount)
API->>Core : discount(coupon_id, item_amount, user_id)
Core->>DB : 检查领取记录 & 券有效期
DB-->>Core : 结果
Core->>Core : 计算折扣(amount, item_amount)
Core-->>API : {coupon_id, type, amount, item_amount}
API->>DB : 保存 order_coupon
API-->>FE : 订单确认

后台管理与日志

  • 后台 CRUD
    • 列表分页、创建、编辑、删除、批量删除
    • 默认数据模板、语言包支持
  • 日志查询
    • 按券 ID、使用时间区间筛选
    • 关联券信息,格式化金额与时间
  • 审计
    • 创建、更新、删除均记录管理员操作日志

数据模型与字段说明

  • 优惠券表(coupon)
    • id、name、coupon_sn、type、face_value、condition、start_at、end_at、brief、status、sort、created_at
  • 领取/使用记录表(coupon_log)
    • id、user_id、coupon_id、claimed_at、used_at、ip、status、created_at
  • 订单关联表(order_coupon)
    • order_id、coupon_id、amount(用于订单侧统计与展示)

依赖关系分析

  • 控制器依赖服务,服务依赖核心领域服务与 ORM 模型
  • API 层复用前台服务与核心服务
  • 订单模块通过模块工厂获取优惠券服务进行统计与展示
graph LR
AC["Admin Controller"] --> ASvc["Admin Service"]
ASvc --> AModel["Models(Coupon/CouponLog)"]
ACtl["API Controller"] --> FSvc["Front Service"]
FSvc --> CSvc["Core Service"]
CSvc --> CElig["Eligibility"]
OSvc["Order Service"] --> CSvc

性能与并发控制

  • 领取幂等
    • 基于 coupon_log 唯一性检查(coupon_id + user_id),避免重复领取
    • 建议增加数据库唯一索引保障强一致
  • 并发安全
    • 高并发领取场景建议使用事务或分布式锁(如 Redis 原子操作)防止超发
    • 折扣计算在订单提交时执行,确保最终一致性
  • 查询优化
    • 列表分页、条件过滤(时间、状态、范围)
    • 对常用查询字段建立索引(user_id、coupon_id、status、start_at、end_at)
  • 防刷机制
    • IP 记录:coupon_log.ip 可用于风控分析
    • 限流:结合网关或中间件对 claim 接口做频率限制
    • 验证码/人机校验:针对高风险渠道开启

故障排查指南

  • 常见问题
    • 券不可用:检查 status、start_at/end_at 时间窗口、is_first_order_only、scope 范围、condition 门槛
    • 重复领取:检查 coupon_log 是否已有记录;必要时清理脏数据并加唯一索引
    • 折扣异常:核对 type、face_value、max_discount、item_amount 计算逻辑
  • 定位方法
    • 查看 coupon_log 领取/使用记录
    • 查看订单中的 order_coupon 明细
    • 使用后台日志筛选功能按券 ID 和时间段过滤
  • 修复建议
    • 修正券配置(时间、范围、门槛)
    • 补充索引提升查询性能
    • 引入限流与风控策略

结论

DouPHP 优惠券系统提供了完整的生命周期管理能力,涵盖类型配置、规则校验、状态流转、后台管理与 API 暴露。通过核心领域服务与资格判定器,实现了灵活的范围命中与首单限制;通过前台服务的幂等领取与订单集成,保障了使用的一致性与可追溯性。建议在部署时完善索引、限流与风控策略,以提升稳定性与安全性。

附录:API接口文档

  • 基础信息
    • 基地址:/api/?route=coupon
    • 认证:小程序端通过 auth('api') 获取用户 ID
  • 接口列表
    • GET /api/coupon
      • 描述:获取优惠券列表(占位实现)
      • 请求参数:无
      • 响应:成功空列表
    • POST /api/coupon/claim
      • 描述:领取优惠券(幂等)
      • 请求参数:id(coupon_id)
      • 响应:成功返回领取后的券列表
    • GET /api/user/coupon/index
      • 描述:会员侧券列表
      • 请求参数:无
      • 响应:会员已领取券列表
sequenceDiagram
participant C as "客户端"
participant R as "路由"
participant A as "API 控制器"
participant F as "前台服务"
participant D as "数据库"
C->>R : POST /api/coupon/claim?id={coupon_id}
R->>A : claim(id)
A->>F : claimCouponIfNew(userId, id, ip)
F->>D : 检查是否已领取
D-->>F : 结果
alt 未领取
F->>D : 插入领取记录
D-->>F : 成功
end
A->>F : getCouponList(userId)
F->>D : 查询有效券
D-->>F : 券集合
F-->>A : 券列表
A-->>C : 响应
添加日期:2026-10-05