chore: update project documentation, audit reports, and initialize IDE configuration files
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 29s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 2m42s
Deploy / Build Web Image (push) Failing after 27s
Deploy / Build AI Services Image (push) Failing after 29s
E2E Tests / Playwright E2E (push) Failing after 43s
Deploy / Build API Image (push) Failing after 1m31s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 6s
Security Scanning / Trivy Scan — API Image (push) Failing after 5m35s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 3m45s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Deploy / Rollback Staging (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
Security Scanning / Trivy Scan — Web Image (push) Failing after 13m51s
Security Scanning / Trivy Filesystem Scan (push) Failing after 14m46s
Security Scanning / Security Gate (push) Has been cancelled
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 29s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 2m42s
Deploy / Build Web Image (push) Failing after 27s
Deploy / Build AI Services Image (push) Failing after 29s
E2E Tests / Playwright E2E (push) Failing after 43s
Deploy / Build API Image (push) Failing after 1m31s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 6s
Security Scanning / Trivy Scan — API Image (push) Failing after 5m35s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 3m45s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Deploy / Rollback Staging (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
Security Scanning / Trivy Scan — Web Image (push) Failing after 13m51s
Security Scanning / Trivy Filesystem Scan (push) Failing after 14m46s
Security Scanning / Security Gate (push) Has been cancelled
This commit is contained in:
@@ -1,21 +1,21 @@
|
||||
# Inquiries Module - Complete File Index
|
||||
# Inquiries Module - Chỉ Mục File Đầy Đủ
|
||||
|
||||
**Generated:** April 11, 2026
|
||||
**Tạo lúc:** Ngày 11 tháng 4 năm 2026
|
||||
**Module:** `apps/api/src/modules/inquiries/`
|
||||
**Total Files:** 25
|
||||
**Tổng số file:** 25
|
||||
|
||||
---
|
||||
|
||||
## 📋 COMPLETE FILE LISTING BY PATH
|
||||
## 📋 DANH SÁCH FILE ĐẦY ĐỦ THEO ĐƯỜNG DẪN
|
||||
|
||||
### **1. APPLICATION LAYER**
|
||||
### **1. TẦNG ỨNG DỤNG (APPLICATION LAYER)**
|
||||
|
||||
#### Commands (4 files)
|
||||
|
||||
```
|
||||
📄 apps/api/src/modules/inquiries/application/commands/create-inquiry/create-inquiry.command.ts
|
||||
Type: Command (CQRS)
|
||||
Purpose: Input DTO for create inquiry use case
|
||||
Purpose: Input DTO cho use case tạo yêu cầu tư vấn
|
||||
Exports: CreateInquiryCommand class
|
||||
Properties: userId, listingId, message, phone
|
||||
Lines: ~8
|
||||
@@ -24,14 +24,14 @@
|
||||
```
|
||||
📄 apps/api/src/modules/inquiries/application/commands/create-inquiry/create-inquiry.handler.ts
|
||||
Type: Command Handler (CQRS)
|
||||
Purpose: Orchestrates inquiry creation
|
||||
Purpose: Điều phối quá trình tạo yêu cầu tư vấn
|
||||
Implements: ICommandHandler<CreateInquiryCommand>
|
||||
Returns: CreateInquiryResult { id, listingId, createdAt }
|
||||
Key Logic:
|
||||
• Validate listing exists (Prisma.listing.findUnique)
|
||||
• Create InquiryEntity via factory method
|
||||
• Save to repository
|
||||
• Publish InquiryCreatedEvent via EventBus
|
||||
• Xác thực tin đăng tồn tại (Prisma.listing.findUnique)
|
||||
• Tạo InquiryEntity qua phương thức factory
|
||||
• Lưu vào repository
|
||||
• Phát sự kiện InquiryCreatedEvent qua EventBus
|
||||
Lines: ~60
|
||||
Dependencies:
|
||||
• INQUIRY_REPOSITORY (injected)
|
||||
@@ -43,7 +43,7 @@
|
||||
```
|
||||
📄 apps/api/src/modules/inquiries/application/commands/mark-inquiry-read/mark-inquiry-read.command.ts
|
||||
Type: Command (CQRS)
|
||||
Purpose: Input DTO for mark inquiry as read
|
||||
Purpose: Input DTO để đánh dấu yêu cầu tư vấn đã đọc
|
||||
Exports: MarkInquiryReadCommand class
|
||||
Properties: inquiryId, agentUserId
|
||||
Lines: ~6
|
||||
@@ -52,20 +52,20 @@
|
||||
```
|
||||
📄 apps/api/src/modules/inquiries/application/commands/mark-inquiry-read/mark-inquiry-read.handler.ts
|
||||
Type: Command Handler (CQRS)
|
||||
Purpose: Orchestrates mark as read operation
|
||||
Purpose: Điều phối thao tác đánh dấu đã đọc
|
||||
Implements: ICommandHandler<MarkInquiryReadCommand>
|
||||
Returns: void (no return value)
|
||||
Returns: void (không có giá trị trả về)
|
||||
Key Logic:
|
||||
• Load inquiry entity from repository
|
||||
• Load listing and verify agent ownership
|
||||
• Call inquiry.markAsRead() to update state
|
||||
• Persist via repository.markAsRead()
|
||||
• Publish InquiryReadEvent via EventBus
|
||||
• Tải entity yêu cầu tư vấn từ repository
|
||||
• Tải tin đăng và xác minh quyền sở hữu của môi giới
|
||||
• Gọi inquiry.markAsRead() để cập nhật trạng thái
|
||||
• Lưu qua repository.markAsRead()
|
||||
• Phát sự kiện InquiryReadEvent qua EventBus
|
||||
Authorization Checks:
|
||||
• Inquiry exists
|
||||
• Listing exists
|
||||
• User is registered as agent
|
||||
• Agent ID matches listing.agentId
|
||||
• Yêu cầu tư vấn tồn tại
|
||||
• Tin đăng tồn tại
|
||||
• Người dùng đã đăng ký làm môi giới
|
||||
• ID môi giới khớp với listing.agentId
|
||||
Lines: ~50
|
||||
Dependencies:
|
||||
• INQUIRY_REPOSITORY (injected)
|
||||
@@ -79,7 +79,7 @@
|
||||
```
|
||||
📄 apps/api/src/modules/inquiries/application/queries/get-inquiries-by-agent/get-inquiries-by-agent.query.ts
|
||||
Type: Query (CQRS)
|
||||
Purpose: Input DTO for listing agent's inquiries
|
||||
Purpose: Input DTO để lấy danh sách yêu cầu tư vấn của môi giới
|
||||
Exports: GetInquiriesByAgentQuery class
|
||||
Properties: agentUserId, page, limit
|
||||
Lines: ~6
|
||||
@@ -88,13 +88,13 @@
|
||||
```
|
||||
📄 apps/api/src/modules/inquiries/application/queries/get-inquiries-by-agent/get-inquiries-by-agent.handler.ts
|
||||
Type: Query Handler (CQRS)
|
||||
Purpose: Resolves paginated inquiries for an agent
|
||||
Purpose: Phân giải danh sách yêu cầu tư vấn phân trang cho một môi giới
|
||||
Implements: IQueryHandler<GetInquiriesByAgentQuery>
|
||||
Returns: PaginatedResult<InquiryReadDto>
|
||||
Key Logic:
|
||||
• Resolve agent ID from userId via Prisma
|
||||
• Throw NotFoundException if user not an agent
|
||||
• Delegate to repository.findByAgent()
|
||||
• Phân giải ID môi giới từ userId qua Prisma
|
||||
• Ném NotFoundException nếu người dùng không phải môi giới
|
||||
• Ủy quyền cho repository.findByAgent()
|
||||
Lines: ~30
|
||||
Dependencies:
|
||||
• INQUIRY_REPOSITORY (injected)
|
||||
@@ -104,7 +104,7 @@
|
||||
```
|
||||
📄 apps/api/src/modules/inquiries/application/queries/get-inquiries-by-listing/get-inquiries-by-listing.query.ts
|
||||
Type: Query (CQRS)
|
||||
Purpose: Input DTO for listing inquiries
|
||||
Purpose: Input DTO để lấy danh sách yêu cầu tư vấn của tin đăng
|
||||
Exports: GetInquiriesByListingQuery class
|
||||
Properties: listingId, page, limit
|
||||
Lines: ~6
|
||||
@@ -113,11 +113,11 @@
|
||||
```
|
||||
📄 apps/api/src/modules/inquiries/application/queries/get-inquiries-by-listing/get-inquiries-by-listing.handler.ts
|
||||
Type: Query Handler (CQRS)
|
||||
Purpose: Resolves paginated inquiries for a listing
|
||||
Purpose: Phân giải danh sách yêu cầu tư vấn phân trang cho một tin đăng
|
||||
Implements: IQueryHandler<GetInquiriesByListingQuery>
|
||||
Returns: PaginatedResult<InquiryReadDto>
|
||||
Key Logic:
|
||||
• Direct delegation to repository.findByListing()
|
||||
• Ủy quyền trực tiếp cho repository.findByListing()
|
||||
Lines: ~20
|
||||
Dependencies:
|
||||
• INQUIRY_REPOSITORY (injected)
|
||||
@@ -128,12 +128,12 @@
|
||||
```
|
||||
📄 apps/api/src/modules/inquiries/application/__tests__/create-inquiry.handler.spec.ts
|
||||
Type: Unit Tests (Jest/Vitest)
|
||||
Test Framework: Vitest with Mock functions (vi.fn)
|
||||
Test Framework: Vitest với hàm Mock (vi.fn)
|
||||
Test Count: 4
|
||||
Tests:
|
||||
✓ creates an inquiry successfully
|
||||
✓ throws NotFoundException when listing not found
|
||||
✓ publishes domain events after saving
|
||||
✓ tạo yêu cầu tư vấn thành công
|
||||
✓ ném NotFoundException khi không tìm thấy tin đăng
|
||||
✓ phát sự kiện domain sau khi lưu
|
||||
Mocks:
|
||||
• mockInquiryRepo (IInquiryRepository implementation)
|
||||
• mockEventBus
|
||||
@@ -146,11 +146,11 @@
|
||||
Type: Unit Tests (Jest/Vitest)
|
||||
Test Count: 5
|
||||
Tests:
|
||||
✓ marks an inquiry as read successfully
|
||||
✓ throws NotFoundException when inquiry not found
|
||||
✓ throws NotFoundException when listing not found
|
||||
✓ throws ForbiddenException when user is not the listing agent
|
||||
✓ throws ForbiddenException when agent not found for user
|
||||
✓ đánh dấu yêu cầu tư vấn là đã đọc thành công
|
||||
✓ ném NotFoundException khi không tìm thấy yêu cầu tư vấn
|
||||
✓ ném NotFoundException khi không tìm thấy tin đăng
|
||||
✓ ném ForbiddenException khi người dùng không phải môi giới của tin đăng
|
||||
✓ ném ForbiddenException khi không tìm thấy môi giới cho người dùng
|
||||
Mocks:
|
||||
• mockInquiryRepo
|
||||
• mockEventBus
|
||||
@@ -164,8 +164,8 @@
|
||||
Type: Unit Tests (Jest/Vitest)
|
||||
Test Count: 2
|
||||
Tests:
|
||||
✓ returns paginated results
|
||||
✓ returns empty data when no inquiries found
|
||||
✓ trả về kết quả phân trang
|
||||
✓ trả về dữ liệu rỗng khi không tìm thấy yêu cầu tư vấn nào
|
||||
Mocks:
|
||||
• mockInquiryRepo.findByListing
|
||||
Lines: ~69
|
||||
@@ -176,8 +176,8 @@
|
||||
Type: Unit Tests (Jest/Vitest)
|
||||
Test Count: 2
|
||||
Tests:
|
||||
✓ returns paginated results
|
||||
✓ throws NotFoundException when agent not found for user
|
||||
✓ trả về kết quả phân trang
|
||||
✓ ném NotFoundException khi không tìm thấy môi giới cho người dùng
|
||||
Mocks:
|
||||
• mockInquiryRepo.findByAgent
|
||||
• mockPrisma.agent.findUnique
|
||||
@@ -186,7 +186,7 @@
|
||||
|
||||
---
|
||||
|
||||
### **2. DOMAIN LAYER**
|
||||
### **2. TẦNG DOMAIN (DOMAIN LAYER)**
|
||||
|
||||
#### Entities (1 file)
|
||||
|
||||
@@ -194,7 +194,7 @@
|
||||
📄 apps/api/src/modules/inquiries/domain/entities/inquiry.entity.ts
|
||||
Type: Aggregate Root (DDD)
|
||||
Extends: AggregateRoot<string>
|
||||
Purpose: Core business logic for inquiries
|
||||
Purpose: Logic nghiệp vụ cốt lõi cho yêu cầu tư vấn
|
||||
|
||||
Properties (Private):
|
||||
• _listingId: string
|
||||
@@ -212,14 +212,14 @@
|
||||
|
||||
Factory Method:
|
||||
• static createNew(id, listingId, userId, message, phone): InquiryEntity
|
||||
- Creates new inquiry with isRead=false
|
||||
- Emits InquiryCreatedEvent
|
||||
- Returns entity instance
|
||||
- Tạo yêu cầu tư vấn mới với isRead=false
|
||||
- Phát InquiryCreatedEvent
|
||||
- Trả về instance của entity
|
||||
|
||||
Domain Methods:
|
||||
• markAsRead(): void
|
||||
- Sets _isRead to true
|
||||
- Emits InquiryReadEvent
|
||||
- Đặt _isRead thành true
|
||||
- Phát InquiryReadEvent
|
||||
|
||||
Lines: ~63
|
||||
Dependencies:
|
||||
@@ -234,7 +234,7 @@
|
||||
📄 apps/api/src/modules/inquiries/domain/events/inquiry-created.event.ts
|
||||
Type: Domain Event (DDD)
|
||||
Implements: DomainEvent interface
|
||||
Purpose: Signals that an inquiry was created
|
||||
Purpose: Báo hiệu rằng một yêu cầu tư vấn đã được tạo
|
||||
|
||||
Properties:
|
||||
• eventName: string = 'inquiry.created'
|
||||
@@ -250,7 +250,7 @@
|
||||
📄 apps/api/src/modules/inquiries/domain/events/inquiry-read.event.ts
|
||||
Type: Domain Event (DDD)
|
||||
Implements: DomainEvent interface
|
||||
Purpose: Signals that an inquiry was marked as read
|
||||
Purpose: Báo hiệu rằng một yêu cầu tư vấn đã được đánh dấu là đã đọc
|
||||
|
||||
Properties:
|
||||
• eventName: string = 'inquiry.read'
|
||||
@@ -267,7 +267,7 @@
|
||||
```
|
||||
📄 apps/api/src/modules/inquiries/domain/repositories/inquiry.repository.ts
|
||||
Type: Repository Interface + Symbol (DDD)
|
||||
Purpose: Defines persistence contract
|
||||
Purpose: Định nghĩa hợp đồng persistence
|
||||
|
||||
Exports:
|
||||
• INQUIRY_REPOSITORY: Symbol (DI token)
|
||||
@@ -295,7 +295,7 @@
|
||||
```
|
||||
📄 apps/api/src/modules/inquiries/domain/repositories/inquiry-read.dto.ts
|
||||
Type: Data Transfer Object (Read Model)
|
||||
Purpose: DTO for query results
|
||||
Purpose: DTO cho kết quả truy vấn
|
||||
Exports: InquiryReadDto interface
|
||||
|
||||
InquiryReadDto Properties:
|
||||
@@ -320,18 +320,18 @@
|
||||
Type: Unit Tests (Jest/Vitest)
|
||||
Test Count: 5
|
||||
Tests:
|
||||
✓ createNew() creates an inquiry with correct properties
|
||||
✓ createNew() creates an inquiry with null phone
|
||||
✓ createNew() emits InquiryCreatedEvent
|
||||
✓ markAsRead() sets isRead to true
|
||||
✓ markAsRead() emits InquiryReadEvent
|
||||
Focus: Entity behavior and domain event emission
|
||||
✓ createNew() tạo yêu cầu tư vấn với các thuộc tính đúng
|
||||
✓ createNew() tạo yêu cầu tư vấn với phone là null
|
||||
✓ createNew() phát InquiryCreatedEvent
|
||||
✓ markAsRead() đặt isRead thành true
|
||||
✓ markAsRead() phát InquiryReadEvent
|
||||
Focus: Hành vi entity và việc phát sự kiện domain
|
||||
Lines: ~96
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **3. INFRASTRUCTURE LAYER**
|
||||
### **3. TẦNG HẠ TẦNG (INFRASTRUCTURE LAYER)**
|
||||
|
||||
#### Repository Implementation (1 file)
|
||||
|
||||
@@ -340,7 +340,7 @@
|
||||
Type: Repository Implementation (Service)
|
||||
Implements: IInquiryRepository
|
||||
Decorator: @Injectable()
|
||||
Purpose: Prisma-based persistence
|
||||
Purpose: Persistence dựa trên Prisma
|
||||
|
||||
Methods:
|
||||
1. findById(id: string): Promise<InquiryEntity | null>
|
||||
@@ -349,21 +349,21 @@
|
||||
|
||||
2. save(entity: InquiryEntity): Promise<void>
|
||||
- Query: prisma.inquiry.create({ data: {...} })
|
||||
- Maps entity properties to Prisma model
|
||||
- Ánh xạ thuộc tính entity sang Prisma model
|
||||
|
||||
3. markAsRead(id: string): Promise<void>
|
||||
- Query: prisma.inquiry.update({ where: { id }, data: { isRead: true } })
|
||||
|
||||
4. findByListing(listingId, page, limit): Promise<PaginatedResult<InquiryReadDto>>
|
||||
- Query: prisma.inquiry.findMany() with joins
|
||||
- Query: prisma.inquiry.findMany() với joins
|
||||
- Includes: listing.property.title, user.fullName, user.phone
|
||||
- Pagination: skip/take pattern
|
||||
- Pagination: mô hình skip/take
|
||||
- Sorting: orderBy: { createdAt: 'desc' }
|
||||
- Returns: Mapped InquiryReadDto array with pagination info
|
||||
- Returns: Mảng InquiryReadDto đã ánh xạ kèm thông tin phân trang
|
||||
|
||||
5. findByAgent(agentId, page, limit): Promise<PaginatedResult<InquiryReadDto>>
|
||||
- Query: prisma.inquiry.findMany() filtered by listing.agentId
|
||||
- Same includes and pagination as findByListing
|
||||
- Query: prisma.inquiry.findMany() lọc theo listing.agentId
|
||||
- Cùng includes và phân trang như findByListing
|
||||
- Filter: { listing: { agentId } }
|
||||
|
||||
6. countUnreadByAgent(agentId): Promise<number>
|
||||
@@ -372,7 +372,7 @@
|
||||
|
||||
Helper Method:
|
||||
• toDomain(raw: PrismaInquiry): InquiryEntity
|
||||
- Maps database record to domain entity
|
||||
- Ánh xạ bản ghi cơ sở dữ liệu sang domain entity
|
||||
|
||||
Lines: ~146
|
||||
Dependencies:
|
||||
@@ -383,7 +383,7 @@
|
||||
|
||||
---
|
||||
|
||||
### **4. PRESENTATION LAYER**
|
||||
### **4. TẦNG TRÌNH BÀY (PRESENTATION LAYER)**
|
||||
|
||||
#### Controller (1 file)
|
||||
|
||||
@@ -392,7 +392,7 @@
|
||||
Type: NestJS Controller
|
||||
Decorator: @Controller('inquiries')
|
||||
Decorator: @ApiTags('inquiries')
|
||||
Purpose: HTTP API endpoints
|
||||
Purpose: Các endpoint HTTP API
|
||||
|
||||
Endpoints:
|
||||
|
||||
@@ -404,8 +404,8 @@
|
||||
Returns: CreateInquiryResult { id, listingId, createdAt }
|
||||
Status Codes: 201 Created, 400 Bad Request, 401 Unauthorized, 404 Not Found
|
||||
Logic:
|
||||
• Dispatch CreateInquiryCommand via CommandBus
|
||||
• Pass user.sub as userId
|
||||
• Điều phối CreateInquiryCommand qua CommandBus
|
||||
• Truyền user.sub làm userId
|
||||
|
||||
2. GET /inquiries/listing/:listingId
|
||||
Decorator: @Get('listing/:listingId')
|
||||
@@ -415,8 +415,8 @@
|
||||
Returns: PaginatedResult<InquiryReadDto>
|
||||
Status Codes: 200 OK, 401 Unauthorized
|
||||
Logic:
|
||||
• Dispatch GetInquiriesByListingQuery via QueryBus
|
||||
• Default page=1, limit=20
|
||||
• Điều phối GetInquiriesByListingQuery qua QueryBus
|
||||
• Mặc định page=1, limit=20
|
||||
|
||||
3. GET /inquiries/agent/me
|
||||
Decorator: @Get('agent/me')
|
||||
@@ -427,9 +427,9 @@
|
||||
Returns: PaginatedResult<InquiryReadDto>
|
||||
Status Codes: 200 OK, 401 Unauthorized, 403 Forbidden
|
||||
Logic:
|
||||
• Dispatch GetInquiriesByAgentQuery via QueryBus
|
||||
• Pass user.sub as agentUserId
|
||||
• Default page=1, limit=20
|
||||
• Điều phối GetInquiriesByAgentQuery qua QueryBus
|
||||
• Truyền user.sub làm agentUserId
|
||||
• Mặc định page=1, limit=20
|
||||
|
||||
4. PATCH /inquiries/:id/read
|
||||
Decorator: @Patch(':id/read')
|
||||
@@ -439,9 +439,9 @@
|
||||
Returns: { success: boolean }
|
||||
Status Codes: 200 OK, 401 Unauthorized, 403 Forbidden, 404 Not Found
|
||||
Logic:
|
||||
• Dispatch MarkInquiryReadCommand via CommandBus
|
||||
• Pass user.sub as agentUserId
|
||||
• Return { success: true }
|
||||
• Điều phối MarkInquiryReadCommand qua CommandBus
|
||||
• Truyền user.sub làm agentUserId
|
||||
• Trả về { success: true }
|
||||
|
||||
Lines: ~121
|
||||
Dependencies:
|
||||
@@ -456,7 +456,7 @@
|
||||
```
|
||||
📄 apps/api/src/modules/inquiries/presentation/dto/create-inquiry.dto.ts
|
||||
Type: Data Transfer Object (Validation)
|
||||
Purpose: Validate POST /inquiries request body
|
||||
Purpose: Xác thực body của request POST /inquiries
|
||||
Decorators: @ApiProperty, @ApiPropertyOptional
|
||||
|
||||
Properties:
|
||||
@@ -467,7 +467,7 @@
|
||||
• message: string (required)
|
||||
Validators: @IsString(), @IsNotEmpty(), @MaxLength(2000)
|
||||
API Doc: "Tin nhắn yêu cầu tư vấn" (Consultation request message)
|
||||
Max: 2000 characters
|
||||
Max: 2000 ký tự
|
||||
|
||||
• phone?: string (optional)
|
||||
Validators: @IsOptional(), @IsString()
|
||||
@@ -480,18 +480,18 @@
|
||||
```
|
||||
📄 apps/api/src/modules/inquiries/presentation/dto/list-inquiries.dto.ts
|
||||
Type: Data Transfer Object (Validation)
|
||||
Purpose: Validate query parameters for list endpoints
|
||||
Purpose: Xác thực query parameter cho các endpoint danh sách
|
||||
Decorators: @ApiPropertyOptional, @Type
|
||||
|
||||
Properties:
|
||||
• page?: number (optional)
|
||||
Validators: @IsOptional(), @IsInt(), @Min(1), @Type(() => Number)
|
||||
API Doc: "Page number", default: 1
|
||||
API Doc: "Số trang", mặc định: 1
|
||||
Example: 1
|
||||
|
||||
• limit?: number (optional)
|
||||
Validators: @IsOptional(), @IsInt(), @Min(1), @Max(100), @Type(() => Number)
|
||||
API Doc: "Items per page", default: 20
|
||||
API Doc: "Số mục mỗi trang", mặc định: 20
|
||||
Example: 20
|
||||
Max: 100
|
||||
|
||||
@@ -507,16 +507,16 @@
|
||||
Test Count: 6
|
||||
|
||||
Tests:
|
||||
✓ POST /inquiries dispatches CreateInquiryCommand with correct parameters
|
||||
✓ POST /inquiries passes null phone when not provided
|
||||
✓ GET /listing/:id dispatches GetInquiriesByListingQuery with defaults
|
||||
✓ GET /listing/:id passes custom pagination
|
||||
✓ GET /agent/me dispatches GetInquiriesByAgentQuery with current user
|
||||
✓ PATCH /:id/read dispatches MarkInquiryReadCommand and returns success
|
||||
✓ POST /inquiries điều phối CreateInquiryCommand với tham số đúng
|
||||
✓ POST /inquiries truyền phone là null khi không được cung cấp
|
||||
✓ GET /listing/:id điều phối GetInquiriesByListingQuery với giá trị mặc định
|
||||
✓ GET /listing/:id truyền phân trang tùy chỉnh
|
||||
✓ GET /agent/me điều phối GetInquiriesByAgentQuery với người dùng hiện tại
|
||||
✓ PATCH /:id/read điều phối MarkInquiryReadCommand và trả về thành công
|
||||
|
||||
Mocks:
|
||||
• mockCommandBus with execute() mock function
|
||||
• mockQueryBus with execute() mock function
|
||||
• mockCommandBus với hàm mock execute()
|
||||
• mockQueryBus với hàm mock execute()
|
||||
• mockBuyer user object (sub='buyer-1', role='BUYER')
|
||||
• mockAgent user object (sub='agent-1', role='AGENT')
|
||||
|
||||
@@ -525,25 +525,25 @@
|
||||
|
||||
---
|
||||
|
||||
### **5. MODULE LAYER**
|
||||
### **5. TẦNG MODULE (MODULE LAYER)**
|
||||
|
||||
```
|
||||
📄 apps/api/src/modules/inquiries/inquiries.module.ts
|
||||
Type: NestJS Module
|
||||
Decorator: @Module()
|
||||
Purpose: Configure module, declare dependencies, exports
|
||||
Purpose: Cấu hình module, khai báo phụ thuộc, xuất khẩu
|
||||
|
||||
Imports: [CqrsModule]
|
||||
Controllers: [InquiriesController]
|
||||
|
||||
Providers:
|
||||
• { provide: INQUIRY_REPOSITORY, useClass: PrismaInquiryRepository }
|
||||
- Dependency injection token mapping
|
||||
- Ánh xạ token dependency injection
|
||||
• CommandHandlers array: [CreateInquiryHandler, MarkInquiryReadHandler]
|
||||
• QueryHandlers array: [GetInquiriesByListingHandler, GetInquiriesByAgentHandler]
|
||||
|
||||
Exports: [INQUIRY_REPOSITORY]
|
||||
- Makes repository available to other modules
|
||||
- Cung cấp repository cho các module khác
|
||||
|
||||
Lines: ~29
|
||||
Dependencies:
|
||||
@@ -555,7 +555,7 @@
|
||||
```
|
||||
📄 apps/api/src/modules/inquiries/index.ts
|
||||
Type: Barrel Export (Public API)
|
||||
Purpose: Define public module interface
|
||||
Purpose: Định nghĩa giao diện công khai của module
|
||||
|
||||
Exports:
|
||||
• InquiriesModule (default export for app imports)
|
||||
@@ -568,31 +568,31 @@
|
||||
|
||||
---
|
||||
|
||||
## 📊 FILE STATISTICS
|
||||
## 📊 THỐNG KÊ FILE
|
||||
|
||||
### By Layer
|
||||
### Theo Tầng
|
||||
|
||||
| Layer | Files | Type | Purpose |
|
||||
|-------|-------|------|---------|
|
||||
| **Presentation** | 5 | Controller + DTOs + Tests | HTTP endpoints and input validation |
|
||||
| **Application** | 8 | Commands/Queries + Handlers + Tests | Use case orchestration |
|
||||
| **Domain** | 6 | Entities + Events + Repository Interface + Tests | Business logic and contracts |
|
||||
| **Infrastructure** | 1 | Repository Implementation | Database persistence |
|
||||
| **Module** | 2 | Module + Exports | Configuration and public API |
|
||||
| **TOTAL** | 25 | - | - |
|
||||
| Tầng | Files | Loại | Mục đích |
|
||||
|------|-------|------|----------|
|
||||
| **Presentation** | 5 | Controller + DTOs + Tests | Endpoint HTTP và xác thực đầu vào |
|
||||
| **Application** | 8 | Commands/Queries + Handlers + Tests | Điều phối use case |
|
||||
| **Domain** | 6 | Entities + Events + Repository Interface + Tests | Logic nghiệp vụ và hợp đồng |
|
||||
| **Infrastructure** | 1 | Repository Implementation | Lưu trữ cơ sở dữ liệu |
|
||||
| **Module** | 2 | Module + Exports | Cấu hình và API công khai |
|
||||
| **TỔNG** | 25 | - | - |
|
||||
|
||||
### By Type
|
||||
### Theo Loại
|
||||
|
||||
| Type | Count |
|
||||
|------|-------|
|
||||
| Source Files (.ts, no tests) | 19 |
|
||||
| Test Files (.spec.ts) | 6 |
|
||||
| **Total** | **25** |
|
||||
| Loại | Số lượng |
|
||||
|------|----------|
|
||||
| File nguồn (.ts, không phải test) | 19 |
|
||||
| File test (.spec.ts) | 6 |
|
||||
| **Tổng** | **25** |
|
||||
|
||||
### By Purpose
|
||||
### Theo Mục Đích
|
||||
|
||||
| Purpose | Count | Files |
|
||||
|---------|-------|-------|
|
||||
| Mục đích | Số lượng | Files |
|
||||
|----------|----------|-------|
|
||||
| Controllers | 1 | inquiries.controller.ts |
|
||||
| DTOs | 2 | create-inquiry.dto.ts, list-inquiries.dto.ts |
|
||||
| Commands | 2 | create-inquiry.command.ts, mark-inquiry-read.command.ts |
|
||||
@@ -601,39 +601,39 @@
|
||||
| Entities | 1 | inquiry.entity.ts |
|
||||
| Events | 2 | inquiry-created.event.ts, inquiry-read.event.ts |
|
||||
| Repositories | 3 | interface + 2 DTOs + 1 implementation |
|
||||
| Tests | 6 | 5 test suites across layers |
|
||||
| Tests | 6 | 5 bộ test trải rộng các tầng |
|
||||
| Module | 2 | inquiries.module.ts, index.ts |
|
||||
|
||||
---
|
||||
|
||||
## 🔍 FILE DISCOVERY GUIDE
|
||||
## 🔍 HƯỚNG DẪN TÌM KIẾM FILE
|
||||
|
||||
### Looking for Business Logic?
|
||||
→ **`domain/entities/inquiry.entity.ts`** - Core business rules
|
||||
→ **`domain/events/*.event.ts`** - Business events
|
||||
### Tìm kiếm Logic Nghiệp Vụ?
|
||||
→ **`domain/entities/inquiry.entity.ts`** - Quy tắc nghiệp vụ cốt lõi
|
||||
→ **`domain/events/*.event.ts`** - Sự kiện nghiệp vụ
|
||||
|
||||
### Looking for Use Cases?
|
||||
→ **`application/commands/*/`** - Write operations
|
||||
→ **`application/queries/*/`** - Read operations
|
||||
→ **`application/[type]/__tests__/`** - Test cases
|
||||
### Tìm kiếm Use Case?
|
||||
→ **`application/commands/*/`** - Thao tác ghi
|
||||
→ **`application/queries/*/`** - Thao tác đọc
|
||||
→ **`application/[type]/__tests__/`** - Các test case
|
||||
|
||||
### Looking for HTTP Endpoints?
|
||||
→ **`presentation/controllers/inquiries.controller.ts`** - All 4 endpoints
|
||||
### Tìm kiếm HTTP Endpoint?
|
||||
→ **`presentation/controllers/inquiries.controller.ts`** - Toàn bộ 4 endpoint
|
||||
|
||||
### Looking for Input Validation?
|
||||
→ **`presentation/dto/`** - All DTOs with validators
|
||||
### Tìm kiếm Xác Thực Đầu Vào?
|
||||
→ **`presentation/dto/`** - Tất cả DTOs kèm validator
|
||||
|
||||
### Looking for Database Logic?
|
||||
→ **`infrastructure/repositories/prisma-inquiry.repository.ts`** - All Prisma queries
|
||||
### Tìm kiếm Logic Cơ Sở Dữ Liệu?
|
||||
→ **`infrastructure/repositories/prisma-inquiry.repository.ts`** - Tất cả Prisma query
|
||||
|
||||
### Looking for Tests?
|
||||
→ **`domain/__tests__/`** - Domain behavior
|
||||
→ **`application/__tests__/`** - Handler tests
|
||||
→ **`presentation/__tests__/`** - Controller tests
|
||||
### Tìm kiếm Tests?
|
||||
→ **`domain/__tests__/`** - Hành vi domain
|
||||
→ **`application/__tests__/`** - Test handler
|
||||
→ **`presentation/__tests__/`** - Test controller
|
||||
|
||||
---
|
||||
|
||||
## 🔗 DEPENDENCY GRAPH
|
||||
## 🔗 ĐỒ THỊ PHỤ THUỘC (DEPENDENCY GRAPH)
|
||||
|
||||
```
|
||||
presentation/controllers/inquiries.controller.ts
|
||||
@@ -671,27 +671,27 @@ inquiries.module.ts
|
||||
|
||||
---
|
||||
|
||||
## 📝 FILE CROSS-REFERENCES
|
||||
## 📝 THAM CHIẾU CHÉO FILE
|
||||
|
||||
### Files Using INQUIRY_REPOSITORY Symbol
|
||||
- `inquiries.module.ts` - Provides implementation
|
||||
### Các File Sử Dụng Symbol INQUIRY_REPOSITORY
|
||||
- `inquiries.module.ts` - Cung cấp implementation
|
||||
- `application/commands/create-inquiry/create-inquiry.handler.ts` - Injects
|
||||
- `application/commands/mark-inquiry-read/mark-inquiry-read.handler.ts` - Injects
|
||||
- `application/queries/get-inquiries-by-agent/get-inquiries-by-agent.handler.ts` - Injects
|
||||
- `application/queries/get-inquiries-by-listing/get-inquiries-by-listing.handler.ts` - Injects
|
||||
|
||||
### Files Creating/Modifying InquiryEntity
|
||||
- `domain/entities/inquiry.entity.ts` - Defines
|
||||
- `application/commands/create-inquiry/create-inquiry.handler.ts` - Creates
|
||||
- `application/commands/mark-inquiry-read/mark-inquiry-read.handler.ts` - Modifies
|
||||
- `infrastructure/repositories/prisma-inquiry.repository.ts` - Maps to/from
|
||||
### Các File Tạo/Chỉnh Sửa InquiryEntity
|
||||
- `domain/entities/inquiry.entity.ts` - Định nghĩa
|
||||
- `application/commands/create-inquiry/create-inquiry.handler.ts` - Tạo mới
|
||||
- `application/commands/mark-inquiry-read/mark-inquiry-read.handler.ts` - Chỉnh sửa
|
||||
- `infrastructure/repositories/prisma-inquiry.repository.ts` - Ánh xạ đến/từ
|
||||
|
||||
### Files Publishing Events
|
||||
- `domain/entities/inquiry.entity.ts` - Emits (via addDomainEvent)
|
||||
- `application/commands/create-inquiry/create-inquiry.handler.ts` - Publishes
|
||||
- `application/commands/mark-inquiry-read/mark-inquiry-read.handler.ts` - Publishes
|
||||
### Các File Phát Sự Kiện
|
||||
- `domain/entities/inquiry.entity.ts` - Phát ra (qua addDomainEvent)
|
||||
- `application/commands/create-inquiry/create-inquiry.handler.ts` - Xuất bản
|
||||
- `application/commands/mark-inquiry-read/mark-inquiry-read.handler.ts` - Xuất bản
|
||||
|
||||
### Files with Tests
|
||||
### Các File Có Tests
|
||||
- `domain/entities/inquiry.entity.ts` → `domain/__tests__/inquiry-domain.spec.ts`
|
||||
- `application/commands/create-inquiry/create-inquiry.handler.ts` → `application/__tests__/create-inquiry.handler.spec.ts`
|
||||
- `application/commands/mark-inquiry-read/mark-inquiry-read.handler.ts` → `application/__tests__/mark-inquiry-read.handler.spec.ts`
|
||||
@@ -701,35 +701,35 @@ inquiries.module.ts
|
||||
|
||||
---
|
||||
|
||||
## 🎯 ENTRY POINTS FOR MODIFICATIONS
|
||||
## 🎯 ĐIỂM VÀO CHO VIỆC CHỈNH SỬA
|
||||
|
||||
### To Add a New Endpoint
|
||||
1. Add method to `presentation/controllers/inquiries.controller.ts`
|
||||
2. Create command/query in `application/[type]/[name]/`
|
||||
3. Create handler: `[name].handler.ts`
|
||||
4. Add tests in `application/__tests__/`
|
||||
5. Optional: Update domain if new business logic needed
|
||||
### Thêm Endpoint Mới
|
||||
1. Thêm phương thức vào `presentation/controllers/inquiries.controller.ts`
|
||||
2. Tạo command/query trong `application/[type]/[name]/`
|
||||
3. Tạo handler: `[name].handler.ts`
|
||||
4. Thêm tests trong `application/__tests__/`
|
||||
5. Tuỳ chọn: Cập nhật domain nếu cần logic nghiệp vụ mới
|
||||
|
||||
### To Add a New Command
|
||||
1. Create `application/commands/[name]/[name].command.ts` (DTO)
|
||||
2. Create `application/commands/[name]/[name].handler.ts` (@CommandHandler)
|
||||
3. Register in `inquiries.module.ts` CommandHandlers array
|
||||
4. Add tests in `application/__tests__/[name].handler.spec.ts`
|
||||
5. Optional: Define new domain events in `domain/events/`
|
||||
### Thêm Command Mới
|
||||
1. Tạo `application/commands/[name]/[name].command.ts` (DTO)
|
||||
2. Tạo `application/commands/[name]/[name].handler.ts` (@CommandHandler)
|
||||
3. Đăng ký trong mảng CommandHandlers của `inquiries.module.ts`
|
||||
4. Thêm tests trong `application/__tests__/[name].handler.spec.ts`
|
||||
5. Tuỳ chọn: Định nghĩa sự kiện domain mới trong `domain/events/`
|
||||
|
||||
### To Add Domain Logic
|
||||
1. Modify `domain/entities/inquiry.entity.ts`
|
||||
2. Add new public methods or properties
|
||||
3. Emit new events if needed: `domain/events/[name].event.ts`
|
||||
4. Update tests in `domain/__tests__/inquiry-domain.spec.ts`
|
||||
5. Update handlers that use the entity
|
||||
### Thêm Logic Domain
|
||||
1. Chỉnh sửa `domain/entities/inquiry.entity.ts`
|
||||
2. Thêm phương thức hoặc thuộc tính công khai mới
|
||||
3. Phát sự kiện mới nếu cần: `domain/events/[name].event.ts`
|
||||
4. Cập nhật tests trong `domain/__tests__/inquiry-domain.spec.ts`
|
||||
5. Cập nhật các handler sử dụng entity
|
||||
|
||||
### To Add Database Operations
|
||||
1. Add method to `infrastructure/repositories/prisma-inquiry.repository.ts`
|
||||
2. Update interface in `domain/repositories/inquiry.repository.ts`
|
||||
3. Call from relevant handlers
|
||||
### Thêm Thao Tác Cơ Sở Dữ Liệu
|
||||
1. Thêm phương thức vào `infrastructure/repositories/prisma-inquiry.repository.ts`
|
||||
2. Cập nhật interface trong `domain/repositories/inquiry.repository.ts`
|
||||
3. Gọi từ các handler liên quan
|
||||
|
||||
---
|
||||
|
||||
**Total Lines of Code:** 1,212
|
||||
**Last Updated:** April 11, 2026
|
||||
**Tổng số dòng code:** 1.212
|
||||
**Cập nhật lần cuối:** Ngày 11 tháng 4 năm 2026
|
||||
|
||||
Reference in New Issue
Block a user