ci / build-test (push) Canceled after 0s
SaaS-мониторинг Telegram: ядро (модули Cards/Kanban/Pipeline/Tenants/Settings/ Discovery, Api, Infrastructure), сервисы telegram/ai/ml/storage, фронт Vue, контракты и grpc-hosting, деплой-конфиги (dev/prod/observability/CI-раннер), Gitea Actions CI, документация (ТЗ, техдок, api-map, код-стайл, планы, бэклог). Текущее состояние: все этапы роадмапа 0–12 закрыты, сборка 5 sln 0/0, тесты 1340/130/52/38/9 зелёные.
30 lines
2.9 KiB
Markdown
30 lines
2.9 KiB
Markdown
### Task 3: Хранилище Discovery (задачи/кандидаты/чёрный список/лог)
|
||
|
||
**Files:**
|
||
- Create: `backend/app/services/discovery.py`
|
||
|
||
**Interfaces:**
|
||
- Consumes: `store` (таблицы Task 1).
|
||
- Produces (все синхронные):
|
||
- `list_tasks() -> list[dict]`, `get_task(id) -> dict | None` (keywords — список)
|
||
- `create_task(payload: dict) -> dict` — валидация: name непустое; `plan_joins` 1..limit; **правило бюджета**: `sum(plan_joins задач, где status NOT IN ('done','failed')) + plan_joins <= discJoinLimit`, иначе `raise ValueError(...)`.
|
||
- `patch_task(id, patch: dict) -> dict` (name/description/keywords/min_subscribers/lang/threshold/sample_size/plan_joins/auto_join; увеличение plan_joins — с той же проверкой)
|
||
- `delete_task(id) -> None` (удалить задачу и её кандидатов)
|
||
- `start_task(id) -> dict` — требует непустой keywords; status=running; `pause_task(id) -> dict` — paused
|
||
- `list_candidates(task_id, status: str | None) -> list[dict]` (декод marks/topics)
|
||
- `add_candidate(task_id, dialog_id, name, username, kind, hue) -> dict | None` — `None`, если: в `dialogs`, в `disc_blacklist`, либо уже есть `disc_candidates` со статусом new/review/joined. Лог `skip` с причиной.
|
||
- `bump_counter(task_id, field: str, n: int = 1)` — found/evaluated/joined/rejected
|
||
- `set_candidate(task_id, dialog_id, patch: dict)` — обновление полей кандидата
|
||
- `set_candidate_status(dialog_id, status)` + лог
|
||
- `delete_candidate(dialog_id) -> None` — удалить кандидата (skip-ветки)
|
||
- `advance_search(task_id) -> None` — `search_idx += 1`; когда индекс >= len(keywords) → `search_done=True`
|
||
- `mark_joined(dialog_id, auto: bool)` — статус joined + `bump_counter('joined')` + лог `join_auto`/`join_manual`
|
||
- `mark_rejected(dialog_id, reason="")` — статус rejected + `bump_counter('rejected')` + лог `reject` + `add_blacklist`
|
||
- `add_blacklist(dialog_id, name, reason) / remove_blacklist(dialog_id) / list_blacklist() -> list[dict]`
|
||
- `add_log(task_id, event, text="")`; `task_log(task_id, limit=100) -> list[dict]`
|
||
|
||
- [ ] **Step 1: Реализовать модуль** (json-поля по конвенции проекта; все `store.execute/query` с параметрами).
|
||
- [ ] **Step 2: Проверить на временной БД** (сценарий): создать задачу plan=25 → ок; создать вторую plan=30 → `ValueError`; кандидат, совпадающий с `dialogs` → `add_candidate` вернул None + лог skip; `mark_rejected` → в чёрном списке; повторный `add_candidate` того же источника → None.
|
||
|
||
---
|