#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
模块名称：performance.py
模块描述：性能测试数据库模型定义

作者：czj
创建日期：2026-08-12
最后修改：2026-08-12
"""

from datetime import datetime
from typing import Optional
from sqlalchemy import String, Integer, Float, JSON, DateTime, Text, Boolean, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.database import Base


class PerformanceProject(Base):
    """
    性能测试项目数据库模型

    用于组织性能测试任务，区分标准版/项目定制场景。

    Attributes:
        id (str): 项目唯一标识 (proj_xxx)
        name (str): 项目名称
        description (str): 项目描述
        project_type (str): 类型: standard(标准)/custom(项目定制)
        icon (str): 项目图标
        sort_order (int): 排序序号
        created_at (datetime): 创建时间
        updated_at (datetime): 更新时间
    """

    __tablename__ = "performance_projects"

    id: Mapped[str] = mapped_column(String(64), primary_key=True, comment="项目唯一标识")
    name: Mapped[str] = mapped_column(String(200), default="", comment="项目名称")
    description: Mapped[str] = mapped_column(Text, default="", comment="项目描述")
    project_type: Mapped[str] = mapped_column(
        String(20), default="standard", comment="类型: standard(标准)/custom(项目定制)"
    )
    icon: Mapped[str] = mapped_column(String(50), default="folder", comment="项目图标")
    sort_order: Mapped[int] = mapped_column(Integer, default=0, comment="排序序号")
    created_at: Mapped[datetime] = mapped_column(
        DateTime, default=datetime.utcnow, comment="创建时间"
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间"
    )

    def __repr__(self) -> str:
        return f"<PerformanceProject(id={self.id}, name={self.name}, type={self.project_type})>"

    def to_dict(self) -> dict:
        """转换为字典"""
        return {
            "id": self.id,
            "name": self.name,
            "description": self.description,
            "project_type": self.project_type,
            "icon": self.icon,
            "sort_order": self.sort_order,
            "created_at": self.created_at.isoformat() if self.created_at else None,
            "updated_at": self.updated_at.isoformat() if self.updated_at else None,
        }


class PerformanceTask(Base):
    """
    性能测试任务数据库模型

    记录每次性能测试的整体信息，包括压测配置、结果统计等。

    Attributes:
        id (str): 任务唯一标识
        name (str): 任务名称
        description (str): 任务描述
        status (str): 状态 (pending/running/completed/failed/cancelled)
        project_id (str): 归属项目ID (nullable)

        # 目标配置
        target_url (str): 目标接口完整 URL
        method (str): HTTP 方法 (GET/POST/PUT/DELETE)
        headers (dict): 自定义请求头
        body (dict): 请求体
        body_template (dict): 请求体模板（支持变量替换）

        # 压测配置
        mode (str): 压测模式 (concurrent/qps/step)
        concurrency (int): 并发数（并发模式）
        qps_target (int): 目标 QPS（QPS 模式）
        duration (int): 压测时长（秒）
        ramp_up (int): 预热时间（秒）
        step_concurrency (list): 阶梯并发配置，如 [10, 50, 100, 200]
        step_duration (int): 每阶梯持续时间（秒）

        # 认证配置
        auth_required (bool): 是否需要登录 Token
        account_key (str): 使用的账号 (superadmin/admin/user)

        # 断言配置
        assertions (list): 断言规则列表

        # 结果统计
        total_requests (int): 总请求数
        success_count (int): 成功数
        fail_count (int): 失败数
        min_response_time (float): 最小响应时间 (ms)
        max_response_time (float): 最大响应时间 (ms)
        avg_response_time (float): 平均响应时间 (ms)
        p50_response_time (float): P50 响应时间 (ms)
        p90_response_time (float): P90 响应时间 (ms)
        p99_response_time (float): P99 响应时间 (ms)
        actual_tps (float): 实际吞吐量
        error_rate (float): 错误率

        # 时间
        start_time (datetime): 开始时间
        end_time (datetime): 结束时间
        duration_actual (float): 实际耗时（秒）
        created_at (datetime): 创建时间
        updated_at (datetime): 更新时间
    """

    __tablename__ = "performance_tasks"

    id: Mapped[str] = mapped_column(String(64), primary_key=True, comment="任务ID")
    name: Mapped[str] = mapped_column(String(200), default="", comment="任务名称")
    description: Mapped[str] = mapped_column(Text, default="", comment="任务描述")
    status: Mapped[str] = mapped_column(
        String(20),
        default="pending",
        comment="状态: pending/running/completed/failed/cancelled"
    )
    error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True, comment="错误信息")
    project_id: Mapped[Optional[str]] = mapped_column(
        String(64), nullable=True, default=None, comment="归属项目ID"
    )

    # 目标配置
    target_url: Mapped[str] = mapped_column(Text, default="", comment="目标接口完整URL")
    method: Mapped[str] = mapped_column(String(10), default="GET", comment="HTTP方法")
    headers: Mapped[dict] = mapped_column(JSON, default=dict, comment="自定义请求头")
    body: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True, comment="请求体")
    body_template: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True, comment="请求体模板")

    # 压测配置
    mode: Mapped[str] = mapped_column(
        String(20),
        default="concurrent",
        comment="压测模式: concurrent/qps/step"
    )
    concurrency: Mapped[int] = mapped_column(Integer, default=10, comment="并发数")
    qps_target: Mapped[int] = mapped_column(Integer, default=100, comment="目标QPS")
    duration: Mapped[int] = mapped_column(Integer, default=60, comment="压测时长(秒)")
    ramp_up: Mapped[int] = mapped_column(Integer, default=10, comment="预热时间(秒)")
    step_concurrency: Mapped[list] = mapped_column(JSON, default=list, comment="阶梯并发配置")
    step_duration: Mapped[int] = mapped_column(Integer, default=60, comment="每阶梯时长(秒)")

    # ===== 场景增强字段（瞬时并发/长稳压测，2026-08-22 新增）=====
    # 场景类型: null=存量标准模式(concurrent/qps/step)，burst=瞬时并发，endurance=长稳压测
    scenario_type: Mapped[Optional[str]] = mapped_column(
        String(20), nullable=True, default=None, comment="场景类型: burst/endurance（null=标准模式）"
    )
    # 长稳接口列表（仅 endurance 场景使用），每接口独立线程组
    # 结构: [{"name":"","method":"","url":"","headers":{},"body":null,"thread_count":10,"assertions":[],"unique_fields":[]},...]
    scenario_apis: Mapped[Optional[list]] = mapped_column(
        JSON, nullable=True, default=None, comment="长稳接口列表，每接口独立线程数"
    )

    # 循环控制
    loop_type: Mapped[str] = mapped_column(
        String(10), default="finite", comment="循环类型: finite(有限次)/infinite(无限)"
    )
    loop_count: Mapped[int] = mapped_column(Integer, default=1, comment="有限循环次数")

    # 集合点（Synchronizing Timer，burst 场景使用）
    synchronizing_timer_enabled: Mapped[bool] = mapped_column(
        Boolean, default=False, comment="是否启用集合点"
    )
    synchronizing_timer_count: Mapped[int] = mapped_column(
        Integer, default=0, comment="集合点阈值，到达该数量线程后齐发"
    )
    synchronizing_timer_timeout: Mapped[int] = mapped_column(
        Integer, default=30000, comment="集合点等待超时(ms)"
    )

    # 错误动作
    error_action: Mapped[str] = mapped_column(
        String(10), default="continue", comment="错误动作: continue/stop"
    )

    # 思考时间（Think Time，endurance 场景使用）
    think_time_enabled: Mapped[bool] = mapped_column(
        Boolean, default=False, comment="是否启用思考时间"
    )
    think_time_min: Mapped[int] = mapped_column(
        Integer, default=1000, comment="均值/固定思考时间(ms)"
    )
    think_time_max: Mapped[int] = mapped_column(
        Integer, default=3000, comment="高斯分布偏差上限(ms)"
    )
    think_time_distribution: Mapped[str] = mapped_column(
        String(10), default="constant", comment="分布类型: constant/gaussian"
    )

    # CSV 参数化（endurance 场景使用）
    csv_parameterization_enabled: Mapped[bool] = mapped_column(
        Boolean, default=False, comment="是否启用CSV参数化"
    )
    csv_content: Mapped[str] = mapped_column(Text, default="", comment="CSV文件内容（首行表头）")
    csv_variable_mapping: Mapped[dict] = mapped_column(
        JSON, default=dict, comment="CSV列名→变量名映射"
    )
    # ===== 场景增强字段 END =====

    # 认证配置
    auth_required: Mapped[bool] = mapped_column(Boolean, default=True, comment="是否需要登录Token")
    account_key: Mapped[str] = mapped_column(String(50), default="superadmin", comment="使用的账号")
    login_username: Mapped[Optional[str]] = mapped_column(
        String(200), nullable=True, default=None, comment="自定义登录用户名"
    )
    login_password: Mapped[Optional[str]] = mapped_column(
        String(200), nullable=True, default=None, comment="自定义登录密码"
    )

    # 签名配置
    sign_request: Mapped[bool] = mapped_column(Boolean, default=False, comment="请求是否需要签名(X-RANDOM/X-TIMESTAMP/X-SIGN)")

    # 关联接口预设（可选，仅记录来源，不参与执行）
    preset_id: Mapped[Optional[str]] = mapped_column(
        String(64), nullable=True, default=None, comment="关联的接口预设ID"
    )

    # 断言配置
    assertions: Mapped[list] = mapped_column(JSON, default=list, comment="断言规则列表")

    # 响应捕获规则
    capture_rules: Mapped[Optional[list]] = mapped_column(JSON, nullable=True, comment="响应捕获规则列表")

    # 唯一性字段配置（每个请求自动生成唯一值，避免并发压测重复冲突）
    unique_fields: Mapped[list] = mapped_column(JSON, default=list, comment="唯一性字段配置列表")

    # 事务步骤列表（多步骤事务模式，None/空 = 单接口模式，向后兼容）
    steps: Mapped[Optional[list]] = mapped_column(JSON, nullable=True, comment="事务步骤列表")
    # 事务失败策略: fail_fast(失败即终止)/continue(继续执行后续步骤)
    transaction_fail_policy: Mapped[str] = mapped_column(
        String(20), default="fail_fast", comment="事务失败策略: fail_fast/continue"
    )
    # 单个事务超时（秒）
    transaction_timeout: Mapped[int] = mapped_column(Integer, default=60, comment="事务超时(秒)")

    # 执行机资源监控汇总（采样序列 + 统计，资源监控功能）
    resource_summary: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True, comment="执行机资源监控汇总")
    # 目标机资源监控汇总（SSH 方式采集被测系统资源）
    target_resource_summary: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True, comment="目标机资源监控汇总")
    # 事务处理时间汇总（每步骤统计，事务功能）
    transaction_summary: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True, comment="事务处理时间汇总")
    # 接口维度指标汇总（长稳压测多接口场景，按接口统计请求数/RT/TPS/错误率）
    api_summary: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True, comment="接口维度指标汇总")

    # 结果统计
    total_requests: Mapped[int] = mapped_column(Integer, default=0, comment="总请求数")
    success_count: Mapped[int] = mapped_column(Integer, default=0, comment="成功数")
    fail_count: Mapped[int] = mapped_column(Integer, default=0, comment="失败数")
    min_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="最小响应时间(ms)")
    max_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="最大响应时间(ms)")
    avg_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="平均响应时间(ms)")
    p50_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P50响应时间(ms)")
    p90_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P90响应时间(ms)")
    p99_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P99响应时间(ms)")
    actual_tps: Mapped[float] = mapped_column(Float, default=0.0, comment="实际吞吐量")
    error_rate: Mapped[float] = mapped_column(Float, default=0.0, comment="错误率(%)")

    # 增强指标
    p95_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P95响应时间(ms)")
    std_dev: Mapped[float] = mapped_column(Float, default=0.0, comment="响应时间标准差(ms)")
    latency_avg: Mapped[float] = mapped_column(Float, default=0.0, comment="平均首字节延迟(ms)")
    latency_min: Mapped[float] = mapped_column(Float, default=0.0, comment="最小首字节延迟(ms)")
    latency_max: Mapped[float] = mapped_column(Float, default=0.0, comment="最大首字节延迟(ms)")
    connect_time_avg: Mapped[float] = mapped_column(Float, default=0.0, comment="平均TCP连接时间(ms)")
    connect_time_max: Mapped[float] = mapped_column(Float, default=0.0, comment="最大TCP连接时间(ms)")
    peak_tps: Mapped[float] = mapped_column(Float, default=0.0, comment="峰值TPS")
    total_sent_bytes: Mapped[int] = mapped_column(Integer, default=0, comment="总发送字节数")
    total_received_bytes: Mapped[int] = mapped_column(Integer, default=0, comment="总接收字节数")
    apdex: Mapped[float] = mapped_column(Float, default=0.0, comment="Apdex用户满意度")
    error_type_timeout: Mapped[int] = mapped_column(Integer, default=0, comment="超时错误数")
    error_type_connect_error: Mapped[int] = mapped_column(Integer, default=0, comment="连接错误数")
    error_type_client_error: Mapped[int] = mapped_column(Integer, default=0, comment="客户端错误数")
    error_type_http_4xx: Mapped[int] = mapped_column(Integer, default=0, comment="HTTP 4xx错误数")
    error_type_http_5xx: Mapped[int] = mapped_column(Integer, default=0, comment="HTTP 5xx错误数")
    error_type_assertion: Mapped[int] = mapped_column(Integer, default=0, comment="断言失败数")
    status_2xx: Mapped[int] = mapped_column(Integer, default=0, comment="HTTP 2xx响应数")
    status_3xx: Mapped[int] = mapped_column(Integer, default=0, comment="HTTP 3xx响应数")
    status_4xx: Mapped[int] = mapped_column(Integer, default=0, comment="HTTP 4xx响应数")
    status_5xx: Mapped[int] = mapped_column(Integer, default=0, comment="HTTP 5xx响应数")

    # 时间
    start_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="开始时间")
    end_time: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="结束时间")
    duration_actual: Mapped[float] = mapped_column(Float, default=0.0, comment="实际耗时(秒)")
    created_at: Mapped[datetime] = mapped_column(
        DateTime, default=datetime.utcnow, comment="创建时间"
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, comment="更新时间"
    )

    # 关联快照（一对多）
    snapshots: Mapped[list["PerformanceSnapshot"]] = relationship(
        "PerformanceSnapshot",
        back_populates="task",
        cascade="all, delete-orphan"
    )

    # 执行记录（一对多，2026-08-24 新增）
    executions: Mapped[list["PerformanceExecution"]] = relationship(
        "PerformanceExecution",
        backref="task",
        cascade="all, delete-orphan",
        foreign_keys="PerformanceExecution.task_id"
    )

    def __repr__(self) -> str:
        """字符串表示"""
        return f"<PerformanceTask(id={self.id}, name={self.name}, status={self.status})>"

    def to_dict(self) -> dict:
        """
        转换为字典

        Returns:
            dict: 任务数据字典
        """
        return {
            "id": self.id,
            "name": self.name,
            "description": self.description,
            "status": self.status,
            "error_message": self.error_message,
            "project_id": self.project_id,
            "target_url": self.target_url,
            "method": self.method,
            "headers": self.headers or {},
            "body": self.body,
            "body_template": self.body_template,
            "mode": self.mode,
            "concurrency": self.concurrency,
            "qps_target": self.qps_target,
            "duration": self.duration,
            "ramp_up": self.ramp_up,
            "step_concurrency": self.step_concurrency or [],
            "step_duration": self.step_duration,
            "scenario_type": self.scenario_type,
            "scenario_apis": self.scenario_apis,
            "loop_type": self.loop_type,
            "loop_count": self.loop_count,
            "synchronizing_timer_enabled": self.synchronizing_timer_enabled,
            "synchronizing_timer_count": self.synchronizing_timer_count,
            "synchronizing_timer_timeout": self.synchronizing_timer_timeout,
            "error_action": self.error_action,
            "think_time_enabled": self.think_time_enabled,
            "think_time_min": self.think_time_min,
            "think_time_max": self.think_time_max,
            "think_time_distribution": self.think_time_distribution,
            "csv_parameterization_enabled": self.csv_parameterization_enabled,
            "csv_content": self.csv_content,
            "csv_variable_mapping": self.csv_variable_mapping,
            "auth_required": self.auth_required,
            "account_key": self.account_key,
            "login_username": self.login_username,
            "login_password": self.login_password,
            "sign_request": self.sign_request,
            "preset_id": self.preset_id,
            "assertions": self.assertions or [],
            "capture_rules": self.capture_rules or [],
            "unique_fields": self.unique_fields or [],
            "steps": self.steps,
            "transaction_fail_policy": self.transaction_fail_policy,
            "transaction_timeout": self.transaction_timeout,
            "resource_summary": self.resource_summary,
            "target_resource_summary": self.target_resource_summary,
            "transaction_summary": self.transaction_summary,
            "api_summary": self.api_summary,
            "total_requests": self.total_requests,
            "success_count": self.success_count,
            "fail_count": self.fail_count,
            "min_response_time": self.min_response_time,
            "max_response_time": self.max_response_time,
            "avg_response_time": self.avg_response_time,
            "p50_response_time": self.p50_response_time,
            "p90_response_time": self.p90_response_time,
            "p95_response_time": self.p95_response_time,
            "p99_response_time": self.p99_response_time,
            "std_dev": self.std_dev,
            "latency_avg": self.latency_avg,
            "latency_min": self.latency_min,
            "latency_max": self.latency_max,
            "connect_time_avg": self.connect_time_avg,
            "connect_time_max": self.connect_time_max,
            "peak_tps": self.peak_tps,
            "total_sent_bytes": self.total_sent_bytes,
            "total_received_bytes": self.total_received_bytes,
            "apdex": self.apdex,
            "error_type_timeout": self.error_type_timeout,
            "error_type_connect_error": self.error_type_connect_error,
            "error_type_client_error": self.error_type_client_error,
            "error_type_http_4xx": self.error_type_http_4xx,
            "error_type_http_5xx": self.error_type_http_5xx,
            "error_type_assertion": self.error_type_assertion,
            "status_2xx": self.status_2xx,
            "status_3xx": self.status_3xx,
            "status_4xx": self.status_4xx,
            "status_5xx": self.status_5xx,
            "actual_tps": self.actual_tps,
            "error_rate": self.error_rate,
            "start_time": self.start_time.isoformat() if self.start_time else None,
            "end_time": self.end_time.isoformat() if self.end_time else None,
            "duration_actual": self.duration_actual,
            "created_at": self.created_at.isoformat() if self.created_at else None,
            "updated_at": self.updated_at.isoformat() if self.updated_at else None,
        }


class PerformanceSnapshot(Base):
    """
    性能测试快照数据库模型

    每秒采集一次性能指标，用于实时监控和历史回放分析。

    Attributes:
        id (int): 自增主键
        task_id (str): 关联任务ID
        timestamp (float): 采集时间戳
        elapsed (float): 距开始时间（秒）
        concurrent_users (int): 当前并发数
        tps (float): 当前吞吐量
        avg_response_time (float): 平均响应时间 (ms)
        min_response_time (float): 最小响应时间 (ms)
        max_response_time (float): 最大响应时间 (ms)
        p50_response_time (float): P50 响应时间 (ms)
        p90_response_time (float): P90 响应时间 (ms)
        p99_response_time (float): P99 响应时间 (ms)
        success_count (int): 本周期成功请求数
        fail_count (int): 本周期失败请求数
        status_2xx (int): 2xx 状态码计数
        status_3xx (int): 3xx 状态码计数
        status_4xx (int): 4xx 状态码计数
        status_5xx (int): 5xx 状态码计数
    """

    __tablename__ = "performance_snapshots"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True, comment="快照ID")
    task_id: Mapped[str] = mapped_column(
        String(64), ForeignKey("performance_tasks.id", ondelete="CASCADE"), comment="关联任务ID"
    )
    timestamp: Mapped[float] = mapped_column(Float, comment="采集时间戳")
    elapsed: Mapped[float] = mapped_column(Float, comment="距开始时间(秒)")
    concurrent_users: Mapped[int] = mapped_column(Integer, default=0, comment="当前并发数")
    tps: Mapped[float] = mapped_column(Float, default=0.0, comment="当前吞吐量")
    avg_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="平均响应时间(ms)")
    min_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="最小响应时间(ms)")
    max_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="最大响应时间(ms)")
    p50_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P50响应时间(ms)")
    p90_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P90响应时间(ms)")
    p99_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P99响应时间(ms)")
    # 增强指标（快照级）
    p95_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P95响应时间(ms)")
    std_dev: Mapped[float] = mapped_column(Float, default=0.0, comment="响应时间标准差(ms)")
    latency_avg: Mapped[float] = mapped_column(Float, default=0.0, comment="平均首字节延迟(ms)")
    connect_time_avg: Mapped[float] = mapped_column(Float, default=0.0, comment="平均TCP连接时间(ms)")
    sent_bytes: Mapped[int] = mapped_column(Integer, default=0, comment="周期发送字节数")
    received_bytes: Mapped[int] = mapped_column(Integer, default=0, comment="周期接收字节数")
    peak_tps: Mapped[float] = mapped_column(Float, default=0.0, comment="当前峰值TPS")
    success_count: Mapped[int] = mapped_column(Integer, default=0, comment="本周期成功数")
    fail_count: Mapped[int] = mapped_column(Integer, default=0, comment="本周期失败数")
    status_2xx: Mapped[int] = mapped_column(Integer, default=0, comment="2xx计数")
    status_3xx: Mapped[int] = mapped_column(Integer, default=0, comment="3xx计数")
    status_4xx: Mapped[int] = mapped_column(Integer, default=0, comment="4xx计数")
    status_5xx: Mapped[int] = mapped_column(Integer, default=0, comment="5xx计数")

    # 关联任务
    task: Mapped["PerformanceTask"] = relationship("PerformanceTask", back_populates="snapshots")

    # 关联执行记录（2026-08-24 新增，SET NULL 兼容存量快照）
    execution_id: Mapped[Optional[str]] = mapped_column(
        String(64), ForeignKey("performance_executions.id", ondelete="SET NULL"),
        nullable=True, index=True, comment="关联执行记录ID"
    )
    execution: Mapped[Optional["PerformanceExecution"]] = relationship(
        "PerformanceExecution", back_populates="snapshots",
        foreign_keys=[execution_id]
    )

    def __repr__(self) -> str:
        """字符串表示"""
        return f"<PerformanceSnapshot(id={self.id}, task_id={self.task_id}, elapsed={self.elapsed}s)>"

    def to_dict(self) -> dict:
        """
        转换为字典

        Returns:
            dict: 快照数据字典
        """
        return {
            "id": self.id,
            "task_id": self.task_id,
            "execution_id": self.execution_id,
            "timestamp": self.timestamp,
            "elapsed": self.elapsed,
            "concurrent_users": self.concurrent_users,
            "tps": self.tps,
            "avg_response_time": self.avg_response_time,
            "min_response_time": self.min_response_time,
            "max_response_time": self.max_response_time,
            "p50_response_time": self.p50_response_time,
            "p90_response_time": self.p90_response_time,
            "p95_response_time": self.p95_response_time,
            "p99_response_time": self.p99_response_time,
            "std_dev": self.std_dev,
            "latency_avg": self.latency_avg,
            "connect_time_avg": self.connect_time_avg,
            "sent_bytes": self.sent_bytes,
            "received_bytes": self.received_bytes,
            "peak_tps": self.peak_tps,
            "success_count": self.success_count,
            "fail_count": self.fail_count,
            "status_2xx": self.status_2xx,
            "status_3xx": self.status_3xx,
            "status_4xx": self.status_4xx,
            "status_5xx": self.status_5xx,
        }


class PerformanceExecution(Base):
    """
    性能测试执行记录

    每次执行创建一条不可变记录，承载该次执行的全部结果数据。
    供执行历史查询、历史报告、历史监控回放使用。

    Attributes:
        id (str): 执行唯一标识 (exec_xxx)
        task_id (str): 关联任务ID
        task_name (str): 任务名快照
        project_id (str): 项目ID（冗余，便于按项目过滤）
        status (str): running/completed/failed/cancelled
        error_message (str): 失败原因
        start_time (datetime): 开始时间
        end_time (datetime): 结束时间
        duration_actual (float): 实际时长（秒）
        # 统计指标 ×34
        total_requests / success_count / fail_count / error_rate /
        actual_tps / peak_tps / min·max·avg·p50·p90·p95·p99_response_time /
        std_dev / latency_avg·min·max / connect_time_avg·max /
        apdex / total_sent·received_bytes /
        error_type_timeout/connect_error/client_error/http_4xx/http_5xx/assertion /
        status_2xx·3xx·4xx·5xx
        # 汇总 JSON
        resource_summary / target_resource_summary / transaction_summary / api_summary
        # 元数据
        snapshot_count (int): 快照条数
        triggered_by (str): manual/batch/legacy
        batch_id (str): 批量执行时关联批次
        created_at (datetime): 创建时间
    """

    __tablename__ = "performance_executions"

    id: Mapped[str] = mapped_column(String(64), primary_key=True, comment="执行唯一标识")
    task_id: Mapped[str] = mapped_column(
        String(64), ForeignKey("performance_tasks.id", ondelete="CASCADE"),
        index=True, comment="关联任务ID"
    )
    task_name: Mapped[str] = mapped_column(String(200), default="", comment="任务名快照")
    project_id: Mapped[Optional[str]] = mapped_column(
        String(64), nullable=True, index=True, comment="项目ID快照"
    )
    status: Mapped[str] = mapped_column(
        String(20), default="running", comment="状态: running/completed/failed/cancelled"
    )
    error_message: Mapped[Optional[str]] = mapped_column(
        Text, nullable=True, default=None, comment="失败原因"
    )
    start_time: Mapped[Optional[datetime]] = mapped_column(
        DateTime, nullable=True, default=None, comment="开始时间"
    )
    end_time: Mapped[Optional[datetime]] = mapped_column(
        DateTime, nullable=True, default=None, comment="结束时间"
    )
    duration_actual: Mapped[float] = mapped_column(Float, default=0.0, comment="实际时长(秒)")

    # 统计指标 ×34
    total_requests: Mapped[int] = mapped_column(Integer, default=0, comment="总请求数")
    success_count: Mapped[int] = mapped_column(Integer, default=0, comment="成功请求数")
    fail_count: Mapped[int] = mapped_column(Integer, default=0, comment="失败请求数")
    error_rate: Mapped[float] = mapped_column(Float, default=0.0, comment="错误率")
    actual_tps: Mapped[float] = mapped_column(Float, default=0.0, comment="实际TPS")
    peak_tps: Mapped[float] = mapped_column(Float, default=0.0, comment="峰值TPS")
    min_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="最小响应时间(ms)")
    max_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="最大响应时间(ms)")
    avg_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="平均响应时间(ms)")
    p50_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P50响应时间(ms)")
    p90_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P90响应时间(ms)")
    p95_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P95响应时间(ms)")
    p99_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P99响应时间(ms)")
    std_dev: Mapped[float] = mapped_column(Float, default=0.0, comment="标准差")
    latency_avg: Mapped[float] = mapped_column(Float, default=0.0, comment="平均延迟(ms)")
    latency_min: Mapped[float] = mapped_column(Float, default=0.0, comment="最小延迟(ms)")
    latency_max: Mapped[float] = mapped_column(Float, default=0.0, comment="最大延迟(ms)")
    connect_time_avg: Mapped[float] = mapped_column(Float, default=0.0, comment="平均连接时间(ms)")
    connect_time_max: Mapped[float] = mapped_column(Float, default=0.0, comment="最大连接时间(ms)")
    apdex: Mapped[float] = mapped_column(Float, default=0.0, comment="Apdex指数")
    total_sent_bytes: Mapped[int] = mapped_column(Integer, default=0, comment="发送字节数")
    total_received_bytes: Mapped[int] = mapped_column(Integer, default=0, comment="接收字节数")
    error_type_timeout: Mapped[int] = mapped_column(Integer, default=0, comment="超时错误数")
    error_type_connect_error: Mapped[int] = mapped_column(Integer, default=0, comment="连接错误数")
    error_type_client_error: Mapped[int] = mapped_column(Integer, default=0, comment="客户端错误数")
    error_type_http_4xx: Mapped[int] = mapped_column(Integer, default=0, comment="HTTP 4xx数")
    error_type_http_5xx: Mapped[int] = mapped_column(Integer, default=0, comment="HTTP 5xx数")
    error_type_assertion: Mapped[int] = mapped_column(Integer, default=0, comment="断言失败数")
    status_2xx: Mapped[int] = mapped_column(Integer, default=0, comment="2xx响应数")
    status_3xx: Mapped[int] = mapped_column(Integer, default=0, comment="3xx响应数")
    status_4xx: Mapped[int] = mapped_column(Integer, default=0, comment="4xx响应数")
    status_5xx: Mapped[int] = mapped_column(Integer, default=0, comment="5xx响应数")

    # 汇总 JSON
    resource_summary: Mapped[Optional[dict]] = mapped_column(
        JSON, nullable=True, default=None, comment="执行机资源汇总"
    )
    target_resource_summary: Mapped[Optional[dict]] = mapped_column(
        JSON, nullable=True, default=None, comment="目标机资源汇总"
    )
    transaction_summary: Mapped[Optional[list]] = mapped_column(
        JSON, nullable=True, default=None, comment="事务汇总"
    )
    api_summary: Mapped[Optional[list]] = mapped_column(
        JSON, nullable=True, default=None, comment="接口维度汇总"
    )

    # 元数据
    snapshot_count: Mapped[int] = mapped_column(Integer, default=0, comment="快照条数")
    triggered_by: Mapped[str] = mapped_column(
        String(20), default="manual", comment="触发方式: manual/batch/legacy"
    )
    batch_id: Mapped[Optional[str]] = mapped_column(
        String(64), nullable=True, default=None, comment="关联批次ID"
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime, default=datetime.utcnow, comment="创建时间"
    )

    # 关系
    snapshots: Mapped[list["PerformanceSnapshot"]] = relationship(
        "PerformanceSnapshot", back_populates="execution",
        cascade="all, delete-orphan",
        foreign_keys="PerformanceSnapshot.execution_id"
    )

    def __repr__(self) -> str:
        return f"<PerformanceExecution(id={self.id}, task_id={self.task_id}, status={self.status})>"

    def to_dict(self) -> dict:
        """序列化为字典"""
        return {
            "id": self.id,
            "task_id": self.task_id,
            "task_name": self.task_name,
            "project_id": self.project_id,
            "status": self.status,
            "error_message": self.error_message,
            "start_time": self.start_time.isoformat() if self.start_time else None,
            "end_time": self.end_time.isoformat() if self.end_time else None,
            "duration_actual": self.duration_actual,
            "total_requests": self.total_requests,
            "success_count": self.success_count,
            "fail_count": self.fail_count,
            "error_rate": self.error_rate,
            "actual_tps": self.actual_tps,
            "peak_tps": self.peak_tps,
            "min_response_time": self.min_response_time,
            "max_response_time": self.max_response_time,
            "avg_response_time": self.avg_response_time,
            "p50_response_time": self.p50_response_time,
            "p90_response_time": self.p90_response_time,
            "p95_response_time": self.p95_response_time,
            "p99_response_time": self.p99_response_time,
            "std_dev": self.std_dev,
            "latency_avg": self.latency_avg,
            "latency_min": self.latency_min,
            "latency_max": self.latency_max,
            "connect_time_avg": self.connect_time_avg,
            "connect_time_max": self.connect_time_max,
            "apdex": self.apdex,
            "total_sent_bytes": self.total_sent_bytes,
            "total_received_bytes": self.total_received_bytes,
            "error_type_timeout": self.error_type_timeout,
            "error_type_connect_error": self.error_type_connect_error,
            "error_type_client_error": self.error_type_client_error,
            "error_type_http_4xx": self.error_type_http_4xx,
            "error_type_http_5xx": self.error_type_http_5xx,
            "error_type_assertion": self.error_type_assertion,
            "status_2xx": self.status_2xx,
            "status_3xx": self.status_3xx,
            "status_4xx": self.status_4xx,
            "status_5xx": self.status_5xx,
            "resource_summary": self.resource_summary,
            "target_resource_summary": self.target_resource_summary,
            "transaction_summary": self.transaction_summary,
            "api_summary": self.api_summary,
            "snapshot_count": self.snapshot_count,
            "triggered_by": self.triggered_by,
            "batch_id": self.batch_id,
            "created_at": self.created_at.isoformat() if self.created_at else None,
        }


class PerformanceBatchExecution(Base):
    """
    批量执行记录数据库模型

    持久化存储批量执行记录，用于追踪批量执行的状态和结果。

    Attributes:
        id (str): 批量执行ID (batch_xxx)
        project_id (str): 关联项目ID（可选）
        task_ids (str): 执行的任务ID列表（JSON数组）
        status (str): running/completed/partially_failed
        total_count (int): 总任务数
        success_count (int): 成功数
        fail_count (int): 失败数
        started_at (datetime): 开始时间
        completed_at (datetime): 完成时间
        created_at (datetime): 创建时间
    """

    __tablename__ = "performance_batch_executions"

    id: Mapped[str] = mapped_column(String(64), primary_key=True, comment="批量执行ID")
    project_id: Mapped[Optional[str]] = mapped_column(
        String(64), nullable=True, default=None, comment="关联项目ID"
    )
    task_ids: Mapped[str] = mapped_column(Text, default="[]", comment="任务ID列表(JSON)")
    status: Mapped[str] = mapped_column(
        String(20), default="running", comment="状态: running/completed/partially_failed"
    )
    total_count: Mapped[int] = mapped_column(Integer, default=0, comment="总任务数")
    success_count: Mapped[int] = mapped_column(Integer, default=0, comment="成功数")
    fail_count: Mapped[int] = mapped_column(Integer, default=0, comment="失败数")
    started_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="开始时间")
    completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True, comment="完成时间")
    created_at: Mapped[datetime] = mapped_column(
        DateTime, default=datetime.utcnow, comment="创建时间"
    )

    def __repr__(self) -> str:
        return f"<PerformanceBatchExecution(id={self.id}, status={self.status})>"

    def to_dict(self) -> dict:
        return {
            "id": self.id,
            "project_id": self.project_id,
            "task_ids": self.task_ids,
            "status": self.status,
            "total_count": self.total_count,
            "success_count": self.success_count,
            "fail_count": self.fail_count,
            "started_at": self.started_at.isoformat() if self.started_at else None,
            "completed_at": self.completed_at.isoformat() if self.completed_at else None,
            "created_at": self.created_at.isoformat() if self.created_at else None,
        }


class PerformanceProjectReport(Base):
    """
    项目合并报告数据库模型

    存储项目维度的合并报告，将批次内所有任务的执行结果聚合为一份综合报告。

    Attributes:
        id (str): 报告唯一标识 (rpt_xxx)
        project_id (str): 关联项目ID
        batch_id (str): 关联批量执行ID（可选）
        task_ids (str): 包含的任务ID列表（JSON数组）

        # 聚合指标
        total_requests (int): 总请求数
        success_count (int): 总成功数
        fail_count (int): 总失败数
        error_rate (float): 整体错误率
        actual_tps (float): 整体 TPS
        avg_response_time (float): 平均响应时间（加权平均）
        min_response_time (float): 最小响应时间
        max_response_time (float): 最大响应时间
        p50_response_time (float): P50
        p90_response_time (float): P90
        p95_response_time (float): P95
        p99_response_time (float): P99
        std_dev (float): 标准差
        apdex (float): Apdex 用户满意度
        status_2xx (int): 2xx 状态码计数
        status_3xx (int): 3xx 状态码计数
        status_4xx (int): 4xx 状态码计数
        status_5xx (int): 5xx 状态码计数
        total_sent_bytes (int): 总发送字节数
        total_received_bytes (int): 总接收字节数
        peak_tps (float): 所有任务中最高峰值 TPS
        duration_actual (float): 总实际耗时

        # 任务明细
        task_summaries (str): 各任务摘要的 JSON 数组

        created_at (datetime): 创建时间
    """

    __tablename__ = "performance_project_reports"

    id: Mapped[str] = mapped_column(String(64), primary_key=True, comment="报告唯一标识")
    project_id: Mapped[str] = mapped_column(String(64), default="", comment="关联项目ID")
    batch_id: Mapped[Optional[str]] = mapped_column(
        String(64), nullable=True, default=None, comment="关联批量执行ID"
    )
    task_ids: Mapped[str] = mapped_column(Text, default="[]", comment="任务ID列表(JSON)")

    # 聚合指标
    total_requests: Mapped[int] = mapped_column(Integer, default=0, comment="总请求数")
    success_count: Mapped[int] = mapped_column(Integer, default=0, comment="总成功数")
    fail_count: Mapped[int] = mapped_column(Integer, default=0, comment="总失败数")
    error_rate: Mapped[float] = mapped_column(Float, default=0.0, comment="整体错误率")
    actual_tps: Mapped[float] = mapped_column(Float, default=0.0, comment="整体TPS")
    avg_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="平均响应时间(ms)")
    min_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="最小响应时间(ms)")
    max_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="最大响应时间(ms)")
    p50_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P50响应时间(ms)")
    p90_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P90响应时间(ms)")
    p95_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P95响应时间(ms)")
    p99_response_time: Mapped[float] = mapped_column(Float, default=0.0, comment="P99响应时间(ms)")
    std_dev: Mapped[float] = mapped_column(Float, default=0.0, comment="标准差(ms)")
    apdex: Mapped[float] = mapped_column(Float, default=0.0, comment="Apdex用户满意度")
    status_2xx: Mapped[int] = mapped_column(Integer, default=0, comment="2xx计数")
    status_3xx: Mapped[int] = mapped_column(Integer, default=0, comment="3xx计数")
    status_4xx: Mapped[int] = mapped_column(Integer, default=0, comment="4xx计数")
    status_5xx: Mapped[int] = mapped_column(Integer, default=0, comment="5xx计数")
    total_sent_bytes: Mapped[int] = mapped_column(Integer, default=0, comment="总发送字节数")
    total_received_bytes: Mapped[int] = mapped_column(Integer, default=0, comment="总接收字节数")
    peak_tps: Mapped[float] = mapped_column(Float, default=0.0, comment="峰值TPS")
    duration_actual: Mapped[float] = mapped_column(Float, default=0.0, comment="总实际耗时(秒)")

    # 任务明细
    task_summaries: Mapped[str] = mapped_column(Text, default="[]", comment="任务摘要JSON")

    # 时间
    created_at: Mapped[datetime] = mapped_column(
        DateTime, default=datetime.utcnow, comment="创建时间"
    )

    def __repr__(self) -> str:
        return f"<PerformanceProjectReport(id={self.id}, project_id={self.project_id})>"

    def to_dict(self) -> dict:
        return {
            "id": self.id,
            "project_id": self.project_id,
            "batch_id": self.batch_id,
            "task_ids": self.task_ids,
            "total_requests": self.total_requests,
            "success_count": self.success_count,
            "fail_count": self.fail_count,
            "error_rate": self.error_rate,
            "actual_tps": self.actual_tps,
            "avg_response_time": self.avg_response_time,
            "min_response_time": self.min_response_time,
            "max_response_time": self.max_response_time,
            "p50_response_time": self.p50_response_time,
            "p90_response_time": self.p90_response_time,
            "p95_response_time": self.p95_response_time,
            "p99_response_time": self.p99_response_time,
            "std_dev": self.std_dev,
            "apdex": self.apdex,
            "status_2xx": self.status_2xx,
            "status_3xx": self.status_3xx,
            "status_4xx": self.status_4xx,
            "status_5xx": self.status_5xx,
            "total_sent_bytes": self.total_sent_bytes,
            "total_received_bytes": self.total_received_bytes,
            "peak_tps": self.peak_tps,
            "duration_actual": self.duration_actual,
            "task_summaries": self.task_summaries,
            "created_at": self.created_at.isoformat() if self.created_at else None,
        }