🏭 B2B-платформа изготовителей и ремонтников оборудования Узбекистана
Техническое задание, архитектура данных, структура интерфейса и пользовательские сценарии (User Flow)
B2B MarketplaceМультиязычность RU / UZ Latin / UZ CyrillicГео-поискАукцион заказовTelegram BotПодписочная модельАрбитраж споровБарахолка станков и инструментаВакансии и резюмеСамостоятельная регистрация без админа
Суть проекта: сервис, который соединяет заказчиков (предприятия, нуждающиеся в изготовлении
деталей, узлов или в ремонте оборудования) с исполнителями (частные мастера, цеха, заводы Узбекистана).
Заказчик публикует заявку в свободной форме (текст/фото/чертежи/видео), система автоматически подбирает
исполнителей по техническим возможностям и геолокации, оповещает их через Telegram, и исполнители
конкурируют за заказ в формате аукциона (цена/срок/условия). Доступ к платформе — только после
обязательной регистрации («жёсткий paywall» на уровне разделов, а не главной страницы). Все профили —
и заказчика, и исполнителя — заполняются пользователем самостоятельно при регистрации, без ручного
участия администратора. Дополнительно платформа включает доску объявлений купли-продажи
металлообрабатывающих станков и инструмента, а также раздел вакансий/резюме (найм персонала) —
оба раздела построены по единой модели «объявление + отклик» с мгновенным Telegram-уведомлением
обеим сторонам.
1 Роли пользователей и права доступа
👤 Заказчик (Customer)
Юр. или физ. лицо, которому требуется изготовление детали/узла или ремонт оборудования.
Публикует заказы, просматривает каталог и карту исполнителей, выбирает победителя аукциона,
оставляет отзывы, инициирует споры.
🔧 Исполнитель (Executor)
Подтипы: Мастер (частное лицо/ИП), Цех (малое производство),
Завод (крупное производство). Заполняет карточку станочного парка и допустимых
габаритов, получает уведомления о заказах, участвует в аукционах, оплачивает подписку.
Не пользователь, а служебная роль: движок автоподбора исполнителей и Telegram-бот,
действующие по правилам модулей 2 и 7.
Один аккаунт (users.id) может иметь только одну активную бизнес-роль
(customer либо executor) для простоты MVP; переключение роли — через создание второго профиля на тот же email,
по аналогии с «личным» и «рабочим» кабинетом. Роль admin — отдельный флаг, не публичная регистрация.
Принцип самообслуживания: ни один профиль на платформе не
создаётся и не заполняется вручную администратором. И заказчик, и исполнитель проходят один и тот же
механизм — обязательную регистрацию (модуль 1) и мастер заполнения профиля (модуль 3) сразу после неё.
Роль администратора ограничена последующей верификацией/модерацией уже заполненных данных, а не их вводом.
2 Архитектура базы данных
Реляционная модель (PostgreSQL + расширение PostGIS для гео-запросов). Ниже — таблицы,
сгруппированные по доменам. Все таблицы имеют id UUID PK, created_at, updated_at —
опущены в схемах для краткости.
2.1 Пользователи и авторизация
users — базовая учётная запись
Поле
Тип
Описание
email
varchar UNIQUE
основной email, обязателен даже при входе через Google
password_hash
varchar NULL
NULL, если вход только через Google
google_id
varchar NULL UNIQUE
sub из Google OAuth
phone
varchar NULL
для Telegram-верификации и связи
role
enum(customer, executor, admin)
основная роль аккаунта
preferred_language
enum(ru, uz_latin, uz_cyrillic)
язык интерфейса
status
enum(active, blocked, pending_verification)
статус аккаунта
is_email_verified
boolean
last_login_at
timestamp
customer_profiles — карточка заказчика (1:1 к users)
Поле
Тип
Описание
user_id
FK → users.id
company_name
varchar NULL
если юр. лицо
stir_inn
varchar NULL
ИНН/СТИР для верификации бизнеса
region_id / city_id
FK → regions / cities
основной регион
address_text
varchar
location
geography(Point,4326)
координаты объекта заказчика (для радиуса поиска)
rating_avg
numeric(3,2)
кэш среднего рейтинга от исполнителей
reviews_count
int
executor_profiles — карточка исполнителя (1:1 к users)
Поле
Тип
Описание
user_id
FK → users.id
org_type
enum(master, tsekh, zavod)
масштаб производства
display_name
varchar
название цеха/завода или ФИО мастера
description
text
описание, хранится по языку публикации + опц. переводы
stir_inn
varchar NULL
region_id / city_id
FK → regions / cities
address_text
varchar
location
geography(Point,4326)
координаты цеха/завода — основа гео-поиска
service_radius_km
int
готовность выезжать/принимать заказы в радиусе (для ремонта на месте)
is_verified
boolean
прошёл проверку админом
rating_avg
numeric(3,2)
кэш рейтинга от заказчиков
reviews_count
int
subscription_status
enum(trial, active, expired, none)
кэш из subscriptions для быстрой проверки доступа
executor_equipment — станочный парк исполнителя (1:N)
executor_capabilities — допустимые габариты и материалы (1:1 или 1:N по категориям)
Поле
Тип
Описание
executor_id
FK → executor_profiles.id
max_length_mm
int NULL
макс. длина детали
max_diameter_mm
int NULL
макс. диаметр (для токарных работ)
max_width_mm / max_height_mm
int NULL
для листовых/фрезерных работ
max_weight_kg
numeric NULL
макс. вес заготовки, которую поднимает оборудование (кран-балка и т.п.)
materials
varchar[] / FK-таблица
сталь, чугун, алюминий, нержавейка, пластик, латунь и т.д.
service_categories
varchar[] / FK-таблица
теги услуг: токарные работы, фрезеровка, лазерная резка, сварочные работы, ремонт редукторов, ремонт насосов…
Поля service_categories и materials — управляемые справочники
(many-to-many через связочные таблицы executor_capability_services /
executor_capability_materials), что необходимо для точного алгоритма автоподбора (модуль 2).
2.2 География
regions / cities — справочник регионов Узбекистана
Поле
Тип
Описание
name_ru / name_uz_latin / name_uz_cyrillic
varchar
14 областей + Каракалпакстан + Ташкент, привязанные города
location
geography(Point,4326)
центр региона/города — для дефолтного центрирования карты
2.3 Заказы, медиа и матчинг
orders — заявка заказчика
Поле
Тип
Описание
customer_id
FK → customer_profiles.id
title
varchar
description
text
content_language
enum(ru, uz_latin, uz_cyrillic)
язык, на котором заказчик реально ввёл текст
order_type
enum(manufacturing, repair)
изготовление или ремонт
service_category
FK → service_categories
тип работ, по которому идёт матчинг
material
FK → materials NULL
dimensions_length_mm / diameter_mm / weight_kg
numeric NULL
параметры детали — сверяются с executor_capabilities
budget_min / budget_max
numeric NULL
вилка бюджета (опционально, влияет на аукцион)
deadline_date
date NULL
желаемый срок
region_id / city_id
FK
location
geography(Point,4326)
точка объекта — для ремонта на месте и радиуса поиска
order_messages — чат заказчик ↔ исполнитель по заказу (1:N)
Поле
Тип
Описание
order_id
FK → orders.id
sender_id / receiver_id
FK → users.id
text
text NULL
attachment_url
varchar NULL
Чат открывается только после того, как заказчик проявил интерес к ставке исполнителя —
защита от спама на этапе аукциона.
content_translations — кэш авто-перевода UGC (опционально)
Поле
Тип
Описание
entity_type
enum(order, executor_profile)
entity_id
uuid
target_language
enum(ru, uz_latin, uz_cyrillic)
translated_title / translated_text
text
provider
varchar
сервис машинного перевода
UI-строки (кнопки, лейблы, статусы) переводятся статически (i18n JSON-файлы ru.json,
uz_latin.json, uz_cyrillic.json), а не через БД. Данная таблица покрывает только
пользовательский контент (заявки, описания цехов), чтобы заказчик и исполнитель с разным родным языком
понимали друг друга.
2.8 Барахолка — купля-продажа станков и инструмента
Отдельный от заказов домен: не услуга, а конкретный физический товар (станок, инструмент, запчасть,
расходники). Продавцом или покупателем может быть любой зарегистрированный пользователь — и заказчик,
и исполнитель (цех нередко одновременно и покупает, и распродаёт оборудование).
listings — объявление о продаже/покупке (1:N к users)
Поле
Тип
Описание
author_id
FK → users.id
автор объявления (продавец либо покупатель)
listing_intent
enum(sell, buy)
«продаю» или «ищу купить»
category
FK → listing_categories
станки с ЧПУ, ручной инструмент, сварочное оборудование, запчасти, расходники и т.д.
title / description
varchar / text
content_language
enum(ru, uz_latin, uz_cyrillic)
condition
enum(new, used, for_parts)
состояние — не заполняется при listing_intent = buy
price
numeric NULL
NULL / «договорная» допускается
currency
enum(UZS, USD)
region_id / city_id
FK
location
geography(Point,4326) NULL
для показа на карте/поиска рядом, опционально
status
enum(active, reserved, closed, archived)
listing_media — фото/видео товара (1:N)
Поле
Тип
Описание
listing_id
FK → listings.id
media_type
enum(photo, video)
file_url
varchar
listing_responses — отклики на объявление (1:N)
Поле
Тип
Описание
listing_id
FK → listings.id
from_user_id
FK → users.id
кто откликнулся («интересует», предложение цены)
message
text NULL
offered_price
numeric NULL
торг
Каждый новый отклик немедленно уведомляет listings.author_id (Telegram + in-app) —
та же механика, что и уведомление заказчика о новой ставке в модуле 2 (см. 10.4).
2.9 Вакансии и резюме (найм персонала)
Двусторонняя доска: работодатель размещает вакансию, соискатель — резюме («ищу работу»); каждая
сторона может откликнуться на объявление другой стороны, инициатором может быть как работодатель, так
и работник.
vacancies — вакансия от работодателя (1:N к users)
Поле
Тип
Описание
employer_id
FK → users.id
любой пользователь — цех/завод (executor) или предприятие (customer)
кто начал: соискатель откликнулся на вакансию, либо работодатель пригласил по резюме
message
text NULL
Ровно один из vacancy_id/resume_id заполнен (CHECK-ограничение). Как и в
listing_responses, каждая запись немедленно порождает уведомление получателю (см. 10.4).
2.10 Сводка связей (ER, кратко)
users 1—1 customer_profiles либо executor_profiles
users 1—N vacancies; users 1—N resumes; оба 1—N job_responses
Индексы: GIST по всем полям geography(Point) для радиус-поиска
(ST_DWithin); составной индекс (service_category, region_id, status) на orders
для быстрого матчинга; полнотекстовый индекс (pg_trgm/tsvector) на
executor_profiles.description и orders.title/description для поиска по каталогу.
3 Структура интерфейса (карта разделов)
3.1 Открытая зона (без авторизации)
Главная (/) — оффер, «как это работает» в 3 шага, преимущества, витрина из 4–6
обезличенных карточек исполнителей/выполненных проектов (без клика), кнопки «Разместить заказ» и
«Стать исполнителем» — обе ведут к paywall-модалке.
О платформе, Тарифы для исполнителей (маркетинговое описание,
без входа в личный кабинет), Контакты/Поддержка.
/auth/login, /auth/register — форма email + кнопка «Войти через Google».
3.2 Закрытая зона (после регистрации)
Раздел
Для кого
Назначение
/orders/new — Конструктор заказа
Заказчик
мультимедийная форма создания заявки (модуль 2)
/orders/:id
Заказчик, приглашённые исполнители
карточка заказа, статус, ставки аукциона, чат
/orders (мои заказы)
Заказчик
список со статусами (open/в аукционе/в работе/завершён/спор)
/catalog
Заказчик
каталог исполнителей с фильтрами по услуге, региону, рейтингу
/map
Заказчик
интерактивная карта Узбекистана с исполнителями (модуль 4)
Лента заказов · Мои ставки · Профиль и оборудование ·
Барахолка · Вакансии · Отзывы · Подписка ·
Сообщения
3.4 Навигация заказчика
Мои заказы · Новый заказ · Каталог · Карта ·
Барахолка · Вакансии · Отзывы · Сообщения
4 Модуль 1 — Авторизация и «жёсткий» Paywall
Логика доступа: публичны только маркетинговые страницы (главная, о платформе, тарифы,
auth). Любой переход на защищённый роут при отсутствии активной сессии перехватывается
route-guard'ом (middleware), который вместо загрузки страницы показывает модальное окно
и не выполняет навигацию (в адресной строке остаётся исходный URL или редирект на
/?authRequired=1&next=/orders/new).
User Flow: попытка неавторизованного доступа
Гость открывает главную страницу — доступна полностью, без ограничений.
Фронтенд проверяет наличие валидного JWT/сессии → сессии нет.
Показывается модальное окно: «Для продолжения работы необходима регистрация» с двумя
вкладками — «Войти» / «Зарегистрироваться», и кнопкой «Войти через Google».
Целевой URL сохраняется как next — после успешной авторизации пользователь
автоматически попадает туда, куда изначально пытался перейти.
User Flow: регистрация через Email
Пользователь выбирает роль: «Я заказчик» / «Я исполнитель».
Система создаёт users со статусом pending_verification, отправляет письмо со ссылкой подтверждения.
Пользователь переходит по ссылке → is_email_verified = true, status = active.
Онбординг: заказчика ведут в конструктор заказа; исполнителя — на обязательное заполнение
профиля (модуль 3), без которого он не появляется в матчинге.
User Flow: вход через Google
Клик «Войти через Google» → OAuth-редирект → согласие пользователя.
Бэкенд получает email и google_id. Если email уже существует —
аккаунты связываются (google_id прикрепляется к существующему users); если нет — создаётся
новый аккаунт, is_email_verified = true сразу (email подтверждён Google).
Если это первый вход — короткий шаг выбора роли (заказчик/исполнитель), т.к. Google не
сообщает эту информацию.
Восстановление пароля — стандартный email-flow. Для пользователей, вошедших
только через Google (password_hash IS NULL), кнопка «Забыли пароль» скрыта — предлагается
«Войти через Google».
5 Модуль 2 — Конструктор заказа и автоподбор исполнителей
Мультимедийный конструктор — шаги формы
Тип и категория: «Изготовление» или «Ремонт» → выбор категории работ
(токарные, фрезерные, лазерная резка, сварка, ремонт редуктора и т.д. — из справочника service_categories).
Описание: текстовое поле (в родном языке пользователя,
content_language проставляется автоматически по preferred_language с
возможностью переключить).
Медиа: drag-and-drop загрузка фото, чертежей (PDF/JPG/DWG-превью), видео
(до N МБ, с клиентским сжатием изображений перед отправкой).
Параметры детали: материал, габариты (длина/диаметр/вес) — опционально,
но повышают точность матчинга и обязательны, если заявка ищет исполнителя по станочным ограничениям.
Локация и бюджет: адрес объекта (для ремонта) или регион доставки
заготовки (для изготовления), опциональная вилка бюджета, желаемый срок и длительность аукциона
(24 / 48 / 72 часа).
Если через 25% времени аукциона ставок меньше 3 — система автоматически расширяет
радиус поиска и/или ослабляет негеографические веса, дозаявляя дополнительных исполнителей.
Если кандидатов не найдено вовсе — заказчику показывается уведомление
«Пока нет подходящих исполнителей, мы оповестим вас, как только появится совпадение» и заказ остаётся
в статусе matching с фоновым пересчётом при регистрации новых исполнителей.
6 Модуль 3 — Профиль и техвозможности исполнителя и заказчика
Профиль исполнителя не считается «опубликованным» (не участвует в матчинге и не виден в каталоге/на
карте), пока не заполнен обязательный минимум: тип организации, регион/адрес с координатами, хотя бы
одна позиция станочного парка и хотя бы одна запись в executor_capabilities с указанием
габаритов и услуг.
User Flow: онбординг заказчика (полностью самостоятельный, без участия администратора)
Сразу после подтверждения email (или первого входа через Google) заказчик попадает на
экран «Заполните карточку компании» — минимум полей, чтобы не отпугнуть, но достаточно для будущих
заказов.
Кто вы: физ. лицо или компания (юр. лицо), название/ФИО, контактный
телефон (совпадает с полем users.phone), опционально ИНН/СТИР.
Локация объекта: адрес текстом + перетаскивание маркера на карте —
та же механика геокодирования, что и у исполнителя (модуль 4), нужна для гео-поиска исполнителей
«рядом» и для полей объявлений в модулях 8/9.
Дальше — сразу шаг «Опубликовать первый заказ» (необязательный, можно пропустить и
зайти в каталог/на карту). Никаких данных за заказчика никто не вводит — все поля заполняются им
самим в личном кабинете, без обращения в поддержку или к администратору.
Профиль можно донаполнить позже в /settings//profile в любой
момент — обязательные поля блокируют только публикацию заказа, а не доступ в кабинет.
Тот же принцип действует и для объявлений барахолки (модуль 8), и для
вакансий/резюме (модуль 9): автор объявления заполняет карточку сам через форму — администратор в
этот процесс не вовлекается, кроме постмодерации по жалобам.
User Flow: онбординг исполнителя
После регистрации — экран «Заполните профиль, чтобы начать получать заказы» с прогресс-баром
(Основная информация → Локация → Станочный парк → Габариты и материалы → Подписка).
Основная информация: тип (мастер/цех/завод), название, описание,
контакты, ИНН/СТИР (для верификации).
Локация: адрес вводится текстом + подтверждается перетаскиванием
маркера на карте (геокодирование с ручной корректировкой), регион/город определяются автоматически по
координатам.
Станочный парк: мультивыбор из справочника оборудования +
возможность добавить произвольную позицию с модерацией админом.
Габариты и допуски: форма макс. длины, диаметра, ширины/высоты, веса;
мультивыбор материалов и категорий услуг (то, что реально сверяется алгоритмом матчинга).
Портфолио (опц.): фото выполненных работ — повышает конверсию в
каталоге, не обязателен для матчинга.
Заявка на верификацию уходит админу (бейдж «Проверено» после ручной/полуавтоматической
проверки ИНН); профиль уже участвует в матчинге и до верификации, но с меньшим весом ранжирования.
Выбор тарифа подписки → оплата (модуль 7) → профиль полностью активен.
Редактирование станочного парка и габаритов доступно в любой момент из
/profile/equipment; изменения пересчитывают матчинг для новых заказов немедленно, но не
затрагивают уже отправленные order_matches.
7 Модуль 4 — Карта и гео-поиск
Заказчик открывает раздел «Карта» — интерактивная карта Узбекистана (Leaflet/MapLibre +
тайлы, напр. 2GIS/OSM) со всеми верифицированными и активными (подписка не истекла) исполнителями в
виде кластеризованных маркеров.
Фильтры на карте: категория услуг, тип организации (мастер/цех/завод), минимальный
рейтинг, только с активной подпиской.
Клик по маркеру → всплывающая карточка с кратким описанием, рейтингом, ключевым
оборудованием и ссылкой на полный профиль /executor/:id.
Геопоиск «рядом»: кнопка «Показать рядом со мной» — браузерная
геолокация или ручной ввод адреса объекта → SQL-запрос
ST_DWithin(executor.location, point, radius) с выбором радиуса (5/10/25/50/100 км) —
список сортируется по расстоянию.
Из карты можно сразу перейти в конструктор заказа с предзаполненной локацией объекта
или отправить точечный запрос конкретному исполнителю в обход общего аукциона («прямой заказ»).
Та же гео-логика используется в модуле 2 (автоподбор): координаты заказа
сравниваются с координатами исполнителей через тот же индекс GIST, поэтому «Карта» и
«Автоподбор» дают согласованные результаты.
8 Модуль 5 — Отзывы и рейтинги
Отзыв становится доступен только когда orders.status = completed
(заказчик подтвердил приёмку работы либо истёк срок автоматического подтверждения — 7 дней без
возражений).
Обеим сторонам приходит уведомление «Оцените сотрудничество» со ссылкой на форму
отзыва по шкале 1–5 + детализация (для исполнителя: качество, соблюдение сроков, коммуникация;
для заказчика: чёткость ТЗ, своевременность оплаты, коммуникация) + текстовый комментарий.
Отзывы скрыты друг от друга до момента, пока не оставлены обе стороны, либо не истекло
14 дней с завершения заказа — после чего публикуются одновременно (защита от предвзятости «жду его
оценку, чтобы ответить тем же»).
Опубликованный отзыв пересчитывает rating_avg/reviews_count в
профиле, влияет на позицию в каталоге, на карте и в весах матчинга (модуль 2).
Жалоба на недостоверный отзыв → эскалация админу (не путать с арбитражем по заказу,
модуль 6) с возможностью скрыть отзыв до проверки.
Любая сторона на заказе в статусе in_progress или completed
(в течение 14 дней после завершения) нажимает «Открыть спор», указывает причину
(брак / задержка / претензия по качеству / расхождение по цене / другое) и текстовое описание.
orders.status → disputed; вторая сторона получает уведомление и обязана
ответить в течение 48 часов.
Сбор доказательств: обе стороны загружают фото/видео в
dispute_evidence — заказчик подтверждает брак/несоответствие, исполнитель — процесс
и результат работы, соответствие ТЗ. Возможна переписка в dispute_messages.
Спор автоматически или вручную (по кнопке «Готово к рассмотрению» от обеих сторон, либо
по истечении 5 дней сбора доказательств) переходит в in_review и назначается
администратору из очереди (round-robin / по загрузке).
Администратор изучает доказательства, при необходимости запрашивает дополнительные
материалы (внутренние заметки/сообщения сторонам), выносит решение с обоснованием.
Решение фиксируется (resolution_text, итоговый статус), обе стороны
уведомляются, заказ переводится в финальный статус (completed с пометкой о споре или
cancelled).
Решение влияет на рейтинг: спор, решённый в пользу заказчика, может блокировать
публикацию заведомо положительного отзыва исполнителя по этому заказу (анти-накрутка).
Рекомендация на будущее (вне текущего ТЗ, но важно для зрелости
платформы): ввести эскроу — оплата исполнителю удерживается платформой до подтверждения
приёмки заказчиком, а при открытии спора автоматически замораживается до решения администратора. Это
резко повышает эффективность арбитража, так как решение админа получает реальный денежный вес, а не
только репутационный.
Исполнитель открывает бота, нажимает Start → бэкенд по токену связывает
telegram_chat_id с users.id в таблице telegram_links.
Тарифные планы (пример)
Тариф
Ставок в мес.
Скорость Telegram-алертов
Приоритет в матчинге
Basic
до 10
с задержкой ~2 часа
базовый
Standard
до 40
мгновенно
средний
Pro
без лимита
мгновенно + первым в очереди
максимальный
Оплата — Payme / Click / Uzcard (локальные для Узбекистана), автопродление ежемесячно; при
subscription.status = expired исполнитель исключается из автоподбора (модуль 2) и скрывается
из каталога/карты, но сохраняет доступ к своему кабинету и истории заказов для продления.
User Flow: аукцион заказа целиком
Заказ опубликован → топ-N исполнителей получают Telegram-сообщение: краткое описание,
категория, регион, фото (первое из вложений), кнопка «Открыть заказ на платформе».
Исполнитель переходит по ссылке (deep-link открывает конкретный /orders/:id
в мини-приложении/браузере), изучает полное описание, чертежи, видео.
Исполнитель подаёт ставку: цена, срок выполнения, комментарий/условия →
bids, статус active. Проверяется лимит max_bids_per_month тарифа.
Заказчик в реальном времени видит список ставок (сортировка по цене/сроку/рейтингу),
может открыть чат с конкретным исполнителем для уточнений — чат разблокируется при первом сообщении
заказчика.
Исполнители, чью ставку «обошли» более выгодным предложением, получают Telegram-уведомление
«Вас обошли по цене» с возможностью подать новую ставку (пока аукцион открыт) — механика реального
тендера.
По истечении auction_deadline_at либо по ручному выбору заказчика раньше
срока — заказчик нажимает «Выбрать исполнителя» на понравившейся ставке →
order_assignments создаётся, orders.status → assigned, остальные ставки
помечаются rejected, все участники получают итоговое уведомление (Telegram + in-app).
Исполнитель отмечает начало работ (in_progress) и завершение
(completed, с подтверждением заказчика) → запускаются модуль 5 (отзывы) и, при
необходимости, модуль 6 (спор).
Команды Telegram-бота (минимальный набор)
/start — привязка аккаунта
/orders — список актуальных заказов, подобранных под профиль
/mybids — статус поданных ставок
/subscription — статус подписки, ссылка на продление
Настройка уведомлений (вкл/выкл по категориям) прямо в чате бота
10.4 Единый принцип: любой отклик = мгновенное Telegram-уведомление второй стороне
Бот не ограничен только заказами и ставками — правило одинаково применяется ко всем «объявленческим»
модулям платформы (2, 8, 9): у каждого объявления есть автор и есть тот, кто на него откликается;
как только в БД появляется новая запись отклика, фоновый воркер немедленно (либо с задержкой тарифа,
см. 10.2) отправляет уведомление стороне-получателю через все привязанные каналы (Telegram + in-app).
Событие (кто откликнулся)
Кто получает Telegram-уведомление
Таблица-источник
Исполнитель подал ставку на заказ
Заказчик — автор заказа
bids
Заказчик выбрал/отклонил ставку
Исполнитель — автор ставки
order_assignments / bids.status
Кто-то откликнулся на объявление барахолки
Автор объявления (продавец или покупатель)
listing_responses
Соискатель откликнулся на вакансию
Работодатель — автор вакансии
job_responses (direction = candidate_to_employer)
Работодатель пригласил по резюме
Соискатель — автор резюме
job_responses (direction = employer_to_candidate)
Иными словами: сторона, которая разместила заказ, объявление, вакансию или резюме, всегда
узнаёт об отклике первой и мгновенно — независимо от того, заказчик она, исполнитель, продавец,
покупатель, работодатель или работник. Это одна и та же нотификационная шина
(notifications + telegram_links), просто с разными значениями поля
type (см. 2.7).
11 Модуль 8 — Барахолка: купля-продажа станков и инструмента
Отдельная от заявок на изготовление/ремонт витрина объявлений: продажа и покупка конкретной
физической единицы оборудования, инструмента, запчастей или расходников. Доступна и заказчикам, и
исполнителям — по сути это классифайд внутри той же аудитории, что снижает трение (не нужно уходить на
сторонние площадки, чтобы продать списанный станок или найти б/у пресс).
User Flow: разместить объявление «Продаю»
Раздел «Барахолка» → «Разместить объявление» → тип «Продаю».
Категория (станки с ЧПУ, ручной инструмент, сварочное оборудование, запчасти,
расходники…), название, описание, состояние (новое/б.у./на запчасти).
Фото/видео товара, цена (или пометка «Договорная»), регион/город, при желании — точка
на карте.
Публикация → listings.status = active, объявление сразу видно в общей
ленте и на карте (если указана локация); дополнительная модерация не блокирует публикацию, а
работает постфактум по жалобам.
User Flow: разместить объявление «Куплю» и откликнуться на чужое объявление
Тип «Куплю» — та же форма, но без поля «состояние»: пользователь описывает, какой
станок/инструмент ищет, и в каком бюджете.
Любой пользователь может как откликнуться на «Продаю» (написать продавцу, предложить
свою цену), так и на «Куплю» (предложить свой товар тому, кто ищет) — форма отклика одна и та же,
различается лишь то, кто выступает инициатором.
Отклик создаёт запись в listing_responses → автору объявления мгновенно
приходит Telegram-уведомление + уведомление в кабинете (см. 10.4), открывается чат по объявлению.
Автор объявления помечает его «Зарезервировано» либо «Закрыто» (сделка состоялась) —
listings.status; объявление уходит из активной ленты, но остаётся в истории пользователя.
Модуль намеренно не включает встроенный эскроу/оплату на MVP — платформа
сводит стороны и уведомляет, а расчёт происходит вне системы (как в классических классифайдах);
это можно пересмотреть на более поздних этапах по аналогии с рекомендацией по эскроу в модуле 6.
12 Модуль 9 — Вакансии и резюме (найм персонала)
Двусторонняя доска объявлений о работе: работодатели (цеха, заводы, предприятия-заказчики) публикуют
вакансии, а соискатели — резюме. Отклик возможен инициативой любой стороны, поэтому Telegram-бот должен
одинаково уведомлять и работодателя, и соискателя — не только «работодатель ищет», но и «работника
заметили».
User Flow: работодатель размещает вакансию
Раздел «Вакансии» → «Разместить вакансию»: профессия (из справочника
profession_categories — токарь, фрезеровщик, сварщик, наладчик ЧПУ, слесарь-ремонтник и
т.д.), описание обязанностей/требований.
Тип занятости, вилка зарплаты, регион/город → публикация,
vacancies.status = active.
Вакансия автоматически показывается в ленте всем соискателям региона/профессии (без
отдельного алгоритма матчинга — простая фильтрация по profession_category +
region_id, в отличие от сложного матчинга заказов в модуле 2).
Работодатель также может зайти в раздел «Резюме», отфильтровать по профессии/региону и
первым пригласить понравившегося кандидата — это создаёт job_responses с
direction = employer_to_candidate.
User Flow: соискатель откликается на вакансию
Любой зарегистрированный пользователь может создать резюме в разделе «Резюме» — это не
требует роли customer/executor, достаточно базовой учётной записи (модуль 1).
Соискатель просматривает ленту вакансий, открывает подходящую, нажимает «Откликнуться»
→ создаётся job_responses с direction = candidate_to_employer, можно
приложить сообщение.
Работодатель мгновенно получает Telegram-уведомление «Новый отклик на вакансию
“…”» (см. 10.4) и переходит либо к резюме кандидата, либо сразу в чат.
Симметрично: если работодатель первым пригласил кандидата по резюме, уведомление о
приглашении мгновенно получает сам соискатель — платформа не делает работника «вторым сортом» по
сравнению с работодателем.
Справочник profession_categories частично пересекается со
справочником услуг заказов (service_categories из модуля 2) — токарь/фрезеровщик/сварщик
актуальны и там, и там, поэтому их стоит вести как связанные, но разные таблицы: одна описывает
компетенцию организации (что умеет цех), другая — компетенцию человека (кого ищет работодатель).
13 Нефункциональные требования
🔒 Безопасность
Хеширование паролей (bcrypt/argon2), OAuth 2.0 для Google, JWT + refresh-токены.
Права доступа проверяются и на фронтенде (route-guard/paywall), и обязательно на бэкенде
(RLS/сервисный слой) — фронтовая блокировка не является security-контролем.
Подписанные временные URL для загруженных медиа (не публичные пути к бакету).
Rate-limiting на подачу ставок и создание заказов — анти-спам/анти-накрутка.
🌍 Мультиязычность
UI-строки — статические словари ru / uz_latin / uz_cyrillic, переключатель языка
в шапке, сохраняется в users.preferred_language.
Латиница и кириллица узбекского — не автоматическая транслитерация «на лету» для UGC
(риск ошибок), а выбор языка ввода самим пользователем + опциональный машинный перевод
(content_translations) для контента другого языка.
📈 Масштабируемость
Матчинг и рассылка уведомлений — асинхронные фоновые задачи (очередь), не блокируют публикацию
заказа.
Геозапросы — индексы PostGIS, кэш каталога/карты (Redis) с инвалидацией по изменению профиля.
⚖️ Соответствие законодательству РУз
Закон «О персональных данных» РУз — согласие на обработку данных при регистрации, хранение
данных с учётом требований локализации.
Интеграция локальных платёжных систем (Payme, Click, Uzcard/Humo) для подписки исполнителей.
14 Рекомендуемый технологический стек
Слой
Технология
Комментарий
Backend
Node.js (NestJS) или Python (FastAPI/Django)
REST/GraphQL API, фоновые задачи через очередь (BullMQ/Celery)
БД
PostgreSQL + PostGIS
гео-запросы, полнотекстовый поиск
Кэш/очереди
Redis
кэш каталога/карты, очередь уведомлений
Хранилище файлов
S3-совместимое (MinIO / облако)
фото, чертежи, видео, доказательства споров
Frontend Web
React/Next.js или Vue/Nuxt
SSR для главной (SEO), SPA для закрытой зоны
Карта
MapLibre GL / Leaflet + тайлы 2ГИС или OSM
2ГИС даёт лучшее покрытие адресов Узбекистана
Telegram
Telegram Bot API (webhook)
уведомления, deep-links, мини-приложение (Telegram Web App) — опционально для подачи ставки прямо в боте
Платежи
Payme Business API, Click Merchant API, Uzcard
подписочные платежи
Auth
Email/пароль + Google OAuth 2.0
как указано в ТЗ
15 Дорожная карта
MVP (этап 1)
Регистрация email/Google + paywall, полностью самостоятельное заполнение профиля заказчиком
и исполнителем (без ручного ввода админом)