Files
aiops/docs/development-guide.md
Logan.Li fd60361d93 chore: 统一本地服务器与远程仓库源码
- 合并 main 与 master 历史并以服务器结构为基线
- 保留迁移、数据保留、AWS 账单与部署安全能力
- 建立 SDD 规格和 CI 验证闭环
2026-08-05 20:03:25 +08:00

6.8 KiB
Raw Permalink Blame History

开发规范

Git 工作流

分支策略

分支 说明
main / master 生产分支,保护分支
dev 开发集成分支
feature/* 功能分支
fix/* 修复分支
hotfix/* 紧急修复

提交规范

<type>: <description>

- 变更点 1
- 变更点 2

Type 列表:

  • feat: 新功能
  • fix: Bug 修复
  • refactor: 重构
  • docs: 文档
  • style: 样式
  • test: 测试
  • chore: 构建/工具

示例:

feat: 添加服务器批量导入功能

- 支持 Excel 导入
- 支持云平台同步

后端规范(Go)

目录职责

目录 职责
main.go / embed.go 应用入口 + 前端嵌入
controller/ HTTP 请求处理(参数校验、响应)
model/ 数据模型定义
config/ 配置管理 (Viper)
middleware/ JWT / 日志 / CORS 中间件
router/ 路由注册
service/ / repository/ 业务逻辑 / 数据访问
pkg/database/ 数据库连接 + 显式基线建表能力
pkg/agentcli/ Agent CLI 引擎(ReAct + Evolver)
pkg/remoteexec/ Go-native SSH 远程执行
static/ 前端 embed 静态资源

命名规范

  • 文件名:snake_case.go
  • 类型/方法:PascalCase
  • 变量:camelCase
  • 常量:PascalCase 或 ALL_CAPS

统一响应格式

// 成功响应
c.JSON(http.StatusOK, models.Response{
    Code:    200,
    Message: "success",
    Data:    result,
})

// 分页响应
c.JSON(http.StatusOK, models.Response{
    Code:    200,
    Message: "success",
    Data: models.ListResponse{
        List:  items,
        Total: total,
    },
})

// 错误响应
c.JSON(http.StatusBadRequest, models.Response{
    Code:    400,
    Message: "参数错误: " + err.Error(),
})

Handler 模板

func (h *ServerHandler) CreateServer(c *gin.Context) {
    // 1. 参数绑定
    var req models.ServerRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, models.Response{Code: 400, Message: err.Error()})
        return
    }

    // 2. 业务逻辑
    server := models.Server{
        Name: req.Name,
        IP:   req.IP,
    }
    if err := h.db.Create(&server).Error; err != nil {
        c.JSON(http.StatusInternalServerError, models.Response{Code: 500, Message: "创建失败"})
        return
    }

    // 3. 返回结果
    c.JSON(http.StatusOK, models.Response{Code: 200, Message: "创建成功", Data: server})
}

注意事项

  • GORM First() 连续查询会带上次 ID,用 Take() 代替
  • Go 编译前设置 GOPROXY=https://goproxy.cn,direct
  • SQLite 需要 CGO_ENABLED=1
  • JWT 使用 golang-jwt/jwt/v5,jwt.TimeFunc() 已移除,用 time.Now()
  • 新增或修改代码注释末尾署名 // Logan.Li

前端规范(React + TypeScript)

目录职责

目录 职责
src/api/ API 封装(按菜单域分子目录,http.ts 自动解包 .data)
src/pages/ 复杂域级页面(多 Tab / 多子面板)
src/components/*/ 可复用业务组件
src/components/layout/ PageShell / Sidebar / Header / ThemePicker
src/components/ui/ shadcn/ui 基础组件
src/locales/ i18n 翻译文件(zh-CN.json / en-US.json)
src/i18n/ i18next 初始化
src/hooks/ 自定义 Hooks
src/store/ Zustand 状态管理
src/types/ TypeScript 类型(按域拆分)
src/utils/ 主题 / 权限 / 导出等工具

命名规范

  • 组件文件:PascalCase.tsx(如 ServerManager.tsx)
  • 工具文件:camelCase.ts(如 formatDate.ts)
  • 组件名:PascalCase

PageShell 页面骨架

新页面优先使用 PageShell 统一布局:

import { PageShell } from '@/components/layout/PageShell';
import { useTranslation } from 'react-i18next';

export function MyPage() {
  const { t } = useTranslation();
  return (
    <PageShell
      title={t('myModule.title')}
      subtitle={t('myModule.subtitle')}
      icon={<ServerIcon className="h-5 w-5" />}
      actions={<Button>操作</Button>}
      toolbar={<SearchInput />}
      tabs={[{ id: 'list', label: t('myModule.tabList') }]}
      activeTab="list"
      onTabChange={() => {}}
      variant="list"
    >
      {/* 页面内容 */}
    </PageShell>
  );
}

variant 取值:default | list | config | workspace。

国际化 (i18n)

  • 框架:i18next + react-i18next
  • 语言:zh-CN(默认)/ en-US
  • 翻译文件:src/locales/zh-CN.json / en-US.json
  • 命名空间按模块划分:common、menu、dashboard、server、alert、approval、audit、deploy、k8s 等

新增文案规范:

// ✅ 使用翻译键
const { t } = useTranslation();
<span>{t('server.batchImport')}</span>

// ❌ 禁止硬编码中文(已迁移模块)
<span>批量导入</span>

双语同步:修改 zh-CN.json 时必须同步更新 en-US.json。

UI 设计原则

  • 能去掉的装饰都去掉(无边框、无多余分隔线)
  • 选中态用 bg-surface-active,不用彩色高亮
  • 页面打开即用,不需要额外的"新建"按钮跳转
  • 主题感知 — 使用 CSS 变量 var(--dg-xxx),不硬编码颜色
  • 按钮文案 2–4 字
  • 禁止 window.confirm() / alert(),使用 useConfirm() + toast
  • Radix Dialog 必须包含 <DialogDescription>

API 调用

前端通过 Vite 代理访问后端 API:

// 开发环境请求会被代理到 http://localhost:8080
// http.ts 已自动提取 Response.Data,列表返回 { list, total }
import { getServers } from '@/api/server';
const { list, total } = await getServers({ page: 1 });

测试

后端测试

cd backend
go vet ./...
go build -o aiops .

前端构建测试

cd frontend
npm run build
npx tsc --noEmit   # 类型检查(提交前零错误)
npm run lint        # ESLint(提交前零错误)

独立二进制构建

./run.sh build tag v1.5.0
./aiops version

集成测试

# 测试账号必须显式传入,避免对生产或共享环境误用默认凭证
AIOPS_TEST_USERNAME=admin AIOPS_TEST_PASSWORD='测试密码' \
  python3 tests/integration_test.py --host localhost --port 8080

GitHub Actions CI

.github/workflows/ci.yml 在 master / dev 的推送与 Pull Request 上并行执行后端、Agent、前端和 SQLite + redka API 集成测试。前端 ESLint 采用阻断模式;仓库已有的 lint 错误会使 CI 失败,必须修复后才会恢复通过,不能以忽略错误的方式放行。

日志

级别 使用场景
Info 服务启动、正常操作
Warn 配置缺失、降级处理
Error 操作失败、异常
Fatal 启动失败、不可恢复
log.Info("server started", zap.String("port", cfg.Port))
log.Error("database error", zap.Error(err))