2026-02-14 17:58:29 +08:00
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
"""
|
|
|
|
|
|
统计分析 API(需要登录):
|
|
|
|
|
|
- GET /api/stats -> 总览统计
|
|
|
|
|
|
- GET /api/stats/daily -> 按日统计
|
|
|
|
|
|
"""
|
|
|
|
|
|
from datetime import timedelta
|
|
|
|
|
|
|
|
|
|
|
|
from django.db.models import Count, Q
|
|
|
|
|
|
from django.utils import timezone
|
|
|
|
|
|
from rest_framework.decorators import api_view
|
|
|
|
|
|
|
2026-02-26 01:27:35 +08:00
|
|
|
|
from server.core.response import api_success
|
2026-02-14 17:58:29 +08:00
|
|
|
|
from server.models import ContactRecord, BossAccount, TaskLog
|
|
|
|
|
|
from server.core.worker_manager import worker_manager
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@api_view(["GET"])
|
|
|
|
|
|
def stats_overview(request):
|
|
|
|
|
|
"""总览统计数据。"""
|
|
|
|
|
|
period = request.query_params.get("period", "all")
|
|
|
|
|
|
now = timezone.now()
|
|
|
|
|
|
|
|
|
|
|
|
# 根据时间段过滤
|
|
|
|
|
|
date_filter = Q()
|
|
|
|
|
|
if period == "today":
|
|
|
|
|
|
date_filter = Q(contacted_at__date=now.date())
|
|
|
|
|
|
elif period == "week":
|
|
|
|
|
|
date_filter = Q(contacted_at__gte=now - timedelta(days=7))
|
|
|
|
|
|
elif period == "month":
|
|
|
|
|
|
date_filter = Q(contacted_at__gte=now - timedelta(days=30))
|
|
|
|
|
|
|
|
|
|
|
|
contacts_qs = ContactRecord.objects.filter(date_filter)
|
|
|
|
|
|
total_contacts = contacts_qs.count()
|
|
|
|
|
|
total_replied = contacts_qs.filter(reply_status="已回复").count()
|
|
|
|
|
|
total_wechat = contacts_qs.filter(wechat_exchanged=True).count()
|
|
|
|
|
|
|
|
|
|
|
|
# 今日数据
|
|
|
|
|
|
today_filter = Q(contacted_at__date=now.date())
|
|
|
|
|
|
today_contacts = ContactRecord.objects.filter(today_filter).count()
|
|
|
|
|
|
today_replied = ContactRecord.objects.filter(today_filter, reply_status="已回复").count()
|
|
|
|
|
|
today_wechat = ContactRecord.objects.filter(today_filter, wechat_exchanged=True).count()
|
|
|
|
|
|
|
|
|
|
|
|
# 账号统计
|
|
|
|
|
|
total_accounts = BossAccount.objects.count()
|
|
|
|
|
|
logged_in_accounts = BossAccount.objects.filter(is_logged_in=True).count()
|
|
|
|
|
|
|
|
|
|
|
|
# Worker 统计
|
|
|
|
|
|
all_workers = worker_manager.get_all_workers()
|
|
|
|
|
|
online_workers = [w for w in all_workers if w.online]
|
|
|
|
|
|
|
|
|
|
|
|
# 任务统计
|
|
|
|
|
|
total_task_logs = TaskLog.objects.count()
|
|
|
|
|
|
|
|
|
|
|
|
reply_rate = round(total_replied / max(total_contacts, 1) * 100, 1)
|
|
|
|
|
|
wechat_rate = round(total_wechat / max(total_replied, 1) * 100, 1)
|
|
|
|
|
|
|
2026-02-26 01:27:35 +08:00
|
|
|
|
return api_success({
|
2026-02-14 17:58:29 +08:00
|
|
|
|
"period": period,
|
|
|
|
|
|
"contacts": {
|
|
|
|
|
|
"total": total_contacts,
|
|
|
|
|
|
"today": today_contacts,
|
|
|
|
|
|
"replied": total_replied,
|
|
|
|
|
|
"today_replied": today_replied,
|
|
|
|
|
|
"reply_rate": reply_rate,
|
|
|
|
|
|
},
|
|
|
|
|
|
"wechat": {
|
|
|
|
|
|
"total": total_wechat,
|
|
|
|
|
|
"today": today_wechat,
|
|
|
|
|
|
"success_rate": wechat_rate,
|
|
|
|
|
|
},
|
|
|
|
|
|
"accounts": {
|
|
|
|
|
|
"total": total_accounts,
|
|
|
|
|
|
"logged_in": logged_in_accounts,
|
|
|
|
|
|
},
|
|
|
|
|
|
"workers": {
|
|
|
|
|
|
"total": len(all_workers),
|
|
|
|
|
|
"online": len(online_workers),
|
|
|
|
|
|
},
|
|
|
|
|
|
"tasks": {
|
|
|
|
|
|
"total_logs": total_task_logs,
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@api_view(["GET"])
|
|
|
|
|
|
def stats_daily(request):
|
|
|
|
|
|
"""按日统计最近 N 天数据。"""
|
|
|
|
|
|
days = int(request.query_params.get("days", 7))
|
|
|
|
|
|
now = timezone.now()
|
|
|
|
|
|
|
|
|
|
|
|
daily_data = []
|
|
|
|
|
|
for i in range(days - 1, -1, -1):
|
|
|
|
|
|
day = (now - timedelta(days=i)).date()
|
|
|
|
|
|
day_filter = Q(contacted_at__date=day)
|
|
|
|
|
|
qs = ContactRecord.objects.filter(day_filter)
|
|
|
|
|
|
total = qs.count()
|
|
|
|
|
|
replied = qs.filter(reply_status="已回复").count()
|
|
|
|
|
|
wechat = qs.filter(wechat_exchanged=True).count()
|
|
|
|
|
|
daily_data.append({
|
2026-03-01 23:18:23 +08:00
|
|
|
|
"date": f"{day.isoformat()}T00:00:00",
|
2026-02-14 17:58:29 +08:00
|
|
|
|
"contacts": total,
|
|
|
|
|
|
"replied": replied,
|
|
|
|
|
|
"wechat": wechat,
|
|
|
|
|
|
"reply_rate": round(replied / max(total, 1) * 100, 1),
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-02-26 01:27:35 +08:00
|
|
|
|
return api_success(daily_data)
|