mirror of
https://github.com/wasrusgen/zov-tech.git
synced 2026-06-03 18:44:47 +00:00
951 lines
45 KiB
Markdown
951 lines
45 KiB
Markdown
# Техническое задание
|
||
|
||
# Telegram-бот + MiniApp «AI-подбор техники для кухни ЗОВ»
|
||
|
||
**Версия:** 1.0
|
||
**Дата:** 2026-05-08
|
||
**Заказчик:** Василий (vasrusgen@gmail.com), куратор партнёрской сети ЗОВ
|
||
**Канал:** [@wasrusgen1](https://t.me/wasrusgen1)
|
||
**Целевая аудитория продукта:** 112 менеджеров салонов ЗОВ + конечные клиенты, ведомые через канал
|
||
|
||
---
|
||
|
||
## 1. Контекст и цели
|
||
|
||
### 1.1 Что это за продукт
|
||
|
||
Telegram-бот выступает шлюзом авторизации, после чего открывается единый MiniApp с двумя ролями:
|
||
|
||
- **Кабинет менеджера** — рабочий инструмент: подбор техники для клиента, замеры, заявки, сделки, статус доступа.
|
||
- **Кабинет клиента** — воронка покупки кухни: замер, подбор, калькулятор, идеи, связь с менеджером.
|
||
|
||
Внутри MiniApp клиент или менеджер заполняют структурированный чек-лист. Данные уходят в backend, который вызывает LLM (OpenAI / Claude). LLM возвращает готовое предложение по технике с учётом брендов, ниш, бюджета и сценария использования. Результат доставляется обратно в Telegram.
|
||
|
||
### 1.2 Какую проблему решает
|
||
|
||
- Сокращает срок согласования кухни с клиентом (главная боль ЗОВ).
|
||
- Привязывает менеджера к куратору: «работаешь со мной — инструмент бесплатно, ушёл — платно».
|
||
- Превращает 112 менеджеров в управляемую партнёрскую сеть с метриками.
|
||
- Даёт клиентам бесплатную ценность до контакта с менеджером (калькулятор, идеи, самозамер).
|
||
|
||
### 1.3 Бизнес-модель в продукте
|
||
|
||
| Сегмент | Доступ | Условие |
|
||
|---|---|---|
|
||
| Менеджеры ЗОВ (свои салоны) | бесплатно навсегда | по умолчанию |
|
||
| Менеджеры партнёрских салонов, активные | бесплатно | сделка через куратора за последние 90 дней |
|
||
| Менеджеры партнёрских салонов, неактивные | платно | подписка / pay-per-use |
|
||
| Конечные клиенты | базовый доступ бесплатно | подбор + замер открыты, премиум-функции потом |
|
||
|
||
Точное определение «активного менеджера» — поле `last_order_date` в реестре + 90 дней.
|
||
|
||
### 1.4 Что в скоупе MVP
|
||
|
||
- Бот с одним диалогом (выбор роли + кнопка «Открыть кабинет»).
|
||
- MiniApp с двумя меню (менеджер / клиент), но **рабочих пунктов всего три**:
|
||
- 🔧 «Подбор техники» (для менеджера, на базе существующего HTML-чек-листа `02_Чек-лист_клиенту.html`).
|
||
- 📐 «Самозамер» (для клиента, новая 5-шаговая форма).
|
||
- 💰 «Мой статус» (для менеджера, простая страница).
|
||
- Backend на Google Apps Script + Google Sheet как БД.
|
||
- AI-подбор через OpenAI API (`gpt-4o-mini`) с готовым промптом.
|
||
- Доставка результата менеджеру через бота.
|
||
|
||
### 1.5 Что НЕ в скоупе MVP (на будущее)
|
||
|
||
- Платёжная интеграция (ЮKassa / Telegram Stars).
|
||
- Раздел «Сделки», «База знаний», «Обучение», «FAQ», «Записаться в салон».
|
||
- Личный кабинет клиента с историей.
|
||
- Push-уведомления об активности.
|
||
- Партнёрская комиссия с производителей техники.
|
||
- Мобильное нативное приложение (всегда останется MiniApp).
|
||
|
||
---
|
||
|
||
## 2. Архитектура
|
||
|
||
### 2.1 Компоненты
|
||
|
||
```
|
||
Telegram канал @wasrusgen1
|
||
│ кнопка/ссылка "Открыть подбор"
|
||
▼
|
||
Telegram бот @zov_tech_bot
|
||
│ /start → выбор роли → кнопка WebApp
|
||
▼
|
||
MiniApp (SPA, HTML+JS)
|
||
на хостинге GitHub Pages / Netlify (HTTPS обязателен)
|
||
│ POST /api/*
|
||
▼
|
||
Backend Google Apps Script (Web App URL)
|
||
│ R/W
|
||
▼
|
||
Google Sheet "ЗОВ — База"
|
||
│
|
||
│ при подборе:
|
||
▼
|
||
OpenAI API (gpt-4o-mini)
|
||
│ результат
|
||
▼
|
||
Backend → Telegram Bot API → менеджер/клиент
|
||
```
|
||
|
||
### 2.2 Технологический стек
|
||
|
||
| Слой | Технология | Обоснование |
|
||
|---|---|---|
|
||
| Бот | Python + aiogram **либо** n8n + Telegram Trigger | aiogram — гибче, n8n — без кода |
|
||
| MiniApp | HTML + Vanilla JS + минимальный CSS | существующий чек-лист уже в этом стеке |
|
||
| Хостинг MiniApp | GitHub Pages | бесплатно, HTTPS, Git-history |
|
||
| Backend | Google Apps Script (Web App) | бесплатно, нативная связка с Sheet |
|
||
| БД | Google Sheet | прозрачность для заказчика, ручное редактирование |
|
||
| AI | OpenAI API, модель `gpt-4o-mini` | дёшево (~$0.001 на запрос), хватает для подбора |
|
||
| Платежи (вне MVP) | ЮKassa или Telegram Stars | российские реалии |
|
||
|
||
### 2.3 Окружения
|
||
|
||
- **Dev:** локальный HTML, бот в режиме polling, тестовая Google Sheet.
|
||
- **Prod:** GitHub Pages, бот в режиме webhook, прод-Sheet с реестром.
|
||
|
||
---
|
||
|
||
## 3. Бот @zov_tech_bot
|
||
|
||
### 3.1 Общие принципы
|
||
|
||
- Бот **не содержит продуктовой логики**. Он только аутентифицирует и открывает MiniApp.
|
||
- Все длинные диалоги, формы, чек-листы — внутри MiniApp.
|
||
- Бот шлёт результаты подбора и системные уведомления.
|
||
|
||
### 3.2 Команды
|
||
|
||
| Команда | Доступ | Действие |
|
||
|---|---|---|
|
||
| `/start` | все | приветствие, выбор роли (если новый пользователь) или кнопка «Открыть кабинет» (если уже зарегистрирован) |
|
||
| `/start <invite_code>` | все | спец-сценарий: переход по ссылке-приглашению от менеджера |
|
||
| `/menu` | все | повторная кнопка «Открыть кабинет» |
|
||
| `/help` | все | краткая справка + контакт куратора |
|
||
| `/admin` | только админ (tg_id куратора) | админ-меню (см. 3.5) |
|
||
|
||
### 3.3 Дерево диалогов
|
||
|
||
```
|
||
[НОВЫЙ ПОЛЬЗОВАТЕЛЬ]
|
||
/start
|
||
│
|
||
▼
|
||
"👋 Здравствуйте! Я помогу подобрать технику для кухни.
|
||
Кто вы?"
|
||
[👤 Менеджер] [🏠 Клиент]
|
||
│ │
|
||
▼ ▼
|
||
сохранить сохранить
|
||
role=manager role=client
|
||
в БД в БД
|
||
│ │
|
||
▼ ▼
|
||
"Отлично, открываю "Спасибо! Открываю
|
||
ваш кабинет 👇" кабинет 👇"
|
||
[🚀 Открыть кабинет] [🚀 Открыть кабинет]
|
||
(WebApp button с URL MiniApp)
|
||
|
||
|
||
[ВЕРНУВШИЙСЯ ПОЛЬЗОВАТЕЛЬ]
|
||
/start
|
||
│
|
||
▼
|
||
"С возвращением, {имя}!"
|
||
[🚀 Открыть кабинет]
|
||
|
||
|
||
[ССЫЛКА-ПРИГЛАШЕНИЕ ОТ МЕНЕДЖЕРА]
|
||
/start client_inv_AB12CD
|
||
│
|
||
▼
|
||
проверить invite_code в БД
|
||
│ найден → manager_id = X
|
||
▼
|
||
сохранить role=client + manager_id=X
|
||
▼
|
||
"Здравствуйте! Вас сопровождает {ФИО менеджера}, ЗОВ {салон}.
|
||
Открываю кабинет 👇"
|
||
[🚀 Открыть кабинет]
|
||
```
|
||
|
||
### 3.4 Кнопка «Открыть кабинет»
|
||
|
||
Это **WebApp-кнопка** Telegram (не URL-button), которая открывает MiniApp с подписанным `initData`.
|
||
|
||
Параметр `web_app.url`:
|
||
```
|
||
https://wasrusgen.github.io/zov-tech/?v=1
|
||
```
|
||
`initData` содержит `tg_id`, имя пользователя, hash для проверки. Backend проверяет hash и определяет роль по БД.
|
||
|
||
### 3.5 Админ-функции (для куратора)
|
||
|
||
Команда `/admin` доступна только при `tg_id == ADMIN_TG_ID` (env-переменная).
|
||
|
||
Меню:
|
||
- `📊 Статистика` — сколько активных менеджеров, заявок за неделю, конверсия
|
||
- `👤 Управление менеджерами`
|
||
- `+ Добавить менеджера` (диалог: ФИО, tg_username, salon)
|
||
- `✏️ Изменить статус` (выбор → active / lapsed + дата)
|
||
- `🔍 Найти менеджера` (по ФИО / tg_username)
|
||
- `📨 Рассылка` — отправить сообщение всем менеджерам / всем клиентам / выбранным сегментам
|
||
|
||
### 3.6 Системные уведомления (исходящие от бота)
|
||
|
||
| Триггер | Получатель | Текст |
|
||
|---|---|---|
|
||
| Клиент завершил подбор | менеджер этого клиента | «Новая заявка от {ФИО клиента}: {short_summary}. [Открыть в кабинете]» |
|
||
| Менеджер запросил подбор | менеджер | «Готово! Подбор для {ФИО клиента}: [PDF / текст]» |
|
||
| Клиент сделал самозамер | менеджер этого клиента | «{ФИО клиента} прислал замер кухни. [Посмотреть]» |
|
||
| Статус менеджера изменён куратором | менеджер | «Ваш доступ продлён до {дата}» / «Доступ переведён в платный режим» |
|
||
|
||
Все уведомления сопровождаются inline-кнопкой, открывающей соответствующий экран MiniApp.
|
||
|
||
### 3.7 Технические требования к боту
|
||
|
||
- Режим **webhook** в проде, polling в деве.
|
||
- Хранилище состояний — память (FSM aiogram) или Redis для масштаба. На MVP — память.
|
||
- Логирование всех `update` в файл / Sheet «Логи бота».
|
||
- Обработка ошибок: при exception — сообщение «Произошла ошибка, мы уже разбираемся» + alert куратору.
|
||
|
||
### 3.8 Регистрация бота в @BotFather
|
||
|
||
1. `/newbot` → `zov_tech_bot` (или альтернативное имя, если занято).
|
||
2. `/setdomain` → домен MiniApp (`wasrusgen.github.io`).
|
||
3. `/newapp` → создать MiniApp:
|
||
- Title: «Подбор техники ЗОВ»
|
||
- Short name: `podbor`
|
||
- Web App URL: `https://wasrusgen.github.io/zov-tech/`
|
||
4. `/setmenubutton` → опционально, кнопка-меню в боте, ведущая в MiniApp.
|
||
|
||
---
|
||
|
||
## 4. MiniApp
|
||
|
||
### 4.1 Карта страниц (hash-роутинг внутри одного SPA)
|
||
|
||
```
|
||
#/ — авто-редирект по роли
|
||
#/m — главное меню менеджера
|
||
#/m/podbor — чек-лист подбора (существующий HTML)
|
||
#/m/measurements — список замеров
|
||
#/m/measurements/new — форма замера (менеджер за клиента)
|
||
#/m/measurements/:id — карточка одного замера
|
||
#/m/leads — список заявок (просмотр, MVP — read-only)
|
||
#/m/status — мой статус и доступ
|
||
#/m/help — связь с куратором
|
||
|
||
#/c — главное меню клиента
|
||
#/c/measure — выбор: вызвать замерщика / замерить самому
|
||
#/c/measure/self — 5-шаговая форма самозамера
|
||
#/c/measure/visit — заявка на выезд замерщика
|
||
#/c/podbor — упрощённый чек-лист (вне MVP, заглушка)
|
||
#/c/contact — связь с менеджером
|
||
```
|
||
|
||
### 4.2 Авторизация и определение роли
|
||
|
||
```js
|
||
// Псевдокод старта MiniApp
|
||
const tg = window.Telegram.WebApp;
|
||
tg.ready();
|
||
tg.expand();
|
||
|
||
const initData = tg.initData; // подписанная строка
|
||
const startParam = tg.initDataUnsafe.start_param || null;
|
||
|
||
const me = await fetch('/api/me', {
|
||
method: 'POST',
|
||
body: JSON.stringify({ initData, startParam })
|
||
}).then(r => r.json());
|
||
|
||
// me = { role: "manager"|"client", user: {...}, manager: {...}, status: "active"|"lapsed" }
|
||
|
||
if (me.role === 'manager') router.go('#/m');
|
||
else router.go('#/c');
|
||
```
|
||
|
||
Backend `/api/me`:
|
||
1. Проверяет HMAC `initData` по `BOT_TOKEN` (стандартная процедура Telegram).
|
||
2. Ищет `tg_id` в листе «Пользователи».
|
||
3. Если нет — создаёт запись (роль уже сохранена ботом, должна быть в БД).
|
||
4. Возвращает профиль + статус.
|
||
|
||
### 4.3 Кабинет менеджера
|
||
|
||
**Главный экран `#/m`:**
|
||
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ Привет, {ФИО} │
|
||
│ Салон: {salon} │
|
||
│ Статус: 🟢 active до 12.08.2026 │
|
||
│ │
|
||
│ ─────────────────────────────── │
|
||
│ │
|
||
│ 🔧 Подбор техники для клиента → │
|
||
│ 📐 Замеры → │
|
||
│ 📋 Заявки клиентов → │
|
||
│ 💼 Сделки (скоро) · │
|
||
│ 📚 База знаний (скоро) · │
|
||
│ 💰 Мой статус → │
|
||
│ 🆘 Связь с куратором → │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
Пункты «Сделки», «База знаний» в MVP — disabled-кнопки с текстом «Скоро».
|
||
|
||
**Страница `#/m/podbor`:**
|
||
|
||
Это существующий файл `02_Чек-лист_клиенту.html` с минимальными доработками:
|
||
1. Удалить захардкоженный блок менеджера (Любовь Алпеева) — подставлять из `me.user`.
|
||
2. Добавить поле «Размеры ниш» (см. п. 4.5 — пробелы из анализа).
|
||
3. Добавить поле «Бюджет по категориям».
|
||
4. Добавить блок «Сценарий использования» (семья, готовка).
|
||
5. Кнопку «Отправить» завести на `POST /api/podbor` вместо `mailto:`.
|
||
6. После отправки — `tg.showAlert('Заявка отправлена, результат придёт в чат')` + `tg.close()`.
|
||
|
||
**Страница `#/m/measurements`:**
|
||
|
||
Список замеров клиентов этого менеджера. Колонки: дата, ФИО клиента, статус (🟡 запрошен / 🟢 готов / 🔵 загружен вручную), кнопка «Открыть».
|
||
|
||
**Страница `#/m/measurements/new`:**
|
||
|
||
Та же форма, что и у клиента в `#/c/measure/self`, но с дополнительным полем «Клиент» (выбор из списка или ввод).
|
||
|
||
**Страница `#/m/status`:**
|
||
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ Ваш статус │
|
||
│ │
|
||
│ 🟢 Active │
|
||
│ Доступ продлён до 12.08.2026 │
|
||
│ Осталось 96 дней │
|
||
│ │
|
||
│ Последняя сделка через куратора: │
|
||
│ «Кухня Петров, 14.05.2026» │
|
||
│ │
|
||
│ Заявок за всё время: 23 │
|
||
│ Сделок через подбор: 7 (30%) │
|
||
│ │
|
||
│ Чтобы продлить доступ — │
|
||
│ проведите следующую сделку │
|
||
│ через @wasrusgen. │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
Если `status == lapsed`:
|
||
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ ⚠️ Доступ ограничен │
|
||
│ │
|
||
│ Последняя сделка через куратора │
|
||
│ была 12.02.2026. │
|
||
│ Бесплатный доступ закончился. │
|
||
│ │
|
||
│ Варианты: │
|
||
│ • Возобновить сопровождение через │
|
||
│ @wasrusgen — доступ снова free. │
|
||
│ • Подписка 3 000 ₽/мес. │
|
||
│ • Pay-per-use 500 ₽/подбор. │
|
||
│ │
|
||
│ [Связаться с куратором] │
|
||
│ [Оформить подписку] (вне MVP) │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
### 4.4 Кабинет клиента
|
||
|
||
**Главный экран `#/c`:**
|
||
|
||
```
|
||
┌─────────────────────────────────────┐
|
||
│ Привет, {имя} │
|
||
│ {если есть} │
|
||
│ Менеджер: {ФИО}, ЗОВ {салон} │
|
||
│ │
|
||
│ ─────────────────────────────── │
|
||
│ │
|
||
│ 📐 Замер кухни → │
|
||
│ 🔧 Подобрать технику (скоро) · │
|
||
│ 📐 Калькулятор бюджета (скоро) · │
|
||
│ 💡 Идеи и кейсы (скоро) · │
|
||
│ 📞 Связаться с менеджером → │
|
||
│ 📍 Записаться в салон (скоро) · │
|
||
└─────────────────────────────────────┘
|
||
```
|
||
|
||
В MVP активны: «Замер кухни», «Связаться с менеджером».
|
||
|
||
**Страница `#/c/measure`:**
|
||
|
||
Развилка:
|
||
```
|
||
Выберите способ замера:
|
||
|
||
[📞 Вызвать замерщика]
|
||
Бесплатно при заказе кухни
|
||
2 000 ₽ — отдельная услуга
|
||
|
||
[📏 Замерить самому]
|
||
5 шагов с инструкцией
|
||
Займёт ~15 минут
|
||
```
|
||
|
||
**Страница `#/c/measure/self`:**
|
||
|
||
5 экранов, каждый — отдельный шаг. Прогресс-бар сверху.
|
||
|
||
**Шаг 1: План кухни**
|
||
- Выбор формы (radio-картинки): прямая / Г-образная / П-образная / с островом / свой вариант
|
||
- Поле «Площадь, м²» (number)
|
||
- Поле «Высота потолка, мм» (number, default 2700)
|
||
|
||
**Шаг 2: Стены и габариты**
|
||
- Длина стены 1, мм
|
||
- Длина стены 2, мм (если Г-/П-образная)
|
||
- Длина стены 3, мм (если П-образная)
|
||
- Загрузка фото общего вида (опционально)
|
||
|
||
**Шаг 3: Окна и двери**
|
||
- Чекбоксы: «Есть окно» / «Есть дверь» / «Есть выход на балкон»
|
||
- Для каждого активного — поля: положение (выбор стены), ширина, высота, расстояние от пола
|
||
|
||
**Шаг 4: Коммуникации (важно для AI-подбора)**
|
||
- Тип подключения варочной: газ / электрика 220 / электрика 380
|
||
- Наличие вентиляционной шахты на кухне: да / нет / не знаю
|
||
- Расположение мойки: стена 1 / 2 / 3 / остров
|
||
- Свободное поле «Особенности коммуникаций»
|
||
|
||
**Шаг 5: Ниши под технику (если планируется встройка)**
|
||
Чекбокс «Планирую встроенную технику» → раскрывает блок:
|
||
- Холодильник: Ш × В × Г, мм
|
||
- Варочная: Ш × В × Г, мм
|
||
- Духовой шкаф: Ш × В × Г, мм
|
||
- Посудомойка: Ш × В × Г, мм
|
||
- Стиральная (если на кухне): Ш × В × Г, мм
|
||
- Микроволновка: Ш × В × Г, мм
|
||
- Кофемашина: Ш × В × Г, мм
|
||
|
||
Поля опциональные. Если не планируется встройка — блок свёрнут, шаг можно пропустить.
|
||
|
||
**Финал:**
|
||
- Кнопка «Сохранить замер».
|
||
- POST `/api/measurement` → сохранение + уведомление менеджеру (если manager_id привязан).
|
||
- `tg.showAlert('Замер сохранён')` + `tg.close()`.
|
||
|
||
### 4.5 Доработки существующего чек-листа подбора
|
||
|
||
Согласно анализу `02_Чек-лист_клиенту.html` нужно добавить:
|
||
|
||
1. **Блок «Параметры кухни»** в онбординг (или подгружать из последнего замера).
|
||
2. **Бюджет по категориям** — простая таблица в финальном экране.
|
||
3. **Блок «Семья и готовка»** — 4–5 быстрых вопросов с радиокнопками:
|
||
- Состав семьи: 1 взрослый / пара / семья с детьми / 2+ поколения
|
||
- Частота готовки: ежедневно / 3–4 раза в неделю / реже
|
||
- Любимые техники: выпечка / на пару / гриль / wok / низкотемпературное (multi-select)
|
||
- Приём гостей: часто / иногда / редко
|
||
4. **Чекбокс «уже есть, не меняю»** в каждой категории — клиент исключает позиции.
|
||
5. **Webhook вместо `mailto:`** — POST на backend.
|
||
6. **Обязательные поля**: имя, контакт, общий бюджет, адрес.
|
||
|
||
### 4.6 Дизайн-токены (брендинг ЗОВ)
|
||
|
||
Использовать существующие в HTML переменные:
|
||
```css
|
||
:root {
|
||
--bronze: #76BD22; /* фирменный зелёный */
|
||
--accent: #003E7E; /* фирменный синий */
|
||
--cream: #FAFAFA;
|
||
--sand: #F0F9E8;
|
||
}
|
||
```
|
||
|
||
Шрифт — системный sans-serif (как в текущем HTML). Скруглённые углы 8px, тени минимальные.
|
||
|
||
---
|
||
|
||
## 5. База данных (Google Sheets)
|
||
|
||
Файл: **«ЗОВ — База»** (один документ, несколько листов).
|
||
|
||
### 5.1 Лист «Users»
|
||
|
||
Все пользователи бота (менеджеры + клиенты).
|
||
|
||
| Колонка | Тип | Описание |
|
||
|---|---|---|
|
||
| tg_id | number | Telegram user ID, **первичный ключ** |
|
||
| tg_username | text | @username, может отсутствовать |
|
||
| first_name | text | Имя из Telegram |
|
||
| last_name | text | Фамилия из Telegram |
|
||
| role | text | `manager` / `client` / `admin` |
|
||
| created_at | datetime | Дата первого `/start` |
|
||
| last_seen_at | datetime | Последний контакт с ботом |
|
||
| invite_code_used | text | Код приглашения, если был |
|
||
|
||
### 5.2 Лист «Managers»
|
||
|
||
Расширенный профиль менеджеров.
|
||
|
||
| Колонка | Тип | Описание |
|
||
|---|---|---|
|
||
| tg_id | number | FK → Users.tg_id |
|
||
| full_name | text | ФИО полное |
|
||
| email | text | Рабочая почта |
|
||
| phone | text | Телефон |
|
||
| salon | text | Название салона |
|
||
| city | text | Город |
|
||
| is_zov_employee | bool | true = свой, false = партнёр |
|
||
| status | formula | `=IF(active_until>=TODAY();"active";"lapsed")` |
|
||
| last_order_date | date | Дата последней сделки через куратора |
|
||
| active_until | formula | `=last_order_date+90` |
|
||
| total_leads | formula | `=COUNTIF(Leads.manager_tg_id; tg_id)` |
|
||
| total_deals | number | Заполняется куратором вручную |
|
||
| conversion_rate | formula | `=total_deals / total_leads` |
|
||
| invite_code | text | Уникальный код для генерации client-ссылок (например `MGR_AB12`) |
|
||
|
||
### 5.3 Лист «Clients»
|
||
|
||
| Колонка | Тип | Описание |
|
||
|---|---|---|
|
||
| tg_id | number | FK → Users.tg_id |
|
||
| full_name | text | |
|
||
| phone | text | |
|
||
| email | text | |
|
||
| address | text | |
|
||
| city | text | |
|
||
| budget_total | number | Общий бюджет на кухню + технику |
|
||
| manager_tg_id | number | FK → Managers.tg_id, **связка клиент—менеджер** |
|
||
| source | text | `channel` / `manager_invite` / `direct` |
|
||
| last_measurement_id | text | FK → Measurements.id |
|
||
|
||
### 5.4 Лист «Measurements»
|
||
|
||
| Колонка | Тип | Описание |
|
||
|---|---|---|
|
||
| id | text | UUID |
|
||
| created_at | datetime | |
|
||
| client_tg_id | number | FK → Clients.tg_id |
|
||
| manager_tg_id | number | FK → Managers.tg_id |
|
||
| filled_by | text | `client_self` / `manager_for_client` |
|
||
| layout | text | прямая / Г / П / остров / другое |
|
||
| area_m2 | number | |
|
||
| ceiling_mm | number | |
|
||
| walls_json | json-text | `{"w1":3200,"w2":2400,"w3":null}` |
|
||
| openings_json | json-text | окна, двери |
|
||
| infra_json | json-text | газ/электрика, шахта, мойка |
|
||
| niches_json | json-text | размеры ниш под встройку |
|
||
| photos_urls | text | через запятую |
|
||
| notes | text | |
|
||
| status | text | `draft` / `submitted` / `verified` |
|
||
|
||
### 5.5 Лист «Leads» (заявки на подбор)
|
||
|
||
| Колонка | Тип | Описание |
|
||
|---|---|---|
|
||
| id | text | UUID |
|
||
| created_at | datetime | |
|
||
| manager_tg_id | number | кто отправил |
|
||
| client_tg_id | number | для кого |
|
||
| client_name | text | |
|
||
| measurement_id | text | FK → Measurements.id, может быть пусто |
|
||
| checklist_json | json-text | весь ответ чек-листа |
|
||
| ai_response | long-text | результат от LLM |
|
||
| ai_model | text | `gpt-4o-mini` |
|
||
| ai_tokens_used | number | для контроля расходов |
|
||
| sent_to_tg | bool | результат доставлен в Telegram |
|
||
| deal_status | text | `new` / `in_progress` / `won` / `lost` (заполняет менеджер вручную или куратор) |
|
||
| deal_amount | number | сумма закрытой кухни |
|
||
|
||
### 5.6 Лист «Logs»
|
||
|
||
Лог всех action-событий (для дебага и аналитики).
|
||
|
||
| Колонка | Тип |
|
||
|---|---|
|
||
| timestamp | datetime |
|
||
| event | text (`bot_start`, `role_selected`, `miniapp_opened`, `measurement_submitted`, `lead_created`, `ai_response_received`, `ai_error`, ...) |
|
||
| tg_id | number |
|
||
| payload | json-text |
|
||
|
||
### 5.7 Лист «Settings»
|
||
|
||
Параметры, которые могут меняться без правки кода.
|
||
|
||
| key | value | description |
|
||
|---|---|---|
|
||
| ACTIVE_PERIOD_DAYS | 90 | срок active-статуса после сделки |
|
||
| GRACE_PERIOD_DAYS | 14 | grace-период перед переводом в lapsed |
|
||
| AI_MODEL | gpt-4o-mini | какая модель используется |
|
||
| AI_TEMPERATURE | 0.3 | температура для подбора |
|
||
| ADMIN_TG_ID | <ваш tg_id> | кто получает алерты |
|
||
| PAID_PRICE_PER_LEAD | 500 | цена pay-per-use |
|
||
| PAID_SUBSCRIPTION | 3000 | цена подписки |
|
||
|
||
### 5.8 Лист «Dashboard» (для куратора)
|
||
|
||
Сводка с формулами:
|
||
|
||
```
|
||
Активных менеджеров: =COUNTIF(Managers.status; "active")
|
||
Lapsed-менеджеров: =COUNTIF(Managers.status; "lapsed")
|
||
Заявок за 30 дней: =COUNTIFS(Leads.created_at; ">="&TODAY()-30)
|
||
Конверсия в сделку: =COUNTIF(Leads.deal_status; "won") / COUNTA(Leads.id)
|
||
Средний чек закрытых сделок: =AVERAGEIF(Leads.deal_status; "won"; Leads.deal_amount)
|
||
Топ-5 менеджеров по сделкам: pivot
|
||
Расход на AI за 30 дней: =SUMPRODUCT(Leads.ai_tokens_used) * 0.0000015 * <курс>
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Backend (Google Apps Script)
|
||
|
||
### 6.1 Структура проекта
|
||
|
||
```
|
||
ZOV_Backend.gs
|
||
├─ doPost(e) — единая точка входа, роутер
|
||
├─ /api/me — определить пользователя по initData
|
||
├─ /api/measurement — сохранить замер
|
||
├─ /api/podbor — создать заявку, вызвать AI, отправить в Telegram
|
||
├─ /api/lead/:id — получить заявку (для менеджера)
|
||
├─ /api/manager/status — статус менеджера
|
||
├─ helpers/
|
||
│ ├─ verifyInitData(initData, botToken)
|
||
│ ├─ readSheet(sheetName, query)
|
||
│ ├─ writeSheet(sheetName, row)
|
||
│ ├─ callOpenAI(prompt, params)
|
||
│ ├─ sendTelegram(chatId, text, options)
|
||
│ └─ generateUuid()
|
||
```
|
||
|
||
### 6.2 Деплой
|
||
|
||
1. Привязать скрипт к Google Sheet «ЗОВ — База».
|
||
2. Deploy → New deployment → Type «Web app».
|
||
3. Execute as: «Me».
|
||
4. Who has access: «Anyone» (для приёма запросов с MiniApp).
|
||
5. Получить URL `https://script.google.com/macros/s/.../exec` — это backend endpoint, прописать в MiniApp как `BACKEND_URL`.
|
||
|
||
### 6.3 Переменные окружения (Script Properties)
|
||
|
||
| Ключ | Описание |
|
||
|---|---|
|
||
| `BOT_TOKEN` | Токен бота от BotFather |
|
||
| `OPENAI_API_KEY` | ключ OpenAI |
|
||
| `ADMIN_TG_ID` | ваш Telegram ID |
|
||
| `SHEET_ID` | ID Google Sheet |
|
||
|
||
### 6.4 Безопасность
|
||
|
||
- Все запросы из MiniApp **обязаны** содержать `initData` от Telegram.
|
||
- Backend проверяет HMAC `initData` по `BOT_TOKEN` (стандартная процедура).
|
||
- Без валидного `initData` любой запрос → 401.
|
||
- Документация процедуры: <https://core.telegram.org/bots/webapps#validating-data-received-via-the-mini-app>
|
||
|
||
### 6.5 Бизнес-логика статусов менеджера
|
||
|
||
```pseudo
|
||
function getManagerStatus(tg_id):
|
||
m = Managers.find(tg_id)
|
||
if not m: return "unknown"
|
||
if m.is_zov_employee: return "active" // свои всегда active
|
||
active_until = m.last_order_date + ACTIVE_PERIOD_DAYS
|
||
grace_until = active_until + GRACE_PERIOD_DAYS
|
||
today = TODAY()
|
||
if today <= active_until: return "active"
|
||
if today <= grace_until: return "grace" // мягкое предупреждение
|
||
return "lapsed"
|
||
```
|
||
|
||
---
|
||
|
||
## 7. AI-подбор
|
||
|
||
### 7.1 Входные данные (контракт LLM-вызова)
|
||
|
||
JSON, который backend собирает перед отправкой в OpenAI:
|
||
|
||
```json
|
||
{
|
||
"client": {
|
||
"name": "Пётр Сидоров",
|
||
"city": "Москва",
|
||
"family": "пара_с_ребёнком",
|
||
"cooking_frequency": "ежедневно",
|
||
"favorite_techniques": ["выпечка", "пар"],
|
||
"guests": "иногда"
|
||
},
|
||
"kitchen": {
|
||
"layout": "Г-образная",
|
||
"area_m2": 12,
|
||
"ceiling_mm": 2700,
|
||
"walls": { "w1": 3200, "w2": 2400 },
|
||
"infra": {
|
||
"stove_power": "электрика_220",
|
||
"vent_shaft": true
|
||
},
|
||
"niches": {
|
||
"fridge": { "w": 600, "h": 1850, "d": 600 },
|
||
"hob": { "w": 600, "h": 60, "d": 520 },
|
||
"oven": { "w": 600, "h": 600, "d": 560 },
|
||
"dw": { "w": 450, "h": 820, "d": 555 }
|
||
}
|
||
},
|
||
"budget": {
|
||
"total": 350000,
|
||
"by_category": {
|
||
"fridge": 80000,
|
||
"hob": 50000,
|
||
"oven": 60000,
|
||
"hood": 25000,
|
||
"dw": 50000,
|
||
"microwave": 15000,
|
||
"coffee": 40000,
|
||
"washer": 30000
|
||
}
|
||
},
|
||
"preferences": {
|
||
"fridge": {
|
||
"tier": "middle",
|
||
"brands_preferred": ["Bosch", "Liebherr"],
|
||
"brands_alternative": ["Samsung"],
|
||
"type": "двухкамерный",
|
||
"color": "inox",
|
||
"features": ["NoFrost", "инвертор", "≤40 дБ"]
|
||
},
|
||
"hob": { "...": "..." }
|
||
/* и т.д. для всех 8 категорий, как в чек-листе */
|
||
},
|
||
"exclusions": ["microwave"] // уже есть, не подбирать
|
||
}
|
||
```
|
||
|
||
### 7.2 Промпт (system + user)
|
||
|
||
**System prompt:**
|
||
|
||
```
|
||
Ты — эксперт-консультант по подбору кухонной техники для фабрики мебели «ЗОВ».
|
||
Ты работаешь с менеджерами салонов и помогаешь им быстро согласовать с клиентом
|
||
комплект техники под их кухню.
|
||
|
||
Принципы подбора:
|
||
1. Физические ограничения важнее эстетики. Если ниша 600×1850×600 — не предлагай
|
||
холодильник 700×2000×650, даже если он лучше.
|
||
2. Уважай бюджет. Если в категории задан лимит — не превышай его более чем на 10%.
|
||
3. Уважай предпочтения по брендам. Сначала пробуй preferred (★), потом alternative (✓),
|
||
потом нейтральные альтернативы. Если ни один preferred бренд не подходит по цене —
|
||
скажи прямо.
|
||
4. Связывай выбор со сценарием использования. Семья с детьми = простота интерфейса,
|
||
защита от детей, легко мыть. Любитель выпечки = пар + 4D HotAir + термощуп.
|
||
5. Учитывай инфраструктуру. Газ исключает индукцию (если пользователь не готов
|
||
менять подключение). Нет шахты = только рециркуляция в вытяжке.
|
||
6. По каждой позиции: модель, цена, 2-3 ключевых преимущества под этого клиента,
|
||
1 предупреждение/нюанс если он есть.
|
||
|
||
Формат ответа — JSON по схеме:
|
||
{
|
||
"summary": "1-2 предложения",
|
||
"items": [
|
||
{
|
||
"category": "fridge",
|
||
"brand": "Bosch",
|
||
"model": "KGN39LB35R",
|
||
"price_rub": 79990,
|
||
"size_mm": { "w": 600, "h": 2030, "d": 660 },
|
||
"fits_niche": true,
|
||
"highlights": ["NoFrost", "инвертор", "тихий 39 дБ"],
|
||
"caveats": "Глубина 660мм — на 60мм глубже стандартной ниши, проверьте",
|
||
"match_score": 0.92,
|
||
"rationale": "Соответствует предпочтениям по бренду и тиру..."
|
||
}
|
||
],
|
||
"total_price_rub": 350000,
|
||
"budget_status": "в рамках" | "превышение" | "значительно ниже",
|
||
"warnings": ["..."],
|
||
"next_steps": ["..."]
|
||
}
|
||
|
||
Не выдумывай несуществующие модели. Если не уверен в модели — указывай линейку
|
||
("Bosch Serie 4, 60см, NoFrost") а не конкретный артикул.
|
||
|
||
Если данных недостаточно для подбора в категории — оставляй её с пометкой
|
||
"need_more_info" и указывай, чего не хватает.
|
||
|
||
Отвечай только валидным JSON без оборачивания в markdown-блоки.
|
||
```
|
||
|
||
**User prompt:**
|
||
|
||
```
|
||
Подбери технику для следующего клиента:
|
||
|
||
{JSON_INPUT}
|
||
|
||
Дай развёрнутое предложение в формате, описанном в инструкции.
|
||
```
|
||
|
||
### 7.3 Параметры вызова OpenAI
|
||
|
||
```python
|
||
{
|
||
"model": "gpt-4o-mini",
|
||
"temperature": 0.3,
|
||
"max_tokens": 4000,
|
||
"response_format": { "type": "json_object" }
|
||
}
|
||
```
|
||
|
||
### 7.4 Постобработка ответа
|
||
|
||
Backend получает JSON от OpenAI и:
|
||
1. Валидирует схему. При расхождении — алёрт куратору, ответ всё равно пытается доставить.
|
||
2. Сохраняет в `Leads.ai_response` (raw JSON).
|
||
3. Рендерит в человекочитаемый вид (текст + кнопки).
|
||
4. Отправляет менеджеру через бота:
|
||
|
||
```
|
||
✅ Подбор готов
|
||
|
||
Клиент: Пётр Сидоров
|
||
Бюджет: 350 000 ₽
|
||
|
||
🧊 Холодильник
|
||
Bosch Serie 4 KGN39LB35R — 79 990 ₽
|
||
✓ NoFrost, инвертор, тихий 39 дБ
|
||
⚠️ Глубина 660мм — на 60мм больше ниши, проверьте
|
||
|
||
🔥 Варочная панель
|
||
Electrolux EHF6342XOK — 49 990 ₽
|
||
✓ Hi-Light, защита от детей, простой интерфейс
|
||
...
|
||
|
||
ИТОГО: 348 970 ₽ (в рамках бюджета)
|
||
|
||
[📄 Скачать предложение для клиента]
|
||
[💬 Открыть в кабинете]
|
||
```
|
||
|
||
---
|
||
|
||
## 8. Безопасность и приватность
|
||
|
||
### 8.1 Аутентификация
|
||
|
||
- Все вызовы backend от MiniApp подписаны `initData` Telegram.
|
||
- Backend верифицирует HMAC `initData` ≤ 24 часов давности.
|
||
- Без подписи → 401.
|
||
|
||
### 8.2 Контроль доступа
|
||
|
||
- Менеджер видит **только свои** заявки и замеры (фильтр `WHERE manager_tg_id = me.tg_id`).
|
||
- Клиент видит **только свои** замеры.
|
||
- Куратор (admin) видит всё.
|
||
|
||
### 8.3 Хранение PII
|
||
|
||
- Имена, телефоны, адреса клиентов лежат в Google Sheet куратора.
|
||
- Доступ к Sheet — только у куратора.
|
||
- В логи не пишутся телефоны/email открытым текстом — только маскированно (`+7***1234`).
|
||
|
||
### 8.4 Согласие на обработку данных
|
||
|
||
- При первом `/start` клиент получает короткое сообщение:
|
||
|
||
> Заполняя форму подбора, вы соглашаетесь с обработкой данных в рамках консультации по подбору кухни. Подробнее — [политика](https://wasrusgen.github.io/zov-tech/privacy.html).
|
||
|
||
- Простая HTML-страница `privacy.html` рядом с MiniApp.
|
||
|
||
---
|
||
|
||
## 9. Метрики успеха MVP
|
||
|
||
Через 60 дней после запуска проверяем:
|
||
|
||
| Метрика | Целевое значение |
|
||
|---|---|
|
||
| Зарегистрированных менеджеров | ≥ 60 из 112 |
|
||
| Менеджеров, сделавших ≥1 подбор | ≥ 30 |
|
||
| Сделанных замеров клиентами | ≥ 50 |
|
||
| Заявок на подбор | ≥ 80 |
|
||
| AI-подборов с положительной обратной связью | ≥ 70% |
|
||
| Время от заявки до результата | ≤ 60 секунд |
|
||
| Конверсия подбор → закрытая сделка | ≥ 20% |
|
||
| Стоимость одного подбора (AI) | ≤ 0.5 ₽ |
|
||
|
||
---
|
||
|
||
## 10. Дорожная карта после MVP
|
||
|
||
**Спринт 4 (через 1 месяц после MVP):**
|
||
- Раздел «Сделки» с возможностью менеджеру отмечать выигранные сделки.
|
||
- Авто-продление статуса по факту отметки сделки (с подтверждением куратора).
|
||
- Раздел «База знаний» с гайдами и сравнениями.
|
||
|
||
**Спринт 5 (через 2 месяца):**
|
||
- Платный режим для lapsed-менеджеров через ЮKassa или Telegram Stars.
|
||
- Подписка / pay-per-use, биллинг.
|
||
|
||
**Спринт 6 (3+ месяца):**
|
||
- Записаться в салон (выбор салона + дата + слот).
|
||
- Личный кабинет клиента с историей замеров и заявок.
|
||
- Раздел «Идеи и кейсы» с галереей.
|
||
- Партнёрки с производителями техники.
|
||
|
||
---
|
||
|
||
## 11. Чек-лист запуска
|
||
|
||
```
|
||
[ ] Бот создан в @BotFather, токен сохранён в Apps Script Properties
|
||
[ ] Домен MiniApp привязан к боту (/setdomain)
|
||
[ ] MiniApp зарегистрирован в @BotFather (/newapp)
|
||
[ ] HTML захостен на GitHub Pages, HTTPS работает
|
||
[ ] Google Sheet «ЗОВ — База» создан, листы 5.1—5.7 готовы
|
||
[ ] Apps Script деплой как Web App, URL получен
|
||
[ ] OpenAI ключ получен, лимиты выставлены ($20/мес)
|
||
[ ] Реестр 112 менеджеров загружен в Sheet «Managers»
|
||
[ ] Свои менеджеры ЗОВ помечены is_zov_employee=true
|
||
[ ] Существующий чек-лист `02_Чек-лист_клиенту.html` доработан (4.5)
|
||
[ ] Форма самозамера реализована (4.4 шаги 1-5)
|
||
[ ] AI-промпт протестирован на 5 синтетических заявках
|
||
[ ] Уведомления бота работают (3.6)
|
||
[ ] Privacy-страница опубликована
|
||
[ ] /admin меню работает только для ADMIN_TG_ID
|
||
[ ] Бот переведён в режим webhook
|
||
[ ] Закрепили пост в канале с кнопкой WebApp
|
||
[ ] Сделали внутренний прогон с 3 «своими» менеджерами
|
||
```
|
||
|
||
---
|
||
|
||
## 12. Контакты и ответственные
|
||
|
||
| Роль | Кто | Контакт |
|
||
|---|---|---|
|
||
| Заказчик / куратор | Василий | vasrusgen@gmail.com, @wasrusgen |
|
||
| Канал | @wasrusgen1 | |
|
||
| Telegram-бот | @zov_tech_bot (после регистрации) | |
|
||
| MiniApp URL | https://wasrusgen.github.io/zov-tech/ (плановый) | |
|
||
|
||
---
|
||
|
||
## 13. Приложение: что отдаём разработчику
|
||
|
||
При выдаче ТЗ разработчику передаём:
|
||
1. Этот документ (`ТЗ_ЗОВ_Бот_MiniApp_v1.md`).
|
||
2. Текущий HTML-чек-лист `02_Чек-лист_клиенту.html`.
|
||
3. Доступ к Telegram-каналу @wasrusgen1 (для размещения кнопки).
|
||
4. Креды от Google-аккаунта для создания Sheet и Apps Script.
|
||
5. (После регистрации бота) — токен и admin_tg_id в зашифрованном виде.
|
||
|
||
**Ожидаемое время выполнения MVP:** 5–7 рабочих дней.
|
||
**Ожидаемая стоимость инфраструктуры в месяц:** $5–15 (только OpenAI, всё остальное бесплатно).
|