LangChain.js 完全开发手册(十六)实战综合项目二:AI 驱动的代码助手

第16章:实战综合项目二:AI 驱动的代码助手

前言

大家好,我是鲫小鱼。是一名不写前端代码的前端工程师,热衷于分享非前端的知识,带领切图仔逃离切图圈子,欢迎关注我,微信公众号:《鲫小鱼不正经》。欢迎点赞、收藏、关注,一键三连!!

🎯 本章学习目标

通过本章学习,您将:

  • 构建一个完整的 AI 驱动代码助手系统,涵盖代码分析、生成、审查和优化
  • 掌握多语言代码解析技术:JavaScript、TypeScript、Python、Java、Go 等
  • 实现智能代码审查系统,支持代码质量评估、安全检测和最佳实践建议
  • 集成 LangGraph 工作流,实现代码重构、自动修复和优化建议
  • 搭建现代化的代码管理界面,支持实时协作和版本控制
  • 掌握企业级代码助手系统的架构设计和最佳实践

📋 项目概述

系统功能特性

🔍 智能代码分析

  • 支持多种编程语言:JavaScript、TypeScript、Python、Java、Go、Rust
  • 代码结构分析和依赖关系提取
  • 代码复杂度计算和性能瓶颈识别
  • 代码风格检查和格式化建议

🧠 AI 代码生成

  • 基于自然语言描述的代码生成
  • 代码补全和智能提示
  • 函数和类的自动生成
  • 测试用例和文档自动生成

智能代码审查

  • 代码质量评估和评分
  • 安全漏洞检测和修复建议
  • 性能优化建议和重构方案
  • 最佳实践检查和改进建议

🎨 协作开发平台

  • 实时代码协作和版本控制
  • 代码审查工作流和审批机制
  • 团队知识库和最佳实践分享
  • 项目管理和进度跟踪

🏗️ 系统架构设计

整体架构图

scss 复制代码
┌─────────────────────────────────────────────────────────────┐
│                    前端层 (Next.js)                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐          │
│  │  代码编辑器  │  │  代码审查    │  │  项目管理    │          │
│  │  界面        │  │  界面        │  │  界面        │          │
│  └─────────────┘  └─────────────┘  └─────────────┘          │
└─────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────┐
│                  API 网关层 (Next.js API Routes)              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐          │
│  │  代码分析    │  │  代码生成    │  │  代码审查    │          │
│  │  API        │  │  API        │  │  API        │          │
│  └─────────────┘  └─────────────┘  └─────────────┘          │
└─────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────┐
│                    业务服务层                                │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐          │
│  │  代码解析    │  │  AI 生成     │  │  LangGraph  │          │
│  │  服务        │  │  服务        │  │  工作流      │          │
│  └─────────────┘  └─────────────┘  └─────────────┘          │
└─────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────┐
│                      数据存储层                              │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐          │
│  │  向量数据库  │  │  关系数据库  │  │  代码仓库    │          │
│  │  (Chroma)   │  │ (PostgreSQL) │  │  (Git)      │          │
│  └─────────────┘  └─────────────┘  └─────────────┘          │
└─────────────────────────────────────────────────────────────┘

技术栈选择

前端技术

  • Next.js 14 (App Router)
  • TypeScript
  • Monaco Editor (VS Code 编辑器)
  • Tailwind CSS
  • React Query

后端技术

  • Node.js + TypeScript
  • LangChain.js
  • LangGraph
  • Tree-sitter (代码解析)
  • ESLint/Prettier

AI 模型服务

  • OpenAI GPT-4/GPT-3.5-turbo
  • OpenAI Codex
  • Anthropic Claude
  • GitHub Copilot API

代码分析工具

  • Tree-sitter (多语言解析)
  • ESLint (JavaScript/TypeScript)
  • Pylint (Python)
  • SonarQube (代码质量)
  • Semgrep (安全扫描)

数据存储

  • Chroma (向量数据库)
  • PostgreSQL (关系数据库)
  • Redis (缓存)
  • Git (版本控制)

🚀 项目初始化

环境准备

bash 复制代码
# 1. 创建项目目录
mkdir ai-code-assistant
cd ai-code-assistant

# 2. 初始化 Next.js 项目
npx create-next-app@latest . --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"

# 3. 安装核心依赖
npm install @langchain/core @langchain/community @langchain/openai @langchain/langgraph
npm install @langchain/chroma @langchain/postgres
npm install tree-sitter tree-sitter-javascript tree-sitter-typescript tree-sitter-python
npm install @monaco-editor/react
npm install @prisma/client prisma
npm install redis ioredis
npm install zod react-hook-form @hookform/resolvers
npm install @tanstack/react-query
npm install lucide-react clsx tailwind-merge

# 4. 安装开发依赖
npm install -D @types/node tsx nodemon
npm install -D prisma
npm install -D eslint prettier @typescript-eslint/parser @typescript-eslint/eslint-plugin

项目结构

bash 复制代码
ai-code-assistant/
├── src/
│   ├── app/                    # Next.js App Router
│   │   ├── api/               # API 路由
│   │   │   ├── code/         # 代码分析 API
│   │   │   ├── generate/      # 代码生成 API
│   │   │   └── review/       # 代码审查 API
│   │   ├── editor/           # 代码编辑器页面
│   │   ├── review/           # 代码审查页面
│   │   └── projects/         # 项目管理页面
│   ├── components/            # 可复用组件
│   │   ├── ui/               # 基础 UI 组件
│   │   ├── editor/           # 代码编辑器组件
│   │   └── review/           # 代码审查组件
│   ├── lib/                   # 工具库
│   │   ├── ai/               # AI 相关工具
│   │   ├── code/             # 代码分析工具
│   │   ├── db/               # 数据库工具
│   │   └── utils.ts          # 通用工具
│   ├── services/             # 业务服务
│   │   ├── parser/           # 代码解析服务
│   │   ├── generator/        # 代码生成服务
│   │   ├── reviewer/        # 代码审查服务
│   │   └── workflow/        # 工作流服务
│   └── types/               # TypeScript 类型定义
├── prisma/                   # 数据库模式
├── docker/                   # Docker 配置
├── docs/                     # 项目文档
└── package.json

📄 数据库设计

Prisma 模式定义

prisma 复制代码
// prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  name      String?
  avatar    String?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  projects  Project[]
  reviews   CodeReview[]
  comments  ReviewComment[]

  @@map("users")
}

model Project {
  id          String   @id @default(cuid())
  name        String
  description String?
  language    String
  framework   String?
  repository  String?
  status      ProjectStatus @default(ACTIVE)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  userId      String
  user        User     @relation(fields: [userId], references: [id])

  files       CodeFile[]
  reviews     CodeReview[]

  @@map("projects")
}

model CodeFile {
  id          String   @id @default(cuid())
  name        String
  path        String
  content     String
  language    String
  size        Int
  lines       Int
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  projectId   String
  project     Project  @relation(fields: [projectId], references: [id])

  reviews     CodeReview[]
  issues      CodeIssue[]

  @@map("code_files")
}

model CodeReview {
  id          String   @id @default(cuid())
  title       String
  description String?
  status      ReviewStatus @default(PENDING)
  score       Float?
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  userId      String
  user        User     @relation(fields: [userId], references: [id])

  projectId   String
  project     Project  @relation(fields: [projectId], references: [id])

  fileId      String?
  file        CodeFile? @relation(fields: [fileId], references: [id])

  comments    ReviewComment[]
  issues      CodeIssue[]

  @@map("code_reviews")
}

model ReviewComment {
  id          String   @id @default(cuid())
  content     String
  lineNumber  Int?
  type        CommentType @default(SUGGESTION)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  userId      String
  user        User     @relation(fields: [userId], references: [id])

  reviewId    String
  review      CodeReview @relation(fields: [reviewId], references: [id])

  @@map("review_comments")
}

model CodeIssue {
  id          String   @id @default(cuid())
  type        IssueType
  severity    IssueSeverity
  message     String
  lineNumber  Int?
  rule        String?
  fix         String?
  createdAt   DateTime @default(now())

  fileId      String
  file        CodeFile @relation(fields: [fileId], references: [id])

  reviewId    String?
  review      CodeReview? @relation(fields: [reviewId], references: [id])

  @@map("code_issues")
}

enum ProjectStatus {
  ACTIVE
  ARCHIVED
  DELETED
}

enum ReviewStatus {
  PENDING
  IN_PROGRESS
  APPROVED
  REJECTED
  NEEDS_CHANGES
}

enum CommentType {
  QUESTION
  SUGGESTION
  PRAISE
  CONCERN
}

enum IssueType {
  BUG
  VULNERABILITY
  CODE_SMELL
  PERFORMANCE
  SECURITY
  STYLE
}

enum IssueSeverity {
  LOW
  MEDIUM
  HIGH
  CRITICAL
}

🔧 核心服务实现

代码解析服务

typescript 复制代码
// src/services/parser/code-parser.ts
import Parser from 'tree-sitter';
import JavaScript from 'tree-sitter-javascript';
import TypeScript from 'tree-sitter-typescript';
import Python from 'tree-sitter-python';

export interface CodeStructure {
  functions: FunctionInfo[];
  classes: ClassInfo[];
  imports: ImportInfo[];
  exports: ExportInfo[];
  variables: VariableInfo[];
  comments: CommentInfo[];
}

export interface FunctionInfo {
  name: string;
  parameters: ParameterInfo[];
  returnType?: string;
  lineNumber: number;
  endLineNumber: number;
  isAsync: boolean;
  isGenerator: boolean;
  visibility: 'public' | 'private' | 'protected';
}

export interface ClassInfo {
  name: string;
  methods: MethodInfo[];
  properties: PropertyInfo[];
  lineNumber: number;
  endLineNumber: number;
  extends?: string;
  implements?: string[];
}

export interface ImportInfo {
  module: string;
  imports: string[];
  isDefault: boolean;
  lineNumber: number;
}

export interface ExportInfo {
  name: string;
  type: 'default' | 'named' | 'namespace';
  lineNumber: number;
}

export interface VariableInfo {
  name: string;
  type?: string;
  value?: string;
  lineNumber: number;
  isConst: boolean;
  isLet: boolean;
}

export interface CommentInfo {
  content: string;
  lineNumber: number;
  type: 'line' | 'block';
}

export interface ParameterInfo {
  name: string;
  type?: string;
  defaultValue?: string;
  isOptional: boolean;
}

export interface MethodInfo {
  name: string;
  parameters: ParameterInfo[];
  returnType?: string;
  lineNumber: number;
  visibility: 'public' | 'private' | 'protected';
}

export interface PropertyInfo {
  name: string;
  type?: string;
  lineNumber: number;
  visibility: 'public' | 'private' | 'protected';
}

export class CodeParser {
  private parsers: Map<string, Parser> = new Map();

  constructor() {
    this.initializeParsers();
  }

  private initializeParsers() {
    // JavaScript
    const jsParser = new Parser();
    jsParser.setLanguage(JavaScript);
    this.parsers.set('javascript', jsParser);

    // TypeScript
    const tsParser = new Parser();
    tsParser.setLanguage(JavaScript);
    this.parsers.set('typescript', tsParser);

    // Python
    const pythonParser = new Parser();
    pythonParser.setLanguage(Python);
    this.parsers.set('python', pythonParser);
  }

  async parseCode(code: string, language: string): Promise<CodeStructure> {
    const parser = this.parsers.get(language.toLowerCase());
    if (!parser) {
      throw new Error(`Unsupported language: ${language}`);
    }

    const tree = parser.parse(code);
    const structure: CodeStructure = {
      functions: [],
      classes: [],
      imports: [],
      exports: [],
      variables: [],
      comments: []
    };

    this.extractStructure(tree.rootNode, code, structure);
    return structure;
  }

  private extractStructure(node: any, code: string, structure: CodeStructure) {
    if (node.type === 'function_declaration' || node.type === 'function') {
      structure.functions.push(this.extractFunction(node, code));
    } else if (node.type === 'class_declaration' || node.type === 'class') {
      structure.classes.push(this.extractClass(node, code));
    } else if (node.type === 'import_statement' || node.type === 'import') {
      structure.imports.push(this.extractImport(node, code));
    } else if (node.type === 'export_statement' || node.type === 'export') {
      structure.exports.push(this.extractExport(node, code));
    } else if (node.type === 'variable_declaration') {
      structure.variables.push(...this.extractVariables(node, code));
    } else if (node.type === 'comment') {
      structure.comments.push(this.extractComment(node, code));
    }

    // 递归处理子节点
    for (const child of node.children) {
      this.extractStructure(child, code, structure);
    }
  }

  private extractFunction(node: any, code: string): FunctionInfo {
    const name = this.getNodeText(node.childForFieldName('name'), code);
    const parameters = this.extractParameters(node.childForFieldName('parameters'), code);
    const returnType = this.getReturnType(node, code);
    const lineNumber = node.startPosition.row + 1;
    const endLineNumber = node.endPosition.row + 1;
    const isAsync = this.hasModifier(node, 'async');
    const isGenerator = this.hasModifier(node, 'generator');

    return {
      name,
      parameters,
      returnType,
      lineNumber,
      endLineNumber,
      isAsync,
      isGenerator,
      visibility: this.getVisibility(node)
    };
  }

  private extractClass(node: any, code: string): ClassInfo {
    const name = this.getNodeText(node.childForFieldName('name'), code);
    const methods: MethodInfo[] = [];
    const properties: PropertyInfo[] = [];
    const lineNumber = node.startPosition.row + 1;
    const endLineNumber = node.endPosition.row + 1;

    // 提取方法和属性
    const body = node.childForFieldName('body');
    if (body) {
      for (const child of body.children) {
        if (child.type === 'method_definition' || child.type === 'method') {
          methods.push(this.extractMethod(child, code));
        } else if (child.type === 'property_definition' || child.type === 'property') {
          properties.push(this.extractProperty(child, code));
        }
      }
    }

    return {
      name,
      methods,
      properties,
      lineNumber,
      endLineNumber,
      extends: this.getExtends(node, code),
      implements: this.getImplements(node, code)
    };
  }

  private extractImport(node: any, code: string): ImportInfo {
    const module = this.getNodeText(node.childForFieldName('source'), code);
    const imports: string[] = [];
    const isDefault = false;

    const specifiers = node.childForFieldName('specifiers');
    if (specifiers) {
      for (const child of specifiers.children) {
        if (child.type === 'import_specifier') {
          const name = this.getNodeText(child.childForFieldName('name'), code);
          imports.push(name);
        }
      }
    }

    return {
      module,
      imports,
      isDefault,
      lineNumber: node.startPosition.row + 1
    };
  }

  private extractExport(node: any, code: string): ExportInfo {
    const name = this.getNodeText(node.childForFieldName('name'), code);
    const type = node.type === 'export_default' ? 'default' : 'named';

    return {
      name,
      type,
      lineNumber: node.startPosition.row + 1
    };
  }

  private extractVariables(node: any, code: string): VariableInfo[] {
    const variables: VariableInfo[] = [];
    const isConst = node.type === 'const_declaration';
    const isLet = node.type === 'let_declaration';

    for (const child of node.children) {
      if (child.type === 'variable_declarator') {
        const name = this.getNodeText(child.childForFieldName('name'), code);
        const value = this.getNodeText(child.childForFieldName('value'), code);
        const type = this.getVariableType(child, code);

        variables.push({
          name,
          type,
          value,
          lineNumber: child.startPosition.row + 1,
          isConst,
          isLet
        });
      }
    }

    return variables;
  }

  private extractComment(node: any, code: string): CommentInfo {
    const content = this.getNodeText(node, code);
    const lineNumber = node.startPosition.row + 1;
    const type = content.startsWith('//') ? 'line' : 'block';

    return {
      content,
      lineNumber,
      type
    };
  }

  private extractParameters(node: any, code: string): ParameterInfo[] {
    const parameters: ParameterInfo[] = [];

    if (node) {
      for (const child of node.children) {
        if (child.type === 'parameter') {
          const name = this.getNodeText(child.childForFieldName('name'), code);
          const type = this.getNodeText(child.childForFieldName('type'), code);
          const defaultValue = this.getNodeText(child.childForFieldName('default'), code);
          const isOptional = this.hasModifier(child, 'optional');

          parameters.push({
            name,
            type,
            defaultValue,
            isOptional
          });
        }
      }
    }

    return parameters;
  }

  private extractMethod(node: any, code: string): MethodInfo {
    const name = this.getNodeText(node.childForFieldName('name'), code);
    const parameters = this.extractParameters(node.childForFieldName('parameters'), code);
    const returnType = this.getReturnType(node, code);
    const lineNumber = node.startPosition.row + 1;

    return {
      name,
      parameters,
      returnType,
      lineNumber,
      visibility: this.getVisibility(node)
    };
  }

  private extractProperty(node: any, code: string): PropertyInfo {
    const name = this.getNodeText(node.childForFieldName('name'), code);
    const type = this.getNodeText(node.childForFieldName('type'), code);
    const lineNumber = node.startPosition.row + 1;

    return {
      name,
      type,
      lineNumber,
      visibility: this.getVisibility(node)
    };
  }

  private getNodeText(node: any, code: string): string {
    if (!node) return '';
    return code.slice(node.startIndex, node.endIndex);
  }

  private getReturnType(node: any, code: string): string | undefined {
    const returnType = node.childForFieldName('return_type');
    return returnType ? this.getNodeText(returnType, code) : undefined;
  }

  private getVariableType(node: any, code: string): string | undefined {
    const type = node.childForFieldName('type');
    return type ? this.getNodeText(type, code) : undefined;
  }

  private getExtends(node: any, code: string): string | undefined {
    const extendsClause = node.childForFieldName('superclass');
    return extendsClause ? this.getNodeText(extendsClause, code) : undefined;
  }

  private getImplements(node: any, code: string): string[] {
    const implementsClause = node.childForFieldName('implements');
    if (!implementsClause) return [];

    const interfaces: string[] = [];
    for (const child of implementsClause.children) {
      if (child.type === 'type_identifier') {
        interfaces.push(this.getNodeText(child, code));
      }
    }
    return interfaces;
  }

  private hasModifier(node: any, modifier: string): boolean {
    // 检查节点是否有特定的修饰符
    return node.children?.some((child: any) => child.type === modifier) || false;
  }

  private getVisibility(node: any): 'public' | 'private' | 'protected' {
    if (this.hasModifier(node, 'private')) return 'private';
    if (this.hasModifier(node, 'protected')) return 'protected';
    return 'public';
  }
}

代码生成服务

typescript 复制代码
// src/services/generator/code-generator.ts
import { ChatOpenAI } from '@langchain/openai';
import { PromptTemplate } from '@langchain/core/prompts';
import { StringOutputParser } from '@langchain/core/output_parsers';

export interface CodeGenerationRequest {
  description: string;
  language: string;
  framework?: string;
  context?: string;
  requirements?: string[];
}

export interface CodeGenerationResponse {
  code: string;
  explanation: string;
  tests?: string;
  documentation?: string;
  suggestions?: string[];
}

export class CodeGenerator {
  private model: ChatOpenAI;

  constructor() {
    this.model = new ChatOpenAI({
      openAIApiKey: process.env.OPENAI_API_KEY,
      modelName: 'gpt-4',
      temperature: 0.3
    });
  }

  async generateCode(request: CodeGenerationRequest): Promise<CodeGenerationResponse> {
    const prompt = this.buildPrompt(request);
    const chain = prompt.pipe(this.model).pipe(new StringOutputParser());

    const response = await chain.invoke({
      description: request.description,
      language: request.language,
      framework: request.framework || 'vanilla',
      context: request.context || '',
      requirements: request.requirements?.join(', ') || ''
    });

    return this.parseResponse(response);
  }

  private buildPrompt(request: CodeGenerationRequest): PromptTemplate {
    const template = `
你是一个专业的${request.language}开发专家。请根据以下要求生成高质量的代码。

要求描述:{description}
编程语言:{language}
框架/库:{framework}
上下文信息:{context}
特殊要求:{requirements}

请提供:
1. 完整的代码实现
2. 代码解释和说明
3. 单元测试(如果适用)
4. 文档注释
5. 改进建议

请按照以下格式输出:

## 代码实现
\`\`\`${request.language}
[代码内容]
\`\`\`

## 代码解释
[详细解释]

## 单元测试
\`\`\`${request.language}
[测试代码]
\`\`\`

## 文档注释
[文档说明]

## 改进建议
[建议列表]
`;

    return PromptTemplate.fromTemplate(template);
  }

  private parseResponse(response: string): CodeGenerationResponse {
    const sections = this.extractSections(response);

    return {
      code: sections.code || '',
      explanation: sections.explanation || '',
      tests: sections.tests,
      documentation: sections.documentation,
      suggestions: sections.suggestions ? sections.suggestions.split('\n').filter(s => s.trim()) : []
    };
  }

  private extractSections(response: string): Record<string, string> {
    const sections: Record<string, string> = {};
    const lines = response.split('\n');
    let currentSection = '';
    let currentContent: string[] = [];

    for (const line of lines) {
      if (line.startsWith('## ')) {
        if (currentSection) {
          sections[currentSection] = currentContent.join('\n').trim();
        }
        currentSection = line.replace('## ', '').toLowerCase();
        currentContent = [];
      } else {
        currentContent.push(line);
      }
    }

    if (currentSection) {
      sections[currentSection] = currentContent.join('\n').trim();
    }

    return sections;
  }

  async generateFunction(
    description: string,
    language: string,
    parameters?: string[],
    returnType?: string
  ): Promise<string> {
    const prompt = PromptTemplate.fromTemplate(`
请生成一个${language}函数,要求如下:

功能描述:{description}
参数:{parameters}
返回值类型:{returnType}

请生成完整的函数代码,包括:
1. 函数签名
2. 参数验证
3. 核心逻辑
4. 错误处理
5. 文档注释

\`\`\`${language}
[函数代码]
\`\`\`
`);

    const chain = prompt.pipe(this.model).pipe(new StringOutputParser());
    const response = await chain.invoke({
      description,
      parameters: parameters?.join(', ') || '无',
      returnType: returnType || 'void'
    });

    return this.extractCodeFromResponse(response, language);
  }

  async generateClass(
    description: string,
    language: string,
    methods?: string[],
    properties?: string[]
  ): Promise<string> {
    const prompt = PromptTemplate.fromTemplate(`
请生成一个${language}类,要求如下:

类描述:{description}
方法:{methods}
属性:{properties}

请生成完整的类代码,包括:
1. 类定义
2. 构造函数
3. 所有方法实现
4. 属性定义
5. 文档注释

\`\`\`${language}
[类代码]
\`\`\`
`);

    const chain = prompt.pipe(this.model).pipe(new StringOutputParser());
    const response = await chain.invoke({
      description,
      methods: methods?.join(', ') || '无',
      properties: properties?.join(', ') || '无'
    });

    return this.extractCodeFromResponse(response, language);
  }

  async generateTests(
    code: string,
    language: string,
    testFramework?: string
  ): Promise<string> {
    const prompt = PromptTemplate.fromTemplate(`
请为以下${language}代码生成单元测试:

代码:
\`\`\`${language}
{code}
\`\`\`

测试框架:{testFramework}

请生成完整的测试代码,包括:
1. 正常情况测试
2. 边界条件测试
3. 异常情况测试
4. 测试覆盖率考虑

\`\`\`${language}
[测试代码]
\`\`\`
`);

    const chain = prompt.pipe(this.model).pipe(new StringOutputParser());
    const response = await chain.invoke({
      code,
      testFramework: testFramework || 'Jest'
    });

    return this.extractCodeFromResponse(response, language);
  }

  private extractCodeFromResponse(response: string, language: string): string {
    const codeBlockRegex = new RegExp(`\`\`\`${language}\\s*([\\s\\S]*?)\`\`\``, 'g');
    const match = codeBlockRegex.exec(response);
    return match ? match[1].trim() : '';
  }
}

代码审查服务

typescript 复制代码
// src/services/reviewer/code-reviewer.ts
import { ChatOpenAI } from '@langchain/openai';
import { PromptTemplate } from '@langchain/core/prompts';
import { StringOutputParser } from '@langchain/core/output_parsers';

export interface CodeReviewRequest {
  code: string;
  language: string;
  context?: string;
  focusAreas?: string[];
}

export interface CodeReviewResponse {
  score: number;
  issues: CodeIssue[];
  suggestions: CodeSuggestion[];
  summary: string;
  metrics: CodeMetrics;
}

export interface CodeIssue {
  type: 'bug' | 'vulnerability' | 'code_smell' | 'performance' | 'security' | 'style';
  severity: 'low' | 'medium' | 'high' | 'critical';
  message: string;
  lineNumber?: number;
  rule?: string;
  fix?: string;
}

export interface CodeSuggestion {
  type: 'refactor' | 'optimize' | 'improve' | 'add_feature';
  description: string;
  priority: 'low' | 'medium' | 'high';
  effort: 'low' | 'medium' | 'high';
}

export interface CodeMetrics {
  complexity: number;
  maintainability: number;
  testability: number;
  readability: number;
  performance: number;
}

export class CodeReviewer {
  private model: ChatOpenAI;

  constructor() {
    this.model = new ChatOpenAI({
      openAIApiKey: process.env.OPENAI_API_KEY,
      modelName: 'gpt-4',
      temperature: 0.1
    });
  }

  async reviewCode(request: CodeReviewRequest): Promise<CodeReviewResponse> {
    const prompt = this.buildReviewPrompt(request);
    const chain = prompt.pipe(this.model).pipe(new StringOutputParser());

    const response = await chain.invoke({
      code: request.code,
      language: request.language,
      context: request.context || '',
      focusAreas: request.focusAreas?.join(', ') || 'all'
    });

    return this.parseReviewResponse(response);
  }

  private buildReviewPrompt(request: CodeReviewRequest): PromptTemplate {
    const template = `
你是一个专业的${request.language}代码审查专家。请对以下代码进行全面审查。

代码:
\`\`\`${request.language}
{code}
\`\`\`

上下文:{context}
重点关注:{focusAreas}

请从以下方面进行审查:
1. 代码质量和可读性
2. 性能优化建议
3. 安全性检查
4. 最佳实践遵循
5. 潜在的 bug 和问题
6. 代码结构和设计模式

请按照以下 JSON 格式输出审查结果:

{
  "score": 85,
  "issues": [
    {
      "type": "bug",
      "severity": "medium",
      "message": "潜在的空指针异常",
      "lineNumber": 15,
      "rule": "null-check",
      "fix": "添加空值检查"
    }
  ],
  "suggestions": [
    {
      "type": "refactor",
      "description": "提取重复代码到独立方法",
      "priority": "medium",
      "effort": "low"
    }
  ],
  "summary": "代码整体质量良好,但存在一些可以改进的地方...",
  "metrics": {
    "complexity": 7,
    "maintainability": 8,
    "testability": 6,
    "readability": 9,
    "performance": 7
  }
}
`;

    return PromptTemplate.fromTemplate(template);
  }

  private parseReviewResponse(response: string): CodeReviewResponse {
    try {
      // 提取 JSON 部分
      const jsonMatch = response.match(/\{[\s\S]*\}/);
      if (!jsonMatch) {
        throw new Error('无法解析审查结果');
      }

      const result = JSON.parse(jsonMatch[0]);
      return {
        score: result.score || 0,
        issues: result.issues || [],
        suggestions: result.suggestions || [],
        summary: result.summary || '',
        metrics: result.metrics || {
          complexity: 0,
          maintainability: 0,
          testability: 0,
          readability: 0,
          performance: 0
        }
      };
    } catch (error) {
      console.error('解析审查结果失败:', error);
      return {
        score: 0,
        issues: [],
        suggestions: [],
        summary: '审查结果解析失败',
        metrics: {
          complexity: 0,
          maintainability: 0,
          testability: 0,
          readability: 0,
          performance: 0
        }
      };
    }
  }

  async analyzeSecurity(code: string, language: string): Promise<CodeIssue[]> {
    const prompt = PromptTemplate.fromTemplate(`
请分析以下${language}代码的安全漏洞:

\`\`\`${language}
{code}
\`\`\`

重点关注:
1. SQL 注入
2. XSS 攻击
3. 代码注入
4. 敏感信息泄露
5. 权限绕过
6. 输入验证不足

请列出发现的安全问题:
`);

    const chain = prompt.pipe(this.model).pipe(new StringOutputParser());
    const response = await chain.invoke({ code });

    return this.parseSecurityIssues(response);
  }

  async analyzePerformance(code: string, language: string): Promise<CodeIssue[]> {
    const prompt = PromptTemplate.fromTemplate(`
请分析以下${language}代码的性能问题:

\`\`\`${language}
{code}
\`\`\`

重点关注:
1. 算法复杂度
2. 内存使用
3. 循环优化
4. 数据库查询
5. 网络请求
6. 缓存策略

请列出发现的性能问题:
`);

    const chain = prompt.pipe(this.model).pipe(new StringOutputParser());
    const response = await chain.invoke({ code });

    return this.parsePerformanceIssues(response);
  }

  private parseSecurityIssues(response: string): CodeIssue[] {
    const issues: CodeIssue[] = [];
    const lines = response.split('\n');

    for (const line of lines) {
      if (line.trim() && !line.startsWith('#')) {
        issues.push({
          type: 'security',
          severity: 'medium',
          message: line.trim(),
          rule: 'security-check'
        });
      }
    }

    return issues;
  }

  private parsePerformanceIssues(response: string): CodeIssue[] {
    const issues: CodeIssue[] = [];
    const lines = response.split('\n');

    for (const line of lines) {
      if (line.trim() && !line.startsWith('#')) {
        issues.push({
          type: 'performance',
          severity: 'medium',
          message: line.trim(),
          rule: 'performance-check'
        });
      }
    }

    return issues;
  }
}

LangGraph 代码审查工作流

typescript 复制代码
// src/services/workflow/code-review-workflow.ts
import { StateGraph, END } from '@langchain/langgraph';
import { CodeParser } from '../parser/code-parser';
import { CodeReviewer } from '../reviewer/code-reviewer';
import { CodeGenerator } from '../generator/code-generator';

export interface CodeReviewState {
  code: string;
  language: string;
  context?: string;
  status: 'parsing' | 'analyzing' | 'reviewing' | 'generating_fixes' | 'completed' | 'failed';
  structure?: any;
  issues?: any[];
  suggestions?: any[];
  fixes?: any[];
  error?: string;
  progress: number;
}

export class CodeReviewWorkflow {
  private parser: CodeParser;
  private reviewer: CodeReviewer;
  private generator: CodeGenerator;

  constructor() {
    this.parser = new CodeParser();
    this.reviewer = new CodeReviewer();
    this.generator = new CodeGenerator();
  }

  async buildGraph() {
    const workflow = new StateGraph<CodeReviewState>({
      channels: {
        code: { value: '' },
        language: { value: '' },
        context: { value: '' },
        status: { value: 'parsing' },
        structure: { value: {} },
        issues: { value: [] },
        suggestions: { value: [] },
        fixes: { value: [] },
        error: { value: '' },
        progress: { value: 0 }
      }
    });

    // 添加节点
    workflow.addNode('parse', this.parseCode.bind(this));
    workflow.addNode('analyze', this.analyzeCode.bind(this));
    workflow.addNode('review', this.reviewCode.bind(this));
    workflow.addNode('generate_fixes', this.generateFixes.bind(this));
    workflow.addNode('complete', this.completeReview.bind(this));
    workflow.addNode('fail', this.handleFailure.bind(this));

    // 添加边
    workflow.addEdge('start', 'parse');
    workflow.addConditionalEdges('parse', this.checkParseResult.bind(this));
    workflow.addConditionalEdges('analyze', this.checkAnalyzeResult.bind(this));
    workflow.addConditionalEdges('review', this.checkReviewResult.bind(this));
    workflow.addEdge('generate_fixes', 'complete');
    workflow.addEdge('complete', END);
    workflow.addEdge('fail', END);

    return workflow.compile();
  }

  private async parseCode(state: CodeReviewState): Promise<Partial<CodeReviewState>> {
    try {
      const structure = await this.parser.parseCode(state.code, state.language);
      return {
        status: 'analyzing',
        structure,
        progress: 25
      };
    } catch (error) {
      return {
        status: 'failed',
        error: error.message,
        progress: 0
      };
    }
  }

  private async analyzeCode(state: CodeReviewState): Promise<Partial<CodeReviewState>> {
    try {
      const reviewResult = await this.reviewer.reviewCode({
        code: state.code,
        language: state.language,
        context: state.context
      });

      return {
        status: 'reviewing',
        issues: reviewResult.issues,
        suggestions: reviewResult.suggestions,
        progress: 50
      };
    } catch (error) {
      return {
        status: 'failed',
        error: error.message,
        progress: 25
      };
    }
  }

  private async reviewCode(state: CodeReviewState): Promise<Partial<CodeReviewState>> {
    try {
      // 进行更深入的代码审查
      const securityIssues = await this.reviewer.analyzeSecurity(state.code, state.language);
      const performanceIssues = await this.reviewer.analyzePerformance(state.code, state.language);

      const allIssues = [
        ...(state.issues || []),
        ...securityIssues,
        ...performanceIssues
      ];

      return {
        status: 'generating_fixes',
        issues: allIssues,
        progress: 75
      };
    } catch (error) {
      return {
        status: 'failed',
        error: error.message,
        progress: 50
      };
    }
  }

  private async generateFixes(state: CodeReviewState): Promise<Partial<CodeReviewState>> {
    try {
      const fixes = [];

      for (const issue of state.issues || []) {
        if (issue.fix) {
          const fixCode = await this.generator.generateFunction(
            issue.fix,
            state.language,
            [],
            'void'
          );

          fixes.push({
            issue,
            fixCode,
            description: issue.fix
          });
        }
      }

      return {
        status: 'completed',
        fixes,
        progress: 100
      };
    } catch (error) {
      return {
        status: 'failed',
        error: error.message,
        progress: 75
      };
    }
  }

  private async completeReview(state: CodeReviewState): Promise<Partial<CodeReviewState>> {
    return {
      status: 'completed',
      progress: 100
    };
  }

  private async handleFailure(state: CodeReviewState): Promise<Partial<CodeReviewState>> {
    return {
      status: 'failed',
      progress: 0
    };
  }

  private checkParseResult(state: CodeReviewState): string {
    return state.status === 'failed' ? 'fail' : 'analyze';
  }

  private checkAnalyzeResult(state: CodeReviewState): string {
    return state.status === 'failed' ? 'fail' : 'review';
  }

  private checkReviewResult(state: CodeReviewState): string {
    return state.status === 'failed' ? 'fail' : 'generate_fixes';
  }
}

🌐 Next.js API 路由实现

代码分析 API

typescript 复制代码
// src/app/api/code/analyze/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { CodeParser } from '@/services/parser/code-parser';
import { prisma } from '@/lib/db';

export async function POST(request: NextRequest) {
  try {
    const { code, language, fileId } = await request.json();

    if (!code || !language) {
      return NextResponse.json(
        { error: '缺少必要参数' },
        { status: 400 }
      );
    }

    const parser = new CodeParser();
    const structure = await parser.parseCode(code, language);

    // 保存分析结果到数据库
    if (fileId) {
      await prisma.codeFile.update({
        where: { id: fileId },
        data: {
          content: code,
          language,
          lines: code.split('\n').length,
          size: Buffer.byteLength(code, 'utf8')
        }
      });
    }

    return NextResponse.json({
      success: true,
      structure
    });

  } catch (error) {
    console.error('代码分析失败:', error);
    return NextResponse.json(
      { error: '代码分析失败' },
      { status: 500 }
    );
  }
}

代码生成 API

typescript 复制代码
// src/app/api/code/generate/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { CodeGenerator } from '@/services/generator/code-generator';

export async function POST(request: NextRequest) {
  try {
    const { description, language, framework, context, requirements } = await request.json();

    if (!description || !language) {
      return NextResponse.json(
        { error: '缺少必要参数' },
        { status: 400 }
      );
    }

    const generator = new CodeGenerator();
    const result = await generator.generateCode({
      description,
      language,
      framework,
      context,
      requirements
    });

    return NextResponse.json({
      success: true,
      result
    });

  } catch (error) {
    console.error('代码生成失败:', error);
    return NextResponse.json(
      { error: '代码生成失败' },
      { status: 500 }
    );
  }
}

代码审查 API

typescript 复制代码
// src/app/api/code/review/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { CodeReviewWorkflow } from '@/services/workflow/code-review-workflow';
import { prisma } from '@/lib/db';

export async function POST(request: NextRequest) {
  try {
    const { code, language, context, fileId, userId } = await request.json();

    if (!code || !language || !userId) {
      return NextResponse.json(
        { error: '缺少必要参数' },
        { status: 400 }
      );
    }

    // 启动代码审查工作流
    const workflow = new CodeReviewWorkflow();
    const graph = await workflow.buildGraph();

    const result = await graph.invoke({
      code,
      language,
      context,
      status: 'parsing',
      progress: 0
    });

    // 保存审查结果到数据库
    const review = await prisma.codeReview.create({
      data: {
        title: `代码审查 - ${language}`,
        description: context || '自动代码审查',
        status: 'COMPLETED',
        score: this.calculateScore(result.issues || []),
        userId,
        fileId,
        metadata: {
          issues: result.issues,
          suggestions: result.suggestions,
          fixes: result.fixes
        }
      }
    });

    // 保存问题到数据库
    if (result.issues && fileId) {
      await Promise.all(
        result.issues.map(issue =>
          prisma.codeIssue.create({
            data: {
              type: issue.type.toUpperCase(),
              severity: issue.severity.toUpperCase(),
              message: issue.message,
              lineNumber: issue.lineNumber,
              rule: issue.rule,
              fix: issue.fix,
              fileId,
              reviewId: review.id
            }
          })
        )
      );
    }

    return NextResponse.json({
      success: true,
      review: {
        id: review.id,
        score: review.score,
        issues: result.issues,
        suggestions: result.suggestions,
        fixes: result.fixes
      }
    });

  } catch (error) {
    console.error('代码审查失败:', error);
    return NextResponse.json(
      { error: '代码审查失败' },
      { status: 500 }
    );
  }

  private calculateScore(issues: any[]): number {
    if (issues.length === 0) return 100;

    let score = 100;
    for (const issue of issues) {
      switch (issue.severity) {
        case 'critical':
          score -= 20;
          break;
        case 'high':
          score -= 15;
          break;
        case 'medium':
          score -= 10;
          break;
        case 'low':
          score -= 5;
          break;
      }
    }

    return Math.max(0, score);
  }
}

流式代码生成 API

typescript 复制代码
// src/app/api/code/generate/stream/route.ts
import { NextRequest } from 'next/server';
import { ChatOpenAI } from '@langchain/openai';
import { PromptTemplate } from '@langchain/core/prompts';

export const runtime = 'edge';

export async function POST(request: NextRequest) {
  try {
    const { description, language, framework } = await request.json();

    const encoder = new TextEncoder();
    const stream = new ReadableStream({
      async start(controller) {
        try {
          // 发送开始信号
          controller.enqueue(
            encoder.encode(`data: ${JSON.stringify({ type: 'start' })}\n\n`)
          );

          // 初始化流式模型
          const model = new ChatOpenAI({
            openAIApiKey: process.env.OPENAI_API_KEY,
            modelName: 'gpt-4',
            temperature: 0.3,
            streaming: true
          });

          const promptTemplate = PromptTemplate.fromTemplate(`
请生成${language}代码,要求如下:

功能描述:{description}
框架/库:{framework}

请生成完整的代码实现,包括:
1. 主要功能代码
2. 必要的导入和依赖
3. 错误处理
4. 文档注释

请直接输出代码,不要额外的解释:
`);

          const chain = promptTemplate.pipe(model);

          // 流式生成代码
          const stream = await chain.stream({
            description,
            framework: framework || 'vanilla'
          });

          let fullCode = '';
          for await (const chunk of stream) {
            const content = chunk.content;
            fullCode += content;

            controller.enqueue(
              encoder.encode(`data: ${JSON.stringify({
                type: 'chunk',
                data: { content }
              })}\n\n`)
            );
          }

          // 发送完成信号
          controller.enqueue(
            encoder.encode(`data: ${JSON.stringify({
              type: 'complete',
              data: { code: fullCode }
            })}\n\n`)
          );

        } catch (error) {
          controller.enqueue(
            encoder.encode(`data: ${JSON.stringify({
              type: 'error',
              data: { message: error.message }
            })}\n\n`)
          );
        } finally {
          controller.close();
        }
      }
    });

    return new Response(stream, {
      headers: {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
      },
    });

  } catch (error) {
    return new Response(
      JSON.stringify({ error: '流式代码生成失败' }),
      { status: 500 }
    );
  }
}

🎨 前端界面实现

代码编辑器组件

tsx 复制代码
// src/components/editor/CodeEditor.tsx
'use client';

import { useState, useRef, useEffect } from 'react';
import Editor from '@monaco-editor/react';
import { Play, Save, Download, Upload, Settings } from 'lucide-react';

interface CodeEditorProps {
  language: string;
  value: string;
  onChange: (value: string) => void;
  onSave?: () => void;
  onRun?: () => void;
  readOnly?: boolean;
}

export default function CodeEditor({
  language,
  value,
  onChange,
  onSave,
  onRun,
  readOnly = false
}: CodeEditorProps) {
  const [theme, setTheme] = useState('vs-dark');
  const [fontSize, setFontSize] = useState(14);
  const [wordWrap, setWordWrap] = useState('on');
  const editorRef = useRef<any>(null);

  const handleEditorDidMount = (editor: any, monaco: any) => {
    editorRef.current = editor;

    // 配置编辑器选项
    editor.updateOptions({
      fontSize,
      wordWrap,
      minimap: { enabled: true },
      scrollBeyondLastLine: false,
      automaticLayout: true,
      tabSize: 2,
      insertSpaces: true,
      renderWhitespace: 'selection',
      bracketPairColorization: { enabled: true },
      guides: {
        bracketPairs: true,
        indentation: true
      }
    });

    // 添加键盘快捷键
    editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
      onSave?.();
    });

    editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, () => {
      onRun?.();
    });
  };

  const handleEditorChange = (value: string | undefined) => {
    onChange(value || '');
  };

  const formatCode = () => {
    if (editorRef.current) {
      editorRef.current.getAction('editor.action.formatDocument')?.run();
    }
  };

  const downloadCode = () => {
    const blob = new Blob([value], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `code.${language}`;
    a.click();
    URL.revokeObjectURL(url);
  };

  const uploadCode = () => {
    const input = document.createElement('input');
    input.type = 'file';
    input.accept = `.${language}`;
    input.onchange = (e) => {
      const file = (e.target as HTMLInputElement).files?.[0];
      if (file) {
        const reader = new FileReader();
        reader.onload = (e) => {
          const content = e.target?.result as string;
          onChange(content);
        };
        reader.readAsText(file);
      }
    };
    input.click();
  };

  return (
    <div className="flex flex-col h-full">
      {/* 工具栏 */}
      <div className="flex items-center justify-between p-2 border-b bg-gray-50">
        <div className="flex items-center space-x-2">
          <span className="text-sm font-medium text-gray-700">
            {language.toUpperCase()}
          </span>
          <div className="flex items-center space-x-1">
            <button
              onClick={formatCode}
              className="p-1 hover:bg-gray-200 rounded"
              title="格式化代码"
            >
              <Settings className="w-4 h-4" />
            </button>
          </div>
        </div>

        <div className="flex items-center space-x-2">
          <button
            onClick={uploadCode}
            className="p-1 hover:bg-gray-200 rounded"
            title="上传文件"
          >
            <Upload className="w-4 h-4" />
          </button>
          <button
            onClick={downloadCode}
            className="p-1 hover:bg-gray-200 rounded"
            title="下载代码"
          >
            <Download className="w-4 h-4" />
          </button>
          {onSave && (
            <button
              onClick={onSave}
              className="p-1 hover:bg-gray-200 rounded"
              title="保存代码"
            >
              <Save className="w-4 h-4" />
            </button>
          )}
          {onRun && (
            <button
              onClick={onRun}
              className="p-1 hover:bg-gray-200 rounded"
              title="运行代码"
            >
              <Play className="w-4 h-4" />
            </button>
          )}
        </div>
      </div>

      {/* 编辑器 */}
      <div className="flex-1">
        <Editor
          height="100%"
          language={language}
          value={value}
          onChange={handleEditorChange}
          onMount={handleEditorDidMount}
          theme={theme}
          options={{
            readOnly,
            fontSize,
            wordWrap,
            minimap: { enabled: true },
            scrollBeyondLastLine: false,
            automaticLayout: true,
            tabSize: 2,
            insertSpaces: true,
            renderWhitespace: 'selection',
            bracketPairColorization: { enabled: true },
            guides: {
              bracketPairs: true,
              indentation: true
            }
          }}
        />
      </div>
    </div>
  );
}

代码审查界面组件

tsx 复制代码
// src/components/review/CodeReviewInterface.tsx
'use client';

import { useState, useEffect } from 'react';
import { CheckCircle, XCircle, AlertTriangle, Info, Code, FileText } from 'lucide-react';

interface CodeReviewInterfaceProps {
  reviewId: string;
}

interface ReviewData {
  id: string;
  score: number;
  issues: CodeIssue[];
  suggestions: CodeSuggestion[];
  summary: string;
  metrics: CodeMetrics;
}

interface CodeIssue {
  type: string;
  severity: string;
  message: string;
  lineNumber?: number;
  rule?: string;
  fix?: string;
}

interface CodeSuggestion {
  type: string;
  description: string;
  priority: string;
  effort: string;
}

interface CodeMetrics {
  complexity: number;
  maintainability: number;
  testability: number;
  readability: number;
  performance: number;
}

export default function CodeReviewInterface({ reviewId }: CodeReviewInterfaceProps) {
  const [reviewData, setReviewData] = useState<ReviewData | null>(null);
  const [loading, setLoading] = useState(true);
  const [selectedIssue, setSelectedIssue] = useState<CodeIssue | null>(null);

  useEffect(() => {
    fetchReviewData();
  }, [reviewId]);

  const fetchReviewData = async () => {
    try {
      const response = await fetch(`/api/code/review/${reviewId}`);
      const data = await response.json();

      if (data.success) {
        setReviewData(data.review);
      }
    } catch (error) {
      console.error('获取审查数据失败:', error);
    } finally {
      setLoading(false);
    }
  };

  const getSeverityIcon = (severity: string) => {
    switch (severity) {
      case 'critical':
        return <XCircle className="w-5 h-5 text-red-500" />;
      case 'high':
        return <AlertTriangle className="w-5 h-5 text-orange-500" />;
      case 'medium':
        return <AlertTriangle className="w-5 h-5 text-yellow-500" />;
      case 'low':
        return <Info className="w-5 h-5 text-blue-500" />;
      default:
        return <Info className="w-5 h-5 text-gray-500" />;
    }
  };

  const getSeverityColor = (severity: string) => {
    switch (severity) {
      case 'critical':
        return 'bg-red-50 border-red-200';
      case 'high':
        return 'bg-orange-50 border-orange-200';
      case 'medium':
        return 'bg-yellow-50 border-yellow-200';
      case 'low':
        return 'bg-blue-50 border-blue-200';
      default:
        return 'bg-gray-50 border-gray-200';
    }
  };

  const getScoreColor = (score: number) => {
    if (score >= 90) return 'text-green-600';
    if (score >= 70) return 'text-yellow-600';
    return 'text-red-600';
  };

  if (loading) {
    return (
      <div className="flex items-center justify-center h-64">
        <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
      </div>
    );
  }

  if (!reviewData) {
    return (
      <div className="text-center py-8">
        <p className="text-gray-500">无法加载审查数据</p>
      </div>
    );
  }

  return (
    <div className="max-w-6xl mx-auto p-6">
      {/* 审查概览 */}
      <div className="bg-white rounded-lg shadow-sm border p-6 mb-6">
        <div className="flex items-center justify-between mb-4">
          <h1 className="text-2xl font-bold text-gray-900">代码审查报告</h1>
          <div className={`text-3xl font-bold ${getScoreColor(reviewData.score)}`}>
            {reviewData.score}/100
          </div>
        </div>

        <p className="text-gray-600 mb-4">{reviewData.summary}</p>

        {/* 指标卡片 */}
        <div className="grid grid-cols-5 gap-4">
          <div className="text-center">
            <div className="text-2xl font-bold text-blue-600">{reviewData.metrics.complexity}</div>
            <div className="text-sm text-gray-500">复杂度</div>
          </div>
          <div className="text-center">
            <div className="text-2xl font-bold text-green-600">{reviewData.metrics.maintainability}</div>
            <div className="text-sm text-gray-500">可维护性</div>
          </div>
          <div className="text-center">
            <div className="text-2xl font-bold text-purple-600">{reviewData.metrics.testability}</div>
            <div className="text-sm text-gray-500">可测试性</div>
          </div>
          <div className="text-center">
            <div className="text-2xl font-bold text-orange-600">{reviewData.metrics.readability}</div>
            <div className="text-sm text-gray-500">可读性</div>
          </div>
          <div className="text-center">
            <div className="text-2xl font-bold text-red-600">{reviewData.metrics.performance}</div>
            <div className="text-sm text-gray-500">性能</div>
          </div>
        </div>
      </div>

      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
        {/* 问题列表 */}
        <div className="bg-white rounded-lg shadow-sm border">
          <div className="p-4 border-b">
            <h2 className="text-lg font-semibold text-gray-900 flex items-center">
              <AlertTriangle className="w-5 h-5 mr-2" />
              发现的问题 ({reviewData.issues.length})
            </h2>
          </div>

          <div className="p-4 space-y-3">
            {reviewData.issues.map((issue, index) => (
              <div
                key={index}
                className={`p-3 rounded-lg border cursor-pointer transition-colors ${getSeverityColor(issue.severity)}`}
                onClick={() => setSelectedIssue(issue)}
              >
                <div className="flex items-start space-x-3">
                  {getSeverityIcon(issue.severity)}
                  <div className="flex-1">
                    <div className="flex items-center justify-between">
                      <span className="font-medium text-gray-900 capitalize">
                        {issue.type.replace('_', ' ')}
                      </span>
                      {issue.lineNumber && (
                        <span className="text-sm text-gray-500">
                          第 {issue.lineNumber} 行
                        </span>
                      )}
                    </div>
                    <p className="text-sm text-gray-600 mt-1">{issue.message}</p>
                    {issue.rule && (
                      <span className="text-xs text-gray-400 mt-1 block">
                        规则: {issue.rule}
                      </span>
                    )}
                  </div>
                </div>
              </div>
            ))}
          </div>
        </div>

        {/* 建议列表 */}
        <div className="bg-white rounded-lg shadow-sm border">
          <div className="p-4 border-b">
            <h2 className="text-lg font-semibold text-gray-900 flex items-center">
              <FileText className="w-5 h-5 mr-2" />
              改进建议 ({reviewData.suggestions.length})
            </h2>
          </div>

          <div className="p-4 space-y-3">
            {reviewData.suggestions.map((suggestion, index) => (
              <div key={index} className="p-3 bg-blue-50 rounded-lg border border-blue-200">
                <div className="flex items-start space-x-3">
                  <Code className="w-5 h-5 text-blue-500 mt-0.5" />
                  <div className="flex-1">
                    <div className="flex items-center justify-between">
                      <span className="font-medium text-gray-900 capitalize">
                        {suggestion.type.replace('_', ' ')}
                      </span>
                      <div className="flex space-x-2">
                        <span className={`text-xs px-2 py-1 rounded ${
                          suggestion.priority === 'high' ? 'bg-red-100 text-red-600' :
                          suggestion.priority === 'medium' ? 'bg-yellow-100 text-yellow-600' :
                          'bg-green-100 text-green-600'
                        }`}>
                          {suggestion.priority}
                        </span>
                        <span className={`text-xs px-2 py-1 rounded ${
                          suggestion.effort === 'high' ? 'bg-red-100 text-red-600' :
                          suggestion.effort === 'medium' ? 'bg-yellow-100 text-yellow-600' :
                          'bg-green-100 text-green-600'
                        }`}>
                          {suggestion.effort} effort
                        </span>
                      </div>
                    </div>
                    <p className="text-sm text-gray-600 mt-1">{suggestion.description}</p>
                  </div>
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>

      {/* 问题详情模态框 */}
      {selectedIssue && (
        <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
          <div className="bg-white rounded-lg max-w-2xl w-full max-h-96 overflow-y-auto">
            <div className="p-6">
              <div className="flex items-center justify-between mb-4">
                <h3 className="text-lg font-semibold text-gray-900">问题详情</h3>
                <button
                  onClick={() => setSelectedIssue(null)}
                  className="text-gray-400 hover:text-gray-600"
                >
                  <XCircle className="w-6 h-6" />
                </button>
              </div>

              <div className="space-y-4">
                <div>
                  <label className="block text-sm font-medium text-gray-700 mb-1">
                    问题类型
                  </label>
                  <p className="text-sm text-gray-900 capitalize">
                    {selectedIssue.type.replace('_', ' ')}
                  </p>
                </div>

                <div>
                  <label className="block text-sm font-medium text-gray-700 mb-1">
                    严重程度
                  </label>
                  <div className="flex items-center space-x-2">
                    {getSeverityIcon(selectedIssue.severity)}
                    <span className="text-sm text-gray-900 capitalize">
                      {selectedIssue.severity}
                    </span>
                  </div>
                </div>

                <div>
                  <label className="block text-sm font-medium text-gray-700 mb-1">
                    问题描述
                  </label>
                  <p className="text-sm text-gray-900">{selectedIssue.message}</p>
                </div>

                {selectedIssue.lineNumber && (
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      行号
                    </label>
                    <p className="text-sm text-gray-900">第 {selectedIssue.lineNumber} 行</p>
                  </div>
                )}

                {selectedIssue.rule && (
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      规则
                    </label>
                    <p className="text-sm text-gray-900">{selectedIssue.rule}</p>
                  </div>
                )}

                {selectedIssue.fix && (
                  <div>
                    <label className="block text-sm font-medium text-gray-700 mb-1">
                      修复建议
                    </label>
                    <p className="text-sm text-gray-900">{selectedIssue.fix}</p>
                  </div>
                )}
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

🐳 Docker 部署配置

Docker Compose 配置

yaml 复制代码
# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://postgres:password@postgres:5432/code_assistant
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - CHROMA_URL=http://chroma:8000
      - REDIS_URL=redis://redis:6379
    depends_on:
      - postgres
      - chroma
      - redis
    volumes:
      - ./uploads:/app/uploads

  postgres:
    image: postgres:15
    environment:
      - POSTGRES_DB=code_assistant
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  chroma:
    image: chromadb/chroma:latest
    ports:
      - "8000:8000"
    volumes:
      - chroma_data:/chroma/chroma
    environment:
      - CHROMA_SERVER_HOST=0.0.0.0

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - app

volumes:
  postgres_data:
  chroma_data:
  redis_data:

📊 性能优化与监控

代码分析缓存

typescript 复制代码
// src/lib/code-cache.ts
import Redis from 'ioredis';

const redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379');

export class CodeCache {
  async getCodeAnalysis(codeHash: string): Promise<any | null> {
    try {
      const cached = await redis.get(`code_analysis:${codeHash}`);
      return cached ? JSON.parse(cached) : null;
    } catch (error) {
      console.error('获取代码分析缓存失败:', error);
      return null;
    }
  }

  async setCodeAnalysis(codeHash: string, analysis: any, ttl: number = 3600): Promise<void> {
    try {
      await redis.setex(`code_analysis:${codeHash}`, ttl, JSON.stringify(analysis));
    } catch (error) {
      console.error('设置代码分析缓存失败:', error);
    }
  }

  async getCodeReview(codeHash: string): Promise<any | null> {
    try {
      const cached = await redis.get(`code_review:${codeHash}`);
      return cached ? JSON.parse(cached) : null;
    } catch (error) {
      console.error('获取代码审查缓存失败:', error);
      return null;
    }
  }

  async setCodeReview(codeHash: string, review: any, ttl: number = 1800): Promise<void> {
    try {
      await redis.setex(`code_review:${codeHash}`, ttl, JSON.stringify(review));
    } catch (error) {
      console.error('设置代码审查缓存失败:', error);
    }
  }

  generateCodeHash(code: string, language: string): string {
    const crypto = require('crypto');
    return crypto.createHash('md5').update(`${code}:${language}`).digest('hex');
  }
}

🧪 测试策略

代码解析测试

typescript 复制代码
// src/services/parser/__tests__/code-parser.test.ts
import { CodeParser } from '../code-parser';

describe('CodeParser', () => {
  let parser: CodeParser;

  beforeEach(() => {
    parser = new CodeParser();
  });

  describe('parseCode', () => {
    it('should parse JavaScript function correctly', async () => {
      const code = `
function calculateSum(a, b) {
  return a + b;
}
`;

      const result = await parser.parseCode(code, 'javascript');

      expect(result.functions).toHaveLength(1);
      expect(result.functions[0].name).toBe('calculateSum');
      expect(result.functions[0].parameters).toHaveLength(2);
      expect(result.functions[0].lineNumber).toBe(2);
    });

    it('should parse TypeScript class correctly', async () => {
      const code = `
class Calculator {
  private value: number = 0;

  add(num: number): number {
    this.value += num;
    return this.value;
  }
}
`;

      const result = await parser.parseCode(code, 'typescript');

      expect(result.classes).toHaveLength(1);
      expect(result.classes[0].name).toBe('Calculator');
      expect(result.classes[0].methods).toHaveLength(1);
      expect(result.classes[0].properties).toHaveLength(1);
    });
  });
});

代码生成测试

typescript 复制代码
// src/services/generator/__tests__/code-generator.test.ts
import { CodeGenerator } from '../code-generator';

describe('CodeGenerator', () => {
  let generator: CodeGenerator;

  beforeEach(() => {
    generator = new CodeGenerator();
  });

  describe('generateCode', () => {
    it('should generate JavaScript function', async () => {
      const request = {
        description: '创建一个计算两个数之和的函数',
        language: 'javascript',
        framework: 'vanilla'
      };

      const result = await generator.generateCode(request);

      expect(result.code).toBeDefined();
      expect(result.code.length).toBeGreaterThan(0);
      expect(result.explanation).toBeDefined();
    });

    it('should generate TypeScript class', async () => {
      const request = {
        description: '创建一个用户管理类',
        language: 'typescript',
        framework: 'vanilla',
        requirements: ['添加用户', '删除用户', '更新用户']
      };

      const result = await generator.generateCode(request);

      expect(result.code).toBeDefined();
      expect(result.code).toContain('class');
      expect(result.tests).toBeDefined();
    });
  });
});

📚 本章总结

通过本章学习,我们完成了:

系统架构设计

  • 设计了完整的 AI 驱动代码助手系统架构
  • 实现了多语言代码解析和智能分析功能
  • 集成了 LangGraph 工作流自动化代码审查

技术实现

  • 使用 Tree-sitter 实现多语言代码解析
  • 基于 LangChain.js 构建代码生成和审查系统
  • 搭建了 Next.js 全栈应用和现代化代码编辑器

工程实践

  • 配置了 Docker 容器化部署
  • 实现了代码分析缓存和性能优化
  • 建立了完整的测试体系

生产就绪

  • 实现了代码质量评估和安全检测
  • 添加了性能监控和错误处理
  • 提供了完整的部署和运维方案

🎯 下章预告

在下一章《实战综合项目三:个性化学习助手平台》中,我们将:

  • 构建智能个性化学习系统
  • 实现学习路径推荐和进度跟踪
  • 集成多种学习资源和评估方式
  • 开发自适应学习算法和知识图谱

最后感谢阅读!欢迎关注我,微信公众号:《鲫小鱼不正经》。欢迎点赞、收藏、关注,一键三连!!!

相关推荐
井柏然4 小时前
从 Monorepo 重温 ESM 的模块化机制
前端·javascript·前端工程化
晓得迷路了4 小时前
栗子前端技术周刊第 102 期 - Vite+ 正式发布、React Native 0.82、Nitro v3 alpha 版...
前端·javascript·vite
XXX-X-XXJ4 小时前
Vue Router完全指南 —— 从基础配置到权限控制
前端·javascript·vue.js
云和数据.ChenGuang4 小时前
vue钩子函数调用问题
前端·javascript·vue.js
鹏多多4 小时前
React动画方案对比:CSS动画和Framer Motion和React Spring
前端·javascript·react.js
亿元程序员5 小时前
8年游戏主程,一篇文章,多少收益?
前端
IT_陈寒5 小时前
5个Java 21新特性实战技巧,让你的代码性能飙升200%!
前端·人工智能·后端
咖啡の猫5 小时前
Vue内置指令与自定义指令
前端·javascript·vue.js
昔人'5 小时前
使用css `focus-visible` 改善用户体验
前端·css·ux