Преглед на файлове

sync API_CONTRACT.md with actual handlers and models (error format, place/service tags, reviews section)

neyrogovnarik преди 2 месеца
родител
ревизия
8573c63a66
променени са 1 файла, в които са добавени 83 реда и са изтрити 74 реда
  1. 83 74
      backend/docs/API_CONTRACT.md

+ 83 - 74
backend/docs/API_CONTRACT.md

@@ -10,20 +10,15 @@
 - **Refresh Token**: JWT, срок 30 дней, передаётся в заголовке `X-Refresh-Token`
 - **Роли**: `superadmin`, `moderator`, `landlord`, `executor`, `customer`
 
-## Формат ошибок (RFC 7807)
+## Формат ошибок
 ```json
 {
-  "type": "https://api.photoplaces.ru/errors/validation-error",
-  "title": "Ошибка валидации",
-  "status": 422,
-  "detail": "Некорректные входные данные",
-  "instance": "/api/v1/places",
-  "errors": [
-    { "field": "title", "message": "Название обязательно" }
-  ]
+  "error": "описание ошибки"
 }
 ```
 
+HTTP статусы: 400 (Bad Request), 401 (Unauthorized), 403 (Forbidden), 404 (Not Found), 409 (Conflict), 422 (Validation Error), 500 (Internal Server Error)
+
 ---
 
 ## Эндпоинты авторизации
@@ -81,12 +76,11 @@
 ### GET /places
 **Параметры запроса:**
 - `type`: `place` | `studio` (по умолчанию оба)
-- `tags`: ID тегов через запятую
+- `tags`: ID тегов (можно несколько, напр. `?tags=portrait&tags=nature`)
 - `min_rating`: число 0-5
-- `features`: ID характеристик через запятую
+- `features`: ID характеристик (можно несколько)
 - `price_min`, `price_max`: для студий
 - `bounds`: `sw_lat,sw_lng,ne_lat,ne_lng` (область карты)
-- `cursor`: курсор пагинации
 - `limit`: 1-100 (по умолчанию 20)
 - `sort`: `rating` | `created_at` | `distance` (требует `user_lat,user_lng`)
 
@@ -97,19 +91,25 @@
     {
       "id": "uuid",
       "type": "place",
+      "owner_id": "uuid",
       "title": "Красивый парк",
-      "cover_image": "https://cdn.../uuid.jpg",
-      "coordinates": { "lat": 55.75, "lng": 37.61 },
+      "description": null,
       "address": "Москва, Парковая 1",
-      "rating": 4.5,
-      "tags": ["nature", "architecture"],
-      "features": ["parking", "public_transport"],
+      "lat": 55.75,
+      "lng": 37.61,
+      "cover_image": "https://cdn.../uuid.jpg",
+      "access_info": null,
       "status": "published",
+      "rating": 4.5,
+      "reviews_count": 12,
+      "hourly_rate": null,
+      "currency": "RUB",
+      "min_hours": 0,
+      "tags": [{ "id": "portrait", "name": "Портрет", "sort_order": 1 }],
+      "features": [{ "id": "parking", "name": "Парковка", "sort_order": 1 }],
       "created_at": "2024-01-15T10:30:00Z"
     }
-  ],
-  "next_cursor": "eyJpZCI6InV1aWQifQ==",
-  "has_more": true
+  ]
 }
 ```
 
@@ -122,28 +122,18 @@
   "title": "Студия 'Свет'",
   "description": "Профессиональная фотостудия...",
   "address": "Москва, ул. Ленина 10, офис 5",
-  "coordinates": { "lat": 55.75, "lng": 37.61 },
+  "lat": 55.75,
+  "lng": 37.61,
   "cover_image": "https://cdn.../cover.jpg",
-  "images": ["https://cdn.../1.jpg", "https://cdn.../2.jpg"],
   "access_info": "Метро 'Площадь Революции', 5 мин пешком. Парковка во дворе.",
-  "tags": ["portrait", "fashion", "product"],
-  "features": [
-    { "id": "flash", "name": "Импульсные источники" },
-    { "id": "continuous", "name": "Постоянный свет" },
-    { "id": "cyclorama", "name": "Циклорама" }
-  ],
-  "pricing": {
-    "hourly_rate": 1500,
-    "currency": "RUB",
-    "min_hours": 2
-  },
+  "tags": [{ "id": "portrait", "name": "Портрет" }],
+  "features": [{ "id": "flash", "name": "Импульсные источники" }],
+  "hourly_rate": 1500,
+  "currency": "RUB",
+  "min_hours": 2,
   "booking_url": "https://booking.photoplaces.ru/studio/uuid",
-  "executor_services": [
-    { "id": "uuid", "title": "Портретная съемка", "price": 5000, "executor_name": "Иван Фотограф" }
-  ],
   "rating": 4.8,
   "reviews_count": 24,
-  "owner": { "id": "uuid", "name": "Студия 'Свет'", "avatar": "..." },
   "status": "published",
   "created_at": "2024-01-15T10:30:00Z",
   "updated_at": "2024-01-20T14:22:00Z"
@@ -159,15 +149,17 @@
   "title": "Новая студия",
   "description": "Описание...",
   "address": "Москва, ул. Новая 5",
-  "coordinates": { "lat": 55.76, "lng": 37.62 },
+  "lat": 55.76,
+  "lng": 37.62,
   "access_info": "Как добраться...",
   "tags": ["portrait", "fashion"],
   "features": ["flash", "cyclorama"],
-  "pricing": { "hourly_rate": 2000, "currency": "RUB", "min_hours": 3 }
+  "hourly_rate": 2000,
+  "currency": "RUB",
+  "min_hours": 3
 }
 
-// Ответ 201
-{ "id": "uuid", "status": "pending_moderation", "message": "Отправлено на проверку" }
+// Ответ 201 — полный объект Place в статусе pending_moderation
 ```
 
 ### PATCH /places/:id
@@ -192,10 +184,11 @@
 ## Эндпоинты услуг исполнителей (Services)
 
 ### GET /services
-**Параметры:** `tags`, `min_rating`, `price_min`, `price_max`, `cursor`, `limit`
+**Параметры:** `tags`, `min_rating`, `price_min`, `price_max`, `limit`
+**Ответ 200:** `{ "data": [ Service ] }`
 
 ### GET /services/:id
-**Ответ 200:** Полная карточка ServiceCard
+**Ответ 200:** Полный объект Service (с тегами)
 
 ### POST /services
 **Роль:** `executor`
@@ -206,10 +199,10 @@
   "price": 5000,
   "currency": "RUB",
   "duration_minutes": 120,
-  "tags": ["portrait", "studio"],
-  "portfolio_images": ["https://cdn.../1.jpg"]
+  "tags": ["portrait", "studio"]
 }
 ```
+- Ответ 201: полный объект Service со статусом `published`
 
 ### PATCH /services/:id
 **Роль:** Владелец (свои услуги), `moderator`, `superadmin`
@@ -238,19 +231,13 @@
 }
 ```
 
-### GET /users/me/places
-**Свои места (все статусы)**
-
-### GET /users/me/services
-**Свои услуги (для исполнителей)**
-
 ---
 
 ## Административные эндпоинты
 
 ### GET /admin/users
 **Роли:** `moderator`, `superadmin`
-**Параметры:** `role`, `status`, `cursor`, `limit`
+**Параметры:** `role`, `status`, `limit`
 
 ### PATCH /admin/users/:id
 **Роли:** `superadmin` (все), `moderator` (кроме superadmin/moderator)
@@ -259,18 +246,10 @@
 // status: active | banned | pending_verification
 ```
 
-### GET /admin/moderation/queue
-**Роли:** `moderator`, `superadmin`
-**Параметры:** `type`: place | studio | service, `status`: pending | rejected
-
-### POST /admin/moderation/:entityType/:id
-**Роли:** `moderator`, `superadmin`
-```json
-{ "action": "approve", "comment": "..." }
-```
-
 ---
 
+## Модерация
+
 ## Бронирования (Bookings)
 
 ### POST /bookings
@@ -293,8 +272,47 @@
 **Роль:** Владелец брони
 - Проверка: `user_id` == текущий пользователь
 
-### PATCH /bookings/:id/confirm
-**Роль:** `moderator`, `superadmin`
+---
+
+## Отзывы (Reviews)
+
+### GET /reviews
+```json
+// Параметры запроса
+// ?target_type=place&target_id=uuid
+
+// Ответ 200
+{
+  "data": [
+    {
+      "id": "uuid",
+      "user_id": "uuid",
+      "target_type": "place",
+      "target_id": "uuid",
+      "rating": 5,
+      "text": "Отличное место",
+      "created_at": "2024-01-15T10:30:00Z",
+      "updated_at": "2024-01-15T10:30:00Z",
+      "user": { "name": "Иван", "avatar_url": "https://cdn.../avatar.jpg" }
+    }
+  ]
+}
+```
+
+### POST /reviews
+**Роль:** Любой авторизованный пользователь
+```json
+{
+  "target_type": "place",
+  "target_id": "uuid",
+  "rating": 5,
+  "text": "Отличное место"
+}
+```
+- `rating`: 1-5
+- `text`: опционально
+- Ответ 201: полный объект Review
+- Ограничение: один отзыв на пользователя на цель (409 Conflict при повторе)
 
 ---
 
@@ -342,10 +360,7 @@
 
 ## Вебхуки
 
-### CloudPayments Webhook
-`POST /webhooks/cloudpayments`
-- Проверка подписи
-- Обновление статуса подписки/платежа
+Планируется интеграция с CloudPayments (`POST /webhooks/cloudpayments`), но пока не реализована.
 
 ---
 
@@ -356,9 +371,3 @@
 - Администрирование: 100 запросов/мин
 
 ---
-
-## Пагинация
-Курсорная:
-- `cursor` = base64(JSON) ключа сортировки последнего элемента + ID
-- `limit` макс. 100
-- Ответ содержит `next_cursor` и `has_more`