feat(listings): add 3-day listing expiry warning notification (GOO-30)

- Add expiryNotifiedAt column to Listing (migration 20260423100000);
  atomic UPDATE…RETURNING guards against duplicate notifications across
  concurrent cron instances
- Add ListingExpiringEvent domain event (listing.expiring)
- Add ListingExpiryCronService: daily cron at 01:00 UTC; marks
  expiryNotifiedAt before publishing events (idempotent)
- Add ListingExpiringListener: sends EMAIL + Zalo OA via
  SendNotificationCommand with daysRemaining context
- Add listing.expiring Handlebars template (Vietnamese)
- Wire cron into ListingsModule, listener into NotificationsModule
- Update template.service spec: 17 → 19 keys (listing.expiring + the
  pre-existing user.phone_login_otp that was missing from assertion)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-23 00:16:46 +07:00
parent 4be5eb90a4
commit 94d462ef4f
11 changed files with 334 additions and 5 deletions

View File

@@ -21,4 +21,5 @@ export { ListingSoldEvent } from './domain/events/listing-sold.event';
export { ListingStatusChangedEvent } from './domain/events/listing-status-changed.event';
export { ListingOwnershipTransferredEvent } from './domain/events/listing-ownership-transferred.event';
export { ListingFeaturedExpiredEvent } from './domain/events/listing-featured-expired.event';
export { ListingExpiringEvent } from './domain/events/listing-expiring.event';
export { Price } from './domain/value-objects/price.vo';