# 优惠券 (Coupon) API 文档 ## 概述 优惠券功能允许用户在充值时获得额外的金币奖励。系统会根据配置的策略自动向符合条件的用户发放优惠券。 **架构**: 采用事件驱动模式 —— 订单支付成功后触发 `OrderPaid` 事件,由 `ProcessCouponOnOrderPaid` Listener 异步处理优惠券逻辑(验证、发放金币、标记已用),与订单核心流程完全解耦。 **路由位置**: `routes/game.php`(`checkGameLogin` + `mustGameLogin` 中间件组) ### 优惠券类型 | 类型 | type_id | 说明 | |------|---------|------| | 固定金额 | 1 | 充值后赠送固定金额金币(与充值额无关) | | 充值百分比 | 2 | 充值后按充值金额的百分比赠送金币,有上限 | ### 优惠券状态 | 状态 | status | 说明 | |------|--------|------| | 未使用 | 0 | 可用 | | 已使用 | 1 | 已绑定订单并发放金币 | | 已过期 | 2 | 超过有效期 | --- ## 接口列表 ### 1. 获取优惠券列表 获取用户当前可用的优惠券列表,**同时触发自动发放逻辑**。系统会根据 `config/coupon.php` 中的规则检查用户是否符合发放条件,符合则自动发放新券。 ``` GET /game/coupon/list ``` **鉴权**: `checkGameLogin` + `mustGameLogin` 中间件(从 `$request->globalUser->UserID` 获取用户ID) **请求示例**: ```http GET /game/coupon/list ``` **成功响应**: ```json { "code": 200, "msg": "success", "data": { "list": [ { "id": 1, "name": "new_user_bonus", "type": "percent", "type_id": 2, "value": 50, "min_recharge": 10, "max_bonus": 50, "desc": "+50% bonus (max 50)", "status": 0, "expire_at": "2026-06-03 12:00:00", "issued_at": "2026-05-27 12:00:00" } ], "new_issued": [ { "id": 1, "name": "new_user_bonus", "type": "percent", "type_id": 2, "value": 50, "min_recharge": 10, "max_bonus": 50, "desc": "+50% bonus (max 50)", "status": 0, "expire_at": "2026-06-03 12:00:00", "issued_at": "2026-05-27 12:00:00" } ] } } ``` **响应字段说明**: | 字段 | 类型 | 说明 | |------|------|------| | list | array | 当前所有可用优惠券 | | new_issued | array | 本次请求新发放的优惠券(首次调用时可能非空) | | id | int | 优惠券ID,充值下单时传入 | | name | string | 优惠券名称(标识) | | type | string | `fixed`=固定金额, `percent`=百分比 | | type_id | int | 1=固定金额, 2=百分比 | | value | float | 优惠值(元 or %) | | min_recharge | float | 最低充值门槛(元) | | max_bonus | float | 最大赠送上限(元),百分比券有效 | | desc | string | 前端展示文案 | | status | int | 0=可用, 1=已用, 2=过期 | | expire_at | string | 过期时间 | | issued_at | string | 发放时间 | --- ### 2. 预估优惠券赠送金额 在用户选择优惠券后、发起支付前调用,用于向用户展示预计获得的额外金币。 ``` POST /game/coupon/preview ``` **鉴权**: `checkGameLogin` + `mustGameLogin` 中间件 **请求参数**: | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | userID | int | 是 | 用户ID | | coupon_id | int | 是 | 优惠券ID | | payAmt | float | 是 | 充值金额(元) | **请求示例**: ```http POST /game/coupon/preview Content-Type: application/json { "userID": 123456, "coupon_id": 1, "payAmt": 50 } ``` **成功响应**: ```json { "code": 200, "msg": "success", "data": { "coupon_id": 1, "bonus_amount": 25.00, "bonus_coins": 2500, "total_amount": 75.00 } } ``` **响应字段说明**: | 字段 | 类型 | 说明 | |------|------|------| | coupon_id | int | 优惠券ID | | bonus_amount | float | 预计赠送金额(元) | | bonus_coins | int | 预计赠送金币(分,1元=100分) | | total_amount | float | 预计到账总额(元)= 充值额 + 赠送额 | **失败响应**: ```json { "code": 301, "msg": "Minimum recharge 10 required for this coupon", "data": [] } ``` --- ### 3. 支付时使用优惠券 在支付接口中新增 `coupon_id` 参数,传入要使用的优惠券ID。 **影响接口**: `POST /payment_entry/pay`(`routes/game.php`) **新增参数**: | 参数 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | coupon_id | int | 否 | 0 | 优惠券ID,0=不使用优惠券 | **请求示例**: ```json { "payAmt": 50, "pay_type": 1, "pay_method": "2", "GiftsID": 0, "coupon_id": 1 } ``` **处理流程(事件驱动)**: 1. `ProcessCouponOnOrderPaid` Listener 异步处理: - 验证优惠券有效性(未使用、未过期、充值金额满足门槛) - 计算赠送金币 - 标记优惠券为已使用,绑定订单号 - 增加用户金币(Reason=55, 优惠券赠送) 2. 如果优惠券使用失败(已过期/已使用/金额不满足),不影响正常充值流程,仅记录日志 --- ## 自动发放策略 系统在用户调用 `GET /game/coupon/list` 时自动检查以下条件并发放优惠券。 配置文件: `config/coupon.php` ### 默认策略 | 策略名称 | 条件 | 券类型 | 优惠 | 最低充值 | 有效期 | |----------|------|--------|------|----------|--------| | new_user_bonus | 注册≤3天 + 从未充值 | 百分比 | 50% (上限50元) | 10元 | 7天 | | comeback_bonus | 最近充值≥14天前 | 百分比 | 30% (上限30元) | 20元 | 3天 | | vip_recharge_bonus | 累计充值≥500元 | 固定 | 20元 | 50元 | 14天 | ### 发放条件类型 | 条件类型 | 参数 | 说明 | |----------|------|------| | new_user | days | 注册天数 ≤ days 且从未充值 | | inactive_days | days | 最近一次充值距今 ≥ days(需有充值历史) | | recharge_total | amount | 累计充值金额 ≥ amount(元) | | vip_level | level | VIP等级 ≥ level | ### 防重复机制 同一用户不会重复获得同名的有效优惠券。只有当前券过期或使用后,下次请求才会重新发放。 --- ## 数据库表结构 ### agent.dbo.user_coupons | 字段 | 类型 | 说明 | |------|------|------| | id | INT IDENTITY PK | 主键 | | user_id | INT NOT NULL | 用户ID | | coupon_name | VARCHAR(50) | 优惠券名称 | | coupon_type | TINYINT | 1=固定金额, 2=百分比 | | coupon_value | DECIMAL(10,2) | 优惠值 | | min_recharge | DECIMAL(10,2) | 最低充值门槛 | | max_bonus | DECIMAL(10,2) | 最大赠送上限 | | bonus_coins | INT | 实际赠送金币(分) | | order_sn | VARCHAR(64) | 关联订单号 | | status | TINYINT | 0=未使用, 1=已使用, 2=已过期 | | issued_at | DATETIME | 发放时间 | | used_at | DATETIME | 使用时间 | | expire_at | DATETIME | 过期时间 | ### 优惠券-订单关联 优惠券通过 `order_sn` 字段关联订单,**不在 order 表中新增字段**,保持订单表结构不变。 `coupon_id` 在支付请求→回调之间通过 Redis 临时存储(key=`coupon_{order_sn}`,TTL=24h)。 --- ## 金币变更记录 使用优惠券后,金币增加记录写入 `QPRecordDB.dbo.RecordUserScoreChange`: | 字段 | 值 | 说明 | |------|-----|------| | Reason | 55 | 优惠券赠送(与现有21/33/44/45/49/51/52/72/73不冲突) | 可通过 `config/coupon.php` 中的 `score_reason` 配置修改。