解决已发现问题

This commit is contained in:
Azmat@qq.com
2026-09-17 15:00:15 +08:00
parent f3c49b36be
commit 5df038c635
24 changed files with 1913 additions and 94 deletions
+6 -1
View File
@@ -116,6 +116,8 @@ class AdminUserSerializer(serializers.ModelSerializer):
class AdminTaskSerializer(serializers.ModelSerializer):
team_name = serializers.CharField(source="team.name", read_only=True, default=None)
model_name = serializers.CharField(source="model_config.name", read_only=True, default=None)
# 不改变 AITask.task_type 的调度语义;后台展示/筛选使用来源分类。
task_category = serializers.SerializerMethodField()
cost_anomaly = serializers.SerializerMethodField()
# 单任务毛利(¥):actual_cost(积分)÷汇率 − base_cost。base_cost=0(成本未知)时 None,报表侧过滤
margin_yuan = serializers.SerializerMethodField()
@@ -125,11 +127,14 @@ class AdminTaskSerializer(serializers.ModelSerializer):
class Meta:
model = AITask
fields = [
"id", "task_type", "status", "team", "team_name", "model_name",
"id", "task_type", "task_category", "status", "team", "team_name", "model_name",
"estimated_cost", "actual_cost", "base_cost", "margin_yuan", "cost_anomaly", "error_code", "reapable", "created_at",
]
read_only_fields = fields
def get_task_category(self, obj) -> str:
return str(getattr(obj, "task_category", "standard") or "standard")
def get_cost_anomaly(self, obj) -> bool:
return is_cost_anomaly(obj.estimated_cost, obj.actual_cost)
+22 -1
View File
@@ -403,9 +403,10 @@ class AdminTaskMonitorTests(TestCase):
]
self.assertTrue(task_selects)
for sql in task_selects:
self.assertNotIn("request_payload", sql)
# 仅允许从 JSON 中提取 feature 做「全能创作」分类,不能把整份 Prompt/响应/报错读进列表页。
self.assertNotIn("response_payload", sql)
self.assertNotIn("error_message", sql)
self.assertNotIn("x" * 100_000, str(response.data))
detail = self.ac.get(f"/api/admin/tasks/{self.t_ok.id}/")
self.assertEqual(detail.status_code, 200)
@@ -420,6 +421,26 @@ class AdminTaskMonitorTests(TestCase):
self.assertIn(str(self.t_anom.id), ids)
self.assertNotIn(str(self.t_ok.id), ids)
def test_omni_create_tasks_have_separate_category_and_filter(self):
task = self.AITask.objects.create(
team=self.team,
model_config=self.mc,
task_type=self.AITask.Type.FREE_VIDEO,
status=self.AITask.Status.SUCCEEDED,
estimated_cost="1.0",
actual_cost="1.0",
idempotency_key="k-omni-create",
request_payload={"feature": "omni_create", "prompt": "完整出片指令"},
)
all_rows = self.ac.get("/api/admin/tasks/").data["results"]
row = next(item for item in all_rows if item["id"] == str(task.id))
self.assertEqual(row["task_type"], "free_video")
self.assertEqual(row["task_category"], "omni_create")
category_rows = self.ac.get("/api/admin/tasks/?category=omni_create").data["results"]
self.assertEqual([item["id"] for item in category_rows], [str(task.id)])
def test_cost_anomaly_flag(self):
self.assertTrue(self.ac.get(f"/api/admin/tasks/{self.t_anom.id}/").data["cost_anomaly"])
self.assertFalse(self.ac.get(f"/api/admin/tasks/{self.t_ok.id}/").data["cost_anomaly"])
+12 -1
View File
@@ -3,7 +3,7 @@
import logging
from decimal import Decimal, ROUND_HALF_UP
from django.db.models import Count, F, Q
from django.db.models import Case, CharField, Count, F, Q, Value, When
from rest_framework import status
from rest_framework.authtoken.models import Token
from rest_framework.decorators import api_view, permission_classes
@@ -469,6 +469,14 @@ def admin_tasks(request):
qs = (
AITask.objects.select_related("team", "model_config")
.defer("request_payload", "response_payload", "error_message")
# task_type 仍用于调度;用请求来源区分「全能创作」和自由视频/图片,不读取整份 Prompt。
.annotate(
task_category=Case(
When(request_payload__feature="omni_create", then=Value("omni_create")),
default=Value("standard"),
output_field=CharField(),
)
)
.order_by("-created_at")
)
st = request.query_params.get("status")
@@ -479,6 +487,9 @@ def admin_tasks(request):
tt = request.query_params.get("task_type")
if tt in dict(AITask.Type.choices):
qs = qs.filter(task_type=tt)
category = request.query_params.get("category")
if category == "omni_create":
qs = qs.filter(request_payload__feature="omni_create")
team_id = request.query_params.get("team")
if team_id:
qs = qs.filter(team_id=team_id)