Skip to content

Commit f81adc1

Browse files
refactor: Logs
1 parent 6093efe commit f81adc1

4 files changed

Lines changed: 149 additions & 78 deletions

File tree

Lines changed: 17 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,21 @@
1-
import db from '../config/db.js';
2-
import { aql } from 'arangojs';
3-
import asyncHandler from '../utils/asyncHandler.js';
4-
5-
export const getBookingLogs = asyncHandler(async (req, res) => {
6-
const {
7-
dateFrom = "",
8-
dateTo = "",
9-
status = "",
10-
fullName = "",
11-
groupCode = "",
12-
bookingId = "",
13-
email = "",
14-
roomName = "",
15-
page = 1,
16-
limit = 10
17-
} = req.query;
1+
/**
2+
* controllers/logController.js
3+
* Обработка запросов для логов бронирований (admin only).
4+
*/
185

19-
const offset = (Number(page) - 1) * Number(limit);
20-
const countLimit = Number(limit);
21-
22-
const cursor = await db.query(aql`
23-
LET all_logs = (
24-
FOR b IN Bookings
25-
// Подтягиваем связанные документы
26-
LET user = DOCUMENT(b._from)
27-
LET computer = DOCUMENT(b._to)
28-
29-
// 1. Фильтр по диапазону дат (сравнение ISO строк)
30-
FILTER ${dateFrom} == "" OR b.start_at >= ${dateFrom}
31-
FILTER ${dateTo} == "" OR b.start_at <= ${dateTo + "T23:59:59"}
32-
33-
// 2. Фильтр по статусу
34-
FILTER ${status} == "" OR b.status == ${status}
35-
36-
// 3. Фильтр по ID бронирования (ключ в ArangoDB)
37-
FILTER ${bookingId} == "" OR CONTAINS(LOWER(b._key), LOWER(${bookingId}))
38-
39-
// 4. Поиск по данным пользователя
40-
FILTER ${fullName} == "" OR CONTAINS(LOWER(user.full_name), LOWER(${fullName}))
41-
FILTER ${email} == "" OR CONTAINS(LOWER(user.email), LOWER(${email}))
42-
FILTER ${groupCode} == "" OR user.group_code == ${groupCode}
43-
44-
// 5. Поиск по аудитории (из документа компьютера)
45-
FILTER ${roomName} == "" OR CONTAINS(LOWER(computer.room_id || ""), LOWER(${roomName}))
6+
import asyncHandler from '../utils/asyncHandler.js';
7+
import logService from '../services/logService.js';
468

47-
// Сортируем: сначала самые свежие записи
48-
SORT b.start_at DESC
49-
50-
RETURN {
51-
id: b._key,
52-
date: b.start_at,
53-
created_at: b.meta.created_at,
54-
status: b.status,
55-
// Текстовое описание статуса для фронтенда
56-
statusText: b.status == 'finished' ? 'Завершено' :
57-
b.status == 'missed' ? 'Неявка' :
58-
b.status == 'cancelled' ? 'Отменено' :
59-
b.status == 'reserved' ? 'Забронировано' :
60-
b.status == 'active' ? 'Активно' : 'Неизвестно',
61-
user_name: user.full_name,
62-
user_email: user.email,
63-
group_code: user.group_code,
64-
room_name: computer.room_id || "Не указана",
65-
pc_name: computer._key,
66-
history: b.history || []
67-
}
68-
)
9+
class LogController {
6910

70-
// Возвращаем общее количество (для пагинации) и нарезанный массив данных
71-
RETURN {
72-
total: LENGTH(all_logs),
73-
data: SLICE(all_logs, ${offset}, ${countLimit})
74-
}
75-
`);
11+
/**
12+
* Method: getBookingLogs
13+
* Получить логи бронирований.
14+
*/
15+
getBookingLogs = asyncHandler(async (req, res) => {
16+
const result = await logService.getAll(req.query);
17+
res.json(result || { total: 0, data: [] });
18+
});
19+
}
7620

77-
const result = await cursor.next();
78-
79-
res.json(result || { total: 0, data: [] });
80-
});
21+
export default new LogController();

backend/src/dao/logs.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* dao/logs.js
3+
* Read-only агрегация бронирований для админ-панели
4+
*/
5+
6+
import db from '../config/db.js';
7+
import { aql } from 'arangojs';
8+
9+
class LogDao {
10+
constructor() {
11+
this.collection = db.collection('Bookings');
12+
}
13+
14+
/**
15+
* Method: findAll
16+
* Получить логи бронирований с фильтрами и пагинацией.
17+
*/
18+
async findAll(filters, { offset, limit }) {
19+
const {
20+
dateFrom = '',
21+
dateTo = '',
22+
status = '',
23+
bookingId = '',
24+
fullName = '',
25+
email = '',
26+
groupCode = '',
27+
roomName = ''
28+
} = filters;
29+
30+
const cursor = await db.query(aql`
31+
LET all_logs = (
32+
FOR b IN Bookings
33+
LET user = DOCUMENT(b._from)
34+
LET computer = DOCUMENT(b._to)
35+
36+
FILTER ${dateFrom} == "" OR b.start_at >= ${dateFrom}
37+
FILTER ${dateTo} == "" OR b.start_at <= ${dateTo}
38+
FILTER ${status} == "" OR b.status == ${status}
39+
FILTER ${bookingId} == "" OR CONTAINS(LOWER(b._key), LOWER(${bookingId}))
40+
FILTER ${fullName} == "" OR CONTAINS(LOWER(user.full_name), LOWER(${fullName}))
41+
FILTER ${email} == "" OR CONTAINS(LOWER(user.email), LOWER(${email}))
42+
FILTER ${groupCode} == "" OR user.group_code == ${groupCode}
43+
FILTER ${roomName} == "" OR CONTAINS(LOWER(computer.room_id || ""), LOWER(${roomName}))
44+
45+
SORT b.start_at DESC
46+
47+
RETURN {
48+
id: b._key,
49+
date: b.start_at,
50+
created_at: b.meta.created_at,
51+
status: b.status,
52+
statusText: b.status == 'finished' ? 'Завершено'
53+
: b.status == 'missed' ? 'Неявка'
54+
: b.status == 'cancelled' ? 'Отменено'
55+
: b.status == 'reserved' ? 'Забронировано'
56+
: b.status == 'active' ? 'Активно'
57+
: 'Неизвестно',
58+
user_name: user.full_name,
59+
user_email: user.email,
60+
group_code: user.group_code,
61+
room_name: computer.room_id || "Не указана",
62+
pc_name: computer._key,
63+
history: b.history || []
64+
}
65+
)
66+
67+
RETURN {
68+
total: LENGTH(all_logs),
69+
data: SLICE(all_logs, ${offset}, ${limit})
70+
}
71+
`);
72+
73+
return await cursor.next();
74+
}
75+
}
76+
77+
export default new LogDao();

backend/src/routes/adminRoutes.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { verifyToken, isAdmin } from '../middlewares/authMiddleware.js'
88
import userRoutes from './userRoutes.js';
99
import equipmentRoutes from './equipmentRoutes.js';
1010
import roomAdminRoutes from './roomAdminRoutes.js';
11-
import { getBookingLogs } from '../controllers/logController.js';
11+
import logController from '../controllers/logController.js';
1212

1313
const router = Router();
1414

@@ -18,7 +18,7 @@ router.use('/users', userRoutes);
1818
router.use('/equipment', equipmentRoutes);
1919
router.use('/rooms', roomAdminRoutes);
2020

21-
router.get('/logs', getBookingLogs);
21+
router.get('/logs', logController.getBookingLogs);
2222

2323

2424
export default router;

backend/src/services/logService.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* services/logService.js
3+
* Бизнес-логика для логов бронирований.
4+
*/
5+
6+
import LogDao from '../dao/logs.js';
7+
import { validatePagination } from '../utils/validators.js';
8+
9+
class LogService {
10+
/**
11+
* Method: getAll
12+
* Получить логи бронирований с фильтрами и пагинацией.
13+
* Нормализует dateTo (добавляет конец дня, если передана только дата).
14+
*/
15+
async getAll(query) {
16+
const {
17+
dateFrom = '',
18+
dateTo = '',
19+
status = '',
20+
bookingId = '',
21+
fullName = '',
22+
email = '',
23+
groupCode = '',
24+
roomName = '',
25+
page = 1,
26+
limit = 10
27+
} = query;
28+
29+
const pagination = validatePagination(page, limit);
30+
const offset = (pagination.page - 1) * pagination.limit;
31+
32+
// Нормализация dateTo: если передана дата без времени — добавляем конец дня
33+
let normalizedDateTo = dateTo;
34+
if (dateTo && !dateTo.includes('T')) {
35+
normalizedDateTo = `${dateTo}T23:59:59Z`;
36+
}
37+
38+
const filters = {
39+
dateFrom,
40+
dateTo: normalizedDateTo,
41+
status,
42+
bookingId,
43+
fullName,
44+
email,
45+
groupCode,
46+
roomName
47+
};
48+
49+
return await LogDao.findAll(filters, { offset, limit: pagination.limit });
50+
}
51+
}
52+
53+
export default new LogService();

0 commit comments

Comments
 (0)