units,lesson as standalone

This commit is contained in:
2026-07-10 11:44:46 +08:00
parent 86fba50b95
commit e1ffdab190
46 changed files with 1463 additions and 197 deletions
+14 -3
View File
@@ -47,13 +47,20 @@ function buildWhere(filters = [], allowedFields = new Set()) {
* @param {Array<{ id: string, desc: boolean }>} sort
* @returns {Array} Sequelize order clause
*/
function buildOrder(sort = [], allowedFields = new Set()) {
function buildOrder(sort = [], allowedFields = new Set(), computedFields = new Set()) {
const order = [];
for (const { id, desc } of sort) {
if (!id) continue;
if (allowedFields.size && !allowedFields.has(id)) continue;
// ─── Computed (subquery/literal) columns — order by the unqualified
// SELECT alias, since they aren't real columns on the model's table ──
if (computedFields.has(id)) {
order.push([Sequelize.literal(`"${id}"`), desc ? "DESC" : "ASC"]);
continue;
}
// ─── ENUM fields — use CASE for custom sort order ─────────────────────
if (ENUM_SORT_ORDER[id]) {
const sequence = desc
@@ -88,12 +95,16 @@ function buildOrder(sort = [], allowedFields = new Set()) {
* @param {Array} sort
* @returns {{ where: Object, order: Array }}
*/
function buildQuery(filters = [], sort = [], allowedFields = []) {
function buildQuery(filters = [], sort = [], allowedFields = [], computedFields = []) {
const fieldSet = new Set(allowedFields);
const computedSet = new Set(computedFields);
const orderFieldSet = new Set([...allowedFields, ...computedFields]);
return {
// Computed (subquery/literal) columns aren't real table columns — filtering
// via Sequelize.col() would error, so only allow them in ORDER BY, not WHERE.
where: buildWhere(filters, fieldSet),
order: buildOrder(sort, fieldSet),
order: buildOrder(sort, orderFieldSet, computedSet),
};
}
+2 -1
View File
@@ -97,8 +97,9 @@ async function paginate(model, req, {
const attributes = modelToAttributes(model, { exclude: excludeAttributes, jsonbSchemas, context });
const ALLOWED_FIELDS = attributes.map((a) => a.field);
const computedFieldKeys = computedAttributes.map((c) => c.key);
const { where, order } = buildQuery(filters, sort, ALLOWED_FIELDS);
const { where, order } = buildQuery(filters, sort, ALLOWED_FIELDS, computedFieldKeys);
// Build attribute includes: jsonb + audit subqueries + any extra from findOptions
const baseIncludes = jsonbAttr ? [jsonbAttr] : [];