mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
perform test
test to courses Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
/***********************************************************************************************************************************************************************
|
||||
* File Name : issue_certificates.cron.js
|
||||
* Type : Cron Job
|
||||
* Description : Issues certificates for users who passed a course assessment
|
||||
* 45 minutes ago. Runs every 5 minutes and processes any
|
||||
* pending_certificates row where issue_at <= NOW() and
|
||||
* processed_at IS NULL.
|
||||
*
|
||||
* For each ready row it:
|
||||
* 1. Grants the course_completed_<uuid> achievement (the key
|
||||
* MyCertificates / Profile use to display certificate cards).
|
||||
* 2. Sends a 'certificate_issued' UserNotification.
|
||||
* 3. Marks the row processed_at = NOW() so it never fires again.
|
||||
*
|
||||
* Safety pattern: processed_at is set only after both step 1 and
|
||||
* step 2 succeed. If the process restarts mid-run the row will be
|
||||
* picked up again on the next tick — both DB writes are idempotent.
|
||||
*
|
||||
* Schedule : Every hour at minute 5 ("5 * * * *"). Registered by
|
||||
* cron/client.cron.js.
|
||||
*
|
||||
* Author: Kenneth Obsequio (@lash0000)
|
||||
* Date Created: Jun. 24, 2026
|
||||
***********************************************************************************************************************************************************************/
|
||||
'use strict';
|
||||
|
||||
const { Op } = require('sequelize');
|
||||
const PendingCertificate = require('../../models/courses/pending_certificate.mdl');
|
||||
const mdl_Achievements = require('../../models/users/achievements.mdl');
|
||||
const UserNotification = require('../../models/notifications/user_notification.mdl');
|
||||
const { NOTIFICATION_REGISTRY } = require('../../data/notifications.data');
|
||||
|
||||
const DELAY_MS = 5 * 60 * 1000; // 5 minutes
|
||||
|
||||
async function run() {
|
||||
// ── 1. Fetch all rows ready to process ────────────────────────────────────
|
||||
let rows;
|
||||
try {
|
||||
rows = await PendingCertificate.findAll({
|
||||
where: {
|
||||
issue_at: { [Op.lte]: new Date() },
|
||||
processed_at: null,
|
||||
},
|
||||
raw: true,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error('[CRON][ISSUE CERTS] Failed to query pending_certificates:', err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rows.length === 0) return;
|
||||
|
||||
console.log(`[CRON][ISSUE CERTS] Processing ${rows.length} pending certificate(s).`);
|
||||
|
||||
for (const row of rows) {
|
||||
const { pending_id, user_id, course_uuid, course_title } = row;
|
||||
const achKey = `course_completed_${course_uuid}`;
|
||||
|
||||
try {
|
||||
// ── 2. Grant course_completed_<uuid> achievement (idempotent) ──────
|
||||
const existing = await mdl_Achievements.findOne({ where: { user_id, key: achKey } });
|
||||
if (!existing) {
|
||||
await mdl_Achievements.create({
|
||||
user_id,
|
||||
type: 'milestone',
|
||||
key: achKey,
|
||||
label: 'Certificate of Completion',
|
||||
description: course_title ?? '',
|
||||
granted_at: new Date(),
|
||||
metadata: { courseTitle: course_title, courseUuid: course_uuid },
|
||||
});
|
||||
}
|
||||
|
||||
// ── 3. Send certificate_issued notification ────────────────────────
|
||||
await UserNotification.create({
|
||||
user_id,
|
||||
...NOTIFICATION_REGISTRY.certificate_issued.build({
|
||||
courseTitle: course_title ?? '',
|
||||
courseUuid: course_uuid,
|
||||
}),
|
||||
});
|
||||
|
||||
// ── 4. Mark row processed ─────────────────────────────────────────
|
||||
await PendingCertificate.update(
|
||||
{ processed_at: new Date() },
|
||||
{ where: { pending_id } }
|
||||
);
|
||||
|
||||
console.log(`[CRON][ISSUE CERTS] Issued certificate for user ${user_id} / course ${course_uuid}.`);
|
||||
} catch (err) {
|
||||
// Log and continue — next tick will retry this row
|
||||
if (err?.parent?.code !== '23505') {
|
||||
console.error(`[CRON][ISSUE CERTS] Failed for pending_id ${pending_id}:`, err);
|
||||
} else {
|
||||
// Unique constraint: achievement already exists — still mark processed
|
||||
await PendingCertificate.update(
|
||||
{ processed_at: new Date() },
|
||||
{ where: { pending_id } }
|
||||
).catch(() => {});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
name: 'issueCertificates',
|
||||
schedule: '5 * * * *',
|
||||
run,
|
||||
};
|
||||
Reference in New Issue
Block a user