## 数据库表结构设计

- **DDS-Section**: 10. 数据库详细设计（SQLite + GORM）
- **DDS-Lines**: L1135-L1347

### Extract

#### ER 关系总览

```
IMPORT_BATCHES ──1:N──> SOURCE_FILES ──1:N──> RAW_RECORDS
IMPORT_BATCHES ──1:N──> TRANSACTIONS
RAW_RECORDS ──1:1──> TRANSACTIONS (normalizes_to)
TRANSACTIONS ──1:N──> DEDUP_RELATIONS
TRANSACTIONS ──1:N──> LINK_RELATIONS
TRANSACTIONS ──1:N──> RULE_HITS
RULES ──1:N──> RULE_HITS
IMPORT_BATCHES ──1:N──> IMPORT_TASKS ──1:N──> IMPORT_RESULTS
TRANSACTIONS ──1:N──> AUDIT_LOGS
```

共 **11 张核心表**。

#### 表结构定义

##### 1. IMPORT_BATCHES — 导入批次

| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | varchar(36) | PK | UUID 主键 |
| status | varchar(32) | | 批次状态 |
| total_files | int | | 文件总数 |
| total_records | int | | 记录总数 |
| success_count | int | | 成功数 |
| failed_count | int | | 失败数 |
| duplicate_count | int | | 重复数 |
| created_at | datetime | | 创建时间 |
| updated_at | datetime | | 更新时间 |

##### 2. SOURCE_FILES — 源文件

| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | varchar(36) | PK | UUID 主键 |
| batch_id | varchar(36) | FK | 批次 ID |
| file_name | varchar(255) | | 原始文件名 |
| file_hash | varchar(64) | INDEX | 文件 SHA256 哈希 |
| source_platform | varchar(32) | | 来源平台 |
| file_type | varchar(16) | | csv/xlsx/txt |
| file_size | int | | 文件大小(bytes) |
| uploaded_at | datetime | | 上传时间 |

##### 3. RAW_RECORDS — 原始记录

| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | varchar(36) | PK | UUID 主键 |
| source_file_id | varchar(36) | FK | 来源文件 ID |
| row_no | int | | 行号 |
| source_platform | varchar(32) | | 平台 |
| source_record_id | varchar(128) | | 原始流水号 |
| row_fingerprint | varchar(64) | INDEX | 行指纹 SHA256 |
| raw_payload | text | | 原始 JSON 快照 |
| parse_status | varchar(32) | | 解析状态 |
| parse_error | text | | 错误信息 |

##### 4. TRANSACTIONS — 统一交易记录（核心表）

| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | varchar(36) | PK | UUID 主键 |
| transaction_id | varchar(64) | UNIQUE | 业务唯一 ID |
| batch_id | varchar(36) | INDEX | 导入批次 |
| raw_record_id | varchar(36) | | 原始记录 ID |
| source_platform | varchar(32) | INDEX(组合) | 来源平台 |
| source_record_id | varchar(128) | INDEX(组合) | 原始记录号 |
| trade_time | datetime | INDEX, NOT NULL | 交易时间 |
| amount | decimal(18,6) | NOT NULL | 金额 |
| currency | varchar(16) | DEFAULT 'CNY' | 币种 |
| direction | varchar(16) | NOT NULL | 方向 |
| counterparty | varchar(255) | | 对手方 |
| merchant_name | varchar(255) | | 商户名 |
| category_raw | varchar(128) | | 原始分类 |
| category_mapped | varchar(128) | | 映射分类 |
| account_mapped | varchar(128) | | 映射账户 |
| tags | varchar(512) | | 标签(逗号分隔) |
| order_id | varchar(128) | INDEX | 订单号 |
| parent_order_id | varchar(128) | | 父链路号 |
| payment_method | varchar(128) | | 支付方式 |
| note | text | | 备注 |
| raw_payload | text | | 原始记录 JSON |
| row_fingerprint | varchar(64) | INDEX | 行指纹 |
| status | varchar(32) | INDEX, DEFAULT 'PENDING_CLEAN' | 状态 |
| firefly_txn_id | varchar(128) | | Firefly 交易 ID |
| imported_at | datetime | | 导入时间 |
| created_at | datetime | | 创建时间 |
| updated_at | datetime | | 更新时间 |

##### 5. DEDUP_RELATIONS — 去重关系

| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | varchar(36) | PK | UUID 主键 |
| src_transaction_id | varchar(36) | FK, INDEX | 原交易 ID |
| target_transaction_id | varchar(36) | FK | 目标交易 ID |
| relation_type | varchar(16) | | strict/fuzzy |
| confidence | int | | 置信度 0-100 |
| status | varchar(16) | | auto/confirmed/rejected |
| reason_json | text | | 判定依据 JSON |
| created_at | datetime | | 创建时间 |

##### 6. LINK_RELATIONS — 链路关系

| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | varchar(36) | PK | UUID 主键 |
| parent_transaction_id | varchar(36) | FK | 主交易 ID |
| child_transaction_id | varchar(36) | FK | 子交易 ID |
| link_type | varchar(16) | | transfer/order/refund/fee |
| reason_json | text | | 关联依据 JSON |
| created_at | datetime | | 创建时间 |

##### 7. RULES — 规则定义

| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | varchar(36) | PK | UUID 主键 |
| rule_type | varchar(32) | INDEX(组合) | 规则类型 |
| priority | int | INDEX(组合) | 优先级 |
| platform_scope | varchar(32) | | 平台范围 |
| conditions_json | text | | 条件 JSON |
| actions_json | text | | 动作 JSON |
| enabled | boolean | | 是否启用 |
| description | varchar(255) | | 规则描述 |
| created_at | datetime | | 创建时间 |
| updated_at | datetime | | 更新时间 |

##### 8. RULE_HITS — 规则命中记录

| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | varchar(36) | PK | UUID 主键 |
| transaction_id | varchar(36) | FK | 交易 ID |
| rule_id | varchar(36) | FK | 规则 ID |
| matched_condition | text | | 命中条件摘要 |
| before_value | text | | 变更前值 |
| after_value | text | | 变更后值 |
| created_at | datetime | | 执行时间 |

##### 9. IMPORT_TASKS — 导入任务

| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | varchar(36) | PK | UUID 主键 |
| batch_id | varchar(36) | FK | 批次 ID |
| export_mode | varchar(16) | | api/csv |
| status | varchar(32) | | pending/running/success/partial_failed/failed |
| total_count | int | | 总记录数 |
| success_count | int | | 成功数 |
| failed_count | int | | 失败数 |
| started_at | datetime | | 开始时间 |
| finished_at | datetime | | 完成时间 |

##### 10. IMPORT_RESULTS — 导入结果

| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | varchar(36) | PK | UUID 主键 |
| task_id | varchar(36) | FK | 任务 ID |
| transaction_id | varchar(36) | FK | 交易 ID |
| status | varchar(16) | | success/failed |
| error_code | varchar(32) | | 错误码 |
| error_message | text | | 错误描述 |
| firefly_txn_id | varchar(128) | | Firefly 返回 ID |
| retry_count | int | | 重试次数 |
| created_at | datetime | | 创建时间 |

##### 11. AUDIT_LOGS — 审计日志

| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | varchar(36) | PK | UUID 主键 |
| entity_type | varchar(64) | | 实体类型 |
| entity_id | varchar(36) | | 实体 ID |
| action | varchar(32) | | 操作类型 |
| before_snapshot | text | | 变更前快照 |
| after_snapshot | text | | 变更后快照 |
| operator | varchar(64) | | 操作者 |
| created_at | datetime | | 操作时间 |

#### GORM Model 示例（Transaction）

```go
type Transaction struct {
    ID               string          `gorm:"primaryKey;type:varchar(36)"`
    TransactionID    string          `gorm:"uniqueIndex;type:varchar(64)"`
    BatchID          string          `gorm:"index;type:varchar(36)"`
    RawRecordID      string          `gorm:"type:varchar(36)"`
    SourcePlatform   string          `gorm:"type:varchar(32);index:idx_platform_record"`
    SourceRecordID   string          `gorm:"type:varchar(128);index:idx_platform_record"`
    TradeTime        time.Time       `gorm:"index;not null"`
    Amount           decimal.Decimal `gorm:"type:decimal(18,6);not null"`
    Currency         string          `gorm:"type:varchar(16);default:'CNY'"`
    Direction        string          `gorm:"type:varchar(16);not null"`
    Counterparty     string          `gorm:"type:varchar(255)"`
    MerchantName     string          `gorm:"type:varchar(255)"`
    CategoryRaw      string          `gorm:"type:varchar(128)"`
    CategoryMapped   string          `gorm:"type:varchar(128)"`
    AccountMapped    string          `gorm:"type:varchar(128)"`
    Tags             string          `gorm:"type:varchar(512)"`
    OrderID          string          `gorm:"index;type:varchar(128)"`
    ParentOrderID    string          `gorm:"type:varchar(128)"`
    PaymentMethod    string          `gorm:"type:varchar(128)"`
    Note             string          `gorm:"type:text"`
    RawPayload       string          `gorm:"type:text"`
    RowFingerprint   string          `gorm:"index;type:varchar(64)"`
    Status           string          `gorm:"index;type:varchar(32);default:'PENDING_CLEAN'"`
    FireflyTxnID     string          `gorm:"type:varchar(128)"`
    ImportedAt       *time.Time
    CreatedAt        time.Time
    UpdatedAt        time.Time
}

func (Transaction) TableName() string { return "transactions" }
```

#### SQLite 性能配置

```go
func initDB(dbPath string) *gorm.DB {
    db, _ := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
    sqlDB, _ := db.DB()
    sqlDB.SetMaxOpenConns(1)    // SQLite 单写
    sqlDB.SetMaxIdleConns(10)
    db.Exec("PRAGMA journal_mode=WAL")
    db.Exec("PRAGMA synchronous=NORMAL")
    db.Exec("PRAGMA cache_size=-64000") // 64MB cache
    return db
}
```

#### 事务边界设计

```
事务 1: 文件入库 + 原始记录入库
事务 2: 标准化结果落库
事务 3: 去重/链路关系落库
事务 4: 规则命中落库
事务 5: 导入结果落库
```

每阶段独立事务，使用 `CreateInBatches` 每批 500 条。
