Docs: sync Back#61 specialist_id + trial/402 (no auto-start). Fixes EventHub/EventHubSpec#14
This commit is contained in:
+21
-12
@@ -78,14 +78,17 @@ EventHub — платформа для управления событиями
|
||||
слоты освобождаются;
|
||||
- уже `confirmed` — **оставляем**;
|
||||
- owner: просмотр/редактирование своих событий и настроек, CRUD specialists — **разрешены**;
|
||||
- create нового commercial / upgrade type → `402`.
|
||||
- create нового commercial / upgrade type → `402` (нужна **уже active** подписка
|
||||
или trial; auto-start trial при create **нет** — см. ниже и §2.9).
|
||||
|
||||
Периодический job (например раз в 60 с): помечает просроченные подписки `expired`, отменяет
|
||||
pending владельца, **не** меняет `calendar.type`. Legacy `downgrade_user_calendars/1`
|
||||
(type → personal) — удалить/не использовать.
|
||||
|
||||
Первое создание commercial без подписки: **auto-start trial** (один раз, `trial_used`);
|
||||
повтор без подписки → `402`. Планы/цены — §2.9.
|
||||
Create / upgrade `commercial` без **уже active** подписки или trial → **`402`**.
|
||||
Trial стартует **только** явным `POST /v1/subscription` с `action=start_trial`
|
||||
(один раз, `trial_used`); при create commercial auto-start trial **нет**.
|
||||
Планы/цены — §2.9.
|
||||
|
||||
В ответах календаря (user API): `booking_open` (boolean) — производное от
|
||||
`type=commercial` ∧ calendar `active` ∧ подписка владельца active.
|
||||
@@ -173,8 +176,9 @@ Legacy: прямой `POST /v1/calendars/:id/specialists` **не использ
|
||||
допускается только как внутренний/тестовый путь или удаляется после миграции на invite.
|
||||
Продуктовый путь: invite → accept → specialist.
|
||||
|
||||
- `event.specialist_id` опционален; если задан — только `active` specialist этого календаря
|
||||
(иначе `400`).
|
||||
- `event.specialist_id` опционален на `POST`/`PUT` события; если задан — персистится
|
||||
и валидируется: только `active` specialist этого календаря (иначе `400`
|
||||
`Invalid specialist_id for this calendar`).
|
||||
- **Confirm/decline booking:** владелец — любые booking календаря; `active` specialist — только
|
||||
booking на событиях, где `event.specialist_id` = его `user_id`.
|
||||
- **Inbox к подтверждению:** `GET /v1/user/booking-requests` агрегирует **actionable** pending
|
||||
@@ -339,8 +343,9 @@ Legacy: прямой `POST /v1/calendars/:id/specialists` **не использ
|
||||
- Цены (`logic_subscription:plan_price/1`, minor units): monthly **999**, quarterly **2499**,
|
||||
biannual **4499**, annual **7999**; trial — 0. Платёжный шлюз — заглушка (`process_payment` → ok).
|
||||
- Статус подписки: `active`, `expired`, `cancelled`.
|
||||
- Отслеживание использования пробного периода (`trial_used`); trial стартует автоматически при
|
||||
первой попытке создать/апгрейднуть commercial (§2.1.2).
|
||||
- Отслеживание использования пробного периода (`trial_used`); trial стартует **только**
|
||||
явным `POST /v1/subscription` (`action=start_trial`), не при create/upgrade commercial
|
||||
(§2.1.2). Create/upgrade commercial без уже active sub/trial → `402`.
|
||||
- Истечение: периодический job → `expired`; commercial-календари **не** меняют type —
|
||||
переходят в restricted; после `activate` / новой active подписки функционал восстанавливается
|
||||
(§2.1.2). Не использовать legacy downgrade type → personal.
|
||||
@@ -480,9 +485,11 @@ src/
|
||||
В calendar-результатах — `image_url` (если задан на календаре). Upload/multipart
|
||||
**не** входит в контракт: поле URL.
|
||||
- `GET /v1/calendars` — список календарей.
|
||||
- `POST /v1/calendars` — создать календарь (`commercial` → подписка/trial; иначе `402`).
|
||||
- `POST /v1/calendars` — создать календарь (`commercial` → нужна уже active sub/trial;
|
||||
иначе `402`; auto-start trial нет).
|
||||
- `GET /v1/calendars/:id` — календарь (`following`, `booking_open` для текущего контекста).
|
||||
- `PUT /v1/calendars/:id` — обновить календарь (`personal→commercial` → gate подписки).
|
||||
- `PUT /v1/calendars/:id` — обновить календарь (`personal→commercial` → нужна уже
|
||||
active sub/trial, иначе `402`).
|
||||
- `DELETE /v1/calendars/:id` — удалить календарь.
|
||||
- `POST /v1/calendars/:id/follow` — отслеживать чужой календарь.
|
||||
- `DELETE /v1/calendars/:id/follow` — снять follow.
|
||||
@@ -495,9 +502,10 @@ src/
|
||||
- `POST /v1/specialist-invites/:id/accept` | `…/decline` — ответ invitee.
|
||||
- `POST /v1/specialist-invites/accept` `{ token }` — accept по email deep-link.
|
||||
- `GET /v1/calendars/:calendar_id/events` — события календаря.
|
||||
- `POST /v1/calendars/:calendar_id/events` — создать событие.
|
||||
- `POST /v1/calendars/:calendar_id/events` — создать событие (тело может включать
|
||||
опциональный `specialist_id`; invalid → `400`).
|
||||
- `GET /v1/events/:id` — событие.
|
||||
- `PUT /v1/events/:id` — обновить событие.
|
||||
- `PUT /v1/events/:id` — обновить событие (в т.ч. `specialist_id`; invalid → `400`).
|
||||
- `DELETE /v1/events/:id` — удалить событие.
|
||||
- `GET /v1/events/:id/occurrences` — вхождения повторяющегося события.
|
||||
- `DELETE /v1/events/:id/occurrences/:start_time` — отменить вхождение серии.
|
||||
@@ -522,7 +530,8 @@ src/
|
||||
- `POST /v1/tickets` — создать или обновить тикет (дедуп по `error_hash`): body `{ error_message, stacktrace?, context?, source?: frontend|manual }`; ответ 201 (или 429 при rate-limit новых).
|
||||
- `GET /v1/tickets/:id` — статус тикета.
|
||||
- `GET /v1/subscription` — подписка пользователя.
|
||||
- `POST /v1/subscription` — активировать подписку.
|
||||
- `POST /v1/subscription` — `action=start_trial` | `activate` (+ `plan`, опционально
|
||||
`payment_info`). Trial — только через `start_trial`, не через create commercial.
|
||||
- `GET /v1/calendars/:calendar_id/view?month=YYYY-MM` — HTML-календарь (владелец), включая архив.
|
||||
|
||||
### WebSocket (порт 8081)
|
||||
|
||||
@@ -142,7 +142,8 @@ cover (`.eh-discover-media--photo`); иначе mood wash + initials (`.eh-disco
|
||||
- Просмотр коммерческого чужого календаря по id; при `booking_open=false` — баннер
|
||||
«Запись временно недоступна», кнопка записи скрыта
|
||||
- HTML month view владельца: `GET /v1/calendars/:calendar_id/view?month=YYYY-MM` — при переключении виджета на **прошедший месяц** (owner + вид «Месяц») автоматически подменяет клиентский grid серверным HTML (live/архив, задача #15). Текущий и будущие месяцы — React-виджет по `GET …/events`.
|
||||
- Создание / апгрейд `commercial` без подписки → `402` → `/subscription`; после оплаты —
|
||||
- Создание / апгрейд `commercial` без **уже active** sub/trial → `402` → `/subscription`
|
||||
(trial — явный `start_trial`, не auto при create); после оплаты —
|
||||
возврат к созданию/редактированию
|
||||
- Владелец commercial: специалисты **через invite** (не сырой `user_id`):
|
||||
typeahead `GET /v1/users/lookup` и/или email → `POST …/specialist-invites`;
|
||||
@@ -191,8 +192,10 @@ UI: форма отзыва скрыта без confirmed booking (event — boo
|
||||
`GET /v1/subscription`, `POST /v1/subscription` (`start_trial` | `activate` + `plan` + опционально `payment_info`).
|
||||
Планы и цены (как Back `plan_price/1`, minor units → ₽): monthly **999**, quarterly **2499**, biannual **4499**, annual **7999**.
|
||||
UI: карточки сравнения, локализованный «Бесплатно» без подписки, trial, демо-оплата (шлюз-заглушка).
|
||||
Создание commercial без подписки → `402` → `/subscription`. После renew commercial-календари
|
||||
владельца снова с `booking_open=true` без смены type (BackSpec §2.1.2 restricted → full).
|
||||
Create/upgrade commercial без **уже active** sub/trial → `402` → `/subscription`
|
||||
(сначала явный `start_trial` или `activate`; auto-start trial при create **нет**,
|
||||
Back#61 / BackSpec §2.1.2, §2.9). После renew commercial-календари владельца снова
|
||||
с `booking_open=true` без смены type (restricted → full).
|
||||
|
||||
### 5.10. Тикеты
|
||||
- ErrorBoundary / необработанные ошибки → `POST /v1/tickets` с `source=frontend`
|
||||
|
||||
+4
-1
@@ -73,7 +73,7 @@ PowerShell в Cursor **ломает экранирование** (кавычки
|
||||
| Windows `npm` / `erl` / `rebar3` | npm и rebar **только в WSL** |
|
||||
| Push Front без локальной проверки | WSL: `npm run lint && npm run build && npm run test:e2e:ift`, потом push |
|
||||
|
||||
Канон для агентов: `EventHubBack/.cursor/rules/shell-wsl-not-powershell.mdc`.
|
||||
Канон для агентов: `EventHubBack/.cursor/rules/shell-wsl-not-powershell.mdc`, **`agent-pitfalls.mdc`** (типовые сбои shell/git/docker).
|
||||
|
||||
| Tool | Version | Path (WSL) |
|
||||
|------|---------|------------|
|
||||
@@ -363,6 +363,7 @@ error rate > 1% **или** mean request > 500 ms (hold ≥2 мин в фазе)
|
||||
- Одна задача — один логический объём работы.
|
||||
- Код фичи — в репозитории-владельце; `EventHubSpec` — для документации и процесса.
|
||||
- **Один агент — один репо** (не смешивать Front/Back/Spec/FrontAdmin в одной сессии): `.cursor/rules/one-repo-per-agent.mdc`.
|
||||
- **Типовые ошибки агентов** (PS, git clean, docker scale, scope): `EventHubBack/.cursor/rules/agent-pitfalls.mdc`; в промпт субагента включать shell + pitfalls.
|
||||
- Метки во всех репозиториях держать одинаковыми (`sync-labels.ps1`).
|
||||
- Секреты и токены не коммитить; Gitea token — только в env / `~/.cursor/secrets/`.
|
||||
- **Git commit/push — через WSL** (`~/.cursor/eventhub/git-commit.sh`, `git-push.sh`); **push — только после подтверждения пользователя** (см. §2.1).
|
||||
@@ -378,6 +379,8 @@ error rate > 1% **или** mean request > 500 ms (hold ≥2 мин в фазе)
|
||||
|
||||
| Дата | Изменение |
|
||||
|------|-----------|
|
||||
| 2026-07-27 | Spec sync Back#61: `specialist_id` на POST event; commercial без active sub → 402; trial только `start_trial` |
|
||||
| 2026-07-27 | `agent-pitfalls.mdc`: типовые сбои PS/git/docker/scope; промпт субагентам |
|
||||
| 2026-07-27 | Spec sync: booking `expired` (Back#60) + Front#31 UX (desktop chrome, empty bookings, agenda copy, Discover covers, nickname, invites badge) |
|
||||
| 2026-07-27 | Spec#12: booking-requests + Bookings IA + image_url; правило one-repo-per-agent |
|
||||
| 2026-07-27 | §6 / STAGE-BROWSER: канон — прямой HTTPS `*.calentiq.com`; proxy `:8787` только DNS-fallback |
|
||||
|
||||
Reference in New Issue
Block a user