erinnereFeiertage(now): stündlich, nur bei Schalter pilot/on (pilot: nur freigegebene Kunden) und nur 08:00 bis 20:00 Berliner Zeit; je Funktion in setup/active zuerst der Merker sha1(featureId|datum) je Tag (409 = schon erinnert), danach eine Mail K25 mit allen neuen Tagen ohne BCC ans Team; Merker älter als 30 Tage werden gelöscht. zeitenAboJob (täglich) und zeitenErinnerungsJob (stündlich) mit eigener Überlappsperre, ohne Wurf; in server/index.js nach 240 s bzw. 300 s gestartet, mit unref. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
3.2 KiB
JavaScript
62 lines
3.2 KiB
JavaScript
import { gleicheZeitenAboAb } from './aboAbgleich.js'
|
|
import { erinnereFeiertage } from './erinnerung.js'
|
|
|
|
/**
|
|
* Hintergrund-Jobs von Öffnungszeiten live (Spezifikation 2026-10-04-oeffnungszeiten-live-design.md, Abschnitte 5 und 7).
|
|
* server/index.js ruft sie mit new Date() auf; `now` ist hier immer ein Parameter.
|
|
*
|
|
* zeitenAboJob täglich (240 s nach dem Start beginnend) Abo-Abgleich (aboAbgleich.js): Abo-Ende erkennen, nach 30 Tagen
|
|
* abschalten, bei Rückkehr wieder einschalten, nach weiteren 30 Tagen
|
|
* löschen. Eigener Tagesjob, nicht an den Schalter ZEITEN_ENABLED
|
|
* gekoppelt (wie beim Terminplaner): ein beendetes Abo muss auch bei
|
|
* Schalter off zum Abschalten und zur Löschung führen. Ohne Zeiten-
|
|
* Dokumente kostet er eine Abfrage.
|
|
* zeitenErinnerungsJob stündlich (300 s nach dem Start beginnend) Feiertags-Erinnerung (erinnerung.js): nur bei Schalter pilot/on,
|
|
* nur 08:00 bis 20:00 Berliner Zeit, Merker älter als 30 Tage löschen
|
|
*
|
|
* Gemeinsame Regeln:
|
|
* - Keine überlappenden Läufe, je Job: läuft ein Job noch, kehrt der nächste Takt sofort mit { uebersprungen: true } zurück (einmal
|
|
* im Log je Überlappung). Die Sperre ist ein Merker im Speicher des Prozesses (ein Server, ein Prozess); die Jobs sperren einander nicht.
|
|
* - Ein Job wirft nie: ein unerwarteter Fehler (etwa ein ungültiges `now`) steht im Log, das Ergebnis ist { fehler: 1 }. Die Bausteine
|
|
* melden einzelne Fehler selbst im Ergebnis (`fehler`).
|
|
* - Das Log nennt weder Namen noch Adressen von Kunden.
|
|
*/
|
|
|
|
const laufend = new Set()
|
|
const ueberlappGemeldet = new Set()
|
|
|
|
const fehlerText = (err) => `${err?.name ?? 'Fehler'} ${String(err?.message ?? '').slice(0, 200)}`
|
|
|
|
/** Führt arbeit() aus, sofern dieser Job nicht schon läuft; fängt jeden Fehler. */
|
|
async function fuehreAus(job, arbeit) {
|
|
if (laufend.has(job)) {
|
|
if (!ueberlappGemeldet.has(job)) {
|
|
ueberlappGemeldet.add(job)
|
|
console.warn(`[zeiten-jobs] ${job}: der vorige Lauf ist noch nicht fertig, dieser Takt wird übersprungen`)
|
|
}
|
|
return { uebersprungen: true }
|
|
}
|
|
laufend.add(job)
|
|
try {
|
|
return await arbeit()
|
|
} catch (err) {
|
|
console.error(`[zeiten-jobs] ${job} fehlgeschlagen: ${fehlerText(err)}`)
|
|
return { fehler: 1 }
|
|
} finally {
|
|
laufend.delete(job)
|
|
ueberlappGemeldet.delete(job)
|
|
}
|
|
}
|
|
|
|
/** Täglicher Abo-Abgleich. => Ergebnis von gleicheZeitenAboAb | { uebersprungen: true } | { fehler: 1 } */
|
|
export const zeitenAboJob = (now) => fuehreAus('zeitenAboJob', () => gleicheZeitenAboAb(now))
|
|
|
|
/** Stündliche Feiertags-Erinnerung. => Ergebnis von erinnereFeiertage | { uebersprungen: true } | { fehler: 1 } */
|
|
export const zeitenErinnerungsJob = (now) => fuehreAus('zeitenErinnerungsJob', () => erinnereFeiertage(now))
|
|
|
|
/** Nur für Tests: alle Sperren freigeben. */
|
|
export function resetZeitenJobsForTests() {
|
|
laufend.clear()
|
|
ueberlappGemeldet.clear()
|
|
}
|