# 【修改接口·管理后台】车型价格日历——三模式调价(ABS/DELTA/PERCENT)+节假日筛选+互斥校验落地(§9 全块说明)

> 服务:hl-fleet-service(8087) | 分支:dev-v3 | PR:#3714 | 已部署测试服并 22 项 API 实测通过(2026-06-12)
> 契约出处:FLEET API §9.1-§9.4(文档已同 PR 更新至 v1.5.61) | 前端触发位:车管控制台「价格日历」页(page_pricing 原型 BulkPriceSheet)
> 本文是价格日历**整块 4 接口的完整说明**(应后端要求全面同步),已上线功能 + 本次新增一并列出,前端可按本文直接对接。

## ⚠️ 关键说明

1. **完全向后兼容,已对接代码零改动**:本次新增字段全部可选。不传 `adjustMode` 默认 `ABS`(固定价),与原行为完全一致。
2. **新增三模式调价**(本次 PR):`adjustMode` = `ABS`(设为固定价) / `DELTA`(在各日现价上加减金额) / `PERCENT`(按百分比调整)。`DELTA`/`PERCENT` 时 `dayPrice` 不需要传,改传 `adjustValue`。
3. **DELTA/PERCENT 的基准价规则**:命中日已设价 → 用当日现价计算;未设价 → 用车型 `basePrice` 兜底;两者都没有 → **该日跳过不写**(不报错)。计算结果 < 0 一律按 `0.00` 落库。`PERCENT` 结果四舍五入保留两位(HALF_UP)。
4. **新增仅节假日筛选**(本次 PR):`holidayOnly=true` 只设置节假日(节假日清单由后端 Nacos 配置维护,**当前为验证占位日期,业务法定节假日清单待车管确认后由后端配置**,前端无需关心数据源)。
5. **三筛选互斥**(本次 PR,行为变更):`weekdayOnly` / `weekendOnly` / `holidayOnly` 同时传两个及以上 `true` → 返 `code=400` 参数错(此前是静默 0 命中返成功,**前端若有依赖旧静默行为的逻辑请注意**)。
6. **金额字段两位小数**(本次 PR):`dayPrice`/`adjustValue` 传超过 2 位小数返 `code=400`。
7. **防双击/并发**(本次 PR):三个写端点(批量设价/批量改状态/区间清除)都加了幂等(同参数 3 秒窗口重复提交返「…处理中,请勿重复提交」)+ 同车型并发互斥锁。前端连点按钮会收到 400 类提示,正常单次操作无感。
8. **错误码口径勘正**:参数校验失败统一返业务码 **400**(HTTP 恒 200,错误信息在 `message`)。文档旧版写的 `400001` 为笔误,以 400 为准。
9. 所有金额出参为**字符串**(如 `"800.00"`),雪花 ID 出参为字符串,前端勿当数字解析。

## 1. 查询价格日历(月视图) — 无变更,完整契约

`GET /admin/fleet/pricing-calendar/{vehicleModelId}?year=2026&month=3`

| 参数 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
| vehicleModelId | path | string | 是 | 车型型号 ID(车型树 models[].id) |
| year | query | number | 是 | 年份(2020-2100,越界返 400) |
| month | query | number | 是 | 月份(1-12,越界返 400) |

### 请求示例

```bash
curl -k "https://web.test.1814.love:9443/admin/fleet/pricing-calendar/2057378612593823745?year=2099&month=1" \
  -H "Authorization: Bearer {token}"
```

### 响应示例(测试服实测)

```json
{
  "code": 200,
  "message": "成功",
  "data": {
    "year": 2099,
    "month": 1,
    "vehicleModelId": "2057378612593823745",
    "modelName": "丰田普拉多",
    "prices": [
      { "date": "2099-01-01", "dayPrice": "800.00", "status": "AVAILABLE", "remark": "旺季价格" },
      { "date": "2099-01-08", "dayPrice": "900.00", "status": "CLOSED",    "remark": "临时停租" }
    ]
  }
}
```

| 出参字段 | 说明 |
|---|---|
| prices | **仅返回已设置价格的日期**(未设价日期不出现,空月返回空数组) |
| prices[].dayPrice | 当日租赁价,**字符串**两位小数 |
| prices[].status | 可售状态:`AVAILABLE`=可售 / `CLOSED`=关闭 |

## 2. 批量设置价格 — 本次新增三模式/节假日/互斥,完整契约

`PUT /admin/fleet/pricing-calendar/{vehicleModelId}`

### 入参(body)

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| startDate | string | 是 | 开始日期 yyyy-MM-dd |
| endDate | string | 是 | 结束日期,须 ≥ startDate 且总天数 ≤ 366 |
| adjustMode | string | 否 | 🆕 调价模式:`ABS`=设为固定价 / `DELTA`=现价加减金额 / `PERCENT`=现价按百分比调;**不传默认 ABS**(向后兼容);非法值返 400 |
| dayPrice | string | ABS 必填 | 当日租赁价(¥,≥0,两位小数)。**仅 ABS 模式必填且生效**,DELTA/PERCENT 不需要传 |
| adjustValue | string | DELTA/PERCENT 必填 | 🆕 调整值:DELTA=加减金额(可负,如 `-100.00`) / PERCENT=百分比数值(`10`=+10%,`-5`=-5%)。两位小数 |
| status | string | 否 | `AVAILABLE` / `CLOSED`,不传默认 AVAILABLE,非法值返 400 |
| remark | string | 否 | 备注 |
| weekdayOnly | boolean | 否 | 仅工作日(命中周末跳过) |
| weekendOnly | boolean | 否 | 仅周末(命中工作日跳过) |
| holidayOnly | boolean | 否 | 🆕 仅节假日(命中非节假日跳过,节假日清单后端 Nacos 维护) |
| selectedWeekdays | number[] | 否 | 指定星期几(1=周一…7=周日),非空则只设命中星期 |
| excludeDates | string[] | 否 | 排除日期列表(区间内这些日期不设置) |

> ⚠️ `weekdayOnly`/`weekendOnly`/`holidayOnly` **互斥**,≥2 个同时 true 返 400「工作日/周末/节假日筛选不可同时启用」。

### 请求示例 1——固定价(原有用法,完全不变)

```bash
curl -k -X PUT "https://web.test.1814.love:9443/admin/fleet/pricing-calendar/2057378612593823745" \
  -H "Authorization: Bearer {token}" -H "Content-Type: application/json" \
  -d '{"startDate":"2099-01-01","endDate":"2099-01-05","dayPrice":"800.00","remark":"旺季价格"}'
```

### 请求示例 2——🆕 全月工作日涨 100 元(DELTA)

```bash
curl -k -X PUT "https://web.test.1814.love:9443/admin/fleet/pricing-calendar/2057378612593823745" \
  -H "Authorization: Bearer {token}" -H "Content-Type: application/json" \
  -d '{"startDate":"2099-01-01","endDate":"2099-01-31","adjustMode":"DELTA","adjustValue":"100","weekdayOnly":true}'
```

### 请求示例 3——🆕 节假日统一上浮 15%(PERCENT + holidayOnly)

```bash
curl -k -X PUT "https://web.test.1814.love:9443/admin/fleet/pricing-calendar/2057378612593823745" \
  -H "Authorization: Bearer {token}" -H "Content-Type: application/json" \
  -d '{"startDate":"2099-01-01","endDate":"2099-01-31","adjustMode":"PERCENT","adjustValue":"15","holidayOnly":true}'
```

### 响应

成功:`{ "code": 200, "message": "成功" }`(无 data)

### 测试服实测数据点(可作前端联调预期)

| 操作 | 结果 |
|---|---|
| ABS 800 后 DELTA +100 | 900.00 |
| 900 后 PERCENT -10 | 810.00 |
| DELTA +100 作用于未设价日(车型 basePrice=1000) | 1100.00 |
| DELTA -99999(负穿) | 0.00(兜底) |
| holidayOnly=true,区间 11 天含 1 个节假日 | 仅节假日 1 天写入 |
| weekdayOnly+weekendOnly 同 true | code=400 |
| adjustMode="FOO" / DELTA 缺 adjustValue / ABS 缺 dayPrice / dayPrice 三位小数 | code=400 |

## 3. 批量修改状态 — 无变更(新增幂等防护),完整契约

`PUT /admin/fleet/pricing-calendar/{vehicleModelId}/status`

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| startDate | string | 是 | 开始日期 yyyy-MM-dd |
| endDate | string | 是 | 结束日期 |
| status | string | 是 | `AVAILABLE` / `CLOSED`,非法值返 400 |
| remark | string | 否 | 备注;**不传则保留各记录原备注** |

只改区间内**已有记录**的状态(不影响价格,未设价日期不会凭空建记录)。

```bash
curl -k -X PUT "https://web.test.1814.love:9443/admin/fleet/pricing-calendar/2057378612593823745/status" \
  -H "Authorization: Bearer {token}" -H "Content-Type: application/json" \
  -d '{"startDate":"2099-01-01","endDate":"2099-01-31","status":"CLOSED","remark":"临时停租"}'
```

成功:`{ "code": 200, "message": "成功" }`

## 4. 清除价格日历 — 无变更(新增幂等防护),完整契约

`DELETE /admin/fleet/pricing-calendar/{vehicleModelId}?startDate=2099-01-01&endDate=2099-01-31`

| 参数 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
| startDate | query | string | 是 | 开始日期 yyyy-MM-dd |
| endDate | query | string | 是 | 结束日期 |

软删除区间内该车型全部价格记录;删后**可重新设置同日价格**(测试服实测通过)。

```bash
curl -k -X DELETE "https://web.test.1814.love:9443/admin/fleet/pricing-calendar/2057378612593823745?startDate=2099-01-01&endDate=2099-01-31" \
  -H "Authorization: Bearer {token}"
```

成功:`{ "code": 200, "message": "成功" }`

## 5. 错误码表(§9 全块)

| code | message | 触发场景 | 涉及端点 |
|---|---|---|---|
| 400 | (具体校验消息) | 参数校验失败:必填缺失/格式错/枚举非法/互斥冲突/超两位小数/year·month 越界 | 全部 |
| 600500 | 日租价不能为负 | dayPrice < 0(防御兜底,正常会先被 400 拦) | §9.2 |
| 600501 | 日期范围非法:开始日期不能晚于结束日期 | startDate > endDate | §9.2 |
| 600502 | 日期范围不能超过 366 天 | 区间总天数(含首尾) > 366 | §9.2 |
| 600503 | 车型型号不存在 | vehicleModelId 无效/已软删 | §9.1 / §9.2 / §9.3 |
| 401 | 未登录 | token 缺失/过期 | 全部 |

> §9.4 清除接口不校验车型存在性(按契约,清不存在车型的区间等于无操作返 200)。
> HTTP 状态码恒 200,业务错误看 `code` 字段。
