- Move 8 stray .md (+5 .txt) from ~/Desktop into docs/explorations/from-desktop/ - Reorganize 27 .md/.txt at workspace root: - audit reports -> docs/audits/ - exploration reports -> docs/explorations/ - design system -> docs/design-system/ - Keep only README/CHANGELOG/CONTRIBUTING/CLAUDE at repo root - Refresh docs/README.md as canonical index with links to all groups - Note: pre-existing docs/audits/AUDIT_INDEX.md and AUDIT_SUMMARY.md were overwritten by the newer root-level versions during the move Co-Authored-By: Paperclip <noreply@paperclip.ing>
10 KiB
Backend API Audit: Trading Exchange UI Refactor
Prepared for TechLead | Gap analysis for sàn giao dịch-style dashboard
Audit date: 2026-04-21
Executive Summary
Backend has strong coverage for analytics, listings, and agent profiles. Critical gaps exist for real-time market tickers, recent-updates feeds, and aggregated district-level indices needed for the home dashboard and listings board.
Available Endpoints (Ready for FE)
Listings Module
📁 apps/api/src/modules/listings/presentation/controllers/listings.controller.ts
Route prefix: /listings
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | / |
Search/filter listings (pagination, sort) | Public |
| GET | /:id |
Listing detail (full property data, seller, agent) | Public |
| GET | /:id/price-history |
Price change history (chart data) | Public |
| GET | /:id/qr-code |
Generate QR code PNG | Public |
| POST | / |
Create listing | JWT + quota |
| PATCH | /:id |
Update listing (price, description, amenities) | JWT + owner |
| PATCH | /:id/status |
Update status (DRAFT→ACTIVE, etc.) | JWT + owner |
| POST | /:id/media |
Upload photo/video | JWT + owner |
| POST | /:id/feature |
Feature listing (payment gateway) | JWT + rate limit |
| POST | /:id/promote |
Promote featured (subscription quota) | JWT + quota |
| POST | bulk-update |
Bulk patch price/status/featured (≤100 items) | JWT + owner |
| GET | /pending |
Get moderation queue (admin) | ADMIN |
| GET | /duplicates |
Find duplicate listings by geo (admin) | ADMIN |
| PATCH | /:id/moderate |
Moderate listing (admin) | ADMIN |
| DELETE | /:id |
Delete listing | JWT + owner |
Analytics Module
📁 apps/api/src/modules/analytics/presentation/controllers/analytics.controller.ts
Route prefix: /analytics
| Method | Path | Description | Auth | Quota |
|---|---|---|---|---|
| GET | /market-report |
Market report by city/district (median price, inventory, absorption) | JWT | Yes |
| GET | /price-trend |
Price trend time-series by district | JWT | Yes |
| GET | /heatmap |
Price heatmap by city (grid: avg_m2, counts) | JWT | Yes |
| GET | /district-stats |
Statistics by district (city-level aggregates) | JWT | Yes |
| GET | /valuation |
AVM estimate (by property ID or coords) | JWT | Yes |
| POST | /valuation |
AVM with custom form data (v1/v2 ensemble) | JWT | Yes |
| POST | /valuation/batch |
Batch AVM (≤50 properties) | JWT | Yes |
| GET | /valuation/history/:propertyId |
Valuation time-series (chart) | JWT | Yes |
| POST | /valuation/compare |
Compare valuations (2–5 properties) | JWT | Yes |
| GET | /neighborhoods/:district/score |
Neighborhood quality score | Public | No |
Note: All analytics queries return cached data (TTL varies by type).
Search Module
📁 apps/api/src/modules/search/presentation/controllers/search.controller.ts
Route prefix: /search
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | / |
Full-text & faceted property search | Public |
| GET | /geo |
Geographic radius search (lat, lng, radiusKm) | Public |
| POST | /reindex |
Trigger full-text reindex (admin) | ADMIN |
Agents Module
📁 apps/api/src/modules/agents/presentation/controllers/agents.controller.ts
Route prefix: /agents
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /:agentId/profile |
Public agent profile (name, quality score, listings count, reviews) | Public |
| GET | /me/dashboard |
Agent dashboard stats (my listings, inquiries, quality metrics) | JWT (AGENT role) |
| POST | /me/upgrade |
Upgrade user account to agent | JWT |
| POST | /:agentId/recalculate-score |
Recalc quality score (admin) | ADMIN |
Admin Module
📁 apps/api/src/modules/admin/presentation/controllers/admin-moderation.controller.ts + admin.controller.ts
Route prefix: /admin
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /moderation |
Moderation queue (pending listings) | ADMIN |
| POST | /moderation/approve |
Approve listing | ADMIN |
| POST | /moderation/reject |
Reject listing with reason | ADMIN |
| POST | /moderation/bulk |
Bulk approve/reject | ADMIN |
| GET | /moderation/audit-logs |
Moderation audit trail | ADMIN |
| GET | /kyc |
KYC approval queue | ADMIN |
| POST | /kyc/approve |
Approve KYC | ADMIN |
| POST | /kyc/reject |
Reject KYC | ADMIN |
| GET | /users |
List all users (paginated, filters) | ADMIN |
| GET | /users/:id |
Get user details | ADMIN |
| POST | /users/ban |
Ban user | ADMIN |
| POST | /subscriptions/adjust |
Adjust user subscription | ADMIN |
| GET | /dashboard |
Admin dashboard stats | ADMIN |
| GET | /revenue |
Revenue analytics | ADMIN |
| GET | /audit-logs |
General audit logs | ADMIN |
Reviews Module
📁 apps/api/src/modules/reviews/presentation/controllers/reviews.controller.ts
Route prefix: /reviews
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | / |
List reviews for target (agent, listing, user) | Public |
| GET | /stats |
Aggregate rating stats (avg, distribution) | Public |
| GET | /me |
User's own reviews | JWT |
| POST | / |
Create review | JWT |
Critical Gaps (Missing or Insufficient)
🔴 1. Market Overview Ticker — NOT PRESENT
UI Need: Home dashboard ticker with price changes, recent listings, trending areas
Gap: No endpoint for:
- Recent listings (last 24h, last 7d) with timestamp
- Trending areas (by inquiry/view volume)
- Price change summary (top gainers/losers by district)
- Active listings count by type
Solution: GET /listings/recent?limit=10&hours=24 + GET /analytics/trending-areas?period=7d
🔴 2. Real-Time Listing Updates — NOT PRESENT
UI Need: Listings board shows "just posted," "price dropped," "featured" badges
Gap:
- Search doesn't sort by
publishedAt - No webhook/SSE for status changes
- No delta updates since last refresh
Solution: Add sortBy=publishedAt to search + GET /listings?newSince=TIMESTAMP
🔴 3. Similar Listings / Comparables — NOT PRESENT
UI Need: Listing detail shows 5–10 comparable properties
Gap: No endpoint; search is generic, not contextual
Solution: GET /listings/:id/similar?limit=5 (same district, ±10% price, same type)
🔴 4. Market Indicators: Ward-Level Data — PARTIAL
Available: District-level market report, heatmap
Missing: Ward (phường)-level price trends, listing volume by ward
Solution: GET /analytics/heatmap?level=ward + GET /analytics/listing-volume?ward=X
🔴 5. Listing Detail: Enrichment Missing
Current: Basic property, seller, agent, media
Missing:
valuationEstimate(AVM not included)inquiryCount(exists but not exposed?)agentQualityScore(denormalized from agent profile)- Similar listings reference
🔴 6. Market Snapshot (Live Indicators) — NOT PRESENT
UI Need: Home dashboard tiles with total active listings, avg price, price change %
Gap: No single endpoint; requires multiple calls
Solution: GET /analytics/market-snapshot?city=HCMC returns { activeCount, avgPrice, medianPrice, priceChange%, inventoryM2, daysOnMarket }
🟡 7. Trending Areas / Hot Markets — NOT PRESENT
UI Need: "Top 10 areas by inquiry volume" for home dashboard heatmap
Gap: No aggregation by inquiry/view counts per district
Solution: GET /analytics/trending-areas?period=7d&limit=10 (sorted by inquiries/views)
🟡 8. Price Movers (Gainers/Losers) — NOT PRESENT
UI Need: "Top 5 price drops" / "Top 5 price increases" for exchange ticker
Gap: No endpoint; requires post-processing of market report data
Solution: GET /analytics/price-movers?direction=up|down&limit=5 aggregated by district
🟡 9. Market History (Time-Series Trends) — NOT PRESENT
UI Need: Analytics page showing 12-month price, volume, absorption trends
Gap: Market report is current snapshot only
Solution: GET /analytics/market-history?city=HCMC&period=12m returns monthly snapshots
🟡 10. Cache Metadata in Analytics Responses — MISSING
Issue: No transparency on data freshness
Gap: Endpoints don't expose cachedAt, nextRefreshAt, or data age
Solution: Add metadata fields to all analytics responses
Summary Table
| Feature | Status | Severity | Module |
|---|---|---|---|
| Listing search & filter | ✅ | — | listings |
| Listing detail | ✅ | — | listings |
| Price history | ✅ | — | listings |
| Market report | ✅ | — | analytics |
| Heatmap | ✅ | — | analytics |
| AVM/Valuation | ✅ | Partial | analytics |
| Agent profile | ✅ | Minor | agents |
| Admin moderation | ✅ | — | admin |
| Recent listings ticker | ❌ | 🔴 High | listings |
| Market snapshot | ❌ | 🔴 High | analytics |
| Trending areas | ❌ | 🔴 High | analytics |
| Similar listings | ❌ | 🔴 High | listings |
| Real-time updates | ❌ | 🟡 Medium | listings |
| Price movers | ❌ | 🟡 Medium | analytics |
| Market history | ❌ | 🟡 Medium | analytics |
| Ward-level heatmap | ❌ | 🟡 Medium | analytics |
| Cache metadata | ❌ | 🟡 Medium | analytics |
Recommendations
Phase 1: Sprint 1–2 (High Priority)
GET /listings?sortBy=publishedAt&limit=20— Recent listings firstGET /analytics/market-snapshot?city=HCMC— Live indicators (total, avg, median)GET /analytics/trending-areas?period=7d— Top areas by activityGET /listings/:id/similar?limit=5— Comparable properties- Enhance listing detail response with
valuationEstimate,inquiryCount,agentScore
Phase 2: Sprint 3 (Medium Priority)
GET /analytics/price-movers— Gainers/losersGET /analytics/market-history?period=12m— Trends for charts- Add cache metadata to all analytics endpoints
Phase 3: Sprint 4+ (Polish)
- Real-time updates (WebSocket/SSE)
- Ward-level drill-down for heatmap
- Most-saved listings for admin analytics
Total Endpoints Reviewed: 70+
Ready for FE: 58
Critical Gaps: 10
Easy Wins: ~5 queries over 2–3 sprints