Files
Sheep-Service/api/services/stand.service.js

358 lines
15 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const db = require("../config/db");
const Notification = require("../utils/notification.js");
class StandService {
getStand(id) {
return new Promise((res, rej) => {
const sql = `SELECT * FROM stand_list WHERE id = ?`;
db.get(sql, [id], (err, row) => {
if (err) {
console.error(err.message);
return res(false);
}
if (!row) {
console.log({ error: "id not found" });
return res(false);
}
let data = {
"id": Number(row.id),
"title": row.title,
"geo": JSON.parse(row.geo),
"hour_start": Number(row.hour_start),
"hour_end": Number(row.hour_end),
"quantity_sheep": Number(row.quantity_sheep),
"week_days": JSON.parse(row.week_days),
"processing_time": Number(row.processing_time),
"status": row.status == 1 ? true : false,
"updated_at": Number(row.updated_at),
"created_at": Number(row.created_at)
}
return res(data);
});
});
}
getList() {
return new Promise((res, rej) => {
const sql = `
SELECT
*
FROM
stand_list
ORDER BY
id
`;
db.all(sql, (err, rows) => {
if (err) {
console.error(err.message);
return res(false);
} else {
let data = rows.map((row) => {
return {
"id": Number(row.id),
"title": row.title,
"geo": JSON.parse(row.geo),
"hour_start": Number(row.hour_start),
"hour_end": Number(row.hour_end),
"quantity_sheep": Number(row.quantity_sheep),
"week_days": JSON.parse(row.week_days),
"processing_time": Number(row.processing_time),
"status": row.status == 1 ? true : false,
"updated_at": Number(row.updated_at),
"created_at": Number(row.created_at)
}
})
return res(data);
}
});
});
}
createStand(data) {
return new Promise((res, rej) => {
let sql = 'INSERT INTO stand_list(title, geo, hour_start, hour_end, quantity_sheep, week_days, processing_time, status, updated_at, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)';
db.run(sql, [
data.title,
JSON.stringify(data.geo || []),
Number(data.hour_start) || 9,
Number(data.hour_end) || 18,
Number(data.quantity_sheep) || 2,
JSON.stringify(data.week_days || [1]),
Number(data.processing_time) || 1,
1,
Math.floor(Date.now()),
Math.floor(Date.now())
], function (err) {
if (err) {
console.error(err.message);
return res(false);
} else {
res({ "status": "ok", "id": this.lastID });
}
});
});
}
updateStand(stand_id, data) {
return new Promise((res, rej) => {
const sql = `
UPDATE stand_list SET
title = COALESCE(?, title),
geo = COALESCE(?, geo),
hour_start = COALESCE(?, hour_start),
hour_end = COALESCE(?, hour_end),
quantity_sheep = COALESCE(?, quantity_sheep),
processing_time = COALESCE(?, processing_time),
week_days = COALESCE(?, week_days),
status = COALESCE(?, status),
updated_at = ?
WHERE id = ?
`;
db.run(sql, [
data.title ?? null,
data.geo !== undefined ? JSON.stringify(data.geo) : null,
data.hour_start !== undefined ? Number(data.hour_start) : null,
data.hour_end !== undefined ? Number(data.hour_end) : null,
data.quantity_sheep !== undefined ? Number(data.quantity_sheep) : null,
data.processing_time !== undefined ? Number(data.processing_time) : null,
data.week_days !== undefined ? JSON.stringify(data.week_days) : null,
data.status !== undefined ? (data.status ? 1 : 0) : null,
Date.now(),
stand_id
], function (err) {
if (err) {
console.error(err.message);
return res(false);
} else if (this.changes === 0) {
return res(false);
} else {
res({ status: "ok", id: stand_id });
}
});
});
}
deleteStand(stand_id) {
return new Promise((res, rej) => {
db.run('DELETE FROM stand_list WHERE id = ?', [stand_id], function (err) {
if (err) {
console.error(err.message);
return res(false);
} else if (this.changes === 0) {
return res(false);
} else {
res({ "status": "ok", "id": stand_id });
}
});
});
}
createSchedule(stand_id) {
return new Promise((res, rej) => {
let date_start;
const MS_DAY = 24 * 60 * 60 * 1000;
// Нормализует timestamp из БД: если в секундах — умножает на 1000
const normalizeTs = (ts) => {
if (!ts) return null;
// если строка — привести к числу
const n = Number(ts);
if (Number.isNaN(n)) return null;
// если кажется секундами (меньше ~1e12) — умножаем
return n < 1e12 ? n * 1000 : n;
};
// Возвращает timestamp (ms) следующего понедельника после ts
const getNextMonday = (ts) => {
let date = new Date(ts);
// нормализуем на начало дня
date.setHours(0, 0, 0, 0);
// всегда переходить к следующему дню (чтобы гарантированно получить следующий понедельник,
// даже если ts уже в понедельник)
date.setDate(date.getDate() + 1);
// пока не понедельник (в JS: 1 — понедельник)
while (date.getDay() !== 1) {
date.setDate(date.getDate() + 1);
}
return date.getTime();
};
// 1. Получаем стенд
db.get(`SELECT * FROM stand_list WHERE id = ?`, [stand_id], (err, stand) => {
if (err) {
console.error(err.message);
return res(false);
}
if (!stand) {
console.log({ error: "id not found" });
return res(false);
}
try {
stand.geo = JSON.parse(stand.geo);
} catch (e) {
stand.geo = stand.geo;
}
try {
stand.week_days = JSON.parse(stand.week_days);
} catch (e) {
stand.week_days = Array.isArray(stand.week_days) ? stand.week_days : [];
}
// 2. Берём последний date из расписания
db.get(
`SELECT MAX(date) AS max_date FROM stand_schedule WHERE stand_id = ?`,
[stand.id],
(err, row) => {
if (err) {
console.error(err.message);
return res(false);
}
const normalized = normalizeTs(row && row.max_date ? row.max_date : null);
if (normalized && normalized > Date.now()) {
date_start = getNextMonday(normalized);
} else {
date_start = getNextMonday(Date.now());
}
// 3. Генерация новых записей
// вычисляем количество слотов (целое)
const stand_length = Math.max(0, Math.floor((stand.hour_end - stand.hour_start) / stand.processing_time));
const timestamp = Date.now();
const list = [];
// Ожидается, что stand.week_days — массив чисел 0..6, где 0 -> Понедельник, 6 -> Воскресенье
for (const dayOffsetRaw of stand.week_days) {
const dayOffset = Number(dayOffsetRaw);
if (!Number.isFinite(dayOffset) || dayOffset < 0 || dayOffset > 6) continue;
// dayOffset: 0 => Monday, 6 => Sunday
const stand_date = date_start + (dayOffset * MS_DAY);
for (let i = 0; i < stand_length; i++) {
for (let q = 0; q < (Number(stand.quantity_sheep) || 0); q++) {
list.push([
stand.hour_start + (stand.processing_time * i),
q,
stand_date,
stand.id,
timestamp,
timestamp
]);
}
}
}
if (list.length === 0) {
return res({ status: "empty" });
}
// 4. Массовая вставка
const placeholders = list.map(() => "(?, ?, ?, ?, ?, ?)").join(",");
const values = list.flat();
const insertSQL = `
INSERT INTO stand_schedule(hour, number_sheep, date, stand_id, updated_at, created_at)
VALUES ${placeholders}
`;
db.run(insertSQL, values, function (err) {
if (err) {
console.error(err.message);
return res(false);
}
let text = [
`Стенд «${stand.title}» отримав новий графік. Можна сміливо записуватися 🙂`,
`Для «${stand.title}» відкрито новий розклад. Хто планував — саме час.`,
`Новий графік для «${stand.title}» вже доступний. Обирайте зручний час 👍`,
`Стенд «${stand.title}» оновив розклад. Запис розпочато.`,
`З’явилися нові дати у «${stand.title}». Встигніть обрати свою 😉`,
`«${stand.title}» відкрив новий період запису. Плануємо заздалегідь 🙂`,
`Оновлення для «${stand.title}». Додано новий графік.`,
`Новий розклад для «${stand.title}» вже чекає на охочих 📋`,
`Стенд «${stand.title}» додав нові години для запису ⏰`,
`Графік «${stand.title}» поповнено. Можна бронювати час 😊`,
`У «${stand.title}» з’явилися нові можливості для запису`,
`Свіжий графік для «${stand.title}» уже доступний 🚀`
];
let randomMessage = text[Math.floor(Math.random() * text.length)];
Notification.sendStand({
title: "Додано новий день служіння",
body: randomMessage,
page: `/stand/card/${stand.id}`
});
res({ status: "ok", inserted: list.length });
});
}
);
});
});
}
getScheduleList(stand_id) {
return new Promise((res, rej) => {
const sql = `
SELECT
ss.*,
s.name AS sheep_name
FROM
stand_schedule AS ss
LEFT JOIN
sheeps AS s
ON
s.id = ss.sheep_id
WHERE
ss.stand_id = ?
AND
date(ss.date / 1000, 'unixepoch', 'localtime') >= date('now', 'localtime')
ORDER BY
ss.id;
`;
db.all(sql, [stand_id], (err, rows) => {
if (err) {
console.error(err.message);
return res(false);
} else {
let data = rows.map((row) => {
return {
"id": Number(row.id),
"stand_id": Number(row.stand_id),
"date": Number(row.date),
"hour": Number(row.hour),
"sheep_id": Number(row.sheep_id),
"sheep_name": row.sheep_name,
"number_sheep": Number(row.number_sheep),
"updated_at": Number(row.updated_at),
"created_at": Number(row.created_at)
}
})
return res(data);
}
});
});
}
getScheduleHistory(id) {
return new Promise((res, rej) => {
return res({ id });
});
}
}
module.exports = new StandService();