91 lines
3.5 KiB
Python
91 lines
3.5 KiB
Python
"""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
|
|
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())
|
|
|
|
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
|