"""create user_feedback table Revision ID: 20260417_0001 Revises: 20260416_0003 Create Date: 2026-04-17 """ from typing import Sequence, Union from alembic import op import sqlalchemy as sa from sqlalchemy.dialects.postgresql import JSONB, UUID revision: str = "20260417_0001" down_revision: Union[str, Sequence[str], None] = "20260416_0003" branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: op.create_table( "user_feedback", sa.Column( "id", UUID(as_uuid=True), server_default=sa.text("gen_random_uuid()"), primary_key=True, ), sa.Column( "user_id", UUID(as_uuid=True), sa.ForeignKey("auth.users.id", ondelete="SET NULL"), nullable=True, ), sa.Column( "feedback_type", sa.String(20), nullable=False, server_default="other", ), sa.Column("content", sa.Text, nullable=False), sa.Column( "images", JSONB, nullable=False, server_default=sa.text("'[]'::jsonb"), ), sa.Column( "device_info", JSONB, nullable=False, server_default=sa.text("'{}'::jsonb"), ), sa.Column("app_version", sa.String(20), nullable=False), sa.Column("os_version", sa.String(50), nullable=False), sa.Column( "status", sa.String(20), nullable=False, server_default="pending", ), sa.Column( "created_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False, ), sa.Column( "updated_at", sa.DateTime(timezone=True), server_default=sa.func.now(), nullable=False, ), ) op.create_index("ix_user_feedback_user_id", "user_feedback", ["user_id"]) op.create_index("ix_user_feedback_created_at", "user_feedback", ["created_at"]) op.create_index("ix_user_feedback_status", "user_feedback", ["status"]) op.execute("COMMENT ON TABLE user_feedback IS '用户反馈表'") op.execute( "COMMENT ON COLUMN user_feedback.user_id IS " "'用户ID,NULL表示匿名(勾选不上传我的个人信息)'" ) op.execute( "COMMENT ON COLUMN user_feedback.feedback_type IS " "'反馈类型: bug/suggestion/other'" ) op.execute("COMMENT ON COLUMN user_feedback.content IS '反馈内容,最多500字'") op.execute( "COMMENT ON COLUMN user_feedback.images IS '图片Storage路径列表,最多3张'" ) op.execute( "COMMENT ON COLUMN user_feedback.device_info IS " "'设备信息JSON,匿名时照样采集(不涉及隐私)'" ) op.execute( "COMMENT ON COLUMN user_feedback.status IS '处理状态: pending/processed'" ) op.execute("ALTER TABLE public.user_feedback ENABLE ROW LEVEL SECURITY") op.execute(""" CREATE POLICY "Service role full access on user_feedback" ON public.user_feedback FOR ALL TO service_role USING (true) WITH CHECK (true) """) def downgrade() -> None: op.execute( 'DROP POLICY IF EXISTS "Service role full access on user_feedback" ON public.user_feedback' ) op.execute("ALTER TABLE public.user_feedback DISABLE ROW LEVEL SECURITY") op.drop_table("user_feedback")