# 菜品优惠接口文档

版本：`v1`  
模块：`alien-store`  
前缀：`/store/productDiscount`

---

## 统一返回结构

接口返回统一为 `R<T>`：

- `code`：200 成功，非 200 失败
- `msg`：提示信息
- `data`：业务数据

---

## 一、优惠规则接口（已实现）

### 1) 列表

- **URL**：`GET /store/productDiscount/list`
- **Query 参数**

| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| storeId | Integer | 是 | 门店ID |
| productId | Integer | 否 | 菜品ID筛选 |
| ruleName | String | 否 | 规则名称模糊搜索 |
| status | Integer | 否 | 0关闭，1开启 |
| pageNum | Integer | 否 | 默认1 |
| pageSize | Integer | 否 | 默认10 |

- **返回 data**：`IPage<StoreProductDiscountRuleListVo>`

| 字段 | 类型 | 说明 |
|---|---|---|
| id | Integer | 规则ID |
| storeId | Integer | 门店ID |
| productId | Integer | 菜品ID |
| productName | String | 菜品名称（当前版本可能为空，见文末说明） |
| ruleName | String | 规则名称 |
| discountType | String | FREE / DISCOUNT |
| discountRate | BigDecimal | 折扣比例（FREE可能为空） |
| effectiveMode | String | PERMANENT / CUSTOM |
| dateRange | String | 日期范围，如 `2026-04-01 ~ 2026-04-30` 或 `永久生效` |
| slotCount | Integer | 时段数量 |
| status | Integer | 0关闭，1开启 |
| updatedTime | DateTime | 更新时间 |

---

### 2) 详情（编辑回显）

- **URL**：`GET /store/productDiscount/detail?id={id}`
- **Query 参数**

| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | Integer | 是 | 规则ID |

- **返回 data**：`StoreProductDiscountRuleDetailVo`

| 字段 | 类型 | 说明 |
|---|---|---|
| id | Integer | 规则ID |
| storeId | Integer | 门店ID |
| productId | Integer | 菜品ID |
| productName | String | 菜品名称（当前版本可能为空） |
| ruleName | String | 规则名称 |
| discountType | String | FREE / DISCOUNT |
| discountRate | BigDecimal | 折扣比例 |
| effectiveMode | String | PERMANENT / CUSTOM |
| startDate | String | 开始日期 yyyy-MM-dd |
| endDate | String | 结束日期 yyyy-MM-dd |
| status | Integer | 状态 |
| slots | Array | 生效时段列表 |

`slots` 子项：

| 字段 | 类型 | 说明 |
|---|---|---|
| weekdayMask | Integer | 星期掩码（周一到周日按位） |
| startTime | String | HH:mm:ss |
| endTime | String | HH:mm:ss |

---

### 3) 新建优惠

- **URL**：`POST /store/productDiscount/create`
- **Content-Type**：`application/json`

请求体：`StoreProductDiscountRuleSaveDto`

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| storeId | Integer | 是 | 门店ID |
| productId | Integer | 是 | 菜品ID |
| ruleName | String | 是 | 规则名称（同店同菜品唯一） |
| discountType | String | 是 | FREE / DISCOUNT |
| discountRate | BigDecimal | DISCOUNT必填 | 折扣比例（0-100） |
| effectiveMode | String | 是 | PERMANENT / CUSTOM |
| startDate | String | CUSTOM必填 | yyyy-MM-dd |
| endDate | String | CUSTOM必填 | yyyy-MM-dd |
| slots | Array | 否 | 生效时段（FREE/DISCOUNT均可不传） |
| status | Integer | 是 | 0关闭，1开启 |

请求示例（折扣）：

```json
{
  "storeId": 1,
  "productId": 10001,
  "ruleName": "周末八折",
  "discountType": "DISCOUNT",
  "discountRate": 80,
  "effectiveMode": "CUSTOM",
  "startDate": "2026-04-01",
  "endDate": "2026-06-30",
  "slots": [
    {
      "weekdayMask": 65,
      "startTime": "10:00:00",
      "endTime": "22:00:00"
    }
  ],
  "status": 1
}
```

请求示例（免费）：

```json
{
  "storeId": 1,
  "productId": 10001,
  "ruleName": "工作日免费",
  "discountType": "FREE",
  "effectiveMode": "PERMANENT",
  "status": 1
}
```

---

### 4) 编辑优惠

- **URL**：`POST /store/productDiscount/update`
- **Content-Type**：`application/json`
- 请求体同“新建”，且 `id` 必填

---

### 5) 删除优惠

- **URL**：`POST /store/productDiscount/delete?id={id}`

| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | Integer | 是 | 规则ID |

---

### 6) 切换启用状态

- **URL**：`POST /store/productDiscount/switchStatus?id={id}&status={0|1}`

| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| id | Integer | 是 | 规则ID |
| status | Integer | 是 | 0关闭，1开启 |

---

## 二、核心校验规则（后端已实现）

1. **名称唯一**：同门店同菜品下，`ruleName` 不可重复  
2. **DISCOUNT 规则**：`discountRate` 必填，且仅支持 `effectiveMode=CUSTOM`  
3. **CUSTOM 日期**：`startDate/endDate` 必填，且开始不能晚于结束  
4. **时间格式**：`startTime/endTime` 必须成对出现，且开始早于结束  
5. **时间冲突校验（同店同菜品，开启状态）**：
   - 日期有交集
   - 星期有交集（weekdayMask 按位与 > 0）
   - 时间有交集（start1 < end2 && start2 < end1）
   - 若重叠且类型不同：报“该菜品在该时间段已配置其他类型优惠”
   - 若重叠且类型相同：报“该菜品优惠时间重叠，请调整生效日期/星期/时间”
6. **未配置时段时**：按“全星期+全天”参与冲突判断

---

## 三、常见错误提示

- `门店ID不能为空`
- `菜品ID不能为空`
- `规则名称不能为空`
- `优惠类型不能为空`
- `优惠类型不合法`
- `折扣比例不能为空`
- `折扣优惠仅支持自定义日期`
- `自定义日期开始/结束不能为空`
- `开始日期不能大于结束日期`
- `生效时间需成对出现`
- `生效开始时间必须早于结束时间`
- `该菜品优惠名称已存在`
- `该菜品在该时间段已配置其他类型优惠`
- `该菜品优惠时间重叠，请调整生效日期/星期/时间`

---

## 四、联调注意事项

1. 当前代码中 `productName` 字段映射查询尚未接入真实菜品表查询，返回可能为空；建议联调先以 `productId` 为主。  
2. 你提到的“菜品列表（按名称模糊）”接口，目前尚未在本次代码中新增；如需我可补：
   - `GET /store/product/list?storeId=1&keyword=鱼&pageNum=1&pageSize=10`

