feat(admin): Phase 9 收尾治理 — 全局项目监控 + 数据完整性体检

后端:adminpanel projects(全局项目流水线监控+状态筛)+ integrity(数据体检:无账户团队/无图商品/过期邀请码/
无文件资产/失败项目/卡 reserved 计数 + totals);AdminProjectSerializer;IsPlatformAdmin。
前端:adminApi adminProjects/integrity;Admin 治理页(体检卡片超0高亮 + 项目监控表状态筛)。
测试:adminpanel 53 单测过(项目列/筛/体检报告);无头 e2e _admin-p9.mjs 4 断言过 + 0 console error(全只读);tsc+build 绿。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
seaislee1209
2026-06-19 22:51:49 +08:00
co-authored by Claude Opus 4.8
parent 4ce7e957d2
commit cdfcffcecd
11 changed files with 362 additions and 0 deletions
+44
View File
@@ -531,3 +531,47 @@ class AdminModelProviderTests(TestCase):
def test_write_requires_admin(self):
self.assertEqual(self.nc.post("/api/admin/providers/", {"name": "x", "display_name": "x"}, format="json").status_code, 403)
self.assertEqual(self.nc.post(f"/api/admin/models/{self.m1.id}/set-default/").status_code, 403)
class AdminGovernanceTests(TestCase):
"""Phase 9:收尾治理(全局项目监控 + 数据完整性体检)+ 权限。"""
def setUp(self):
from apps.products.models import Product
from apps.projects.models import Project
self.admin = User.objects.create_user(username="padmin9", password="x", is_platform_admin=True)
self.normal = User.objects.create_user(username="normal9", password="x")
self.team = Team.objects.create(name="GovTeam", owner=self.normal)
TeamMember.objects.create(team=self.team, user=self.normal, role=TeamMember.Role.OWNER)
self.product = Product.objects.create(team=self.team, title="无图商品", created_by=self.normal)
self.proj_ok = Project.objects.create(team=self.team, name="P-ok", product=self.product, status=Project.Status.SCRIPTING, created_by=self.normal)
self.proj_failed = Project.objects.create(team=self.team, name="P-failed", product=self.product, status=Project.Status.FAILED, created_by=self.normal)
self.ac = APIClient()
self.ac.force_authenticate(self.admin)
self.nc = APIClient()
self.nc.force_authenticate(self.normal)
def test_projects_list_permission_and_filter(self):
self.assertEqual(self.nc.get("/api/admin/projects/").status_code, 403)
r = self.ac.get("/api/admin/projects/")
self.assertEqual(r.status_code, 200)
self.assertGreaterEqual(r.data["count"], 2)
failed = self.ac.get("/api/admin/projects/?status=failed")
self.assertTrue(all(p["status"] == "failed" for p in failed.data["results"]))
# 行带团队名 + 商品名
row = next(p for p in r.data["results"] if p["name"] == "P-ok")
self.assertEqual(row["team_name"], "GovTeam")
self.assertEqual(row["product_title"], "无图商品")
def test_integrity_report(self):
self.assertEqual(self.nc.get("/api/admin/integrity/").status_code, 403)
r = self.ac.get("/api/admin/integrity/")
self.assertEqual(r.status_code, 200)
checks = {c["key"]: c["count"] for c in r.data["checks"]}
self.assertIn("products_without_image", checks)
self.assertGreaterEqual(checks["products_without_image"], 1) # 我们建了一个无图商品
self.assertGreaterEqual(checks["failed_projects"], 1)
self.assertGreaterEqual(checks["teams_without_account"], 1) # GovTeam 无 credit_account
self.assertIn("totals", r.data)
self.assertGreaterEqual(r.data["totals"]["projects"], 2)