第四批(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 已注册
76 lines
3.5 KiB
Python
76 lines
3.5 KiB
Python
"""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")
|