Files
aiops/CLAUDE.md
T
attacker a7e2351ad2 feat: 定时任务编辑入口全栈打通 + 修复ApprovalFlow编译错误 + 完成大文件拆分重构
- CronJobManager: 编辑按钮接通 onClick → openEdit(job),弹出编辑对话框预填数据,
  复用新建/编辑共用 Dialog(editingId 区分模式),标题/按钮文案动态切换,
  保存时根据 editingId 调用 createJob 或 updateJob — 消除占位按钮
- ApprovalFlow: 修复 API 方法名不匹配导致 tsc -b 编译失败
  (getInstances→getProcesses, getMyApprovals→getProcesses({status:'pending'}),
   approveInstance→approveProcess, rejectInstance→rejectProcess)
  + 添加 ApprovalFlowManager 导出别名兼容 App.tsx 导入
- 合并已完成的大文件拆分重构: automation.go→automation_cron/script/playbook,
  ldap.go→ldap_ou/ldap_users, server.go→cloud_accounts/domain/platform/region,
  user.go→user_admin; 前端 Dashboard/K8s/Server/Approval/Monitor 子组件拆分
  // Logan.Li
2026-06-15 01:09:49 +08:00

173 lines
8.1 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.
# EZOps AIOps — AI 开发全局规范
> 本文件供 AI 编程助手(Claude/Cursor/Copilot/Hermes)自动读取并遵循。
> 定义项目架构约束、代码规范、拆分策略和开发流程。
> 作者: Logan.Li · 语言: 中文(代码注释中文 + 署名 `// Logan.Li`)
## 项目概述
**EZOps AIOps** — AI 智能运维平台,涵盖 13 大模块:
服务器管理 · 云资源(账号/域名/账单) · K8s 集群 · 监控告警(Agent) · CI/CD 流水线 ·
自动化运维(CronJob/Script/Ansible) · 审批流程 · AI 智能运维(告警分析/故障诊断/预测/脚本生成) ·
导航收藏 · 审计日志 · 系统管理(用户/LDAP) · 监控 Agent(双通道: 指标采集+远程命令)
- **远程仓库**: https://gitee.com/attacker/aiops.git
- **Git 用户**: attacker / attacker@gitee.com
## 技术栈
| 层 | 技术 | 端口 |
|---|---|---|
| 后端 | Go 1.21 + Gin + GORM + SQLite (+ MySQL/PG 可选) + JWT + Zap | 8080 |
| 前端 | React 19 + TypeScript + Vite + Tailwind + shadcn/ui + Zustand + TanStack Query | 5173 |
| Agent | Go 1.24 (CGO_ENABLED=0 静态二进制, amd64+arm64) | — |
## 核心规则
### ⛔ 文件大小红线
| 类型 | 上限 | 超出处理 |
|------|------|----------|
| Go handler | **400 行** | 按 sub-domain 拆分到独立文件(同一 package) |
| Go model | **300 行** | 按模块拆分 |
| React 组件 (.tsx) | **700 行** | 提取子组件到同目录,主组件负责编排 |
| TypeScript 工具/服务 | **500 行** | 按功能域拆分 |
| 任何文件 | **700 行(硬上限)** | 必须拆分,无例外 |
**拆分策略**:
- **Go**: 同 package 下按职责拆文件(如 `automation.go` → `automation_cron.go` + `automation_script.go` + `automation_playbook.go`),共享 struct 放原文件或新建 `automation_types.go`
- **React**: 子组件提取到同目录独立 `.tsx` 文件(如 `SystemManager.tsx` → `SystemManager.tsx` + `SystemUserTab.tsx` + `SystemLdapTab.tsx`),通过 props 传递数据和回调
- **Mock 数据**: 按模块拆分(如 `data.ts` → `data/servers.ts` + `data/cloud.ts` + `data/k8s.ts` + `index.ts` 汇总导出)
### ⛔ Go 代码规范
1. **所有 handler 返回必须用 `models.Response{Code:200, Data:...}` 包裹** — 前端 `http.ts` 从 `json.data` 取值,裸 `gin.H{}` 会导致前端拿到 undefined 静默失败
2. **Go 编译命令**(每次必须):
```bash
cd ~/devops/aiops/backend && unset http_proxy https_proxy && export GOPROXY=https://goproxy.cn,direct && GOTOOLCHAIN=local CGO_ENABLED=1 go build -o aiops-server ./cmd/
```
3. **新增 handler 不绑 model** — 用独立 request struct 接收 `ShouldBindJSON`,再映射到 GORM model
4. **GORM 指针字段必须解引用** — model 中 `*int`/`*string` 字段传给 `fmt.Sprintf` 时先 `*field`
5. **SQLite 必须 CGO_ENABLED=1**(macOS 默认开)
6. **新模块 4 步**: Model → Handler → Routes(routes.go 注册) → AutoMigrate(database.go 添加)
### ⛔ React 前端规范
1. **API 列表响应解包** — 后端返回 `{code, data: {list: T[], total: N}}`,http.ts 提取 `.data` 后仍是 `{list, total}` 对象,组件中必须 `.list` 提取数组
2. **禁止 `window.confirm()`/`alert()`** — 用 `ConfirmProvider`(`useConfirm()` hook)替代 confirm,用 `toast.error()`/`toast.success()` 替代 alert
3. **Radix Dialog 必须有 `<DialogDescription>`** — 至少加 `<DialogDescription className="sr-only">`
4. **Token 不在 localStorage** — 用 `useUserStore.getState().token`(Zustand persist)
5. **新增组件 2 步**: API 层(`services/api.ts` 添加) → 组件+路由(`App.tsx` 加 Route,`Sidebar.tsx` 加菜单)
### ⛔ UI 设计原则
- **能去掉的都去掉** — 不要多余边框、高亮框、装饰性分隔线
- **按钮文案 2-4 字** — "批量导入" 而非 "批量导入用户"
- **不留占位按钮** — 功能没做好就不放按钮,不要 `toast.info('开发中')`
- **页面打开即用** — 不需要额外"新建"按钮跳转
- **主题感知** — 用 CSS 变量 `bg-[var(--dg-bg-secondary)]`,不用硬编码颜色
- **表单操作按钮在字段下方** — 用 `mt-4 pt-4 border-t` 分隔,不放 Card 标题右侧
- **主动排查同类问题** — 用户报一个 UI 问题,全局搜索同类一次性修复
### ⛔ 通用开发流程
```
分析需求 → 写代码 → go build + tsc --noEmit → ./run.sh restart → 浏览器验证 → git commit + push
```
- **修改前端后必须在浏览器实际测试**,不能只说"刷新试试"
- **提交前编译验证**:后端 `go build ./...`,前端 `npx tsc --noEmit && npm run build`
- **Git 提交**: `git add -A && git commit -m "feat: 描述" && git push origin master`
- **开发日志**: 每次开发后更新 `docs/dev-log.md`
## 架构约束
### 目录结构(核心部分)
```
backend/
├── cmd/main.go + seed.go # 入口 + 种子数据
├── internal/
│ ├── config/config.go # Viper 配置
│ ├── models/ # GORM 模型(每模块独立文件)
│ └── api/
│ ├── handlers/ # HTTP handler(按模块拆分,每文件 <400 行)
│ ├── middleware/auth.go # JWT + Admin 中间件
│ └── routes/routes.go # 路由注册
├── pkg/database/database.go # DB 连接 + AutoMigrate
└── scripts/mock.go # Mock 种子
frontend/
├── src/
│ ├── components/{模块}/ # 页面组件(每文件 <700 行,超出拆子组件)
│ ├── services/{api.ts, http.ts} # API 服务层
│ ├── store/ # Zustand 状态管理
│ ├── hooks/ # React hooks
│ └── types/index.ts # 全局类型定义
├── App.tsx # 路由 + 顶层 Provider
agent/ # 监控 Agent(独立 Go module)
├── main.go # CLI 入口
├── collector/ # /proc 采集器
├── task/ # 远程任务执行器
└── reporter/http.go # HTTP 上报
```
### API 响应格式
```go
// 统一响应(所有 handler 必须遵循)
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
// 列表响应(Data 内层)
type ListResponse struct {
List interface{} `json:"list"`
Total int64 `json:"total"`
}
```
### 认证流程
```
Login(本地/LDAP fallback) → JWT Token → 前端 Zustand persist 存储 → http.ts 自动注入 Bearer
→ JWTAuth 中间件验证 → 需 admin 权限的路由额外 AdminAuth 中间件
```
### Agent 双通道架构
```
指标通道: ticker(10s) → collect(/proc) → POST /agent/report
任务通道: ticker(5s) → GET /agent/tasks/poll → execute → POST /agent/tasks/:id/result
```
### 主题系统
5 套主题(CSS 变量驱动): `ocean-deep`(默认暗色) · `light-professional`(亮色) · `midnight-purple` · `forest-green` · `dark-rose`
所有组件必须用 `var(--dg-xxx)` 变量,禁止硬编码颜色值。
## 常见陷阱速查
| # | 陷阱 | 解决 |
|---|------|------|
| Go1 | go build 超时 | `unset proxy + GOPROXY=goproxy.cn + GOTOOLCHAIN=local` |
| Go2 | handler 不包裹 Response | 必须 `models.Response{Code:200, Data:...}` |
| Go3 | GORM 指针字段 | `*field` 解引用后再用 |
| Go4 | mock.go 重运行 | UNIQUE constraint → 用 API 注入新数据 |
| FE1 | 白屏无堆栈 | 90% 是列表 `{list,total}` 未解包 |
| FE2 | confirm() 冻结自动化 | 用 `useConfirm()` |
| FE3 | Token 获取 | `useUserStore.getState().token`,不是 localStorage |
| FE4 | Radix Dialog 警告 | 必须有 `<DialogDescription>` |
| FE5 | browser_click 不触发 toggle | 改用 `browser_console` 注入 `.click()` |
## 文件大小监控
```bash
# 检查超限文件(CI 检查脚本)
find backend -name "*.go" -not -path "*/vendor/*" | xargs wc -l | awk '$1 > 400 && $2 != "total"'
find frontend/src -name "*.tsx" -o -name "*.ts" | xargs wc -l | awk '$1 > 700 && $2 != "total"'
```