Listings - xem danh sách và chi tiết tin đăng
Feature URL
Module
listing
Status
shipped
Priority
P0
Platforms
fe · be
AC progress
0 / 9
Last reviewed
2026-05-30
Mục tiêu
Cho phép người dùng xem danh sách, tìm kiếm và mở chi tiết tin đăng theo đúng phạm vi nhìn thấy. Public chỉ nhìn thấy tin ACTIVE; owner có thêm màn hình quản lý tin của mình và danh sách nháp. Số điện thoại liên hệ bị che mặc định; nguồn số điện thoại lấy từ profile user (nếu USER tự đăng) hoặc từ bảng contacts (nếu BROKER hoặc crawl tạo) — người xem không phân biệt nguồn gốc.
Phạm vi
Trong phạm vi (In scope):
- Public search listing qua
GET /listingsvới filter và pagination. - Public detail qua
GET /listings/{id}theo visibility rule. - Owner xem toàn bộ listing của mình qua
GET /listings/me. - Owner xem listing nháp qua
GET /listings/me/drafts. - Áp dụng masking số điện thoại với viewer không đủ quyền xem đầy đủ.
- Xác định nguồn số điện thoại để masking theo loại listing:
contact_id = null(USER tự đăng): lấyuser.phonetừ profile owner.contact_idcó giá trị (BROKER hoặc crawl): lấy số từ bảngcontactstheocontact_id.
Ngoài phạm vi (Out of scope):
- Tạo, cập nhật hoặc xoá listing.
- Admin search mọi status.
- Mở khóa số điện thoại, xem feature
masking-unlock. - Marketplace trust-band nâng cao ngoài rule public listing hiện có.
User Stories
- Là người xem, tôi muốn tìm kiếm listing active theo filter, để tìm bất động sản phù hợp.
- Là người xem, tôi muốn mở chi tiết listing, để xem đầy đủ thông tin được phép.
- Là người xem, tôi muốn xem số điện thoại liên hệ sau khi mở khóa, dù đó là số của chủ tài khoản hay số contact do broker quản lý.
- Là owner, tôi muốn xem tất cả tin của mình, để quản lý trạng thái đăng tin.
- Là owner, tôi muốn xem riêng tin nháp, để tiếp tục hoàn thiện trước khi publish.
Luồng chức năng
sequenceDiagram
actor Viewer
participant App
participant BE
participant Listings
participant Users
participant ContactSvc as Contacts
participant Masking
Viewer->>App: Mở danh sách hoặc chi tiết listing
App->>BE: Gửi yêu cầu xem listing
BE->>Listings: Lấy listing theo visibility
alt contact_id là null - USER tự đăng
BE->>Users: Lấy user.phone của owner
else contact_id có giá trị - BROKER hoặc crawl
BE->>ContactSvc: Lấy phone theo contact_id
end
BE->>Masking: Xác định phone được xem hay bị che
BE-->>App: Trả danh sách hoặc chi tiết đã áp ruleAcceptance Criteria
- AC-1:
GET /listingschỉ trả listingACTIVE. - AC-2: Public search hỗ trợ filter keyword, địa bàn, loại listing, loại property, giá, diện tích và các filter property liên quan.
- AC-3:
GET /listings/{id}trảACTIVEcho mọi viewer theo masking rule. - AC-4: Listing
DRAFTchỉ owner được xem qua detail/owner views. - AC-5: Các status không public như
DELETED,BANNED,EXPIRED,SOLD,VIP_ARCHIVEDkhông bị lộ qua endpoint public. - AC-6: Owner xem
/listings/methấy listing của mình across status;/listings/me/draftschỉ thấyDRAFT. - AC-7: Khi
listing.contact_id = null(USER tự đăng), số điện thoại dùng cho masking lấy từuser.phonecủa owner listing. - AC-8: Khi
listing.contact_idcó giá trị (BROKER hoặc crawl), số điện thoại dùng cho masking lấy từcontactstheocontact_id. - AC-9: Response listing không lộ nguồn số điện thoại (user profile hay contacts) với người xem — chỉ trả dạng masked hoặc đầy đủ tùy quyền.
Quy tắc nghiệp vụ
- Public search luôn hard-wire status
ACTIVE. - Viewer không đủ quyền chỉ thấy số điện thoại đã che.
- Owner luôn xem được dữ liệu đầy đủ của listing thuộc sở hữu của mình, trừ projection giới hạn với listing bị ban nếu có.
- Non-owner hoặc anonymous xem listing không public nhận kết quả không tìm thấy để tránh lộ sự tồn tại.
- Nguồn số điện thoại cho masking phụ thuộc vào
contact_idtrên listing:contact_id = null: dùnguser.phonecủa owner listing.contact_idcó giá trị: dùng số điện thoại từ bảngcontactstheocontact_id.
- Người xem không biết số điện thoại đến từ user profile hay từ contacts; response luôn thống nhất dạng masked/unmasked.
- Nếu contact không tồn tại khi
contact_idcó giá trị, hệ thống không hiển thị số điện thoại và không lỗi visible ra ngoài.
Dữ liệu & Trạng thái
Entity nghiệp vụ:
Listing: tin đăng được search/detail; cócontact_idnullable xác định nguồn số điện thoại liên hệ.Property: dữ liệu vật lý của bất động sản được nhúng vào response listing.User: khicontact_id = null,user.phonecủa owner là số liên hệ bị masking.Contact: khicontact_idcó giá trị, số điện thoại liên hệ được masking lấy từ bảngcontactstheocontact_id.
Trạng thái user-facing:
ACTIVE- hiển thị public.DRAFT- owner preview/quản lý nháp.DELETED,BANNED,EXPIRED,SOLD,VIP_ARCHIVED- không hiển thị public.
Liên quan
- Phụ thuộc: listing-create, contacts-create
- Ảnh hưởng: masking-unlock