Files
shanxia2.0/backend/app/crud/crm/statistics.py
T
zlwuandCursor 3107d537f6 feat(crm): 客户与线索模块接入数据权限过滤
列表按 owner_user_id/create_by 过滤,详情与 CUD 校验 scope;公海客户列表保持租户内全员可见。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-28 20:30:41 +08:00

174 lines
5.5 KiB
Python

"""CRM 统计分析 CRUD"""
from __future__ import annotations
from datetime import datetime, timedelta
from typing import Any, Dict, List, Optional
from sqlalchemy import and_, func
from sqlalchemy.orm import Session
from app.models.crm_models import CrmCustomer, CrmCustomerFollow
from app.crud.crm.data_scope_helpers import apply_crm_create_by_scope, apply_crm_owner_scope
def _customer_base_query(db: Session, tenant_no: str, user_info=None):
q = db.query(CrmCustomer).filter(
CrmCustomer.tenant_no == tenant_no,
CrmCustomer.del_flag == 0,
)
if user_info:
q = apply_crm_owner_scope(q, CrmCustomer, user_info, db, tenant_no)
return q
def get_overview_statistics(
db: Session, tenant_no: str, user_id: str = None, user_info=None
) -> Dict[str, Any]:
"""客户统计概览:客户总数、公海池数、本月新增、成交数"""
base = _customer_base_query(db, tenant_no, user_info=user_info)
total = base.count()
pool_count = base.filter(CrmCustomer.status == "pool").count()
now = datetime.now()
month_start = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
month_new = base.filter(CrmCustomer.create_time >= month_start).count()
deal_count = base.filter(CrmCustomer.deal_status == "dealed").count()
return {
"total": total,
"poolCount": pool_count,
"monthNew": month_new,
"dealCount": deal_count,
}
def get_source_statistics(
db: Session, tenant_no: str, user_info=None
) -> List[Dict[str, Any]]:
"""按客户来源统计"""
rows = (
_customer_base_query(db, tenant_no, user_info=user_info)
.with_entities(CrmCustomer.source, func.count(CrmCustomer.id).label("count"))
.group_by(CrmCustomer.source)
.all()
)
return [{"source": r.source or "未知", "count": r.count} for r in rows]
def get_level_statistics(
db: Session, tenant_no: str, user_info=None
) -> List[Dict[str, Any]]:
"""按客户级别统计"""
rows = (
_customer_base_query(db, tenant_no, user_info=user_info)
.with_entities(CrmCustomer.level, func.count(CrmCustomer.id).label("count"))
.group_by(CrmCustomer.level)
.all()
)
return [{"level": r.level or "未知", "count": r.count} for r in rows]
def get_industry_statistics(
db: Session, tenant_no: str, user_info=None
) -> List[Dict[str, Any]]:
"""按行业统计"""
rows = (
_customer_base_query(db, tenant_no, user_info=user_info)
.with_entities(CrmCustomer.industry, func.count(CrmCustomer.id).label("count"))
.group_by(CrmCustomer.industry)
.all()
)
return [{"industry": r.industry or "未知", "count": r.count} for r in rows]
def get_trend_statistics(
db: Session,
tenant_no: str,
start_date: str = None,
end_date: str = None,
user_info=None,
) -> List[Dict[str, Any]]:
"""成交趋势(按月统计成交客户数)"""
query = _customer_base_query(db, tenant_no, user_info=user_info).with_entities(
func.date_format(CrmCustomer.deal_time, "%Y-%m").label("month"),
func.count(CrmCustomer.id).label("count"),
).filter(
CrmCustomer.deal_status == "dealed",
CrmCustomer.deal_time.isnot(None),
)
if start_date:
query = query.filter(CrmCustomer.deal_time >= start_date)
if end_date:
query = query.filter(CrmCustomer.deal_time <= end_date + " 23:59:59")
rows = query.group_by("month").order_by("month").all()
return [{"month": r.month, "count": r.count} for r in rows]
def get_owner_statistics(
db: Session, tenant_no: str, user_info=None
) -> List[Dict[str, Any]]:
"""按负责人统计客户数"""
rows = (
_customer_base_query(db, tenant_no, user_info=user_info)
.with_entities(
CrmCustomer.owner_user_name,
func.count(CrmCustomer.id).label("count"),
)
.group_by(CrmCustomer.owner_user_id, CrmCustomer.owner_user_name)
.all()
)
return [{"ownerName": r.owner_user_name or "无", "count": r.count} for r in rows]
def get_follow_statistics(
db: Session,
tenant_no: str,
start_date: str = None,
end_date: str = None,
user_info=None,
) -> Dict[str, Any]:
"""跟进统计"""
base = db.query(CrmCustomerFollow).filter(
CrmCustomerFollow.tenant_no == tenant_no,
CrmCustomerFollow.del_flag == 0,
)
if user_info:
base = apply_crm_create_by_scope(base, CrmCustomerFollow, user_info, db, tenant_no)
if start_date:
base = base.filter(CrmCustomerFollow.follow_time >= start_date)
if end_date:
base = base.filter(CrmCustomerFollow.follow_time <= end_date + " 23:59:59")
total = base.count()
# 按跟进方式分组
type_rows = (
base.with_entities(
CrmCustomerFollow.follow_type,
func.count(CrmCustomerFollow.id).label("count"),
)
.group_by(CrmCustomerFollow.follow_type)
.all()
)
by_type = [{"type": r.follow_type or "未知", "count": r.count} for r in type_rows]
# 按负责人分组
owner_rows = (
base.with_entities(
CrmCustomerFollow.owner_user_name,
func.count(CrmCustomerFollow.id).label("count"),
)
.group_by(CrmCustomerFollow.owner_user_id, CrmCustomerFollow.owner_user_name)
.all()
)
by_owner = [
{"ownerName": r.owner_user_name or "无", "count": r.count} for r in owner_rows
]
return {
"total": total,
"byType": by_type,
"byOwner": by_owner,
}