This commit is contained in:
rgrgogu
2026-05-06 14:15:04 +08:00
parent a8f10a25d7
commit d5fcc574a4
12 changed files with 389 additions and 219 deletions
+43 -18
View File
@@ -1,12 +1,12 @@
// utils/paginate.util.js
const { Sequelize } = require('sequelize');
const { modelToAttributes } = require('./modelToAttributes');
const { excludeJsonbPaths } = require('./excludeJsonbPaths');
const { modelToAttributes } = require('./modelToAttributes.util');
const { excludeJsonbPaths } = require('./excludeJSONBPaths.util');
const { buildQuery } = require('./buildQuery.util');
const PAGE_START = 1;
const PAGE_SIZE = 10;
const MAX_LIMIT = 100;
const PAGE_SIZE = 10;
const MAX_LIMIT = 1000;
function safeParseJSON(value, fallback = []) {
try {
@@ -44,6 +44,25 @@ function auditInclude(mdl_Users, parentAlias = 'User') {
};
}
const auditIdToName = (row) => {
const map = {
createdBy: 'createdByName',
updatedBy: 'updatedByName',
deletedBy: 'deletedByName',
};
const result = { ...row };
for (const [idField, nameField] of Object.entries(map)) {
if (result[idField] !== null && result[idField] !== undefined) {
result[idField] = result[nameField] ?? result[idField]; // ← use name if available, fallback to id
}
delete result[nameField]; // ← remove the redundant *ByName fields
}
return result;
};
/**
* Reusable paginated findAndCountAll
*
@@ -58,30 +77,30 @@ function auditInclude(mdl_Users, parentAlias = 'User') {
*/
async function paginate(model, req, {
excludeAttributes = [],
jsonbSchemas = {},
jsonbColumn = null,
findOptions = {},
auditOptions = null, // ← { mdl_Users, parentAlias }
jsonbSchemas = {},
jsonbColumn = null,
findOptions = {},
auditOptions = null, // ← { mdl_Users, parentAlias }
} = {}) {
const page = Math.max(PAGE_START, parseInt(req.query.page, 10) || PAGE_START);
const limit = Math.min(parseInt(req.query.limit, 10) || PAGE_SIZE, MAX_LIMIT);
const page = Math.max(PAGE_START, parseInt(req.query.page, 10) || PAGE_START);
const limit = Math.min(parseInt(req.query.limit, 10) || PAGE_SIZE, MAX_LIMIT);
const offset = (page - PAGE_START) * limit;
const filters = safeParseJSON(req.query.filters);
const sort = safeParseJSON(req.query.sort);
const sort = safeParseJSON(req.query.sort);
const topLevelExclude = excludeAttributes.filter((f) => !f.includes('.'));
const jsonbExclude = excludeAttributes.filter((f) => f.includes('.'));
const jsonbAttr = jsonbColumn ? excludeJsonbPaths(jsonbColumn, jsonbExclude) : null;
const jsonbExclude = excludeAttributes.filter((f) => f.includes('.'));
const jsonbAttr = jsonbColumn ? excludeJsonbPaths(jsonbColumn, jsonbExclude) : null;
const attributes = modelToAttributes(model, { exclude: excludeAttributes, jsonbSchemas });
const attributes = modelToAttributes(model, { exclude: excludeAttributes, jsonbSchemas });
const ALLOWED_FIELDS = attributes.map((a) => a.field);
const { where, order } = buildQuery(filters, sort, ALLOWED_FIELDS);
// Build attribute includes: jsonb + audit subqueries + any extra from findOptions
const baseIncludes = jsonbAttr ? [jsonbAttr] : [];
const auditAttrs = auditOptions
const baseIncludes = jsonbAttr ? [jsonbAttr] : [];
const auditAttrs = auditOptions
? auditInclude(auditOptions.mdl_Users, auditOptions.parentAlias).attributes
: [];
const extraIncludes = findOptions.attributes?.include ?? [];
@@ -91,7 +110,7 @@ async function paginate(model, req, {
const { count, rows } = await model.findAndCountAll({
...restFindOptions,
where: { ...where, ...(restFindOptions.where ?? {}) },
where: { ...where, ...(restFindOptions.where ?? {}) },
order,
limit,
offset,
@@ -101,10 +120,16 @@ async function paginate(model, req, {
},
});
const data = rows
.map((row) => row.toJSON?.() ?? row)
.map(auditIdToName);
// ─── Flatten JSONB columns into dot-notation keys ──────────────────────────
const jsonbCols = jsonbColumn ? [jsonbColumn] : [];
const totalPages = Math.ceil(count / limit);
return {
data: rows,
data,
pagination: {
page,
limit,