Skip to content

Development Guide

thend edited this page Nov 30, 2025 · 2 revisions

开发指南

欢迎为 CCMage 贡献代码!本指南将帮助你快速了解项目结构和开发流程。

🏗️ 项目结构

ccmage/
├── backend/                    # 后端服务
│   ├── server.js              # Express 主服务器
│   ├── routes.js              # 进程/AI 路由模块
│   ├── managementRoutes.js    # 项目管理 API 路由
│   ├── database.js            # SQLite 数据库服务层
│   ├── database-schema.sql    # 数据库模式定义
│   ├── processManager.js      # 进程管理(EventEmitter)
│   ├── startupDetector.js     # 启动命令自动检测
│   └── claudeCodeManager.js   # AI 对话管理
├── frontend/                   # 前端应用
│   └── src/
│       ├── components/        # React 组件
│       │   ├── TodoManager.tsx   # Todo 任务管理
│       │   ├── ProjectCard.tsx   # 项目卡片
│       │   ├── AiDialog.tsx      # AI 对话框
│       │   └── LogViewer.tsx     # 日志查看器
│       ├── App.tsx            # 主应用
│       ├── api.ts             # API 封装
│       └── types.ts           # TypeScript 类型
├── .claude/                    # 项目配置
│   └── projects.json          # 项目列表配置
└── docs/                       # 文档
    ├── ARCHITECTURE.md        # 架构文档
    └── API.md                 # API 文档

🚀 开发环境搭建

1. Fork 和克隆

# Fork 仓库到你的 GitHub 账号
# 然后克隆
git clone https://github.com/YOUR_USERNAME/ccmage.git
cd ccmage

2. 安装依赖

npm run install:all

3. 配置环境

cp .env.example .env
# 编辑 .env 添加必要的配置

4. 启动开发服务

# 同时启动前后端
npm run dev

# 或分别启动
npm run dev:frontend  # http://localhost:5173
npm run dev:backend   # http://localhost:9999

📝 代码规范

通用规范

  1. KISS 原则 - 保持简单直观
  2. YAGNI 原则 - 只实现当前所需
  3. DRY 原则 - 消除重复
  4. SOLID 原则 - 单一职责等

文件大小限制

  • TypeScript/JavaScript: ≤ 200 行
  • Python: ≤ 200 行
  • 超出限制时主动拆分文件

命名规范

文件命名

  • React 组件:PascalCase.tsx(例如:ProjectCard.tsx
  • 工具函数:camelCase.ts(例如:apiClient.ts
  • 常量文件:UPPER_CASE.ts(例如:CONSTANTS.ts

变量命名

// 组件
const ProjectCard = () => { ... }

// 函数
const fetchProjects = async () => { ... }

// 常量
const MAX_PROJECTS = 100

// 类型
interface Project { ... }
type ProjectStatus = 'active' | 'archived'

TypeScript 使用

严格模式

// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true
  }
}

避免 any

// ❌ 不好
function process(data: any) { ... }

// ✅ 好
function process(data: Project) { ... }

定义类型

// frontend/src/types.ts
export interface Project {
  name: string
  path: string
  tech: string[]
  status: ProjectStatus
  port?: number
}

export type ProjectStatus = 'active' | 'production' | 'archived'

🎨 前端开发

组件结构

import React, { useState, useEffect } from 'react'
import type { Project } from './types'

interface ProjectCardProps {
  project: Project
  onUpdate?: (project: Project) => void
}

export const ProjectCard: React.FC<ProjectCardProps> = ({
  project,
  onUpdate
}) => {
  const [status, setStatus] = useState<string>('loading')

  useEffect(() => {
    loadStatus()
  }, [project.name])

  const loadStatus = async () => {
    // 实现
  }

  return (
    <div className="project-card">
      {/* UI */}
    </div>
  )
}

API 调用

// frontend/src/api.ts
export const api = {
  async getProjects(): Promise<ProjectsResponse> {
    const response = await fetch('/api/projects')
    if (!response.ok) {
      throw new Error('Failed to fetch projects')
    }
    return response.json()
  },

  async startProject(name: string, command?: string): Promise<void> {
    const response = await fetch(`/api/projects/${name}/start`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ command })
    })
    if (!response.ok) {
      throw new Error('Failed to start project')
    }
  }
}

状态管理

使用 React Hooks:

// 本地状态
const [projects, setProjects] = useState<Project[]>([])

// 副作用
useEffect(() => {
  loadProjects()
}, [])

// 引用
const logViewerRef = useRef<HTMLDivElement>(null)

⚙️ 后端开发

路由定义

// backend/server.js
app.get('/api/projects', async (req, res) => {
  try {
    const projects = await loadProjects()
    res.json({ success: true, data: projects })
  } catch (error) {
    res.status(500).json({
      success: false,
      error: error.message
    })
  }
})

错误处理

// 统一错误处理
app.use((err, req, res, next) => {
  console.error('Error:', err)
  res.status(500).json({
    success: false,
    error: err.name,
    message: err.message
  })
})

数据库操作

// backend/database.js
class Database {
  getTodos(projectName, filters = {}) {
    const { status, priority, type } = filters

    let query = 'SELECT * FROM todos WHERE project_name = ?'
    const params = [projectName]

    if (status) {
      query += ' AND status = ?'
      params.push(status)
    }

    return this.db.prepare(query).all(...params)
  }
}

AI SDK 集成

// backend/claudeCodeManager.js
async execute(projectName, projectPath, prompt, sessionId) {
  const sdk = await this.loadSDK()

  const queryInstance = sdk.query({
    prompt,
    options: {
      cwd: projectPath,
      systemPrompt: { type: 'preset', preset: 'claude_code' },
      maxTurns: 50
    }
  })

  for await (const message of queryInstance) {
    const logEntry = this.messageToLogEntry(message, sessionId)
    this.emit(`ai-output:${sessionId}`, logEntry)
  }
}

🧪 测试

单元测试(计划中)

// __tests__/database.test.js
import { Database } from '../backend/database'

describe('Database', () => {
  let db

  beforeEach(() => {
    db = new Database(':memory:')
  })

  test('should create todo', () => {
    const todo = db.createTodo('test-project', {
      title: 'Test Todo',
      priority: 'high'
    })

    expect(todo.title).toBe('Test Todo')
    expect(todo.priority).toBe('high')
  })
})

E2E 测试(计划中)

// e2e/project-management.spec.js
test('should create and manage project', async ({ page }) => {
  await page.goto('http://localhost:5173')

  // 创建项目
  await page.click('button:has-text("新建项目")')
  await page.fill('input[name="name"]', 'test-project')
  await page.click('button:has-text("创建")')

  // 验证项目显示
  await expect(page.locator('.project-card')).toBeVisible()
})

🔧 调试技巧

前端调试

React DevTools

# 安装浏览器扩展
# Chrome: React Developer Tools
# Firefox: React Developer Tools

Console 调试

console.log('[ProjectCard] Loading status for:', project.name)
console.error('[API] Failed to fetch:', error)

后端调试

Nodemon 配置

{
  "watch": ["backend/**/*.js"],
  "ext": "js,json",
  "ignore": ["backend/node_modules/**"],
  "exec": "node backend/server.js"
}

日志输出

console.log(`[Server] Starting on port ${PORT}`)
console.error('[Database] Failed to connect:', error)

📦 构建和部署

构建前端

npm run build
# 输出到 frontend/dist

生产环境

# 设置环境变量
NODE_ENV=production

# 启动服务器
npm start

Docker 部署(计划中)

FROM node:16

WORKDIR /app

COPY package*.json ./
RUN npm install --production

COPY . .
RUN npm run build

EXPOSE 9999
CMD ["npm", "start"]

🤝 贡献流程

1. 创建特性分支

git checkout -b feature/amazing-feature

2. 开发和提交

# 开发功能
# ...

# 提交更改
git add .
git commit -m "feat: add amazing feature"

3. 推送和 PR

# 推送到你的 fork
git push origin feature/amazing-feature

# 在 GitHub 上创建 Pull Request

Commit 信息规范

使用语义化提交信息:

feat: 新功能
fix: Bug 修复
docs: 文档更新
style: 代码格式(不影响功能)
refactor: 重构
test: 测试相关
chore: 构建/工具相关

示例

git commit -m "feat: add project filtering by status"
git commit -m "fix: resolve AI dialog streaming issue"
git commit -m "docs: update API reference"

🎯 开发任务清单

新功能开发

  • 在 GitHub Issues 中创建功能需求
  • Fork 仓库并创建特性分支
  • 实现功能(遵循代码规范)
  • 编写测试(如果有测试框架)
  • 更新文档
  • 提交 Pull Request
  • 等待 Code Review

Bug 修复

  • 在 Issues 中报告 Bug
  • 重现问题
  • 定位问题代码
  • 修复并测试
  • 提交 PR 关联 Issue

📚 相关资源

学习资源

工具推荐

  • 代码编辑器:VS Code
  • Git 客户端:GitKraken / SourceTree
  • API 测试:Postman / Insomnia
  • 数据库查看:DB Browser for SQLite

💡 最佳实践

代码质量

  1. 保持简洁 - 每个函数只做一件事
  2. 注释清晰 - 复杂逻辑必须注释
  3. 错误处理 - 所有异步操作都要处理错误
  4. 类型安全 - 充分利用 TypeScript

性能优化

  1. 避免不必要的渲染 - 使用 React.memo
  2. 懒加载 - 大组件使用 lazy loading
  3. 缓存数据 - 避免重复请求
  4. 批量操作 - 使用 Promise.all

安全考虑

  1. 输入验证 - 验证所有用户输入
  2. 路径检查 - 防止路径遍历攻击
  3. 敏感信息 - 不要提交密钥到 Git
  4. CORS 配置 - 生产环境使用白名单

📞 获取帮助


感谢你的贡献! 🎉

Clone this wiki locally