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

## 概述

本接口用于核销订单前的效验，支持代金券和团购券两种类型的验证。

---

## 接口信息

### 核销订单前效验

- **接口名称**：核销订单前效验
- **接口路径**：`GET /couponManage/verifyOrder`
- **请求方式**：GET
- **接口描述**：在核销订单前进行效验，验证券是否可用

---

## 请求参数

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

---

## 请求示例

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

---

## 响应参数

### 成功响应（效验通过）

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

### 失败响应示例

#### 1. 券不是待使用状态

```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "该劵不是待使用状态"
}
```

#### 2. 券不在有效期内

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

#### 3. 券在不可用日期内

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

#### 4. 超过今日核销次数

```json
{
    "code": 500,
    "success": false,
    "data": null,
    "msg": "该订单已经超过今日单次核销数量"
}
```

---

## 业务逻辑

### 验证流程图

```
开始验证
    ↓
根据券码查询订单券中间表
    ↓
检查券状态（status=1 待使用）
    ↓
查询用户订单
    ↓
判断券类型
    ├─ couponType=1 → 代金券验证流程
    └─ couponType=2 → 团购券验证流程
```

---

### 代金券验证流程

```
代金券验证开始
    ↓
1. 验证有效期
    ├─ 类型1：指定天数（支付时间+天数）
    └─ 类型2：指定时间段（开始-结束日期）
    ↓
2. 验证不可用日期
    ├─ 类型2：限制星期 + 节日
    └─ 类型3：自定义日期范围
    ↓
3. 验证使用时间段
    ├─ 正常时间段（如 9:00-18:00）
    └─ 跨天时间段（如 22:00-3:00）
    ↓
4. 验证单次核销数量
    └─ 检查今日已核销次数是否超限
    ↓
验证通过
```

---

### 团购券验证流程

```
团购券验证开始
    ↓
1. 验证有效期
    ├─ 类型0：指定天数（支付时间+天数）
    └─ 类型1：指定时间段（开始-结束日期）
    ↓
2. 验证不可用日期
    ├─ 类型1：限制星期 + 节日
    └─ 类型2：自定义日期范围
    ↓
验证通过
```

---

## 详细验证规则

### 1. 有效期验证

#### 代金券有效期

| ExpirationType | 说明 | 验证逻辑 |
|----------------|------|----------|
| 1 | 指定天数 | `支付时间 + 天数 > 当前时间` |
| 2 | 指定时间段 | `开始时间 ≤ 当前时间 ≤ 结束时间` |

**代金券有效期字段**：
- `expirationType`：有效期类型（"1"-指定天数，"2"-指定时间段）
- `expirationDate`：有效天数（当类型为1时）
- `validityPeriod`：有效时间段（当类型为2时，格式：时间戳1,时间戳2）

#### 团购券有效期

| EffectiveDateType | 说明 | 验证逻辑 |
|-------------------|------|----------|
| 0 | 指定天数 | `支付时间 + 天数 > 当前时间` |
| 1 | 指定时间段 | `开始日期 ≤ 当前日期 ≤ 结束日期` |

**团购券有效期字段**：
- `effectiveDateType`：有效期类型（0-指定天数，1-指定时间段）
- `effectiveDateValue`：有效期值
  - 类型0：天数（如 "30"）
  - 类型1：日期段（如 "2025-01-01,2025-12-31"）

---

### 2. 不可用日期验证

#### 代金券不可用日期

| UnusedType | 说明 | 格式 | 示例 |
|------------|------|------|------|
| 2 | 限制星期+节日 | `星期;节日ID` | `星期一,星期二;1,2,3` |
| 3 | 自定义日期范围 | `开始,结束;开始,结束` | `2025-01-01,2025-01-03;2025-02-01,2025-02-05` |

**代金券字段**：
- `unusedType`：不可用类型（"2"-限制星期+节日，"3"-自定义日期）
- `unavaiLableDate`：不可用日期值

#### 团购券不可用日期

| DisableDateType | 说明 | 格式 | 示例 |
|-----------------|------|------|------|
| 1 | 限制星期+节日 | `星期;节日ID` | `星期一,星期二;1,2,3` |
| 2 | 自定义日期范围 | `开始,结束;开始,结束` | `2025-01-01,2025-01-03;2025-02-01,2025-02-05` |

**团购券字段**：
- `disableDateType`：不可用类型（1-限制星期+节日，2-自定义日期）
- `disableDateValue`：不可用日期值

---

### 3. 使用时间段验证（仅代金券）

验证当前时间是否在允许使用的时间段内。

| 字段 | 说明 | 格式 |
|------|------|------|
| buyUseStartTime | 开始时间 | 小时数（0-24） |
| buyUseEndTime | 结束时间 | 小时数（0-24） |

**验证规则**：
```
正常时间段（如 9-18）：
  9 ≤ 当前小时 ≤ 18

跨天时间段（如 22-3）：
  当前小时 ≥ 22 OR 当前小时 ≤ 3
```

**示例**：
- `9,18`：9点到18点可用
- `22,3`：22点到次日3点可用
- `0,24`：全天可用

---

### 4. 单次核销数量验证（仅代金券）

验证同一订单今日是否已超过核销次数限制。

| 字段 | 说明 |
|------|------|
| singleCanUse | 单次可核销次数 |

**验证逻辑**：
```sql
SELECT COUNT(*) 
FROM order_coupon_middle
WHERE order_id = ? 
  AND used_time BETWEEN '今日00:00:00' AND '今日23:59:59'
  AND status = 2

IF count >= singleCanUse THEN
  返回"该订单已经超过今日单次核销数量"
```

**特殊值**：
- `"0"`：无限制
- `"1"`：每天只能核销1次
- `"N"`：每天最多核销N次

---

## 数据库表

### 涉及的表

1. **order_coupon_middle**（订单券中间表）
   - `coupon_code`：券码
   - `status`：状态（1-待使用，2-已使用）
   - `order_id`：订单ID
   - `coupon_id`：券ID
   - `used_time`：使用时间

2. **life_user_order**（用户订单表）
   - `id`：订单ID
   - `coupon_type`：券类型（1-代金券，2-团购）
   - `pay_time`：支付时间

3. **life_coupon**（代金券表）
   - `id`：券ID
   - `expiration_type`：有效期类型
   - `expiration_date`：有效天数
   - `validity_period`：有效时间段
   - `unused_type`：不可用类型
   - `unavai_lable_date`：不可用日期
   - `buy_use_start_time`：开始使用时间
   - `buy_use_end_time`：结束使用时间
   - `single_can_use`：单次可核销次数

4. **life_group_buy_main**（团购主表）
   - `id`：团购ID
   - `effective_date_type`：有效期类型
   - `effective_date_value`：有效期值
   - `disable_date_type`：不可用类型
   - `disable_date_value`：不可用日期值

5. **essential_holiday_comparison**（节日对照表）
   - `id`：节日ID
   - `start_time`：开始时间
   - `end_time`：结束时间

---

## 错误码说明

| 错误信息 | 说明 | 处理建议 |
|----------|------|----------|
| 该劵不是待使用状态 | 券已被使用/过期/取消 | 检查券状态 |
| 该劵不在有效期内 | 券已过期或未到使用时间 | 检查有效期配置 |
| 该劵在不可用日期内 | 当前日期在不可用范围内 | 更换使用日期 |
| 该订单已经超过今日单次核销数量 | 今日核销次数达到上限 | 明日再试 |
| 订单不存在 | 订单ID无效 | 检查订单数据 |
| 代金券不存在 | 券ID无效 | 检查券数据 |
| 团购券不存在 | 团购ID无效 | 检查团购数据 |
| 有效期配置异常 | 配置数据格式错误 | 检查配置 |
| 使用时间配置异常 | 时间格式错误 | 检查配置 |

---

## 技术实现

### Controller 层

```java
@ApiOperation("核销订单前效验")
@GetMapping("/verifyOrder")
public R<String> verifyOrder(@RequestParam("orderCode") String orderCode) {
    log.info("CouponManageController.verifyOrder?orderCode={}", orderCode);
    try {
        return couponManageService.verifyOrder(orderCode);
    } catch (Exception e) {
        log.error("CouponManageController.verifyOrder ERROR: {}", e.getMessage(), e);
        return R.fail("效验失败：" + e.getMessage());
    }
}
```

### Service 层架构

```
verifyOrder (主入口)
    ↓
    ├─ verifyCoupon (代金券验证)
    │   ├─ validateCouponValidity (有效期验证)
    │   ├─ validateCouponUnavailableDate (不可用日期验证)
    │   ├─ validateCouponUseTime (使用时间验证)
    │   └─ validateCouponSingleUse (单次核销验证)
    │
    └─ verifyGroupBuy (团购券验证)
        ├─ validateGroupBuyValidity (有效期验证)
        └─ validateGroupBuyUnavailableDate (不可用日期验证)
```

**优势**：
- ✅ 模块化设计，每个验证逻辑独立
- ✅ 易于扩展和维护
- ✅ 完整的日志记录
- ✅ 详细的异常处理

---

## 测试用例

### 测试场景

#### 1. 正常场景

| 场景 | 券码 | 预期结果 |
|------|------|----------|
| 有效的代金券 | COUPON001 | 效验通过 |
| 有效的团购券 | GROUP001 | 效验通过 |

#### 2. 异常场景

| 场景 | 预期结果 |
|------|----------|
| 券码不存在 | 该劵不是待使用状态 |
| 券已使用 | 该劵不是待使用状态 |
| 券已过期 | 该劵不在有效期内 |
| 当前星期不可用 | 该劵在不可用日期内 |
| 当前时间不可用 | 该劵不在有效期内 |
| 超过核销次数 | 该订单已经超过今日单次核销数量 |

---

## 注意事项

1. **时间相关**
   - 所有时间比较都基于服务器当前时间
   - 跨天时间段需要特殊处理（如22点-3点）
   - 日期格式统一使用 `yyyy-MM-dd`

2. **数据格式**
   - 多个值使用逗号分隔（如 `星期一,星期二`）
   - 范围值使用分号分隔（如 `星期;节日ID`）
   - 日期范围使用逗号分隔（如 `2025-01-01,2025-01-03`）

3. **空值处理**
   - 配置为空或"0"时，表示无限制
   - 需要对所有字段进行空值检查

4. **性能优化**
   - 数据库查询使用索引（券码、订单ID等）
   - 复杂验证逻辑可考虑缓存节日信息

5. **安全性**
   - 验证失败统一返回错误信息，不暴露内部逻辑
   - 记录详细日志用于问题排查

---

## 迁移说明

### 原接口（app端）

- **路径**：`/alienStore/coupon/orderVerify`
- **Controller**：`LifeCouponController`
- **Service**：`LifeCouponService.orderVerify()`

### 新接口（web端）

- **路径**：`/couponManage/verifyOrder`
- **Controller**：`CouponManageController`
- **Service**：`CouponManageService.verifyOrder()`

### 改进点

1. ✅ **代码结构优化**
   - 将验证逻辑拆分为多个独立方法
   - 每个验证步骤职责单一

2. ✅ **错误处理增强**
   - 添加更详细的异常信息
   - 完整的日志记录

3. ✅ **代码可读性**
   - 使用清晰的方法命名
   - 添加详细的注释

4. ✅ **业务逻辑复用**
   - 完全复用原有业务逻辑
   - 保持与app端一致的验证规则

---

## 更新日志

### 2025-11-13

**新增功能**：
- ✅ 创建 `CouponManageController`（优惠券管理控制器）
- ✅ 创建 `CouponManageService` 接口
- ✅ 创建 `CouponManageServiceImpl` 实现类
- ✅ 实现 `verifyOrder` 核销前效验接口
- ✅ 支持代金券和团购券两种类型验证
- ✅ 完整的验证逻辑：
  - 有效期验证
  - 不可用日期验证
  - 使用时间段验证
  - 单次核销数量验证
- ✅ 完整的日志记录和异常处理
- ✅ Linter检查：无错误

**涉及文件**：
- `CouponManageController.java` - 新增
- `CouponManageService.java` - 新增
- `CouponManageServiceImpl.java` - 新增
- `API_COUPON_VERIFY.md` - 新增（本文档）

**开发人员**：ssk

---

## 常见问题

### Q1：券码格式有什么要求？

**A**：券码由系统生成，通常是字母+数字组合，无固定格式要求。

### Q2：一张券可以被核销多次吗？

**A**：取决于代金券的 `singleCanUse` 配置：
- `"0"`：无限制
- `"1"`：每天只能核销1次
- `"N"`：每天最多核销N次

### Q3：跨天时间段如何处理？

**A**：例如22点-3点表示22:00-次日03:00，验证逻辑为：
```
当前小时 >= 22 OR 当前小时 <= 3 → 可用
```

### Q4：节日ID从哪里获取？

**A**：节日ID来自 `essential_holiday_comparison` 表，由系统管理员配置。

### Q5：如何测试这个接口？

**A**：
1. 准备测试数据（创建订单券）
2. 使用 Swagger UI 测试：`http://localhost:port/doc.html`
3. 或使用 Postman/curl 发送GET请求

---

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

