Files
Mutual_Fund/_conflict_backup/repositories/customer_relation.py
T

143 lines
5.5 KiB
Python
Raw Normal View History

2026-09-12 20:42:33 +08:00
"""customer_relation 客户-投顾关系仓储:数据权限根(仅本人名下客户)+ 客户列表 + AUM 聚合。"""
from __future__ import annotations
from decimal import Decimal
from sqlalchemy import func, or_, select
from model.customer_relation import CustomerRelation
from model.fin_customer_profile import FinCustomerProfile
from model.fin_holdings import FinHoldings
from model.sys_user import SysUser
from repositories.base import BaseRepository
# 持仓中状态:仅统计「持有中」市值,与 service/holdings.py 口径一致(DDL 注释,未入 common_const)
_HOLDING_STATUS = "持有中"
class CustomerRelationRepo(BaseRepository):
model = CustomerRelation
async def get_by_customer_advisor(
self, customer_id: int, advisor_id: int
) -> CustomerRelation | None:
"""取指定客户与投顾的关系(数据权限判断用)。"""
return await self.db.scalar(
select(CustomerRelation).where(
CustomerRelation.customer_id == customer_id,
CustomerRelation.advisor_id == advisor_id,
)
)
async def get_current_by_customer(self, customer_id: int) -> CustomerRelation | None:
"""取客户当前有效关系(历史重分配可能多行,取最近一条,按 id 倒序)。"""
return await self.db.scalar(
select(CustomerRelation)
.where(CustomerRelation.customer_id == customer_id)
.order_by(CustomerRelation.id.desc())
.limit(1)
)
async def list_by_status(self, status: str) -> list[CustomerRelation]:
"""按状态取全部关系(定时调度遍历 signed 客户用,跨投顾)。"""
return list(
(
await self.db.scalars(
select(CustomerRelation).where(CustomerRelation.status == status)
)
).all()
)
async def list_by_advisor(
self, advisor_id: int, status: str | None = None
) -> list[CustomerRelation]:
"""名下客户关系列表(status=None 不过滤)。"""
stmt = select(CustomerRelation).where(CustomerRelation.advisor_id == advisor_id)
if status:
stmt = stmt.where(CustomerRelation.status == status)
return list((await self.db.scalars(stmt)).all())
async def list_customer_rows(
self,
*,
advisor_id: int,
status: str | None = None,
keyword: str | None = None,
limit: int = 100,
offset: int = 0,
) -> list[tuple[CustomerRelation, SysUser, FinCustomerProfile | None]]:
"""客户列表:关系 + 客户账号 + 画像(左连),返回三元组供 service 拼装响应。"""
conds = [CustomerRelation.advisor_id == advisor_id]
if status:
conds.append(CustomerRelation.status == status)
if keyword:
like = f"%{keyword}%"
conds.append(or_(SysUser.real_name.like(like), SysUser.phone.like(like)))
stmt = (
select(CustomerRelation, SysUser, FinCustomerProfile)
.join(SysUser, SysUser.id == CustomerRelation.customer_id)
.outerjoin(
FinCustomerProfile,
FinCustomerProfile.customer_id == CustomerRelation.customer_id,
)
.where(*conds)
.order_by(CustomerRelation.id.desc())
.limit(limit)
.offset(offset)
)
return list((await self.db.execute(stmt)).all())
async def count_customer_rows(
self,
*,
advisor_id: int,
status: str | None = None,
keyword: str | None = None,
) -> int:
conds = [CustomerRelation.advisor_id == advisor_id]
if status:
conds.append(CustomerRelation.status == status)
if keyword:
like = f"%{keyword}%"
conds.append(or_(SysUser.real_name.like(like), SysUser.phone.like(like)))
stmt = (
select(func.count())
.select_from(CustomerRelation)
.join(SysUser, SysUser.id == CustomerRelation.customer_id)
.where(*conds)
)
return (await self.db.scalar(stmt)) or 0
async def risk_level_distribution(
self, advisor_id: int
) -> list[tuple[str | None, int]]:
"""名下客户风险等级分布(C1-C5 各档人数),供驾驶舱客户分层。"""
stmt = (
select(FinCustomerProfile.risk_level, func.count())
.select_from(CustomerRelation)
.join(
FinCustomerProfile,
FinCustomerProfile.customer_id == CustomerRelation.customer_id,
)
.where(CustomerRelation.advisor_id == advisor_id)
.group_by(FinCustomerProfile.risk_level)
)
return list((await self.db.execute(stmt)).all())
async def sum_holdings_value(
self, advisor_id: int, relation_status: str | None = None
) -> Decimal:
"""AUM:名下客户「持有中」持仓当前市值之和(relation_status 过滤,如 signed)。"""
stmt = (
select(func.coalesce(func.sum(FinHoldings.current_value), 0))
.select_from(CustomerRelation)
.join(FinHoldings, FinHoldings.customer_id == CustomerRelation.customer_id)
.where(
CustomerRelation.advisor_id == advisor_id,
FinHoldings.status == _HOLDING_STATUS,
)
)
if relation_status:
stmt = stmt.where(CustomerRelation.status == relation_status)
return await self.db.scalar(stmt)