# Web端商户优惠券核销接口文档

## 模块概述

本模块提供优惠券的核销功能,包括核销前效验和核销订单两个接口,支持代金券和团购券的核销处理。

---

## 接口列表

1. [核销订单前效验](#接口一核销订单前效验) - 验证券是否可用
2. [核销订单](#接口二核销订单) - 实际执行核销操作

---

## 接口一:核销订单前效验

### 接口信息

- **接口名称**:核销订单前效验
- **接口路径**:`GET /couponManage/verifyOrder`
- **请求方式**:GET
- **接口描述**:在核销订单前进行效验,验证券是否满足核销条件(有效期、不可用日期、使用时间段等)

### 请求参数

| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| orderCode | String | 是 | 劵code(券码) |

### 请求示例

```http
GET /couponManage/verifyOrder?orderCode=ABC123456789
```

### 响应参数

#### 成功响应(效验通过)

```json
{
    "code": 200,
    "success": true,
    "data": "效验通过",
    "msg": "效验通过"
}
```

#### 失败响应示例

```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "该劵不在有效期内"
}
```

### 业务规则

详见**核销前效验规则**章节。

---

## 接口二:核销订单

### 接口信息

- **接口名称**:核销订单
- **接口路径**:`GET /couponManage/verifyCoupon`
- **请求方式**:GET
- **接口描述**:执行券的核销操作,更新券状态、计算收入、更新店铺余额

### 请求参数

| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| storeId | String | 是 | 门店ID |
| quanCode | String | 是 | 券码 |

### 请求示例

```http
GET /couponManage/verifyCoupon?storeId=102&quanCode=ABC123456789
```

### 响应参数

#### 成功响应(核销成功)

```json
{
    "code": 200,
    "success": true,
    "data": {
        "code": "true",
        "message": "核销成功"
    }
}
```

#### 失败响应示例

##### 1. 重复核销

```json
{
    "code": 200,
    "success": true,
    "data": {
        "code": "false",
        "message": "请勿重复核销"
    }
}
```

##### 2. 券不存在或状态不正确

```json
{
    "code": 200,
    "success": true,
    "data": {
        "code": "false",
        "message": "核销失败"
    }
}
```

##### 3. 订单不存在

```json
{
    "code": 200,
    "success": true,
    "data": {
        "code": "false",
        "message": "订单不存在"
    }
}
```

##### 4. 门店不存在

```json
{
    "code": 200,
    "success": true,
    "data": {
        "code": "false",
        "message": "门店不存在"
    }
}
```

---

## 核销业务流程

### 完整核销流程图

```
┌─────────────────────────────────────────────┐
│  客户端调用核销接口                           │
│  /couponManage/verifyCoupon                  │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  1. Redis分布式锁                             │
│  检查 coupon:use:{quanCode} 是否存在          │
│  存在 → 返回"请勿重复核销"                    │
│  不存在 → 设置锁并继续                        │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  2. 查询订单券中间表                          │
│  - 根据券码查询                               │
│  - 状态必须为:待使用(1) 或 退款失败(8)       │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  3. 更新订单券状态                            │
│  - status → 已使用(2)                        │
│  - usedTime → 当前时间                       │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  4. 查询用户订单信息                          │
│  - 根据 orderId 查询                         │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  5. 检查平台优惠券                            │
│  - 查询 quanId                               │
│  - 如果 type=3,标记为平台券                  │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  6-7. 查询订单下所有券                        │
│  - 检查是否全部已核销                        │
│  - 筛选未完成的券                            │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  8. 订单完成判断                              │
│  如果所有券都已核销 或 无未完成券             │
│  → 更新订单状态为已完成(3)                   │
│  → 设置完成时间                              │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  9. 查询门店信息                              │
│  - 获取抽成比例 commissionRate               │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  10. 计算抽成和收入                           │
│  抽成 = 券价 × 100 × 抽成比例                │
│  收入 = 券价 × 100 - 抽成                    │
│  (如有平台券,需加上平均优惠价格)             │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  11. 插入收入明细记录                         │
│  - storeId, userOrderId                      │
│  - incomeType, businessId                    │
│  - commission, money                         │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  12. 更新店铺账户余额                         │
│  - store_user.money += 收入金额              │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  13. 返回核销成功                             │
│  { code: "true", message: "核销成功" }       │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  14. 释放分布式锁 (finally)                   │
│  删除 coupon:use:{quanCode}                  │
└─────────────────────────────────────────────┘
```

---

## 核销前效验规则

### 效验内容

核销前效验(`verifyOrder`)会根据券类型进行不同的验证:

#### 代金券验证(couponType=1)

1. **有效期验证**
   - 指定天数:支付时间 + 天数 > 当前时间
   - 指定时间段:开始时间 ≤ 当前时间 ≤ 结束时间

2. **不可用日期验证**
   - 限制星期:检查当前是否为不可用星期
   - 限制节日:检查当前是否在节日范围
   - 自定义日期:检查当前是否在自定义不可用日期

3. **使用时间段验证**
   - 检查当前小时是否在允许使用的时间段内
   - 支持跨天时间段(如22点-3点)

4. **单次核销数量验证**
   - 检查今日已核销次数是否达到上限

#### 团购券验证(couponType=2)

1. **有效期验证**
   - 指定天数:支付时间 + 天数 > 当前时间
   - 指定时间段:开始日期 ≤ 当前日期 ≤ 结束日期

2. **不可用日期验证**
   - 限制星期:检查当前是否为不可用星期
   - 限制节日:检查当前是否在节日范围
   - 自定义日期:检查当前是否在自定义不可用日期

---

## 金额计算规则

### 基础计算公式

#### 1. 抽成金额计算

```
抽成比例(小数) = storeInfo.commissionRate ÷ 100
抽成金额(分) = 券价(元) × 100 × 抽成比例
```

**示例**:
```
券价 = 100元
抽成比例 = 4%
抽成金额 = 100 × 100 × 0.04 = 400分(4元)
```

#### 2. 实际收入计算

##### 普通订单(无平台优惠券)

```
实际收入(分) = 券价(元) × 100 - 抽成金额(分)
```

**示例**:
```
券价 = 100元
抽成 = 400分
实际收入 = 100 × 100 - 400 = 9600分(96元)
```

##### 使用平台优惠券的订单

```
实际收入(分) = (券价(元) + 平均优惠券价格(元)) × 100 - 抽成金额(分)
```

**示例**:
```
券价 = 100元
平台优惠 = 10元
抽成 = 400分
实际收入 = (100 + 10) × 100 - 400 = 10600分(106元)
```

### 金额单位说明

| 存储单位 | 显示单位 | 转换关系 |
|----------|----------|----------|
| 分(数据库) | 元(前端) | 1元 = 100分 |

**注意**:
- 所有金额计算使用 `BigDecimal`,精度为2位小数
- 金额存储到数据库时转换为整数(分)
- 显示给用户时转换为元(除以100)

---

## 并发控制

### Redis分布式锁机制

#### 锁的生命周期

```
┌─────────────────────────────────────────────┐
│  1. 检查锁                                    │
│  getString("coupon:use:{quanCode}")          │
│  如果返回值不为空 → 锁已存在 → 拒绝核销      │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  2. 设置锁                                    │
│  setListRight("coupon:use:{quanCode}",       │
│               quanCode)                      │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  3. 执行核销业务逻辑                          │
│  (数据库操作、金额计算等)                   │
└─────────────────┬───────────────────────────┘
                  ↓
┌─────────────────────────────────────────────┐
│  4. 释放锁 (finally块)                       │
│  delete("coupon:use:{quanCode}")             │
│  确保无论成功失败都会释放                    │
└─────────────────────────────────────────────┘
```

#### 锁的作用

- **防止重复核销**:同一券码在处理过程中不允许再次核销
- **并发安全**:多个核销请求同时到达时,只有一个能获取锁
- **自动释放**:finally块确保锁一定会被释放

---

## 数据库操作

### 涉及的表

#### 1. order_coupon_middle(订单券中间表)

**查询操作**:
```sql
SELECT * FROM order_coupon_middle
WHERE coupon_code = ?
  AND status IN (1, 8)  -- 待使用或退款失败
```

**更新操作**:
```sql
UPDATE order_coupon_middle
SET status = 2,           -- 已使用
    used_time = NOW()
WHERE id = ?
```

#### 2. life_user_order(用户订单表)

**查询操作**:
```sql
SELECT * FROM life_user_order
WHERE id = ?
```

**更新操作**(订单完成时):
```sql
UPDATE life_user_order
SET status = 3,              -- 已完成
    finish_time = NOW(),
    created_time = NOW()
WHERE id = ?
```

#### 3. life_discount_coupon(优惠券表)

**查询操作**(检查平台券):
```sql
SELECT * FROM life_discount_coupon
WHERE id = ?
  AND type = 3  -- 平台券
```

#### 4. store_info(门店信息表)

**查询操作**:
```sql
SELECT * FROM store_info
WHERE id = ?
```

#### 5. store_income_details_record(收入明细表)

**插入操作**:
```sql
INSERT INTO store_income_details_record
(store_id, user_order_id, income_type, business_id, commission, money)
VALUES (?, ?, ?, ?, ?, ?)
```

#### 6. store_user(门店用户表)

**更新操作**:
```sql
UPDATE store_user
SET money = money + ?
WHERE store_id = ?
  AND delete_flag = 0
```

---

## 订单状态说明

### order_coupon_middle 状态

| Status | 枚举常量 | 说明 |
|--------|----------|------|
| 1 | WAIT_USE | 待使用 |
| 2 | USED | 已使用(核销完成) |
| 3 | REFUND | 已退款 |
| 8 | REFUND_FAILED | 退款失败 |

### life_user_order 状态

| Status | 枚举常量 | 说明 |
|--------|----------|------|
| 1 | WAIT_PAY | 待支付 |
| 2 | WAIT_USE | 待使用 |
| 3 | COMPLETE | 已完成 |
| 4 | REFUND | 已退款 |
| 5 | CANCEL | 已取消 |
| 6 | EXPIRE | 已过期 |

---

## 收入明细记录

### StoreIncomeDetailsRecord 字段说明

| 字段名 | 类型 | 说明 | 示例 |
|--------|------|------|------|
| storeId | Integer | 门店ID | 102 |
| userOrderId | Integer | 订单券ID | 12345 |
| incomeType | Integer | 收入类型(券类型) | 1-代金券, 2-团购 |
| businessId | Integer | 业务ID(券ID) | 67890 |
| commission | Integer | 抽成金额(分) | 400 |
| money | Integer | 实际收入(分) | 9600 |

---

## 事务管理

### 事务范围

核销订单接口使用 `@Transactional` 注解,确保以下操作的原子性:

1. 更新订单券状态
2. 更新用户订单状态(如需要)
3. 插入收入明细记录
4. 更新店铺账户余额

### 回滚机制

```java
@Transactional(rollbackFor = Exception.class)
public Map<String, String> verifyCoupon(String storeId, String quanCode) {
    // 任何异常都会触发回滚
}
```

---

## 异常处理

### 异常场景

| 场景 | 返回code | 返回message | 处理方式 |
|------|----------|-------------|----------|
| 重复核销 | "false" | "请勿重复核销" | 检测到锁存在 |
| 券不存在 | "false" | "核销失败" | 查询结果为空 |
| 订单不存在 | "false" | "订单不存在" | 订单查询失败 |
| 门店不存在 | "false" | "门店不存在" | 门店查询失败 |
| 系统异常 | "false" | "核销失败:{异常信息}" | 捕获Exception |

---

## 迁移说明

### 原接口(app端)

- **路径**:`/alienStore/coupon/newVerify`
- **Controller**:`LifeCouponController`
- **Service**:`LifeCouponService.newCouponVerify()`

### 新接口(web端)

- **路径**:`/couponManage/verifyCoupon`
- **Controller**:`CouponManageController`
- **Service**:`CouponManageService.verifyCoupon()`

### 差异说明

| 项目 | app端 | web端 | 说明 |
|------|-------|-------|------|
| 好友券发放 | ✅ 支持 | ⚠️ 暂不支持 | 需通过Feign调用实现 |
| 其他逻辑 | ✅ | ✅ | 完全一致 |

---

## 更新日志

### 2025-11-14

**新增接口**:
- ✅ `GET /couponManage/verifyOrder` - 核销订单前效验
- ✅ `GET /couponManage/verifyCoupon` - 核销订单

**核心功能**:
- ✅ Redis分布式锁防止重复核销
- ✅ 券状态更新(待使用→已使用)
- ✅ 订单状态更新(所有券核销完成→订单完成)
- ✅ 平台优惠券识别和处理
- ✅ 抽成计算(从门店信息获取比例)
- ✅ 收入记录插入
- ✅ 店铺余额更新
- ✅ 完整的事务管理
- ✅ 详细的日志记录
- ✅ 异常处理和回滚机制

**涉及文件**:
- `CouponManageController.java` - 更新
- `CouponManageService.java` - 更新
- `CouponManageServiceImpl.java` - 更新(新增200行)

---

**文档版本**:v1.0  
**最后更新**:2025-11-14  
**维护人员**:ssk

