Bỏ qua nội dung

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 /listings vớ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ấy user.phone từ profile owner.
    • contact_id có giá trị (BROKER hoặc crawl): lấy số từ bảng contacts theo contact_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

  • 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.
  • người xem, tôi muốn mở chi tiết listing, để xem đầy đủ thông tin được phép.
  • 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ý.
  • owner, tôi muốn xem tất cả tin của mình, để quản lý trạng thái đăng tin.
  • 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 rule

Acceptance Criteria

  • AC-1: GET /listings chỉ trả listing ACTIVE.
  • 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ả ACTIVE cho mọi viewer theo masking rule.
  • AC-4: Listing DRAFT chỉ owner được xem qua detail/owner views.
  • AC-5: Các status không public như DELETED, BANNED, EXPIRED, SOLD, VIP_ARCHIVED không bị lộ qua endpoint public.
  • AC-6: Owner xem /listings/me thấy listing của mình across status; /listings/me/drafts chỉ thấy DRAFT.
  • AC-7: Khi listing.contact_id = null (USER tự đăng), số điện thoại dùng cho masking lấy từ user.phone của owner listing.
  • AC-8: Khi listing.contact_id có giá trị (BROKER hoặc crawl), số điện thoại dùng cho masking lấy từ contacts theo contact_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_id trên listing:
    • contact_id = null: dùng user.phone của owner listing.
    • contact_id có giá trị: dùng số điện thoại từ bảng contacts theo contact_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_id có 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_id nullable 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: khi contact_id = null, user.phone của owner là số liên hệ bị masking.
  • Contact: khi contact_id có giá trị, số điện thoại liên hệ được masking lấy từ bảng contacts theo contact_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