fix: 提醒模块两个性能问题
1. list_reminders 从内存分页改为 SQL LIMIT/OFFLOAD 分页 2. list_inactive_customers 从 N+1 逐个查询改为批量查询+内存聚合 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0bda0bbaa0
commit
4c12149953
@ -22,30 +22,37 @@ class ReminderRepository:
|
||||
被 ReminderService(提醒服务)调用。
|
||||
"""
|
||||
|
||||
def list_reminders(self, session: Session, filters: dict) -> list[SystemReminder]:
|
||||
"""查询系统提醒列表
|
||||
def list_reminders(self, session: Session, filters: dict) -> tuple[list[SystemReminder], int]:
|
||||
"""查询系统提醒列表(SQL 分页)
|
||||
|
||||
根据提醒类型、状态、接收人等条件过滤提醒记录,
|
||||
按 id 倒序返回。
|
||||
按 id 倒序返回当前页数据及总条数。
|
||||
|
||||
参数:
|
||||
session: 数据库会话
|
||||
filters: 过滤条件字典,支持 reminder_type / status / receiver_user_id
|
||||
filters: 过滤条件字典,支持 reminder_type / status / receiver_user_id / page_no / page_size
|
||||
|
||||
返回:
|
||||
符合条件的系统提醒列表
|
||||
(当前页系统提醒列表, 总条数) 元组
|
||||
"""
|
||||
stmt = select(SystemReminder)
|
||||
base = select(SystemReminder)
|
||||
|
||||
if filters.get("reminder_type"):
|
||||
stmt = stmt.where(SystemReminder.reminder_type == filters["reminder_type"])
|
||||
base = base.where(SystemReminder.reminder_type == filters["reminder_type"])
|
||||
if filters.get("status"):
|
||||
stmt = stmt.where(SystemReminder.status == filters["status"])
|
||||
base = base.where(SystemReminder.status == filters["status"])
|
||||
if filters.get("receiver_user_id") is not None:
|
||||
stmt = stmt.where(SystemReminder.receiver_user_id == filters["receiver_user_id"])
|
||||
base = base.where(SystemReminder.receiver_user_id == filters["receiver_user_id"])
|
||||
|
||||
stmt = stmt.order_by(SystemReminder.id.desc())
|
||||
return list(session.execute(stmt).scalars())
|
||||
count_stmt = select(func.count()).select_from(base.subquery())
|
||||
total = session.execute(count_stmt).scalar() or 0
|
||||
|
||||
page_no = filters.get("page_no", 1)
|
||||
page_size = filters.get("page_size", 20)
|
||||
offset = max(page_no - 1, 0) * page_size
|
||||
stmt = base.order_by(SystemReminder.id.desc()).offset(offset).limit(page_size)
|
||||
rows = list(session.execute(stmt).scalars())
|
||||
return rows, total
|
||||
|
||||
def get_reminder(self, session: Session, reminder_id: int) -> SystemReminder | None:
|
||||
"""根据 ID 查询单条系统提醒
|
||||
@ -364,29 +371,63 @@ class ReminderRepository:
|
||||
.order_by(Customer.id.asc())
|
||||
).scalars()
|
||||
)
|
||||
if not customers:
|
||||
return []
|
||||
|
||||
customer_ids = [c.id for c in customers]
|
||||
|
||||
# 批量查累计合同金额(替代逐个 N+1)
|
||||
amount_rows = session.execute(
|
||||
select(
|
||||
SalesOrder.customer_id,
|
||||
func.coalesce(func.sum(SalesOrder.contract_amount), 0).label("total_amount"),
|
||||
)
|
||||
.where(
|
||||
SalesOrder.deleted == 0,
|
||||
SalesOrder.customer_id.in_(customer_ids),
|
||||
SalesOrder.order_status != "canceled",
|
||||
)
|
||||
.group_by(SalesOrder.customer_id)
|
||||
).all()
|
||||
amount_map = {row.customer_id: float(row.total_amount) for row in amount_rows}
|
||||
|
||||
# 批量查每个客户最近一笔订单(窗口函数,替代逐个 N+1)
|
||||
from sqlalchemy import over
|
||||
latest_order_sq = (
|
||||
select(
|
||||
SalesOrder.customer_id,
|
||||
SalesOrder.id.label("order_id"),
|
||||
SalesOrder.created_at,
|
||||
over(func.row_number(), partition_by=SalesOrder.customer_id,
|
||||
order_by=(SalesOrder.created_at.desc(), SalesOrder.id.desc())).label("rn"),
|
||||
)
|
||||
.where(
|
||||
SalesOrder.deleted == 0,
|
||||
SalesOrder.customer_id.in_(customer_ids),
|
||||
SalesOrder.order_status != "canceled",
|
||||
)
|
||||
.subquery()
|
||||
)
|
||||
latest_rows = session.execute(
|
||||
select(latest_order_sq).where(latest_order_sq.c.rn == 1)
|
||||
).all()
|
||||
latest_order_map = {row.customer_id: row.order_id for row in latest_rows}
|
||||
|
||||
# 组装结果,用已查到的 order_id 批量加载 SalesOrder 对象
|
||||
candidate_order_ids = [oid for oid in latest_order_map.values() if oid is not None]
|
||||
if candidate_order_ids:
|
||||
order_objs = session.execute(
|
||||
select(SalesOrder).where(SalesOrder.id.in_(candidate_order_ids))
|
||||
).scalars().all()
|
||||
order_obj_map = {o.id: o for o in order_objs}
|
||||
else:
|
||||
order_obj_map = {}
|
||||
|
||||
results: list[tuple[Customer, SalesOrder | None, float]] = []
|
||||
for customer in customers:
|
||||
total_amount_stmt = (
|
||||
select(func.coalesce(func.sum(SalesOrder.contract_amount), 0))
|
||||
.where(
|
||||
SalesOrder.deleted == 0,
|
||||
SalesOrder.customer_id == customer.id,
|
||||
SalesOrder.order_status != "canceled",
|
||||
)
|
||||
)
|
||||
total_amount = float(session.execute(total_amount_stmt).scalar() or 0)
|
||||
|
||||
latest_order_stmt = (
|
||||
select(SalesOrder)
|
||||
.where(
|
||||
SalesOrder.deleted == 0,
|
||||
SalesOrder.customer_id == customer.id,
|
||||
SalesOrder.order_status != "canceled",
|
||||
)
|
||||
.order_by(SalesOrder.created_at.desc(), SalesOrder.id.desc())
|
||||
)
|
||||
latest_order = session.execute(latest_order_stmt).scalars().first()
|
||||
total_amount = amount_map.get(customer.id, 0.0)
|
||||
latest_order_id = latest_order_map.get(customer.id)
|
||||
latest_order = order_obj_map.get(latest_order_id) if latest_order_id else None
|
||||
if latest_order is None or (latest_order.created_at and latest_order.created_at <= inactive_before):
|
||||
results.append((customer, latest_order, total_amount))
|
||||
return results
|
||||
|
||||
@ -55,16 +55,14 @@ class ReminderService:
|
||||
if cached is not None:
|
||||
return cached
|
||||
try:
|
||||
reminders = self.repository.list_reminders(session, filters)
|
||||
reminders, total = self.repository.list_reminders(session, filters)
|
||||
page_no = filters.get("page_no", 1)
|
||||
page_size = filters.get("page_size", 20)
|
||||
start = max(page_no - 1, 0) * page_size
|
||||
page_list = reminders[start : start + page_size]
|
||||
result = {
|
||||
"total": len(reminders),
|
||||
"total": total,
|
||||
"page_no": page_no,
|
||||
"page_size": page_size,
|
||||
"list": [self._map_reminder(item) for item in page_list],
|
||||
"list": [self._map_reminder(item) for item in reminders],
|
||||
}
|
||||
cache_set(cache_key, result, ttl=30)
|
||||
return result
|
||||
|
||||
Loading…
Reference in New Issue
Block a user