From 0035dd4faec2cbaf8d8a9d4f1c5364b02f22cdff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=BC=D0=B8=D1=82=D1=80=D0=B8=D0=B9?= Date: Wed, 8 Jul 2026 17:10:36 +0300 Subject: [PATCH] =?UTF-8?q?docs(autopodbor):=20=D0=BF=D0=BB=D0=B0=D0=BD=20?= =?UTF-8?q?=E2=80=94=20=D0=B2=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D1=8B=20=D0=BD?= =?UTF-8?q?=D0=B0=D1=85=D0=BE=D0=B4=D0=BA=D0=B8=20=D0=97=D0=B0=D0=B4=D0=B0?= =?UTF-8?q?=D1=87=D0=B8=200=20(pgsql=5Fsupplier,=20web.php,=20FieldWorkspa?= =?UTF-8?q?ceScreen)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/observer/STATUS.md | 2 +- ...07-08-autopodbor-step2-batch-fair-queue.md | 37 ++++++++----------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/docs/observer/STATUS.md b/docs/observer/STATUS.md index ff0d63df..4f72fc6b 100644 --- a/docs/observer/STATUS.md +++ b/docs/observer/STATUS.md @@ -1,6 +1,6 @@ # Brain Status (auto-generated) -Last updated: 2026-07-08T13:51:29.033Z +Last updated: 2026-07-08T14:02:29.221Z | Контролёр | Состояние | Детали | |---|---|---| diff --git a/docs/superpowers/plans/2026-07-08-autopodbor-step2-batch-fair-queue.md b/docs/superpowers/plans/2026-07-08-autopodbor-step2-batch-fair-queue.md index 3a1549d9..7c03fc42 100644 --- a/docs/superpowers/plans/2026-07-08-autopodbor-step2-batch-fair-queue.md +++ b/docs/superpowers/plans/2026-07-08-autopodbor-step2-batch-fair-queue.md @@ -70,13 +70,13 @@ ls app/resources/js/components | grep -iE "konkurent|pole|autopodbor|competit" ``` Expected: получить точные имена `AutopodborChargeService::chargeForRun` (сигнатура), `Tenant::requiredLeadsForTomorrow()`, `PricingTierRepository::activeAt()`, файл маршрутов автоподбора, имя Vue-компонента «Конкурентное поле» и его подвала выбора. **Записать найденное в шапку плана-исполнения** — на них ссылаются Задачи 2,4,5,7,8. -- [ ] **Step 4: Зафиксировать, как ставится tenant-контекст в системных (межтенантных) запросах.** Распорядителю нужен запрос «есть ли `running` study по ВСЕМ тенантам». Свериться, как это делают существующие джобы/команды (поиск роли/обхода RLS): +- [ ] **Step 4: Зафиксировать, как ставится tenant-контекст в системных (межтенантных) запросах.** Распорядителю нужен запрос «есть ли `running` study по ВСЕМ тенантам». -Run: -```bash -grep -rn "set_config('app.current_tenant\|BYPASSRLS\|crm_supplier_worker\|withoutTenantScope\|SET app.current_tenant" app/ | head -20 -``` -Expected: понять паттерн системного доступа (напр. отдельное соединение/роль в джобах). Заложить его в Задачу 3. Если единого паттерна нет — использовать выделенный запрос от системной роли; согласовать с `rls-reviewer`. +**РЕЗУЛЬТАТ РАЗВЕДКИ (2026-07-08, заполнено):** +- Джобы автоподбора ходят под `crm_app_user` (FORCE RLS) и ставят `set_config('app.current_tenant_id', ...)` per-tenant — **межтенантного пути у них нет**. +- Штатный системный путь в проекте: роль **`crm_supplier_worker` (BYPASSRLS)** через соединение **`pgsql_supplier`** (trait `SharesSupplierPdo`, прецедент — `RouteSupplierLeadJob`). Это и есть «системный» доступ для queue-воркеров через тенанты. +- **Решение:** распорядитель (`AutopodborStudyScheduler::systemQuery()`) ходит через `DB::connection('pgsql_supplier')`. RLS-политики НЕ трогаем. Тесты распорядителя — `uses(SharesSupplierPdo::class)` (см. `feedback-prod-full-test-isolated-db`). Согласовать с `rls-reviewer` (Задача 3). +- Точное имя соединения подтвердить в `config/database.php` (`pgsql_supplier`). --- @@ -508,24 +508,17 @@ final class AutopodborStudyScheduler /** * Системный (межтенантный) билдер БД — распорядителю нужно видеть ВСЕ тенанты. - * ПОДСТАВИТЬ реальный путь из Задачи 0 Step 4 (отдельное соединение/роль crm_supplier_worker - * или системный запрос без tenant-контекста). Ниже — плейсхолдер-обёртка для читаемости. + * Соединение `pgsql_supplier` = роль crm_supplier_worker (BYPASSRLS), штатный системный + * путь queue-воркеров (прецедент RouteSupplierLeadJob / trait SharesSupplierPdo). Задача 0 Step 4. */ private function systemQuery(): \Illuminate\Database\Query\Builder { - // TODO(Task 0 Step 4): вернуть builder на системном соединении с обходом RLS. - return DB::connection($this->systemConnectionName())->table('autopodbor_runs'); - } - - private function systemConnectionName(): string - { - // Имя соединения из config/database.php, под которым джобы автоподбора уже ходят межтенантно. - return config('autopodbor.system_connection', config('database.default')); + return DB::connection('pgsql_supplier')->table('autopodbor_runs'); } } ``` -> **Важно исполнителю:** `systemQuery()`/`systemConnectionName()` — единственное место с неопределённостью. В Задаче 0 Step 4 найден реальный способ межтенантного доступа (как в существующих джобах). Подставить его сюда ДО прогона межтенантных тестов; согласовать с `rls-reviewer`. Функционально `pickNextRunId()` уже полон. +> **RLS-нить:** `systemQuery()` идёт через `pgsql_supplier` (BYPASSRLS) — это и даёт межтенантную видимость. Политики НЕ ослабляем. Прогнать `rls-reviewer` на этот сервис. Тесты — `uses(SharesSupplierPdo::class)`. `pickNextRunId()` функционально полон. - [ ] **Step 4: Запустить тест — PASS.** @@ -908,11 +901,11 @@ public function studyBatch(Request $request, AutopodborRunService $svc): JsonRes return response()->json(['batch_id' => $res->batch_id, 'queued' => $res->queued], 202); } ``` -Маршрут (рядом с существующим `study`): +Маршрут — в **`app/routes/web.php`**, внутри существующей группы `Route::middleware(['auth:sanctum,impersonation','tenant'])->prefix('/api/autopodbor')`, рядом с `POST /study` (строка ~359): ```php -Route::post('autopodbor/study/batch', [AutopodborController::class, 'studyBatch']); +Route::post('/study/batch', 'App\Http\Controllers\Api\AutopodborController@studyBatch'); ``` -> Способ получить tenant/`$uid` скопировать 1:1 из существующего `study()` (Задача 0 Step 3) — не изобретать. +> Способ получить tenant/`$uid` скопировать 1:1 из существующего `study()` в `AutopodborController` — не изобретать (там уже разрешён tenant из middleware `tenant`). - [ ] **Step 4: Запустить тесты — PASS.** @@ -929,8 +922,8 @@ git commit -m "feat(autopodbor): endpoint POST /api/autopodbor/study/batch" ## Task 8: Фронт — кнопка в подвале выбора + яркий алерт баланса + статусы -**Files:** (точные имена — из Задачи 0 Step 3) -- Modify: Vue-компонент «Конкурентное поле» (список + нижний подвал выбора), api-модуль автоподбора. +**Files:** (уточнено разведкой Задачи 0) +- Modify: `app/resources/js/views/autopodbor/screens/FieldWorkspaceScreen.vue` (список + нижний подвал выбора «Выбрано конкурентов: N»), `app/resources/js/api/autopodbor.ts`, при необходимости `app/resources/js/stores/autopodborStore.ts`. Исполнителю: открыть `FieldWorkspaceScreen.vue` и подтвердить, что подвал выбора именно там (иначе — соседний `FieldCompetitorScreen.vue`). - Test: `app/tests/Frontend/KonkurentnoePoleBatch.spec.ts` - [ ] **Step 1: Добавить API-вызов в фронтовый api-модуль.**