# 【需求·管理后台】资源默认收费 + 收费=否禁价格日历 + 退费说明接入（景区 / 活动 / 酒店）

> PR: #3392 #3395（工单 #3389）  服务: hl-resource-service | 更新时间: 2026-06-03
> 存放目录: changelogs-v2/2026-06/  影响范围: 管理后台「资源管理 / 景区·游玩项目·酒店」编辑页
> 状态: 已合并 dev-v3 + 测试服部署双实例 + API 实测通过

## ⚠️ 关键说明

来自景区编辑页的 3 点反馈，后端已处理（部分是前端没接已实现的接口）：

1. **默认收费**：景区 / 游玩项目 / 酒店新建时「是否收费」默认改为 **收费(1)**。前端开关初始态请置为「收费」开。
2. **收费=否 → 没有价格日历**：前端在「是否收费=否」时**隐藏价格日历区**；后端已加兜底——给收费=否的景区 / 活动设价会被拒（错误码 **390901**）。
3. **价格日历不做合并接口**：基本信息与价格日历仍是两个接口，前端保存时**分两次调**（先存基本信息，再批量设价）。
4. **退费说明**：后端早已实现（`/admin/refund-note`，覆盖景区+活动），**前端编辑页缺这块 UI**，本文补齐接口契约请接入。

服务资源 ServiceItem 的「是否收费」是 `isPaid`+真实定价语义（另有定价校验），**不在本次默认收费范围**，保持现状。

## 1. 默认收费（景区 / 活动 / 酒店）

新建资源不传 `isCharged` 时，后端默认 **1=收费**；前端显式传 `0` 仍尊重为「否」。

| 资源 | 字段 | 新建接口 | 默认值 |
|------|------|---------|--------|
| 景区 Scenic | `isCharged` | POST `/admin/scenic/spot` | 1=收费 |
| 游玩项目 Activity | `isCharged` | POST `/admin/activity/item` | 1=收费 |
| 酒店 Hotel | `isCharged` | POST `/admin/hotel/item` | 1=收费 |

前端动作：新建表单「是否收费」开关初始置「收费(开)」。编辑回显仍读后端返回的 `isCharged`。

## 2. 收费=否 → 无价格日历

### 2.1 前端
「是否收费=否」时**隐藏价格日历区**（不展示、不允许设价）。

### 2.2 后端兜底（防脏数据）
对收费=否（`isCharged=0`）的景区 / 活动调批量设价接口，后端直接拒绝：

```bash
# 收费=否的景区设价 → 被拦截
curl -X PUT -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{"startDate":"2026-07-01","endDate":"2026-07-03","costPrice":100,"status":1}' \
  "https://api.test.1814.love:9443/admin/scenic/spot/{scenicId}/prices"
```

响应（测试服实测）：

```json
{ "code": 390901, "message": "资源未设为收费，不可设置价格日历" }
```

- 仅拦**批量设价** `PUT /spot/{id}/prices`；查询、改可售状态、清除价格不受影响。
- 酒店价格日历是 room-type 级，本期不做后端兜底，前端按 2.1 隐藏即可。

## 3. 价格日历保存方式（分两次调，不合并接口）

经确认**不新增合并接口**。前端编辑保存时分两次调用现有接口：

1. 先存基本信息：`PUT /admin/scenic/spot/{scenicId}`
2. 再批量设价：`PUT /admin/scenic/spot/{scenicId}/prices`（仅在收费=是时调）

## 4. 退费说明接入（景区 / 活动）

> ⚠️ **本节已废弃（2026-06-03 更新）**：退费说明**已改为融合进资源编辑接口**（不再走独立 `/admin/refund-note`），并扩展支持**服务 SERVICE**。前端请按新 changelog `03_3406_退费说明融合进资源编辑接口-景区活动服务-管理后台.md` 接入，**勿按下方独立接口接**。下方内容仅作历史留存。

后端模块早已上线（PR #3273 等），前端编辑页缺 UI，请接入。仅覆盖 **SCENIC / ACTIVITY** 两类。

### 4.1 接口表

| 操作 | 方法 / 路径 | 参数 |
|------|------------|------|
| 查询 | GET `/admin/refund-note` | query: `resourceType`(SCENIC/ACTIVITY) + `resourceId`。未配置返 `data:null`（不报错） |
| 保存(upsert) | PUT `/admin/refund-note` | body: RefundNoteSaveReqVO（按 resourceType+resourceId upsert 整块） |
| 删除(软删) | DELETE `/admin/refund-note` | query: `resourceType` + `resourceId`（幂等，未配置也返 code:200 data:false） |

### 4.2 保存请求体（RefundNoteSaveReqVO）

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| resourceType | String | 是 | SCENIC / ACTIVITY |
| resourceId | Long | 是 | 关联资源 ID |
| intro | String | 否 | 资源级备注（≤255 字），如「苔藓为赠送项目，不退费」 |
| items | List | 是 | 退费明细，至少 1 条、最多 50 条 |

items[] 每项（RefundNoteItemVO）：

| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| title | String | 是 | 展示标题，如「成人未参加」 |
| amount | BigDecimal | 是 | 退费金额（赠送项目填 0） |
| unitLabel | String | 否 | 展示文案 /人 /团 /辆 |
| settleScope | String | 否 | 结算粒度 PER_PERSON / PER_TEAM / PER_VEHICLE |
| settleScopeLabel | String | — | 结算粒度中文名（出参后端拼，入参可省） |
| remark | String | 否 | 备注，如「仅限儿童」 |
| effectiveFrom | LocalDate | 否 | 规则生效起日（null=无限制） |
| effectiveTo | LocalDate | 否 | 规则生效止日（null=无限制） |

### 4.3 示例

```bash
# 查询景区退费说明
curl -H "Authorization: Bearer <token>" \
  "https://api.test.1814.love:9443/admin/refund-note?resourceType=SCENIC&resourceId=3001000000000000019"

# 保存
curl -X PUT -H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
  -d '{
    "resourceType": "SCENIC",
    "resourceId": 3001000000000000019,
    "intro": "苔藓为赠送项目，不退费",
    "items": [
      {"title":"成人未参加","amount":44.00,"unitLabel":"/人","settleScope":"PER_PERSON","remark":"仅限成人"}
    ]
  }' \
  "https://api.test.1814.love:9443/admin/refund-note"
```

查询响应（RefundNoteRespVO）：`noteId / resourceType / resourceId / intro / items[] / createTime / updateTime`，`noteId` 字符串透传防精度丢失，`settleScopeLabel` 后端回填中文。

## 5. 前端动作清单

1. 景区 / 活动 / 酒店新建表单「是否收费」开关默认置「收费」。
2. 「是否收费=否」时隐藏价格日历区（编辑页 + 新建页）。
3. 保存仍分两次调（基本信息 + 价格日历），价格日历仅收费=是时调。
4. 景区 / 活动编辑页接入「退费说明」模块（GET/PUT/DELETE `/admin/refund-note`）。
