Files
Mutual_Fund/repositories/fin_holdings.py
T

91 lines
3.5 KiB
Python
Raw Normal View History

2026-09-10 23:21:30 +08:00
"""fin_holdings 持仓仓储:按客户(+状态)查持仓、申购加仓 upsert、赎回减仓。"""
from __future__ import annotations
from decimal import Decimal
from sqlalchemy import case, select, update
from sqlalchemy.dialects.mysql import insert as mysql_insert
from model.fin_holdings import FinHoldings
from model.fin_product import FinProduct
2026-09-10 23:21:30 +08:00
from repositories.base import BaseRepository
class FinHoldingsRepo(BaseRepository):
model = FinHoldings
async def list_by_customer(
self, customer_id: int, status: str | None = None
) -> list[FinHoldings]:
"""按客户 ID 查持仓,可按状态过滤(status=None 表示不过滤)。"""
stmt = select(FinHoldings).where(FinHoldings.customer_id == customer_id)
if status is not None:
stmt = stmt.where(FinHoldings.status == status)
stmt = stmt.order_by(FinHoldings.id)
return list((await self.db.scalars(stmt)).all())
async def list_with_products(
self, customer_id: int, *, include_closed: bool = True
) -> list[tuple[FinHoldings, FinProduct | None]]:
"""按客户关联查询持仓和基金产品,产品缺失时保留持仓记录。"""
stmt = (
select(FinHoldings, FinProduct)
.outerjoin(FinProduct, FinProduct.id == FinHoldings.product_id)
.where(FinHoldings.customer_id == customer_id)
.order_by(FinHoldings.update_time.desc(), FinHoldings.id.desc())
)
if not include_closed:
stmt = stmt.where(FinHoldings.status == "持有中")
return list((await self.db.execute(stmt)).all())
2026-09-10 23:21:30 +08:00
async def get_by_customer_product(
self, customer_id: int, product_id: int
) -> FinHoldings | None:
return await self.db.scalar(
select(FinHoldings).where(
FinHoldings.customer_id == customer_id,
FinHoldings.product_id == product_id,
)
)
async def upsert(
self, customer_id: int, product_id: int, add_shares: Decimal, add_cost: Decimal
) -> None:
"""申购加仓:有则加份额/成本,无则新增(靠 uk_customer_product 唯一键)。不 commit。"""
stmt = mysql_insert(FinHoldings).values(
customer_id=customer_id,
product_id=product_id,
shares=add_shares,
cost_amount=add_cost,
)
stmt = stmt.on_duplicate_key_update(
shares=FinHoldings.shares + add_shares,
cost_amount=FinHoldings.cost_amount + add_cost,
)
await self.db.execute(stmt)
async def redeem(
self, customer_id: int, product_id: int, redeem_shares: Decimal
) -> bool:
"""赎回减仓:shares -= redeem_shares,份额归 0 时状态置'已清仓'。
持仓份额不足(含无持仓、shares=0)时不动作,返回 False。
不 commit,由 service 层事务统一提交,保证「减仓 + 入账」原子性。
"""
result = await self.db.execute(
update(FinHoldings)
.where(
FinHoldings.customer_id == customer_id,
FinHoldings.product_id == product_id,
FinHoldings.shares >= redeem_shares,
)
.values(
shares=FinHoldings.shares - redeem_shares,
status=case(
(FinHoldings.shares - redeem_shares == 0, "已清仓"),
else_=FinHoldings.status,
),
)
)
return result.rowcount > 0