Files
meida_backend/migrations/versions/0006_report_tables.py
T
xsl d974526904 chore(prep): shared groundwork for batch-4 (report pipeline)
第四批(T15 编排/T16 视觉诊断/T17 文案校验)协调提交:
- 迁移 0006:rp_report(result JSONB 按阶段键承载中间态)/rp_input
 (derived_features 视觉中间态)/rp_share(V1 只建结构)
- ORM 三模型;错误码 40420/40920(文案无价格字样)
- 配置:report_pipeline_stages(dotted path 动态装配)+ vision/tier
- packages/report/pipeline.py:Stage Protocol + ReportContext(三任务共同契约,
  动态 import 的动机与代价写在 docstring)
- 路由桩 reports.py 已注册
2026-08-14 21:51:05 +08:00

76 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Report tables for the Sprint-3 (M3) parallel batch (架构文档 §6.2 report 域).
三张表一次落齐,T15(编排)不再写迁移:
rp_report — result JSONB 承载流水线中间态与最终产物(按 stage 键增量更新)
rp_input — questionnaire / photo_meta / derived_features(视觉中间态,T-P3-09)
rp_share — V1 只建结构(路由属后续任务)
Reversible: 是。
"""
from __future__ import annotations
from collections.abc import Sequence
import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects import postgresql
revision: str = "0006"
down_revision: str | None = "0005"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None
def upgrade() -> None:
op.create_table(
"rp_report",
sa.Column("report_id", sa.BigInteger(), nullable=False),
sa.Column("user_id", sa.BigInteger(), nullable=False),
sa.Column("tier", sa.String(length=8), nullable=False, server_default=sa.text("'basic'")),
sa.Column("status", sa.String(length=16), nullable=False, server_default=sa.text("'pending'")),
sa.Column("version", sa.Integer(), nullable=False, server_default=sa.text("1")),
sa.Column("result", postgresql.JSONB(), nullable=False),
sa.Column("model_info", postgresql.JSONB(), nullable=False),
sa.Column("content_code", sa.String(length=32), nullable=True),
sa.Column("rendered_asset_key", sa.String(length=255), nullable=True),
sa.Column("cost_cents", sa.Numeric(14, 6), nullable=False, server_default=sa.text("0")),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
sa.PrimaryKeyConstraint("report_id"),
)
op.create_index("ix_rp_report_user_created", "rp_report", ["user_id", "created_at"])
op.create_table(
"rp_input",
sa.Column("input_id", sa.BigInteger(), nullable=False),
sa.Column("report_id", sa.BigInteger(), nullable=False),
sa.Column("questionnaire", postgresql.JSONB(), nullable=False),
sa.Column("photo_meta", postgresql.JSONB(), nullable=False),
sa.Column("derived_features", postgresql.JSONB(), nullable=False),
sa.PrimaryKeyConstraint("input_id"),
sa.UniqueConstraint("report_id", name="uq_rp_input_report"),
sa.ForeignKeyConstraint(["report_id"], ["rp_report.report_id"], ondelete="CASCADE"),
)
op.create_table(
"rp_share",
sa.Column("share_id", sa.BigInteger(), nullable=False),
sa.Column("report_id", sa.BigInteger(), nullable=False),
sa.Column("share_token", sa.String(length=64), nullable=False),
sa.Column("expire_at", sa.DateTime(timezone=True), nullable=False),
sa.Column("view_count", sa.Integer(), nullable=False, server_default=sa.text("0")),
sa.Column("revoked", sa.Boolean(), nullable=False, server_default=sa.text("false")),
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now()),
sa.PrimaryKeyConstraint("share_id"),
sa.UniqueConstraint("share_token", name="uq_rp_share_token"),
sa.ForeignKeyConstraint(["report_id"], ["rp_report.report_id"], ondelete="CASCADE"),
)
def downgrade() -> None:
op.drop_table("rp_share")
op.drop_table("rp_input")
op.drop_index("ix_rp_report_user_created", table_name="rp_report")
op.drop_table("rp_report")