first commit
This commit is contained in:
8
backend/app/models/__init__.py
Normal file
8
backend/app/models/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
"""SQLAlchemy models - export Base and all models for create_all."""
|
||||
|
||||
from app.models.database import Base, get_db, init_db, engine, async_session_maker
|
||||
from app.models.case import Case
|
||||
from app.models.screenshot import Screenshot
|
||||
from app.models.transaction import Transaction
|
||||
|
||||
__all__ = ["Base", "Case", "Screenshot", "Transaction", "get_db", "init_db", "engine", "async_session_maker"]
|
||||
28
backend/app/models/case.py
Normal file
28
backend/app/models/case.py
Normal file
@@ -0,0 +1,28 @@
|
||||
"""Case model - 案件."""
|
||||
|
||||
from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from sqlalchemy import String, Text, DateTime, Numeric
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.models.database import Base
|
||||
|
||||
|
||||
class Case(Base):
|
||||
__tablename__ = "cases"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
case_number: Mapped[str] = mapped_column(String(64), unique=True, index=True)
|
||||
victim_name: Mapped[str] = mapped_column(String(128))
|
||||
description: Mapped[str] = mapped_column(Text, default="")
|
||||
total_loss: Mapped[Decimal] = mapped_column(Numeric(18, 2), default=0)
|
||||
status: Mapped[str] = mapped_column(String(32), default="in_progress") # in_progress | completed
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
screenshots: Mapped[list["Screenshot"]] = relationship("Screenshot", back_populates="case", cascade="all, delete-orphan")
|
||||
transactions: Mapped[list["Transaction"]] = relationship("Transaction", back_populates="case", cascade="all, delete-orphan")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Case(id={self.id}, case_number={self.case_number})>"
|
||||
33
backend/app/models/database.py
Normal file
33
backend/app/models/database.py
Normal file
@@ -0,0 +1,33 @@
|
||||
"""Database session and initialization."""
|
||||
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
from app.config import get_settings
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
engine = None
|
||||
async_session_maker = None
|
||||
|
||||
|
||||
async def init_db():
|
||||
global engine, async_session_maker
|
||||
settings = get_settings()
|
||||
engine = create_async_engine(
|
||||
settings.database_url,
|
||||
echo=settings.debug,
|
||||
)
|
||||
async_session_maker = async_sessionmaker(
|
||||
engine, class_=AsyncSession, expire_on_commit=False
|
||||
)
|
||||
from app.models import Case, Screenshot, Transaction # noqa: F401
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
|
||||
|
||||
async def get_db():
|
||||
async with async_session_maker() as session:
|
||||
yield session
|
||||
27
backend/app/models/screenshot.py
Normal file
27
backend/app/models/screenshot.py
Normal file
@@ -0,0 +1,27 @@
|
||||
"""Screenshot model - 截图记录."""
|
||||
|
||||
from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from sqlalchemy import String, DateTime, ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.models.database import Base
|
||||
|
||||
|
||||
class Screenshot(Base):
|
||||
__tablename__ = "screenshots"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
case_id: Mapped[int] = mapped_column(ForeignKey("cases.id", ondelete="CASCADE"), index=True)
|
||||
filename: Mapped[str] = mapped_column(String(255))
|
||||
file_path: Mapped[str] = mapped_column(String(512))
|
||||
status: Mapped[str] = mapped_column(String(32), default="pending") # pending | extracted | failed
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
||||
|
||||
case: Mapped["Case"] = relationship("Case", back_populates="screenshots")
|
||||
transactions: Mapped[list["Transaction"]] = relationship(
|
||||
"Transaction", back_populates="screenshot", cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Screenshot(id={self.id}, filename={self.filename})>"
|
||||
35
backend/app/models/transaction.py
Normal file
35
backend/app/models/transaction.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""Transaction model - 交易记录."""
|
||||
|
||||
from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from sqlalchemy import String, Text, DateTime, Numeric, ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.models.database import Base
|
||||
|
||||
|
||||
class Transaction(Base):
|
||||
__tablename__ = "transactions"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
case_id: Mapped[int] = mapped_column(ForeignKey("cases.id", ondelete="CASCADE"), index=True)
|
||||
screenshot_id: Mapped[int] = mapped_column(ForeignKey("screenshots.id", ondelete="CASCADE"), index=True)
|
||||
app_source: Mapped[str] = mapped_column(String(128))
|
||||
transaction_type: Mapped[str] = mapped_column(String(32)) # 转出/转入/消费/收款/提现/充值
|
||||
amount: Mapped[Decimal] = mapped_column(Numeric(18, 2))
|
||||
currency: Mapped[str] = mapped_column(String(16), default="CNY")
|
||||
counterparty_name: Mapped[str | None] = mapped_column(String(256), nullable=True)
|
||||
counterparty_account: Mapped[str | None] = mapped_column(String(512), nullable=True)
|
||||
order_number: Mapped[str | None] = mapped_column(String(128), nullable=True)
|
||||
transaction_time: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
|
||||
remark: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
raw_text: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
confidence: Mapped[str] = mapped_column(String(16), default="medium") # high | medium | low
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
|
||||
|
||||
case: Mapped["Case"] = relationship("Case", back_populates="transactions")
|
||||
screenshot: Mapped["Screenshot"] = relationship("Screenshot", back_populates="transactions")
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<Transaction(id={self.id}, amount={self.amount}, app={self.app_source})>"
|
||||
Reference in New Issue
Block a user