- core_ro 新增 get_nav_as_of(D10) / get_redeem_fee_rules / list_share_lots(S1 确定性排序) / sum_remain_qty(仅 >0) / get_holding;不动 get_latest_nav 与 list_trades_range 白名单 - 新增 share_lot_repository:core_share_lot 读侧 FIFO 贪心选批 + 汇总,排序复用 core_ro 的 ORDER BY(D18 单一副本) - 新增 tests/test_share_lot.py 15 用例;pytest 全量 624 passed / 3 skipped(基线 609,零回归) - 同步开发计划 §5.1 执行记录与交接文档 §B 状态(v1.5 / T-3 ✅ / 基线 624) Co-Authored-By: WorkBuddy <workbuddy@tencent.com>
65 lines
2.6 KiB
Python
65 lines
2.6 KiB
Python
"""份额批次读侧仓储(jinrong_core · 仅 SELECT)· T-3。
|
||
|
||
core_share_lot 的读侧封装:FIFO 选批 + 可用份额汇总。写侧(扣减 / 补建新批)
|
||
归 `convert_core_repository`(D2),本文件不写。
|
||
|
||
D18(单一副本):FIFO 排序规则(confirmed_at ASC, lot_id ASC)的唯一出处是
|
||
`CoreReadOnlyRepository.list_share_lots` 的 ORDER BY —— 本类复用它取批次,
|
||
**不另写一份排序 SQL**,贪心分配只在 Python 端做,避免排序口径漂移。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from decimal import Decimal
|
||
from typing import Any
|
||
|
||
from sqlalchemy.engine import Engine
|
||
|
||
from app.config.settings import settings
|
||
from app.repository.core_ro import CoreReadOnlyRepository
|
||
from app.utils.db import get_engine
|
||
|
||
|
||
class ShareLotRepository:
|
||
"""core_share_lot 读侧:FIFO 选批 + 汇总(D2:写侧在 convert_core_repository)。"""
|
||
|
||
def __init__(self, engine: Engine | None = None) -> None:
|
||
self._engine = engine or get_engine(settings.mysql_core_database, "ro")
|
||
# 复用 core_ro 的 ORDER BY(D18 单一副本),避免排序规则漂移
|
||
self._core = CoreReadOnlyRepository(engine=self._engine)
|
||
|
||
def select_for_convert(
|
||
self, customer_id: str, product_id: str, qty: Decimal
|
||
) -> list[dict[str, Any]]:
|
||
"""FIFO 选批:按 confirmed_at ASC, lot_id ASC 贪心覆盖 `qty`。
|
||
|
||
返回被选中的批次(dict:lot_id / qty 本次可用 / nav / confirmed_at)。
|
||
若可用份额不足 `qty`,返回全部可用批次(差额由调用方校验,读侧不抛业务异常)。
|
||
零请求量返回空列表。
|
||
"""
|
||
if qty <= 0:
|
||
return []
|
||
remaining = Decimal(qty)
|
||
selected: list[dict[str, Any]] = []
|
||
for lot in self._core.list_share_lots(customer_id, product_id):
|
||
if remaining <= 0:
|
||
break
|
||
available = Decimal(lot["remain_qty"])
|
||
if available <= 0:
|
||
continue
|
||
take = min(available, remaining)
|
||
selected.append(
|
||
{
|
||
"lot_id": lot["lot_id"],
|
||
"qty": take,
|
||
"nav": Decimal(lot["nav"]),
|
||
"confirmed_at": lot["confirmed_at"],
|
||
}
|
||
)
|
||
remaining -= take
|
||
return selected
|
||
|
||
def available_qty(self, customer_id: str, product_id: str) -> Decimal:
|
||
"""可用份额合计(委托 core_ro.sum_remain_qty,仅统计 remain_qty > 0)。"""
|
||
return self._core.sum_remain_qty(customer_id, product_id)
|