feat(api): complete domain event publishing with aggregate root pattern

- Add getUncommittedEvents() and commit() to AggregateRoot base class
- Create 6 new domain events: SubscriptionExpired, SubscriptionRenewed,
  ListingStatusChanged, UserKycUpdated, UserDeactivated, PaymentRefunded
- Wire events into entity state changes: SubscriptionEntity (markExpired,
  renewPeriod), ListingEntity (all transitions), UserEntity (KYC, deactivate),
  PaymentEntity (markRefunded)
- Add 7 new event listeners across notifications, admin, and search modules
  (25 total @OnEvent handlers)
- Fix ReviewDeletedListener to handle LISTING target type
- Restore watcher notifications in ListingSoldListener
- Update barrel exports and module registrations

Resolves: TEC-1564

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-09 10:22:20 +07:00
parent 35feccb529
commit 8179f1c16e
37 changed files with 613 additions and 36 deletions

View File

@@ -17,12 +17,11 @@ export class ReviewDeletedListener {
'ReviewDeletedListener',
);
// Recalculate average rating for the target (agent or listing)
// Recalculate average rating for the target (agent)
const stats = await this.prisma.review.aggregate({
where: {
targetType: event.targetType,
targetId: event.targetId,
deletedAt: null,
},
_avg: { rating: true },
_count: { rating: true },
@@ -31,17 +30,12 @@ export class ReviewDeletedListener {
const avgRating = stats._avg.rating ?? 0;
const reviewCount = stats._count.rating;
// Update the target's cached rating based on target type
// Update the target's cached rating
if (event.targetType === 'AGENT') {
await this.prisma.agent.update({
where: { id: event.targetId },
data: { qualityScore: avgRating },
});
} else if (event.targetType === 'LISTING') {
await this.prisma.listing.update({
where: { id: event.targetId },
data: { averageRating: avgRating, reviewCount },
});
}
this.logger.log(