add type
This commit is contained in:
@@ -12,7 +12,7 @@ Nguồn tham chiếu trong code:
|
|||||||
Snapshot hiện tại:
|
Snapshot hiện tại:
|
||||||
|
|
||||||
- Không có `schema_version`.
|
- Không có `schema_version`.
|
||||||
- Không lưu `section` (project/section được xác định bằng context record `commits.project_id`).
|
- Không lưu `section` (entity trong DB là `projects`; project được xác định bằng context record `commits.project_id`).
|
||||||
- Không dùng `ref:{id}` nữa: **`id` là canonical**, `source:"ref"` nghĩa là tham chiếu theo `id`.
|
- Không dùng `ref:{id}` nữa: **`id` là canonical**, `source:"ref"` nghĩa là tham chiếu theo `id`.
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
@@ -28,6 +28,130 @@ export type CommitSnapshot = {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 1.1 Type đầy đủ (TypeScript)
|
||||||
|
|
||||||
|
Đây là bản type "đúng để BEGo implement chuyển đổi snapshot → DB". FE có thể gửi thêm field legacy (xem mục 6), nhưng BE nên normalize theo các type dưới đây.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// ---- GeoJSON ----
|
||||||
|
|
||||||
|
export type Geometry =
|
||||||
|
| { type: "Point"; coordinates: [number, number] }
|
||||||
|
| { type: "MultiPoint"; coordinates: [number, number][] }
|
||||||
|
| { type: "LineString"; coordinates: [number, number][] }
|
||||||
|
| { type: "MultiLineString"; coordinates: [number, number][][] }
|
||||||
|
| { type: "Polygon"; coordinates: [number, number][][] }
|
||||||
|
| { type: "MultiPolygon"; coordinates: [number, number][][][] };
|
||||||
|
|
||||||
|
export type FeatureId = string | number; // FE hiện dùng UUIDv7 string
|
||||||
|
|
||||||
|
export type FeatureProperties = {
|
||||||
|
id: FeatureId;
|
||||||
|
type?: string | null;
|
||||||
|
geometry_preset?: string | null;
|
||||||
|
time_start?: number | null;
|
||||||
|
time_end?: number | null;
|
||||||
|
binding?: string[]; // entity ids used as "binding filter"
|
||||||
|
|
||||||
|
// Legacy UI fields. FE persist snapshot hiện tại KHONG gửi các field này,
|
||||||
|
// nhưng BE nên ignore nếu gặp trong snapshot cũ:
|
||||||
|
entity_id?: string | null;
|
||||||
|
entity_ids?: string[];
|
||||||
|
entity_name?: string | null;
|
||||||
|
entity_names?: string[];
|
||||||
|
entity_type_id?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Feature = {
|
||||||
|
type: "Feature";
|
||||||
|
properties: FeatureProperties;
|
||||||
|
geometry: Geometry;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FeatureCollection = {
|
||||||
|
type: "FeatureCollection";
|
||||||
|
features: Feature[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- Snapshot rows ----
|
||||||
|
|
||||||
|
export type SnapshotSource = "inline" | "ref";
|
||||||
|
|
||||||
|
export type EntitySnapshotOperation = "create" | "update" | "delete" | "reference";
|
||||||
|
export type GeometrySnapshotOperation = "create" | "update" | "delete" | "reference";
|
||||||
|
export type WikiSnapshotOperation = "create" | "update" | "delete" | "reference";
|
||||||
|
|
||||||
|
export type EntitySnapshot = {
|
||||||
|
id: string; // UUIDv7 string (canonical)
|
||||||
|
source: SnapshotSource;
|
||||||
|
operation?: EntitySnapshotOperation;
|
||||||
|
name?: string;
|
||||||
|
slug?: string | null;
|
||||||
|
description?: string | null;
|
||||||
|
type_id?: string | null;
|
||||||
|
status?: number | null;
|
||||||
|
base_updated_at?: string;
|
||||||
|
base_hash?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GeometrySnapshot = {
|
||||||
|
id: string; // UUIDv7 string (canonical)
|
||||||
|
source: SnapshotSource;
|
||||||
|
operation?: GeometrySnapshotOperation;
|
||||||
|
|
||||||
|
// Present when source:"inline" (draft features)
|
||||||
|
type?: string | null;
|
||||||
|
draw_geometry?: Geometry;
|
||||||
|
binding?: string[];
|
||||||
|
time_start?: number | null;
|
||||||
|
time_end?: number | null;
|
||||||
|
bbox?: {
|
||||||
|
min_lng: number;
|
||||||
|
min_lat: number;
|
||||||
|
max_lng: number;
|
||||||
|
max_lat: number;
|
||||||
|
} | null;
|
||||||
|
|
||||||
|
base_updated_at?: string;
|
||||||
|
base_hash?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WikiSnapshot = {
|
||||||
|
id: string; // UUIDv7 string (canonical)
|
||||||
|
source: SnapshotSource;
|
||||||
|
operation?: WikiSnapshotOperation;
|
||||||
|
title: string;
|
||||||
|
doc: unknown; // tiptap JSON (inline) hoặc null (ref)
|
||||||
|
updated_at?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- Join tables ----
|
||||||
|
|
||||||
|
export type GeometryEntitySnapshot = {
|
||||||
|
geometry_id: string;
|
||||||
|
entity_id: string;
|
||||||
|
base_links_hash?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EntityWikiLinkSnapshot = {
|
||||||
|
entity_id: string;
|
||||||
|
wiki_id: string;
|
||||||
|
// If missing, BE should treat as "reference" (active link) for backwards-compat.
|
||||||
|
operation?: "reference" | "delete";
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- Root ----
|
||||||
|
|
||||||
|
export type CommitSnapshot = {
|
||||||
|
editor_feature_collection?: FeatureCollection;
|
||||||
|
entities?: EntitySnapshot[];
|
||||||
|
geometries?: GeometrySnapshot[];
|
||||||
|
wikis?: WikiSnapshot[];
|
||||||
|
geometry_entity?: GeometryEntitySnapshot[];
|
||||||
|
entity_wiki?: EntityWikiLinkSnapshot[];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
## 2) Quy Ước `source` và `operation`
|
## 2) Quy Ước `source` và `operation`
|
||||||
|
|
||||||
### 2.1 `source` (bắt buộc)
|
### 2.1 `source` (bắt buộc)
|
||||||
@@ -113,6 +237,11 @@ Nếu feature bị xoá khỏi draft, FE thêm 1 delete row:
|
|||||||
|
|
||||||
Lưu ý: geometry `operation:"delete"` **không xuất hiện trên map**, vì map render theo `editor_feature_collection.features[]`.
|
Lưu ý: geometry `operation:"delete"` **không xuất hiện trên map**, vì map render theo `editor_feature_collection.features[]`.
|
||||||
|
|
||||||
|
Gợi ý cho BE khi apply vào DB:
|
||||||
|
|
||||||
|
- Có thể coi `editor_feature_collection` là state hiện tại để render/map.
|
||||||
|
- `geometries[]` là "rows + deltas": sẽ có 1 row cho mỗi feature đang tồn tại trong draft (có/không có `operation`), và có thể có thêm các row `operation:"delete"` để xoá geometry khỏi project state.
|
||||||
|
|
||||||
### 3.4 `geometry_entity[]` (join table Geometry ↔ Entity)
|
### 3.4 `geometry_entity[]` (join table Geometry ↔ Entity)
|
||||||
|
|
||||||
Join table many-to-many giữa geometry và entity. Mỗi cặp geometry↔entity là một row:
|
Join table many-to-many giữa geometry và entity. Mỗi cặp geometry↔entity là một row:
|
||||||
@@ -206,3 +335,21 @@ Toggle link trong UI:
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 5) Notes Cho BackEnd (Normalize + Compat)
|
||||||
|
|
||||||
|
BE nên normalize trước khi convert snapshot → DB:
|
||||||
|
|
||||||
|
- Ignore toàn bộ field entity denormalize trên `feature.properties` (nếu có): `entity_id/entity_ids/entity_name/entity_names/entity_type_id`. Quan hệ geometry↔entity lấy từ `geometry_entity[]`.
|
||||||
|
- `entity_wiki[].operation`:
|
||||||
|
- `"reference"`: link active
|
||||||
|
- `"delete"`: link removed trong snapshot
|
||||||
|
- missing: treat as `"reference"` (compat)
|
||||||
|
|
||||||
|
## 6) Legacy Compatibility (nếu gặp snapshot cũ)
|
||||||
|
|
||||||
|
FE đã từng gửi các field legacy; BE có thể gặp nếu đang xử lý commit cũ:
|
||||||
|
|
||||||
|
- `entity_wikis` (plural) thay vì `entity_wiki` (singular): treat như nhau.
|
||||||
|
- `ref:{id}` trong `entities/geometries/wikis`: ignore (id canonical).
|
||||||
|
- `is_deleted` trong join table entity↔wiki: map sang `operation:"delete"` khi `is_deleted==1`, ngược lại `"reference"`.
|
||||||
|
|||||||
@@ -8,19 +8,18 @@ export type EntityWikiLinkSnapshot = {
|
|||||||
operation?: "reference" | "delete";
|
operation?: "reference" | "delete";
|
||||||
};
|
};
|
||||||
|
|
||||||
// API mới (BackEndGo) dùng Projects/Commits/Submissions.
|
// BackEndGo uses Projects/Commits/Submissions. "Section" is legacy naming in FE.
|
||||||
// Giữ tên type "Section" để tránh thay đổi lan rộng trong FE hiện tại.
|
export type ProjectStatus = string;
|
||||||
export type SectionStatus = string;
|
export type ProjectSubmissionStatus = "PENDING" | "APPROVED" | "REJECTED" | string;
|
||||||
export type SectionSubmissionStatus = "PENDING" | "APPROVED" | "REJECTED" | string;
|
|
||||||
|
|
||||||
export type SectionState = {
|
export type ProjectState = {
|
||||||
// Derived state from ProjectResponse (not persisted as-is in API mới).
|
// Derived state from ProjectResponse (not persisted as-is in API mới).
|
||||||
status: SectionStatus;
|
status: ProjectStatus;
|
||||||
head_commit_id: string | null;
|
head_commit_id: string | null;
|
||||||
locked_by?: string | null;
|
locked_by?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Section = {
|
export type Project = {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
description: string | null;
|
description: string | null;
|
||||||
@@ -36,7 +35,7 @@ export type Section = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SectionCommit = {
|
export type ProjectCommit = {
|
||||||
id: string;
|
id: string;
|
||||||
project_id: string;
|
project_id: string;
|
||||||
snapshot_json: EditorSnapshot;
|
snapshot_json: EditorSnapshot;
|
||||||
@@ -46,13 +45,13 @@ export type SectionCommit = {
|
|||||||
created_at?: string;
|
created_at?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SectionSubmission = {
|
export type ProjectSubmission = {
|
||||||
id: string;
|
id: string;
|
||||||
project_id: string;
|
project_id: string;
|
||||||
commit_id: string;
|
commit_id: string;
|
||||||
user_id: string;
|
user_id: string;
|
||||||
created_at?: string;
|
created_at?: string;
|
||||||
status: SectionSubmissionStatus;
|
status: ProjectSubmissionStatus;
|
||||||
reviewed_by?: string | null;
|
reviewed_by?: string | null;
|
||||||
reviewed_at?: string | null;
|
reviewed_at?: string | null;
|
||||||
review_note?: string | null;
|
review_note?: string | null;
|
||||||
@@ -75,10 +74,13 @@ export type EditorSnapshot = {
|
|||||||
entity_wiki?: EntityWikiLinkSnapshot[];
|
entity_wiki?: EntityWikiLinkSnapshot[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Alias for clearer naming at API boundary: commits.snapshot_json is this shape.
|
||||||
|
export type CommitSnapshot = EditorSnapshot;
|
||||||
|
|
||||||
export type EditorLoadResponse = {
|
export type EditorLoadResponse = {
|
||||||
section: Section;
|
section: Project;
|
||||||
state: SectionState;
|
state: ProjectState;
|
||||||
commit: SectionCommit | null;
|
commit: ProjectCommit | null;
|
||||||
snapshot: EditorSnapshot | null;
|
snapshot: EditorSnapshot | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -96,3 +98,11 @@ export type CreateCommitInput = {
|
|||||||
export type RestoreCommitInput = {
|
export type RestoreCommitInput = {
|
||||||
commit_id: string;
|
commit_id: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Legacy aliases (to reduce churn in existing FE code). Prefer Project* names above.
|
||||||
|
export type SectionStatus = ProjectStatus;
|
||||||
|
export type SectionSubmissionStatus = ProjectSubmissionStatus;
|
||||||
|
export type SectionState = ProjectState;
|
||||||
|
export type Section = Project;
|
||||||
|
export type SectionCommit = ProjectCommit;
|
||||||
|
export type SectionSubmission = ProjectSubmission;
|
||||||
|
|||||||
Reference in New Issue
Block a user