船舶 Profile 與統一資料模型
統一資料模型將船舶 Profile 串接至航次、作業事件、來源報告、欄位擷取證據、人工審查與不可 變更的 audit history。
本頁定義 domain model。Endpoint 的 request 與 response schema 仍以自動產生的 API Reference 為準。
Entity 關係
Vessel
└── Trip
└── HaulEvent
└── SourceReport
└── ExtractedField
├── Confidence
├── ReviewStatus
└── AuditLog
一艘 Vessel 可以有多個 Trip。Trip 包含依時間排序的 HaulEvent 或其他作業事件。 SourceReport 記錄匯入或產生資訊的來源。每個 ExtractedField 都會保留來源位置、標準化值、 Confidence、ReviewStatus 與 AuditLog。
Entity 定義
Vessel
單一漁船的穩定 Profile,保存跨 Trip 與報告仍然有效的識別資料與描述欄位。
Trip
單一 Vessel 的一段有明確範圍的航次,將出港與返港資訊連接至航次期間產生的事件與報告。
HaulEvent
Trip 中具有開始與結束時間的作業事件。eventType 用來區分 haul、set、catch、transit 或
其他控制類型,並共用同一套來源追蹤模型。
SourceReport
用來產生標準化資料的來源文件、電子表單、錄影群組或機器產生報告。它會記錄來源型別、 關聯對象、時間範圍、版本與處理狀態。
ExtractedField
從 SourceReport 擷取或投影出的單一欄位,保存原始值、標準化值、來源位置、擷取方式、 Confidence 與 ReviewStatus。
Confidence
附加於擷取值的模型或規則判斷。score 必須介於 0 到 1;level 使用 low、
medium 或 high,讓審查流程採用一致的判斷。
ReviewStatus
擷取值目前的人工審查狀態:pending、in_review、accepted、corrected 或
rejected。修正值不會覆蓋原始擷取結果。
AuditLog
記錄重要變更或審查決策的 append-only 資料。它會保存變更內容、原因、時間,以及執行操作 的授權使用者或服務,但不會保存憑證。
Field dictionary|欄位字典
Vessel
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
vesselId | string | 是 | 穩定的 Fishtube 船舶識別碼。 |
name | string | 是 | 組織內唯一的船舶顯示名稱。 |
registrationNumber | string 或 null | 否 | 可取得時使用的官方登記號碼。 |
flagState | string 或 null | 否 | 受控制的船旗國代碼。 |
organizationId | string | 是 | 擁有船舶 Profile 的 tenant。 |
createdAt、updatedAt | ISO-8601 datetime | 是 | Profile lifecycle 時間。 |
Trip
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
tripId | string | 是 | 穩定的航次識別碼。 |
vesselId | string | 是 | 所屬 Vessel。 |
tripNumber | string 或 null | 否 | 業者或主管機關的航次編號。 |
departedAt、returnedAt | ISO-8601 datetime 或 null | 否 | 航次開始與結束時間。 |
departurePort、returnPort | string 或 null | 否 | 受控制的港口參照。 |
status | enum | 是 | planned、active、completed 或 cancelled。 |
HaulEvent
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
eventId | string | 是 | 穩定的作業事件識別碼。 |
tripId | string | 是 | 所屬 Trip。 |
eventType | enum | 是 | set、haul、catch、transit 或其他控制類型。 |
startedAt、endedAt | ISO-8601 datetime | 是 | 事件開始與結束時間。 |
location | object 或 null | 否 | 可取得時保存經緯度。 |
sourceReportIds | string array | 是 | 支援此事件的 SourceReport。 |
SourceReport
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
sourceReportId | string | 是 | 穩定的來源報告識別碼。 |
tripId、eventId | string 或 null | 否 | 關聯的 Trip 與 HaulEvent。 |
sourceType | enum | 是 | observer_form、edge_recording、ai_analysis 或 import。 |
sourceReference | string | 是 | 不含機密的文件、segment 或錄影群組參照。 |
capturedAt | ISO-8601 datetime | 是 | 來源所代表的時間。 |
version | string | 是 | 來源 schema 或文件版本。 |
processingStatus | enum | 是 | received、processing、ready 或 failed。 |
ExtractedField
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
extractedFieldId | string | 是 | 穩定的擷取值識別碼。 |
sourceReportId | string | 是 | 產生此值的 SourceReport。 |
fieldKey | string | 是 | 穩定的欄位字典 key。 |
rawValue | any 或 null | 否 | 來源中原本呈現的值。 |
normalizedValue | any 或 null | 否 | 依欄位字典標準化後的值。 |
sourcePointer | object | 是 | Page、segment、camera、timestamp 或 JSON path 來源。 |
extractionMethod | enum | 是 | manual、rule、ocr 或 model。 |
confidence | Confidence | 是 | 信心評估。 |
reviewStatus | ReviewStatus | 是 | 目前審查狀態。 |
Confidence
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
score | number | 是 | 包含 0 與 1 的數值範圍。 |
level | enum | 是 | low、medium 或 high。 |
method | string | 是 | 規則、模型或人工判斷方式。 |
modelVersion | string 或 null | 否 | 適用時保存產生結果的模型版本。 |
ReviewStatus
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
status | enum | 是 | pending、in_review、accepted、corrected 或 rejected。 |
reviewedValue | any 或 null | 否 | 不覆蓋來源資料的接受或修正值。 |
reasonCode | string 或 null | 否 | 修正或拒絕的控制原因。 |
reviewedBy、reviewedAt | string/datetime 或 null | 否 | 授權審查者與決策時間。 |
AuditLog
| 欄位 | 型別 | 必填 | 說明 |
|---|---|---|---|
auditLogId | string | 是 | 穩定的 audit event 識別碼。 |
entityType、entityId | string | 是 | 被變更的 entity。 |
action | string | 是 | 例如 field.corrected 的重要操作。 |
actorType、actorId | string | 是 | 授權使用者或服務參照。 |
occurredAt | ISO-8601 datetime | 是 | 不可變更的事件時間。 |
reasonCode | string 或 null | 否 | 受控制的業務原因。 |
changes | object | 是 | 已去除機密的前後值或變更欄位名稱。 |
correlationId | string | 是 | Request 或 workflow correlation 參照。 |
範例 JSON
{
"vessel": {
"vesselId": "vessel_demo_001",
"name": "Example Vessel 01",
"registrationNumber": null,
"flagState": "TW",
"organizationId": "organization_demo_001",
"createdAt": "2026-07-01T08:00:00Z",
"updatedAt": "2026-07-28T04:00:00Z"
},
"trip": {
"tripId": "trip_demo_001",
"vesselId": "vessel_demo_001",
"tripNumber": "DEMO-2026-001",
"departedAt": "2026-07-20T00:00:00Z",
"returnedAt": null,
"departurePort": "PORT_DEMO_A",
"returnPort": null,
"status": "active"
},
"haulEvent": {
"eventId": "event_demo_001",
"tripId": "trip_demo_001",
"eventType": "haul",
"startedAt": "2026-07-27T02:10:00Z",
"endedAt": "2026-07-27T02:42:00Z",
"location": {
"latitude": 23.5,
"longitude": 121.0
},
"sourceReportIds": ["source_report_demo_001"]
},
"sourceReport": {
"sourceReportId": "source_report_demo_001",
"tripId": "trip_demo_001",
"eventId": "event_demo_001",
"sourceType": "ai_analysis",
"sourceReference": "recording_group_demo_001",
"capturedAt": "2026-07-27T02:20:14Z",
"version": "1.0",
"processingStatus": "ready"
},
"extractedFields": [
{
"extractedFieldId": "field_demo_001",
"sourceReportId": "source_report_demo_001",
"fieldKey": "catch.species_code",
"rawValue": "YFT",
"normalizedValue": "YFT",
"sourcePointer": {
"segmentId": "segment_demo_001",
"cameraId": "camera_demo_01",
"timestampSeconds": 614
},
"extractionMethod": "model",
"confidence": {
"score": 0.94,
"level": "high",
"method": "object_detection",
"modelVersion": "demo-model-1.0"
},
"reviewStatus": {
"status": "accepted",
"reviewedValue": "YFT",
"reasonCode": null,
"reviewedBy": "reviewer_demo_001",
"reviewedAt": "2026-07-28T03:45:00Z"
}
}
],
"auditLogs": [
{
"auditLogId": "audit_demo_001",
"entityType": "ExtractedField",
"entityId": "field_demo_001",
"action": "field.accepted",
"actorType": "user",
"actorId": "reviewer_demo_001",
"occurredAt": "2026-07-28T03:45:00Z",
"reasonCode": null,
"changes": {
"reviewStatus": {
"before": "pending",
"after": "accepted"
}
},
"correlationId": "request_demo_001"
}
]
}