first commit

This commit is contained in:
2026-03-11 16:28:04 +08:00
commit c0f9ddabbf
101 changed files with 11601 additions and 0 deletions

View File

View File

@@ -0,0 +1,34 @@
from uuid import UUID
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from app.models.assessment import FraudAssessment, ConfidenceLevel
from app.repositories.base import BaseRepository
class AssessmentRepository(BaseRepository[FraudAssessment]):
def __init__(self, session: AsyncSession):
super().__init__(FraudAssessment, session)
async def list_by_case(
self,
case_id: UUID,
confidence_level: ConfidenceLevel | None = None,
) -> tuple[list[FraudAssessment], int]:
query = (
select(FraudAssessment)
.options(selectinload(FraudAssessment.transaction))
.where(FraudAssessment.case_id == case_id)
)
count_q = select(func.count()).select_from(FraudAssessment).where(FraudAssessment.case_id == case_id)
if confidence_level:
query = query.where(FraudAssessment.confidence_level == confidence_level)
count_q = count_q.where(FraudAssessment.confidence_level == confidence_level)
total = (await self.session.execute(count_q)).scalar() or 0
query = query.order_by(FraudAssessment.created_at.asc())
result = await self.session.execute(query)
return list(result.scalars().all()), total

View File

@@ -0,0 +1,50 @@
from typing import TypeVar, Generic, Type
from uuid import UUID
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import Base
ModelT = TypeVar("ModelT", bound=Base)
class BaseRepository(Generic[ModelT]):
def __init__(self, model: Type[ModelT], session: AsyncSession):
self.model = model
self.session = session
async def get(self, id: UUID) -> ModelT | None:
return await self.session.get(self.model, id)
async def list(self, offset: int = 0, limit: int = 50, **filters) -> tuple[list[ModelT], int]:
query = select(self.model)
count_query = select(func.count()).select_from(self.model)
for attr, value in filters.items():
if value is not None and hasattr(self.model, attr):
query = query.where(getattr(self.model, attr) == value)
count_query = count_query.where(getattr(self.model, attr) == value)
total = (await self.session.execute(count_query)).scalar() or 0
query = query.offset(offset).limit(limit)
result = await self.session.execute(query)
return list(result.scalars().all()), total
async def create(self, obj: ModelT) -> ModelT:
self.session.add(obj)
await self.session.flush()
await self.session.refresh(obj)
return obj
async def update(self, obj: ModelT, data: dict) -> ModelT:
for key, value in data.items():
if value is not None:
setattr(obj, key, value)
await self.session.flush()
await self.session.refresh(obj)
return obj
async def delete(self, obj: ModelT) -> None:
await self.session.delete(obj)
await self.session.flush()

View File

@@ -0,0 +1,35 @@
from sqlalchemy import select, func, or_
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.case import Case, CaseStatus
from app.repositories.base import BaseRepository
class CaseRepository(BaseRepository[Case]):
def __init__(self, session: AsyncSession):
super().__init__(Case, session)
async def list_cases(
self,
offset: int = 0,
limit: int = 50,
status: CaseStatus | None = None,
search: str | None = None,
) -> tuple[list[Case], int]:
query = select(Case).where(Case.deleted_at.is_(None))
count_query = select(func.count()).select_from(Case).where(Case.deleted_at.is_(None))
if status:
query = query.where(Case.status == status)
count_query = count_query.where(Case.status == status)
if search:
pattern = f"%{search}%"
search_filter = or_(Case.case_no.ilike(pattern), Case.title.ilike(pattern))
query = query.where(search_filter)
count_query = count_query.where(search_filter)
total = (await self.session.execute(count_query)).scalar() or 0
query = query.order_by(Case.created_at.desc()).offset(offset).limit(limit)
result = await self.session.execute(query)
return list(result.scalars().all()), total

View File

@@ -0,0 +1,39 @@
from uuid import UUID
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.evidence_image import EvidenceImage, SourceApp, PageType
from app.repositories.base import BaseRepository
class ImageRepository(BaseRepository[EvidenceImage]):
def __init__(self, session: AsyncSession):
super().__init__(EvidenceImage, session)
async def find_by_hash(self, file_hash: str) -> EvidenceImage | None:
result = await self.session.execute(
select(EvidenceImage).where(EvidenceImage.file_hash == file_hash)
)
return result.scalar_one_or_none()
async def list_by_case(
self,
case_id: UUID,
source_app: SourceApp | None = None,
page_type: PageType | None = None,
) -> list[EvidenceImage]:
query = select(EvidenceImage).where(EvidenceImage.case_id == case_id)
if source_app:
query = query.where(EvidenceImage.source_app == source_app)
if page_type:
query = query.where(EvidenceImage.page_type == page_type)
query = query.order_by(EvidenceImage.uploaded_at.desc())
result = await self.session.execute(query)
return list(result.scalars().all())
async def count_by_case(self, case_id: UUID) -> int:
result = await self.session.execute(
select(func.count()).select_from(EvidenceImage).where(EvidenceImage.case_id == case_id)
)
return result.scalar() or 0

View File

@@ -0,0 +1,40 @@
from uuid import UUID
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.transaction import TransactionRecord
from app.repositories.base import BaseRepository
class TransactionRepository(BaseRepository[TransactionRecord]):
def __init__(self, session: AsyncSession):
super().__init__(TransactionRecord, session)
async def list_by_case(
self,
case_id: UUID,
filter_type: str | None = None,
) -> tuple[list[TransactionRecord], int]:
query = select(TransactionRecord).where(TransactionRecord.case_id == case_id)
count_q = select(func.count()).select_from(TransactionRecord).where(TransactionRecord.case_id == case_id)
if filter_type == "unique":
query = query.where(TransactionRecord.is_duplicate.is_(False))
count_q = count_q.where(TransactionRecord.is_duplicate.is_(False))
elif filter_type == "duplicate":
query = query.where(TransactionRecord.is_duplicate.is_(True))
count_q = count_q.where(TransactionRecord.is_duplicate.is_(True))
total = (await self.session.execute(count_q)).scalar() or 0
query = query.order_by(TransactionRecord.trade_time.asc())
result = await self.session.execute(query)
return list(result.scalars().all()), total
async def get_all_by_case(self, case_id: UUID) -> list[TransactionRecord]:
result = await self.session.execute(
select(TransactionRecord)
.where(TransactionRecord.case_id == case_id)
.order_by(TransactionRecord.trade_time.asc())
)
return list(result.scalars().all())