Files

72 lines
2.3 KiB
Python

"""fin_product 产品仓储:列表搜索(关键字/类型/风险/状态筛选 + 排序 + 分页)。"""
from __future__ import annotations
from sqlalchemy import or_, select
from model.fin_product import FinProduct
from repositories.base import BaseRepository
from utils.exceptions import ParamError
# 排序字段白名单:防止 sort_by 注入,字符串映射到 ORM 列(不拼 SQL)
SORTABLE_FIELDS = {
"expected_return": FinProduct.expected_return,
"nav": FinProduct.nav,
"fee_rate": FinProduct.fee_rate,
"risk_level": FinProduct.risk_level,
"create_time": FinProduct.create_time,
}
class ProductRepo(BaseRepository):
model = FinProduct
async def get_by_code(self, product_code: str) -> FinProduct | None:
return await self.db.scalar(
select(FinProduct).where(FinProduct.product_code == product_code)
)
async def search(
self,
*,
keyword: str | None = None,
product_type: str | None = None,
risk_level: str | None = None,
status: str | None = "在售",
sort_by: str = "create_time",
sort_order: str = "desc",
limit: int = 10,
offset: int = 0,
) -> tuple[list[FinProduct], int]:
conds = []
if status and status != "全部":
conds.append(FinProduct.status == status)
if product_type:
conds.append(FinProduct.product_type == product_type)
if risk_level:
conds.append(FinProduct.risk_level == risk_level)
if keyword:
like = f"%{keyword}%"
conds.append(
or_(
FinProduct.product_code.like(like),
FinProduct.product_name.like(like),
FinProduct.fund_manager.like(like),
)
)
col = SORTABLE_FIELDS.get(sort_by)
if col is None:
raise ParamError(f"不支持的排序字段: {sort_by}")
order_col = col.desc() if sort_order == "desc" else col.asc()
stmt = (
select(FinProduct)
.where(*conds)
.order_by(order_col)
.limit(limit)
.offset(offset)
)
items = list((await self.db.scalars(stmt)).all())
total = await self.count(where=conds)
return items, total