# 前台住宿查詢 API（`api/hotel`）

與後台 `api-documentation.md` 分離；僅描述**查詢頁**使用之端點。

## 共通

- **商家識別**：與既有前台相同，依網域子網域或本機 `config('daydreamlab.dddream.defaultMerchant')` 對應 `merchants.alias`。
- **回應格式**：JJAJ `ResponseHelper` 或 `Response::success`；前端 axios 攔截後多為 `response.data.data`（內含 `items`、`records` 等）。
- **分店開關**：查詢與建立臨時單皆須 `hotelSetting.productSetting.isEnable=1`，且 `products.state=1`、`hidden=0`。

## 查詢頁建議組合

| 資料 | Path |
|------|------|
| 住宿啟用分店 | `GET /api/v2/hotel/products` |
| 體型分類 | `GET /api/hotel/categories` |
| 單店房型預約查詢（日期／庫存／適用性） | `GET /api/v2/hotel/room-type/booking` |
| 全部房型＋各體型價（無庫存） | `GET /api/v2/hotel/room-types` |

試算用 `POST /api/hotel/calculate`；建立臨時單用 `POST /api/hotel/temporary-order`（亦驗證分店開關）。

舊版 `GET /api/hotel/room-types` 已移除，請改用 `GET /api/v2/hotel/room-type/booking`。

---

## `GET /api/v2/hotel/products`

回傳目前商家**住宿模組已啟用**分店（`hotelSetting.productSetting.isEnable=1`，且 `products.state=1`、`hidden=0`）。

| Query | 必填 | 說明 |
|--------|------|------|
| （無） | — | 商家由子網域識別 |

**成功**：`status` = `HOTEL_PRODUCT_FRONT_V2_INDEX_SUCCESS`（由 message 自動生成）  
**data**：陣列（無其他欄位時不包 `items`）

```json
[
  {
    "id": "98a635d4-f0a2-423c-8d97-8bd328f6b9a3",
    "title": "台北店",
    "alias": "taipei",
    "fullAddress": "台北市大安區忠孝東路一段1號",
    "phoneNumber": "02-12345678",
    "lat": 25.033,
    "lng": 121.565
  }
]
```

外層為 `Response::success`：`{ status, code, message, data }`（與後台 options 類列表相同）。

---

## `GET /api/hotel/categories`

寵物**主分類樹**（**商家層級**）：一次回傳根節點陣列，每筆含 **`childrens`**（直接子分類／體型）。

| Query | 必填 | 說明 |
|--------|------|------|
| `merchant_id` | 是 | 商家 UUID，須與請求子網域所對應之 `merchants.id` 一致 |

**成功**：`status` = `SERVICE_CATEGORY_FRONT_SEARCH_SUCCESS`  
**data**：`{ id, title, alias, type, childrens: [{ id, title, alias, type }] }[]`

---

## `GET /api/v2/hotel/room-types`

回傳上述住宿啟用分店下所有**啟用**房型，每筆含各體型價格；**無**日期庫存／`rooms` 適用性判斷。

| Query | 必填 | 說明 |
|--------|------|------|
| （無） | — | 商家由子網域識別 |

**成功**：`status` = `HOTEL_ROOM_TYPE_FRONT_V2_INDEX_SUCCESS`（由 message 自動生成）  
**data**：陣列（無其他欄位時不包 `items`）

```json
[
  {
    "id": 1,
    "productId": "98a635d4-f0a2-423c-8d97-8bd328f6b9a3",
    "title": "豪華單人房",
    "enTitle": "Deluxe Single",
    "subTitle": null,
    "maxPets": 2,
    "allowedCategoryIds": [10, 11],
    "description": "...",
    "images": [{ "path": "https://...", "sort": 0 }],
    "features": ["冷暖空調"],
    "priceStartAt": 1000,
    "pricesByCategory": [
      {
        "categoryId": 10,
        "pricePerNight": 1000,
        "basePricePerNight": 1000,
        "extraPrice": 300,
        "currency": "TWD"
      }
    ]
  }
]
```

外層為 `Response::success`：`{ status, code, message, data }`。

---

## `GET /api/v2/hotel/room-type/booking`（單一分店＋可選庫存）

查詢**啟用中**房型列表；簡查與詳查同一 route，以是否帶 **`rooms`** 區分。分店須通過 Hotel Setting 開關（與 products 相同），否則回傳產品不存在。

| Query | 必填 | 說明 |
|--------|------|------|
| `product_id` | 是 | 分店（Product）ID：UUID 字串或正整數（與 `products.id` 一致） |
| `check_in` | 否 | `Y-m-d` |
| `check_out` | 否 | `Y-m-d`，須晚於 `check_in`（有帶 `check_in` 時） |
| `main_category_id` | 否 | 主分類 ID；僅保留在該分類樹下**有價格列**之房型 |
| `room_type_id` | 否 | 房型 ID；與 `main_category_id` 採 AND |
| `rooms` | 否 | **URL 編碼**後之 JSON 陣列；每房**必填** `petCount`（1–10）與 `pets`（陣列，可為 `[]`）。例：`[{ "petCount": 2, "pets": [] }]` 或 `[{ "petCount": 2, "pets": [{ "categoryId": 1 }, { "categoryId": 2 }] }]`（pet 亦接受 `sizeId`）。`petCount` 供比對房型 `maxPets`；`pets` 非空時逐筆做體型適用檢查 |

**成功**：`status` = `HOTEL_ROOM_TYPE_FRONT_SEARCH_SUCCESS`  

**items**（物件）：

- `maxSelectableRooms`：全站前台可選房間上限（目前預設 6，可再改設定或後端設定檔）。
- `roomTypes`：陣列，每筆含：
  - 基本：`id`, `productId`, `title`, `enTitle`, `subTitle`, `maxPets`, `description`, `images`, `features`, `priceStartAt`
  - 可入住：`eligible`（boolean）, `message`, `allowedCategoryIds`, `allowedPetCount`
  - 價格：`pricesByCategory[]`：`{ categoryId, pricePerNight, basePricePerNight, currency }`（供前端加總，無獨立試算 API）
  - 庫存占位：`remaining`（目前為 `null`，可後續接排程／庫存）
  - `quantityCap`：該房型可選間數上限（與實體房間數取 min）

## `POST /api/hotel/temporary-order`

建立暫存住宿訂單。`product_id` 須為住宿啟用且可見分店；否則回傳「分店不存在」（404）。

## 部署

主專案執行 `php artisan migrate` 以載入 dddream 遷移（若尚未執行）。
