Backend Reminders
The pengingat scheduler — type-specific matching, timezone handling, and dispatch.
The reminder engine is the richest domain logic in the backend. A background goroutine (startPengingatScheduler) ticks every 20 seconds and calls pengingatService.CheckAndSendPengingatBySchedule().
Schedule matching by type
obat (medicine)
- Reads the
jadwalarray from JSONBdetails. - Compares each
jam_original(user's input time) against current time in the user's timezone, with a 30-second tolerance. - Tracks per-schedule send state via a
sent_schedulesmap indetailsso a single reminder row holds multiple daily dose times (e.g. 08:00 + 20:00) and fires each independently. - Marks
IsSent = trueonly when all schedules are sent.
makan (meal)
- Same pattern, tracks
matched_meal_schedule_key(meal_<idx>_<time>) and asent_meal_schedulesmap. - Each meal slot fires once; fully sent only when every schedule is consumed.
terapi (therapy)
- Matches both day of week (Indonesian names: Senin/Selasa/...) and
HH:mmtime against thejadwalarray. Weekly recurring.
kontrol (consultation)
- Matches a full datetime (
2006-01-02 15:04) — one-shot appointment reminder.
default
- Time-only match against
pengingat.Waktu.
Timezone handling
All times are stored as UTC in the database. User-supplied times are converted to UTC at create time using the timezone field (default Asia/Makassar, WITA). On send, the service converts back to the user's timezone for the message body. The jam_original field preserves the user's literal input for matching.
Dispatch
For each matched schedule, the service:
- Builds a
PengingatDTO. - Calls
notificationService.SendPengingatViaWhatsapp(WhatsApp). - Calls
pushService.SendReminderNotification(web push). - Persists updated
sent_schedules/IsSentback viarepo.Update.
WhatsApp message formatting
whatsapp/service.go SendPengingatViaWhatsapp constructs Bahasa Indonesia messages per type:
| Type | Message includes |
|---|---|
obat | "waktunya minum obat" + drug name |
terapi | tempat_terapi, jenis_terapi, time |
kontrol | doctor name, specialization, facility, date |
makan | suggested karbohidrat/protein/lemak/vitamin/serat menu |
| default | "Judul/Tanggal/Waktu" format |
Per-schedule state
The clever bit: instead of one row per dose time, a single pengingats row carries the whole day's schedule in JSONB and the scheduler mutates a sent_schedules map to remember which slots already fired. This keeps the reminder list compact and the "all done" check trivial.