第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 容器化部署
- 实现了代码分析缓存和性能优化
- 建立了完整的测试体系
✅ 生产就绪
- 实现了代码质量评估和安全检测
- 添加了性能监控和错误处理
- 提供了完整的部署和运维方案
🎯 下章预告
在下一章《实战综合项目三:个性化学习助手平台》中,我们将:
- 构建智能个性化学习系统
- 实现学习路径推荐和进度跟踪
- 集成多种学习资源和评估方式
- 开发自适应学习算法和知识图谱
最后感谢阅读!欢迎关注我,微信公众号:
《鲫小鱼不正经》
。欢迎点赞、收藏、关注,一键三连!!!