简介
本文件面向 DouPHP 小程序“优惠券”功能页面的开发与维护,覆盖以下目标:
- 优惠券列表展示、领取、使用的全链路说明
- 数据模型设计:类型(满减券/折扣券/现金券)、使用条件(最低消费、适用商品范围、有效期)、状态管理(未使用/已使用/已过期)
- 领取业务逻辑:用户资格校验、库存控制、防重复领取
- 使用流程:购物车集成与订单结算时的选择与应用
- 用户体验优化:分类筛选、搜索、到期提醒等
- 常见问题:冲突处理、退款恢复、异常修复
项目结构
围绕优惠券的小程序端能力由 API 层控制器、前台服务与核心服务共同实现,路由集中在模块声明式路由文件中;订单结算时通过 CheckoutService 完成核销。
graph TB
subgraph "小程序前端"
WX["小程序页面"]
end
subgraph "API 层"
R["路由: coupon.php"]
AC["CouponController (API)"]
UC["UserController (API)"]
end
subgraph "前台服务"
FS["Front CouponService"]
end
subgraph "核心服务"
CS["Core CouponService"]
CE["Core CouponEligibility"]
end
subgraph "数据层"
DB1["表: coupon"]
DB2["表: coupon_log"]
end
subgraph "订单结算"
CO["CheckoutService"]
end
WX --> R
R --> AC
R --> UC
AC --> FS
UC --> FS
FS --> CS
FS --> DB2
CS --> DB1
CS --> DB2
CO --> CE
CO --> DB2
核心组件
- API 控制器
- 优惠券主控制器:提供列表占位与领取接口
- 会员侧控制器:返回分页的优惠券列表数据
- 前台服务
- 领取防重与入库
- 构建会员优惠券列表(含类型、金额、有效期、状态)
- 核心服务
- 优惠券列表与详情、折扣计算、状态判定、过期判断、编号生成
- 使用资格判定:时间窗、首单限制、范围命中、最低消费门槛
- 订单结算
- 校验可用后核销并写入使用记录
架构总览
小程序端通过声明式路由访问 API,API 控制器调用前台服务进行领取与列表组装,前台服务再委托核心服务完成复杂逻辑(折扣计算、状态判定、资格校验)。订单结算阶段由 CheckoutService 统一校验并使用优惠券。
sequenceDiagram
participant M as "小程序"
participant R as "路由(coupon.php)"
participant C as "CouponController(API)"
participant S as "Front CouponService"
participant Core as "Core CouponService"
participant DB as "数据库"
M->>R : POST /api/?route=coupon/claim
R->>C : claim(id)
C->>S : claimCouponIfNew(userId, id, ip)
S->>DB : 检查是否已领取
alt 未领取
S->>DB : 插入 coupon_log(user_id, coupon_id, claimed_at, ip)
else 已领取
S-->>C : 直接返回
end
C->>Core : getCouponList(userId)
Core->>DB : 查询 coupon(有效且启用)
Core-->>C : 返回带状态的券列表
C-->>M : 成功响应
详细组件分析
数据模型设计
- 优惠券主表(coupon)
- 关键字段:名称、编号、类型(满减/折扣/现金)、面值或折扣率、最低消费门槛、开始/结束时间、简介、排序、状态
- 参考字段映射与可填充字段定义
- 领取/使用记录表(coupon_log)
- 关键字段:用户ID、券ID、领取时间、IP、创建时间、状态、使用时间、关联订单号
- 类型与状态
- 类型:百分比折扣、固定金额减免
- 状态:未使用、已使用、已过期(由服务根据时间与使用记录判定)
优惠券列表展示
- 小程序列表接口
- 会员侧接口返回分页的优惠券列表,包含券名、编号、类型、金额/折扣、条件、起止日期、简介与状态
- 列表数据来源
- 从 coupon_log 中按用户拉取,并关联 coupon 表补齐信息
- 使用核心服务的 status 方法计算每条券的状态
flowchart TD
Start(["进入会员优惠券页"]) --> Load["加载分页参数"]
Load --> Query["查询 coupon_log(user_id) 分页"]
Query --> Map["关联 coupon 表补齐信息"]
Map --> Status["调用 status 计算状态"]
Status --> Render["渲染列表(类型/金额/条件/有效期/状态)"]
优惠券领取
- 入口与路由
- 小程序通过 POST /api/?route=coupon/claim 提交领取请求
- 防重复领取
- 前台服务在插入前检查 coupon_log 是否存在该用户+券的记录
- 入库字段
- 用户ID、券ID、领取时间、客户端IP、创建时间
- 返回结果
- 成功后返回当前用户的券列表(用于刷新界面)
sequenceDiagram
participant App as "小程序"
participant API as "CouponController(API)"
participant FS as "Front CouponService"
participant DB as "数据库"
App->>API : POST claim(id)
API->>FS : claimCouponIfNew(userId, id, ip)
FS->>DB : 检查是否存在 user_id + coupon_id
alt 不存在
FS->>DB : 插入 coupon_log(...)
else 存在
FS-->>API : 忽略重复
end
API-->>App : 成功(可选携带券列表)
优惠券使用(结算)
- 资格判定
- 时间有效性、首单限制、范围命中(全部/模块/分类/商品)、最低消费门槛
- 折扣计算
- 百分比折扣:按比例扣减并可设置上限;固定金额:直接减去面值
- 核销与记录
- 校验通过后更新 coupon_log 的使用时间与状态,并写入订单明细
flowchart TD
A["选择优惠券"] --> B{"资格判定<br/>时间/首单/范围/门槛"}
B -- 否 --> E["不可用提示"]
B -- 是 --> C["计算折扣金额"]
C --> D{"折扣>0 ?"}
D -- 否 --> E
D -- 是 --> F["更新 coupon_log 使用状态"]
F --> G["写入订单优惠明细"]
G --> H["完成结算"]
用户体验优化建议
- 分类筛选:基于 scope_type/scope_value 在前端提供“全品类/某分类/指定商品”的快速筛选
- 搜索:支持按券名、编号模糊检索
- 到期提醒:对即将过期的券高亮提示,并在列表中标注剩余天数
- 状态可视化:未使用/已使用/已过期以不同标签与颜色区分
- 空态引导:无可用券时提供领取入口或活动入口
依赖关系分析
- 控制器依赖服务:API 控制器依赖前台服务与核心服务
- 服务依赖数据:前台服务负责领取与列表拼装,核心服务负责折扣与状态
- 订单结算依赖资格判定:CheckoutService 使用 CouponEligibility 进行可用性判断
graph LR
AC["API CouponController"] --> FS["Front CouponService"]
UC["API UserController"] --> FS
FS --> CS["Core CouponService"]
CS --> DB1["coupon"]
FS --> DB2["coupon_log"]
CO["CheckoutService"] --> CE["CouponEligibility"]
CE --> DB1
CO --> DB2
性能考虑
- 列表分页:会员券列表采用分页查询,避免一次性加载过多数据
- 索引建议:对 coupon_log.user_id、coupon_log.coupon_id、coupon.status、coupon.start_at/end_at 建立合适索引以提升查询效率
- 缓存策略:热门券列表可考虑短期缓存,减少高频读取压力
- 折扣计算:百分比折扣的上限与精度需统一配置,避免浮点误差
故障排查指南
- 领取失败或重复领取
- 检查 coupon_log 是否已有该用户+券的记录;确认领取接口幂等性
- 券不可用
- 核对时间窗口、是否首单限制、范围是否命中、是否满足最低消费
- 结算未生效
- 确认 CheckoutService 是否正确调用资格判定与核销逻辑
- 状态异常
- 检查 coupon_log.used_at 与 coupon.end_at 是否一致;必要时手动修复状态
结论
DouPHP 小程序优惠券功能通过清晰的职责分层实现了“列表—领取—使用”的完整闭环:API 层暴露稳定接口,前台服务保障领取幂等与列表拼装,核心服务负责折扣与状态判定,订单结算阶段统一核销。配合合理的索引与缓存策略,可在保证正确性的同时获得良好性能。后续可按“分类筛选、搜索、到期提醒”等方向持续优化用户体验。
附录
- 关键接口一览
- 领取:POST /api/?route=coupon/claim
- 会员券列表:GET /api/?route=user/coupon
- 相关视图与样式
- 后台模板与样式位于 theme 与 admin/view 下,可按需扩展