05. 项目结构解析
本章将深入分析 sco-commit 的项目结构,帮助你理解前后端代码的组织方式。掌握项目结构是进行开发的前提,让我们从整体到局部逐步了解。
本章目标
- 理解前后端目录组织方式
- 掌握分层架构设计思想
- 了解各目录和文件的职责
- 能够定位特定功能的代码位置
项目整体结构
sco-commit 采用标准的 Go + React 双仓库结构:
commit-dashboard/
├── server/ # Go 后端服务
│ ├── main.go # 程序入口
│ ├── go.mod # Go 模块定义
│ ├── router/ # 路由配置
│ ├── app/ # 业务逻辑层
│ ├── infrastructure/ # 基础设施层
│ ├── common/ # 通用工具
│ ├── config/ # 配置管理
│ ├── migrations/ # 数据库迁移
│ └── utils/ # 工具函数
│
├── web/ # React 前端应用
│ ├── src/ # 源代码
│ ├── public/ # 静态资源
│ ├── index.html # HTML 入口
│ └── package.json # 依赖管理
│
├── docker-compose.yml # Docker 编排
├── Dockerfile.server # 后端镜像
└── Dockerfile.web # 前端镜像
这种结构将后端和前端完全分离,各自独立运行,通过 HTTP API 通信。
后端结构详解
后端采用分层架构,将不同职责的代码分离到不同目录:
server/
├── main.go # 应用入口
├── router/router.go # 主路由配置
├── config/database.go # 数据库配置
│
├── app/ # 业务逻辑层
│ ├── gitea/ # Gitea 相关业务
│ │ ├── handlers/ # 处理器(接收请求)
│ │ ├── models/ # 数据模型
│ │ │ ├── request/ # 请求参数
│ │ │ ├── response/ # 响应结构
│ │ │ └── db/ # 数据库模型
│ │ ├── repository/ # 数据仓库(数据库操作)
│ │ ├── services/ # 业务服务
│ │ ├── helpers/ # 辅助函数
│ │ └── router/ # Gitea 模块路由
│ │
│ └── ai/ # AI 相关业务
│ ├── handlers/ # AI 处理器
│ └── router/ # AI 模块路由
│
├── infrastructure/ # 基础设施层
│ ├── database/ # 数据库连接
│ ├── gitea/ # Gitea API 客户端
│ ├── llm/ # LLM 客户端(INO)
│ └── agent/ # AI Agent 核心
│
├── common/ # 通用工具
│ ├── response/ # 统一响应格式
│ ├── pagination/ # 分页工具
│ └── option/ # 选项模式
│
├── migrations/ # 数据库迁移
│ ├── gitea/ # Gitea 模块表
│ └── ai/ # AI 模块表
│
└── utils/ # 工具函数
├── env.go # 环境变量
└── jwt.go # JWT 认证
入口文件:main.go
每个 Go 程序都有一个入口文件,这是程序开始执行的地方:
// server/main.go
package main
import (
"log"
"github.com/commit-dashboard/server/config"
"github.com/commit-dashboard/server/router"
)
func main() {
// 初始化数据库连接
config.InitDB()
// 设置路由
app := router.Setup()
// 启动服务器
log.Fatal(app.Listen(":3000"))
}
这段代码做了三件事:
- 初始化数据库连接
- 配置路由
- 监听 3000 端口启动服务
业务逻辑层:app/
app/ 目录是核心业务代码所在,按照功能模块划分:
| 目录 | 职责 | 说明 |
|---|---|---|
app/gitea/ | Gitea 集成 | 仓库、同步、提交等业务逻辑 |
app/ai/ | AI 功能 | AI Agent 对话、周报生成 |
每个功能模块内部采用类似的结构:
app/gitea/
├── handlers/ # 处理 HTTP 请求
├── models/ # 数据结构定义
├── repository/ # 数据库操作
├── services/ # 业务逻辑
├── helpers/ # 辅助函数
└── router/ # 模块路由
基础设施层:infrastructure/
infrastructure/ 目录存放与外部系统交互的代码:
// infrastructure/gitea/client.go
// Gitea API 客户端封装
type GiteaClient struct {
BaseURL string
Token string
HTTPClient *http.Client
}
// infrastructure/llm/eino_client.go
// LLM 客户端封装
type EinoClient struct {
APIKey string
BaseURL string
}
通用工具:common/
common/ 目录存放各模块都可能用到的工具代码:
// common/response/response.go
// 统一响应格式
type Response struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}
// 成功响应
func Success(data interface{}) Response {
return Response{Code: 0, Message: "success", Data: data}
}
// 失败响应
func Error(msg string) Response {
return Response{Code: -1, Message: msg}
}
数据库迁移:migrations/
SQL 脚本定义数据库表结构:
-- migrations/gitea/001_init.sql
CREATE TABLE repositories (
id BIGSERIAL PRIMARY KEY,
owner VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
stars INTEGER DEFAULT 0,
language VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
前端结构详解
前端采用 React + TypeScript + Vite:
web/src/
├── main.tsx # React 入口
├── index.css # 全局样式
├── api/ # API 客户端
│ ├── index.ts # axios 实例配置
│ ├── gitea/ # Gitea API 调用
│ └── ai/ # AI API 调用
│
├── components/ # 可复用组件
│ ├── sag-ui/ # 基础 UI 组件
│ │ ├── page-container/
│ │ ├── pagination/
│ │ └── custom-error/
│ └── ai-elements/ # AI 相关组件
│ ├── agent.tsx
│ └── sources.tsx
│
├── pages/ # 页面组件
│ ├── dashboard/ # 总览页
│ ├── repos/ # 仓库列表页
│ ├── commits/ # 提交分析页
│ ├── members/ # 成员贡献页
│ ├── ai/ # AI 助手页
│ ├── sync/ # 同步管理页
│ └── settings/ # 设置页
│
├── hooks/ # 自定义 Hooks
│ └── use-repos.ts
│
├── stores/ # 状态管理(Zustand)
│ ├── app.ts
│ ├── gitea.ts
│ ├── sync-page.ts
│ ├── recent-members.ts
│ └── recent-repos.ts
│
├── routes/ # 路由配置
│ ├── __root.tsx # 根路由
│ ├── _layout.tsx # 布局路由
│ └── _layout/ # 各页面路由
│
├── types/ # TypeScript 类型
│ ├── api.ts
│ └── gitea.ts
│
├── utils/ # 工具函数
│ ├── helpers.ts
│ └── request/ # 请求封装
│
└── layout/ # 布局组件
├── index.tsx
└── sidebar/
API 客户端:api/
封装与后端通信的逻辑:
// web/src/api/index.ts
import axios from 'axios';
// 创建 axios 实例
const request = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000',
timeout: 30000,
});
// 请求拦截器
request.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
export default request;
// web/src/api/gitea/repos.ts
import request from '../index';
// 获取仓库列表
export const getRepos = (params?: { page?: number; limit?: number }) => {
return request.get('/api/gitea/repos', { params });
};
// 获取单个仓库详情
export const getRepo = (owner: string, repo: string) => {
return request.get(`/api/gitea/repos/${owner}/${repo}`);
};
页面组件:pages/
每个功能模块对应一个页面目录:
pages/
├── dashboard/ # 总览仪表盘
│ └── index.tsx
├── repos/ # 仓库管理
│ ├── index.tsx # 列表页
│ └── components/ # 页面内组件
├── commits/ # 提交分析
│ ├── index.tsx
│ └── components/ # 图表组件
├── members/ # 成员贡献
│ └── components/
├── ai/ # AI 助手
│ └── index.tsx
├── sync/ # 数据同步
│ └── components/ # 同步相关组件
└── settings/ # 系统设置
路由配置:routes/
使用 TanStack Router 的文件路由:
// web/src/routes/__root.tsx
import { createRootRoute, createRouter } from '@tanstack/react-router';
import { rootRoute } from './__root';
import { layoutRoute } from './_layout';
import { indexRoute } from './_layout/index';
// 构建路由树
const routeTree = rootRoute.addChildren([
layoutRoute.addChildren([
indexRoute,
// 更多子路由...
]),
]);
export const router = createRouter({ routeTree });
状态管理:stores/
使用 Zustand 进行状态管理:
// web/src/stores/gitea.ts
import { create } from 'zustand';
interface GiteaState {
// 状态
repos: Repo[];
selectedRepo: Repo | null;
// 方法
setRepos: (repos: Repo[]) => void;
setSelectedRepo: (repo: Repo | null) => void;
}
export const useGiteaStore = create<GiteaState>((set) => ({
repos: [],
selectedRepo: null,
setRepos: (repos) => set({ repos }),
setSelectedRepo: (repo) => set({ selectedRepo: repo }),
}));
分层架构解析
整个项目采用清晰的分层架构:
graph TB
subgraph "前端 Web"
A[Pages 页面] --> B[Components 组件]
B --> C[API 客户端]
C --> D[Stores 状态]
end
subgraph "后端 Server"
E[Router 路由] --> F[Handlers 处理器]
F --> G[Services 服务]
G --> H[Repository 仓库]
H --> I[Database 数据库]
end
C -.->|HTTP| E
I --> J[(PostgreSQL)]
subgraph "外部服务"
K[Gitea]
L[LLM 服务]
end
G --> K
G --> L
各层职责
| 层级 | 前端 | 后端 |
|---|---|---|
| 表现层 | Pages / Components | Handlers |
| 业务层 | Hooks / Stores | Services |
| 数据层 | API 客户端 | Repository |
| 基础设施 | - | Database / Gitea Client / LLM Client |
数据流向
-
前端请求流程:
- 用户在页面点击 → 调用 API 函数 → Zustand 存储状态 → 页面更新
-
后端处理流程:
- 收到 HTTP 请求 → 路由匹配 → 处理器处理 → 服务层业务逻辑 → 仓库层数据库操作 → 返回响应
代码定位示例
假设我们需要查找「获取仓库列表」功能的代码:
flowchart LR
A[前端] --> B[web/src/api/gitea/repos.ts]
A --> C[web/src/pages/repos/index.tsx]
D[后端] --> E[server/app/gitea/handlers/repo.go]
D --> F[server/app/gitea/repository/repo_repo.go]
D --> G[server/app/gitea/router/gitea.go]
| 功能 | 前端文件 | 后端文件 |
|---|---|---|
| API 调用 | api/gitea/repos.ts | - |
| 页面展示 | pages/repos/index.tsx | - |
| 请求处理 | - | handlers/repo.go |
| 数据库操作 | - | repository/repo_repo.go |
| 路由配置 | routes/ | router/gitea.go |