From e3fe1fc12a9db8d7625b86df9b69e01f15b0cf0c Mon Sep 17 00:00:00 2001 From: yueli Date: Fri, 18 Sep 2026 09:34:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(borrow):=20=E5=80=9F=E8=BF=98=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E3=80=8C=E5=B7=B2=E5=BD=92=E8=BF=98=E3=80=8D=E9=A1=B5?= =?UTF-8?q?=E7=AD=BE=E6=94=B9=E4=B8=BA=E6=8C=89=E5=BD=92=E8=BF=98=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E5=80=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题 ---- 三个页签共用同一套排序(有限期单在前 → 最早应还时间 ASC → 最早借出时间 DESC), 这套「优先关注快到期/逾期」的逻辑对「未归还」是对的,但对「已归还」正好** 反了**:已归还列表要回答的是「最近还了哪几笔」,而按应还时间排会让最近刚还的 那几笔排到最后。 改动 ---- order_subq 增加 max_return_time(单号内**最晚**一次归还时间)作为排序键, 并按页签分流: · 已归还 → nullslast(max_return_time DESC),borrow_no DESC 兜底保证稳定 · 全部/未归还 → 原三级排序不变 ★ 为什么取「最晚」而不是「最早」一次归还时间 多明细分批归还时,整单结清的那一刻才是有意义的节点;且与主行「归还时间」列 的展示口径一致(前端同样取 latest),排序依据与可见值不会打架。 验证(真实数据) 已归还页签:09-15 11:41 → 09-14 16:02 → 09-09 11:46 → 09-08 13:22 → 09-04 17:26 → 09-04 09:45 → 09-03 15:25,第 2 页续 08-27 → …, 严格递减且**跨页连续**; 未归还页签:排序与改动前完全一致(回归确认)。 --- .../app/services/trans_service.py | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/inventory-backend/app/services/trans_service.py b/inventory-backend/app/services/trans_service.py index 11549db..3d1e7c2 100644 --- a/inventory-backend/app/services/trans_service.py +++ b/inventory-backend/app/services/trans_service.py @@ -1089,7 +1089,10 @@ class TransService: # 无限期梯队内:单号内最早借出时间(呆滞借用盘点) func.min(TransBorrow.borrow_time).label('min_borrow_time'), # 单内是否含"有限期"明细:任一 expected_return_time 非空 → 1(有限期单排前) - func.max(case((TransBorrow.expected_return_time.isnot(None), 1), else_=0)).label('has_finite') + func.max(case((TransBorrow.expected_return_time.isnot(None), 1), else_=0)).label('has_finite'), + # 「已归还」页签的排序键:单号内**最晚**一次归还时间 + # (多明细分批归还时,整单结清的那一刻才是有意义的节点) + func.max(TransBorrow.return_time).label('max_return_time') ) .group_by(TransBorrow.borrow_no) .subquery() @@ -1434,18 +1437,31 @@ class TransService: ) ) - # ★ 默认排序(多级复合,符合"优先关注快到期/逾期"业务): - # 1) 有限期单(含 expected_return_time)排前,无限期单排后 - # 2) 有限期内按最早预计归还时间 ASC(越快到期/逾期越久越靠前) - # 3) 无限期内按最早借出时间 ASC(借出越久越靠前,暴露长期未还的呆滞借用) - borrow_no_q = borrow_no_q.order_by( - case((order_subq.c.has_finite == 0, 1), else_=0).asc(), - nullslast(asc(order_subq.c.sort_key)), - # ★ 无限期梯队内按借出时间**从近到远**(desc)。 - # 原实现是 asc「借出越久越靠前」,设计意图是暴露呆滞借用; - # 业务方明确要求改为从近到远,故反转。 - desc(order_subq.c.min_borrow_time) + # ★ 排序按页签分开: + # + # 「已归还」页签 —— 按**归还时间倒序**(从近到远)。 + # 这张列表此时回答的是「最近还了哪几笔」,而不是「哪笔快到期」, + # 所以不能沿用未归还那套「逾期优先」的排序,否则最近刚还的 + # 反而排在最后。取单号内**最晚**一次归还时间:多明细分批归还时, + # 整单结清的那一刻才是有意义的节点,也与主行「归还时间」列的 + # 展示口径一致(前端同样取 latest)。 + # + # 其余页签(全部 / 未归还)—— 沿用「优先关注快到期/逾期」: + # 1) 有限期单(含 expected_return_time)排前,无限期单排后 + # 2) 有限期内按最早预计归还时间 ASC(越快到期/逾期越久越靠前) + # 3) 无限期内按最早借出时间 DESC(从近到远) + _order_by = ( + [nullslast(desc(order_subq.c.max_return_time)), + desc(order_subq.c.borrow_no)] # 同一时刻的稳定兜底 + if status == 'returned' else + [case((order_subq.c.has_finite == 0, 1), else_=0).asc(), + nullslast(asc(order_subq.c.sort_key)), + # ★ 无限期梯队内按借出时间**从近到远**(desc)。 + # 原实现是 asc「借出越久越靠前」,设计意图是暴露呆滞借用; + # 业务方明确要求改为从近到远,故反转。 + desc(order_subq.c.min_borrow_time)] ) + borrow_no_q = borrow_no_q.order_by(*_order_by) # 分页(基准 = borrow_no 单号数) pagination = borrow_no_q.paginate(page=page, per_page=limit, error_out=False)