refactor(backend): 仓储层散参数收敛为查询 DTO
仓储层的 list_*/count_* 此前以 keyword/status/offset/limit 四五个散参数 传参,调用点重复铺开、易漏传且不可扩展;按项目「参数 >4 封装 DTO」的 约定统一收敛为各模块的 Query 对象(如 RoleQuery、BusinessQuery), 服务层与测试调用同步调整,行为不变。 - repositories 层查询方法签名改为 (session, query),分页与筛选条件 由 Query 承载,_apply_filters 同步收口 - schemas 层补齐各模块 Query 模型(approval/business_role/report 等) - services 层调用点同步改为传 Query - tests 随之调整;新增用例:待办角色流转(财务通过后领导出现待办)、 参数预览顺序稳定 验证:全量 pytest 900 passed(11:08);单元 526 passed;ruff check 绿。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
1 parent
c48d24ab6d
commit
5ece9f6dab
45 files changed
+1045
-1028
No files matched your search
@@ -23,6 +23,7 @@
|
||||
"""
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
|
||||
@@ -39,7 +40,7 @@ from app.core.constants import (
|
||||
from app.core.db_errors import is_duplicate_entry
|
||||
from app.core.exceptions import BadRequestError, NotFoundError
|
||||
from app.core.response import PageResult
|
||||
from app.models import Receipt
|
||||
from app.models import Business, Receipt
|
||||
from app.models.business import BUSINESS_STATUS_ACTIVE
|
||||
from app.models.receipt import SETTLEMENT_DONE, SETTLEMENT_PENDING
|
||||
from app.repositories import business_repository, receipt_repository
|
||||
@@ -129,24 +130,8 @@ def _ensure_business_writable(session: Session, business_id: int, action: str):
|
||||
|
||||
def list_receipts(session: Session, query: ReceiptQuery) -> PageResult[ReceiptOut]:
|
||||
"""分页查询回款(附业务号/发货单号与 payment_days 派生值)。"""
|
||||
rows = receipt_repository.list_receipts(
|
||||
session,
|
||||
offset=query.offset,
|
||||
limit=query.page_size,
|
||||
business_id=query.business_id,
|
||||
date_from=query.date_from,
|
||||
date_to=query.date_to,
|
||||
is_barter=query.is_barter,
|
||||
settlement_status=query.settlement_status,
|
||||
)
|
||||
total = receipt_repository.count_receipts(
|
||||
session,
|
||||
business_id=query.business_id,
|
||||
date_from=query.date_from,
|
||||
date_to=query.date_to,
|
||||
is_barter=query.is_barter,
|
||||
settlement_status=query.settlement_status,
|
||||
)
|
||||
rows = receipt_repository.list_receipts(session, query)
|
||||
total = receipt_repository.count_receipts(session, query)
|
||||
return PageResult[ReceiptOut](
|
||||
records=[_to_out(row) for row in rows],
|
||||
total=total,
|
||||
@@ -166,6 +151,57 @@ def get_detail(session: Session, receipt_id: int) -> ReceiptOut:
|
||||
return _to_out(row)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class _CreatedReceipt:
|
||||
"""建行结果(出参 + 固化的 ID/批次号,commit 后不再触碰 ORM 属性)。"""
|
||||
|
||||
out: ReceiptOut
|
||||
receipt_id: int
|
||||
batch_no: str
|
||||
|
||||
|
||||
def _create_receipt_once(
|
||||
session: Session,
|
||||
payload: ReceiptCreateIn,
|
||||
business: Business | None,
|
||||
batch_no: str,
|
||||
) -> _CreatedReceipt:
|
||||
"""一次建行尝试:准入校验 → 建行 → 重算合计 → 出参。
|
||||
|
||||
**不 commit**:提交与回滚由调用方(重试循环)统一控制,保证「一次尝试
|
||||
= 一个事务边界」。
|
||||
"""
|
||||
if business is None:
|
||||
raise NotFoundError("业务不存在")
|
||||
if business.status != BUSINESS_STATUS_ACTIVE:
|
||||
raise BadRequestError("业务已完成或已终止,禁止新增回款")
|
||||
# 月结锁定拦截(2026-10-15 补充需求):比需求原文「提示」取硬,
|
||||
# 一期安全侧(策略点注释);未指定日期时以当天月近似
|
||||
from app.services import settlement_service
|
||||
|
||||
_lock_month = (payload.receipt_date or date.today()).strftime("%Y-%m")
|
||||
settlement_service.ensure_month_not_locked(session, _lock_month)
|
||||
shipment_no, ship_date = _ensure_shipment_of_business(
|
||||
session, payload.shipment_id, payload.business_id
|
||||
)
|
||||
receipt = receipt_repository.create(
|
||||
session,
|
||||
Receipt(
|
||||
receipt_batch_no=batch_no,
|
||||
business_id=payload.business_id,
|
||||
shipment_id=payload.shipment_id,
|
||||
receipt_date=payload.receipt_date,
|
||||
amount=Decimal(payload.amount),
|
||||
is_barter=payload.is_barter,
|
||||
settlement_status=SETTLEMENT_PENDING,
|
||||
remark=payload.remark,
|
||||
),
|
||||
)
|
||||
_recalc_totals(session, business)
|
||||
out = _to_out((receipt, business.business_no, shipment_no, ship_date))
|
||||
return _CreatedReceipt(out=out, receipt_id=receipt.id, batch_no=batch_no)
|
||||
|
||||
|
||||
def create_receipt(
|
||||
session: Session,
|
||||
payload: ReceiptCreateIn,
|
||||
@@ -206,35 +242,7 @@ def create_receipt(
|
||||
# 锁业务行与生成批次号——成功/最终失败各只记一条审计,无递归
|
||||
for attempt in range(_NO_RETRY + 1):
|
||||
try:
|
||||
if business is None:
|
||||
raise NotFoundError("业务不存在")
|
||||
if business.status != BUSINESS_STATUS_ACTIVE:
|
||||
raise BadRequestError("业务已完成或已终止,禁止新增回款")
|
||||
# 月结锁定拦截(2026-10-15 补充需求):比需求原文「提示」取硬,
|
||||
# 一期安全侧(策略点注释);未指定日期时以当天月近似
|
||||
from app.services import settlement_service
|
||||
|
||||
_lock_month = (payload.receipt_date or date.today()).strftime("%Y-%m")
|
||||
settlement_service.ensure_month_not_locked(session, _lock_month)
|
||||
shipment_no, ship_date = _ensure_shipment_of_business(
|
||||
session, payload.shipment_id, payload.business_id
|
||||
)
|
||||
receipt = receipt_repository.create(
|
||||
session,
|
||||
Receipt(
|
||||
receipt_batch_no=batch_no,
|
||||
business_id=payload.business_id,
|
||||
shipment_id=payload.shipment_id,
|
||||
receipt_date=payload.receipt_date,
|
||||
amount=Decimal(payload.amount),
|
||||
is_barter=payload.is_barter,
|
||||
settlement_status=SETTLEMENT_PENDING,
|
||||
remark=payload.remark,
|
||||
),
|
||||
)
|
||||
_recalc_totals(session, business)
|
||||
out = _to_out((receipt, business.business_no, shipment_no, ship_date))
|
||||
new_id, new_no = receipt.id, batch_no # 固化:commit 后不碰 ORM
|
||||
created = _create_receipt_once(session, payload, business, batch_no)
|
||||
session.commit()
|
||||
break
|
||||
except IntegrityError as exc:
|
||||
@@ -249,11 +257,11 @@ def create_receipt(
|
||||
|
||||
logger.info(
|
||||
"新增回款 | receipt_id=%s | no=%s | business_id=%s",
|
||||
new_id,
|
||||
new_no,
|
||||
created.receipt_id,
|
||||
created.batch_no,
|
||||
payload.business_id,
|
||||
)
|
||||
return out
|
||||
return created.out
|
||||
|
||||
|
||||
def update_receipt(
|
||||
|
||||
Reference in new issue
Block a user