обновление логики

This commit is contained in:
2025-11-21 16:53:13 +03:00
parent fa860921da
commit 1d4521b803
10 changed files with 111 additions and 51 deletions

View File

@@ -1,4 +1,4 @@
const mongoose = require('../../../utils/mongoose'); const mongoose = require('mongoose');
const criterionItemSchema = new mongoose.Schema({ const criterionItemSchema = new mongoose.Schema({
name: { name: {
@@ -14,6 +14,11 @@ const criterionItemSchema = new mongoose.Schema({
}, { _id: false }); }, { _id: false });
const criteriaSchema = new mongoose.Schema({ const criteriaSchema = new mongoose.Schema({
eventId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Event',
required: true
},
blockName: { blockName: {
type: String, type: String,
required: true required: true

View File

@@ -1,4 +1,4 @@
const mongoose = require('../../../utils/mongoose'); const mongoose = require('mongoose');
const eventSchema = new mongoose.Schema({ const eventSchema = new mongoose.Schema({
name: { name: {

View File

@@ -1,7 +1,12 @@
const mongoose = require('../../../utils/mongoose'); const mongoose = require('mongoose');
const crypto = require('crypto'); const crypto = require('crypto');
const expertSchema = new mongoose.Schema({ const expertSchema = new mongoose.Schema({
eventId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Event',
required: true
},
fullName: { fullName: {
type: String, type: String,
required: true required: true

View File

@@ -1,4 +1,4 @@
const mongoose = require('../../../utils/mongoose'); const mongoose = require('mongoose');
const ratingItemSchema = new mongoose.Schema({ const ratingItemSchema = new mongoose.Schema({
criteriaId: { criteriaId: {
@@ -19,6 +19,11 @@ const ratingItemSchema = new mongoose.Schema({
}, { _id: false }); }, { _id: false });
const ratingSchema = new mongoose.Schema({ const ratingSchema = new mongoose.Schema({
eventId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Event',
required: true
},
expertId: { expertId: {
type: mongoose.Schema.Types.ObjectId, type: mongoose.Schema.Types.ObjectId,
ref: 'Expert', ref: 'Expert',

View File

@@ -1,6 +1,11 @@
const mongoose = require('../../../utils/mongoose'); const mongoose = require('mongoose');
const teamSchema = new mongoose.Schema({ const teamSchema = new mongoose.Schema({
eventId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Event',
required: true
},
type: { type: {
type: String, type: String,
enum: ['team', 'participant'], enum: ['team', 'participant'],

View File

@@ -23,7 +23,10 @@ const DEFAULT_CRITERIA = [
// GET /api/criteria - получить все блоки критериев // GET /api/criteria - получить все блоки критериев
router.get('/', async (req, res) => { router.get('/', async (req, res) => {
try { try {
const criteria = await Criteria.find().sort({ order: 1 }); const { eventId } = req.query;
const filter = {};
if (eventId) filter.eventId = eventId;
const criteria = await Criteria.find(filter).sort({ order: 1 });
res.json(criteria); res.json(criteria);
} catch (error) { } catch (error) {
res.status(500).json({ error: error.message }); res.status(500).json({ error: error.message });
@@ -46,13 +49,14 @@ router.get('/:id', async (req, res) => {
// POST /api/criteria - создать блок критериев // POST /api/criteria - создать блок критериев
router.post('/', async (req, res) => { router.post('/', async (req, res) => {
try { try {
const { blockName, criteria, order } = req.body; const { eventId, blockName, criteria, order } = req.body;
if (!blockName || !criteria || !Array.isArray(criteria)) { if (!eventId || !blockName || !criteria || !Array.isArray(criteria)) {
return res.status(400).json({ error: 'Block name and criteria array are required' }); return res.status(400).json({ error: 'EventId, block name and criteria array are required' });
} }
const criteriaBlock = await Criteria.create({ const criteriaBlock = await Criteria.create({
eventId,
blockName, blockName,
criteria, criteria,
order: order !== undefined ? order : 0 order: order !== undefined ? order : 0
@@ -67,11 +71,21 @@ router.post('/', async (req, res) => {
// POST /api/criteria/default - загрузить критерии по умолчанию из hack.md // POST /api/criteria/default - загрузить критерии по умолчанию из hack.md
router.post('/default', async (req, res) => { router.post('/default', async (req, res) => {
try { try {
// Удаляем все существующие критерии const { eventId } = req.body;
await Criteria.deleteMany({});
// Создаем критерии по умолчанию if (!eventId) {
const createdCriteria = await Criteria.insertMany(DEFAULT_CRITERIA); return res.status(400).json({ error: 'EventId is required' });
}
// Удаляем все существующие критерии для этого мероприятия
await Criteria.deleteMany({ eventId });
// Создаем критерии по умолчанию с eventId
const criteriaWithEventId = DEFAULT_CRITERIA.map(c => ({
...c,
eventId
}));
const createdCriteria = await Criteria.insertMany(criteriaWithEventId);
res.status(201).json(createdCriteria); res.status(201).json(createdCriteria);
} catch (error) { } catch (error) {

View File

@@ -4,7 +4,10 @@ const { Expert } = require('../models');
// GET /api/experts - список экспертов // GET /api/experts - список экспертов
router.get('/', async (req, res) => { router.get('/', async (req, res) => {
try { try {
const experts = await Expert.find().sort({ createdAt: -1 }); const { eventId } = req.query;
const filter = {};
if (eventId) filter.eventId = eventId;
const experts = await Expert.find(filter).sort({ createdAt: -1 });
res.json(experts); res.json(experts);
} catch (error) { } catch (error) {
res.status(500).json({ error: error.message }); res.status(500).json({ error: error.message });
@@ -40,14 +43,15 @@ router.get('/:id', async (req, res) => {
// POST /api/experts - создать эксперта (с генерацией уникальной ссылки) // POST /api/experts - создать эксперта (с генерацией уникальной ссылки)
router.post('/', async (req, res) => { router.post('/', async (req, res) => {
try { try {
const { fullName } = req.body; const { eventId, fullName } = req.body;
if (!fullName) { if (!eventId || !fullName) {
return res.status(400).json({ error: 'Full name is required' }); return res.status(400).json({ error: 'EventId and full name are required' });
} }
// Создаем нового эксперта // Создаем нового эксперта
const expert = new Expert({ const expert = new Expert({
eventId,
fullName fullName
}); });

View File

@@ -4,11 +4,12 @@ const { Rating, Team, Expert, Criteria } = require('../models');
// GET /api/ratings - получить все оценки (с фильтрами) // GET /api/ratings - получить все оценки (с фильтрами)
router.get('/', async (req, res) => { router.get('/', async (req, res) => {
try { try {
const { expertId, teamId } = req.query; const { expertId, teamId, eventId } = req.query;
const filter = {}; const filter = {};
if (expertId) filter.expertId = expertId; if (expertId) filter.expertId = expertId;
if (teamId) filter.teamId = teamId; if (teamId) filter.teamId = teamId;
if (eventId) filter.eventId = eventId;
const ratings = await Rating.find(filter) const ratings = await Rating.find(filter)
.populate('expertId', 'fullName') .populate('expertId', 'fullName')
@@ -49,14 +50,18 @@ router.get('/expert/:expertId', async (req, res) => {
// GET /api/ratings/statistics - статистика с группировкой по командам // GET /api/ratings/statistics - статистика с группировкой по командам
router.get('/statistics', async (req, res) => { router.get('/statistics', async (req, res) => {
try { try {
const { type } = req.query; const { type, eventId } = req.query;
// Получаем все команды // Получаем все команды
const teamFilter = type ? { type, isActive: true } : { isActive: true }; const teamFilter = { isActive: true };
if (type) teamFilter.type = type;
if (eventId) teamFilter.eventId = eventId;
const teams = await Team.find(teamFilter); const teams = await Team.find(teamFilter);
// Получаем все оценки // Получаем все оценки
const ratings = await Rating.find() const ratingFilter = {};
if (eventId) ratingFilter.eventId = eventId;
const ratings = await Rating.find(ratingFilter)
.populate('expertId', 'fullName') .populate('expertId', 'fullName')
.populate('teamId', 'name type projectName'); .populate('teamId', 'name type projectName');
@@ -117,13 +122,17 @@ router.get('/statistics', async (req, res) => {
// GET /api/ratings/top3 - топ-3 команды/участники // GET /api/ratings/top3 - топ-3 команды/участники
router.get('/top3', async (req, res) => { router.get('/top3', async (req, res) => {
try { try {
const { type } = req.query; const { type, eventId } = req.query;
// Получаем статистику // Получаем статистику
const teamFilter = type ? { type, isActive: true } : { isActive: true }; const teamFilter = { isActive: true };
if (type) teamFilter.type = type;
if (eventId) teamFilter.eventId = eventId;
const teams = await Team.find(teamFilter); const teams = await Team.find(teamFilter);
const ratings = await Rating.find() const ratingFilter = {};
if (eventId) ratingFilter.eventId = eventId;
const ratings = await Rating.find(ratingFilter)
.populate('teamId', 'name type projectName'); .populate('teamId', 'name type projectName');
// Группируем и считаем средние баллы // Группируем и считаем средние баллы
@@ -161,10 +170,10 @@ router.get('/top3', async (req, res) => {
// POST /api/ratings - создать/обновить оценку эксперта // POST /api/ratings - создать/обновить оценку эксперта
router.post('/', async (req, res) => { router.post('/', async (req, res) => {
try { try {
const { expertId, teamId, ratings } = req.body; const { eventId, expertId, teamId, ratings } = req.body;
if (!expertId || !teamId || !ratings || !Array.isArray(ratings)) { if (!eventId || !expertId || !teamId || !ratings || !Array.isArray(ratings)) {
return res.status(400).json({ error: 'Expert ID, team ID, and ratings array are required' }); return res.status(400).json({ error: 'EventId, expert ID, team ID, and ratings array are required' });
} }
// Проверяем существование эксперта и команды // Проверяем существование эксперта и команды
@@ -185,7 +194,7 @@ router.post('/', async (req, res) => {
} }
// Ищем существующую оценку // Ищем существующую оценку
let rating = await Rating.findOne({ expertId, teamId }); let rating = await Rating.findOne({ eventId, expertId, teamId });
if (rating) { if (rating) {
// Обновляем существующую оценку // Обновляем существующую оценку
@@ -194,6 +203,7 @@ router.post('/', async (req, res) => {
} else { } else {
// Создаем новую оценку // Создаем новую оценку
rating = await Rating.create({ rating = await Rating.create({
eventId,
expertId, expertId,
teamId, teamId,
ratings ratings

View File

@@ -4,8 +4,10 @@ const { Team } = require('../models');
// GET /api/teams - список всех команд // GET /api/teams - список всех команд
router.get('/', async (req, res) => { router.get('/', async (req, res) => {
try { try {
const { type } = req.query; const { type, eventId } = req.query;
const filter = type ? { type } : {}; const filter = {};
if (type) filter.type = type;
if (eventId) filter.eventId = eventId;
const teams = await Team.find(filter).sort({ createdAt: -1 }); const teams = await Team.find(filter).sort({ createdAt: -1 });
res.json(teams); res.json(teams);
} catch (error) { } catch (error) {
@@ -16,7 +18,10 @@ router.get('/', async (req, res) => {
// GET /api/teams/active/voting - получить активную для оценки команду (ДОЛЖЕН БЫТЬ ПЕРЕД /:id) // GET /api/teams/active/voting - получить активную для оценки команду (ДОЛЖЕН БЫТЬ ПЕРЕД /:id)
router.get('/active/voting', async (req, res) => { router.get('/active/voting', async (req, res) => {
try { try {
const team = await Team.findOne({ isActiveForVoting: true }); const { eventId } = req.query;
const filter = { isActiveForVoting: true };
if (eventId) filter.eventId = eventId;
const team = await Team.findOne(filter);
res.json(team); res.json(team);
} catch (error) { } catch (error) {
res.status(500).json({ error: error.message }); res.status(500).json({ error: error.message });
@@ -26,9 +31,13 @@ router.get('/active/voting', async (req, res) => {
// PATCH /api/teams/stop-all-voting/global - остановить все оценивания (ДОЛЖЕН БЫТЬ ПЕРЕД /:id) // PATCH /api/teams/stop-all-voting/global - остановить все оценивания (ДОЛЖЕН БЫТЬ ПЕРЕД /:id)
router.patch('/stop-all-voting/global', async (req, res) => { router.patch('/stop-all-voting/global', async (req, res) => {
try { try {
const { eventId } = req.body;
// Находим все команды, которые сейчас оцениваются // Находим все команды, которые сейчас оцениваются
const filter = { isActiveForVoting: true };
if (eventId) filter.eventId = eventId;
const result = await Team.updateMany( const result = await Team.updateMany(
{ isActiveForVoting: true }, filter,
{ {
isActiveForVoting: false, isActiveForVoting: false,
votingStatus: 'evaluated' votingStatus: 'evaluated'
@@ -60,13 +69,14 @@ router.get('/:id', async (req, res) => {
// POST /api/teams - создать команду/участника // POST /api/teams - создать команду/участника
router.post('/', async (req, res) => { router.post('/', async (req, res) => {
try { try {
const { type, name, projectName, caseDescription } = req.body; const { eventId, type, name, projectName, caseDescription } = req.body;
if (!type || !name) { if (!eventId || !type || !name) {
return res.status(400).json({ error: 'Type and name are required' }); return res.status(400).json({ error: 'EventId, type and name are required' });
} }
const team = await Team.create({ const team = await Team.create({
eventId,
type, type,
name, name,
projectName: projectName || '', projectName: projectName || '',
@@ -118,8 +128,17 @@ router.delete('/:id', async (req, res) => {
// PATCH /api/teams/:id/activate-for-voting - активировать команду для оценки // PATCH /api/teams/:id/activate-for-voting - активировать команду для оценки
router.patch('/:id/activate-for-voting', async (req, res) => { router.patch('/:id/activate-for-voting', async (req, res) => {
try { try {
// Деактивируем все команды и сохраняем их статус // Получаем команду для активации
const previouslyActive = await Team.findOne({ isActiveForVoting: true }); const team = await Team.findById(req.params.id);
if (!team) {
return res.status(404).json({ error: 'Team not found' });
}
// Деактивируем все команды этого мероприятия
const previouslyActive = await Team.findOne({
isActiveForVoting: true,
eventId: team.eventId
});
if (previouslyActive) { if (previouslyActive) {
previouslyActive.isActiveForVoting = false; previouslyActive.isActiveForVoting = false;
previouslyActive.votingStatus = 'evaluated'; previouslyActive.votingStatus = 'evaluated';
@@ -127,11 +146,6 @@ router.patch('/:id/activate-for-voting', async (req, res) => {
} }
// Активируем выбранную команду // Активируем выбранную команду
const team = await Team.findById(req.params.id);
if (!team) {
return res.status(404).json({ error: 'Team not found' });
}
team.isActiveForVoting = true; team.isActiveForVoting = true;
team.votingStatus = 'evaluating'; team.votingStatus = 'evaluating';
await team.save(); await team.save();

View File

@@ -1,14 +1,13 @@
// Импортировать mongoose из общего модуля (подключение происходит в server/utils/mongoose.ts) // Импортировать mongoose из общего модуля (подключение происходит автоматически)
const mongoose = require('../../../utils/mongoose'); const mongoose = require('../../../utils/mongoose');
const { Event } = require('../models'); const Event = require('../models/Event');
async function recreateTestUser() { async function recreateTestUser() {
try { try {
// Ждем, пока подключение будет готово // Проверяем подключение к MongoDB
if (mongoose.connection.readyState !== 1) { if (mongoose.connection.readyState !== 1) {
await new Promise(resolve => { console.log('Waiting for MongoDB connection...');
mongoose.connection.once('connected', resolve); await new Promise(resolve => setTimeout(resolve, 1000));
});
} }
console.log('Connected to MongoDB'); console.log('Connected to MongoDB');
@@ -22,12 +21,11 @@ async function recreateTestUser() {
votingEnabled: false votingEnabled: false
}); });
console.log('Test event created:', event.name); console.log('Test event created:', event.name);
} else {
console.log('Event already exists:', event.name);
} }
console.log('Database initialized successfully'); console.log('Database initialized successfully');
await mongoose.disconnect();
console.log('Disconnected from MongoDB');
} catch (error) { } catch (error) {
console.error('Error:', error); console.error('Error:', error);
process.exit(1); process.exit(1);