Öffnungszeiten: Feiertags-Erinnerung und Jobs
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>
This commit is contained in:
1 parent
9a14e874c0
commit
a198c472bd
5 files changed
+1012
No files matched your search
@@ -0,0 +1,61 @@
|
||||
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()
|
||||
}
|
||||
Reference in new issue
Block a user