Initial project commit
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
import asyncio
|
||||
from logging.config import fileConfig
|
||||
|
||||
from alembic import context
|
||||
from sqlalchemy import pool
|
||||
from sqlalchemy.ext.asyncio import async_engine_from_config
|
||||
|
||||
from app.core.config import settings
|
||||
from app.db.base import Base
|
||||
import app.models # noqa: F401 – ensure all models are registered
|
||||
|
||||
config = context.config
|
||||
if config.config_file_name is not None:
|
||||
fileConfig(config.config_file_name)
|
||||
|
||||
config.set_main_option("sqlalchemy.url", settings.DATABASE_URL)
|
||||
|
||||
target_metadata = Base.metadata
|
||||
|
||||
|
||||
def run_migrations_offline() -> None:
|
||||
url = config.get_main_option("sqlalchemy.url")
|
||||
context.configure(
|
||||
url=url,
|
||||
target_metadata=target_metadata,
|
||||
literal_binds=True,
|
||||
dialect_opts={"paramstyle": "named"},
|
||||
)
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
def do_run_migrations(connection):
|
||||
context.configure(connection=connection, target_metadata=target_metadata)
|
||||
with context.begin_transaction():
|
||||
context.run_migrations()
|
||||
|
||||
|
||||
async def run_async_migrations() -> None:
|
||||
connectable = async_engine_from_config(
|
||||
config.get_section(config.config_ini_section, {}),
|
||||
prefix="sqlalchemy.",
|
||||
poolclass=pool.NullPool,
|
||||
)
|
||||
async with connectable.connect() as connection:
|
||||
await connection.run_sync(do_run_migrations)
|
||||
await connectable.dispose()
|
||||
|
||||
|
||||
def run_migrations_online() -> None:
|
||||
asyncio.run(run_async_migrations())
|
||||
|
||||
|
||||
if context.is_offline_mode():
|
||||
run_migrations_offline()
|
||||
else:
|
||||
run_migrations_online()
|
||||
@@ -0,0 +1,26 @@
|
||||
"""${message}
|
||||
|
||||
Revision ID: ${up_revision}
|
||||
Revises: ${down_revision | comma,n}
|
||||
Create Date: ${create_date}
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
${imports if imports else ""}
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = ${repr(up_revision)}
|
||||
down_revision: Union[str, None] = ${repr(down_revision)}
|
||||
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
|
||||
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
${upgrades if upgrades else "pass"}
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
${downgrades if downgrades else "pass"}
|
||||
@@ -0,0 +1,95 @@
|
||||
"""initial schema
|
||||
|
||||
Revision ID: 0001
|
||||
Revises:
|
||||
Create Date: 2026-03-27
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from geoalchemy2 import Geometry
|
||||
|
||||
revision: str = "0001"
|
||||
down_revision: Union[str, None] = None
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.execute("CREATE EXTENSION IF NOT EXISTS postgis")
|
||||
|
||||
op.create_table(
|
||||
"users",
|
||||
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
||||
sa.Column("phone", sa.String(20), unique=True, nullable=True),
|
||||
sa.Column("email", sa.String(255), unique=True, nullable=True),
|
||||
sa.Column("password_hash", sa.String(255), nullable=False),
|
||||
sa.Column("nickname", sa.String(50), nullable=False),
|
||||
sa.Column("avatar_url", sa.String(500), nullable=True),
|
||||
sa.Column("city", sa.String(100), nullable=True),
|
||||
sa.Column("identity", sa.String(20), server_default="both"),
|
||||
sa.Column("role", sa.String(20), server_default="user"),
|
||||
sa.Column("is_active", sa.Boolean, server_default=sa.text("true")),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"spots",
|
||||
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
||||
sa.Column("title", sa.String(200), nullable=False, index=True),
|
||||
sa.Column("city", sa.String(100), nullable=False, index=True),
|
||||
sa.Column("location", Geometry("POINT", srid=4326), nullable=False),
|
||||
sa.Column("description", sa.Text, nullable=True),
|
||||
sa.Column("transport", sa.Text, nullable=True),
|
||||
sa.Column("best_time", sa.String(200), nullable=True),
|
||||
sa.Column("difficulty", sa.Text, nullable=True),
|
||||
sa.Column("audit_status", sa.String(20), server_default="pending"),
|
||||
sa.Column("reject_reason", sa.String(500), nullable=True),
|
||||
sa.Column("creator_id", sa.Integer, sa.ForeignKey("users.id"), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"spot_images",
|
||||
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
||||
sa.Column("spot_id", sa.Integer, sa.ForeignKey("spots.id"), nullable=False),
|
||||
sa.Column("image_url", sa.String(500), nullable=False),
|
||||
sa.Column("is_cover", sa.Boolean, server_default=sa.text("false")),
|
||||
sa.Column("audit_status", sa.String(20), server_default="pending"),
|
||||
sa.Column("sort_order", sa.Integer, server_default="0"),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"favorites",
|
||||
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
||||
sa.Column("user_id", sa.Integer, sa.ForeignKey("users.id"), nullable=False),
|
||||
sa.Column("spot_id", sa.Integer, sa.ForeignKey("spots.id"), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("user_id", "spot_id", name="uq_user_spot"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"point_ledger",
|
||||
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
||||
sa.Column("user_id", sa.Integer, sa.ForeignKey("users.id"), nullable=False),
|
||||
sa.Column("change", sa.Integer, nullable=False),
|
||||
sa.Column("balance", sa.Integer, nullable=False),
|
||||
sa.Column("reason", sa.String(200), nullable=False),
|
||||
sa.Column("ref_type", sa.String(50), nullable=True),
|
||||
sa.Column("ref_id", sa.Integer, nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("point_ledger")
|
||||
op.drop_table("favorites")
|
||||
op.drop_table("spot_images")
|
||||
op.drop_table("spots")
|
||||
op.drop_table("users")
|
||||
op.execute("DROP EXTENSION IF EXISTS postgis")
|
||||
@@ -0,0 +1,33 @@
|
||||
"""add audit_logs table
|
||||
|
||||
Revision ID: 0002
|
||||
Revises: 0001
|
||||
Create Date: 2026-03-27
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision: str = "0002"
|
||||
down_revision: Union[str, None] = "0001"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"audit_logs",
|
||||
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
||||
sa.Column("operator_id", sa.Integer, sa.ForeignKey("users.id"), nullable=False),
|
||||
sa.Column("action", sa.String(100), nullable=False, index=True),
|
||||
sa.Column("target_type", sa.String(50), nullable=False),
|
||||
sa.Column("target_id", sa.Integer, nullable=True),
|
||||
sa.Column("detail", sa.Text, nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("audit_logs")
|
||||
@@ -0,0 +1,86 @@
|
||||
"""phase2 community: comments, reports, ratings, tags, spot_tags, spot rating columns
|
||||
|
||||
Revision ID: 0003
|
||||
Revises: 0002
|
||||
Create Date: 2026-03-27
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision: str = "0003"
|
||||
down_revision: Union[str, None] = "0002"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column("spots", sa.Column("avg_rating", sa.Float, nullable=True))
|
||||
op.add_column("spots", sa.Column("rating_count", sa.Integer, server_default="0", nullable=False))
|
||||
|
||||
op.create_table(
|
||||
"comments",
|
||||
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
||||
sa.Column("spot_id", sa.Integer, sa.ForeignKey("spots.id"), nullable=False, index=True),
|
||||
sa.Column("user_id", sa.Integer, sa.ForeignKey("users.id"), nullable=False),
|
||||
sa.Column("parent_id", sa.Integer, sa.ForeignKey("comments.id"), nullable=True),
|
||||
sa.Column("content", sa.Text, nullable=False),
|
||||
sa.Column("audit_status", sa.String(20), server_default="approved"),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"reports",
|
||||
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
||||
sa.Column("reporter_id", sa.Integer, sa.ForeignKey("users.id"), nullable=False),
|
||||
sa.Column("target_type", sa.String(50), nullable=False, index=True),
|
||||
sa.Column("target_id", sa.Integer, nullable=False),
|
||||
sa.Column("reason", sa.Text, nullable=False),
|
||||
sa.Column("status", sa.String(20), server_default="pending"),
|
||||
sa.Column("handler_id", sa.Integer, sa.ForeignKey("users.id"), nullable=True),
|
||||
sa.Column("conclusion", sa.Text, nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.Column("resolved_at", sa.DateTime(timezone=True), nullable=True),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"ratings",
|
||||
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
||||
sa.Column("spot_id", sa.Integer, sa.ForeignKey("spots.id"), nullable=False, index=True),
|
||||
sa.Column("user_id", sa.Integer, sa.ForeignKey("users.id"), nullable=False),
|
||||
sa.Column("score", sa.Integer, nullable=False),
|
||||
sa.Column("short_comment", sa.String(200), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("user_id", "spot_id", name="uq_user_spot_rating"),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"tags",
|
||||
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
||||
sa.Column("name", sa.String(50), unique=True, nullable=False),
|
||||
sa.Column("category", sa.String(50), nullable=True),
|
||||
sa.Column("is_active", sa.Boolean, server_default="true"),
|
||||
sa.Column("usage_count", sa.Integer, server_default="0"),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"spot_tags",
|
||||
sa.Column("id", sa.Integer, primary_key=True, autoincrement=True),
|
||||
sa.Column("spot_id", sa.Integer, sa.ForeignKey("spots.id"), nullable=False),
|
||||
sa.Column("tag_id", sa.Integer, sa.ForeignKey("tags.id"), nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
sa.UniqueConstraint("spot_id", "tag_id", name="uq_spot_tag"),
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_table("spot_tags")
|
||||
op.drop_table("tags")
|
||||
op.drop_table("ratings")
|
||||
op.drop_table("reports")
|
||||
op.drop_table("comments")
|
||||
op.drop_column("spots", "rating_count")
|
||||
op.drop_column("spots", "avg_rating")
|
||||
@@ -0,0 +1,23 @@
|
||||
"""add is_free and price to spots
|
||||
|
||||
Revision ID: 0004
|
||||
Revises: 0003
|
||||
Create Date: 2026-03-27
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision = "0004"
|
||||
down_revision = "0003"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column("spots", sa.Column("is_free", sa.Boolean(), server_default=sa.text("true"), nullable=False))
|
||||
op.add_column("spots", sa.Column("price", sa.Numeric(10, 2), nullable=True))
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column("spots", "price")
|
||||
op.drop_column("spots", "is_free")
|
||||
@@ -0,0 +1,23 @@
|
||||
"""replace price with price_min and price_max
|
||||
|
||||
Revision ID: 0005
|
||||
Revises: 0004
|
||||
Create Date: 2026-03-27
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision = "0005"
|
||||
down_revision = "0004"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.alter_column("spots", "price", new_column_name="price_min")
|
||||
op.add_column("spots", sa.Column("price_max", sa.Numeric(10, 2), nullable=True))
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column("spots", "price_max")
|
||||
op.alter_column("spots", "price_min", new_column_name="price")
|
||||
@@ -0,0 +1,21 @@
|
||||
"""add bio to users
|
||||
|
||||
Revision ID: 0006
|
||||
Revises: 0005
|
||||
Create Date: 2026-03-27
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision = "0006"
|
||||
down_revision = "0005"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column("users", sa.Column("bio", sa.Text(), nullable=True))
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column("users", "bio")
|
||||
@@ -0,0 +1,35 @@
|
||||
"""add corrections table
|
||||
|
||||
Revision ID: 0007
|
||||
Revises: 0006
|
||||
Create Date: 2026-03-29
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision = "0007"
|
||||
down_revision = "0006"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"corrections",
|
||||
sa.Column("id", sa.Integer(), primary_key=True, autoincrement=True),
|
||||
sa.Column("spot_id", sa.Integer(), sa.ForeignKey("spots.id"), nullable=False),
|
||||
sa.Column("user_id", sa.Integer(), sa.ForeignKey("users.id"), nullable=False),
|
||||
sa.Column("field_name", sa.String(50), nullable=False),
|
||||
sa.Column("suggested_value", sa.Text(), nullable=False),
|
||||
sa.Column("reason", sa.Text(), nullable=True),
|
||||
sa.Column("status", sa.String(20), server_default="pending", nullable=False),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.func.now()),
|
||||
)
|
||||
op.create_index("ix_corrections_spot_id", "corrections", ["spot_id"])
|
||||
op.create_index("ix_corrections_status", "corrections", ["status"])
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index("ix_corrections_status", "corrections")
|
||||
op.drop_index("ix_corrections_spot_id", "corrections")
|
||||
op.drop_table("corrections")
|
||||
@@ -0,0 +1,43 @@
|
||||
"""add_favorite_count_and_notifications
|
||||
|
||||
Revision ID: 3b977a379456
|
||||
Revises: 0007
|
||||
Create Date: 2026-03-31 12:30:46.524068
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision: str = '3b977a379456'
|
||||
down_revision: Union[str, None] = '0007'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table('notifications',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('type', sa.String(length=50), nullable=False),
|
||||
sa.Column('title', sa.String(length=200), nullable=False),
|
||||
sa.Column('content', sa.Text(), nullable=True),
|
||||
sa.Column('ref_type', sa.String(length=50), nullable=True),
|
||||
sa.Column('ref_id', sa.Integer(), nullable=True),
|
||||
sa.Column('is_read', sa.Boolean(), nullable=False, server_default=sa.text('false')),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id']),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_notifications_type'), 'notifications', ['type'], unique=False)
|
||||
op.create_index(op.f('ix_notifications_user_id'), 'notifications', ['user_id'], unique=False)
|
||||
op.add_column('spots', sa.Column('favorite_count', sa.Integer(), nullable=False, server_default=sa.text('0')))
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_column('spots', 'favorite_count')
|
||||
op.drop_index(op.f('ix_notifications_user_id'), table_name='notifications')
|
||||
op.drop_index(op.f('ix_notifications_type'), table_name='notifications')
|
||||
op.drop_table('notifications')
|
||||
@@ -0,0 +1,69 @@
|
||||
"""add_shooting_system
|
||||
|
||||
Revision ID: 7bf40aa6c4b5
|
||||
Revises: 3b977a379456
|
||||
Create Date: 2026-03-31 12:41:00.407691
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision: str = '7bf40aa6c4b5'
|
||||
down_revision: Union[str, None] = '3b977a379456'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table('shooting_requests',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('creator_id', sa.Integer(), nullable=False),
|
||||
sa.Column('title', sa.String(length=200), nullable=False),
|
||||
sa.Column('city', sa.String(length=100), nullable=False),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('style', sa.String(length=100), nullable=True),
|
||||
sa.Column('shoot_date', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column('is_free', sa.Boolean(), nullable=False),
|
||||
sa.Column('budget_min', sa.Numeric(precision=10, scale=2), nullable=True),
|
||||
sa.Column('budget_max', sa.Numeric(precision=10, scale=2), nullable=True),
|
||||
sa.Column('role_needed', sa.String(length=20), nullable=False),
|
||||
sa.Column('max_applicants', sa.Integer(), nullable=False),
|
||||
sa.Column('contact_info', sa.String(length=200), nullable=True),
|
||||
sa.Column('spot_id', sa.Integer(), nullable=True),
|
||||
sa.Column('status', sa.String(length=20), nullable=False),
|
||||
sa.Column('audit_status', sa.String(length=20), nullable=False),
|
||||
sa.Column('reject_reason', sa.String(length=500), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.ForeignKeyConstraint(['creator_id'], ['users.id'], ),
|
||||
sa.ForeignKeyConstraint(['spot_id'], ['spots.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_shooting_requests_city'), 'shooting_requests', ['city'], unique=False)
|
||||
op.create_index(op.f('ix_shooting_requests_creator_id'), 'shooting_requests', ['creator_id'], unique=False)
|
||||
op.create_index(op.f('ix_shooting_requests_status'), 'shooting_requests', ['status'], unique=False)
|
||||
op.create_table('shooting_applications',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('request_id', sa.Integer(), nullable=False),
|
||||
sa.Column('applicant_id', sa.Integer(), nullable=False),
|
||||
sa.Column('message', sa.Text(), nullable=True),
|
||||
sa.Column('status', sa.String(length=20), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.ForeignKeyConstraint(['applicant_id'], ['users.id'], ),
|
||||
sa.ForeignKeyConstraint(['request_id'], ['shooting_requests.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_shooting_applications_applicant_id'), 'shooting_applications', ['applicant_id'], unique=False)
|
||||
op.create_index(op.f('ix_shooting_applications_request_id'), 'shooting_applications', ['request_id'], unique=False)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(op.f('ix_shooting_applications_request_id'), table_name='shooting_applications')
|
||||
op.drop_index(op.f('ix_shooting_applications_applicant_id'), table_name='shooting_applications')
|
||||
op.drop_table('shooting_applications')
|
||||
op.drop_index(op.f('ix_shooting_requests_status'), table_name='shooting_requests')
|
||||
op.drop_index(op.f('ix_shooting_requests_creator_id'), table_name='shooting_requests')
|
||||
op.drop_index(op.f('ix_shooting_requests_city'), table_name='shooting_requests')
|
||||
op.drop_table('shooting_requests')
|
||||
@@ -0,0 +1,82 @@
|
||||
"""add_event_system
|
||||
|
||||
Revision ID: a35876e08b8e
|
||||
Revises: 7bf40aa6c4b5
|
||||
Create Date: 2026-03-31 12:51:29.923126
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision: str = 'a35876e08b8e'
|
||||
down_revision: Union[str, None] = '7bf40aa6c4b5'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table('events',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('creator_id', sa.Integer(), nullable=False),
|
||||
sa.Column('title', sa.String(length=200), nullable=False),
|
||||
sa.Column('city', sa.String(length=100), nullable=False),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('cover_url', sa.String(length=500), nullable=True),
|
||||
sa.Column('location_name', sa.String(length=200), nullable=True),
|
||||
sa.Column('start_time', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column('end_time', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column('max_participants', sa.Integer(), nullable=False),
|
||||
sa.Column('spot_id', sa.Integer(), nullable=True),
|
||||
sa.Column('status', sa.String(length=20), nullable=False),
|
||||
sa.Column('audit_status', sa.String(length=20), nullable=False),
|
||||
sa.Column('reject_reason', sa.String(length=500), nullable=True),
|
||||
sa.Column('registration_count', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.ForeignKeyConstraint(['creator_id'], ['users.id'], ),
|
||||
sa.ForeignKeyConstraint(['spot_id'], ['spots.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_events_city'), 'events', ['city'], unique=False)
|
||||
op.create_index(op.f('ix_events_creator_id'), 'events', ['creator_id'], unique=False)
|
||||
op.create_index(op.f('ix_events_status'), 'events', ['status'], unique=False)
|
||||
op.create_table('event_photos',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('event_id', sa.Integer(), nullable=False),
|
||||
sa.Column('uploader_id', sa.Integer(), nullable=False),
|
||||
sa.Column('image_url', sa.String(length=500), nullable=False),
|
||||
sa.Column('caption', sa.String(length=200), nullable=True),
|
||||
sa.Column('spot_id', sa.Integer(), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.ForeignKeyConstraint(['event_id'], ['events.id'], ),
|
||||
sa.ForeignKeyConstraint(['spot_id'], ['spots.id'], ),
|
||||
sa.ForeignKeyConstraint(['uploader_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_event_photos_event_id'), 'event_photos', ['event_id'], unique=False)
|
||||
op.create_table('event_registrations',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('event_id', sa.Integer(), nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('status', sa.String(length=20), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.ForeignKeyConstraint(['event_id'], ['events.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_event_registrations_event_id'), 'event_registrations', ['event_id'], unique=False)
|
||||
op.create_index(op.f('ix_event_registrations_user_id'), 'event_registrations', ['user_id'], unique=False)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(op.f('ix_event_registrations_user_id'), table_name='event_registrations')
|
||||
op.drop_index(op.f('ix_event_registrations_event_id'), table_name='event_registrations')
|
||||
op.drop_table('event_registrations')
|
||||
op.drop_index(op.f('ix_event_photos_event_id'), table_name='event_photos')
|
||||
op.drop_table('event_photos')
|
||||
op.drop_index(op.f('ix_events_status'), table_name='events')
|
||||
op.drop_index(op.f('ix_events_creator_id'), table_name='events')
|
||||
op.drop_index(op.f('ix_events_city'), table_name='events')
|
||||
op.drop_table('events')
|
||||
@@ -0,0 +1,74 @@
|
||||
"""add_commercial_system
|
||||
|
||||
Revision ID: c28508a3a8d4
|
||||
Revises: a35876e08b8e
|
||||
Create Date: 2026-03-31 15:01:24.482893
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
revision: str = 'c28508a3a8d4'
|
||||
down_revision: Union[str, None] = 'a35876e08b8e'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table('membership_plans',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('name', sa.String(length=100), nullable=False),
|
||||
sa.Column('description', sa.Text(), nullable=True),
|
||||
sa.Column('duration_days', sa.Integer(), nullable=False),
|
||||
sa.Column('price', sa.Numeric(precision=10, scale=2), nullable=False),
|
||||
sa.Column('benefits', sa.Text(), nullable=True),
|
||||
sa.Column('extra_uploads', sa.Integer(), nullable=False),
|
||||
sa.Column('extra_top_count', sa.Integer(), nullable=False),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False),
|
||||
sa.Column('sort_order', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('promotions',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('title', sa.String(length=200), nullable=False),
|
||||
sa.Column('image_url', sa.String(length=500), nullable=False),
|
||||
sa.Column('link_type', sa.String(length=20), nullable=False),
|
||||
sa.Column('link_id', sa.Integer(), nullable=True),
|
||||
sa.Column('link_url', sa.String(length=500), nullable=True),
|
||||
sa.Column('position', sa.String(length=30), nullable=False),
|
||||
sa.Column('sort_order', sa.Integer(), nullable=False),
|
||||
sa.Column('start_time', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column('end_time', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False),
|
||||
sa.Column('impressions', sa.Integer(), nullable=False),
|
||||
sa.Column('clicks', sa.Integer(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_promotions_is_active'), 'promotions', ['is_active'], unique=False)
|
||||
op.create_index(op.f('ix_promotions_position'), 'promotions', ['position'], unique=False)
|
||||
op.create_table('user_memberships',
|
||||
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column('user_id', sa.Integer(), nullable=False),
|
||||
sa.Column('plan_id', sa.Integer(), nullable=False),
|
||||
sa.Column('start_date', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('end_date', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
||||
sa.ForeignKeyConstraint(['plan_id'], ['membership_plans.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_user_memberships_user_id'), 'user_memberships', ['user_id'], unique=False)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(op.f('ix_user_memberships_user_id'), table_name='user_memberships')
|
||||
op.drop_table('user_memberships')
|
||||
op.drop_index(op.f('ix_promotions_position'), table_name='promotions')
|
||||
op.drop_index(op.f('ix_promotions_is_active'), table_name='promotions')
|
||||
op.drop_table('promotions')
|
||||
op.drop_table('membership_plans')
|
||||
@@ -0,0 +1,93 @@
|
||||
"""add_app_nav_config_and_promotion_refs
|
||||
|
||||
Revision ID: e9f8b1234cde
|
||||
Revises: c28508a3a8d4
|
||||
Create Date: 2026-04-12 16:20:00.000000
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision: str = "e9f8b1234cde"
|
||||
down_revision: Union[str, None] = "c28508a3a8d4"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column("promotions", sa.Column("spot_id", sa.Integer(), nullable=True))
|
||||
op.add_column("promotions", sa.Column("event_id", sa.Integer(), nullable=True))
|
||||
op.add_column("promotions", sa.Column("shooting_id", sa.Integer(), nullable=True))
|
||||
op.create_foreign_key("fk_promotions_spot_id", "promotions", "spots", ["spot_id"], ["id"])
|
||||
op.create_foreign_key("fk_promotions_event_id", "promotions", "events", ["event_id"], ["id"])
|
||||
op.create_foreign_key("fk_promotions_shooting_id", "promotions", "shooting_requests", ["shooting_id"], ["id"])
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE promotions
|
||||
SET spot_id = link_id
|
||||
WHERE link_type = 'spot' AND link_id IS NOT NULL
|
||||
"""
|
||||
)
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE promotions
|
||||
SET event_id = link_id
|
||||
WHERE link_type = 'event' AND link_id IS NOT NULL
|
||||
"""
|
||||
)
|
||||
op.execute(
|
||||
"""
|
||||
UPDATE promotions
|
||||
SET shooting_id = link_id
|
||||
WHERE link_type = 'shooting' AND link_id IS NOT NULL
|
||||
"""
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"app_nav_configs",
|
||||
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column("key", sa.String(length=50), nullable=False),
|
||||
sa.Column("label", sa.String(length=50), nullable=False),
|
||||
sa.Column("page_path", sa.String(length=200), nullable=False),
|
||||
sa.Column("icon", sa.String(length=50), nullable=False),
|
||||
sa.Column("active_icon", sa.String(length=50), nullable=False),
|
||||
sa.Column("color", sa.String(length=20), nullable=False, server_default="#999999"),
|
||||
sa.Column("active_color", sa.String(length=20), nullable=False, server_default="#6366f1"),
|
||||
sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.text("true")),
|
||||
sa.Column("sort_order", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("key"),
|
||||
)
|
||||
op.create_index(op.f("ix_app_nav_configs_is_active"), "app_nav_configs", ["is_active"], unique=False)
|
||||
op.create_index(op.f("ix_app_nav_configs_key"), "app_nav_configs", ["key"], unique=True)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
INSERT INTO app_nav_configs (key, label, page_path, icon, active_icon, color, active_color, is_active, sort_order)
|
||||
VALUES
|
||||
('discover', '发现', 'pages/index/index', 'compass', 'compass-filled', '#999999', '#6366f1', true, 1),
|
||||
('activity', '活动', 'pages/activity/index', 'calendar', 'calendar-filled', '#999999', '#6366f1', true, 2),
|
||||
('upload', '投稿', 'pages/spot/create', 'plusempty', 'plusempty', '#999999', '#6366f1', true, 3),
|
||||
('message', '消息', 'pages/mine/notifications', 'chat', 'chat-filled', '#999999', '#6366f1', true, 4),
|
||||
('mine', '我的', 'pages/mine/index', 'person', 'person-filled', '#999999', '#6366f1', true, 5)
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(op.f("ix_app_nav_configs_key"), table_name="app_nav_configs")
|
||||
op.drop_index(op.f("ix_app_nav_configs_is_active"), table_name="app_nav_configs")
|
||||
op.drop_table("app_nav_configs")
|
||||
|
||||
op.drop_constraint("fk_promotions_shooting_id", "promotions", type_="foreignkey")
|
||||
op.drop_constraint("fk_promotions_event_id", "promotions", type_="foreignkey")
|
||||
op.drop_constraint("fk_promotions_spot_id", "promotions", type_="foreignkey")
|
||||
op.drop_column("promotions", "shooting_id")
|
||||
op.drop_column("promotions", "event_id")
|
||||
op.drop_column("promotions", "spot_id")
|
||||
@@ -0,0 +1,89 @@
|
||||
"""add_system_configs
|
||||
|
||||
Revision ID: f1a2b3c4d5e6
|
||||
Revises: e9f8b1234cde
|
||||
Create Date: 2026-04-12 20:40:00.000000
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision: str = "f1a2b3c4d5e6"
|
||||
down_revision: Union[str, None] = "e9f8b1234cde"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.create_table(
|
||||
"system_configs",
|
||||
sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
|
||||
sa.Column("config_key", sa.String(length=100), nullable=False),
|
||||
sa.Column("category", sa.String(length=50), nullable=False),
|
||||
sa.Column("title", sa.String(length=200), nullable=False),
|
||||
sa.Column("config_json", sa.Text(), nullable=False, server_default="{}"),
|
||||
sa.Column("description", sa.Text(), nullable=True),
|
||||
sa.Column("is_active", sa.Boolean(), nullable=False, server_default=sa.text("true")),
|
||||
sa.Column("sort_order", sa.Integer(), nullable=False, server_default="0"),
|
||||
sa.Column("updated_by", sa.Integer(), nullable=True),
|
||||
sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
|
||||
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint("config_key"),
|
||||
)
|
||||
op.create_index(op.f("ix_system_configs_config_key"), "system_configs", ["config_key"], unique=True)
|
||||
op.create_index(op.f("ix_system_configs_category"), "system_configs", ["category"], unique=False)
|
||||
op.create_index(op.f("ix_system_configs_is_active"), "system_configs", ["is_active"], unique=False)
|
||||
|
||||
system_configs_table = sa.table(
|
||||
"system_configs",
|
||||
sa.column("config_key", sa.String(length=100)),
|
||||
sa.column("category", sa.String(length=50)),
|
||||
sa.column("title", sa.String(length=200)),
|
||||
sa.column("config_json", sa.Text()),
|
||||
sa.column("description", sa.Text()),
|
||||
sa.column("is_active", sa.Boolean()),
|
||||
sa.column("sort_order", sa.Integer()),
|
||||
)
|
||||
op.bulk_insert(
|
||||
system_configs_table,
|
||||
[
|
||||
{
|
||||
"config_key": "notification_template_default",
|
||||
"category": "notification_template",
|
||||
"title": "默认通知模板",
|
||||
"config_json": '{"type":"system","title_template":"{title}","content_template":"{content}","channels":["in_app"]}',
|
||||
"description": "系统默认通知模板(JSON字符串)",
|
||||
"is_active": True,
|
||||
"sort_order": 1,
|
||||
},
|
||||
{
|
||||
"config_key": "notification_rule_auto_dispatch",
|
||||
"category": "notification_rule",
|
||||
"title": "通知自动触发规则",
|
||||
"config_json": '{"enabled":true,"triggers":[{"event":"report_resolved","template":"notification_template_default"}],"rate_limit_per_minute":60}',
|
||||
"description": "通知规则引擎配置(JSON字符串)",
|
||||
"is_active": True,
|
||||
"sort_order": 2,
|
||||
},
|
||||
{
|
||||
"config_key": "report_sop_default",
|
||||
"category": "report_sop",
|
||||
"title": "举报处理SOP",
|
||||
"config_json": '{"steps":[{"status":"pending","action":"初筛"},{"status":"processing","action":"调查取证"},{"status":"resolved","action":"处置并回执"},{"status":"rejected","action":"驳回并说明"}],"sla_hours":24}',
|
||||
"description": "举报工单标准化流程(JSON字符串)",
|
||||
"is_active": True,
|
||||
"sort_order": 3,
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.drop_index(op.f("ix_system_configs_is_active"), table_name="system_configs")
|
||||
op.drop_index(op.f("ix_system_configs_category"), table_name="system_configs")
|
||||
op.drop_index(op.f("ix_system_configs_config_key"), table_name="system_configs")
|
||||
op.drop_table("system_configs")
|
||||
Reference in New Issue
Block a user