# Models 模块

数据库模型模块，用于同步数据库表设计，提供 ORM 支持。

## 目录结构

```
src/models/
├── __init__.py          # 模块导出
├── database.py          # 数据库配置和连接管理
├── decode_video.py      # DecodeVideo 模型
└── README.md            # 本文件
```

## 环境变量配置

在使用模型之前，需要配置以下环境变量（如果使用默认配置，则无需设置）：

```bash
# 数据库配置（使用外网地址）
export DB_HOST=rm-t4nh1xx6o2a6vj8qu3o.mysql.singapore.rds.aliyuncs.com  # 数据库主机（默认已配置）
export DB_PORT=3306              # 数据库端口（默认: 3306）
export DB_USER=content_rw        # 数据库用户名（默认已配置）
export DB_PASSWORD=bC1aH4bA1lB0 # 数据库密码（默认已配置）
export DB_NAME=content-deconstruction  # 数据库名称（默认已配置）
```

或者在 `.env` 文件中配置：

```env
# 使用外网地址访问数据库
DB_HOST=rm-t4nh1xx6o2a6vj8qu3o.mysql.singapore.rds.aliyuncs.com
DB_PORT=3306
DB_USER=content_rw
DB_PASSWORD=bC1aH4bA1lB0
DB_NAME=content-deconstruction

# 如需使用内网地址，可切换为：
# DB_HOST=rm-t4nh1xx6o2a6vj8qu.mysql.singapore.rds.aliyuncs.com
```

**注意**：代码中已配置默认值，可以直接使用。如需覆盖，可通过环境变量设置。

## 快速开始

### 1. 初始化数据库

```python
from src.models import init_db

# 创建所有表（如果不存在）
init_db()
```

### 2. 使用模型

#### 创建记录

```python
from src.models import get_db, DecodeVideo, DecodeStatus

# 获取数据库会话
db = next(get_db())

# 创建新记录
video = DecodeVideo.create(
    task_id=12345,
    video_id="58840748",
    status=DecodeStatus.PENDING
)

# 保存到数据库
db.add(video)
db.commit()
db.close()
```

#### 查询记录

```python
from src.models import get_db, DecodeVideo, DecodeStatus

db = next(get_db())

# 根据 task_id 查询
video = db.query(DecodeVideo).filter_by(task_id=12345).first()

# 根据 video_id 查询
videos = db.query(DecodeVideo).filter_by(video_id="58840748").all()

# 根据状态查询
pending_videos = db.query(DecodeVideo).filter_by(status=DecodeStatus.PENDING).all()

# 查询所有记录
all_videos = db.query(DecodeVideo).all()

db.close()
```

#### 更新记录

```python
from src.models import get_db, DecodeVideo, DecodeStatus

db = next(get_db()

# 查询记录
video = db.query(DecodeVideo).filter_by(task_id=12345).first()

if video:
    # 更新状态
    video.update_status(DecodeStatus.EXECUTING)
    
    # 更新结果
    video.update_result('{"result": "..."}')
    
    # 或者直接更新字段
    video.status = DecodeStatus.SUCCESS
    video.result = '{"result": "..."}'
    
    db.commit()

db.close()
```

#### 删除记录

```python
from src.models import get_db, DecodeVideo

db = next(get_db())

video = db.query(DecodeVideo).filter_by(task_id=12345).first()
if video:
    db.delete(video)
    db.commit()

db.close()
```

### 3. 使用上下文管理器（推荐）

```python
from contextlib import contextmanager
from src.models import get_db, DecodeVideo, DecodeStatus

@contextmanager
def db_session():
    """数据库会话上下文管理器"""
    db = next(get_db())
    try:
        yield db
        db.commit()
    except Exception:
        db.rollback()
        raise
    finally:
        db.close()

# 使用示例
with db_session() as db:
    video = DecodeVideo.create(
        task_id=12345,
        video_id="58840748",
        status=DecodeStatus.PENDING
    )
    db.add(video)
    # 自动提交和关闭
```

## 模型说明

### DecodeVideo

对应数据库表 `decode_videos`

#### 字段

- `task_id` (BigInteger, Primary Key): 任务ID，非空
- `video_id` (String(100)): 视频ID，可为空，已建立索引
- `result` (Text): 解码结果（JSON 格式），可为空
- `status` (Integer): 状态，可为空，默认值为 0（待执行），已建立索引
  - 0: 待执行 (PENDING)
  - 1: 执行中 (EXECUTING)
  - 2: 执行成功 (SUCCESS)
  - 3: 执行失败 (FAILED)
- `error_reason` (Text): 失败原因，可为空
- `created_at` (DateTime): 创建时间，自动设置
- `updated_at` (DateTime): 更新时间，自动更新

#### 方法

- `to_dict()`: 将模型转换为字典
- `create()`: 类方法，创建新实例
- `update_status()`: 更新状态
- `update_result()`: 更新解码结果

### DecodeStatus

状态枚举类

```python
DecodeStatus.PENDING    # 0 - 待执行
DecodeStatus.EXECUTING  # 1 - 执行中
DecodeStatus.SUCCESS    # 2 - 执行成功
DecodeStatus.FAILED     # 3 - 执行失败
```

## 完整示例

```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
使用 DecodeVideo 模型的完整示例
"""

from contextlib import contextmanager
from src.models import get_db, DecodeVideo, DecodeStatus, init_db

@contextmanager
def db_session():
    """数据库会话上下文管理器"""
    db = next(get_db())
    try:
        yield db
        db.commit()
    except Exception:
        db.rollback()
        raise
    finally:
        db.close()


def main():
    # 1. 初始化数据库（首次使用）
    init_db()
    
    # 2. 创建新记录
    with db_session() as db:
        video = DecodeVideo.create(
            task_id=12345,
            video_id="58840748",
            status=DecodeStatus.PENDING
        )
        db.add(video)
        print(f"创建记录: {video}")
    
    # 3. 查询记录
    with db_session() as db:
        video = db.query(DecodeVideo).filter_by(task_id=12345).first()
        if video:
            print(f"查询结果: {video.to_dict()}")
    
    # 4. 更新状态
    with db_session() as db:
        video = db.query(DecodeVideo).filter_by(task_id=12345).first()
        if video:
            video.update_status(DecodeStatus.EXECUTING)
            print(f"更新状态为: {DecodeStatus.get_description(video.status)}")
    
    # 5. 更新结果
    with db_session() as db:
        video = db.query(DecodeVideo).filter_by(task_id=12345).first()
        if video:
            result = '{"result": "解码成功"}'
            video.update_result(result)
            print(f"更新结果: {video.result}")
    
    # 6. 查询所有待执行的记录
    with db_session() as db:
        pending_videos = db.query(DecodeVideo).filter_by(
            status=DecodeStatus.PENDING
        ).all()
        print(f"待执行任务数: {len(pending_videos)}")


if __name__ == "__main__":
    main()
```

## 注意事项

1. **数据库连接**: 使用 `get_db()` 获取数据库会话后，务必在最后调用 `db.close()` 关闭连接
2. **事务管理**: 修改数据后需要调用 `db.commit()` 提交事务，出错时调用 `db.rollback()` 回滚
3. **环境变量**: 确保正确配置数据库连接相关的环境变量
4. **表结构同步**: 如果数据库表结构发生变化，需要更新模型定义以保持同步
5. **索引**: `video_id` 和 `status` 字段已建立索引，适合用于查询条件

## 与工作流集成

可以在工作流中使用模型来保存和查询解码结果：

```python
from src.models import get_db, DecodeVideo, DecodeStatus

def save_decode_result(task_id: int, video_id: str, result: dict):
    """保存解码结果到数据库"""
    db = next(get_db())
    try:
        video = db.query(DecodeVideo).filter_by(task_id=task_id).first()
        if not video:
            video = DecodeVideo.create(task_id=task_id, video_id=video_id)
            db.add(video)
        
        import json
        video.update_result(json.dumps(result, ensure_ascii=False))
        db.commit()
    except Exception as e:
        db.rollback()
        raise
    finally:
        db.close()
```

