# 学时套餐接口文档

## 0. 相关文档

- 学时套餐购买接口文档：`../hourPackagePurchase/hour-package-purchase.md`

## 1. 模块说明

- 模块：总部端学时套餐管理
- 控制器：`/operationManagement/hourPackage`
- 当前套餐类型固定为 `1=通用`
- 当前暂不需要前端传“适用类型”，后端会统一按通用套餐处理

## 2. 套餐字段说明

### 2.1 返回字段

| 字段名               | 类型           | 说明                              |
|-------------------|--------------|---------------------------------|
| `id`              | `Long`       | 套餐ID                            |
| `packageCode`     | `String`     | 套餐编码，新增时后端自动生成                  |
| `packageName`     | `String`     | 套餐名                             |
| `hourType`        | `Long`       | 套餐类型，当前固定 `1`                   |
| `hourCount`       | `Long`       | 学时                              |
| `originPrice`     | `BigDecimal` | 套餐原价格                           |
| `packagePrice`    | `BigDecimal` | 套餐活动价，可为空                       |
| `unitPrice`       | `BigDecimal` | 学时单价；有活动价取 `活动价/学时`，否则取 `原价/学时` |
| `packageDesc`     | `String`     | 套餐说明                            |
| `status`          | `Long`       | 是否启用：`1=启用`，`0=停用`              |
| `remark`          | `String`     | 备注                              |
| `sortNo`          | `Long`       | 排序号                             |
| `applicableTypes` | `String`     | 适用类型，当前固定为 `"1"`                |
| `createByName`    | `String`     | 创建人姓名                           |
| `updateByName`    | `String`     | 更新人姓名                           |

### 2.2 请求字段

| 字段名            | 类型           | 必填    | 说明                       |
|----------------|--------------|-------|--------------------------|
| `id`           | `Long`       | 修改时必填 | 套餐ID                     |
| `packageName`  | `String`     | 是     | 套餐名                      |
| `originPrice`  | `BigDecimal` | 是     | 套餐原价格，必须大于 `0`           |
| `packagePrice` | `BigDecimal` | 否     | 套餐活动价；传了必须大于 `0` 且不能大于原价 |
| `hourCount`    | `Long`       | 是     | 学时，必须大于 `0`              |
| `packageDesc`  | `String`     | 否     | 套餐说明                     |
| `status`       | `Long`       | 是     | 是否启用：`1=启用`，`0=停用`       |
| `remark`       | `String`     | 否     | 备注                       |
| `sortNo`       | `Long`       | 否     | 排序号                      |

说明：

- `hourType` 不需要前端传，后端固定写入 `1=通用`
- `applicableTypes` 不需要前端传，后端固定写入 `"1"`
- 修改接口如果未传 `packagePrice`、`packageDesc`、`remark`、`sortNo`，后端会保留原值

## 3. 接口1：分页查询学时套餐列表

- 地址：`GET /operationManagement/hourPackage/list`
- 说明：分页返回学时套餐列表

### 3.1 请求参数

| 参数名            | 类型           | 必填 | 说明                 |
|----------------|--------------|----|--------------------|
| `pageNum`      | `Integer`    | 是  | 页码                 |
| `pageSize`     | `Integer`    | 是  | 每页条数               |
| `packageName`  | `String`     | 否  | 套餐名，模糊查询           |
| `hourCount`    | `Long`       | 否  | 学时，精确查询            |
| `originPrice`  | `BigDecimal` | 否  | 原价格，精确查询           |
| `packagePrice` | `BigDecimal` | 否  | 活动价，精确查询           |
| `status`       | `Long`       | 否  | 是否启用：`1=启用`，`0=停用` |
| `packageDesc`  | `String`     | 否  | 套餐说明，模糊查询          |

### 3.2 返回示例

```json
{
  "code": 200,
  "rows": [
    {
      "id": 1,
      "packageCode": "HP1931512345678901234",
      "packageName": "基础学时包",
      "hourType": 1,
      "hourCount": 40,
      "originPrice": 1000.00,
      "packagePrice": 800.00,
      "unitPrice": 20.00,
      "packageDesc": "总部通用学时套餐",
      "status": 1,
      "remark": "活动套餐",
      "sortNo": 0,
      "applicableTypes": "1"
    }
  ],
  "total": 1,
  "msg": "查询成功"
}
```

## 4. 接口2：修改指定套餐

- 地址：`PUT /operationManagement/hourPackage`
- 说明：修改套餐名、套餐原价格、学时、是否启用；活动价和套餐说明如果不传则保留原值

### 4.1 请求示例

```json
{
  "id": 1,
  "packageName": "基础学时包",
  "originPrice": 1000.00,
  "hourCount": 40,
  "status": 1
}
```

### 4.2 返回示例

```json
{
  "code": 200,
  "msg": "操作成功",
  "data": null
}
```

## 5. 接口3：删除套餐

- 地址：`DELETE /operationManagement/hourPackage/{ids}`
- 说明：支持单个或批量删除

### 5.1 请求示例

```text
DELETE /operationManagement/hourPackage/1
```

或

```text
DELETE /operationManagement/hourPackage/1,2,3
```

### 5.2 返回示例

```json
{
  "code": 200,
  "msg": "操作成功",
  "data": null
}
```

## 6. 接口4：新增套餐

- 地址：`POST /operationManagement/hourPackage`
- 说明：新增总部端学时套餐

### 6.1 请求示例

```json
{
  "packageName": "高级学时包",
  "originPrice": 2000.00,
  "packagePrice": 1500.00,
  "hourCount": 80,
  "packageDesc": "适用于总部统一活动投放",
  "status": 1,
  "remark": "新客活动",
  "sortNo": 1
}
```

### 6.2 返回示例

```json
{
  "code": 200,
  "msg": "操作成功",
  "data": null
}
```

## 7. 业务规则

- 套餐类型固定：`1=通用`
- 套餐名不能为空，且不能重复
- 学时必须大于 `0`
- 套餐原价格必须大于 `0`
- 套餐活动价如果传入，必须大于 `0`，且不能大于原价格
- 学时单价由后端计算，前端不需要传
- 新增时如果未传 `packageCode`，由后端自动生成

## 8. 前端表格字段对照清单

### 8.1 列表页字段

| 列表列名  | 对应字段           | 展示建议        | 说明             |
|-------|----------------|-------------|----------------|
| 套餐名   | `packageName`  | 文本          | 必显             |
| 套餐原价格 | `originPrice`  | 金额，保留两位小数   | 可显示为 `1000.00` |
| 学时    | `hourCount`    | 数字          | 建议后缀显示“学时”     |
| 套餐活动价 | `packagePrice` | 金额，空值显示 `-` | 没有活动价时为空       |
| 学时单价  | `unitPrice`    | 金额，保留两位小数   | 后端已计算好，直接展示    |
| 套餐说明  | `packageDesc`  | 文本，过长省略     | 鼠标悬停可看完整内容     |
| 是否启用  | `status`       | 开关/标签       | `1=启用`，`0=停用`  |
| 操作    | `id`           | 按钮组         | `编辑`、`删除`      |

### 8.2 列表页筛选项

| 筛选项  | 对应参数           | 组件建议  | 说明             |
|------|----------------|-------|----------------|
| 套餐名  | `packageName`  | 输入框   | 模糊查询           |
| 学时   | `hourCount`    | 数字输入框 | 精确查询           |
| 原价格  | `originPrice`  | 金额输入框 | 精确查询           |
| 活动价  | `packagePrice` | 金额输入框 | 精确查询           |
| 是否启用 | `status`       | 下拉框   | `全部 / 启用 / 停用` |
| 套餐说明 | `packageDesc`  | 输入框   | 模糊查询           |

### 8.3 列表页接口对接

- 请求地址：`GET /operationManagement/hourPackage/list`
- 分页参数：`pageNum`、`pageSize`
- 表格数据源：响应中的 `rows`
- 总数：响应中的 `total`

## 9. 前端弹窗表单字段对照清单

### 9.1 新增/编辑弹窗字段

| 表单项   | 对应字段           | 必填 | 组件建议  | 说明                      |
|-------|----------------|----|-------|-------------------------|
| 套餐名   | `packageName`  | 是  | 输入框   | 最长建议前端限制 `100` 字        |
| 套餐原价格 | `originPrice`  | 是  | 金额输入框 | 必须大于 `0`                |
| 学时    | `hourCount`    | 是  | 数字输入框 | 必须大于 `0`                |
| 套餐活动价 | `packagePrice` | 否  | 金额输入框 | 可为空；传了必须大于 `0` 且不能大于原价格 |
| 套餐说明  | `packageDesc`  | 否  | 多行文本框 | 建议前端限制 `500` 字          |
| 是否启用  | `status`       | 是  | 开关/单选 | `1=启用`，`0=停用`           |
| 备注    | `remark`       | 否  | 输入框   | 内部管理备注                  |
| 排序号   | `sortNo`       | 否  | 数字输入框 | 可选                      |

### 9.2 表单中不需要前端传的字段

| 字段                | 原因           |
|-------------------|--------------|
| `id`              | 新增时不用传，编辑时才传 |
| `packageCode`     | 新增时后端自动生成    |
| `hourType`        | 后端固定为 `1=通用` |
| `applicableTypes` | 后端固定为 `"1"`  |
| `unitPrice`       | 后端自动计算       |
| `createByName`    | 后端返回字段       |
| `updateByName`    | 后端返回字段       |

### 9.3 新增弹窗提交示例

```json
{
  "packageName": "标准学时包",
  "originPrice": 1200.00,
  "hourCount": 60,
  "packagePrice": 999.00,
  "packageDesc": "用于总部常规投放",
  "status": 1,
  "remark": "默认套餐",
  "sortNo": 1
}
```

### 9.4 编辑弹窗回显建议

- 先调 `GET /operationManagement/hourPackage/{id}` 获取详情
- 弹窗回显字段建议：
    - `packageName`
    - `originPrice`
    - `hourCount`
    - `packagePrice`
    - `packageDesc`
    - `status`
    - `remark`
    - `sortNo`
- `unitPrice` 可只读展示，不参与提交

## 10. 前端实现建议

- 列表页的“是否启用”建议直接渲染成标签：
    - `status=1` -> `启用`
    - `status=0` -> `停用`
- 活动价为空时，列表页显示 `-`
- 学时单价直接使用后端返回值，不要前端自行计算
- 新增/编辑提交前，前端可先做基础校验：
    - 套餐名不能为空
    - 原价格必须大于 `0`
    - 学时必须大于 `0`
    - 活动价若填写，不能大于原价格
