Files
xiaoxia-saas/packages/adapters/sqlalchemy_impl/points_transaction_repository.py
T

85 lines
2.6 KiB
Python

from __future__ import annotations
from datetime import datetime, timezone
from typing import Optional
from uuid import uuid4
from sqlalchemy.orm import Session
from packages.adapters.sqlalchemy_impl.models import PointsTransactionModel
class SQLAlchemyPointsTransactionRepository:
"""积分流水仓储 — 只追加,不修改/删除."""
def __init__(self, session: Session):
self.session = session
def create(
self,
*,
user_id: str,
account_id: str,
type_: str,
source: str,
amount: int,
balance_after: int,
description: str = "",
ref_id: str = "",
) -> PointsTransactionModel:
model = PointsTransactionModel(
id=str(uuid4()),
user_id=user_id,
account_id=account_id,
type=type_,
source=source,
amount=amount,
balance_after=balance_after,
description=description,
ref_id=ref_id,
created_at=datetime.now(timezone.utc),
)
self.session.add(model)
self.session.flush()
return model
def get_by_id(self, tx_id: str) -> PointsTransactionModel | None:
return self.session.get(PointsTransactionModel, tx_id)
def exists_refund_for(self, original_tx_id: str) -> bool:
"""判断给定原 spend 流水是否已有 refund 流水(幂等检查)."""
from sqlalchemy import func
return bool(
self.session.query(func.count(PointsTransactionModel.id))
.filter(
PointsTransactionModel.type == "refund",
PointsTransactionModel.ref_id == original_tx_id,
)
.scalar()
)
def list_by_user(
self,
user_id: str,
*,
offset: int = 0,
limit: int = 20,
type_: Optional[str] = None,
source: Optional[str] = None,
start_date: Optional[datetime] = None,
end_date: Optional[datetime] = None,
) -> tuple[list[PointsTransactionModel], int]:
q = self.session.query(PointsTransactionModel).filter(PointsTransactionModel.user_id == user_id)
if type_:
q = q.filter(PointsTransactionModel.type == type_)
if source:
q = q.filter(PointsTransactionModel.source == source)
if start_date:
q = q.filter(PointsTransactionModel.created_at >= start_date)
if end_date:
q = q.filter(PointsTransactionModel.created_at <= end_date)
total = q.count()
items = q.order_by(PointsTransactionModel.created_at.desc()).offset(offset).limit(limit).all()
return items, total