From 568f2615b9f1c739fe15a61ebaff7cd3fe029177 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 28 May 2025 16:23:11 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B2=D0=B0=D1=88=D0=B5=20=D1=81=D0=BE=D0=BE?= =?UTF-8?q?=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=BC?= =?UTF-8?q?=D0=B8=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app.js | 4 +- backend/db.js | 8 +- .../026_add_preferred_language_to_users.sql | 2 + backend/routes/messages.js | 23 ++ backend/routes/users.js | 50 +++ frontend/src/components/ContactDetails.vue | 320 ++++++++++++++++++ frontend/src/components/ContactTable.vue | 23 +- frontend/src/services/contactsService.js | 8 + frontend/src/services/messagesService.js | 9 + frontend/src/views/CrmView.vue | 16 +- 10 files changed, 453 insertions(+), 10 deletions(-) create mode 100644 backend/db/migrations/026_add_preferred_language_to_users.sql create mode 100644 backend/routes/messages.js create mode 100644 frontend/src/components/ContactDetails.vue create mode 100644 frontend/src/services/messagesService.js diff --git a/backend/app.js b/backend/app.js index ebae5af..a3b2c24 100644 --- a/backend/app.js +++ b/backend/app.js @@ -11,6 +11,7 @@ const db = require('./db'); // Добавляем импорт db const aiAssistant = require('./services/ai-assistant'); // Добавляем импорт aiAssistant const fs = require('fs'); const path = require('path'); +const messagesRoutes = require('./routes/messages'); // Проверка и создание директорий для хранения данных контрактов const ensureDirectoriesExist = () => { @@ -77,7 +78,7 @@ app.use( 'http://127.0.0.1:5173', // Добавляем альтернативный origin ], credentials: true, - methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization', 'Cookie'], }) ); @@ -163,6 +164,7 @@ app.use('/api/isic', isicRoutes); // Добавленное использова app.use('/api/geocoding', geocodingRoutes); // Добавленное использование роута app.use('/api/dle', dleRoutes); // Добавляем маршрут DLE app.use('/api/settings', settingsRoutes); // Добавляем маршрут настроек +app.use('/api/messages', messagesRoutes); const nonceStore = new Map(); // или любая другая реализация хранилища nonce diff --git a/backend/db.js b/backend/db.js index 5f306c1..5785645 100644 --- a/backend/db.js +++ b/backend/db.js @@ -97,10 +97,4 @@ async function saveGuestMessageToDatabase(message, language, guestId) { } // Экспортируем функции для работы с базой данных -module.exports = { - getPool, - getQuery, - reinitPoolFromDbSettings, - saveGuestMessageToDatabase, - setPoolChangeCallback, -}; +module.exports = { getQuery, pool, getPool, setPoolChangeCallback }; diff --git a/backend/db/migrations/026_add_preferred_language_to_users.sql b/backend/db/migrations/026_add_preferred_language_to_users.sql new file mode 100644 index 0000000..37c104b --- /dev/null +++ b/backend/db/migrations/026_add_preferred_language_to_users.sql @@ -0,0 +1,2 @@ +-- Добавление поля preferred_language для хранения языков пользователя (множественный выбор) +ALTER TABLE users ADD COLUMN preferred_language jsonb; \ No newline at end of file diff --git a/backend/routes/messages.js b/backend/routes/messages.js new file mode 100644 index 0000000..ccc6c5b --- /dev/null +++ b/backend/routes/messages.js @@ -0,0 +1,23 @@ +const express = require('express'); +const router = express.Router(); +const db = require('../db'); + +// GET /api/messages?userId=123 +router.get('/', async (req, res) => { + const userId = req.query.userId; + if (!userId) return res.status(400).json({ error: 'userId required' }); + try { + const result = await db.getQuery()( + `SELECT id, user_id, sender_type, content, channel, role, direction, created_at, attachment_filename, attachment_mimetype, attachment_size, attachment_data, metadata + FROM messages + WHERE user_id = $1 + ORDER BY created_at ASC`, + [userId] + ); + res.json(result.rows); + } catch (e) { + res.status(500).json({ error: 'DB error', details: e.message }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/backend/routes/users.js b/backend/routes/users.js index 2139a04..3f1afd3 100644 --- a/backend/routes/users.js +++ b/backend/routes/users.js @@ -138,4 +138,54 @@ router.get('/', async (req, res) => { }); */ +// PATCH /api/users/:id — обновить имя и язык +router.patch('/:id', async (req, res) => { + const userId = req.params.id; + const { name, language } = req.body; + if (!name && !language) return res.status(400).json({ error: 'Nothing to update' }); + try { + const fields = []; + const values = []; + let idx = 1; + if (name !== undefined) { + // Разделяем имя на first_name и last_name (по пробелу) + const [firstName, ...lastNameArr] = name.split(' '); + fields.push(`first_name = $${idx++}`); + values.push(firstName); + fields.push(`last_name = $${idx++}`); + values.push(lastNameArr.join(' ') || null); + } + if (language !== undefined) { + fields.push(`preferred_language = $${idx++}`); + values.push(Array.isArray(language) ? JSON.stringify(language) : language); + } + values.push(userId); + const sql = `UPDATE users SET ${fields.join(', ')} WHERE id = $${idx} RETURNING *`; + const result = await db.getQuery()(sql, values); + res.json(result.rows[0]); + } catch (e) { + res.status(500).json({ error: 'DB error', details: e.message }); + } +}); + +// DELETE /api/users/:id — удалить контакт и все связанные данные +router.delete('/:id', async (req, res) => { + const userId = req.params.id; + const client = await db.getPool().connect(); + try { + await client.query('BEGIN'); + await client.query('DELETE FROM user_identities WHERE user_id = $1', [userId]); + await client.query('DELETE FROM messages WHERE user_id = $1', [userId]); + // Добавьте другие связанные таблицы, если нужно + await client.query('DELETE FROM users WHERE id = $1', [userId]); + await client.query('COMMIT'); + res.json({ success: true }); + } catch (e) { + await client.query('ROLLBACK'); + res.status(500).json({ error: 'DB error', details: e.message }); + } finally { + client.release(); + } +}); + module.exports = router; diff --git a/frontend/src/components/ContactDetails.vue b/frontend/src/components/ContactDetails.vue new file mode 100644 index 0000000..cbffd3d --- /dev/null +++ b/frontend/src/components/ContactDetails.vue @@ -0,0 +1,320 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/ContactTable.vue b/frontend/src/components/ContactTable.vue index 7134d57..b9bf29c 100644 --- a/frontend/src/components/ContactTable.vue +++ b/frontend/src/components/ContactTable.vue @@ -12,6 +12,7 @@ Telegram Кошелек Дата создания + Действие @@ -21,6 +22,9 @@ {{ contact.telegram || '-' }} {{ contact.wallet || '-' }} {{ formatDate(contact.created_at) }} + + + @@ -28,14 +32,18 @@ \ No newline at end of file diff --git a/frontend/src/services/contactsService.js b/frontend/src/services/contactsService.js index e9628c8..06cd67a 100644 --- a/frontend/src/services/contactsService.js +++ b/frontend/src/services/contactsService.js @@ -7,5 +7,13 @@ export default { return res.data.contacts; } return []; + }, + async updateContact(id, data) { + const res = await api.patch(`/api/users/${id}`, data); + return res.data; + }, + async deleteContact(id) { + const res = await api.delete(`/api/users/${id}`); + return res.data; } }; \ No newline at end of file diff --git a/frontend/src/services/messagesService.js b/frontend/src/services/messagesService.js new file mode 100644 index 0000000..5117e88 --- /dev/null +++ b/frontend/src/services/messagesService.js @@ -0,0 +1,9 @@ +import axios from 'axios'; + +export default { + async getMessagesByUserId(userId) { + if (!userId) return []; + const { data } = await axios.get(`/api/messages?userId=${userId}`); + return data; + } +}; \ No newline at end of file diff --git a/frontend/src/views/CrmView.vue b/frontend/src/views/CrmView.vue index f235ea6..1e77589 100644 --- a/frontend/src/views/CrmView.vue +++ b/frontend/src/views/CrmView.vue @@ -20,7 +20,8 @@ Подробнее - + + @@ -36,6 +37,7 @@ import dleService from '../services/dleService'; import ContactTable from '../components/ContactTable.vue'; import contactsService from '../services/contactsService.js'; import DleManagement from '../components/DleManagement.vue'; +import ContactDetails from '../components/ContactDetails.vue'; // Определяем props const props = defineProps({ @@ -58,6 +60,8 @@ const showDleManagement = ref(false); const showContacts = ref(false); const contacts = ref([]); const isLoadingContacts = ref(false); +const selectedContact = ref(null); +const showContactDetails = ref(false); // Функция для перехода на домашнюю страницу и открытия боковой панели const goToHomeAndShowSidebar = () => { @@ -135,6 +139,16 @@ async function loadContacts() { watch(showContacts, (val) => { if (val) loadContacts(); }); + +function openContactDetails(contact) { + selectedContact.value = contact; + showContactDetails.value = true; +} + +function onContactDeleted() { + showContactDetails.value = false; + loadContacts(); +}