feat(analytics): integrate AI/ML services — AVM endpoint, moderation pipeline, market index cron

- Add AiServiceClient HTTP client for Python FastAPI AI service with timeout and fallback
- Add HttpAVMService that calls Python AVM endpoint, falls back to PrismaAVMService on failure
- Add ListingCreatedModerationHandler: auto-flags suspicious listings via AI moderation on create
- Add MarketIndexCronService: daily cron job aggregating market stats per district/city/type
- Wire ScheduleModule and new providers into AnalyticsModule and AppModule
- Add unit tests for AiServiceClient, HttpAVMService, and moderation handler (all passing)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-09 10:13:06 +07:00
parent d64bbe97e2
commit 35feccb529
13 changed files with 1436 additions and 8 deletions

View File

@@ -3,6 +3,7 @@ import { CqrsModule } from '@nestjs/cqrs';
import { GenerateReportHandler } from './application/commands/generate-report/generate-report.handler';
import { TrackEventHandler } from './application/commands/track-event/track-event.handler';
import { UpdateMarketIndexHandler } from './application/commands/update-market-index/update-market-index.handler';
import { ListingCreatedModerationHandler } from './application/event-handlers/listing-created-moderation.handler';
import { GetDistrictStatsHandler } from './application/queries/get-district-stats/get-district-stats.handler';
import { GetHeatmapHandler } from './application/queries/get-heatmap/get-heatmap.handler';
import { GetMarketReportHandler } from './application/queries/get-market-report/get-market-report.handler';
@@ -13,6 +14,9 @@ import { VALUATION_REPOSITORY } from './domain/repositories/valuation.repository
import { AVM_SERVICE } from './domain/services/avm-service';
import { PrismaMarketIndexRepository } from './infrastructure/repositories/prisma-market-index.repository';
import { PrismaValuationRepository } from './infrastructure/repositories/prisma-valuation.repository';
import { AI_SERVICE_CLIENT, AiServiceClient } from './infrastructure/services/ai-service.client';
import { HttpAVMService } from './infrastructure/services/http-avm.service';
import { MarketIndexCronService } from './infrastructure/services/market-index-cron.service';
import { PrismaAVMService } from './infrastructure/services/prisma-avm.service';
import { AnalyticsController } from './presentation/controllers/analytics.controller';
@@ -30,19 +34,33 @@ const QueryHandlers = [
GetValuationHandler,
];
const EventHandlers = [
ListingCreatedModerationHandler,
];
@Module({
imports: [CqrsModule],
controllers: [AnalyticsController],
providers: [
// AI service client
{ provide: AI_SERVICE_CLIENT, useClass: AiServiceClient },
// Repositories
{ provide: MARKET_INDEX_REPOSITORY, useClass: PrismaMarketIndexRepository },
{ provide: VALUATION_REPOSITORY, useClass: PrismaValuationRepository },
{ provide: AVM_SERVICE, useClass: PrismaAVMService },
// AVM: HttpAVMService calls Python AI first, falls back to PrismaAVMService
PrismaAVMService,
{ provide: AVM_SERVICE, useClass: HttpAVMService },
// Cron
MarketIndexCronService,
// CQRS
...CommandHandlers,
...QueryHandlers,
...EventHandlers,
],
exports: [MARKET_INDEX_REPOSITORY, VALUATION_REPOSITORY, AVM_SERVICE],
exports: [MARKET_INDEX_REPOSITORY, VALUATION_REPOSITORY, AVM_SERVICE, AI_SERVICE_CLIENT],
})
export class AnalyticsModule {}