# 优惠券兑换 API 文档 本文档提供了**用户端兑换优惠券接口**的详细说明。当前接口主要用于 App 或小程序中,用户消耗对应的积分来兑换相应的优惠券(如洗车券、折扣券等)。 --- ## 接口说明 **接口路径**: `/coupon/exchange` **请求方法**: `POST` **功能描述**: 用户通过指定的积分兑换某一种优惠券。系统将自动扣除该用户的积分并向其发放对应的一张优惠券。该接口具有防重放设计,依赖 `requestId` 保证请求幂等性。 ### 接口鉴权 建议配合 JWT 认证/统一网关身份校验后使用。(接口本身会读取入参 `memberId`,在实际部署时可能需要由网关/会话态中解析并传递此参数,防止越权)。 --- ## 请求参数 | 参数名称 | 参数位置 | 参数类型 | 是否必填 | 示例值 | 描述说明 | |----------|----------|----------|----------|--------|----------| | `memberId` | Query/Form | `String` | 是 | `u_1001` | 会员用户的全局唯一标识 | | `templateId` | Query/Form | `Long` | 是 | `102` | 需要兑换的优惠券所属的券模板 ID | | `requestId` | Query/Form | `String` | 是 | `9b1deb4d-3b7d-...` | 客户端生成的防重放幂等键 (UUID),防止网络原因导致的重复提交和重复扣积分 | **注意事项**: - `requestId` 务必保证在至少一次完整的业务生命周期内保持唯一,建议客户端每次通过点击或提交生成唯一的 UUID 字符串。 - `templateId` 必须处于“上架”状态,并处于允许兑换的时间范围内。 --- ## 响应数据 返回标准的 `AjaxResult` (或标准格式的 JSON 对象),其中包含接口处理的状态码、提示信息以及新发放的优惠券明细。 ### 成功响应示例 ```json { "code": 200, "msg": "操作成功", "data": { "id": 50012, "couponNo": "CP20260311ABCDE", "exchangeRequestId": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d", "templateId": 102, "memberId": "u_1001", "couponType": 1, "pointsCost": 500.00, "discountRate": null, "status": 0, "source": 1, "exchangeTime": "2026-03-11 10:30:15", "expireTime": "2026-04-10 10:30:15", "useTime": null, "useStoreId": null, "useOperator": null } } ``` ### 失败响应示例 | 场景 | Code | Msg 示例 | |------|------|----------| | 积分不足 | 500 | `用户积分不足,仅剩余 100 积分` | | 券库存不足 | 500 | `该券已兑完` | | 兑换次数达上限 | 500 | `此券单日仅支持兑换 1 次` / `累计兑换次数已达上限` | | 模板不存在或已下架 | 500 | `此券无法兑换` | | 不在指定范围内 | 500 | `您当前所在站点不支持兑换该券` | ```json { "code": 500, "msg": "用户积分不足,仅剩余 100 积分" } ``` --- ## 业务逻辑说明 本接口在服务端实现了以下前置校验与扣除逻辑: 1. **幂等性判定**:如果之前已有相同的 `requestId` 被成功处理过,将直接返回上次处理并落库的该张优惠券数据,不对积分和库存进行额外消耗。 2. **时效与库存判定**:若该券已下架、不在兑换时间范围内,或余量已被耗穿,则终止请求并抛出异常。 3. **频次限制判定**:验证该用户对本模板的单日 (`dailyLimit`)、单月 (`monthlyLimit`) 及总兑换 (`totalLimit`) 限流次数表现。 4. **事务与原子操作**:在单次事务中执行“扣减用户积分”与“扣减优惠券剩余库存/新增用户持券记录”的原子操作,确保资产的一致性。