fix: 亮灯表重复卡片 — 修复同客户经理同日多条拜访导致的 fan-out
- 原查询用 outerjoin(Visit, visit_date == MAX(visit_date)) 获取拜访方式, 当同一天有多条拜访(协同拜访)时会扩展成多行,导致亮灯表同一客户显示多张卡片 - 改用 DISTINCT ON (manager_id, customer_id) 子查询,保证每对(经理,客户) 只取最新一条拜访,消除重复 - 移除不再使用的 func 导入 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@ Status (rolling 30-day window):
|
|||||||
|
|
||||||
from datetime import date, timedelta
|
from datetime import date, timedelta
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
from sqlalchemy import select, func, and_
|
from sqlalchemy import select, and_
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from app.models.user import User
|
from app.models.user import User
|
||||||
from app.models.customer import Customer
|
from app.models.customer import Customer
|
||||||
@@ -86,18 +86,28 @@ async def get_light_board(
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ── All customers with their last visit per manager ──
|
# ── All customers with their last visit per manager ──
|
||||||
# Subquery: latest visit date + method per (manager_id, customer_id)
|
# Subquery: latest visit (date + method) per (manager_id, customer_id).
|
||||||
|
# DISTINCT ON keeps exactly one row per pair even when multiple visits
|
||||||
|
# share the same latest date (e.g. companion/collaborative visits),
|
||||||
|
# preventing fan-out that would duplicate customer cards on the board.
|
||||||
latest_visit = (
|
latest_visit = (
|
||||||
select(
|
select(
|
||||||
Visit.manager_id,
|
Visit.manager_id,
|
||||||
Visit.customer_id,
|
Visit.customer_id,
|
||||||
func.max(Visit.visit_date).label("last_visit_date"),
|
Visit.visit_date.label("last_visit_date"),
|
||||||
|
Visit.visit_method.label("last_visit_method"),
|
||||||
|
)
|
||||||
|
.distinct(Visit.manager_id, Visit.customer_id)
|
||||||
|
.order_by(
|
||||||
|
Visit.manager_id,
|
||||||
|
Visit.customer_id,
|
||||||
|
Visit.visit_date.desc(),
|
||||||
|
Visit.id.desc(),
|
||||||
)
|
)
|
||||||
.group_by(Visit.manager_id, Visit.customer_id)
|
|
||||||
.subquery()
|
.subquery()
|
||||||
)
|
)
|
||||||
|
|
||||||
# Join: assignments → customers → latest_visit → visits (for method)
|
# Join: assignments → customers → latest_visit (date + method)
|
||||||
rows = await db.execute(
|
rows = await db.execute(
|
||||||
select(
|
select(
|
||||||
CustomerAssignment.manager_id,
|
CustomerAssignment.manager_id,
|
||||||
@@ -108,18 +118,13 @@ async def get_light_board(
|
|||||||
Customer.in_use_services,
|
Customer.in_use_services,
|
||||||
Customer.monthly_fee,
|
Customer.monthly_fee,
|
||||||
latest_visit.c.last_visit_date,
|
latest_visit.c.last_visit_date,
|
||||||
Visit.visit_method,
|
latest_visit.c.last_visit_method,
|
||||||
)
|
)
|
||||||
.join(Customer, Customer.id == CustomerAssignment.customer_id)
|
.join(Customer, Customer.id == CustomerAssignment.customer_id)
|
||||||
.outerjoin(latest_visit, and_(
|
.outerjoin(latest_visit, and_(
|
||||||
latest_visit.c.manager_id == CustomerAssignment.manager_id,
|
latest_visit.c.manager_id == CustomerAssignment.manager_id,
|
||||||
latest_visit.c.customer_id == CustomerAssignment.customer_id,
|
latest_visit.c.customer_id == CustomerAssignment.customer_id,
|
||||||
))
|
))
|
||||||
.outerjoin(Visit, and_(
|
|
||||||
Visit.manager_id == CustomerAssignment.manager_id,
|
|
||||||
Visit.customer_id == CustomerAssignment.customer_id,
|
|
||||||
Visit.visit_date == latest_visit.c.last_visit_date,
|
|
||||||
))
|
|
||||||
.where(CustomerAssignment.role == "primary")
|
.where(CustomerAssignment.role == "primary")
|
||||||
.order_by(Customer.name)
|
.order_by(Customer.name)
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user