mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
100 lines
3.5 KiB
JavaScript
100 lines
3.5 KiB
JavaScript
"use strict";
|
|
|
|
const REF_TYPE_CONFIG = {
|
|
course: { pk: "course_id" },
|
|
unit: { pk: "unit_id" },
|
|
lesson: { pk: "lesson_id" },
|
|
};
|
|
|
|
/**
|
|
* Enriches CoursePrerequisite rows ({ref_type, ref_id, ...}) with the
|
|
* referenced entity's title (and subscription tier slug, where the entity
|
|
* has one) — one batched findAll per ref_type instead of a query per row.
|
|
* Rows whose referenced entity no longer exists (deleted) get title: null
|
|
* so callers can drop or flag them instead of crashing.
|
|
*/
|
|
async function resolvePrerequisiteTitles(prereqs, { Course, Unit, Lesson }) {
|
|
if (!prereqs?.length) return prereqs ?? [];
|
|
|
|
const models = { course: Course, unit: Unit, lesson: Lesson };
|
|
// Only Course/Unit carry a subscription tier gate — Lesson has none.
|
|
const attrsByType = { course: ["title", "subscription"], unit: ["title", "subscription"], lesson: ["title"] };
|
|
const idsByType = { course: new Set(), unit: new Set(), lesson: new Set() };
|
|
for (const p of prereqs) {
|
|
if (idsByType[p.ref_type]) idsByType[p.ref_type].add(p.ref_id);
|
|
}
|
|
|
|
const infoMaps = {};
|
|
for (const [type, { pk }] of Object.entries(REF_TYPE_CONFIG)) {
|
|
const ids = [...idsByType[type]];
|
|
infoMaps[type] = new Map();
|
|
if (!ids.length) continue;
|
|
const rows = await models[type].findAll({
|
|
where: { [pk]: ids },
|
|
attributes: [pk, ...attrsByType[type]],
|
|
});
|
|
for (const row of rows) infoMaps[type].set(String(row[pk]), row);
|
|
}
|
|
|
|
return prereqs.map((p) => {
|
|
const info = infoMaps[p.ref_type]?.get(String(p.ref_id));
|
|
return {
|
|
...p,
|
|
title: info?.title ?? null,
|
|
subscription: info?.subscription ?? null,
|
|
};
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Enriches prerequisite rows (already carrying `title`) with `completed` —
|
|
* whether the given learner has finished the referenced course/unit/lesson.
|
|
* "Completed" means: a Certificate exists for a course prereq, or the
|
|
* matching UnitReadingProgress/LessonReadingProgress row has status
|
|
* "completed" for a unit/lesson prereq.
|
|
*/
|
|
async function resolvePrerequisiteCompletion(prereqs, { Certificate, UnitReadingProgress, LessonReadingProgress }, userId) {
|
|
if (!prereqs?.length) return prereqs ?? [];
|
|
|
|
const idsByType = { course: new Set(), unit: new Set(), lesson: new Set() };
|
|
for (const p of prereqs) {
|
|
if (idsByType[p.ref_type]) idsByType[p.ref_type].add(p.ref_id);
|
|
}
|
|
|
|
const completedIds = { course: new Set(), unit: new Set(), lesson: new Set() };
|
|
|
|
const courseIds = [...idsByType.course];
|
|
if (courseIds.length) {
|
|
const certs = await Certificate.findAll({
|
|
where: { user_id: userId, course_id: courseIds },
|
|
attributes: ["course_id"],
|
|
});
|
|
certs.forEach((c) => completedIds.course.add(String(c.course_id)));
|
|
}
|
|
|
|
const unitIds = [...idsByType.unit];
|
|
if (unitIds.length) {
|
|
const rows = await UnitReadingProgress.findAll({
|
|
where: { user_id: userId, unit_id: unitIds, status: "completed" },
|
|
attributes: ["unit_id"],
|
|
});
|
|
rows.forEach((r) => completedIds.unit.add(String(r.unit_id)));
|
|
}
|
|
|
|
const lessonIds = [...idsByType.lesson];
|
|
if (lessonIds.length) {
|
|
const rows = await LessonReadingProgress.findAll({
|
|
where: { user_id: userId, lesson_id: lessonIds, status: "completed" },
|
|
attributes: ["lesson_id"],
|
|
});
|
|
rows.forEach((r) => completedIds.lesson.add(String(r.lesson_id)));
|
|
}
|
|
|
|
return prereqs.map((p) => ({
|
|
...p,
|
|
completed: completedIds[p.ref_type]?.has(String(p.ref_id)) ?? false,
|
|
}));
|
|
}
|
|
|
|
module.exports = { resolvePrerequisiteTitles, resolvePrerequisiteCompletion };
|