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
This commit is contained in:
attacker committed 2026-06-15 01:09:49 +08:00
1 parent 8be63ac168
commit a7e2351ad2
37 files changed
+7374 -4891

No files matched your search

+172
View File
@@ -0,0 +1,172 @@
# 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"'
```