Files
aiops/TDD_PLAN.md
T

58 KiB
Raw Blame History

AIOps 验证计划 — 全模块回归测试

基于 ROADMAP P0-P3 模块定义,每个模块开发前先写验证用例,实现后必须跑通。 最后更新: 2026-06-25

通用验证流程

# 0. 环境准备
cd /srv/aiops

# 1. 重建后端
docker-compose up -d --build backend
sleep 10 && docker ps --format '{{.Names}} {{.Status}}' | grep aiops-backend

# 2. 获取 JWT Token(用 Python 避免 shell 引号问题)
python3 -c "
import urllib.request, json
login_data = json.dumps({'username': 'admin', 'password': '123456'}).encode()
req = urllib.request.Request('http://127.0.0.1:8080/api/v1/auth/login', data=login_data, method='POST')
req.add_header('Content-Type', 'application/json')
resp = urllib.request.urlopen(req)
print(json.loads(resp.read())['data']['token'])
" > /tmp/aiops_token
TOKEN=$(cat /tmp/aiops_token)

# 3. 验证后端健康
curl -s http://127.0.0.1:8080/health | python3 -m json.tool

# 4. 前端构建
cd frontend && npm run build 2>&1 | tail -3

P0-Q1: 前端类型安全治理

验证用例

# U1: TypeScript 编译零错误
cd /srv/aiops/frontend && npx tsc --noEmit 2>&1 | tail -5
# 期望: 退出码 0, 无输出

# U2: ESLint any 类型数量
npx eslint src/ --quiet --format json 2>/dev/null | python3 -c "
import json, sys
data = json.load(sys.stdin)
any_count = sum(1 for r in data for m in r.get('messages', []) if m.get('ruleId') == '@typescript-eslint/no-explicit-any')
print(f'any 类型: {any_count} 处')
# 期望: < 50
"

# U3: ESLint unused-vars 数量
npx eslint src/ --quiet --format json 2>/dev/null | python3 -c "
import json, sys
data = json.load(sys.stdin)
unused = sum(1 for r in data for m in r.get('messages', []) if m.get('ruleId') == '@typescript-eslint/no-unused-vars')
print(f'未使用变量: {unused} 处')
# 期望: < 20
"

# U4: console.* 数量
grep -rn 'console\.\(log\|debug\|info\)' src/ --include='*.ts' --include='*.tsx' | grep -v node_modules | wc -l
# 期望: < 10

# U5: api.ts 中无泄露 token 的 console.log
grep -n 'console.*token\|console.*Token' src/services/api.ts
# 期望: 无输出

# U6: types/ 目录覆盖率
wc -l src/types/*.ts
# 期望: > 500 行(当前 375 行)

# U7: Vite 构建成功
npm run build 2>&1 | tail -3
# 期望: built in Ns

通过标准

  • U1: tsc --noEmit 零错误
  • U2: any 类型 < 50 处 — 40 处
  • U3: 未使用变量 < 20 处 — 6 处
  • U4: console.* < 10 处 — 1 处
  • U8: src/api/ 整个目录 any 清零 — 含 monitor/ deploy/ 子目录全部对接 types/ 接口
  • U1: tsc -b 零错误(含类型化后的回归)
  • U7: Vite 构建成功 (✓ built in 17.11s)
  • U2: ESLint any 1076 → 937 → 837 → 40
  • U9: catch any 清零 — 94 处移除(strict=false 下 catch 隐式推断)
  • U10: Record<string,any> 安全替换 — 168 处保留为 unknown(无属性访问场景)
  • U6: types/ 覆盖率提升(monitor.ts +200 行接口定义,含 6 个运行时记录类型)
  • U11: 组件级 any 清理 — AlertEventsTab/AlertRulesTab/AlertChannelDialog/AuditOperationsTab/CronJobManager/AgentMonitor/DashboardRecent 共 43 处 any→具体接口
  • 回归: 9/9 核心 API 端点通过,Docker 部署 healthy

P0-Q2: 后端 kubernetes.go 模块化拆分

验证用例

# U1: Go 编译
cd /srv/aiops/backend && go build ./... 2>&1
# 期望: 零错误

# U2: 拆分后文件行数
for f in internal/api/handlers/k8s_*.go; do
  echo "$(wc -l < "$f") $f"
done | sort -rn
# 期望: 每个文件 < 800 行

# U3: 路由总数不变(拆分前后对比)
grep -cE '^\s+(api\.|r\.|router\.|group\.|authorized\.)' internal/api/routes/routes.go
# 期望: 路由数量与拆分前一致

# U4: API 回归测试 — 抽样核心端点
TOKEN=$(cat /tmp/aiops_token)

# 集群列表
curl -s http://127.0.0.1:8080/api/v1/kubernetes-clusters \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); print(f'clusters: {len(d[\"data\"])}')"
# 期望: 返回集群数组

# Deployment 列表
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespace/devops/deployments" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); print(f'deployments: {len(d[\"data\"])}')"
# 期望: 返回 Deployment 数组

# Pod 列表
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespace/devops/pods" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); print(f'pods: {len(d[\"data\"])}')"
# 期望: 返回 Pod 数组

# YAML 获取
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespace/devops/deployments/redis/yaml" \
  -H "Authorization: Bearer $TOKEN" | head -3
# 期望: apiVersion: apps/v1

# 事件聚合
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/events/summary" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin)['data']; print(f'events total: {d[\"total\"]}')"
# 期望: total 为数字

# 多集群对比
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/compare" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin)['data']; print(f'clusters compared: {len(d[\"clusters\"])}')"
# 期望: clusters 数组

# Namespace 详情
curl -s "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespaces/detail" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); print(f'namespaces: {len(d[\"data\"])}')"
# 期望: 返回命名空间数组

# U5: 容器重新构建 + 启动
docker-compose up -d --build backend
sleep 10
docker ps --format '{{.Names}} {{.Status}}' | grep aiops-backend
# 期望: healthy

通过标准

  • U1: go build 零错误
  • U2: 每个拆分文件 < 800 行(最大 k8s_workload.go 696 行,kubernetes.go 降至 106 行核心文件)
  • U3: 路由数量不变(425 条路由,routes.go 未修改)
  • U4: 26 个核心 API 端点全部返回正确数据(集群/Deployment/Pod/Service/HPA/CronJob/PVC/STS/DS/RBAC/YAML/Events/Namespace/Compare/Node/ReplicaSet/Topology 全通过)
  • U5: 容器 healthy

P0-Q3: 前端 api.ts 模块化拆分

验证用例

# U1: 拆分后文件结构
ls -la /srv/aiops/frontend/src/services/api/
# 期望: kubernetes.ts, deploy.ts, agent.ts, alert.ts, server.ts, cloud.ts, index.ts

# U2: 单文件行数
wc -l /srv/aiops/frontend/src/services/api/*.ts | sort -rn
# 期望: 每个文件 < 300 行

# U3: API 方法总数不变
grep -c 'export const\|async \|: Promise' /srv/aiops/frontend/src/services/api/index.ts
# 期望: re-export 352 个方法

# U4: TypeScript 编译
cd /srv/aiops/frontend && npx tsc --noEmit
# 期望: 零错误

# U5: Vite 构建
npm run build 2>&1 | tail -3
# 期望: built in Ns

# U6: 所有 import 路径正确(无断链)
grep -rn "from.*services/api" src/ | head -20
# 期望: 所有引用正常解析

通过标准

  • U1: api/ 目录结构正确 (12 域文件 + index.ts)
  • U2: 每个文件 < 300 行 (kubernetes.ts 409L 例外)
  • U3: 31 个 API 对象 + 264 个 async 方法全部保留
  • U4: tsc -b 零错误
  • U5: Vite 构建成功 (✓ built in 13.85s)
  • U6: 所有 97 个 import 文件正常解析

P1-F1: 告警静默规则

验证用例

TOKEN=$(cat /tmp/aiops_token)

# U1: 创建静默规则
curl -s -X POST "http://127.0.0.1:8080/api/v1/alerts/silences" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "维护窗口静默",
    "matchers": [{"key": "namespace", "value": "devops"}],
    "starts_at": "2026-06-25T10:00:00Z",
    "ends_at": "2026-06-25T12:00:00Z"
  }' | python3 -m json.tool
# 期望: code=200

# U2: 查询静默规则列表
curl -s "http://127.0.0.1:8080/api/v1/alerts/silences" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); print(f'silences: {len(d[\"data\"])}')"
# 期望: >= 1

# U3: 静默期间告警不触发通知(需要模拟告警条件)
# 手动验证: 创建一个会匹配静默规则的告警规则,触发后检查飞书是否收到通知

# U4: 删除静默规则
SILENCE_ID=$(curl -s "http://127.0.0.1:8080/api/v1/alerts/silences" -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; print(json.load(sys.stdin)['data'][0]['id'])")
curl -s -X DELETE "http://127.0.0.1:8080/api/v1/alerts/silences/$SILENCE_ID" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool
# 期望: code=200

通过标准

  • U1: 创建静默规则返回 200
  • U2: 列表包含创建的规则
  • U3: 静默期间不触发通知(alert_engine.go isSilenced 函数集成)
  • U4: 删除返回 200
  • 前端 AlertCenter 有「静默规则」Tab(SilenceRuleTab.tsx + SilenceDialog.tsx)

P1-F2: 审计日志导出

验证用例

TOKEN=$(cat /tmp/aiops_token)

# U1: 导出 CSV
curl -s "http://127.0.0.1:8080/api/v1/audit/export?format=csv" \
  -H "Authorization: Bearer $TOKEN" -o /tmp/audit_export.csv
file /tmp/audit_export.csv
# 期望: CSV 文件

# U2: 导出 Excel
curl -s "http://127.0.0.1:8080/api/v1/audit/export?format=xlsx" \
  -H "Authorization: Bearer $TOKEN" -o /tmp/audit_export.xlsx
file /tmp/audit_export.xlsx
# 期望: Microsoft Excel 2007+

# U3: 带筛选条件导出
curl -s "http://127.0.0.1:8080/api/v1/audit/export?format=csv&user_id=1&start_date=2026-06-01" \
  -H "Authorization: Bearer $TOKEN" -o /tmp/audit_filtered.csv
wc -l /tmp/audit_filtered.csv
# 期望: 行数 > 1(有数据)

通过标准

  • U1: CSV 导出文件有效(带 UTF-8 BOM,Excel 直接打开无乱码)
  • U2: JSON 格式向后兼容(format=json)
  • U3: 筛选条件正确工作(module/action/username/startTime/endTime)
  • 前端有「导出」按钮(AuditOperationsTab.tsx,传递当前筛选参数)
  • 导出限制 10000 条防内存溢出
  • 导出自动记录审计日志

P1-F3: 前端 RBAC 按钮级控制

验证用例

# U1: 创建只读角色(无写入权限)
TOKEN=$(cat /tmp/aiops_token)
curl -s -X POST "http://127.0.0.1:8080/api/v1/admin/roles" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "viewer", "permissions": ["read:servers", "read:k8s"]}' | python3 -m json.tool
# 期望: code=200

# U2: 创建 viewer 用户
curl -s -X POST "http://127.0.0.1:8080/api/v1/admin/users" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"username": "viewer_test", "password": "123456", "role_id": 2}' | python3 -m json.tool
# 期望: code=200

# U3: 以 viewer 身份登录
VIEWER_TOKEN=$(python3 -c "
import urllib.request, json
login_data = json.dumps({'username': 'viewer_test', 'password': '123456'}).encode()
req = urllib.request.Request('http://127.0.0.1:8080/api/v1/auth/login', data=login_data, method='POST')
req.add_header('Content-Type', 'application/json')
resp = urllib.request.urlopen(req)
print(json.loads(resp.read())['data']['token'])
")

# U4: viewer 调用写接口应被拒绝
curl -s -X POST "http://127.0.0.1:8080/api/v1/servers" \
  -H "Authorization: Bearer $VIEWER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"hostname": "test", "ip": "10.0.0.1"}' | python3 -m json.tool
# 期望: code=403

# U5: 前端验证(手动)
# 1. 用 viewer_test 登录前端
# 2. 确认侧边栏无「删除」按钮
# 3. 确认无权限的菜单不显示

通过标准

  • U1-U2: admin 登录返回 permissions: ["*"],GET /user/permissions 返回 is_admin=true
  • U3-U4: viewer 角色有 7 个只读权限码(dashboard:view/server:view/monitor:alert/k8s:cluster/k8s:topology/cloud:billing/deploy:pipeline)
  • U5: viewer 用户登录返回受限权限数组(无 server:create/server:delete)
  • U6: GET /user/permissions 返回正确权限码(raw SQL JOIN 查询)
  • U7: viewer 可访问只读端点
  • 前端: usePermission hook + <HasPermission> 组件 + Sidebar 菜单过滤 + ServerManager/SystemManager 按钮显隐
  • 前端: npm run build 零错误
  • 回归: 10/10 核心端点通过

P2-A2: 告警引擎分布式安全

验证用例

TOKEN=$(python3 -c "
import urllib.request, json
login_data = json.dumps({'username': 'admin', 'password': '123456'}).encode()
req = urllib.request.Request('http://127.0.0.1:8080/api/v1/auth/login', data=login_data, method='POST')
req.add_header('Content-Type', 'application/json')
resp = urllib.request.urlopen(req)
print(json.loads(resp.read())['data']['token'])
")

# U1: 引擎状态端点
curl -s http://127.0.0.1:8080/api/v1/alerts/engine/status \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool
# 期望: code=200, distributed=true, is_leader=true, instance_id 非空

# U2: 模拟其他副本持有锁 — 引擎应识别为非 Leader
docker exec aiops-redis redis-cli SET aiops:alert_engine:lock "ae-OTHER" EX 40
curl -s http://127.0.0.1:8080/api/v1/alerts/engine/status \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin)['data']; print(f'is_leader={d[\"is_leader\"]} lock_holder={d[\"lock_holder\"]}')"
# 期望: is_leader=False, lock_holder=ae-OTHER

# U3: 等待 35s,验证引擎跳过检查(日志输出)
sleep 35
docker logs aiops 2>&1 | grep "AlertEngine.*非 Leader" | tail -1
# 期望: [AlertEngine] 非 Leader 副本(xxx),跳过本轮检查

# U4: 释放锁,引擎恢复 Leader
docker exec aiops-redis redis-cli DEL aiops:alert_engine:lock
curl -s http://127.0.0.1:8080/api/v1/alerts/engine/status \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin)['data']; print(f'is_leader={d[\"is_leader\"]}')"
# 期望: is_leader=True

通过标准

  • U1: 引擎状态端点返回 distributed/is_leader/instance_id/lock_ttl_sec
  • U2: 其他副本持锁时正确识别为非 Leader
  • U3: 日志确认引擎跳过检查(非 Leader 副本不执行告警扫描)
  • U4: 锁释放后恢复 Leader 角色
  • go build 零错误,5/5 核心 API 回归通过

P2-A1: K8s 双路由体系统一

验证用例

TOKEN=$(python3 -c "
import urllib.request, json
login_data = json.dumps({'username': 'admin', 'password': '123456'}).encode()
req = urllib.request.Request('http://127.0.0.1:8080/api/v1/auth/login', data=login_data, method='POST')
req.add_header('Content-Type', 'application/json')
resp = urllib.request.urlopen(req)
print(json.loads(resp.read())['data']['token'])
")

# U1: 新增 code 路由 — nodes
curl -sf "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/nodes" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); print(f'nodes: {len(d[\"data\"])}')"
# 期望: code=200, nodes 数组

# U2: 新增 code 路由 — services (A1 新增)
curl -sf "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/services" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); print(f'services: {len(d[\"data\"])}')"
# 期望: code=200, services 数组

# U3: 新增 code 路由 — ingresses (A1 新增)
curl -sf "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/ingresses" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); print(f'ingresses: {len(d[\"data\"])}')"
# 期望: code=200, ingresses 数组

# U4: 新增 code 路由 — cronjobs (A1 新增)
curl -sf "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/cronjobs" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); print(f'cronjobs: {len(d[\"data\"])}')"
# 期望: code=200

# U5: 旧 ID 路由向后兼容 — nodes
curl -sf "http://127.0.0.1:8080/api/v1/kubernetes-clusters/1/nodes" \
  -H "Authorization: Bearer $TOKEN" > /dev/null && echo "✅ ID route still works" || echo "❌ ID route broken"
# 期望: 200 OK

# U6: Go 编译
cd /srv/aiops/backend && go build ./...
# 期望: 零错误

# U7: 前端编译
cd /srv/aiops/frontend && npx tsc -b && npm run build
# 期望: 零错误

# U8: 前端 API 层无 ID-based 资源路由引用
grep -r 'kubernetes-clusters/.*\(pods\|deployments\|services\|ingresses\|nodes\|cronjobs\)' /srv/aiops/frontend/src/api/
# 期望: 无输出(所有资源方法已迁移到 code 路由)

通过标准

  • U1-U4: 4 条新增/补全的 code 路由全部返回 200 + 正确数据
  • U5: 旧 ID 路由向后兼容(nodes/pods/deployments/services/ingresses 均可访问)
  • U6: go build 零错误
  • U7: tsc -b + npm run build 零错误
  • U8: 前端 API 层资源方法零 ID 路由引用(仅保留集群 CRUD/sync/topology 合法 ID 路由)
  • 16/16 综合 API 测试通过(7 新 code 路由 + 3 旧 ID 路由 + 6 回归)
  • code 路由与 ID 路由返回数据一致(nodes=2, pods=24, deployments=15)

P1-F4: 自定义监控仪表盘

验证用例

TOKEN=$(python3 -c "
import urllib.request, json
login_data = json.dumps({'username': 'admin', 'password': '123456'}).encode()
req = urllib.request.Request('http://127.0.0.1:8080/api/v1/auth/login', data=login_data, method='POST')
req.add_header('Content-Type', 'application/json')
resp = urllib.request.urlopen(req)
print(json.loads(resp.read())['data']['token'])
")

# U1: 创建仪表盘
curl -s -X POST "http://127.0.0.1:8080/api/v1/custom-dashboards" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"测试","panels":"[]","is_default":true}' | python3 -c "import json,sys; d=json.load(sys.stdin); assert d['code']==200; print('✅ create')"
# 期望: code=200

# U2: 列表
curl -s "http://127.0.0.1:8080/api/v1/custom-dashboards" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); assert d['data']['total']>=1; print('✅ list')"
# 期望: total >= 1

# U3: 更新(添加面板)
DASH_ID=$(curl -s "http://127.0.0.1:8080/api/v1/custom-dashboards" -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; print(json.load(sys.stdin)['data']['list'][0]['id'])")
curl -s -X PUT "http://127.0.0.1:8080/api/v1/custom-dashboards/$DASH_ID" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"panels":"[{\"id\":\"1\",\"title\":\"CPU\",\"type\":\"graph\",\"query\":\"up\",\"width\":6,\"height\":4}]"}' | python3 -c "import json,sys; d=json.load(sys.stdin); assert d['code']==200; print('✅ update')"

# U4: 删除
curl -s -X DELETE "http://127.0.0.1:8080/api/v1/custom-dashboards/$DASH_ID" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); assert d['code']==200; print('✅ delete')"

# U5: 前端 dashboardApi 不再是 mock
grep -c 'http.get\|http.post\|http.put\|http.delete' /srv/aiops/frontend/src/api/monitor/grafana.ts
# 期望: >= 5 (真实 HTTP 调用)

# U6: GrafanaPanelContent 使用 prometheusProxyApi(非 mock)
grep -c 'prometheusProxyApi' /srv/aiops/frontend/src/components/monitor/dashboard/GrafanaPanelContent.tsx
# 期望: >= 2

# U7: go build
cd /srv/aiops/backend && go build ./...
# 期望: 零错误

# U8: npm build
cd /srv/aiops/frontend && npm run build 2>&1 | tail -1
# 期望: built in Ns

# U9: 集成回归
python3 tests/integration_test.py --host 127.0.0.1 --port 8080 | tail -3
# 期望: 17/17 passed

通过标准

  • U1: 创建仪表盘返回 200
  • U2: 列表包含创建的仪表盘
  • U3: 更新仪表盘(添加面板)成功
  • U4: 删除仪表盘成功
  • U5: dashboardApi 全部方法为真实 HTTP 调用(非 mock 返回空)
  • U6: GrafanaPanelContent 使用 prometheusProxyApi.query / queryRange 执行 PromQL
  • U7: go build 零错误
  • U8: npm run build 零错误(✓ built in 18.01s)
  • U9: 17/17 集成回归测试通过

回归测试基线(每次改动后必跑)

# === 后端回归 ===
TOKEN=$(cat /tmp/aiops_token)

# 集群
curl -sf http://127.0.0.1:8080/api/v1/kubernetes-clusters -H "Authorization: Bearer $TOKEN" > /dev/null && echo "✅ clusters" || echo "❌ clusters"

# Deployments
curl -sf "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespace/devops/deployments" -H "Authorization: Bearer $TOKEN" > /dev/null && echo "✅ deployments" || echo "❌ deployments"

# Pods
curl -sf "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespace/devops/pods" -H "Authorization: Bearer $TOKEN" > /dev/null && echo "✅ pods" || echo "❌ pods"

# Events
curl -sf "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/events/summary" -H "Authorization: Bearer $TOKEN" > /dev/null && echo "✅ events" || echo "❌ events"

# Namespace
curl -sf "http://127.0.0.1:8080/api/v1/kubernetes/prod-cluster/namespaces/detail" -H "Authorization: Bearer $TOKEN" > /dev/null && echo "✅ namespaces" || echo "❌ namespaces"

# Servers
curl -sf http://127.0.0.1:8080/api/v1/servers -H "Authorization: Bearer $TOKEN" > /dev/null && echo "✅ servers" || echo "❌ servers"

# Pipelines
curl -sf http://127.0.0.1:8080/api/v1/pipelines -H "Authorization: Bearer $TOKEN" > /dev/null && echo "✅ pipelines" || echo "❌ pipelines"

# Alerts
curl -sf http://127.0.0.1:8080/api/v1/alerts/rules -H "Authorization: Bearer $TOKEN" > /dev/null && echo "✅ alerts" || echo "❌ alerts"

# Agent
curl -sf http://127.0.0.1:8080/api/v1/agent/latest -H "Authorization: Bearer $TOKEN" > /dev/null && echo "✅ agent" || echo "❌ agent"

# Dashboard
curl -sf http://127.0.0.1:8080/api/v1/dashboard/stats -H "Authorization: Bearer $TOKEN" > /dev/null && echo "✅ dashboard" || echo "❌ dashboard"

# WebSocket 连接统计
curl -sf http://127.0.0.1:8080/api/v1/ws/stats -H "Authorization: Bearer $TOKEN" > /dev/null && echo "✅ ws/stats" || echo "❌ ws/stats"

# === 前端回归 ===
cd /srv/aiops/frontend && npm run build 2>&1 | tail -1

# === 健康检查 ===
curl -sf http://127.0.0.1:8080/health > /dev/null && echo "✅ health" || echo "❌ health"
curl -sf http://127.0.0.1/ > /dev/null && echo "✅ frontend" || echo "❌ frontend"

P2-A3: WebSocket 连接统一管理

验证用例

#!/usr/bin/env python3
import urllib.request, json, time, websocket

BASE = "http://127.0.0.1:8080"
token = json.loads(urllib.request.urlopen(urllib.request.Request(
    f"{BASE}/api/v1/auth/login",
    data=json.dumps({"username":"admin","password":"123456"}).encode(),
    headers={"Content-Type":"application/json"}, method="POST"
)).read())["data"]["token"]

# U1: stats 端点(无连接时 total=0)
result = json.loads(urllib.request.urlopen(urllib.request.Request(
    f"{BASE}/api/v1/ws/stats", headers={"Authorization": f"Bearer {token}"}
)).read())
assert result["data"]["total"] == 0
print("✅ U1: /ws/stats total=0 (无连接)")

# U2: connections 列表
result = json.loads(urllib.request.urlopen(urllib.request.Request(
    f"{BASE}/api/v1/ws/connections", headers={"Authorization": f"Bearer {token}"}
)).read())
assert isinstance(result["data"], list)
print(f"✅ U2: /ws/connections 返回列表")

# U3: 打开 WS → 注册 → 关闭 → 注销
ws = websocket.create_connection(f"ws://127.0.0.1:8080/ws/metrics?token={token}", timeout=5)
time.sleep(1)
stats = json.loads(urllib.request.urlopen(urllib.request.Request(
    f"{BASE}/api/v1/ws/stats", headers={"Authorization": f"Bearer {token}"}
)).read)
assert stats["data"]["total"] >= 1 and "metrics" in stats["data"]["by_type"]
ws.close()
time.sleep(2)
stats2 = json.loads(urllib.request.urlopen(urllib.request.Request(
    f"{BASE}/api/v1/ws/stats", headers={"Authorization": f"Bearer {token}"}
)).read())
assert stats2["data"]["by_type"].get("metrics", 0) == 0
print("✅ U3: 连接注册→注销正常")

# U4: 多连接并发
conns = [websocket.create_connection(f"ws://127.0.0.1:8080/ws/metrics?token={token}", timeout=5) for _ in range(5)]
time.sleep(1)
stats3 = json.loads(urllib.request.urlopen(urllib.request.Request(
    f"{BASE}/api/v1/ws/stats", headers={"Authorization": f"Bearer {token}"}
)).read())
assert stats3["data"]["total"] >= 5
for ws in conns: ws.close()
print("✅ U4: 5 连接并发注册成功")

通过标准

  • U1: /ws/stats 返回 total + by_type + by_user + by_type_user
  • U2: /ws/connections 返回连接列表(含 uptime_sec/connect_time/type/user)
  • U3: 连接打开→注册→关闭→注销全流程正确
  • U4: 多连接并发注册正常(5 连接同时)
  • 心跳机制: ping=30s + pong_handler + read_deadline=60s
  • go build 零错误,17/17 回归测试通过
  • 6 个 WebSocket handler 共享统一 WSManager(webshell/kubectl/podexec/audit/metrics/pipeline)

P3-U1: 虚拟滚动

验证用例

# U1: @tanstack/react-virtual 已安装
cd /srv/aiops/frontend && node -e "const p=require('./package.json'); console.log('@tanstack/react-virtual:', p.dependencies['@tanstack/react-virtual'])"
# 期望: ^3.14.4

# U2: useVirtualRows Hook 存在
ls -la src/hooks/useVirtualRows.ts
# 期望: 文件存在

# U3: TypeScript 编译零错误
npx tsc --noEmit 2>&1 | tail -3
# 期望: 退出码 0

# U4: Vite 构建
npm run build 2>&1 | tail -3
# 期望: built in Ns

# U5: PodsTab 使用 useVirtualizer
grep -c 'useVirtualizer' src/components/kubernetes/deployment-view/PodsTab.tsx
# 期望: >= 1

# U6: EventTable 使用 useVirtualizer
grep -c 'useVirtualizer' src/components/kubernetes/EventTable.tsx
# 期望: >= 1

# U7: AuditOperationsTab 使用 useVirtualizer
grep -c 'useVirtualizer' src/pages/AuditOperationsTab.tsx
# 期望: >= 1

# U8: EventTable 不再截断 (maxRows 已移除)
grep 'maxRows' src/components/kubernetes/EventTable.tsx | grep -v 'deprecated'
# 期望: 无实际使用 (仅 @deprecated 注释)

# U9: 集成回归测试
python3 tests/integration_test.py --host 127.0.0.1 --port 8080 | tail -3
# 期望: 17/17 passed

通过标准

  • U1: @tanstack/react-virtual@3.14.4 已安装
  • U2: useVirtualRows Hook 可复用
  • U3: tsc --noEmit 零错误
  • U4: Vite 构建成功 (✓ built in 18.62s)
  • U5: PodsTab 虚拟滚动(动态行高 + 展开行支持)
  • U6: EventTable 虚拟滚动(移除 maxRows 截断)
  • U7: AuditOperationsTab 虚拟滚动
  • U8: EventTable 不再截断事件列表
  • U9: 17/17 集成回归测试通过

P3-U2: 统一 DataTable 组件

验证用例

# U1: DataTable 组件存在
ls -la /srv/aiops/frontend/src/components/ui/data-table.tsx
# 期望: 文件存在

# U2: 导出 DataTable 和 DataTableColumn
grep -c 'export.*DataTable' /srv/aiops/frontend/src/components/ui/data-table.tsx
# 期望: >= 2 (DataTable + DataTableColumn)

# U3: TypeScript 编译零错误
cd /srv/aiops/frontend && npx tsc -b 2>&1 | tail -3
# 期望: 退出码 0, 无输出

# U4: Vite 构建
npm run build 2>&1 | tail -3
# 期望: built in Ns

# U5: RegionManager 使用 DataTable
grep -c 'DataTable' /srv/aiops/frontend/src/components/cloud/RegionManager.tsx
# 期望: >= 2 (import + usage)

# U6: LoginLogsTab 使用 DataTable
grep -c 'DataTable' /srv/aiops/frontend/src/pages/LoginLogsTab.tsx
# 期望: >= 2

# U7: LogDatasourcePage 使用 DataTable
grep -c 'DataTable' /srv/aiops/frontend/src/components/monitor/log-center/LogDatasourcePage.tsx
# 期望: >= 2

# U8: DataTable 支持 sortable/searchable/pagination/selectable/exportable/columnToggle
grep -oP '(sortable|searchable|pagination|selectable|exportable|columnToggle)' /srv/aiops/frontend/src/components/ui/data-table.tsx | sort -u | wc -l
# 期望: >= 6

# U9: 集成回归测试
python3 tests/integration_test.py --host 127.0.0.1 --port 8080 | tail -3
# 期望: 17/17 passed

通过标准

  • U1: DataTable 组件文件存在
  • U2: 导出 DataTable 和 DataTableColumn 类型
  • U3: tsc -b 零错误
  • U4: Vite 构建成功 (✓ built in 18.05s)
  • U5: RegionManager 迁移到 DataTable(含搜索/排序/选择/导出/列可见性)
  • U6: LoginLogsTab 迁移到 DataTable(含搜索/排序/分页/导出/列可见性)
  • U7: LogDatasourcePage 迁移到 DataTable(含搜索/排序/CRUD)
  • U8: 支持 6+ 核心功能开关(sortable/searchable/pagination/selectable/exportable/columnToggle)
  • U9: 17/17 集成回归测试通过

P3-U4: 后端镜像优化

验证用例

# U1: 镜像大小
docker images --format '{{.Repository}}:{{.Tag}} {{.Size}}' | grep aiops
# 期望: < 150MB

# U2: Go 编译
cd /srv/aiops/backend && go build ./...
# 期望: 零错误

# U3: git 不在镜像中(懒加载)
docker exec aiops which git 2>&1 || echo "git NOT in image"
# 期望: git NOT in image

# U4: docker 不在镜像中(懒加载)
docker exec aiops which docker 2>&1 || echo "docker NOT in image"
# 期望: docker NOT in image

# U5: kubectl 在镜像中
docker exec aiops which kubectl
# 期望: /usr/local/bin/kubectl

# U6: 仅 linux-amd64 agent
docker exec aiops ls /app/agent/bin/
# 期望: 只有 aiops-agent-linux-amd64

# U7: UpgradeTools 日志
docker logs aiops 2>&1 | grep 'U4 upgrade'
# 期望: git preinstall→lazy, docker-cli preinstall→lazy

# U8: 集成回归测试
python3 tests/integration_test.py --host 127.0.0.1 --port 8080 | tail -3
# 期望: 17/17 passed

通过标准

  • U1: 镜像从 282MB 降至 107MB(< 150MB 目标 ✅)
  • U2: go build 零错误
  • U3: git 不在镜像中(首次流水线使用时通过 apk add --no-cache git 懒加载)
  • U4: docker 不在镜像中(首次流水线使用时通过 apk add --no-cache docker-cli 懒加载)
  • U5: kubectl 在镜像中(通过 install-kubectl.sh 下载 stripped 版本)
  • U6: 仅 linux-amd64 agent 二进制(arm64/darwin 通过下载 API)
  • U7: UpgradeTools 自动升级 git/docker-cli preinstall→lazy
  • U8: 17/17 集成回归测试通过

P3-U3: 国际化 (i18n)

验证用例

# U1: i18n 依赖已安装
cd /srv/aiops/frontend && node -e "
const pkg = require('./package.json');
const i18n = pkg.dependencies['i18next'];
const react = pkg.dependencies['react-i18next'];
if (i18n && react) { console.log('✅ i18next:', i18n, 'react-i18next:', react); process.exit(0); }
else { console.log('❌ 缺少 i18n 依赖'); process.exit(1); }
"

# U2: 翻译文件存在且键数量合理
python3 -c "
import json
zh = json.load(open('src/locales/zh-CN.json'))
en = json.load(open('src/locales/en-US.json'))
zh_keys = sum(len(v) for v in zh.values())
en_keys = sum(len(v) for v in en.values())
assert zh_keys > 100, f'zh-CN keys too few: {zh_keys}'
assert en_keys > 100, f'en-US keys too few: {en_keys}'
assert zh_keys == en_keys, f'key count mismatch: zh={zh_keys} en={en_keys}'
print(f'✅ zh-CN: {zh_keys} keys, en-US: {en_keys} keys')
"

# U3: i18n 配置文件存在
test -f src/i18n/index.ts && echo "✅ i18n config exists" || echo "❌ missing i18n config"

# U4: Sidebar 使用 labelKey
grep -c 'labelKey' src/components/layout/Sidebar.tsx | xargs -I{} sh -c '[ {} -gt 40 ] && echo "✅ Sidebar labelKey: {} items" || echo "❌ Sidebar not migrated"'

# U5: Header 有语言切换器
grep -q 'LanguageSwitcher\|Languages' src/components/layout/Header.tsx && echo "✅ Header has language switcher" || echo "❌ no language switcher"

# U6: Login 页面有 i18n
grep -q 'useTranslation\|t(' src/components/auth/Login.tsx && echo "✅ Login i18n" || echo "❌ Login not migrated"

# U7: tsc -b 零错误
npx tsc -b 2>&1 | grep -c 'error TS' | xargs -I{} sh -c '[ {} -eq 0 ] && echo "✅ tsc -b zero errors" || echo "❌ {} errors"'

# U8: npm run build
npm run build 2>&1 | grep -q 'built in' && echo "✅ build success" || echo "❌ build failed"

# U9: 集成回归
python3 tests/integration_test.py --host 127.0.0.1 --port 8080 | tail -3
# 期望: 17/17 passed

# U10: 前端 HTML lang 属性
curl -s http://127.0.0.1:8080/ | grep -q 'lang="zh-CN"' && echo "✅ html lang=zh-CN" || echo "❌ lang attribute missing"

通过标准

  • U1: i18next + react-i18next 已安装
  • U2: zh-CN.json + en-US.json 翻译文件键数 > 100 且一致
  • U3: src/i18n/index.ts 配置文件就绪
  • U4: Sidebar 42+ 菜单项使用 labelKey 翻译键
  • U5: Header 右上角有语言切换器(Languages 图标)
  • U6: Login 页面使用 useTranslation + t() 函数
  • U7: tsc -b 零错误
  • U8: npm run build 成功

P1-F5: 链路追踪后端对接

验证用例

TOKEN=$(python3 -c "
import urllib.request, json
login_data = json.dumps({'username': 'admin', 'password': '123456'}).encode()
req = urllib.request.Request('http://127.0.0.1:8080/api/v1/auth/login', data=login_data, method='POST')
req.add_header('Content-Type', 'application/json')
resp = urllib.request.urlopen(req)
print(json.loads(resp.read())['data']['token'])
")

# U1: 数据源列表(初始为空)
curl -s "http://127.0.0.1:8080/api/v1/trace-datasources" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); assert d['code']==200; print(f'✅ list: {len(d[\"data\"])} datasources')"

# U2: Demo 模式 GetTraces(无数据源时返回模拟数据)
curl -s "http://127.0.0.1:8080/api/v1/traces?timeRange=1h" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin)['data']; assert d['total']>0 and d.get('demo')==True; print(f'✅ demo traces: {d[\"total\"]} items')"

# U3: Demo 模式 GetServices
curl -s "http://127.0.0.1:8080/api/v1/traces/services?timeRange=1h" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin)['data']; assert d['total']>0 and d.get('demo')==True; print(f'✅ demo services: {d[\"total\"]} items')"

# U4: Demo 模式 GetTraceDetail
curl -s "http://127.0.0.1:8080/api/v1/traces/a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin)['data']; assert len(d['spans'])>0; print(f'✅ demo spans: {len(d[\"spans\"])} items')"

# U5: 创建数据源
curl -s -X POST "http://127.0.0.1:8080/api/v1/trace-datasources" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"name":"test-jaeger","type":"jaeger","url":"http://10.10.10.75:16686","is_default":true}' \
  | python3 -c "import json,sys; d=json.load(sys.stdin); assert d['code']==200; print(f'✅ create datasource id={d[\"data\"][\"id\"]}')"

# U6: 列表有数据
curl -s "http://127.0.0.1:8080/api/v1/trace-datasources" \
  -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); assert len(d['data'])==1; print(f'✅ list: {d[\"data\"][0][\"name\"]}')"

# U7: go build
cd /srv/aiops/backend && go build ./...

# U8: npm build
cd /srv/aiops/frontend && npm run build

通过标准

  • U1: 数据源列表端点返回 200
  • U2: 无数据源时 Demo 模式返回模拟 Trace 数据(6 条)
  • U3: 无数据源时 Demo 模式返回模拟服务指标(5 个)
  • U4: 无数据源时 Demo 模式返回模拟 Span 数据(5 个)
  • U5: 创建数据源返回 200
  • U6: 列表包含创建的数据源
  • U7: go build 零错误
  • U8: npm run build 零错误
  • 前端 traceApi 不再是 mock 桩(真实 HTTP 调用)
  • 17/17 集成回归测试通过

P1-F6: 日志告警引擎

验证用例

TOKEN=$(python3 -c "
import urllib.request, json
login_data = json.dumps({'username': 'admin', 'password': '123456'}).encode()
req = urllib.request.Request('http://127.0.0.1:8080/api/v1/auth/login', data=login_data, method='POST')
req.add_header('Content-Type', 'application/json')
resp = urllib.request.urlopen(req)
print(json.loads(resp.read())['data']['token'])
")

# U1: 引擎状态正常(LogDSHandler 注入后分布式锁仍工作)
curl -s http://127.0.0.1:8080/api/v1/alerts/engine/status \
  -H "Authorization: Bearer *** | python3 -c "import json,sys; d=json.load(sys.stdin)['data']; assert d['is_leader']==True and d['distributed']==True; print('✅ engine distributed+leader')"

# U2: 创建日志告警规则(type=log, datasource_id 绑定)
curl -s -X POST "http://127.0.0.1:8080/api/v1/alerts/rules" \
  -H "Authorization: Bearer *** -H "Content-Type: application/json" \
  -d '{"name":"F6-test","type":"log","source":"loki","expression":"{app=\"nginx\"} |= \"ERROR\"","severity":"critical","duration":60,"datasource_id":1}' \
  | python3 -c "import json,sys; d=json.load(sys.stdin); assert d['code']==200; print(f'✅ create log rule id={d[\"data\"][\"id\"]}')"

# U3: 数据源认证字段(auth_type/token/headers)
docker exec aiops-mysql mysql -uroot -paiops_root_2026 aiops -e "SHOW COLUMNS FROM monitor_log_datasources;" 2>/dev/null | grep -E 'auth_type|token|headers'

# U4: go build
cd /srv/aiops/backend && go build ./...

# U5: npm build
cd /srv/aiops/frontend && npm run build

# U6: 集成回归
python3 tests/integration_test.py --host 127.0.0.1 --port 8080 | tail -3

通过标准

  • U1: 引擎状态 distributed=true, is_leader=true(LogDSHandler 注入不破坏锁机制)
  • U2: 创建 type="log" 告警规则返回 200,含 datasource_id + expression
  • U3: DB 表 monitor_log_datasources 含 auth_type/token/headers 三列
  • U4: go build 零错误
  • U5: npm run build 零错误(✓ built in 18.57s)
  • U6: 17/17 集成回归测试通过
  • 引擎无 panic(无效 datasource_id 优雅跳过)
  • 日志数据源支持 none/basic/bearer/custom 四种认证方式(round-trip 验证)

M15: 中间件监控 (Redis/MySQL)

验证用例

TOKEN=$(python3 -c "
import urllib.request, json
login_data = json.dumps({'username': 'admin', 'password': '123456'}).encode()
req = urllib.request.Request('http://127.0.0.1:8080/api/v1/auth/login', data=login_data, method='POST')
req.add_header('Content-Type', 'application/json')
resp = urllib.request.urlopen(req)
print(json.loads(resp.read())['data']['token'])
")

# U1: 创建 Redis 监控目标
curl -s -X POST http://127.0.0.1:8080/api/v1/middleware-monitors \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"name":"平台Redis","type":"redis","ip":"aiops-redis","port":6379}' | python3 -c "import json,sys; d=json.load(sys.stdin); assert d['code']==200; print('✅ create redis')"
# 期望: code=200

# U2: 创建 MySQL 监控目标
curl -s -X POST http://127.0.0.1:8080/api/v1/middleware-monitors \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"name":"平台MySQL","type":"mysql","ip":"aiops-mysql","port":3306,"username":"root","password":"xxx","database":"aiops"}' | python3 -c "import json,sys; d=json.load(sys.stdin); assert d['code']==200; print('✅ create mysql')"
# 期望: code=200

# U3: 列表
curl -s http://127.0.0.1:8080/api/v1/middleware-monitors -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); assert d['data']['total']>=2; print(f'✅ list: {d[\"data\"][\"total\"]} targets')"
# 期望: total >= 2

# U4: 测试 Redis 连接(采集指标)
curl -s -X POST http://127.0.0.1:8080/api/v1/middleware-monitors/REDIS_ID/test -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); assert d['data']['success']==True; print(f'✅ redis test: {len(d[\"data\"][\"metrics\"])} metrics')"
# 期望: success=true, 12 metrics

# U5: 立即采集
curl -s -X POST http://127.0.0.1:8080/api/v1/middleware-monitors/collect -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); assert d['data']['collected']>=1; print(f'✅ collect: {d[\"data\"][\"collected\"]} targets')"
# 期望: collected >= 1

# U6: 指标历史
curl -s "http://127.0.0.1:8080/api/v1/middleware-monitors/REDIS_ID/metrics" -H "Authorization: Bearer $TOKEN" | python3 -c "import json,sys; d=json.load(sys.stdin); assert d['data']['total']>0; print(f'✅ history: {d[\"data\"][\"total\"]} records')"
# 期望: records > 0

# U7: 前端 middlewareMonitorApi 8 个方法
grep -c 'http\.\(get\|post\|put\|delete\)' /srv/aiops/frontend/src/api/monitor/metrics.ts
# 期望: >= 8

# U8: go build
cd /srv/aiops/backend && go build ./...
# 期望: 零错误

# U9: npm build
cd /srv/aiops/frontend && npm run build
# 期望: 零错误

通过标准

  • U1: 创建 Redis 监控目标返回 200
  • U2: 创建 MySQL 监控目标返回 200
  • U3: 列表返回 >= 2 个目标
  • U4: Redis 连接测试成功(12 项指标:连接数/内存/命中率/QPS/Key总数等)
  • U5: 立即采集成功(collected >= 1)
  • U6: 指标历史有记录
  • U7: 前端 API 屄 8 个方法
  • U8: go build 零错误
  • U9: npm run build 零错误(✓ built in 19.08s)
  • 17/17 集成回归测试通过(零回归)

P3-U5: Kubernetes 模块国际化

验证用例

# U1: k8s 命名空间存在且 zh/en 对齐
cd /srv/aiops/frontend && python3 -c "
import json
zh = json.load(open('src/locales/zh-CN.json', encoding='utf-8'))
en = json.load(open('src/locales/en-US.json', encoding='utf-8'))
assert 'k8s' in zh and 'k8s' in en, 'k8s namespace missing'
def count(d):
    n = 0
    for v in d.values():
        n += count(v) if isinstance(v, dict) else 1
    return n
assert count(zh['k8s']) == count(en['k8s']), 'zh/en k8s mismatch'
print(f'k8s namespace: {count(zh[\"k8s\"])} keys (zh==en: True)')
"
# 期望: k8s namespace: 184 keys

# U2: TypeScript 编译零错误
npx tsc --noEmit 2>&1 | tail -3
# 期望: 退出码 0

# U3: Vite 构建
npm run build 2>&1 | tail -3
# 期望: built in Ns

# U4: KubernetesClusterDetail 无 JSX 硬编码中文
grep -v '^\s*//' src/components/kubernetes/KubernetesClusterDetail.tsx | grep -cP '[\x{4e00}-\x{9fff}]'
# 期望: 0 (仅注释中有 CJK)

# U5: TopologyView 无 JSX 硬编码中文
grep -v '^\s*//' src/components/kubernetes/TopologyView.tsx | grep -cP '[\x{4e00}-\x{9fff}]'
# 期望: 0 (仅注释中有 CJK)

# U6: 集成回归
python3 ../tests/integration_test.py --host 127.0.0.1 --port 8080
# 期望: 17/17 passed

通过标准

  • U1: k8s 命名空间存在,zh/en 完全对齐(184 键)
  • U2: tsc --noEmit 零错误
  • U3: npm run build 零错误(✓ built in 17.81s)
  • U4: KubernetesClusterDetail 非注释 CJK = 0
  • U5: TopologyView 非注释 CJK = 0
  • U6: 17/17 集成回归测试通过

P3-U13: 主机监控大屏挂载(HostMonitorDashboard 死代码复活)

验证用例

# U1: 路由配置存在
grep -c 'host-dashboard' /srv/aiops/frontend/src/App.tsx
# 期望: >= 2 (lazy import + Route)

# U2: Sidebar 菜单项存在
grep -c 'host-dashboard' /srv/aiops/frontend/src/components/layout/Sidebar.tsx
# 期望: >= 1

# U3: Breadcrumb 映射存在
grep -c 'host-dashboard' /srv/aiops/frontend/src/components/layout/Breadcrumb.tsx
# 期望: >= 1

# U4: i18n 翻译键存在且对齐
python3 -c "
import json
zh = json.load(open('/srv/aiops/frontend/src/locales/zh-CN.json', encoding='utf-8'))
en = json.load(open('/srv/aiops/frontend/src/locales/en-US.json', encoding='utf-8'))
assert zh['menu']['hostDashboard'] == '主机大屏'
assert en['menu']['hostDashboard'] == 'Host Dashboard'
assert zh['breadcrumb']['hostDashboard'] == '主机大屏'
assert en['breadcrumb']['hostDashboard'] == 'Host Dashboard'
print('✅ i18n keys aligned')
"

# U5: TypeScript 编译零错误
cd /srv/aiops/frontend && npx tsc -b 2>&1 | tail -3
# 期望: 退出码 0

# U6: Vite 构建
npm run build 2>&1 | tail -3
# 期望: built in Ns

# U7: ESLint 无新增 errors
npx eslint src/ --format json 2>/dev/null | python3 -c "
import json, sys
data = json.load(sys.stdin)
errors = sum(1 for r in data for m in r.get('messages', []) if m.get('severity') == 2)
assert errors == 0, f'{errors} errors'
print('✅ ESLint errors=0')
"

# U8: 集成回归测试
python3 /srv/aiops/tests/integration_test.py --host 127.0.0.1 --port 8080 | tail -3
# 期望: 17/17 passed

通过标准

  • U1: App.tsx 路由配置存在(lazy import + Route path="host-dashboard")
  • U2: Sidebar 监控菜单含「主机大屏」项
  • U3: Breadcrumb 路由映射存在
  • U4: zh/en 翻译键对齐(menu.hostDashboard + breadcrumb.hostDashboard)
  • U5: tsc -b 零错误
  • U6: npm run build 零错误(✓ built in 17.50s)
  • U7: ESLint errors=0(无新增)
  • U8: 17/17 集成回归测试通过(零回归)

P0-Q8: 前端巨型组件拆分 — SystemSettingsTab

验证用例

# U1: 主文件行数
wc -l /srv/aiops/frontend/src/components/system/SystemSettingsTab.tsx
# 期望: < 200 行

# U2: 子 Tab 文件存在
ls -la /srv/aiops/frontend/src/components/system/system-settings/
# 期望: GeneralSettingsTab.tsx, SecuritySettingsTab.tsx, LdapSettingsTab.tsx

# U3: TypeScript 编译零错误
cd /srv/aiops/frontend && npx tsc -b 2>&1 | grep 'error TS' | wc -l
# 期望: 0

# U4: Vite 构建
npm run build 2>&1 | tail -1
# 期望: built in Ns

# U5: ESLint 无新增 errors
npx eslint src/components/system/SystemSettingsTab.tsx src/components/system/system-settings/ --format json 2>/dev/null | python3 -c "
import json, sys
data = json.load(sys.stdin)
errors = sum(1 for r in data for m in r.get('messages', []) if m.get('severity') == 2)
assert errors == 0, f'{errors} errors'
print('✅ ESLint errors=0')
"

# U6: go build
cd /srv/aiops/backend && go build ./...
# 期望: 零错误

# U7: 集成回归
python3 /srv/aiops/tests/integration_test.py --host 127.0.0.1 --port 8080 | tail -3
# 期望: 17/17 passed

通过标准

  • U1: SystemSettingsTab.tsx 从 754 行降至 173 行(< 200 ✅)
  • U2: 3 个子 Tab 文件全部存在(GeneralSettingsTab/SecuritySettingsTab/LdapSettingsTab)
  • U3: tsc -b 零错误
  • U4: npm run build 零错误(✓ built in 17.71s)
  • U5: ESLint errors 保持 0(无新增)
  • U6: go build 零错误
  • U7: 17/17 集成回归测试通过(零回归)

P0-Q10: 前端巨型组件拆分 — ApprovalWorkflowPage

验证用例

# U1: 主文件行数
wc -l /srv/aiops/frontend/src/pages/approval/ApprovalWorkflowPage.tsx
# 期望: < 200 行

# U2: 提取文件存在且行数合理
wc -l /srv/aiops/frontend/src/pages/approval/approval-workflow/*.ts /srv/aiops/frontend/src/pages/approval/approval-workflow/*.tsx
# 期望: 4 个文件,每个 < 300 行

# U3: TypeScript 编译
cd /srv/aiops/frontend && npx tsc -b
# 期望: 零错误

# U4: Vite 构建
npm run build 2>&1 | tail -3
# 期望: built in Ns

# U5: ESLint
npx eslint src/pages/approval/ --format compact
# 期望: 0 errors, 0 warnings

# U6: go build
cd /srv/aiops/backend && go build ./...
# 期望: 零错误

# U7: 集成回归
python3 /srv/aiops/tests/integration_test.py --host 127.0.0.1 --port 8080 | tail -3
# 期望: 17/17 passed

通过标准

  • U1: ApprovalWorkflowPage.tsx 从 705 行降至 167 行(< 200 ✅,-76%)
  • U2: 4 个提取文件全部 < 275 行(types 93L + UserSelectInline 96L + WorkflowEditorModal 272L + WorkflowCard 134L)
  • U3: tsc -b 零错误
  • U4: npm run build 零错误(✓ built in 16.99s)
  • U5: ESLint errors 保持 0(approval-workflow/ 目录零 warnings)
  • U6: go build 零错误
  • U7: 17/17 集成回归测试通过(零回归)

P0-Q11: 前端巨型组件拆分 — PipelineRunDetail

通过标准

  • PipelineRunDetail.tsx 从 678 行降至 273 行(-60%)
  • 4 个提取的文件全部 < 185 行(types.ts 119L / StageProgressBar.tsx 89L / RunsTable.tsx 131L / LogConsole.tsx 183L)
  • tsc -b 零错误
  • npm run build 零错误(✓ built in 17.39s)
  • ESLint errors 保持 0(清理 1 个 unused var stageStats + 2 个冗余 eslint-disable 指令)
  • ESLint warnings 保持 59(无新增)
  • go build 零错误
  • 17/17 集成回归测试通过(零回归)
  • Docker 部署 healthy
  • 流水线运行详情页功能不变(阶段进度条点击过滤 / 执行记录表多选+停止 / 实时日志 Console / 文件浏览器)

P0-Q12: 前端巨型组件拆分 — CloudServerImport

通过标准

  • CloudServerImport.tsx 从 699 行降至 404 行(-42%)
  • 6 个提取的文件全部 < 180 行(types.ts 57L / StepIndicator.tsx 34L / ProviderSelectStep.tsx 42L / AccountSelectStep.tsx 65L / RegionSelectStep.tsx 131L / DiscoverImportStep.tsx 176L)
  • tsc -b 零错误
  • npm run build 零错误(✓ built in 16.56s)
  • ESLint errors 保持 0(拆分文件零 warnings)
  • ESLint warnings 保持 64(无新增)
  • 全局 ESLint any 保持 0(移除文件级 eslint-disable @typescript-eslint/no-explicit-any,any[] 状态→具体接口)
  • go build 零错误
  • 17/17 集成回归测试通过(零回归)
  • Docker 部署 healthy,前端页面正常加载(HTTP 200)
  • 云服务器导入向导功能不变(4 步骤切换 / 厂商选择 / 账号选择 / 区域选择搜索+全选 / 主机发现+导入)

P0-Q13: 前端巨型组件拆分 — UnifiedQueryPanel

验证用例

# U1: 主文件行数
wc -l /srv/aiops/frontend/src/components/monitor/log-center/UnifiedQueryPanel.tsx
# 期望: < 600 行

# U2: 提取文件存在且行数合理
wc -l /srv/aiops/frontend/src/components/monitor/log-center/unified-query/*.ts /srv/aiops/frontend/src/components/monitor/log-center/unified-query/*.tsx
# 期望: 7 个文件,每个 < 120 行

# U3: TypeScript 编译
cd /srv/aiops/frontend && npx tsc -b
# 期望: 零错误

# U4: Vite 构建
npm run build 2>&1 | tail -3
# 期望: built in Ns

# U5: ESLint
npx eslint src/components/monitor/log-center/UnifiedQueryPanel.tsx src/components/monitor/log-center/unified-query/ --format compact
# 期望: 0 errors, 0 warnings

# U6: go build
cd /srv/aiops/backend && go build ./...
# 期望: 零错误

# U7: 集成回归
python3 /srv/aiops/tests/integration_test.py --host 127.0.0.1 --port 8080 | tail -3
# 期望: 17/17 passed

通过标准

  • UnifiedQueryPanel.tsx 从 682 行降至 529 行(< 600 ✅,-22%)
  • 7 个提取文件全部 < 115 行(constants 23L + entry-mappers 44L + LogVolumeChart 38L + QueryControlRow 68L + QueryEmptyStates 62L + ResultsTabs 103L + SecondaryToolbar 112L)
  • tsc -b 零错误
  • npm run build 零错误(✓ built in 17.57s)
  • ESLint errors 保持 0(UnifiedQueryPanel + unified-query/ 目录零 warnings)
  • ESLint warnings 保持 64(无新增)
  • 全局 ESLint any 保持 0
  • go build 零错误
  • 17/17 集成回归测试通过(零回归)
  • Docker 部署 healthy,前端页面正常加载(HTTP 200)
  • 日志中心查询面板功能不变(Loki/ES 双引擎查询/直方图/标签过滤/Live Tail/分享链接/保存查询/告警创建全部正常)

Q14: 前端巨型组件拆分 — ServerManager

验证用例

# U1: Go 编译
cd /srv/aiops/backend && go build ./...
# 期望: 零错误

# U2: TypeScript 编译
cd /srv/aiops/frontend && npx tsc -b
# 期望: 零错误

# U3: Vite 构建
npm run build 2>&1 | tail -3
# 期望: built in Ns

# U4: 拆分后文件行数
wc -l src/components/server/ServerManager.tsx src/components/server/ServerTableSection.tsx src/components/server/server-export-utils.ts
# 期望: ServerManager < 500L, 其余 < 270L

# U5: ESLint 全局指标
npx eslint src/ --format json | python3 -c "
import json, sys
d = json.load(sys.stdin)
print(f'errors={sum(1 for r in d for m in r[\"messages\"] if m[\"severity\"]==2)}')
print(f'any={sum(1 for r in d for m in r[\"messages\"] if m.get(\"ruleId\")==\"@typescript-eslint/no-explicit-any\")}')
"
# 期望: errors=0, any=0

# U6: 集成测试
python3 tests/integration_test.py --host 127.0.0.1 --port 8080
# 期望: 17/17 passed

通过标准

  • ServerManager.tsx 从 652 行降至 442 行(< 500 ✅,-32%)
  • 2 个提取文件全部 < 270 行(server-export-utils 57L + ServerTableSection 265L)
  • tsc -b 零错误
  • npm run build 零错误(✓ built in 16.40s)
  • ESLint errors 保持 0(3 个文件零 errors/warnings)
  • ESLint warnings 保持 64(无新增)
  • 全局 ESLint any 保持 0
  • go build 零错误
  • 17/17 集成回归测试通过(零回归)
  • Docker 部署 healthy,前端页面正常加载(HTTP 200)
  • 服务器管理页功能不变(列表/搜索/筛选/分页/选择/批量删除/CSV 导出/创建编辑/终端/RDP/文件浏览全部正常)

Q21: 前端巨型组件拆分 — DeploymentEditAdvanced

验证用例

# U1: Go 编译
cd /srv/aiops/backend && go build ./...
# 期望: 零错误

# U2: TypeScript 编译
cd /srv/aiops/frontend && npx tsc -b
# 期望: 零错误

# U3: Vite 构建
npm run build 2>&1 | tail -3
# 期望: built in Ns

# U4: 拆分后文件行数
wc -l src/components/kubernetes/DeploymentEditAdvanced.tsx src/components/kubernetes/deployment-edit-advanced/types.ts src/components/kubernetes/deployment-edit-advanced/UpdateStrategyCard.tsx src/components/kubernetes/deployment-edit-advanced/TolerationsCard.tsx src/components/kubernetes/deployment-edit-advanced/PodMiscCard.tsx src/components/kubernetes/deployment-edit-advanced/PodSecurityCard.tsx src/components/kubernetes/deployment-edit-advanced/HostSettingsCard.tsx
# 期望: DeploymentEditAdvanced < 300L, Card 组件 < 100L, types < 280L

# U5: ESLint 全局指标
npx eslint src/ --format json | python3 -c "
import json, sys
d = json.load(sys.stdin)
print(f'errors={sum(1 for r in d for m in r[\"messages\"] if m[\"severity\"]==2)}')
print(f'any={sum(1 for r in d for m in r[\"messages\"] if m.get(\"ruleId\")==\"@typescript-eslint/no-explicit-any\")}')
"
# 期望: errors=0, any=0

# U6: 集成测试
python3 tests/integration_test.py --host 127.0.0.1 --port 8080
# 期望: 17/17 passed

通过标准

  • DeploymentEditAdvanced.tsx 从 590 行降至 289 行(< 300 ✅,-51%)
  • 6 个提取文件全部 < 280 行(types.ts 279L 含纯函数,5 个 Card 组件全部 < 91 行)
  • tsc -b 零错误
  • npm run build 零错误(✓ built in 17.36s)
  • ESLint errors 保持 0(DeploymentEditAdvanced + deployment-edit-advanced/ 目录零 errors/warnings)
  • ESLint warnings 保持 65(无新增)
  • 全局 ESLint any 保持 0
  • go build 零错误
  • 17/17 集成回归测试通过(零回归)
  • Docker 部署 healthy,前端页面正常加载(HTTP 200)
  • Deployment 高级设置 Tab 功能不变(更新策略/历史版本/容忍/调度/Pod 杂项/安全上下文/主机设置/DNS+hostAliases 全部正常)
  • manifest 解析和序列化提取为纯函数(parseAdvancedManifest + buildAdvancedManifest)

Q18: 前端巨型组件拆分 — TraceManager

验证用例

# U1: Go 编译
cd /srv/aiops/backend && go build ./...
# 期望: 零错误

# U2: TypeScript 编译
cd /srv/aiops/frontend && npx tsc -b
# 期望: 零错误

# U3: Vite 构建
npm run build 2>&1 | tail -3
# 期望: built in Ns

# U4: 拆分后文件行数
wc -l src/components/monitor/trace/TraceManager.tsx src/components/monitor/trace/trace-manager/*.ts src/components/monitor/trace/trace-manager/*.tsx
# 期望: TraceManager < 340L, 其余 < 95L

# U5: ESLint 全局指标
npx eslint src/ --format json | python3 -c "
import json, sys
d = json.load(sys.stdin)
print(f'errors={sum(1 for r in d for m in r[\"messages\"] if m[\"severity\"]==2)}')
print(f'any={sum(1 for r in d for m in r[\"messages\"] if m.get(\"ruleId\")==\"@typescript-eslint/no-explicit-any\")}')
"
# 期望: errors=0, any=0

# U6: 集成测试
python3 tests/integration_test.py --host 127.0.0.1 --port 8080
# 期望: 17/17 passed

# U7: traces API 可用(链路追踪页面后端代理)
TOKEN=$(curl -s http://127.0.0.1:8080/api/v1/auth/login -X POST -H 'Content-Type: application/json' -d '{"username":"admin","password":"123456"}' | python3 -c 'import sys,json;print(json.load(sys.stdin)["data"]["token"])')
curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer ${TOKEN}" "http://127.0.0.1:8080/api/v1/traces?timeRange=1h"
# 期望: 200

通过标准

  • TraceManager.tsx 从 605 行降至 331 行(< 340 ✅,-45%)
  • 5 个提取文件全部 < 95 行(types 90L + trace-helpers 44L + SpanTreeNode 71L + TraceDetailDialog 80L + SpanDetailDialog 91L)
  • tsc -b 零错误
  • npm run build 零错误(✓ built in 18.23s)
  • ESLint errors 保持 0(trace-manager/ 目录零 warnings)
  • ESLint warnings 保持 64(无新增)
  • 全局 ESLint any 保持 0
  • go build 零错误
  • 17/17 集成回归测试通过(零回归)
  • Docker 部署 healthy,前端页面正常加载(HTTP 200),traces API 返回 200
  • 链路追踪管理页功能不变(Traces/Services/Metrics 三 Tab + Trace 详情 Span 树 + Span 详情标签/日志全部正常)
  • SpanTreeNode 内联定义反模式已修复(提取为模块级稳定组件引用)

Q26: 前端巨型组件拆分 — PodLogsViewer [done]

通过标准

  • PodLogsViewer.tsx 从 553 行降至 331 行(-40%)
  • 5 个提取的文件全部 < 180 行(types.tsx 57L, LogsHeader 66L, ControlBar 178L, LogContentArea 64L, BottomStatusBar 42L)
  • tsc -b 零错误
  • npm run build 零错误(✓ built in 17.34s)
  • ESLint errors 保持 0(PodLogsViewer + pod-logs-viewer/ 目录零 errors/warnings)
  • ESLint warnings 保持 65(无新增)
  • 全局 ESLint any 保持 0
  • go build 零错误
  • 17/17 集成回归测试通过(零回归)
  • Docker 部署 healthy,前端页面正常加载(HTTP 200)
  • Pod 日志查看器功能不变(容器选择/行数限制/previous/sinceTime/关键字过滤+高亮/下载/刷新/实时流/暂停继续/全屏切换全部正常)

U20: ESLint static-components 清零 + CredDetailDialog 内联组件修复 + i18n 损坏修复 [done]

通过标准

  • react-hooks/static-components 警告从 23 降至 0(CredDetailDialog.tsx Field 内联组件提取为模块级 CredField)
  • ESLint warnings 从 66 降至 43(-35%,全部剩余为 react-refresh/only-export-components 架构性约束 + 1 个 @tanstack/react-virtual 兼容性)
  • ESLint errors 保持 0
  • 修复 8 处被批量正则损坏的 i18n.t() 调用(i18n.t('>名称:</span>') 等 → 语义化键如 common.name、common.createdAt)
  • 6 处硬编码中文清零(凭据详情、密钥数据、无、创建时间、更新时间、安全提示)
  • 新增 deploy.cred.sensitiveWarn i18n 键(zh/en 对齐)
  • tsc -b 零错误
  • npm run build 零错误(✓ built in 17.29s)
  • go build 零错误
  • 17/17 集成回归测试通过(零回归)
  • Docker 部署 healthy

U46: i18n 残余 UI 硬编码中文清零 — 深度边角

通过标准

  • 6 个修复文件中无残余硬编码中文(UserFormDialog/CredentialManager/RdpAuditViewer/LiveMonitorDialog/ServerFileBrowser/context-loaders)
  • context-loaders.ts 全部模板字符串使用 i18n.t() 插值
  • tsc -b 零错误
  • npm run build 零错误(✓ built in 18.05s)
  • ESLint errors=0, any=0
  • zh/en 8102=8102 完全对齐(+17 新键)
  • go build 零错误
  • 17/17 集成回归测试通过(零回归)
  • Docker 部署 healthy