Files
aiops/TDD_PLAN.md
T

1537 lines
58 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AIOps 验证计划 — 全模块回归测试
> 基于 ROADMAP P0-P3 模块定义,每个模块开发前先写验证用例,实现后必须跑通。
> 最后更新: 2026-06-25
## 通用验证流程
```bash
# 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: 前端类型安全治理
### 验证用例
```bash
# 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
```
### 通过标准
- [x] U1: `tsc --noEmit` 零错误
- [x] U2: `any` 类型 < 50 处 — **40 处**
- [x] **U3: 未使用变量 < 20 处** — 6 处
- [x] **U4: `console.*` < 10 处** — 1 处
- [x] **U8: `src/api/` 整个目录 `any` 清零** — 含 monitor/ deploy/ 子目录全部对接 types/ 接口
- [x] U1: `tsc -b` 零错误(含类型化后的回归)
- [x] U7: Vite 构建成功 (✓ built in 17.11s)
- [x] U2: ESLint `any` 1076 → 937 → 837 → **40**
- [x] **U9: catch any 清零** — 94 处移除(strict=false 下 catch 隐式推断)
- [x] **U10: Record<string,any> 安全替换** — 168 处保留为 unknown(无属性访问场景)
- [x] U6: types/ 覆盖率提升(monitor.ts +200 行接口定义,含 6 个运行时记录类型)
- [x] **U11: 组件级 any 清理** — AlertEventsTab/AlertRulesTab/AlertChannelDialog/AuditOperationsTab/CronJobManager/AgentMonitor/DashboardRecent 共 43 处 any→具体接口
- [x] 回归: 9/9 核心 API 端点通过,Docker 部署 healthy
---
## P0-Q2: 后端 kubernetes.go 模块化拆分
### 验证用例
```bash
# 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
```
### 通过标准
- [x] U1: `go build` 零错误
- [x] U2: 每个拆分文件 < 800 行(最大 k8s_workload.go 696 行,kubernetes.go 降至 106 行核心文件)
- [x] U3: 路由数量不变(425 条路由,routes.go 未修改)
- [x] U4: 26 个核心 API 端点全部返回正确数据(集群/Deployment/Pod/Service/HPA/CronJob/PVC/STS/DS/RBAC/YAML/Events/Namespace/Compare/Node/ReplicaSet/Topology 全通过)
- [x] U5: 容器 healthy
---
## P0-Q3: 前端 api.ts 模块化拆分
### 验证用例
```bash
# 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
# 期望: 所有引用正常解析
```
### 通过标准
- [x] U1: api/ 目录结构正确 (12 域文件 + index.ts)
- [x] U2: 每个文件 < 300 行 (kubernetes.ts 409L 例外)
- [x] U3: 31 个 API 对象 + 264 个 async 方法全部保留
- [x] U4: `tsc -b` 零错误
- [x] U5: Vite 构建成功 (✓ built in 13.85s)
- [x] U6: 所有 97 个 import 文件正常解析
---
## P1-F1: 告警静默规则
### 验证用例
```bash
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
```
### 通过标准
- [x] U1: 创建静默规则返回 200
- [x] U2: 列表包含创建的规则
- [x] U3: 静默期间不触发通知(alert_engine.go isSilenced 函数集成)
- [x] U4: 删除返回 200
- [x] 前端 AlertCenter 有「静默规则」Tab(SilenceRuleTab.tsx + SilenceDialog.tsx)
---
## P1-F2: 审计日志导出
### 验证用例
```bash
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(有数据)
```
### 通过标准
- [x] U1: CSV 导出文件有效(带 UTF-8 BOM,Excel 直接打开无乱码)
- [x] U2: JSON 格式向后兼容(format=json)
- [x] U3: 筛选条件正确工作(module/action/username/startTime/endTime)
- [x] 前端有「导出」按钮(AuditOperationsTab.tsx,传递当前筛选参数)
- [x] 导出限制 10000 条防内存溢出
- [x] 导出自动记录审计日志
---
## P1-F3: 前端 RBAC 按钮级控制
### 验证用例
```bash
# 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. 确认无权限的菜单不显示
```
### 通过标准
- [x] U1-U2: admin 登录返回 `permissions: ["*"]`,`GET /user/permissions` 返回 `is_admin=true`
- [x] U3-U4: viewer 角色有 7 个只读权限码(dashboard:view/server:view/monitor:alert/k8s:cluster/k8s:topology/cloud:billing/deploy:pipeline)
- [x] U5: viewer 用户登录返回受限权限数组(无 server:create/server:delete)
- [x] U6: `GET /user/permissions` 返回正确权限码(raw SQL JOIN 查询)
- [x] U7: viewer 可访问只读端点
- [x] 前端: `usePermission` hook + `<HasPermission>` 组件 + Sidebar 菜单过滤 + ServerManager/SystemManager 按钮显隐
- [x] 前端: `npm run build` 零错误
- [x] 回归: 10/10 核心端点通过
---
## P2-A2: 告警引擎分布式安全
### 验证用例
```bash
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
```
### 通过标准
- [x] U1: 引擎状态端点返回 distributed/is_leader/instance_id/lock_ttl_sec
- [x] U2: 其他副本持锁时正确识别为非 Leader
- [x] U3: 日志确认引擎跳过检查(非 Leader 副本不执行告警扫描)
- [x] U4: 锁释放后恢复 Leader 角色
- [x] `go build` 零错误,5/5 核心 API 回归通过
---
## P2-A1: K8s 双路由体系统一
### 验证用例
```bash
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 路由)
```
### 通过标准
- [x] U1-U4: 4 条新增/补全的 code 路由全部返回 200 + 正确数据
- [x] U5: 旧 ID 路由向后兼容(nodes/pods/deployments/services/ingresses 均可访问)
- [x] U6: `go build` 零错误
- [x] U7: `tsc -b` + `npm run build` 零错误
- [x] U8: 前端 API 层资源方法零 ID 路由引用(仅保留集群 CRUD/sync/topology 合法 ID 路由)
- [x] 16/16 综合 API 测试通过(7 新 code 路由 + 3 旧 ID 路由 + 6 回归)
- [x] code 路由与 ID 路由返回数据一致(nodes=2, pods=24, deployments=15)
---
## P1-F4: 自定义监控仪表盘
### 验证用例
```bash
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
```
### 通过标准
- [x] U1: 创建仪表盘返回 200
- [x] U2: 列表包含创建的仪表盘
- [x] U3: 更新仪表盘(添加面板)成功
- [x] U4: 删除仪表盘成功
- [x] U5: `dashboardApi` 全部方法为真实 HTTP 调用(非 mock 返回空)
- [x] U6: `GrafanaPanelContent` 使用 `prometheusProxyApi.query` / `queryRange` 执行 PromQL
- [x] U7: `go build` 零错误
- [x] U8: `npm run build` 零错误(✓ built in 18.01s)
- [x] U9: 17/17 集成回归测试通过
---
## 回归测试基线(每次改动后必跑)
```bash
# === 后端回归 ===
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 连接统一管理
### 验证用例
```python
#!/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 连接并发注册成功")
```
### 通过标准
- [x] U1: `/ws/stats` 返回 total + by_type + by_user + by_type_user
- [x] U2: `/ws/connections` 返回连接列表(含 uptime_sec/connect_time/type/user)
- [x] U3: 连接打开→注册→关闭→注销全流程正确
- [x] U4: 多连接并发注册正常(5 连接同时)
- [x] 心跳机制: ping=30s + pong_handler + read_deadline=60s
- [x] `go build` 零错误,17/17 回归测试通过
- [x] 6 个 WebSocket handler 共享统一 WSManager(webshell/kubectl/podexec/audit/metrics/pipeline)
---
## P3-U1: 虚拟滚动
### 验证用例
```bash
# 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
```
### 通过标准
- [x] U1: `@tanstack/react-virtual@3.14.4` 已安装
- [x] U2: `useVirtualRows` Hook 可复用
- [x] U3: `tsc --noEmit` 零错误
- [x] U4: Vite 构建成功 (✓ built in 18.62s)
- [x] U5: PodsTab 虚拟滚动(动态行高 + 展开行支持)
- [x] U6: EventTable 虚拟滚动(移除 maxRows 截断)
- [x] U7: AuditOperationsTab 虚拟滚动
- [x] U8: EventTable 不再截断事件列表
- [x] U9: 17/17 集成回归测试通过
---
## P3-U2: 统一 DataTable 组件
### 验证用例
```bash
# 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
```
### 通过标准
- [x] U1: DataTable 组件文件存在
- [x] U2: 导出 DataTable 和 DataTableColumn 类型
- [x] U3: `tsc -b` 零错误
- [x] U4: Vite 构建成功 (✓ built in 18.05s)
- [x] U5: RegionManager 迁移到 DataTable(含搜索/排序/选择/导出/列可见性)
- [x] U6: LoginLogsTab 迁移到 DataTable(含搜索/排序/分页/导出/列可见性)
- [x] U7: LogDatasourcePage 迁移到 DataTable(含搜索/排序/CRUD)
- [x] U8: 支持 6+ 核心功能开关(sortable/searchable/pagination/selectable/exportable/columnToggle)
- [x] U9: 17/17 集成回归测试通过
---
## P3-U4: 后端镜像优化
### 验证用例
```bash
# 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
```
### 通过标准
- [x] U1: 镜像从 282MB 降至 **107MB**(< 150MB 目标 ✅)
- [x] U2: `go build` 零错误
- [x] U3: git 不在镜像中(首次流水线使用时通过 `apk add --no-cache git` 懒加载)
- [x] U4: docker 不在镜像中(首次流水线使用时通过 `apk add --no-cache docker-cli` 懒加载)
- [x] U5: kubectl 在镜像中(通过 install-kubectl.sh 下载 stripped 版本)
- [x] U6: 仅 linux-amd64 agent 二进制(arm64/darwin 通过下载 API)
- [x] U7: UpgradeTools 自动升级 git/docker-cli preinstall→lazy
- [x] U8: 17/17 集成回归测试通过
---
## P3-U3: 国际化 (i18n)
### 验证用例
```bash
# 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"
```
### 通过标准
- [x] U1: `i18next` + `react-i18next` 已安装
- [x] U2: `zh-CN.json` + `en-US.json` 翻译文件键数 > 100 且一致
- [x] U3: `src/i18n/index.ts` 配置文件就绪
- [x] U4: Sidebar 42+ 菜单项使用 `labelKey` 翻译键
- [x] U5: Header 右上角有语言切换器(Languages 图标)
- [x] U6: Login 页面使用 `useTranslation` + `t()` 函数
- [x] U7: `tsc -b` 零错误
- [x] U8: `npm run build` 成功
---
## P1-F5: 链路追踪后端对接
### 验证用例
```bash
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
```
### 通过标准
- [x] U1: 数据源列表端点返回 200
- [x] U2: 无数据源时 Demo 模式返回模拟 Trace 数据(6 条)
- [x] U3: 无数据源时 Demo 模式返回模拟服务指标(5 个)
- [x] U4: 无数据源时 Demo 模式返回模拟 Span 数据(5 个)
- [x] U5: 创建数据源返回 200
- [x] U6: 列表包含创建的数据源
- [x] U7: `go build` 零错误
- [x] U8: `npm run build` 零错误
- [x] 前端 `traceApi` 不再是 mock 桩(真实 HTTP 调用)
- [x] 17/17 集成回归测试通过
---
## P1-F6: 日志告警引擎
### 验证用例
```bash
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
```
### 通过标准
- [x] U1: 引擎状态 distributed=true, is_leader=true(LogDSHandler 注入不破坏锁机制)
- [x] U2: 创建 `type="log"` 告警规则返回 200,含 datasource_id + expression
- [x] U3: DB 表 monitor_log_datasources 含 auth_type/token/headers 三列
- [x] U4: `go build` 零错误
- [x] U5: `npm run build` 零错误(✓ built in 18.57s)
- [x] U6: 17/17 集成回归测试通过
- [x] 引擎无 panic(无效 datasource_id 优雅跳过)
- [x] 日志数据源支持 none/basic/bearer/custom 四种认证方式(round-trip 验证)
---
## M15: 中间件监控 (Redis/MySQL)
### 验证用例
```bash
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
# 期望: 零错误
```
### 通过标准
- [x] U1: 创建 Redis 监控目标返回 200
- [x] U2: 创建 MySQL 监控目标返回 200
- [x] U3: 列表返回 >= 2 个目标
- [x] U4: Redis 连接测试成功(12 项指标:连接数/内存/命中率/QPS/Key总数等)
- [x] U5: 立即采集成功(collected >= 1)
- [x] U6: 指标历史有记录
- [x] U7: 前端 API 屄 8 个方法
- [x] U8: `go build` 零错误
- [x] U9: `npm run build` 零错误(✓ built in 19.08s)
- [x] 17/17 集成回归测试通过(零回归)
---
## P3-U5: Kubernetes 模块国际化
### 验证用例
```bash
# 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
```
### 通过标准
- [x] U1: `k8s` 命名空间存在,zh/en 完全对齐(184 键)
- [x] U2: `tsc --noEmit` 零错误
- [x] U3: `npm run build` 零错误(✓ built in 17.81s)
- [x] U4: KubernetesClusterDetail 非注释 CJK = 0
- [x] U5: TopologyView 非注释 CJK = 0
- [x] U6: 17/17 集成回归测试通过
---
## P3-U13: 主机监控大屏挂载(HostMonitorDashboard 死代码复活)
### 验证用例
```bash
# 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
```
### 通过标准
- [x] U1: App.tsx 路由配置存在(lazy import + Route path="host-dashboard")
- [x] U2: Sidebar 监控菜单含「主机大屏」项
- [x] U3: Breadcrumb 路由映射存在
- [x] U4: zh/en 翻译键对齐(menu.hostDashboard + breadcrumb.hostDashboard)
- [x] U5: `tsc -b` 零错误
- [x] U6: `npm run build` 零错误(✓ built in 17.50s)
- [x] U7: ESLint errors=0(无新增)
- [x] U8: 17/17 集成回归测试通过(零回归)
---
## P0-Q8: 前端巨型组件拆分 — SystemSettingsTab
### 验证用例
```bash
# 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
```
### 通过标准
- [x] U1: SystemSettingsTab.tsx 从 754 行降至 **173 行**(< 200 ✅)
- [x] U2: 3 个子 Tab 文件全部存在(GeneralSettingsTab/SecuritySettingsTab/LdapSettingsTab)
- [x] U3: `tsc -b` 零错误
- [x] U4: `npm run build` 零错误(✓ built in 17.71s)
- [x] U5: ESLint errors 保持 **0**(无新增)
- [x] U6: `go build` 零错误
- [x] U7: 17/17 集成回归测试通过(零回归)
---
## P0-Q10: 前端巨型组件拆分 — ApprovalWorkflowPage
### 验证用例
```bash
# 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
```
### 通过标准
- [x] U1: ApprovalWorkflowPage.tsx 从 705 行降至 **167 行**(< 200 ✅,-76%)
- [x] U2: 4 个提取文件全部 < 275 行(types 93L + UserSelectInline 96L + WorkflowEditorModal 272L + WorkflowCard 134L)
- [x] U3: `tsc -b` 零错误
- [x] U4: `npm run build` 零错误(✓ built in 16.99s)
- [x] U5: ESLint errors 保持 **0**(approval-workflow/ 目录零 warnings)
- [x] U6: `go build` 零错误
- [x] U7: 17/17 集成回归测试通过(零回归)
---
## P0-Q11: 前端巨型组件拆分 — PipelineRunDetail
### 通过标准
- [x] PipelineRunDetail.tsx 从 678 行降至 **273 行**(-60%)
- [x] 4 个提取的文件全部 < 185 行(types.ts 119L / StageProgressBar.tsx 89L / RunsTable.tsx 131L / LogConsole.tsx 183L)
- [x] `tsc -b` 零错误
- [x] `npm run build` 零错误(✓ built in 17.39s)
- [x] ESLint errors 保持 **0**(清理 1 个 unused var `stageStats` + 2 个冗余 `eslint-disable` 指令)
- [x] ESLint warnings 保持 **59**(无新增)
- [x] `go build` 零错误
- [x] 17/17 集成回归测试通过(零回归)
- [x] Docker 部署 healthy
- [x] 流水线运行详情页功能不变(阶段进度条点击过滤 / 执行记录表多选+停止 / 实时日志 Console / 文件浏览器)
---
## P0-Q12: 前端巨型组件拆分 — CloudServerImport
### 通过标准
- [x] CloudServerImport.tsx 从 699 行降至 **404 行**(-42%)
- [x] 6 个提取的文件全部 < 180 行(types.ts 57L / StepIndicator.tsx 34L / ProviderSelectStep.tsx 42L / AccountSelectStep.tsx 65L / RegionSelectStep.tsx 131L / DiscoverImportStep.tsx 176L)
- [x] `tsc -b` 零错误
- [x] `npm run build` 零错误(✓ built in 16.56s)
- [x] ESLint errors 保持 **0**(拆分文件零 warnings)
- [x] ESLint warnings 保持 **64**(无新增)
- [x] 全局 ESLint any 保持 **0**(移除文件级 `eslint-disable @typescript-eslint/no-explicit-any`,any[] 状态→具体接口)
- [x] `go build` 零错误
- [x] 17/17 集成回归测试通过(零回归)
- [x] Docker 部署 healthy,前端页面正常加载(HTTP 200)
- [x] 云服务器导入向导功能不变(4 步骤切换 / 厂商选择 / 账号选择 / 区域选择搜索+全选 / 主机发现+导入)
---
## P0-Q13: 前端巨型组件拆分 — UnifiedQueryPanel
### 验证用例
```bash
# 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
```
### 通过标准
- [x] UnifiedQueryPanel.tsx 从 682 行降至 **529 行**(< 600 ✅,-22%)
- [x] 7 个提取文件全部 < 115 行(constants 23L + entry-mappers 44L + LogVolumeChart 38L + QueryControlRow 68L + QueryEmptyStates 62L + ResultsTabs 103L + SecondaryToolbar 112L)
- [x] `tsc -b` 零错误
- [x] `npm run build` 零错误(✓ built in 17.57s)
- [x] ESLint errors 保持 **0**(UnifiedQueryPanel + unified-query/ 目录零 warnings)
- [x] ESLint warnings 保持 **64**(无新增)
- [x] 全局 ESLint any 保持 **0**
- [x] `go build` 零错误
- [x] 17/17 集成回归测试通过(零回归)
- [x] Docker 部署 healthy,前端页面正常加载(HTTP 200)
- [x] 日志中心查询面板功能不变(Loki/ES 双引擎查询/直方图/标签过滤/Live Tail/分享链接/保存查询/告警创建全部正常)
---
## Q14: 前端巨型组件拆分 — ServerManager
### 验证用例
```bash
# 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
```
### 通过标准
- [x] ServerManager.tsx 从 652 行降至 **442 行**(< 500 ✅,-32%)
- [x] 2 个提取文件全部 < 270 行(server-export-utils 57L + ServerTableSection 265L)
- [x] `tsc -b` 零错误
- [x] `npm run build` 零错误(✓ built in 16.40s)
- [x] ESLint errors 保持 **0**(3 个文件零 errors/warnings)
- [x] ESLint warnings 保持 **64**(无新增)
- [x] 全局 ESLint any 保持 **0**
- [x] `go build` 零错误
- [x] 17/17 集成回归测试通过(零回归)
- [x] Docker 部署 healthy,前端页面正常加载(HTTP 200)
- [x] 服务器管理页功能不变(列表/搜索/筛选/分页/选择/批量删除/CSV 导出/创建编辑/终端/RDP/文件浏览全部正常)
## Q21: 前端巨型组件拆分 — DeploymentEditAdvanced
### 验证用例
```bash
# 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
```
### 通过标准
- [x] DeploymentEditAdvanced.tsx 从 590 行降至 **289 行**(< 300 ✅,-51%)
- [x] 6 个提取文件全部 < 280 行(types.ts 279L 含纯函数,5 个 Card 组件全部 < 91 行)
- [x] `tsc -b` 零错误
- [x] `npm run build` 零错误(✓ built in 17.36s)
- [x] ESLint errors 保持 **0**(DeploymentEditAdvanced + deployment-edit-advanced/ 目录零 errors/warnings)
- [x] ESLint warnings 保持 **65**(无新增)
- [x] 全局 ESLint any 保持 **0**
- [x] `go build` 零错误
- [x] 17/17 集成回归测试通过(零回归)
- [x] Docker 部署 healthy,前端页面正常加载(HTTP 200)
- [x] Deployment 高级设置 Tab 功能不变(更新策略/历史版本/容忍/调度/Pod 杂项/安全上下文/主机设置/DNS+hostAliases 全部正常)
- [x] manifest 解析和序列化提取为纯函数(parseAdvancedManifest + buildAdvancedManifest)
---
## Q18: 前端巨型组件拆分 — TraceManager
### 验证用例
```bash
# 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
```
### 通过标准
- [x] TraceManager.tsx 从 605 行降至 **331 行**(< 340 ✅,-45%)
- [x] 5 个提取文件全部 < 95 行(types 90L + trace-helpers 44L + SpanTreeNode 71L + TraceDetailDialog 80L + SpanDetailDialog 91L)
- [x] `tsc -b` 零错误
- [x] `npm run build` 零错误(✓ built in 18.23s)
- [x] ESLint errors 保持 **0**(trace-manager/ 目录零 warnings)
- [x] ESLint warnings 保持 **64**(无新增)
- [x] 全局 ESLint any 保持 **0**
- [x] `go build` 零错误
- [x] 17/17 集成回归测试通过(零回归)
- [x] Docker 部署 healthy,前端页面正常加载(HTTP 200),traces API 返回 200
- [x] 链路追踪管理页功能不变(Traces/Services/Metrics 三 Tab + Trace 详情 Span 树 + Span 详情标签/日志全部正常)
- [x] SpanTreeNode 内联定义反模式已修复(提取为模块级稳定组件引用)
---
## Q26: 前端巨型组件拆分 — PodLogsViewer [done]
### 通过标准
- [x] PodLogsViewer.tsx 从 553 行降至 **331 行**(-40%)
- [x] 5 个提取的文件全部 < 180 行(types.tsx 57L, LogsHeader 66L, ControlBar 178L, LogContentArea 64L, BottomStatusBar 42L)
- [x] `tsc -b` 零错误
- [x] `npm run build` 零错误(✓ built in 17.34s)
- [x] ESLint errors 保持 **0**(PodLogsViewer + pod-logs-viewer/ 目录零 errors/warnings)
- [x] ESLint warnings 保持 **65**(无新增)
- [x] 全局 ESLint any 保持 **0**
- [x] `go build` 零错误
- [x] 17/17 集成回归测试通过(零回归)
- [x] Docker 部署 healthy,前端页面正常加载(HTTP 200)
- [x] Pod 日志查看器功能不变(容器选择/行数限制/previous/sinceTime/关键字过滤+高亮/下载/刷新/实时流/暂停继续/全屏切换全部正常)
---
## U20: ESLint static-components 清零 + CredDetailDialog 内联组件修复 + i18n 损坏修复 [done]
### 通过标准
- [x] `react-hooks/static-components` 警告从 23 降至 **0**(CredDetailDialog.tsx `Field` 内联组件提取为模块级 `CredField`)
- [x] ESLint warnings 从 66 降至 **43**(-35%,全部剩余为 react-refresh/only-export-components 架构性约束 + 1 个 @tanstack/react-virtual 兼容性)
- [x] ESLint errors 保持 **0**
- [x] 修复 8 处被批量正则损坏的 `i18n.t()` 调用(`i18n.t('>名称:</span>')` 等 → 语义化键如 `common.name`、`common.createdAt`)
- [x] 6 处硬编码中文清零(凭据详情、密钥数据、无、创建时间、更新时间、安全提示)
- [x] 新增 `deploy.cred.sensitiveWarn` i18n 键(zh/en 对齐)
- [x] `tsc -b` 零错误
- [x] `npm run build` 零错误(✓ built in 17.29s)
- [x] `go build` 零错误
- [x] 17/17 集成回归测试通过(零回归)
- [x] Docker 部署 healthy
---
## U46: i18n 残余 UI 硬编码中文清零 — 深度边角
### 通过标准
- [x] 6 个修复文件中无残余硬编码中文(UserFormDialog/CredentialManager/RdpAuditViewer/LiveMonitorDialog/ServerFileBrowser/context-loaders)
- [x] context-loaders.ts 全部模板字符串使用 i18n.t() 插值
- [x] `tsc -b` 零错误
- [x] `npm run build` 零错误(✓ built in 18.05s)
- [x] ESLint errors=0, any=0
- [x] zh/en 8102=8102 完全对齐(+17 新键)
- [x] `go build` 零错误
- [x] 17/17 集成回归测试通过(零回归)
- [x] Docker 部署 healthy