-
Notifications
You must be signed in to change notification settings - Fork 5
Description
Архитектурный аудит подсистем
Комплексный анализ связей между подсистемами проекта. Выявлены проблемы неконсистентности, дублирования кода, потенциальной несовместимости и неясной логики взаимодействия.
1. Бэкенд: двойные пути доступа к сервисам
1.1 Глобалы + ServiceContainer одновременно
orchestrator.py инициализирует сервисы двумя способами: сначала как глобальные переменные (строки 401–476), затем копирует их в ServiceContainer (строки 1024–1034). Разные роутеры используют разные пути доступа:
| Сервис | Глобал | Container | Кто использует |
|---|---|---|---|
llm_service |
orchestrator.py:463 |
container.llm_service |
health_check (оба!), llm.py (service_manager) |
voice_service |
orchestrator.py:463 |
container.voice_service |
tts.py (container), health_check (глобал) |
piper_service |
orchestrator.py:466 |
container.piper_service |
tts.py (container) |
stt_service |
orchestrator.py:468 |
— | Только глобал |
Проблема: Health-check демонстрирует недоверие к обоим путям (orchestrator.py:1172):
current_llm_service = container.llm_service if container.llm_service else llm_serviceНет механизма синхронизации при горячей замене сервиса (например, смена LLM-бэкенда через API) — обновление одного пути не гарантирует обновления другого.
1.2 Три слоя сервисов без чёткого разделения
| Слой | Расположение | Примеры | Принцип? |
|---|---|---|---|
| AI/Runtime | Корень проекта | cloud_llm_service.py, voice_clone_service.py, multi_bot_manager.py, bridge_manager.py |
Нет |
| Domain | app/services/ |
amocrm_service.py, wiki_rag_service.py, gdpr_service.py, sales_funnel.py |
Нет |
| Data | db/integration.py |
28 Async*Manager классов |
Обёртки над репозиториями |
Неконсистентность: orchestrator.py импортирует GSMService из app/services/ (строка 1039), но CloudLLMService — из корня. Нет документированного принципа, определяющего в каком слое размещать новый сервис.
1.3 Два фреймворка Telegram в одном проекте
| Фреймворк | Где используется | Для чего |
|---|---|---|
python-telegram-bot |
telegram_bot_service.py, app/services/sales_funnel.py |
Legacy single-bot сервис |
aiogram 3.x |
telegram_bot/handlers/, telegram_bot/sales/ |
Новый multi-instance бот |
Дублирование: Sales-воронка реализована дважды:
- Legacy:
app/services/sales_funnel.py(importsfrom telegram.ext import ContextTypes) - Новый:
telegram_bot/handlers/sales/(importsfrom aiogram)
Оба пакета экспортируют типы с одинаковыми именами (Update, Message, InlineKeyboardButton), что потенциально создаёт конфликт импортов.
2. База данных: безопасность workspace-изоляции
2.1 Обход workspace-фильтра в FAQ
db/repositories/faq.py:
find_answer()(строка 61) — нет параметраworkspace_id→ возвращает ответы из всех workspaceget_by_question()(строка 81) — аналогично, без фильтраdelete_by_question()(строка 158) —workspace_id=Noneудаляет FAQ из всех workspace
Риск: пользователь Workspace 1 может получить FAQ-ответ из Workspace 2.
2.2 Глобальные роли без workspace-привязки
db/repositories/role.py — ни один метод не принимает workspace_id. Если роли должны быть workspace-scoped, это дыра в изоляции.
2.3 Redis-кеш без workspace в ключах
db/redis_client.py — ключи кеша глобальные:
key = f"{CacheKey.CHAT_SESSION}:{session_id}" # Нет workspace_id
key = f"{CacheKey.FAQ}:{...}" # Нет workspace_idFAQ кешируется только при workspace_id=None (faq.py:36-59), но если два workspace запросят FAQ без фильтра — получат общий кеш.
2.4 redis.keys() в production
db/redis_client.py:159 — cache_delete_pattern() использует await redis_client.keys(pattern), что блокирует весь Redis на больших БД. Нужен .scan().
2.5 SQLite: нет WAL-режима
db/database.py:27-33 — отсутствуют PRAGMA:
PRAGMA journal_mode=WAL; -- для параллельных чтения/записи
PRAGMA busy_timeout=5000; -- для избежания SQLITE_BUSY
PRAGMA synchronous=NORMAL; -- производительность без потери надёжности
При 10+ одновременных пользователях — риск SQLITE_BUSY ошибок.
3. Монолитные god-файлы
| Файл | Строк | Проблема |
|---|---|---|
db/models.py |
3,636 | 55 моделей в одном файле: auth, chat, bot, CRM, kanban, GSM — всё вместе |
db/integration.py |
2,422 | 28 Async*Manager классов — тонкие обёртки без добавленной логики |
orchestrator.py |
~4,100 | Инициализация, 100+ legacy-эндпоинтов, глобалы, startup/shutdown |
admin/src/views/ChatView.vue |
3,201 | Чат, бранчинг, артефакты, стриминг, RAG, TTS, STT, Claude Code |
admin/src/views/TelegramView.vue |
2,603 | 9 табов, формы настроек, sales-конфигурация |
admin/src/plugins/i18n.ts |
~3,400 | Все 3 языка в одном файле |
3.1 integration.py: бессмысленные обёртки
Каждый Async*Manager создаёт сессию → инстанцирует репозиторий → вызывает тот же метод → возвращает результат. Нулевая добавленная логика:
# db/integration.py:125-140
async def list_sessions(self, owner_id=None, ...):
async with AsyncSessionLocal() as session:
repo = ChatRepository(session)
return await repo.list_sessions(owner_id=owner_id, ...)При этом ряд роутеров (bot_sales.py) уже обходят менеджеры и работают с репозиториями напрямую — два паттерна в одном проекте.
3.2 Транзакционная целостность нарушена
integration.py создаёт отдельную сессию на каждый вызов метода. Невозможно выполнить несколько операций в одной транзакции:
await chat_mgr.create_session(...) # Сессия 1, auto-commit
await chat_mgr.add_message(...) # Сессия 2, отдельная транзакция
# Если add_message упадёт — create_session уже закоммичен4. Дублирование бот-подсистем (Telegram ↔ WhatsApp)
4.1 Дублирование текстов (98%)
| Файл | Строк | Дублирование |
|---|---|---|
telegram_bot/sales/texts.py |
467 | Оригинал |
whatsapp_bot/sales/texts.py |
445 | 98% копия, разница: **bold** → *bold* |
Каждое исправление текста требует правки двух файлов. Возможна рассинхронизация.
4.2 Дублирование менеджеров (90%)
| Файл | Строк | Структура |
|---|---|---|
multi_bot_manager.py |
346 | BotProcess + MultiBotManager |
whatsapp_manager.py |
324 | WhatsAppProcess + WhatsAppManager |
Классы практически идентичны: единственные отличия — имя модуля (telegram_bot / whatsapp_bot), путь к логам, и способ загрузки конфига.
4.3 Несовместимость паттернов конфигурации
| Аспект | Telegram | |
|---|---|---|
| Загрузка конфига | Pre-cache в /tmp/bot_config_{id}.json |
Запрос к БД при каждом старте |
| Состояние | FSM (aiogram) | Колонка funnel_state в отдельной SQLite |
| БД | Основная SQLite (SQLAlchemy) | Изолированная SQLite (data/wa_sales_{id}.db) |
| user_id | INTEGER (Telegram ID) | TEXT (телефон) |
Проблемы:
- WhatsApp бот не может стартовать, если основная БД недоступна (нет кеша)
- Данные WhatsApp-воронки не видны в общей аналитике (отдельная БД)
- Telegram имеет 7 таблиц (включая payments, testimonials), WhatsApp — только 3
4.4 Дублирование клавиатур (70%)
telegram_bot/sales/keyboards.py (959 строк) и whatsapp_bot/sales/keyboards.py (922 строки) используют одинаковые callback ID (sales:start_quiz, faq:what_is), но разные UI-примитивы (InlineKeyboard vs quick-reply/list). Изменение ID требует правки в 4 местах (keyboards + handlers × 2 бота).
4.5 Правильное переиспользование (подтверждено)
telegram_bot/sales/segments.py— единственный источник сегментации, WhatsApp корректно импортируетtelegram_bot/services/llm_router.py— WhatsApp корректно использует черезfrom telegram_bot.services.llm_router import LLMRouter
5. Фронтенд: несогласованность и пробелы
5.1 Прямые fetch() в обход API-клиента
3 места делают fetch() напрямую, минуя api клиент из client.ts:
| Файл | Строка | Эндпоинт |
|---|---|---|
stores/auth.ts |
62 | GET /admin/auth/permissions |
views/AuditView.vue |
— | GET /admin/audit/export |
views/SettingsView.vue |
— | GET /admin/auth/profile, POST /change-password |
Последствия: эти вызовы не работают в demo-режиме (не перехватываются setupDemoInterceptor), не участвуют в централизованной обработке ошибок.
5.2 Demo-режим рассинхронизирован с реальным API
- Demo-типы определены отдельно от основных (
demo/store.tsvsapi/telegram.ts) - Пример: реальный API использует
knowledge_collection_ids(массив), demo —knowledge_collection_id(единственное число) - Нет автоматической проверки, что mock-данные соответствуют реальным типам
- Новые поля в API не попадают в demo, пока кто-то не заметит расхождение
5.3 Неполная i18n для казахского языка
| Язык | Ключей | Покрытие |
|---|---|---|
| Русский (ru) | ~845 | 100% |
| English (en) | ~844 | ~99% (7 строк — кириллица по ошибке) |
| Қазақша (kk) | ~244 | 29% |
71% казахских переводов отсутствует. Целые секции (kanban, CRM, WooCommerce) не переведены.
5.4 God-компоненты на фронтенде
ChatView.vue (3,201 строк) совмещает: чат, бранчинг, файлы, артефакты, стриминг, шаринг, Claude Code, RAG, TTS/STT, zen-mode. 60+ реактивных переменных. Должен быть разделён на 5-6 компонентов.
TelegramView.vue (2,603 строки) — 9 табов с формами, нетипизированный salesForm: ref<Record<string, any>>({}).
6. Миграции: две параллельные системы
Текущее состояние
| Система | Файлов | Когда используется |
|---|---|---|
| Alembic | 16 миграций | Новые schema changes (roles, workspaces, kanban, resource_shares) |
scripts/migrate_*.py |
25 скриптов | Старые features + data migrations |
Нет документации, определяющей:
- Когда использовать Alembic vs ручной скрипт
- Какие таблицы покрыты какой системой
- Как проверить, что обе системы не конфликтуют
Потенциальный конфликт
Новый разработчик может создать и Alembic-миграцию, и ручной скрипт для одной и той же таблицы. Base.metadata.create_all на старте сервера дополнительно создаёт недостающие таблицы — третий путь, который может маскировать пропущенные миграции.
7. Сводная таблица по приоритетам
Критические (безопасность)
| # | Проблема | Файл |
|---|---|---|
| 1 | FAQ find_answer() без workspace-фильтра |
db/repositories/faq.py:61 |
| 2 | FAQ get_by_question() без workspace-фильтра |
db/repositories/faq.py:81 |
| 3 | FAQ delete_by_question() может удалить из всех workspace |
db/repositories/faq.py:158 |
| 4 | Redis-кеш без workspace в ключах | db/redis_client.py |
Высокие (архитектура, надёжность)
| # | Проблема | Файл |
|---|---|---|
| 5 | SQLite без WAL и busy_timeout | db/database.py:27-33 |
| 6 | Двойной путь доступа к сервисам (global + container) | orchestrator.py |
| 7 | Транзакционная целостность нарушена в integration.py | db/integration.py |
| 8 | Два Telegram-фреймворка с дублированием sales-логики | telegram_bot_service.py + telegram_bot/ |
| 9 | God-файлы: models.py (3.6K), integration.py (2.4K), orchestrator.py (4.1K) | db/ |
| 10 | WhatsApp бот на изолированной БД (data не видна в аналитике) | whatsapp_bot/sales/database.py |
Средние (качество кода, поддерживаемость)
| # | Проблема | Файл |
|---|---|---|
| 11 | 98% дублирование текстов Telegram ↔ WhatsApp | */sales/texts.py |
| 12 | 90% дублирование bot-менеджеров | multi_bot_manager.py + whatsapp_manager.py |
| 13 | Demo-режим рассинхронизирован с реальным API | admin/src/api/demo/ |
| 14 | Прямые fetch() в обход API-клиента (3 места) | auth.ts, AuditView.vue, SettingsView.vue |
| 15 | i18n: 71% казахских переводов отсутствует | admin/src/plugins/i18n.ts |
| 16 | God-компоненты: ChatView (3.2K), TelegramView (2.6K) | admin/src/views/ |
| 17 | redis.keys() вместо .scan() |
db/redis_client.py:159 |
| 18 | Две системы миграций без документированного разделения | alembic/ + scripts/migrate_*.py |
| 19 | Несовместимые паттерны конфигурации ботов | multi_bot_manager.py vs whatsapp_manager.py |
| 20 | Inconsistent flush/commit в BaseRepository | db/repositories/base.py:54-70 |
Рекомендуемый порядок действий
Немедленно (1-2 дня)
- Добавить
workspace_idвfind_answer()иget_by_question()в FAQ - Включить WAL-режим и
busy_timeoutдля SQLite - Заменить
redis.keys()наredis.scan()вcache_delete_pattern()
Короткосрочно (1 спринт)
- Удалить legacy
telegram_bot_service.pyиapp/services/sales_funnel.py - Вынести общие тексты ботов в
shared/sales/texts.pyс форматтером - Создать
BaseBotManagerдля Telegram и WhatsApp менеджеров - Перевести прямые
fetch()на централизованный API-клиент - Привести Redis-ключи к workspace-aware формату
Среднесрочно (2-3 спринта)
- Разделить
db/models.pyна доменные файлы - Устранить менеджеры-обёртки из
db/integration.py, перейти на прямое использование репозиториев - Консолидировать ServiceContainer как единственный путь доступа к сервисам
- Разделить god-компоненты фронтенда
- Документировать стратегию миграций (Alembic vs scripts)
- Добавить автотесты совместимости demo ↔ real API
Отчёт сгенерирован автоматическим анализом кодовой базы. Все ссылки на строки актуальны на момент создания.