147 lines
7.9 KiB
Python
147 lines
7.9 KiB
Python
"""add marketplace tables: catalog_items, marketplace_listings, purchases, creator_balances
|
|
|
|
Revision ID: 0013_add_marketplace_tables
|
|
Revises: 0012_add_wallet_and_transactions
|
|
Create Date: 2026-09-15 00:00:00.000000
|
|
|
|
docs/planning/economy.md US-3, US-4 (task 166836d55365).
|
|
"""
|
|
from typing import Sequence, Union
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
from sqlalchemy.dialects import postgresql
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = "0013_add_marketplace_tables"
|
|
down_revision: Union[str, None] = "0012_add_wallet_and_transactions"
|
|
branch_labels: Union[str, Sequence[str], None] = None
|
|
depends_on: Union[str, Sequence[str], None] = None
|
|
|
|
ITEM_TYPES = ("asset", "avatar_item", "experience_pass")
|
|
LISTING_STATUSES = ("active", "sold", "delisted", "removed_by_moderation")
|
|
PURCHASE_STATUSES = ("pending_escrow", "completed", "refunded", "reversed")
|
|
|
|
|
|
def upgrade() -> None:
|
|
bind = op.get_bind()
|
|
is_pg = bind.dialect.name == "postgresql"
|
|
|
|
# Enums
|
|
if is_pg:
|
|
item_type_enum = postgresql.ENUM(*ITEM_TYPES, name="catalog_item_type", create_type=False)
|
|
item_type_enum.create(bind, checkfirst=True)
|
|
item_type_col = postgresql.ENUM(*ITEM_TYPES, name="catalog_item_type", create_type=False)
|
|
|
|
listing_status_enum = postgresql.ENUM(*LISTING_STATUSES, name="marketplace_listing_status", create_type=False)
|
|
listing_status_enum.create(bind, checkfirst=True)
|
|
listing_status_col = postgresql.ENUM(*LISTING_STATUSES, name="marketplace_listing_status", create_type=False)
|
|
|
|
purchase_status_enum = postgresql.ENUM(*PURCHASE_STATUSES, name="purchase_status", create_type=False)
|
|
purchase_status_enum.create(bind, checkfirst=True)
|
|
purchase_status_col = postgresql.ENUM(*PURCHASE_STATUSES, name="purchase_status", create_type=False)
|
|
else:
|
|
item_type_col = sa.Enum(*ITEM_TYPES, name="catalog_item_type")
|
|
listing_status_col = sa.Enum(*LISTING_STATUSES, name="marketplace_listing_status")
|
|
purchase_status_col = sa.Enum(*PURCHASE_STATUSES, name="purchase_status")
|
|
|
|
# 1. catalog_items
|
|
op.create_table(
|
|
"catalog_items",
|
|
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
|
sa.Column("owner_id", sa.Integer(), nullable=False),
|
|
sa.Column("asset_id", sa.Integer(), nullable=True),
|
|
sa.Column("item_type", item_type_col, nullable=False),
|
|
sa.Column("name", sa.String(length=100), nullable=False),
|
|
sa.Column("description", sa.String(length=2000), nullable=True),
|
|
sa.Column("metadata_json", postgresql.JSONB(astext_type=sa.Text()) if is_pg else sa.JSON(), nullable=True),
|
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
|
sa.ForeignKeyConstraint(["owner_id"], ["users.id"], ondelete="CASCADE"),
|
|
sa.ForeignKeyConstraint(["asset_id"], ["assets.id"], ondelete="SET NULL"),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
)
|
|
op.create_index(op.f("ix_catalog_items_owner_id"), "catalog_items", ["owner_id"], unique=False)
|
|
op.create_index(op.f("ix_catalog_items_asset_id"), "catalog_items", ["asset_id"], unique=False)
|
|
|
|
# 2. marketplace_listings
|
|
op.create_table(
|
|
"marketplace_listings",
|
|
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
|
sa.Column("catalog_item_id", sa.BigInteger(), nullable=False),
|
|
sa.Column("seller_id", sa.Integer(), nullable=False),
|
|
sa.Column("price", sa.BigInteger(), nullable=False),
|
|
sa.Column("status", listing_status_col, server_default="active", 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.CheckConstraint("price > 0", name="check_listing_price_positive"),
|
|
sa.ForeignKeyConstraint(["catalog_item_id"], ["catalog_items.id"], ondelete="CASCADE"),
|
|
sa.ForeignKeyConstraint(["seller_id"], ["users.id"], ondelete="CASCADE"),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
)
|
|
op.create_index(op.f("ix_marketplace_listings_catalog_item_id"), "marketplace_listings", ["catalog_item_id"], unique=False)
|
|
op.create_index(op.f("ix_marketplace_listings_seller_id"), "marketplace_listings", ["seller_id"], unique=False)
|
|
|
|
# 3. purchases
|
|
op.create_table(
|
|
"purchases",
|
|
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
|
sa.Column("listing_id", sa.BigInteger(), nullable=True),
|
|
sa.Column("buyer_id", sa.Integer(), nullable=False),
|
|
sa.Column("seller_id", sa.Integer(), nullable=False),
|
|
sa.Column("price", sa.BigInteger(), nullable=False),
|
|
sa.Column("platform_fee", sa.BigInteger(), nullable=False),
|
|
sa.Column("seller_net", sa.BigInteger(), nullable=False),
|
|
sa.Column("status", purchase_status_col, server_default="pending_escrow", nullable=False),
|
|
sa.Column("escrow_hold_until", sa.DateTime(timezone=True), nullable=True),
|
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
|
sa.ForeignKeyConstraint(["listing_id"], ["marketplace_listings.id"], ondelete="SET NULL"),
|
|
sa.ForeignKeyConstraint(["buyer_id"], ["users.id"], ondelete="CASCADE"),
|
|
sa.ForeignKeyConstraint(["seller_id"], ["users.id"], ondelete="CASCADE"),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
)
|
|
op.create_index(op.f("ix_purchases_listing_id"), "purchases", ["listing_id"], unique=False)
|
|
op.create_index(op.f("ix_purchases_buyer_id"), "purchases", ["buyer_id"], unique=False)
|
|
op.create_index(op.f("ix_purchases_seller_id"), "purchases", ["seller_id"], unique=False)
|
|
|
|
# 4. creator_balances
|
|
op.create_table(
|
|
"creator_balances",
|
|
sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
|
|
sa.Column("user_id", sa.Integer(), nullable=False),
|
|
sa.Column("available", sa.BigInteger(), server_default="0", nullable=False),
|
|
sa.Column("pending", sa.BigInteger(), server_default="0", nullable=False),
|
|
sa.Column("lifetime_earned", sa.BigInteger(), server_default="0", nullable=False),
|
|
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
|
sa.CheckConstraint("available >= 0", name="check_creator_available_non_negative"),
|
|
sa.CheckConstraint("pending >= 0", name="check_creator_pending_non_negative"),
|
|
sa.CheckConstraint("lifetime_earned >= 0", name="check_creator_lifetime_non_negative"),
|
|
sa.ForeignKeyConstraint(["user_id"], ["users.id"], ondelete="CASCADE"),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
sa.UniqueConstraint("user_id", name="uq_creator_balances_user_id"),
|
|
)
|
|
op.create_index(op.f("ix_creator_balances_user_id"), "creator_balances", ["user_id"], unique=True)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.drop_index(op.f("ix_creator_balances_user_id"), table_name="creator_balances")
|
|
op.drop_table("creator_balances")
|
|
|
|
op.drop_index(op.f("ix_purchases_seller_id"), table_name="purchases")
|
|
op.drop_index(op.f("ix_purchases_buyer_id"), table_name="purchases")
|
|
op.drop_index(op.f("ix_purchases_listing_id"), table_name="purchases")
|
|
op.drop_table("purchases")
|
|
|
|
op.drop_index(op.f("ix_marketplace_listings_seller_id"), table_name="marketplace_listings")
|
|
op.drop_index(op.f("ix_marketplace_listings_catalog_item_id"), table_name="marketplace_listings")
|
|
op.drop_table("marketplace_listings")
|
|
|
|
op.drop_index(op.f("ix_catalog_items_asset_id"), table_name="catalog_items")
|
|
op.drop_index(op.f("ix_catalog_items_owner_id"), table_name="catalog_items")
|
|
op.drop_table("catalog_items")
|
|
|
|
bind = op.get_bind()
|
|
if bind.dialect.name == "postgresql":
|
|
sa.Enum(*PURCHASE_STATUSES, name="purchase_status").drop(bind, checkfirst=True)
|
|
sa.Enum(*LISTING_STATUSES, name="marketplace_listing_status").drop(bind, checkfirst=True)
|
|
sa.Enum(*ITEM_TYPES, name="catalog_item_type").drop(bind, checkfirst=True)
|