# 房务清空配房信息与驳回按钮动作契约

- 工单：HL #4777
- 状态：front-end todo
- 影响端：管理后台房务详情弹窗 / 订单详情行程安排住宿卡片

## 背景

已有配房的住宿需求不能直接驳回。后端驳回接口已拦截 active 配房，但前端仍在已有配房时展示可点击的“驳回需求”，导致页面看起来像还能驳回。

本次后端补齐详情动作字段和清空配房接口，前端按后端 actions 渲染按钮，不再自行用流程状态猜测。

## 新增接口

清空当前生效需求全部配房：

```http
DELETE /v3/admin/order/hotel-requirements/{requirementId}/assignments
```

响应：

```json
{
  "code": 200,
  "message": "success",
  "data": {
    "clearedCount": 2
  }
}
```

语义：

- 清空当前生效需求下全部 active 配房行。
- 级联清空房间分配并释放已扣库存。
- 不释放抢单人，不回抢单池，仍由当前房务继续处理。
- 清空成功后刷新订单详情。

## 详情动作字段

订单详情 `actions` 新增：

```json
{
  "canRejectRequirement": {
    "enabled": false,
    "code": "REJECT_REQUIREMENT",
    "label": "驳回需求",
    "disabledReason": "已有配房, 不能驳回, 请先清空/删除配房",
    "endpoint": "POST /v3/admin/order/{orderId}/hotel-requirement/supplier-reject"
  },
  "canClearAssignments": {
    "enabled": true,
    "code": "CLEAR_ASSIGNMENTS",
    "label": "清空配房",
    "disabledReason": null,
    "endpoint": "DELETE /v3/admin/order/hotel-requirements/{requirementId}/assignments"
  }
}
```

`requirement.actions` 同步新增布尔字段：

```json
{
  "canRejectRequirement": false,
  "canClearAssignments": true
}
```

## 前端渲染要求

1. 有任意 active 配房时，`canRejectRequirement.enabled=false`，不要展示可点击“驳回需求”。
2. `canClearAssignments.enabled=true` 时展示“清空配房”按钮。
3. 点击“清空配房”调用新增 DELETE 接口，成功后刷新详情。
4. 清空后如果后端返回 `canRejectRequirement.enabled=true`，再允许填写驳回原因并调用驳回接口。
5. 已驳回需求只读展示，不允许修改、删除、配房、替换、最终确认。

## 验收

- 已配房两晚：驳回按钮隐藏或置灰；清空配房按钮可用。
- 清空成功：配房行消失，状态从待最终确认回到配房中，驳回按钮按详情刷新结果变更。
- 无配房：清空按钮隐藏或置灰；驳回按钮按后端 action 显示。

## 2026-07-07 补充：每个行程日都要有清空按钮

- 补充工单：HL #4804
- 状态：front-end todo
- 后端接口：已补按天清空，整体清空接口保留为批量动作。

### 新增按天清空接口

只清空当前生效需求某一天的 active 配房：

```http
DELETE /v3/admin/order/hotel-requirements/{requirementId}/assignments/days/{dayNumber}
```

响应：

```json
{
  "code": 200,
  "message": "success",
  "data": {
    "clearedCount": 1
  }
}
```

语义：

- 只清空 `{dayNumber}` 当天 active 配房行，不影响其他天。
- 级联清空当天房间分配。
- 只释放实际扣过资源库存的配房行；`deductInventory=false` 的配房快照不释放库存。
- 不释放抢单人，不回抢单池。
- 清空成功后刷新订单详情；该天应回到待配房，其他天保持原配房状态。

### 前端渲染要求

1. 在每个行程 day 行展示“清空”按钮；不能只在弹窗底部放整体清空。
2. 当 `itinerary[].assignments` 非空时，该 day 的“清空”按钮可用。
3. 点击 day 行“清空”时调用新增按天清空接口：
   `DELETE /v3/admin/order/hotel-requirements/{requirementId}/assignments/days/{dayNumber}`。
4. 清空前仍需要二次确认，文案要说明“只清空第 N 晚配房，不影响其他晚”。
5. 底部整体“清空配房”可保留，但语义必须是清空当前需求全部天；不要替代 day 行清空按钮。

### 补充验收

- 两晚订单第 1 晚、第 2 晚都有独立“清空”按钮。
- 清空第 1 晚后，第 1 晚回到待配房，第 2 晚配房仍保留。
- 清空不扣系统库存的配房时，不应释放资源库存。

## 2026-07-07 补充：有任意配房/询房数据时禁止驳回

本次补充针对前端底部“驳回需求”按钮误展示问题。后端口径不变，但前端需要严格按详情动作字段渲染，不能只看 `houseStatus` / 流程节点 / 是否处于“配房中”。

### 后端已验证行为

测试订单 `2074303545149980674` 当前第 1 晚已有配房/询房数据：

```json
{
  "actions": {
    "canRejectRequirement": {
      "enabled": false,
      "code": "REJECT_REQUIREMENT",
      "label": "驳回需求",
      "disabledReason": "已有配房, 不能驳回, 请先清空/删除配房"
    },
    "canClearAssignments": {
      "enabled": true,
      "code": "CLEAR_ASSIGNMENTS",
      "label": "清空配房"
    }
  },
  "requirement": {
    "actions": {
      "canRejectRequirement": false,
      "canClearAssignments": true
    }
  }
}
```

即使前端绕过按钮直接调用：

```http
POST /v3/admin/order/2074303545149980674/hotel-requirement/supplier-reject
```

后端也返回：

```json
{
  "code": 582086,
  "success": false,
  "message": "该需求已有配房记录, 不能驳回, 请走替换/修改配房"
}
```

### 前端必须调整

1. 底部“驳回需求”按钮以 `data.actions.canRejectRequirement.enabled` 为唯一可点击依据。
2. `enabled=false` 时必须隐藏或禁用按钮，并展示/保留 `disabledReason`，不能因为当前是 `CLAIMING` / “配房中”就展示可点击按钮。
3. 判断“有数据”包括所有 active 配房行：已配房、已确认、询房中、替换中，只要详情里该需求还存在配房/询房行，就不能驳回。
4. 每个 day 行清空后必须重新拉取订单详情；只有全部 day 清空到没有 active 配房行，后端返回 `enabled=true` 时，才允许驳回。
5. 如果同时存在底部整体清空和 day 行清空，驳回按钮仍然只看刷新后的后端 action，不在前端自行推算。

### 补充验收

- 第 1 晚有“询房中”或已选酒店数据、第 2 晚待配房：底部“驳回需求”不可点击。
- 清空第 1 晚后刷新详情，如果所有 day 都无配房/询房数据，底部“驳回需求”才可点击。
- 前端强制调用驳回接口时，收到 `582086` 必须提示错误并刷新详情，不能把订单展示为已驳回。
