update
This commit is contained in:
@@ -2,7 +2,9 @@ const express = require('express');
|
||||
const cors = require('cors');
|
||||
const dotenv = require('dotenv');
|
||||
const fs = require('fs');
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
// Импортировать mongoose из общего модуля (подключение происходит в server/utils/mongoose.ts)
|
||||
const mongoose = require('../../utils/mongoose');
|
||||
|
||||
// Загрузить переменные окружения
|
||||
dotenv.config();
|
||||
@@ -27,6 +29,7 @@ const reviewsRoutes = require('./routes/reviews');
|
||||
const buyProductsRoutes = require('./routes/buyProducts');
|
||||
const requestsRoutes = require('./routes/requests');
|
||||
const homeRoutes = require('./routes/home');
|
||||
const activityRoutes = require('./routes/activity');
|
||||
|
||||
const app = express();
|
||||
|
||||
@@ -89,6 +92,7 @@ app.use('/products', productsRoutes);
|
||||
app.use('/reviews', reviewsRoutes);
|
||||
app.use('/requests', requestsRoutes);
|
||||
app.use('/home', homeRoutes);
|
||||
app.use('/activities', activityRoutes);
|
||||
|
||||
// Обработка ошибок
|
||||
app.use((err, req, res, next) => {
|
||||
|
||||
61
server/routers/procurement/models/Activity.js
Normal file
61
server/routers/procurement/models/Activity.js
Normal file
@@ -0,0 +1,61 @@
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
const activitySchema = new mongoose.Schema({
|
||||
companyId: {
|
||||
type: String,
|
||||
required: true,
|
||||
index: true
|
||||
},
|
||||
userId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: [
|
||||
'message_received',
|
||||
'message_sent',
|
||||
'request_received',
|
||||
'request_sent',
|
||||
'request_response',
|
||||
'product_accepted',
|
||||
'review_received',
|
||||
'profile_updated',
|
||||
'product_added',
|
||||
'buy_product_added'
|
||||
],
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
description: {
|
||||
type: String
|
||||
},
|
||||
relatedCompanyId: {
|
||||
type: String
|
||||
},
|
||||
relatedCompanyName: {
|
||||
type: String
|
||||
},
|
||||
metadata: {
|
||||
type: mongoose.Schema.Types.Mixed
|
||||
},
|
||||
read: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
createdAt: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
index: true
|
||||
}
|
||||
});
|
||||
|
||||
// Индексы для оптимизации
|
||||
activitySchema.index({ companyId: 1, createdAt: -1 });
|
||||
activitySchema.index({ companyId: 1, read: 1, createdAt: -1 });
|
||||
|
||||
module.exports = mongoose.model('Activity', activitySchema);
|
||||
|
||||
101
server/routers/procurement/routes/activity.js
Normal file
101
server/routers/procurement/routes/activity.js
Normal file
@@ -0,0 +1,101 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { verifyToken } = require('../middleware/auth');
|
||||
const Activity = require('../models/Activity');
|
||||
const User = require('../models/User');
|
||||
|
||||
// Получить последние активности компании
|
||||
router.get('/', verifyToken, async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const user = await User.findById(userId);
|
||||
|
||||
if (!user || !user.companyId) {
|
||||
return res.json({ activities: [] });
|
||||
}
|
||||
|
||||
const companyId = user.companyId.toString();
|
||||
const limit = parseInt(req.query.limit) || 10;
|
||||
|
||||
const activities = await Activity.find({ companyId })
|
||||
.sort({ createdAt: -1 })
|
||||
.limit(limit)
|
||||
.lean();
|
||||
|
||||
res.json({ activities });
|
||||
} catch (error) {
|
||||
console.error('Error getting activities:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Отметить активность как прочитанную
|
||||
router.patch('/:id/read', verifyToken, async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const user = await User.findById(userId);
|
||||
|
||||
if (!user || !user.companyId) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
const companyId = user.companyId.toString();
|
||||
const activityId = req.params.id;
|
||||
|
||||
const activity = await Activity.findOne({
|
||||
_id: activityId,
|
||||
companyId
|
||||
});
|
||||
|
||||
if (!activity) {
|
||||
return res.status(404).json({ error: 'Activity not found' });
|
||||
}
|
||||
|
||||
activity.read = true;
|
||||
await activity.save();
|
||||
|
||||
res.json({ success: true, activity });
|
||||
} catch (error) {
|
||||
console.error('Error updating activity:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Отметить все активности как прочитанные
|
||||
router.post('/mark-all-read', verifyToken, async (req, res) => {
|
||||
try {
|
||||
const userId = req.userId;
|
||||
const user = await User.findById(userId);
|
||||
|
||||
if (!user || !user.companyId) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
const companyId = user.companyId.toString();
|
||||
|
||||
await Activity.updateMany(
|
||||
{ companyId, read: false },
|
||||
{ $set: { read: true } }
|
||||
);
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Error marking all as read:', error);
|
||||
res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Создать активность (вспомогательная функция)
|
||||
router.createActivity = async (data) => {
|
||||
try {
|
||||
const activity = new Activity(data);
|
||||
await activity.save();
|
||||
return activity;
|
||||
} catch (error) {
|
||||
console.error('Error creating activity:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = router;
|
||||
|
||||
122
server/routers/procurement/scripts/seed-activities.js
Normal file
122
server/routers/procurement/scripts/seed-activities.js
Normal file
@@ -0,0 +1,122 @@
|
||||
const mongoose = require('mongoose');
|
||||
require('dotenv').config();
|
||||
|
||||
// Подключение моделей - прямые пути без path.join и __dirname
|
||||
const Activity = require('../models/Activity');
|
||||
const User = require('../models/User');
|
||||
const Company = require('../models/Company');
|
||||
|
||||
const MONGODB_URI = process.env.MONGODB_URI || 'mongodb://localhost:27017/procurement-platform';
|
||||
|
||||
const activityTemplates = [
|
||||
{
|
||||
type: 'request_received',
|
||||
title: 'Получен новый запрос',
|
||||
description: 'Компания отправила вам запрос на поставку товаров',
|
||||
},
|
||||
{
|
||||
type: 'request_sent',
|
||||
title: 'Запрос отправлен',
|
||||
description: 'Ваш запрос был отправлен компании',
|
||||
},
|
||||
{
|
||||
type: 'request_response',
|
||||
title: 'Получен ответ на запрос',
|
||||
description: 'Компания ответила на ваш запрос',
|
||||
},
|
||||
{
|
||||
type: 'product_accepted',
|
||||
title: 'Товар акцептован',
|
||||
description: 'Ваш товар был акцептован компанией',
|
||||
},
|
||||
{
|
||||
type: 'message_received',
|
||||
title: 'Новое сообщение',
|
||||
description: 'Вы получили новое сообщение от компании',
|
||||
},
|
||||
{
|
||||
type: 'review_received',
|
||||
title: 'Новый отзыв',
|
||||
description: 'Компания оставила отзыв о сотрудничестве',
|
||||
},
|
||||
{
|
||||
type: 'profile_updated',
|
||||
title: 'Профиль обновлен',
|
||||
description: 'Информация о вашей компании была обновлена',
|
||||
},
|
||||
{
|
||||
type: 'buy_product_added',
|
||||
title: 'Добавлен товар для закупки',
|
||||
description: 'В раздел "Я покупаю" добавлен новый товар',
|
||||
},
|
||||
];
|
||||
|
||||
async function seedActivities() {
|
||||
try {
|
||||
console.log('🌱 Connecting to MongoDB...');
|
||||
await mongoose.connect(MONGODB_URI);
|
||||
console.log('✅ Connected to MongoDB');
|
||||
|
||||
// Найти тестового пользователя
|
||||
const testUser = await User.findOne({ email: 'admin@test-company.ru' });
|
||||
if (!testUser) {
|
||||
console.log('❌ Test user not found. Please run recreate-test-user.js first.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const company = await Company.findById(testUser.companyId);
|
||||
if (!company) {
|
||||
console.log('❌ Company not found');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Найти другие компании для связанных активностей
|
||||
const otherCompanies = await Company.find({
|
||||
_id: { $ne: company._id }
|
||||
}).limit(3);
|
||||
|
||||
console.log('🗑️ Clearing existing activities...');
|
||||
await Activity.deleteMany({ companyId: company._id.toString() });
|
||||
|
||||
console.log('➕ Creating activities...');
|
||||
const activities = [];
|
||||
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const template = activityTemplates[i % activityTemplates.length];
|
||||
const relatedCompany = otherCompanies[i % otherCompanies.length];
|
||||
|
||||
const activity = {
|
||||
companyId: company._id.toString(),
|
||||
userId: testUser._id.toString(),
|
||||
type: template.type,
|
||||
title: template.title,
|
||||
description: template.description,
|
||||
relatedCompanyId: relatedCompany?._id.toString(),
|
||||
relatedCompanyName: relatedCompany?.shortName || relatedCompany?.fullName,
|
||||
read: i >= 5, // Первые 5 непрочитанные
|
||||
createdAt: new Date(Date.now() - i * 3600000), // Каждый час назад
|
||||
};
|
||||
|
||||
activities.push(activity);
|
||||
}
|
||||
|
||||
await Activity.insertMany(activities);
|
||||
|
||||
console.log(`✅ Created ${activities.length} activities`);
|
||||
console.log('✨ Activities seeded successfully!');
|
||||
|
||||
await mongoose.connection.close();
|
||||
console.log('👋 Database connection closed');
|
||||
} catch (error) {
|
||||
console.error('❌ Error seeding activities:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Запуск
|
||||
if (require.main === module) {
|
||||
seedActivities();
|
||||
}
|
||||
|
||||
module.exports = { seedActivities };
|
||||
|
||||
114
server/routers/procurement/scripts/seed-requests.js
Normal file
114
server/routers/procurement/scripts/seed-requests.js
Normal file
@@ -0,0 +1,114 @@
|
||||
const mongoose = require('mongoose');
|
||||
const Request = require('../models/Request');
|
||||
const Company = require('../models/Company');
|
||||
const User = require('../models/User');
|
||||
|
||||
const mongoUri = process.env.MONGODB_URI || 'mongodb://admin:password@localhost:27017/procurement_db?authSource=admin';
|
||||
|
||||
async function seedRequests() {
|
||||
try {
|
||||
await mongoose.connect(mongoUri);
|
||||
console.log('✅ Connected to MongoDB');
|
||||
|
||||
// Получаем все компании
|
||||
const companies = await Company.find().limit(10).exec();
|
||||
if (companies.length < 2) {
|
||||
console.error('❌ Need at least 2 companies in database');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Получаем тестового пользователя
|
||||
const testUser = await User.findOne({ email: 'admin@test-company.ru' }).exec();
|
||||
if (!testUser) {
|
||||
console.error('❌ Test user not found');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const testCompanyId = testUser.companyId.toString();
|
||||
console.log('📋 Test company ID:', testCompanyId);
|
||||
console.log('📋 Found', companies.length, 'companies');
|
||||
|
||||
// Удаляем старые запросы
|
||||
await Request.deleteMany({});
|
||||
console.log('🗑️ Cleared old requests');
|
||||
|
||||
const requests = [];
|
||||
const now = new Date();
|
||||
|
||||
// Создаем отправленные запросы (от тестовой компании)
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const recipientCompany = companies[i % companies.length];
|
||||
if (recipientCompany._id.toString() === testCompanyId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const createdAt = new Date(now.getTime() - i * 24 * 60 * 60 * 1000); // За последние 5 дней
|
||||
|
||||
requests.push({
|
||||
senderCompanyId: testCompanyId,
|
||||
recipientCompanyId: recipientCompany._id.toString(),
|
||||
subject: `Запрос на поставку ${i + 1}`,
|
||||
text: `Здравствуйте! Интересует поставка товаров/услуг. Запрос ${i + 1}. Прошу предоставить коммерческое предложение.`,
|
||||
files: [],
|
||||
responseFiles: [],
|
||||
status: i % 3 === 0 ? 'accepted' : i % 3 === 1 ? 'rejected' : 'pending',
|
||||
response: i % 3 === 0
|
||||
? 'Благодарим за запрос! Готовы предоставить услуги. Отправили КП на почту.'
|
||||
: i % 3 === 1
|
||||
? 'К сожалению, в данный момент не можем предоставить эти услуги.'
|
||||
: null,
|
||||
respondedAt: i % 3 !== 2 ? new Date(createdAt.getTime() + 2 * 60 * 60 * 1000) : null,
|
||||
createdAt,
|
||||
updatedAt: i % 3 !== 2 ? new Date(createdAt.getTime() + 2 * 60 * 60 * 1000) : createdAt,
|
||||
});
|
||||
}
|
||||
|
||||
// Создаем полученные запросы (к тестовой компании)
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const senderCompany = companies[(i + 2) % companies.length];
|
||||
if (senderCompany._id.toString() === testCompanyId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const createdAt = new Date(now.getTime() - (i + 1) * 12 * 60 * 60 * 1000); // За последние 1.5 дня
|
||||
|
||||
requests.push({
|
||||
senderCompanyId: senderCompany._id.toString(),
|
||||
recipientCompanyId: testCompanyId,
|
||||
subject: `Предложение о сотрудничестве ${i + 1}`,
|
||||
text: `Добрый день! Предлагаем сотрудничество. Запрос ${i + 1}. Заинтересованы в вашей продукции.`,
|
||||
files: [],
|
||||
responseFiles: [],
|
||||
status: 'pending',
|
||||
response: null,
|
||||
respondedAt: null,
|
||||
createdAt,
|
||||
updatedAt: createdAt,
|
||||
});
|
||||
}
|
||||
|
||||
// Сохраняем все запросы
|
||||
const savedRequests = await Request.insertMany(requests);
|
||||
console.log('✅ Created', savedRequests.length, 'test requests');
|
||||
|
||||
// Статистика
|
||||
const sentCount = await Request.countDocuments({ senderCompanyId: testCompanyId });
|
||||
const receivedCount = await Request.countDocuments({ recipientCompanyId: testCompanyId });
|
||||
const withResponses = await Request.countDocuments({ senderCompanyId: testCompanyId, response: { $ne: null } });
|
||||
|
||||
console.log('📊 Statistics:');
|
||||
console.log(' - Sent requests:', sentCount);
|
||||
console.log(' - Received requests:', receivedCount);
|
||||
console.log(' - With responses:', withResponses);
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error:', error);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await mongoose.connection.close();
|
||||
console.log('👋 Disconnected from MongoDB');
|
||||
}
|
||||
}
|
||||
|
||||
seedRequests();
|
||||
|
||||
@@ -9,3 +9,8 @@ mongoose.connect(mongoUrl).then(() => {
|
||||
console.error(err)
|
||||
})
|
||||
|
||||
export default mongoose
|
||||
|
||||
// Для совместимости с CommonJS
|
||||
module.exports = mongoose
|
||||
module.exports.default = mongoose
|
||||
|
||||
Reference in New Issue
Block a user