feat(analytics): add NeighborhoodScoreService with POI-based scoring and API endpoint

- Create INeighborhoodScoreService interface and implementation
- Score districts 0-100 across 6 categories: education, healthcare, transport, shopping, greenery, safety
- Calculate scores from POI data with configurable weights and max counts
- Add GetNeighborhoodScoreQuery handler with lazy calculation
- Add GET /analytics/neighborhoods/:district/score endpoint
- Wire service and handler into AnalyticsModule

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Ho Ngoc Hai
2026-04-16 05:21:28 +07:00
parent 5db3dfbda6
commit 30d3039b94
6 changed files with 214 additions and 0 deletions

View File

@@ -26,6 +26,8 @@ import { type ValuationDto } from '../../application/queries/get-valuation/get-v
import { GetValuationQuery } from '../../application/queries/get-valuation/get-valuation.query';
import { type ValuationComparisonDto as ValuationComparisonResultDto } from '../../application/queries/valuation-comparison/valuation-comparison.handler';
import { ValuationComparisonQuery } from '../../application/queries/valuation-comparison/valuation-comparison.query';
import { type NeighborhoodScoreResult } from '../../domain/services/neighborhood-score.service';
import { GetNeighborhoodScoreQuery } from '../../application/queries/get-neighborhood-score/get-neighborhood-score.query';
import { type ValuationHistoryDto as ValuationHistoryResultDto } from '../../application/queries/valuation-history/valuation-history.handler';
import { ValuationHistoryQuery } from '../../application/queries/valuation-history/valuation-history.query';
import { type BatchValuationDto } from '../dto/batch-valuation.dto';
@@ -158,4 +160,17 @@ export class AnalyticsController {
new ValuationComparisonQuery(dto.propertyIds),
);
}
@ApiOperation({ summary: 'Get neighborhood score for a district' })
@ApiParam({ name: 'district', description: 'District name', example: 'Quận 1' })
@ApiResponse({ status: 200, description: 'Neighborhood score retrieved' })
@Get('neighborhoods/:district/score')
async getNeighborhoodScore(
@Param('district') district: string,
@Query('city') city: string = 'Hồ Chí Minh',
): Promise<NeighborhoodScoreResult> {
return this.queryBus.execute(
new GetNeighborhoodScoreQuery(district, city),
);
}
}