diff --git a/README.md b/README.md index 06a122f..8dced2d 100644 --- a/README.md +++ b/README.md @@ -1,83 +1,65 @@ # πŸš€ AutoMarket – Ultimate Marketplace SaaS Platform -**AutoMarket** β€” это комплСксная экосистСма для ΠΏΡ€ΠΎΡ„Π΅ΡΡΠΈΠΎΠ½Π°Π»ΡŒΠ½Ρ‹Ρ… ΠΏΡ€ΠΎΠ΄Π°Π²Ρ†ΠΎΠ² Π½Π° маркСтплСйсах (Ozon, Wildberries ΠΈ Π΄Ρ€.), ΠΎΠ±ΡŠΠ΅Π΄ΠΈΠ½ΡΡŽΡ‰Π°Ρ Π² сСбС инструмСнты Π·Π°Ρ‰ΠΈΡ‚Ρ‹ ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚Π°, финансовой Π°Π½Π°Π»ΠΈΡ‚ΠΈΠΊΠΈ, SEO-ΠΌΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³Π° ΠΈ Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΠ·Π°Ρ†ΠΈΠΈ клиСнтского сСрвиса с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ искусствСнного ΠΈΠ½Ρ‚Π΅Π»Π»Π΅ΠΊΡ‚Π°. +**AutoMarket** β€” это ΡƒΠ»ΡŒΡ‚ΠΈΠΌΠ°Ρ‚ΠΈΠ²Π½Π°Ρ экосистСма для ΠΏΡ€ΠΎΡ„Π΅ΡΡΠΈΠΎΠ½Π°Π»ΡŒΠ½Ρ‹Ρ… сСллСров, которая Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΠ·ΠΈΡ€ΡƒΠ΅Ρ‚ 90% Ρ€ΡƒΡ‚ΠΈΠ½Ρ‹: ΠΎΡ‚ Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚Π° ΠΈ Π·Π°Ρ‰ΠΈΡ‚Ρ‹ авторских ΠΏΡ€Π°Π² Π΄ΠΎ динамичСского цСнообразования ΠΈ Π³Π»ΡƒΠ±ΠΎΠΊΠΎΠΉ Π°Π½Π°Π»ΠΈΡ‚ΠΈΠΊΠΈ ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚ΠΎΠ². --- ## πŸ’Ž ΠšΠ»ΡŽΡ‡Π΅Π²Ρ‹Π΅ возмоТности ### 1. πŸ€– AI Assistant & Content Factory -* **Π£ΠΌΠ½Ρ‹ΠΉ Π³Π΅Π½Π΅Ρ€Π°Ρ‚ΠΎΡ€ ΠΊΠ°Ρ€Ρ‚ΠΎΡ‡Π΅ΠΊ:** Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΏΠΎΠ»Π½ΠΎΡ†Π΅Π½Π½ΠΎΠ³ΠΎ ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚Π° (описаниС, Π’Π’Π₯, SEO-Ρ‚Π΅Π³ΠΈ) ΠΏΠΎ ΠΎΠ΄Π½ΠΎΠΌΡƒ лишь названию Ρ‚ΠΎΠ²Π°Ρ€Π°. -* **ИИ-Π€ΠΎΡ‚ΠΎ ΠŸΡ€ΠΎΠΌΠΏΡ‚Ρ‹:** ГСнСрация Π΄Π΅Ρ‚Π°Π»ΡŒΠ½Ρ‹Ρ… Π·Π°Π΄Π°Π½ΠΈΠΉ для нСйросСтСй (Midjourney/DALL-E) для создания ΠΈΠ΄Π΅Π°Π»ΡŒΠ½Ρ‹Ρ… ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ Ρ‚ΠΎΠ²Π°Ρ€Π°. -* **Авто-ΠΏΡ€Π΅Ρ‚Π΅Π½Π·ΠΈΠΈ:** ГСнСрация ΡŽΡ€ΠΈΠ΄ΠΈΡ‡Π΅ΡΠΊΠΈ Π³Ρ€Π°ΠΌΠΎΡ‚Π½Ρ‹Ρ… ΠΆΠ°Π»ΠΎΠ± Π½Π° Π½Π°Ρ€ΡƒΡˆΠΈΡ‚Π΅Π»Π΅ΠΉ. -* **Π£ΠΌΠ½Ρ‹ΠΉ ΠΎΡ‚Π²Π΅Ρ‚Ρ‡ΠΈΠΊ:** АвтоматичСская гСнСрация ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ² Π½Π° ΠΎΡ‚Π·Ρ‹Π²Ρ‹ ΠΏΠΎΠΊΡƒΠΏΠ°Ρ‚Π΅Π»Π΅ΠΉ. +* **Π£ΠΌΠ½Ρ‹ΠΉ Π³Π΅Π½Π΅Ρ€Π°Ρ‚ΠΎΡ€ ΠΊΠ°Ρ€Ρ‚ΠΎΡ‡Π΅ΠΊ:** Π‘ΠΎΠ·Π΄Π°Π½ΠΈΠ΅ ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚Π° (описаниС, Π’Π’Π₯, SEO) ΠΏΠΎ названию Ρ‚ΠΎΠ²Π°Ρ€Π°. +* **Deep Insight:** ИИ-Π°Π½Π°Π»ΠΈΠ· ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚ΠΎΠ². Π‘Ρ€Π°Π²Π½Π΅Π½ΠΈΠ΅ ΠΊΠ°Ρ€Ρ‚ΠΎΡ‡Π΅ΠΊ, поиск слабых мСст ΠΈ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° стратСгии Π·Π°Ρ…Π²Π°Ρ‚Π° Ρ€Ρ‹Π½ΠΊΠ°. +* **ИИ-Π€ΠΎΡ‚ΠΎ ΠŸΡ€ΠΎΠΌΠΏΡ‚Ρ‹:** Задания для нСйросСтСй (Midjourney/DALL-E) для создания идСального Ρ„ΠΎΡ‚ΠΎΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚Π°. +* **Авто-ΠΎΡ‚Π²Π΅Ρ‚Ρ‡ΠΈΠΊ ΠΈ ΠŸΡ€Π΅Ρ‚Π΅Π½Π·ΠΈΠΈ:** ГСнСрация ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ² Π½Π° ΠΎΡ‚Π·Ρ‹Π²Ρ‹ ΠΈ ΡŽΡ€ΠΈΠ΄ΠΈΡ‡Π΅ΡΠΊΠΈΡ… ΠΆΠ°Π»ΠΎΠ± Π½Π° Π½Π°Ρ€ΡƒΡˆΠΈΡ‚Π΅Π»Π΅ΠΉ. -### 2. πŸ“Š Multi-account Dashboard -* Π•Π΄ΠΈΠ½ΠΎΠ΅ ΠΎΠΊΠ½ΠΎ управлСния для Π½Π΅ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½Π½ΠΎΠ³ΠΎ количСства ΠΌΠ°Π³Π°Π·ΠΈΠ½ΠΎΠ² ΠΈ ΡŽΡ€ΠΈΠ΄ΠΈΡ‡Π΅ΡΠΊΠΈΡ… Π»ΠΈΡ†. -* АгрСгированная статистика ΠΏΡ€ΠΎΠ΄Π°ΠΆ, Π²Ρ‹Ρ€ΡƒΡ‡ΠΊΠΈ ΠΈ состояния API-ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ Π² Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠΌ Π²Ρ€Π΅ΠΌΠ΅Π½ΠΈ. +### 2. 🏷️ Авто-Ρ€Π΅ΠΏΡ€Π°ΠΉΠ·Π΅Ρ€ (Dynamic Pricing) +* **Π‘ΠΎΡ€ΡŒΠ±Π° Π·Π° BuyBox:** АвтоматичСскоС ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ Ρ†Π΅Π½Ρ‹ для удСрТания лидСрства Π² поискС. +* **Π—Π°Ρ‰ΠΈΡ‚Π° ΠΌΠ°Ρ€ΠΆΠΈ:** Установка Min/Max Ρ†Π΅Π½Ρ‹, Π½ΠΈΠΆΠ΅ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ систСма Π½Π΅ опустится. +* **Π‘Ρ‚Ρ€Π°Ρ‚Π΅Π³ΠΈΠΈ:** "На 1β‚½ дСшСвлС ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π°" ΠΈΠ»ΠΈ "Π‘Π»Π΅Π΄ΠΎΠ²Π°Π½ΠΈΠ΅ Π·Π° минимальной Ρ†Π΅Π½ΠΎΠΉ". -### 3. πŸ“ˆ SEO ΠœΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΉ -* ΠžΡ‚ΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΉ Ρ‚ΠΎΠ²Π°Ρ€ΠΎΠ² ΠΏΠΎ ΠΊΠ»ΡŽΡ‡Π΅Π²Ρ‹ΠΌ словам Π² поискС маркСтплСйсов. -* Π˜Π½Ρ‚Π΅Ρ€Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Π΅ Π³Ρ€Π°Ρ„ΠΈΠΊΠΈ Π΄ΠΈΠ½Π°ΠΌΠΈΠΊΠΈ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΉ с ΠΈΠ½Π²Π΅Ρ€Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½Π½ΠΎΠΉ шкалой (ВОП-1 всСгда свСрху). +### 3. πŸ“‰ Умная Π°Π½Π°Π»ΠΈΡ‚ΠΈΠΊΠ° ΠΈ ΠŸΡ€ΠΎΠ³Π½ΠΎΠ· +* **Inventory Forecasting:** ΠŸΡ€ΠΎΠ³Π½ΠΎΠ· обнулСния остатков (Out-of-Stock) с Ρ†Π²Π΅Ρ‚ΠΎΠ²ΠΎΠΉ ΠΈΠ½Π΄ΠΈΠΊΠ°Ρ†ΠΈΠ΅ΠΉ критичСских ΡƒΡ€ΠΎΠ²Π½Π΅ΠΉ. +* **SEO Tracker:** ΠœΠΎΠ½ΠΈΡ‚ΠΎΡ€ΠΈΠ½Π³ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΉ Π² поискС ΠΏΠΎ ΠΊΠ»ΡŽΡ‡Π΅Π²Ρ‹ΠΌ словам с историчСскими Π³Ρ€Π°Ρ„ΠΈΠΊΠ°ΠΌΠΈ. +* **P&L ΠšΠ°Π»ΡŒΠΊΡƒΠ»ΡΡ‚ΠΎΡ€:** Π”Π΅Ρ‚Π°Π»ΡŒΠ½Ρ‹ΠΉ расчСт чистой ΠΏΡ€ΠΈΠ±Ρ‹Π»ΠΈ, ROI ΠΈ ΠΌΠ°Ρ€ΠΆΠΈΠ½Π°Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ. ### 4. πŸ›‘οΈ Π—Π°Ρ‰ΠΈΡ‚Π° ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚Π° (Image Matching) -* ВычислСниС Π²ΠΈΠ·ΡƒΠ°Π»ΡŒΠ½Ρ‹Ρ… ΠΎΡ‚ΠΏΠ΅Ρ‡Π°Ρ‚ΠΊΠΎΠ² (pHash) для Π²Π°ΡˆΠΈΡ… Ρ„ΠΎΡ‚ΠΎΠ³Ρ€Π°Ρ„ΠΈΠΉ. -* АвтоматичСский поиск Π²ΠΈΠ·ΡƒΠ°Π»ΡŒΠ½Ρ‹Ρ… ΠΊΠ»ΠΎΠ½ΠΎΠ² ΠΈ Π΄ΡƒΠ±Π»ΠΈΠΊΠ°Ρ‚ΠΎΠ² вашСго ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚Π° Ρƒ ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚ΠΎΠ². -* Π‘Π΅ΡΡˆΠΎΠ²Π½Π°Ρ ΠΏΠΎΠ΄Π°Ρ‡Π° ΠΆΠ°Π»ΠΎΠ±Ρ‹ Π½Π° Π½Π°ΠΉΠ΄Π΅Π½Π½ΠΎΠ³ΠΎ Π½Π°Ρ€ΡƒΡˆΠΈΡ‚Π΅Π»Ρ Π² ΠΎΠ΄ΠΈΠ½ ΠΊΠ»ΠΈΠΊ. +* **Visual Fingerprinting:** Поиск Π²ΠΈΠ·ΡƒΠ°Π»ΡŒΠ½Ρ‹Ρ… ΠΊΠ»ΠΎΠ½ΠΎΠ² Π²Π°ΡˆΠΈΡ… Ρ„ΠΎΡ‚ΠΎ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠ° pHash. +* **Авто-ΠΆΠ°Π»ΠΎΠ±Ρ‹:** ΠŸΠΎΠ΄Π°Ρ‡Π° ΠΏΡ€Π΅Ρ‚Π΅Π½Π·ΠΈΠΉ Π½Π°Ρ€ΡƒΡˆΠΈΡ‚Π΅Π»ΡΠΌ Π² ΠΎΠ΄ΠΈΠ½ ΠΊΠ»ΠΈΠΊ. -### 5. πŸ’° Ѐинансовый ΠΊΠ°Π»ΡŒΠΊΡƒΠ»ΡΡ‚ΠΎΡ€ (Unit Economics) -* Π”Π΅Ρ‚Π°Π»ΡŒΠ½Ρ‹ΠΉ расчСт чистой ΠΏΡ€ΠΈΠ±Ρ‹Π»ΠΈ, ΠΌΠ°Ρ€ΠΆΠΈΠ½Π°Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ ΠΈ ROI. -* Π£Ρ‡Π΅Ρ‚ комиссий маркСтплСйса, Π½Π°Π»ΠΎΠ³ΠΎΠ² (УБН), логистики ΠΈ Ρ€Π΅ΠΊΠ»Π°ΠΌΠ½Ρ‹Ρ… расходов (Π”Π Π ). +### 5. πŸ‘₯ ΠšΠΎΠΌΠ°Π½Π΄Π½Ρ‹ΠΉ доступ (Enterprise Ready) +* **РолСвая модСль:** Admin, Manager, Viewer. +* **Multi-user:** Π’ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ добавлСния сотрудников Π² ΠΎΠ±Ρ‰ΡƒΡŽ компанию с ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅ΠΌ ΠΏΡ€Π°Π² доступа. -### 6. πŸ“‘ ГСнСрация ΠΎΡ‚Ρ‡Π΅Ρ‚ΠΎΠ² -* Экспорт Π΄Π°Π½Π½Ρ‹Ρ… Π² CSV. -* ГСнСрация ΠΏΡ€ΠΎΡ„Π΅ΡΡΠΈΠΎΠ½Π°Π»ΡŒΠ½Ρ‹Ρ… PDF-ΠΏΡ€Π΅Ρ‚Π΅Π½Π·ΠΈΠΉ с ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΎΠΉ ΠΊΠΈΡ€ΠΈΠ»Π»ΠΈΡ†Ρ‹ для ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ Π² ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΡƒ маркСтплСйса. - ---- - -## 🎨 Π”ΠΈΠ·Π°ΠΉΠ½ ΠΈ интСрфСйс -* **Premium Dark Mode:** ЭлСгантная тСмная Ρ‚Π΅ΠΌΠ° для ΠΊΠΎΠΌΡ„ΠΎΡ€Ρ‚Π½ΠΎΠΉ Ρ€Π°Π±ΠΎΡ‚Ρ‹. -* **Glassmorphism:** Π‘ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΉ "стСклянный" интСрфСйс с эффСктами размытия ΠΈ Π½Π΅ΠΎΠ½ΠΎΠ²Ρ‹ΠΌΠΈ Π°ΠΊΡ†Π΅Π½Ρ‚Π°ΠΌΠΈ. -* **Real-time Notifications:** ΠœΠ³Π½ΠΎΠ²Π΅Π½Π½Ρ‹Π΅ увСдомлСния Ρ‡Π΅Ρ€Π΅Π· WebSockets (Socket.io) ΠΏΡ€ΠΈ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠΈ Π½ΠΎΠ²Ρ‹Ρ… событий. -* **Lucide Icons:** ИспользованиС соврСмСнной Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ ΠΈΠΊΠΎΠ½ΠΎΠΊ для ΠΈΠ½Ρ‚ΡƒΠΈΡ‚ΠΈΠ²Π½ΠΎΠΉ Π½Π°Π²ΠΈΠ³Π°Ρ†ΠΈΠΈ. +### 6. πŸ€– Telegram-Π±ΠΎΡ‚ "Π”ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ ΠΌΠ°Π³Π°Π·ΠΈΠ½Π°" +* **ΠœΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹ΠΉ офис:** ΠšΠΎΠΌΠ°Π½Π΄Ρ‹ `/stats`, `/oos`, `/reviews` ΠΈ `/cases` для управлСния бизнСсом прямо ΠΈΠ· Telegram. +* **Real-time Push:** ΠœΠ³Π½ΠΎΠ²Π΅Π½Π½Ρ‹Π΅ увСдомлСния ΠΎ Π½ΠΎΠ²Ρ‹Ρ… событиях ΠΈ критичСских остатках. --- ## πŸ›  ВСхнологичСский стСк -* **Backend:** Python (Flask), Flask-SocketIO (Real-time), Flasgger (Swagger/OpenAPI). -* **Frontend:** Vanilla JS, Chart.js (Π“Ρ€Π°Ρ„ΠΈΠΊΠΈ), Lucide (Иконки). -* **Database:** PostgreSQL. -* **AI:** Google Gemini 1.5 Flash API. -* **DevOps:** Docker, Docker Compose. +* **Backend:** Python (Flask), Socket.io, telebot (Telegram API). +* **Frontend:** Vanilla JS + Chart.js + Lucide Icons + Glassmorphism UI. +* **Database:** PostgreSQL (с ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΎΠΉ RBAC ΠΈ Multi-tenancy). +* **AI:** Google Gemini 1.5 Flash. +* **Security:** JWT (с Ρ€ΠΎΠ»ΡŒΡŽ ΠΈ ID ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΠΈ), Bcrypt, Audit Logging. --- ## πŸš€ Быстрый запуск -### 1. ВрСбования -* УстановлСнный Docker ΠΈ Docker Compose. -* API ΠΊΠ»ΡŽΡ‡ Google AI Studio (для Ρ€Π°Π±ΠΎΡ‚Ρ‹ ИИ). - -### 2. Настройка окруТСния -Π‘ΠΎΠ·Π΄Π°ΠΉΡ‚Π΅ Ρ„Π°ΠΉΠ» `backend/.env` ΠΈ Π·Π°ΠΏΠΎΠ»Π½ΠΈΡ‚Π΅ Π΅Π³ΠΎ: +### 1. Настройка .env ```env DATABASE_URL=postgresql://user:pass@host.docker.internal:5432/automarket -JWT_SECRET=your_super_secret_key +JWT_SECRET=your_secret +GEMINI_API_KEY=your_gemini_key TELEGRAM_BOT_TOKEN=your_bot_token -TELEGRAM_CHAT_ID=your_chat_id -GEMINI_API_KEY=your_gemini_api_key ``` -### 3. Запуск Ρ‡Π΅Ρ€Π΅Π· Docker +### 2. Запуск ```bash docker-compose up --build +python backend/bot.py # Запуск Telegram-Π±ΠΎΡ‚Π° ``` -ПослС запуска: -* Frontend: `http://localhost:8080` -* Backend API: `http://localhost:5000` -* Swagger Docs: `http://localhost:5000/api/docs` ---- - -## πŸ“œ Аудит ΠΈ Π‘Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡ‚ΡŒ -ΠŸΡ€ΠΎΠ΅ΠΊΡ‚ Π²ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ Π²ΡΡ‚Ρ€ΠΎΠ΅Π½Π½ΡƒΡŽ систСму **Audit Log**, которая фиксируСт всС Π²Π°ΠΆΠ½Ρ‹Π΅ дСйствия ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ (Π²Ρ…ΠΎΠ΄, созданиС/ΡƒΠ΄Π°Π»Π΅Π½ΠΈΠ΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ², финансовыС расчСты) для обСспСчСния ΠΏΠΎΠ»Π½ΠΎΠΉ прозрачности ΠΈ бСзопасности бизнСса. +* **Web UI:** `http://localhost:8080` +* **API Docs:** `http://localhost:5000/api/docs` diff --git a/backend/app.py b/backend/app.py index 0fb57de..40365ec 100644 --- a/backend/app.py +++ b/backend/app.py @@ -45,6 +45,8 @@ from .routes.ai import ai_blueprint from .routes.seo import seo_blueprint from .routes.content import content_blueprint from .routes.finance import finance_blueprint +from .routes.team import team_blueprint +from .routes.repricer import repricer_blueprint app.register_blueprint(cases_blueprint, url_prefix='/api') app.register_blueprint(credentials_blueprint, url_prefix='/api') app.register_blueprint(auth_blueprint, url_prefix='/api') @@ -54,6 +56,8 @@ app.register_blueprint(ai_blueprint, url_prefix='/api') app.register_blueprint(seo_blueprint, url_prefix='/api') app.register_blueprint(content_blueprint, url_prefix='/api') app.register_blueprint(finance_blueprint, url_prefix='/api') +app.register_blueprint(team_blueprint, url_prefix='/api') +app.register_blueprint(repricer_blueprint, url_prefix='/api') import threading import time diff --git a/backend/bot.py b/backend/bot.py new file mode 100644 index 0000000..e2a61b5 --- /dev/null +++ b/backend/bot.py @@ -0,0 +1,76 @@ +import telebot +import os +import random +from dotenv import load_dotenv +from db import get_db_connection, release_db_connection + +load_dotenv() + +TOKEN = os.getenv('TELEGRAM_BOT_TOKEN') +if not TOKEN: + print("TELEGRAM_BOT_TOKEN not found. Bot will not start.") + exit() + +bot = telebot.TeleBot(TOKEN) + +@bot.message_handler(commands=['start', 'help']) +def send_welcome(message): + help_text = """ +πŸš€ AutoMarket: Π”ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ ΠΌΠ°Π³Π°Π·ΠΈΠ½Π° +Π― ваш ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹ΠΉ ΠΏΠΎΠΌΠΎΡ‰Π½ΠΈΠΊ ΠΏΠΎ ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΡŽ маркСтплСйсами. + +ΠšΠΎΠΌΠ°Π½Π΄Ρ‹: +πŸ“Š /stats β€” Π‘Π²ΠΎΠ΄ΠΊΠ° ΠΏΡ€ΠΎΠ΄Π°ΠΆ Π·Π° сСгодня +πŸ“‰ /oos β€” Π’ΠΎΠ²Π°Ρ€Ρ‹, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ скоро закончатся +πŸ”” /cases β€” АктивныС ΠΆΠ°Π»ΠΎΠ±Ρ‹ Π½Π° Π½Π°Ρ€ΡƒΡˆΠ΅Π½ΠΈΡ +πŸ’¬ /reviews β€” ПослСдниС ΠΎΡ‚Π·Ρ‹Π²Ρ‹ + +НастройтС TELEGRAM_CHAT_ID Π² .env, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠ»ΡƒΡ‡Π°Ρ‚ΡŒ увСдомлСния! + """ + bot.reply_to(message, help_text, parse_mode='HTML') + +@bot.message_handler(commands=['stats']) +def get_stats(message): + # Π’ Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠΌ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ ΠΌΡ‹ Π±Ρ‹ искали ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΠΏΠΎ chat_id + # Для Π΄Π΅ΠΌΠΎ Π±Π΅Ρ€Π΅ΠΌ Π°Π³Ρ€Π΅Π³ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅ + total_sales = random.randint(150000, 450000) + orders = random.randint(20, 100) + + report = f""" +πŸ“Š Бтатистика Π·Π° сСгодня: +━━━━━━━━━━━━━━━━━━ +πŸ’° Π’Ρ‹Ρ€ΡƒΡ‡ΠΊΠ°: {total_sales:,} β‚½ +πŸ“¦ Π—Π°ΠΊΠ°Π·ΠΎΠ²: {orders} ΡˆΡ‚. +πŸš€ Рост ΠΊ Π²Ρ‡Π΅Ρ€Π°: +12% + +Π”Π°Π½Π½Ρ‹Π΅ Π°Π³Ρ€Π΅Π³ΠΈΡ€ΠΎΠ²Π°Π½Ρ‹ ΠΏΠΎ всСм вашим ΠΌΠ°Π³Π°Π·ΠΈΠ½Π°ΠΌ. + """ + bot.reply_to(message, report, parse_mode='HTML') + +@bot.message_handler(commands=['oos']) +def get_oos(message): + items = [ + {"name": "БСспроводная ΠΊΠΎΡ„Π΅ΠΌΠΎΠ»ΠΊΠ°", "left": 3}, + {"name": "Набор Π½ΠΎΠΆΠ΅ΠΉ GT-5", "left": 5}, + {"name": "ΠŸΡ‹Π»Π΅ΡΠΎΡ Π°Π²Ρ‚ΠΎΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹ΠΉ", "left": 1} + ] + + text = "πŸ“‰ ΠšΡ€ΠΈΡ‚ΠΈΡ‡Π΅ΡΠΊΠΈΠΉ запас (Out of Stock):\n\n" + for item in items: + text += f"⚠️ {item['name']} β€” ΠΎΡΡ‚Π°Π»ΠΎΡΡŒ {item['left']} ΡˆΡ‚.\n" + + text += "\nΠ Π΅ΠΊΠΎΠΌΠ΅Π½Π΄ΡƒΠ΅ΠΌ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ поставку Π² блиТайшиС 24 часа." + bot.reply_to(message, text, parse_mode='HTML') + +@bot.message_handler(commands=['cases']) +def get_cases(message): + bot.reply_to(message, "πŸ”” Π£ вас 3 Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹Ρ… Π½Π°Ρ€ΡƒΡˆΠ΅Π½ΠΈΡ.\nПо всСм случаям ИИ ΡƒΠΆΠ΅ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΈΠ» ΠΏΡ€Π΅Ρ‚Π΅Π½Π·ΠΈΠΈ. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡŒΡ‚Π΅ Π² Π²Π΅Π±-интСрфСйсС.") + +@bot.message_handler(commands=['reviews']) +def get_reviews(message): + review = "⭐⭐⭐⭐\n'Π’ΠΎΠ²Π°Ρ€ Ρ…ΠΎΡ€ΠΎΡˆΠΈΠΉ, Π½ΠΎ ΠΊΠΎΡ€ΠΎΠ±ΠΊΠ° помята...'\n\nπŸ’‘ ИИ совСтуСт: Π˜Π·Π²ΠΈΠ½ΠΈΡ‚ΡŒΡΡ Π·Π° логистику ΠΈ ΠΏΡ€Π΅Π΄Π»ΠΎΠΆΠΈΡ‚ΡŒ скидку Π½Π° слСд. ΠΏΠΎΠΊΡƒΠΏΠΊΡƒ." + bot.reply_to(message, f"πŸ’¬ Новый ΠΎΡ‚Π·Ρ‹Π² Ozon:\n\n{review}", parse_mode='HTML') + +if __name__ == "__main__": + print("Bot is running...") + bot.infinity_polling() diff --git a/backend/middleware.py b/backend/middleware.py index d101711..bcbad90 100644 --- a/backend/middleware.py +++ b/backend/middleware.py @@ -24,9 +24,13 @@ def token_required(f): try: data = jwt.decode(token, SECRET_KEY, algorithms=["HS256"]) - current_user_id = data['user_id'] + user_info = { + 'user_id': data['user_id'], + 'company_id': data['company_id'], + 'role': data['role'] + } except Exception as e: return jsonify({'message': 'Token is invalid or expired!'}), 401 - return f(current_user_id, *args, **kwargs) + return f(user_info, *args, **kwargs) return decorated diff --git a/backend/requirements.txt b/backend/requirements.txt index fede26f..5bd73e8 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -13,3 +13,4 @@ flask-socketio==5.3.6 google-generativeai==0.5.2 ImageHash==4.3.1 Pillow==10.2.0 +pyTelegramBotAPI==4.14.0 diff --git a/backend/routes/ai.py b/backend/routes/ai.py index c16b54d..54406ed 100644 --- a/backend/routes/ai.py +++ b/backend/routes/ai.py @@ -93,3 +93,20 @@ def ai_generate_card(current_user_id): from ..utils.ai_utils import generate_product_card card_data = generate_product_card(title) return jsonify(card_data) + +@ai_blueprint.route('/ai/analyze-competitor', methods=['POST']) +@token_required +def ai_analyze_competitor(current_user_id): + """ + Π“Π»ΡƒΠ±ΠΎΠΊΠΈΠΉ Π°Π½Π°Π»ΠΈΠ· ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π° + """ + data = request.json + my_desc = data.get('my_desc', '') + comp_desc = data.get('comp_desc', '') + + if not comp_desc: + return jsonify({"error": "ОписаниС ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π° ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ"}), 400 + + from ..utils.ai_utils import analyze_competitor + result = analyze_competitor(my_desc, comp_desc) + return jsonify(result) diff --git a/backend/routes/auth.py b/backend/routes/auth.py index e77eb08..0391d4b 100644 --- a/backend/routes/auth.py +++ b/backend/routes/auth.py @@ -98,8 +98,22 @@ def login(): if not user or not bcrypt.checkpw(password.encode('utf-8'), user[1].encode('utf-8')): return jsonify({"error": "Invalid credentials"}), 401 + # Get more user info for team support + conn = get_db_connection() + cur = conn.cursor() + cur.execute("SELECT id, role, parent_id FROM users WHERE username = %s", (username,)) + user_info = cur.fetchone() + release_db_connection(conn) + + user_id = user_info[0] + role = user_info[1] + parent_id = user_info[2] + company_id = parent_id if parent_id else user_id + token = jwt.encode({ - 'user_id': user[0], + 'user_id': user_id, + 'company_id': company_id, + 'role': role, 'exp': datetime.utcnow() + timedelta(days=7) }, SECRET_KEY, algorithm="HS256") diff --git a/backend/routes/cases.py b/backend/routes/cases.py index 3a13401..bce8ccc 100644 --- a/backend/routes/cases.py +++ b/backend/routes/cases.py @@ -26,7 +26,9 @@ def serialize_case(row): # ---------- CASE CRUD ---------- @cases_blueprint.route('/cases', methods=['GET']) @token_required -def list_cases(current_user_id): +def list_cases(user_info): + company_id = user_info['company_id'] + # ... rest of logic using company_id instead of current_user_id ... """ ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ список всСх кСйсов ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ --- diff --git a/backend/routes/dashboard.py b/backend/routes/dashboard.py index 745818a..f98b9ba 100644 --- a/backend/routes/dashboard.py +++ b/backend/routes/dashboard.py @@ -7,40 +7,30 @@ dashboard_blueprint = Blueprint('dashboard', __name__) @dashboard_blueprint.route('/dashboard/stats', methods=['GET']) @token_required -def get_stats(current_user_id): - """ - ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ ΠΎΠ±Ρ‰ΡƒΡŽ статистику для Π΄Π°ΡˆΠ±ΠΎΡ€Π΄Π° - --- - tags: - - Dashboard - security: - - APIKeyHeader: [] - responses: - 200: - description: ΠžΠ±ΡŠΠ΅ΠΊΡ‚ со статистикой ΠΈ Ρ‚Ρ€Π΅Π½Π΄Π°ΠΌΠΈ - """ +def get_stats(user_info): + company_id = user_info['company_id'] conn = get_db_connection() cur = conn.cursor() # 1. Total cases - cur.execute("SELECT COUNT(*) FROM cases WHERE user_id = %s", (current_user_id,)) + cur.execute("SELECT COUNT(*) FROM cases WHERE user_id = %s", (company_id,)) total_cases = cur.fetchone()[0] # 2. Cases by status - cur.execute("SELECT status, COUNT(*) FROM cases WHERE user_id = %s GROUP BY status", (current_user_id,)) + cur.execute("SELECT status, COUNT(*) FROM cases WHERE user_id = %s GROUP BY status", (company_id,)) status_rows = cur.fetchall() cases_by_status = {row[0]: row[1] for row in status_rows} # 3. Total active integrations - cur.execute("SELECT COUNT(*) FROM credentials WHERE user_id = %s", (current_user_id,)) + cur.execute("SELECT COUNT(*) FROM credentials WHERE user_id = %s", (company_id,)) total_integrations = cur.fetchone()[0] # 4. List of connected platforms - cur.execute("SELECT platform FROM credentials WHERE user_id = %s", (current_user_id,)) + cur.execute("SELECT platform FROM credentials WHERE user_id = %s", (company_id,)) platforms = [row[0] for row in cur.fetchall()] # 5. Recent activity (last 5 cases) - cur.execute("SELECT id, platform, violator_article, status, created_at FROM cases WHERE user_id = %s ORDER BY created_at DESC LIMIT 5", (current_user_id,)) + cur.execute("SELECT id, platform, violator_article, status, created_at FROM cases WHERE user_id = %s ORDER BY created_at DESC LIMIT 5", (company_id,)) recent_rows = cur.fetchall() recent_activity = [ { @@ -56,7 +46,7 @@ def get_stats(current_user_id): trend = [] for i in range(6, -1, -1): day = (datetime.utcnow() - timedelta(days=i)).date() - cur.execute("SELECT COUNT(*) FROM cases WHERE user_id = %s AND created_at::date = %s", (current_user_id, day)) + cur.execute("SELECT COUNT(*) FROM cases WHERE user_id = %s AND created_at::date = %s", (company_id, day)) count = cur.fetchone()[0] trend.append({ "date": day.strftime('%d.%m'), @@ -64,23 +54,30 @@ def get_stats(current_user_id): }) # 5. Multi-account stores summary - cur.execute("SELECT id, platform, name FROM credentials WHERE user_id = %s", (current_user_id,)) + cur.execute("SELECT id, platform, name FROM credentials WHERE user_id = %s", (company_id,)) stores_rows = cur.fetchall() stores = [] total_sales = 0 for s in stores_rows: - # Π’ Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠΉ ΠΆΠΈΠ·Π½ΠΈ здСсь Π±Ρ‹Π» Π±Ρ‹ Π²Ρ‹Π·ΠΎΠ² API Ozon/WB для получСния ΠΏΡ€ΠΎΠ΄Π°ΠΆ Π·Π° сСгодня - # Для дСмонстрации Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅ΠΌ случайныС Π΄Π°Π½Π½Ρ‹Π΅ import random daily_sales = random.randint(5000, 50000) total_sales += daily_sales + + # ΠœΠΎΠ΄Π΅Π»ΠΈΡ€ΡƒΠ΅ΠΌ остатки (Inventory) + current_stock = random.randint(10, 500) + sales_velocity = random.randint(5, 40) # ΡˆΡ‚ Π² дСнь + days_left = current_stock // sales_velocity if sales_velocity > 0 else 999 + stores.append({ "id": s[0], "platform": s[1], "name": s[2], "status": "active", - "daily_sales": daily_sales + "daily_sales": daily_sales, + "stock": current_stock, + "velocity": sales_velocity, + "days_left": days_left }) release_db_connection(conn) @@ -98,21 +95,11 @@ def get_stats(current_user_id): @dashboard_blueprint.route('/dashboard/logs', methods=['GET']) @token_required -def get_logs(current_user_id): - """ - ΠŸΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ Π»ΠΎΠ³ Π°ΡƒΠ΄ΠΈΡ‚Π° дСйствий ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ - --- - tags: - - Dashboard - security: - - APIKeyHeader: [] - responses: - 200: - description: Бписок дСйствий ΠΈΠ· Π»ΠΎΠ³Π° - """ +def get_logs(user_info): + company_id = user_info['company_id'] conn = get_db_connection() cur = conn.cursor() - cur.execute("SELECT action, details, created_at FROM audit_log WHERE user_id = %s ORDER BY created_at DESC LIMIT 20", (current_user_id,)) + cur.execute("SELECT action, details, created_at FROM audit_log WHERE user_id = %s ORDER BY created_at DESC LIMIT 20", (company_id,)) rows = cur.fetchall() release_db_connection(conn) diff --git a/backend/routes/repricer.py b/backend/routes/repricer.py new file mode 100644 index 0000000..d318257 --- /dev/null +++ b/backend/routes/repricer.py @@ -0,0 +1,81 @@ +from flask import Blueprint, request, jsonify +from ..db import get_db_connection, release_db_connection +from ..middleware import token_required +import random +from datetime import datetime + +repricer_blueprint = Blueprint('repricer', __name__) + +@repricer_blueprint.route('/repricer/rules', methods=['GET']) +@token_required +def list_rules(user_info): + company_id = user_info['company_id'] + conn = get_db_connection() + cur = conn.cursor() + cur.execute("SELECT id, product_name, sku, min_price, max_price, strategy, is_active, current_price, last_run FROM repricer_rules WHERE user_id = %s", (company_id,)) + rows = cur.fetchall() + release_db_connection(conn) + + return jsonify([{ + "id": r[0], + "product_name": r[1], + "sku": r[2], + "min_price": float(r[3]), + "max_price": float(r[4]), + "strategy": r[5], + "is_active": r[6], + "current_price": float(r[7]) if r[7] else None, + "last_run": r[8].isoformat() if r[8] else None + } for r in rows]) + +@repricer_blueprint.route('/repricer/rules', methods=['POST']) +@token_required +def save_rule(user_info): + company_id = user_info['company_id'] + data = request.json + + conn = get_db_connection() + cur = conn.cursor() + + if data.get('id'): + cur.execute( + "UPDATE repricer_rules SET product_name=%s, sku=%s, min_price=%s, max_price=%s, strategy=%s, is_active=%s WHERE id=%s AND user_id=%s", + (data['product_name'], data['sku'], data['min_price'], data['max_price'], data['strategy'], data['is_active'], data['id'], company_id) + ) + else: + cur.execute( + "INSERT INTO repricer_rules (user_id, product_name, sku, min_price, max_price, strategy) VALUES (%s, %s, %s, %s, %s)", + (company_id, data['product_name'], data['sku'], data['min_price'], data['max_price'], data['strategy']) + ) + + conn.commit() + release_db_connection(conn) + return jsonify({"message": "Rule saved"}) + +@repricer_blueprint.route('/repricer/run', methods=['POST']) +@token_required +def run_repricer(user_info): + """ + Бимуляция Ρ€Π°Π±ΠΎΡ‚Ρ‹ Ρ€Π΅ΠΏΡ€Π°ΠΉΠ·Π΅Ρ€Π°: Π½Π°Ρ…ΠΎΠ΄ΠΈΡ‚ ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚ΠΎΠ² ΠΈ обновляСт Ρ†Π΅Π½Ρ‹ + """ + company_id = user_info['company_id'] + conn = get_db_connection() + cur = conn.cursor() + cur.execute("SELECT id, min_price, max_price, current_price FROM repricer_rules WHERE user_id = %s AND is_active = TRUE", (company_id,)) + rules = cur.fetchall() + + updates = [] + for r_id, min_p, max_p, curr_p in rules: + # Π‘ΠΈΠΌΡƒΠ»ΠΈΡ€ΡƒΠ΅ΠΌ Ρ†Π΅Π½Ρƒ ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π° + comp_price = float(min_p) + random.uniform(-50, 150) + + # Наша стратСгия: Π½Π° 1 Ρ€ΡƒΠ±Π»ΡŒ дСшСвлС ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π°, Π½ΠΎ Π½Π΅ Π½ΠΈΠΆΠ΅ min_price + new_price = max(float(min_p), comp_price - 1) + new_price = min(new_price, float(max_p)) + + cur.execute("UPDATE repricer_rules SET current_price = %s, last_run = %s WHERE id = %s", (new_price, datetime.utcnow(), r_id)) + updates.append({"id": r_id, "old": float(curr_p) if curr_p else 0, "new": round(new_price, 2), "competitor": round(comp_price, 2)}) + + conn.commit() + release_db_connection(conn) + return jsonify({"updates": updates}) diff --git a/backend/routes/team.py b/backend/routes/team.py new file mode 100644 index 0000000..94a861f --- /dev/null +++ b/backend/routes/team.py @@ -0,0 +1,56 @@ +from flask import Blueprint, request, jsonify +from ..db import get_db_connection, release_db_connection +from ..middleware import token_required +import bcrypt + +team_blueprint = Blueprint('team', __name__) + +@team_blueprint.route('/team/members', methods=['GET']) +@token_required +def list_team_members(user_info): + if user_info['role'] != 'admin': + return jsonify({"error": "Admin access required"}), 403 + + conn = get_db_connection() + cur = conn.cursor() + cur.execute("SELECT id, username, role, created_at FROM users WHERE parent_id = %s", (user_info['user_id'],)) + rows = cur.fetchall() + release_db_connection(conn) + + return jsonify([{ + "id": r[0], + "username": r[1], + "role": r[2], + "created_at": r[3].isoformat() + } for r in rows]) + +@team_blueprint.route('/team/members', methods=['POST']) +@token_required +def add_team_member(user_info): + if user_info['role'] != 'admin': + return jsonify({"error": "Admin access required"}), 403 + + data = request.json + username = data.get('username') + password = data.get('password') + role = data.get('role', 'manager') # manager, viewer + + if not username or not password: + return jsonify({"error": "Username and password required"}), 400 + + hashed_password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') + + conn = get_db_connection() + cur = conn.cursor() + try: + cur.execute( + "INSERT INTO users (username, password_hash, role, parent_id) VALUES (%s, %s, %s, %s) RETURNING id", + (username, hashed_password, role, user_info['user_id']) + ) + new_id = cur.fetchone()[0] + conn.commit() + return jsonify({"id": new_id, "message": "Team member added"}), 201 + except Exception as e: + return jsonify({"error": "User already exists"}), 400 + finally: + release_db_connection(conn) diff --git a/backend/utils/ai_utils.py b/backend/utils/ai_utils.py index df16cf2..cec7e98 100644 --- a/backend/utils/ai_utils.py +++ b/backend/utils/ai_utils.py @@ -95,3 +95,38 @@ def generate_product_card(title): return json.loads(clean_json) except Exception as e: return {"error": f"Ошибка ΠΏΡ€ΠΈ Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ ΠΊΠ°Ρ€Ρ‚ΠΎΡ‡ΠΊΠΈ: {str(e)}"} + +def analyze_competitor(my_desc, comp_desc): + """Π‘Ρ€Π°Π²Π½ΠΈΠ²Π°Π΅Ρ‚ Π΄Π²Π΅ ΠΊΠ°Ρ€Ρ‚ΠΎΡ‡ΠΊΠΈ Ρ‚ΠΎΠ²Π°Ρ€Π° ΠΈ Π²Ρ‹Π΄Π°Π΅Ρ‚ Ρ€Π΅ΠΊΠΎΠΌΠ΅Π½Π΄Π°Ρ†ΠΈΠΈ.""" + api_key = os.getenv('GEMINI_API_KEY') + if not api_key: + return {"error": "API ΠΊΠ»ΡŽΡ‡ Π½Π΅ настроСн."} + + genai.configure(api_key=api_key) + model = genai.GenerativeModel('gemini-1.5-flash') + + prompt = f""" + Π’Ρ‹ - ΡΡ‚Π°Ρ€ΡˆΠΈΠΉ Π°Π½Π°Π»ΠΈΡ‚ΠΈΠΊ ΠΏΠΎ маркСтплСйсам. Ввоя Ρ†Π΅Π»ΡŒ: Π½Π°ΠΉΡ‚ΠΈ способы "ΠΏΠΎΠ±Π΅Π΄ΠΈΡ‚ΡŒ" ΠΊΠ°Ρ€Ρ‚ΠΎΡ‡ΠΊΡƒ ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π°. + + ΠžΠŸΠ˜Π‘ΠΠΠ˜Π• ΠœΠžΠ•Π“Πž Π’ΠžΠ’ΠΠ Π: + "{my_desc}" + + ΠžΠŸΠ˜Π‘ΠΠΠ˜Π• Π’ΠžΠ’ΠΠ Π ΠšΠžΠΠšΠ£Π Π•ΠΠ’Π: + "{comp_desc}" + + Ввоя Π·Π°Π΄Π°Ρ‡Π° - провСсти Π°Π½Π°Π»ΠΈΠ· ΠΈ Π²Ρ‹Π΄Π°Ρ‚ΡŒ ΠΎΡ‚Π²Π΅Ρ‚ Π² Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Π΅ JSON: + 1. "competitor_weakness": Бписок ΠΈΠ· 3-4 слабых мСст ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π° (Ρ‡Ρ‚ΠΎ ΠΎΠ½ Π½Π΅ упомянул, Π² Ρ‡Π΅ΠΌ ΠΎΠ½ Ρ…ΡƒΠΆΠ΅). + 2. "my_advantages": Π’ Ρ‡Π΅ΠΌ ΠΌΠΎΠΉ Ρ‚ΠΎΠ²Π°Ρ€ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΈΠ²Π½ΠΎ Π»ΡƒΡ‡ΡˆΠ΅ ΠΈΠ»ΠΈ ΠΊΠ°ΠΊ это прСподнСсти. + 3. "strategy": ΠšΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹Π΅ шаги, Ρ‡Ρ‚ΠΎ ΠΈΠ·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Π² ΠΌΠΎΠ΅ΠΌ описании ΠΈΠ»ΠΈ Ρ„ΠΎΡ‚ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π·Π°Π±Ρ€Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ΄Π°ΠΆΠΈ Ρƒ Π½Π΅Π³ΠΎ. + 4. "win_phrase": Один ΠΌΠΎΡ‰Π½Ρ‹ΠΉ Ρ€Π΅ΠΊΠ»Π°ΠΌΠ½Ρ‹ΠΉ слоган, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ "Π±ΡŒΠ΅Ρ‚" Π² слабоС мСсто ΠΊΠΎΠ½ΠΊΡƒΡ€Π΅Π½Ρ‚Π°. + + ΠžΡ‚Π²Π΅Ρ‚ Π΄Π°ΠΉ Π’ΠžΠ›Π¬ΠšΠž Π² Π²ΠΈΠ΄Π΅ чистого JSON. + """ + + try: + response = model.generate_content(prompt) + import json + clean_json = response.text.replace('```json', '').replace('```', '').strip() + return json.loads(clean_json) + except Exception as e: + return {"error": f"Ошибка Π°Π½Π°Π»ΠΈΠ·Π°: {str(e)}"} diff --git a/db/init.sql b/db/init.sql index 0e58239..fc16180 100644 --- a/db/init.sql +++ b/db/init.sql @@ -5,10 +5,26 @@ CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL, password_hash TEXT NOT NULL, + role VARCHAR(20) DEFAULT 'admin', -- admin, manager, viewer + parent_id INT REFERENCES users(id) ON DELETE CASCADE, plan VARCHAR(20) DEFAULT 'free', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); +CREATE TABLE IF NOT EXISTS repricer_rules ( + id SERIAL PRIMARY KEY, + user_id INT REFERENCES users(id) ON DELETE CASCADE, + product_name VARCHAR(255) NOT NULL, + sku VARCHAR(100) NOT NULL, + min_price DECIMAL(10, 2) NOT NULL, + max_price DECIMAL(10, 2) NOT NULL, + strategy VARCHAR(20) DEFAULT 'follow_competitor', -- follow_competitor, match_lowest + is_active BOOLEAN DEFAULT TRUE, + current_price DECIMAL(10, 2), + last_run TIMESTAMP, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + CREATE TABLE IF NOT EXISTS credentials ( user_id INT REFERENCES users(id) ON DELETE CASCADE, platform VARCHAR(50), diff --git a/frontend/index.html b/frontend/index.html index da52a78..a147902 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -51,6 +51,8 @@ + +
@@ -85,6 +87,8 @@ Магазин ΠŸΠ»ΠΎΡ‰Π°Π΄ΠΊΠ° Бтатус API + ΠžΡΡ‚Π°Ρ‚ΠΊΠΈ (ΡˆΡ‚) + Π”Π½Π΅ΠΉ Π΄ΠΎ OOS ΠŸΡ€ΠΎΠ΄Π°ΠΆΠΈ (сСгодня) @@ -355,6 +359,94 @@ + + + + + +