This commit is contained in:
rgrgogu
2026-05-20 13:22:29 +08:00
parent 6907e9bb2d
commit 6a5145f553
26 changed files with 2615 additions and 221 deletions
+31
View File
@@ -0,0 +1,31 @@
"use strict";
/**
* Single archive — soft delete one record
*/
async function archiveOne(Model, where, deletedBy, transaction) {
const record = await Model.findOne({ where });
if (!record) return null;
await record.update({ deletedBy: deletedBy ?? null }, { transaction });
await record.destroy({ transaction });
return record;
}
/**
* Bulk archive — soft delete multiple records by primary key
*/
async function archiveMany(Model, pkField, ids, deletedBy, transaction) {
if (!ids?.length) return 0;
const records = await Model.findAll({ where: { [pkField]: ids, deletedAt: null } });
if (!records.length) return 0;
for (const record of records) {
await record.update({ deletedBy: deletedBy ?? null }, { transaction });
await record.destroy({ transaction });
}
return records.length;
}
module.exports = { archiveOne, archiveMany };
+16
View File
@@ -0,0 +1,16 @@
"use strict";
/**
* Replace all junction rows for a course in one shot (delete + bulk create).
*/
async function syncJunction(Model, courseId, ids, fkField, transaction) {
await Model.destroy({ where: { course_id: courseId }, transaction });
if (ids?.length) {
await Model.bulkCreate(
ids.map((id) => ({ course_id: courseId, [fkField]: id })),
{ transaction }
);
}
}
module.exports = { syncJunction };
+62
View File
@@ -0,0 +1,62 @@
"use strict";
/**
* For CREATE — plain text array, no IDs needed
* objectives: ["text1"] or [{ text: "text1" }]
*/
async function syncObjectivesCreate(Model, parentField, parentId, objectives, transaction) {
if (!objectives?.length) return;
await Model.bulkCreate(
objectives.map((o, i) => ({
[parentField]: parentId,
text: typeof o === "string" ? o : o.text,
order_index: i,
})),
{ transaction }
);
}
/**
* For UPDATE — upsert by objective_id, hard delete removed ones
* objectives: [{ objective_id: "123", text: "text1" }, { text: "new" }]
*/
async function syncObjectivesUpdate(Model, parentField, parentId, objectives, transaction) {
if (!objectives?.length) {
await Model.destroy({ where: { [parentField]: parentId }, force: true, transaction });
return;
}
const existing = await Model.findAll({ where: { [parentField]: parentId }, transaction });
const existingMap = new Map(existing.map((o) => [String(o.objective_id), o]));
const incomingIds = new Set(
objectives.filter((o) => o.objective_id).map((o) => String(o.objective_id))
);
// Hard delete removed
const toDelete = existing.filter((o) => !incomingIds.has(String(o.objective_id)));
if (toDelete.length) {
await Model.destroy({
where: { objective_id: toDelete.map((o) => o.objective_id) },
force: true,
transaction,
});
}
// Update existing or create new
for (let i = 0; i < objectives.length; i++) {
const item = objectives[i];
const record = item.objective_id ? existingMap.get(String(item.objective_id)) : null;
if (record) {
await record.update({ text: item.text, order_index: i }, { transaction });
} else {
await Model.create(
{ [parentField]: parentId, text: item.text, order_index: i },
{ transaction }
);
}
}
}
module.exports = { syncObjectivesCreate, syncObjectivesUpdate };
+44
View File
@@ -0,0 +1,44 @@
"use strict";
const { Op } = require("sequelize");
const onlyDeleted = { deletedAt: { [Op.not]: null } };
/**
* Soft-restore a single record by clearing deletedAt / deletedBy.
* Returns the record on success, null if not found.
*
* @param {Model} Model - Sequelize model
* @param {object} where - Where clause (must include onlyDeleted or equivalent)
* @param {*} restoredBy - User ID stamped onto updatedBy
* @param {object} t - Sequelize transaction
*/
async function restoreOne(Model, where, restoredBy, t) {
const record = await Model.findOne({ where, paranoid: false });
if (!record) return null;
await record.restore();
await record.update({ updatedBy: restoredBy, deletedBy: null })
return record;
}
/**
* Soft-restore many records by their primary key column.
* Returns the count of restored records.
*
* @param {Model} Model - Sequelize model
* @param {string} pkColumn - Primary key column name (e.g. "course_id")
* @param {Array} ids - Array of primary key values to restore
* @param {*} restoredBy - User ID stamped onto updatedBy
* @param {object} t - Sequelize transaction
*/
async function restoreMany(Model, pkColumn, ids, restoredBy, t) {
const [count] = await Model.update(
{ deletedAt: null, deletedBy: null, updatedBy: restoredBy ?? null },
{ where: { [pkColumn]: ids, ...onlyDeleted }, transaction: t, paranoid: false }
);
return count;
}
module.exports = { restoreOne, restoreMany };