feat: 导入增强 + 客户合并 + 同伴自定义 + 性能优化
=== 导入系统全面增强 === - 5 Sheet 完整导入:拜访/计划/商机/要客/纪要 - 同访人智能解析:中英文逗号/顿号/分号 → 系统用户UUID + 外部人员TEXT - 客户自动创建:Excel中不存在的客户自动入库 - 模糊名称匹配:去空格 + 包含关系纠错 - manager_id 修正:导入时用客户分配的经理(非Excel列/非导入人) - 模板更新:Sheet5「客户经理」→「填报人」,同访人示例含逗号分隔 === 客户合并功能 === - PUT 改名碰撞检测 → 409 + 合并预览 - GET merge-preview / POST merge 端点 - 事务级迁移:Visit/WorkPlan/MiniBusiness/KeyVisit/联系人/分配 - 去重逻辑:联系人(name+phone)、分配(manager+role) - last_visit_date 取最大值 === 拜访记录完善 === - visits 新增 companion_names TEXT[] 列 - 同访人支持自定义输入(外部人员),el-select allow-create - 移动端 VisitForm + PC端 ManagerWorkspace 统一 - 周报「客户经理」→「相关人员」(创建人+同访人) - 纪要「客户经理」→「填报人」 - 删除拜访后重新计算 customer.last_visit_date === 客户选择放开 === - 客户经理可看到全部客户(不再限自己分配的) - 拜访时客户下拉返回全量 === 前端性能优化 === - Element Plus 按需加载 (unplugin-vue-components + unplugin-element-plus) - 周报照片URL并行请求 (Promise.all) - onMounted 三路并行 (loadReport + dropdowns + AI) - nginx Cache-Control: immutable for /assets/ - Google Fonts preconnect hints - 图片上传前 Canvas 压缩 (max 1920px, quality 0.8) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,12 @@ async def _enrich_visit(visit: Visit, db: AsyncSession) -> dict:
|
||||
mgr_result = await db.execute(select(User.name).where(User.id == visit.manager_id))
|
||||
manager_name = mgr_result.scalar_one_or_none()
|
||||
|
||||
# Resolve companion UUIDs to names
|
||||
companion_names_resolved = list(visit.companion_names or [])
|
||||
if visit.companions:
|
||||
comp_result = await db.execute(select(User.name).where(User.id.in_(visit.companions)))
|
||||
companion_names_resolved = [n for n, in comp_result.all()] + companion_names_resolved
|
||||
|
||||
return {
|
||||
"id": str(visit.id),
|
||||
"customer_id": str(visit.customer_id),
|
||||
@@ -40,6 +46,8 @@ async def _enrich_visit(visit: Visit, db: AsyncSession) -> dict:
|
||||
"communication_content": visit.communication_content,
|
||||
"customer_demand": visit.customer_demand,
|
||||
"companions": visit.companions,
|
||||
"companion_names": visit.companion_names or [],
|
||||
"companion_names_resolved": companion_names_resolved,
|
||||
"photos": visit.photos,
|
||||
"manager_id": str(visit.manager_id),
|
||||
"manager_name": manager_name,
|
||||
@@ -137,6 +145,7 @@ async def create_visit(
|
||||
communication_content=data.communication_content,
|
||||
customer_demand=data.customer_demand,
|
||||
companions=data.companions,
|
||||
companion_names=data.companion_names,
|
||||
photos=data.photos,
|
||||
manager_id=uuid.UUID(current_user["user_id"]),
|
||||
)
|
||||
@@ -245,10 +254,39 @@ async def delete_visit(
|
||||
if current_user["role"] == "manager" and str(visit.manager_id) != current_user["user_id"]:
|
||||
raise HTTPException(status_code=403, detail="Access denied")
|
||||
|
||||
customer_id = visit.customer_id
|
||||
manager_id = visit.manager_id
|
||||
|
||||
# Clean up photos in MinIO
|
||||
if visit.photos:
|
||||
delete_objects(visit.photos)
|
||||
|
||||
await db.delete(visit)
|
||||
|
||||
# Recalculate customer's last_visit_date from remaining visits
|
||||
latest = await db.execute(
|
||||
select(func.max(Visit.visit_date)).where(
|
||||
Visit.customer_id == customer_id,
|
||||
)
|
||||
)
|
||||
new_latest = latest.scalar()
|
||||
cust = await db.get(Customer, customer_id)
|
||||
if cust:
|
||||
if new_latest:
|
||||
cust.last_visit_date = new_latest
|
||||
# Keep the existing manager if date unchanged, or find who made the latest visit
|
||||
latest_visit = await db.execute(
|
||||
select(Visit).where(
|
||||
Visit.customer_id == customer_id,
|
||||
Visit.visit_date == new_latest,
|
||||
).order_by(Visit.created_at.desc()).limit(1)
|
||||
)
|
||||
lv = latest_visit.scalar_one_or_none()
|
||||
if lv:
|
||||
cust.last_visit_manager_id = lv.manager_id
|
||||
else:
|
||||
cust.last_visit_date = None
|
||||
cust.last_visit_manager_id = None
|
||||
|
||||
await db.commit()
|
||||
return {"detail": "deleted"}
|
||||
|
||||
Reference in New Issue
Block a user