326 lines
9.8 KiB
JavaScript
326 lines
9.8 KiB
JavaScript
const db = require('../../database/database');
|
|
const { logger } = require('../../middleware/logger');
|
|
|
|
class CoffeeRepository {
|
|
async _syncSortOrders(coffeeId, conn) {
|
|
const cx = conn || db;
|
|
const [rows] = await cx.query(
|
|
`SELECT id
|
|
FROM coffee_table_images
|
|
WHERE coffee_id = ?
|
|
ORDER BY sort_order ASC, id ASC`,
|
|
[coffeeId]
|
|
);
|
|
|
|
for (let i = 0; i < (rows || []).length; i += 1) {
|
|
await cx.query('UPDATE coffee_table_images SET sort_order = ? WHERE id = ?', [i, rows[i].id]);
|
|
}
|
|
}
|
|
|
|
async ensureLegacyPictureRow(coffeeId, conn) {
|
|
const cx = conn || db;
|
|
|
|
const [coffeeRows] = await cx.query(
|
|
`SELECT object_storage_id, original_filename
|
|
FROM coffee_table
|
|
WHERE id = ?
|
|
LIMIT 1`,
|
|
[coffeeId]
|
|
);
|
|
const coffee = coffeeRows?.[0];
|
|
if (!coffee || !coffee.object_storage_id) return;
|
|
|
|
const [countRows] = await cx.query(
|
|
`SELECT COUNT(*) AS c
|
|
FROM coffee_table_images
|
|
WHERE coffee_id = ?`,
|
|
[coffeeId]
|
|
);
|
|
const count = Number(countRows?.[0]?.c || 0);
|
|
if (count > 0) return;
|
|
|
|
await cx.query(
|
|
`INSERT INTO coffee_table_images (coffee_id, object_storage_id, original_filename, sort_order)
|
|
VALUES (?, ?, ?, 0)`,
|
|
[coffeeId, coffee.object_storage_id, coffee.original_filename || null]
|
|
);
|
|
}
|
|
|
|
async listPicturesByCoffeeId(coffeeId, conn) {
|
|
const cx = conn || db;
|
|
const [rows] = await cx.query(
|
|
`SELECT id, coffee_id, object_storage_id, original_filename, sort_order, created_at
|
|
FROM coffee_table_images
|
|
WHERE coffee_id = ?
|
|
ORDER BY sort_order ASC, id ASC`,
|
|
[coffeeId]
|
|
);
|
|
return rows || [];
|
|
}
|
|
|
|
async deleteAllPicturesByCoffeeId(coffeeId, conn) {
|
|
const cx = conn || db;
|
|
const toDelete = await this.listPicturesByCoffeeId(coffeeId, cx);
|
|
if (!toDelete.length) return [];
|
|
|
|
await cx.query('DELETE FROM coffee_table_images WHERE coffee_id = ?', [coffeeId]);
|
|
return toDelete;
|
|
}
|
|
|
|
async deletePicturesByIds(coffeeId, pictureIds, conn) {
|
|
const cx = conn || db;
|
|
const ids = Array.isArray(pictureIds)
|
|
? pictureIds.map((x) => Number(x)).filter((x) => Number.isFinite(x) && x > 0)
|
|
: [];
|
|
if (!ids.length) return [];
|
|
|
|
const placeholders = ids.map(() => '?').join(',');
|
|
const [rows] = await cx.query(
|
|
`SELECT id, coffee_id, object_storage_id, original_filename, sort_order, created_at
|
|
FROM coffee_table_images
|
|
WHERE coffee_id = ?
|
|
AND id IN (${placeholders})`,
|
|
[coffeeId, ...ids]
|
|
);
|
|
if (!(rows || []).length) return [];
|
|
|
|
const deletePlaceholders = rows.map(() => '?').join(',');
|
|
await cx.query(
|
|
`DELETE FROM coffee_table_images
|
|
WHERE coffee_id = ?
|
|
AND id IN (${deletePlaceholders})`,
|
|
[coffeeId, ...rows.map((r) => r.id)]
|
|
);
|
|
return rows;
|
|
}
|
|
|
|
async addPictures(coffeeId, images, conn) {
|
|
const cx = conn || db;
|
|
const existing = await this.listPicturesByCoffeeId(coffeeId, cx);
|
|
let nextSort = existing.length;
|
|
|
|
for (const img of (images || [])) {
|
|
await cx.query(
|
|
`INSERT INTO coffee_table_images (coffee_id, object_storage_id, original_filename, sort_order)
|
|
VALUES (?, ?, ?, ?)`,
|
|
[
|
|
coffeeId,
|
|
img.object_storage_id,
|
|
img.original_filename || null,
|
|
nextSort,
|
|
]
|
|
);
|
|
nextSort += 1;
|
|
}
|
|
}
|
|
|
|
async syncPrimaryPictureFromGallery(coffeeId, conn) {
|
|
const cx = conn || db;
|
|
const [rows] = await cx.query(
|
|
`SELECT object_storage_id, original_filename
|
|
FROM coffee_table_images
|
|
WHERE coffee_id = ?
|
|
ORDER BY sort_order ASC, id ASC
|
|
LIMIT 1`,
|
|
[coffeeId]
|
|
);
|
|
|
|
const first = rows?.[0];
|
|
await cx.query(
|
|
`UPDATE coffee_table
|
|
SET object_storage_id = ?,
|
|
original_filename = ?,
|
|
updated_at = NOW()
|
|
WHERE id = ?`,
|
|
[first?.object_storage_id || null, first?.original_filename || null, coffeeId]
|
|
);
|
|
}
|
|
|
|
async _attachPictures(rows, conn) {
|
|
const cx = conn || db;
|
|
if (!Array.isArray(rows) || !rows.length) return rows || [];
|
|
|
|
const ids = rows.map((r) => Number(r.id)).filter((id) => Number.isFinite(id));
|
|
if (!ids.length) return rows;
|
|
|
|
const placeholders = ids.map(() => '?').join(',');
|
|
const [pictureRows] = await cx.query(
|
|
`SELECT id, coffee_id, object_storage_id, original_filename, sort_order, created_at
|
|
FROM coffee_table_images
|
|
WHERE coffee_id IN (${placeholders})
|
|
ORDER BY coffee_id ASC, sort_order ASC, id ASC`,
|
|
ids
|
|
);
|
|
|
|
const grouped = new Map();
|
|
for (const p of (pictureRows || [])) {
|
|
const key = Number(p.coffee_id);
|
|
if (!grouped.has(key)) grouped.set(key, []);
|
|
grouped.get(key).push({
|
|
id: Number(p.id),
|
|
coffee_id: Number(p.coffee_id),
|
|
object_storage_id: p.object_storage_id,
|
|
original_filename: p.original_filename,
|
|
sort_order: Number(p.sort_order || 0),
|
|
created_at: p.created_at,
|
|
});
|
|
}
|
|
|
|
for (const row of rows) {
|
|
const coffeeId = Number(row.id);
|
|
const fromTable = grouped.get(coffeeId) || [];
|
|
if (fromTable.length) {
|
|
row.pictures = fromTable;
|
|
} else if (row.object_storage_id) {
|
|
row.pictures = [{
|
|
id: null,
|
|
coffee_id: coffeeId,
|
|
object_storage_id: row.object_storage_id,
|
|
original_filename: row.original_filename || null,
|
|
sort_order: 0,
|
|
created_at: row.created_at || null,
|
|
}];
|
|
} else {
|
|
row.pictures = [];
|
|
}
|
|
}
|
|
|
|
return rows;
|
|
}
|
|
|
|
async listAll(conn) {
|
|
const cx = conn || db;
|
|
const [rows] = await cx.query('SELECT * FROM coffee_table ORDER BY id DESC');
|
|
return this._attachPictures(rows || [], cx);
|
|
}
|
|
|
|
async getById(id, conn) {
|
|
const cx = conn || db;
|
|
const [rows] = await cx.query('SELECT * FROM coffee_table WHERE id = ? LIMIT 1', [id]);
|
|
if (!rows || !rows[0]) return null;
|
|
const hydrated = await this._attachPictures([rows[0]], cx);
|
|
return hydrated[0] || null;
|
|
}
|
|
|
|
async create(data, conn) {
|
|
const cx = conn || db;
|
|
const sql = `INSERT INTO coffee_table (
|
|
title, description, price, currency, is_featured,
|
|
billing_interval, interval_count,
|
|
object_storage_id, original_filename,
|
|
state, created_at, updated_at
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())`;
|
|
const params = [
|
|
data.title,
|
|
data.description,
|
|
data.price,
|
|
data.currency,
|
|
data.is_featured,
|
|
data.billing_interval,
|
|
data.interval_count,
|
|
data.object_storage_id,
|
|
data.original_filename,
|
|
data.state
|
|
];
|
|
const [result] = await cx.query(sql, params);
|
|
const createdId = result.insertId;
|
|
|
|
const pictures = Array.isArray(data.images) ? [...data.images] : [];
|
|
if (!pictures.length && data.object_storage_id) {
|
|
pictures.push({
|
|
object_storage_id: data.object_storage_id,
|
|
original_filename: data.original_filename || null,
|
|
sort_order: 0,
|
|
});
|
|
}
|
|
|
|
for (const picture of pictures) {
|
|
await cx.query(
|
|
`INSERT INTO coffee_table_images (coffee_id, object_storage_id, original_filename, sort_order)
|
|
VALUES (?, ?, ?, ?)`,
|
|
[
|
|
createdId,
|
|
picture.object_storage_id,
|
|
picture.original_filename || null,
|
|
Number.isFinite(Number(picture.sort_order)) ? Number(picture.sort_order) : 0,
|
|
]
|
|
);
|
|
}
|
|
|
|
logger.info('[CoffeeRepository.create] insert', { id: result.insertId });
|
|
return this.getById(createdId, cx);
|
|
}
|
|
|
|
async update(id, data, conn) {
|
|
const cx = conn || db;
|
|
const sql = `UPDATE coffee_table
|
|
SET title = ?, description = ?, price = ?, currency = ?, is_featured = ?,
|
|
billing_interval = ?, interval_count = ?,
|
|
object_storage_id = ?, original_filename = ?,
|
|
state = ?, updated_at = NOW()
|
|
WHERE id = ?`;
|
|
const params = [
|
|
data.title,
|
|
data.description,
|
|
data.price,
|
|
data.currency,
|
|
data.is_featured,
|
|
data.billing_interval,
|
|
data.interval_count,
|
|
data.object_storage_id,
|
|
data.original_filename,
|
|
data.state,
|
|
id
|
|
];
|
|
const [result] = await cx.query(sql, params);
|
|
logger.info('[CoffeeRepository.update] update', { id, affected: result.affectedRows });
|
|
return result.affectedRows > 0;
|
|
}
|
|
|
|
async setState(id, state, conn) {
|
|
const cx = conn || db;
|
|
const [result] = await cx.query('UPDATE coffee_table SET state = ?, updated_at = NOW() WHERE id = ?', [state, id]);
|
|
return result.affectedRows > 0;
|
|
}
|
|
|
|
async delete(id, conn) {
|
|
const cx = conn || db;
|
|
const [result] = await cx.query('DELETE FROM coffee_table WHERE id = ?', [id]);
|
|
return result.affectedRows > 0;
|
|
}
|
|
|
|
async listActive(conn) {
|
|
const cx = conn || db;
|
|
const [rows] = await cx.query('SELECT * FROM coffee_table WHERE state = TRUE ORDER BY id DESC');
|
|
return this._attachPictures(rows || [], cx);
|
|
}
|
|
|
|
async editPictures(coffeeId, { replaceAll = false, removePictureIds = [], images = [] } = {}, conn) {
|
|
const cx = conn || db;
|
|
|
|
await this.ensureLegacyPictureRow(coffeeId, cx);
|
|
|
|
let deleted = [];
|
|
if (replaceAll) {
|
|
deleted = await this.deleteAllPicturesByCoffeeId(coffeeId, cx);
|
|
} else if (Array.isArray(removePictureIds) && removePictureIds.length) {
|
|
deleted = await this.deletePicturesByIds(coffeeId, removePictureIds, cx);
|
|
}
|
|
|
|
if (Array.isArray(images) && images.length) {
|
|
await this.addPictures(coffeeId, images, cx);
|
|
}
|
|
|
|
await this._syncSortOrders(coffeeId, cx);
|
|
await this.syncPrimaryPictureFromGallery(coffeeId, cx);
|
|
|
|
const updated = await this.getById(coffeeId, cx);
|
|
return {
|
|
updated,
|
|
deleted,
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = new CoffeeRepository();
|