# Thiết kế Entity -- Quản lý Mặt bằng (Premise Management -- EF Core)

## 1. Tổng quan

Tài liệu này định nghĩa mô hình thực thể (Entity Model) cho module
**quản lý mặt bằng mở bưu cục**.

Thiết kế theo nguyên tắc:

-   `Premise` là **Aggregate Root**
-   Tái sử dụng các thực thể có sẵn:
    -   `User`
    -   `Location`
    -   `Media`
-   Hỗ trợ:
    -   Nhiều liên hệ (chủ nhà, môi giới)
    -   Nhiều điều khoản thuê (phục vụ lịch sử đàm phán)
    -   Nhiều lần đánh giá
    -   1--1 thông tin pháp lý
    -   Nhiều hình ảnh / tài liệu đính kèm

Tài liệu này chỉ bao gồm: - Định nghĩa entity (C# -- EF Core, Code
First) - Enum - Navigation property

Không bao gồm DbContext hoặc Fluent API.

------------------------------------------------------------------------

## 2. Sơ đồ quan hệ tổng thể

-   Location (1) → (n) Premise
-   Premise (1) → (n) PremiseContact
-   Premise (1) → (n) PremiseRentalTerm
-   Premise (1) → (1) PremiseLegalInfo
-   Premise (1) → (n) PremiseReview
-   Premise (1) → (n) PremiseMedia → (1) Media
-   User (1) → (n) Premise (CreatedBy)
-   User (1) → (n) PremiseReview

------------------------------------------------------------------------

## 3. Ghi chú thiết kế

### 3.1 Aggregate Root

`Premise` là thực thể trung tâm.\
Mọi thao tác liên quan đến mặt bằng nên đi qua thực thể này.

------------------------------------------------------------------------

### 3.2 Chiến lược lưu điều khoản thuê

Nếu **không cần lưu lịch sử đàm phán**: - Thiết lập unique constraint
trên `PremiseId` trong `PremiseRentalTerm`.

Nếu **cần lưu lịch sử**: - Cho phép nhiều bản ghi. - Bản ghi mới nhất
(theo `CreatedAt`) là điều khoản hiện hành.

------------------------------------------------------------------------

### 3.3 Thông tin pháp lý (1--1)

`PremiseLegalInfo` nên có unique index trên `PremiseId`.

------------------------------------------------------------------------

### 3.4 Media

`PremiseMedia` là bảng trung gian giữa `Premise` và `Media`.

Cho phép: - Phân loại ảnh (mặt tiền, bên trong, tài liệu...) - Sắp xếp
thứ tự hiển thị - Ghi chú cho từng media

------------------------------------------------------------------------

# 4. Enums

``` csharp
public enum PremiseStatus
{
    Surveying = 1,
    Evaluating = 2,
    Negotiating = 3,
    Approved = 4,
    Rented = 5,
    Rejected = 6,
    Closed = 7
}

public enum PremiseType
{
    RawHouse = 1,
    AlleyHouse = 2,
    StreetFrontHouse = 3,
    SteelWarehouse = 4,
    ConcreteWarehouse = 5
}

public enum RoadDirection
{
    OneWay = 1,
    TwoWay = 2
}

public enum AccessLevel
{
    Low = 1,
    Medium = 2,
    High = 3
}

public enum DensityLevel
{
    Low = 1,
    Medium = 2,
    High = 3
}

public enum ContactRole
{
    Owner = 1,
    Broker = 2,
    Other = 3
}

public enum PremiseMediaType
{
    Front = 1,
    Interior = 2,
    Road = 3,
    Sidewalk = 4,
    Document = 5
}
```

------------------------------------------------------------------------

# 5. Định nghĩa Entity

## 5.1 Premise (Aggregate Root)

``` csharp
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

public class Premise
{
    public Guid Id { get; set; }

    [MaxLength(50)]
    public string? Code { get; set; }

    public PremiseStatus Status { get; set; } = PremiseStatus.Surveying;

    // Location
    public Guid LocationId { get; set; }
    [MaxLength(500)]
    public string AddressDetail { get; set; } = default!;

    public double? Latitude { get; set; }
    public double? Longitude { get; set; }

    [MaxLength(1000)]
    public string? GoogleMapUrl { get; set; }

    // Thông số mặt bằng
    public decimal? FrontageWidthM { get; set; }
    public decimal? AreaM2 { get; set; }
    public int? NumberOfFloors { get; set; }
    public PremiseType PremiseType { get; set; } = PremiseType.RawHouse;
    public decimal? SidewalkWidthM { get; set; }

    // Giao thông
    public decimal? RoadWidthM { get; set; }
    public int? LaneCount { get; set; }
    public RoadDirection RoadDirection { get; set; } = RoadDirection.TwoWay;
    public bool IsNearMainRoad { get; set; }
    public bool HasRestrictedHours { get; set; }

    [MaxLength(1000)]
    public string? RestrictedHoursNote { get; set; }

    public AccessLevel VehicleAccessLevel { get; set; } = AccessLevel.Medium;

    // Khu vực
    public DensityLevel PopulationDensity { get; set; } = DensityLevel.Medium;
    public DensityLevel NeighborhoodComplexity { get; set; } = DensityLevel.Medium;

    [MaxLength(2000)]
    public string? SurroundingBusiness { get; set; }

    // Setup
    public DateTime? AvailableFrom { get; set; }
    public int? MaxSetupDays { get; set; }

    [MaxLength(4000)]
    public string? Description { get; set; }

    public int? CreatorScore0To100 { get; set; }

    // Audit
    public Guid CreatedByUserId { get; set; }
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    public DateTime? UpdatedAt { get; set; }

    // Navigation
    public Location Location { get; set; } = default!;
    public User CreatedByUser { get; set; } = default!;

    public ICollection<PremiseContact> Contacts { get; set; } = new List<PremiseContact>();
    public ICollection<PremiseRentalTerm> RentalTerms { get; set; } = new List<PremiseRentalTerm>();
    public ICollection<PremiseReview> Reviews { get; set; } = new List<PremiseReview>();
    public ICollection<PremiseMedia> MediaItems { get; set; } = new List<PremiseMedia>();

    public PremiseLegalInfo? LegalInfo { get; set; }
}
```

------------------------------------------------------------------------
