# MySQL schema Class 初稿

## 1. 文件目的

本文件用於整理 MySQL TaskStore PoC 第一版的 C# schema class 初稿。

目標是讓 MySQL 欄位型別、索引與描述可以先在 C# Class 中維護，再由 Schema Generator 產生 MySQL 5.6.2 相容 DDL。

本文件仍屬設計初稿，不代表已經同意修改程式 repo、安裝套件、建立資料表、變更 public method 或改變專案啟動方式。

## 2. 設計原則

- 第一版只保存任務執行歷程，不保存完整設備主檔、案場資料、權限或報表。
- Core 不依賴資料庫 class；schema class 應放在 Infrastructure / DB 相關模組。
- MySQL 5.6.2 不使用 `JSON` 欄位，原始資料以 `TEXT` 或 `LONGTEXT` 保存。
- MySQL 5.6.2 不使用小數秒時間欄位，耗時以 `TimeTakenMs` 另外保存。
- 第一版不使用外鍵約束，先以 `TaskId`、`NodeExecutionId`、`CommandExecutionId` 建立邏輯關聯與索引。
- 不自動刪欄位、不自動改欄位型別、不自動搬資料。
- 欄位名稱採 snake_case，C# property 採 PascalCase。

## 3. 共用型別規則

| 語意 | MySQL 型別 | C# 型別 | 說明 |
|---|---|---|---|
| 識別碼 | `varchar(64)` | `string` | TaskId、NodeId、DeviceId 等 |
| 名稱 | `varchar(128)` | `string` | WorkflowName、NodeName、DeviceName |
| 狀態 | `varchar(32)` | `string` | running、completed、failed 等 |
| 錯誤碼 | `varchar(64)` | `string` | 標準 ErrorCode |
| 訊息 | `text` | `string` | ResultMessage、ErrorMessage |
| 原始資料 | `longtext` | `string` | RawRequest、RawResponse、AdapterData、StackTrace |
| 時間 | `datetime` | `DateTime?` | 不使用小數秒 |
| 耗時 | `int` | `int?` | 毫秒 |

## 4. 第一版資料表總覽

| C# Class | Table | 用途 |
|---|---|---|
| `TaskExecutionRecord` | `task_executions` | 任務層級狀態與摘要 |
| `NodeExecutionRecord` | `node_executions` | 節點執行歷程 |
| `CommandExecutionRecord` | `command_executions` | Adapter / Device command 執行結果 |
| `TaskErrorRecord` | `task_errors` | 錯誤快照與例外資訊 |

## 5. TaskExecutionRecord

用途：保存一次 Workflow / Task 執行的總體狀態。

建議 Table：`task_executions`

建議 Class：

```csharp
[DbTable("task_executions", Description = "任務執行紀錄")]
[DbIndex("idx_task_workflow", "workflow_id")]
[DbIndex("idx_task_status", "status")]
[DbIndex("idx_task_started_at", "started_at")]
[DbIndex("idx_task_error_code", "error_code")]
public class TaskExecutionRecord
{
    [DbColumn("task_id", "varchar(64)", IsPrimaryKey = true, IsRequired = true, Description = "任務識別碼")]
    public string TaskId { get; set; }

    [DbColumn("workflow_id", "varchar(64)", IsRequired = true, Description = "Workflow 識別碼")]
    public string WorkflowId { get; set; }

    [DbColumn("workflow_name", "varchar(128)", Description = "Workflow 顯示名稱")]
    public string WorkflowName { get; set; }

    [DbColumn("workflow_version", "varchar(32)", Description = "Workflow 版本")]
    public string WorkflowVersion { get; set; }

    [DbColumn("status", "varchar(32)", IsRequired = true, Description = "任務狀態")]
    public string Status { get; set; }

    [DbColumn("start_node_id", "varchar(64)", Description = "起始節點識別碼")]
    public string StartNodeId { get; set; }

    [DbColumn("current_node_id", "varchar(64)", Description = "目前或最後執行節點識別碼")]
    public string CurrentNodeId { get; set; }

    [DbColumn("started_at", "datetime", Description = "開始時間")]
    public DateTime? StartedAt { get; set; }

    [DbColumn("ended_at", "datetime", Description = "結束時間")]
    public DateTime? EndedAt { get; set; }

    [DbColumn("time_taken_ms", "int", Description = "任務總耗時毫秒")]
    public int? TimeTakenMs { get; set; }

    [DbColumn("result_code", "varchar(64)", Description = "任務結果代碼")]
    public string ResultCode { get; set; }

    [DbColumn("result_message", "text", Description = "任務結果訊息")]
    public string ResultMessage { get; set; }

    [DbColumn("error_code", "varchar(64)", Description = "最後錯誤代碼")]
    public string ErrorCode { get; set; }

    [DbColumn("operator_name", "varchar(64)", Description = "操作者或觸發來源")]
    public string OperatorName { get; set; }

    [DbColumn("host_name", "varchar(128)", Description = "執行主機名稱")]
    public string HostName { get; set; }

    [DbColumn("app_version", "varchar(32)", Description = "應用程式版本")]
    public string AppVersion { get; set; }

    [DbColumn("created_at", "datetime", IsRequired = true, Description = "資料建立時間")]
    public DateTime CreatedAt { get; set; }

    [DbColumn("updated_at", "datetime", Description = "資料更新時間")]
    public DateTime? UpdatedAt { get; set; }
}
```

## 6. NodeExecutionRecord

用途：保存每個 Workflow Node 的執行結果、重試與錯誤摘要。

建議 Table：`node_executions`

建議索引：

| Index | Columns | 說明 |
|---|---|---|
| `idx_node_task_id` | `task_id` | 查詢指定任務所有節點 |
| `idx_node_task_node` | `task_id`, `node_id` | 查詢任務中特定節點 |
| `idx_node_status` | `status` | 查詢節點狀態 |
| `idx_node_started_at` | `started_at` | 依時間查詢 |

建議欄位：

| Property | Column | MySQL 型別 | 必填 | 描述 |
|---|---|---|---|---|
| `NodeExecutionId` | `node_execution_id` | `varchar(64)` | 是，PK | 節點執行紀錄識別碼 |
| `TaskId` | `task_id` | `varchar(64)` | 是 | 所屬任務識別碼 |
| `NodeId` | `node_id` | `varchar(64)` | 是 | Workflow Node 識別碼 |
| `NodeName` | `node_name` | `varchar(128)` | 否 | 節點顯示名稱 |
| `SequenceNo` | `sequence_no` | `int` | 是 | 節點執行順序 |
| `Status` | `status` | `varchar(32)` | 是 | 節點狀態 |
| `DeviceId` | `device_id` | `varchar(64)` | 否 | 節點使用設備 |
| `CommandName` | `command_name` | `varchar(64)` | 否 | 節點執行命令 |
| `RetryCount` | `retry_count` | `int` | 否 | 已執行重試次數 |
| `TimeoutMs` | `timeout_ms` | `int` | 否 | 節點逾時毫秒 |
| `StartedAt` | `started_at` | `datetime` | 否 | 開始時間 |
| `EndedAt` | `ended_at` | `datetime` | 否 | 結束時間 |
| `TimeTakenMs` | `time_taken_ms` | `int` | 否 | 節點耗時毫秒 |
| `ErrorCode` | `error_code` | `varchar(64)` | 否 | 節點錯誤代碼 |
| `ErrorMessage` | `error_message` | `text` | 否 | 節點錯誤訊息 |
| `CreatedAt` | `created_at` | `datetime` | 是 | 資料建立時間 |

## 7. CommandExecutionRecord

用途：保存 Adapter / Device command 的實際執行紀錄，包含 RawRequest、RawResponse 與 AdapterData。

建議 Table：`command_executions`

建議索引：

| Index | Columns | 說明 |
|---|---|---|
| `idx_command_task_id` | `task_id` | 查詢任務下所有 command |
| `idx_command_node_execution` | `node_execution_id` | 查詢節點下 command |
| `idx_command_device` | `device_id`, `command_name` | 查詢設備命令 |
| `idx_command_status` | `status` | 查詢命令狀態 |
| `idx_command_started_at` | `started_at` | 依時間查詢 |

建議欄位：

| Property | Column | MySQL 型別 | 必填 | 描述 |
|---|---|---|---|---|
| `CommandExecutionId` | `command_execution_id` | `varchar(64)` | 是，PK | 命令執行紀錄識別碼 |
| `TaskId` | `task_id` | `varchar(64)` | 是 | 所屬任務識別碼 |
| `NodeExecutionId` | `node_execution_id` | `varchar(64)` | 否 | 所屬節點執行紀錄 |
| `NodeId` | `node_id` | `varchar(64)` | 否 | Workflow Node 識別碼 |
| `DeviceId` | `device_id` | `varchar(64)` | 是 | 設備識別碼 |
| `DeviceName` | `device_name` | `varchar(128)` | 否 | 設備顯示名稱 |
| `ConnectionType` | `connection_type` | `varchar(32)` | 否 | TCP、UDP、Serial、ModbusTcp 等 |
| `CommandName` | `command_name` | `varchar(64)` | 是 | 命令名稱 |
| `Status` | `status` | `varchar(32)` | 是 | 命令狀態 |
| `RetryCount` | `retry_count` | `int` | 否 | 命令重試次數 |
| `StartedAt` | `started_at` | `datetime` | 否 | 開始時間 |
| `EndedAt` | `ended_at` | `datetime` | 否 | 結束時間 |
| `TimeTakenMs` | `time_taken_ms` | `int` | 否 | 命令耗時毫秒 |
| `ResultCode` | `result_code` | `varchar(64)` | 否 | 命令結果代碼 |
| `RawRequest` | `raw_request` | `longtext` | 否 | 原始請求內容 |
| `RawResponse` | `raw_response` | `longtext` | 否 | 原始回應內容 |
| `AdapterData` | `adapter_data` | `longtext` | 否 | Adapter 附加資料 |
| `ErrorCode` | `error_code` | `varchar(64)` | 否 | 命令錯誤代碼 |
| `ErrorMessage` | `error_message` | `text` | 否 | 命令錯誤訊息 |
| `CreatedAt` | `created_at` | `datetime` | 是 | 資料建立時間 |

## 8. TaskErrorRecord

用途：保存錯誤快照，讓任務、節點、命令錯誤可被獨立查詢。

建議 Table：`task_errors`

建議索引：

| Index | Columns | 說明 |
|---|---|---|
| `idx_error_task_id` | `task_id` | 查詢指定任務錯誤 |
| `idx_error_code` | `error_code` | 依錯誤碼查詢 |
| `idx_error_occurred_at` | `occurred_at` | 依發生時間查詢 |
| `idx_error_device` | `device_id` | 查詢設備相關錯誤 |

建議欄位：

| Property | Column | MySQL 型別 | 必填 | 描述 |
|---|---|---|---|---|
| `ErrorRecordId` | `error_record_id` | `varchar(64)` | 是，PK | 錯誤紀錄識別碼 |
| `TaskId` | `task_id` | `varchar(64)` | 是 | 所屬任務識別碼 |
| `NodeExecutionId` | `node_execution_id` | `varchar(64)` | 否 | 所屬節點執行紀錄 |
| `CommandExecutionId` | `command_execution_id` | `varchar(64)` | 否 | 所屬命令執行紀錄 |
| `NodeId` | `node_id` | `varchar(64)` | 否 | Workflow Node 識別碼 |
| `DeviceId` | `device_id` | `varchar(64)` | 否 | 設備識別碼 |
| `CommandName` | `command_name` | `varchar(64)` | 否 | 命令名稱 |
| `ErrorCode` | `error_code` | `varchar(64)` | 是 | 標準錯誤代碼 |
| `ErrorMessage` | `error_message` | `text` | 否 | 錯誤訊息 |
| `ExceptionType` | `exception_type` | `varchar(128)` | 否 | Exception 型別 |
| `StackTrace` | `stack_trace` | `longtext` | 否 | Stack trace |
| `SourceLayer` | `source_layer` | `varchar(64)` | 否 | Core、Adapter、Infrastructure、Host |
| `OccurredAt` | `occurred_at` | `datetime` | 是 | 錯誤發生時間 |
| `CreatedAt` | `created_at` | `datetime` | 是 | 資料建立時間 |

## 9. 不建議第一版納入的欄位

| 欄位 | 延後原因 |
|---|---|
| `site_id` | 多案場尚未進入第二階段第一個 PoC |
| `tenant_id` | 權限與租戶模型尚未定義 |
| `user_id` | 使用者系統尚未導入 |
| `device_group_id` | 設備主檔與群組管理尚未定義 |
| `report_key` | 報表需求尚未定義 |
| `json` 型態欄位 | MySQL 5.6.2 不適合使用 |

## 10. 待確認問題

1. `CreatedAt` / `StartedAt` / `EndedAt` 是否統一由程式端寫入。
2. 時間是否統一保存 UTC，顯示時再轉本地時間。
3. 第一版是否允許 `RawRequest` / `RawResponse` 保存完整內容。
4. `AdapterData` 是否需限制大小或只保存摘要。
5. 第一版是否先不建立外鍵，只保留索引與邏輯關聯。
6. `OperatorName` 是否足夠，或需要等待未來使用者系統再補 `OperatorId`。

## 11. 建議下一步

MySQL 行前連線資訊確認請參考：[MySQL 行前連線資訊確認](./mysql-preflight-connection-checklist.md)。

建議下一步做「MySQL 行前連線資訊確認」，先確認測試 database、Host、Port、User、Password 提供方式與權限。

資訊齊全後再進入「Schema Attribute 實作前確認」與程式 repo 實作。
