diff --git a/server/api/stats.py b/server/api/stats.py index db7b3c4..d59a2cf 100644 --- a/server/api/stats.py +++ b/server/api/stats.py @@ -98,7 +98,7 @@ def stats_daily(request): replied = qs.filter(reply_status="已回复").count() wechat = qs.filter(wechat_exchanged=True).count() daily_data.append({ - "date": day.isoformat(), + "date": f"{day.isoformat()}T00:00:00", "contacts": total, "replied": replied, "wechat": wechat, diff --git a/server/api/tasks.py b/server/api/tasks.py index f8921a4..c67c568 100644 --- a/server/api/tasks.py +++ b/server/api/tasks.py @@ -5,6 +5,7 @@ """ import json import logging +from datetime import datetime from asgiref.sync import async_to_sync from rest_framework import status as http_status @@ -20,6 +21,12 @@ from server.core.task_dispatcher import task_dispatcher logger = logging.getLogger("server.api.tasks") +def _format_timestamp(ts: float) -> str: + """将时间戳转换为指定格式的字符串:2026-03-01T20:19:51""" + dt = datetime.fromtimestamp(ts) + return dt.strftime("%Y-%m-%dT%H:%M:%S") + + def _task_to_dict(t) -> dict: """将 TaskInfo 转为可序列化字典。""" return { @@ -32,23 +39,49 @@ def _task_to_dict(t) -> dict: "progress": t.progress, "result": t.result, "error": t.error, - "created_at": t.created_at, - "updated_at": t.updated_at, + "created_at": _format_timestamp(t.created_at), + "updated_at": _format_timestamp(t.updated_at), } @api_view(["GET", "POST"]) def task_list(request): """ - GET -> 查询任务列表,支持 ?worker_id= / ?status= / ?limit= 过滤 + GET -> 查询任务列表,支持以下过滤参数: + - ?worker_id= : 按 Worker ID 过滤 + - ?account_id= : 按账号 ID 过滤(自动查询对应的 account_name) + - ?status= : 按任务状态过滤 + - ?limit= : 返回条数上限,默认 50 POST -> 提交新任务(支持 JSON 和 form-data) """ if request.method == "GET": wid = request.query_params.get("worker_id") + account_id = request.query_params.get("account_id") st = request.query_params.get("status") limit = int(request.query_params.get("limit", 50)) task_status = TaskStatus(st) if st else None + + # 如果指定了 account_id,先查询账号信息,转换为 account_name 过滤 + account_name = None + if account_id: + try: + account_id = int(account_id) + account = BossAccount.objects.get(pk=account_id) + account_name = account.browser_name + if not wid: + wid = account.worker_id + except (ValueError, BossAccount.DoesNotExist): + return api_error( + http_status.HTTP_404_NOT_FOUND, + f"未找到 id={account_id} 的账号", + ) + tasks = task_dispatcher.list_tasks(worker_id=wid, status=task_status, limit=limit) + + # 按 account_name 进一步过滤(如果指定了 account_id) + if account_name: + tasks = [t for t in tasks if t.account_name == account_name] + return api_success([_task_to_dict(t) for t in tasks]) # POST: 提交新任务 diff --git a/server/serializers.py b/server/serializers.py index dec0c1f..5d5c86f 100644 --- a/server/serializers.py +++ b/server/serializers.py @@ -56,8 +56,8 @@ class TaskOutSerializer(serializers.Serializer): progress = serializers.CharField(allow_null=True) result = serializers.JSONField(allow_null=True) error = serializers.CharField(allow_null=True) - created_at = serializers.FloatField() - updated_at = serializers.FloatField() + created_at = serializers.CharField() + updated_at = serializers.CharField() # ────────────────────────── Worker ────────────────────────── diff --git a/server/settings.py b/server/settings.py index 0f64ba1..54af651 100644 --- a/server/settings.py +++ b/server/settings.py @@ -68,6 +68,7 @@ REST_FRAMEWORK = { "rest_framework.parsers.MultiPartParser", ], "EXCEPTION_HANDLER": "server.core.exception_handler.custom_exception_handler", + "DATETIME_FORMAT": "%Y-%m-%dT%H:%M:%S", } # ─── Channels ───