589 lines
14 KiB
Markdown
589 lines
14 KiB
Markdown
# 代码风格指南
|
||
|
||
<div align="center">
|
||
|
||
**🌍 Language / 语言**
|
||
|
||
[🇺🇸 English](./Code-Style.en.md) | [🇨🇳 中文](./Code-Style.md)
|
||
|
||
</div>
|
||
|
||
本指南概述了 AI Proxy Worker 项目的编码标准和最佳实践。遵循这些准则确保整个项目的代码一致性、可维护性和可读性。
|
||
|
||
## 📋 通用原则
|
||
|
||
### 代码质量标准
|
||
- **可读性优先**:编写能讲述故事的代码
|
||
- **一致性**:在整个代码库中遵循既定模式
|
||
- **简洁性**:优先选择简单的解决方案而非复杂的
|
||
- **模块化设计**:拆分复杂函数为单一职责的小函数
|
||
- **低认知复杂度**:保持函数的认知复杂度在15以下
|
||
- **性能**:考虑代码的性能影响
|
||
- **安全性**:在实现中始终优先考虑安全性
|
||
|
||
### 文件组织
|
||
```
|
||
ai-proxy-worker/
|
||
├── worker.js # 主要 worker 脚本
|
||
├── wrangler.toml # 配置文件
|
||
├── docs/ # 文档
|
||
├── examples/ # 使用示例
|
||
└── tests/ # 测试文件(未来)
|
||
```
|
||
|
||
## 🔧 JavaScript/TypeScript 标准
|
||
|
||
### 代码格式化
|
||
- **缩进**:使用 2 个空格(不使用制表符)
|
||
- **行长度**:每行最多 100 个字符
|
||
- **分号**:可选,但要保持一致
|
||
- **引号**:字符串使用单引号
|
||
- **尾随逗号**:在多行对象/数组中使用尾随逗号
|
||
|
||
```javascript
|
||
// ✅ 好的示例
|
||
const config = {
|
||
apiUrl: 'https://api.deepseek.com',
|
||
timeout: 30000,
|
||
retries: 3,
|
||
}
|
||
|
||
const models = [
|
||
'deepseek-chat',
|
||
'deepseek-reasoner',
|
||
]
|
||
|
||
// ❌ 避免这样写
|
||
const config = {
|
||
"apiUrl": "https://api.deepseek.com",
|
||
"timeout": 30000,
|
||
"retries": 3
|
||
};
|
||
```
|
||
|
||
### 命名约定
|
||
|
||
#### 变量和函数
|
||
变量和函数使用 camelCase:
|
||
|
||
```javascript
|
||
// ✅ 好的示例
|
||
const apiResponse = await fetchData()
|
||
const userMessage = request.body.message
|
||
const isValidRequest = validateInput(data)
|
||
|
||
function processUserRequest(request) {
|
||
// 实现
|
||
}
|
||
|
||
async function sendUpstreamRequest(payload) {
|
||
// 实现
|
||
}
|
||
|
||
// ❌ 避免这样写
|
||
const api_response = await fetchData()
|
||
const user_message = request.body.message
|
||
const IsValidRequest = validateInput(data)
|
||
|
||
function ProcessUserRequest(request) {
|
||
// 实现
|
||
}
|
||
```
|
||
|
||
#### 常量
|
||
常量使用 UPPER_SNAKE_CASE:
|
||
|
||
```javascript
|
||
// ✅ 好的示例
|
||
const API_BASE_URL = 'https://api.deepseek.com'
|
||
const DEFAULT_TIMEOUT = 30000
|
||
const MAX_RETRIES = 3
|
||
const SUPPORTED_MODELS = ['deepseek-chat', 'deepseek-reasoner']
|
||
|
||
// ❌ 避免这样写
|
||
const apiBaseUrl = 'https://api.deepseek.com'
|
||
const defaultTimeout = 30000
|
||
```
|
||
|
||
#### 类和对象
|
||
类和构造函数使用 PascalCase:
|
||
|
||
```javascript
|
||
// ✅ 好的示例
|
||
class RequestHandler {
|
||
constructor(config) {
|
||
this.config = config
|
||
}
|
||
}
|
||
|
||
class ApiError extends Error {
|
||
constructor(message, statusCode) {
|
||
super(message)
|
||
this.statusCode = statusCode
|
||
}
|
||
}
|
||
|
||
// ❌ 避免这样写
|
||
class requestHandler {
|
||
// 实现
|
||
}
|
||
```
|
||
|
||
### 函数结构
|
||
|
||
#### 函数声明
|
||
短函数优先使用箭头函数,复杂逻辑使用常规函数:
|
||
|
||
```javascript
|
||
// ✅ 好的示例 - 短工具函数
|
||
const isValidModel = (model) => SUPPORTED_MODELS.includes(model)
|
||
const createErrorResponse = (message, status = 400) =>
|
||
new Response(JSON.stringify({ error: message }), { status })
|
||
|
||
// ✅ 好的示例 - 复杂函数
|
||
async function handleChatRequest(request, env) {
|
||
try {
|
||
// 验证请求
|
||
const validation = await validateRequest(request)
|
||
if (!validation.valid) {
|
||
return createErrorResponse(validation.error, 400)
|
||
}
|
||
|
||
// 处理请求
|
||
const response = await processChat(request, env)
|
||
return response
|
||
|
||
} catch (error) {
|
||
console.error('聊天请求失败:', error)
|
||
return createErrorResponse('内部服务器错误', 500)
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 参数处理
|
||
对象参数使用解构:
|
||
|
||
```javascript
|
||
// ✅ 好的示例
|
||
async function processChat({ messages, model, temperature }, env) {
|
||
// 实现
|
||
}
|
||
|
||
// 带验证的替代方案
|
||
async function processChat(params, env) {
|
||
const { messages, model, temperature = 0.7 } = params
|
||
|
||
// 验证必需参数
|
||
if (!messages || !model) {
|
||
throw new Error('缺少必需参数')
|
||
}
|
||
|
||
// 实现
|
||
}
|
||
|
||
// ❌ 避免这样写
|
||
async function processChat(params, env) {
|
||
const messages = params.messages
|
||
const model = params.model
|
||
const temperature = params.temperature
|
||
// 实现
|
||
}
|
||
```
|
||
|
||
## 📝 文档标准
|
||
|
||
### JSDoc 注释
|
||
函数文档使用 JSDoc:
|
||
|
||
```javascript
|
||
/**
|
||
* 向上游 API 发送聊天请求
|
||
* @param {Object} request - 聊天请求对象
|
||
* @param {Array} request.messages - 聊天消息数组
|
||
* @param {string} request.model - 要使用的模型
|
||
* @param {number} [request.temperature=0.7] - 采样温度
|
||
* @param {Object} env - 环境变量
|
||
* @param {string} env.DEEPSEEK_API_KEY - DeepSeek API 密钥
|
||
* @returns {Promise<Response>} API 响应
|
||
* @throws {Error} 当 API 密钥缺失或请求失败时
|
||
*/
|
||
async function sendChatRequest(request, env) {
|
||
// 实现
|
||
}
|
||
```
|
||
|
||
### 行内注释
|
||
使用注释解释复杂逻辑:
|
||
|
||
```javascript
|
||
// ✅ 好的示例 - 解释"为什么"
|
||
async function handleRequest(request, env) {
|
||
// 提取客户端 IP 用于速率限制
|
||
const clientIP = request.headers.get('CF-Connecting-IP') ||
|
||
request.headers.get('X-Forwarded-For') ||
|
||
'unknown'
|
||
|
||
// 在处理昂贵操作之前检查速率限制
|
||
if (!await checkRateLimit(clientIP)) {
|
||
return new Response('超出速率限制', { status: 429 })
|
||
}
|
||
|
||
// 处理实际请求
|
||
return await processRequest(request, env)
|
||
}
|
||
|
||
// ❌ 避免这样写 - 陈述显而易见的事实
|
||
async function handleRequest(request, env) {
|
||
// 获取客户端 IP
|
||
const clientIP = request.headers.get('CF-Connecting-IP')
|
||
|
||
// 返回速率限制响应
|
||
if (!await checkRateLimit(clientIP)) {
|
||
return new Response('超出速率限制', { status: 429 })
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🔧 模块化验证架构
|
||
|
||
### 验证函数设计原则
|
||
|
||
本项目采用模块化验证架构,将复杂的验证逻辑拆分为多个单一职责的函数:
|
||
|
||
```javascript
|
||
// ✅ 好的示例 - 模块化验证
|
||
async function validateRequest(request) {
|
||
validateContentType(request); // 验证Content-Type
|
||
validateContentLength(request); // 验证请求大小
|
||
|
||
if (CONFIG.VALIDATE_REQUEST_BODY) {
|
||
await validateRequestBody(request); // 验证请求体
|
||
}
|
||
}
|
||
|
||
function validateContentType(request) {
|
||
const contentType = request.headers.get('content-type') || '';
|
||
if (!contentType.includes('application/json')) {
|
||
throw new Error('Invalid content type. Expected application/json');
|
||
}
|
||
}
|
||
|
||
async function validateRequestBody(request) {
|
||
try {
|
||
const body = await request.clone().json();
|
||
validateMessages(body.messages); // 验证消息数组
|
||
validateModel(body.model); // 验证模型
|
||
} catch (e) {
|
||
// 错误处理逻辑
|
||
}
|
||
}
|
||
```
|
||
|
||
### 函数复杂度控制
|
||
|
||
- **认知复杂度限制**: 每个函数保持认知复杂度 ≤ 15
|
||
- **单一职责原则**: 每个验证函数只负责一种验证
|
||
- **可组合性**: 验证函数可以独立测试和复用
|
||
|
||
```javascript
|
||
// ✅ 好的示例 - 单一职责
|
||
function validateSingleMessage(message) {
|
||
if (!message.role || !message.content) {
|
||
throw new Error('Invalid request format. Each message must have role and content');
|
||
}
|
||
if (!['system', 'user', 'assistant', 'tool'].includes(message.role)) {
|
||
throw new Error('Invalid request format. Invalid message role');
|
||
}
|
||
}
|
||
|
||
// ❌ 避免这样写 - 复杂的巨大函数
|
||
function validateEverything(request) {
|
||
// 100+ 行验证代码,认知复杂度 > 20
|
||
}
|
||
```
|
||
|
||
## 🛡️ 错误处理
|
||
|
||
### 错误响应格式
|
||
使用一致的错误响应格式:
|
||
|
||
```javascript
|
||
// ✅ 好的示例 - 一致的错误格式
|
||
function createErrorResponse(error, statusCode = 500, details = null) {
|
||
const errorResponse = {
|
||
error: error.code || 'unknown_error',
|
||
message: error.message || '发生意外错误',
|
||
timestamp: new Date().toISOString(),
|
||
}
|
||
|
||
// 在开发/调试模式下添加详细信息
|
||
if (details && env.DEBUG_MODE === 'true') {
|
||
errorResponse.details = details
|
||
}
|
||
|
||
return new Response(JSON.stringify(errorResponse), {
|
||
status: statusCode,
|
||
headers: { 'Content-Type': 'application/json' }
|
||
})
|
||
}
|
||
|
||
// 使用方法
|
||
try {
|
||
const result = await riskyOperation()
|
||
return new Response(JSON.stringify(result))
|
||
} catch (error) {
|
||
console.error('操作失败:', error)
|
||
return createErrorResponse(error, 500, { operation: 'riskyOperation' })
|
||
}
|
||
```
|
||
|
||
### 错误类型
|
||
为不同场景定义自定义错误类型:
|
||
|
||
```javascript
|
||
// ✅ 好的示例 - 自定义错误类
|
||
class ValidationError extends Error {
|
||
constructor(message, field = null) {
|
||
super(message)
|
||
this.name = 'ValidationError'
|
||
this.code = 'validation_error'
|
||
this.field = field
|
||
}
|
||
}
|
||
|
||
class ApiError extends Error {
|
||
constructor(message, statusCode = 500, originalError = null) {
|
||
super(message)
|
||
this.name = 'ApiError'
|
||
this.code = 'api_error'
|
||
this.statusCode = statusCode
|
||
this.originalError = originalError
|
||
}
|
||
}
|
||
|
||
// 使用方法
|
||
function validateChatRequest(data) {
|
||
if (!data.messages || !Array.isArray(data.messages)) {
|
||
throw new ValidationError('消息必须是数组', 'messages')
|
||
}
|
||
|
||
if (!data.model || typeof data.model !== 'string') {
|
||
throw new ValidationError('模型必须是字符串', 'model')
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🔒 安全最佳实践
|
||
|
||
### 输入验证
|
||
始终验证和清理输入:
|
||
|
||
```javascript
|
||
// ✅ 好的示例 - 全面验证
|
||
function validateChatRequest(data) {
|
||
const errors = []
|
||
|
||
// 必需字段
|
||
if (!data.messages || !Array.isArray(data.messages)) {
|
||
errors.push('messages 必须是数组')
|
||
}
|
||
|
||
if (!data.model || typeof data.model !== 'string') {
|
||
errors.push('model 必须是字符串')
|
||
}
|
||
|
||
// 验证消息数组
|
||
if (data.messages) {
|
||
data.messages.forEach((msg, index) => {
|
||
if (!msg.role || !['user', 'assistant', 'system'].includes(msg.role)) {
|
||
errors.push(`messages[${index}].role 必须是 user、assistant 或 system`)
|
||
}
|
||
|
||
if (!msg.content || typeof msg.content !== 'string') {
|
||
errors.push(`messages[${index}].content 必须是非空字符串`)
|
||
}
|
||
|
||
// 内容长度限制
|
||
if (msg.content && msg.content.length > 100000) {
|
||
errors.push(`messages[${index}].content 超过最大长度`)
|
||
}
|
||
})
|
||
}
|
||
|
||
// 可选参数验证
|
||
if (data.temperature !== undefined) {
|
||
if (typeof data.temperature !== 'number' ||
|
||
data.temperature < 0 ||
|
||
data.temperature > 2) {
|
||
errors.push('temperature 必须是 0 到 2 之间的数字')
|
||
}
|
||
}
|
||
|
||
return {
|
||
valid: errors.length === 0,
|
||
errors
|
||
}
|
||
}
|
||
```
|
||
|
||
### 敏感数据处理
|
||
永远不要记录敏感信息:
|
||
|
||
```javascript
|
||
// ✅ 好的示例 - 清理后的日志
|
||
function logRequest(request, response) {
|
||
const logData = {
|
||
method: request.method,
|
||
url: new URL(request.url).pathname, // 不记录查询参数
|
||
status: response.status,
|
||
timestamp: new Date().toISOString(),
|
||
// 不记录授权头或正文内容
|
||
}
|
||
|
||
console.log('请求已处理:', logData)
|
||
}
|
||
|
||
// ❌ 避免这样写 - 记录敏感数据
|
||
function logRequest(request, response) {
|
||
console.log('请求:', {
|
||
headers: Object.fromEntries(request.headers), // 包含 API 密钥!
|
||
body: request.body, // 包含用户数据!
|
||
url: request.url // 可能包含敏感查询参数!
|
||
})
|
||
}
|
||
```
|
||
|
||
## ⚡ 性能指南
|
||
|
||
### Async/Await 最佳实践
|
||
正确使用 async/await:
|
||
|
||
```javascript
|
||
// ✅ 好的示例 - 尽可能并行执行
|
||
async function processMultipleRequests(requests, env) {
|
||
// 并行执行请求
|
||
const promises = requests.map(request => processRequest(request, env))
|
||
const results = await Promise.allSettled(promises)
|
||
|
||
return results.map(result =>
|
||
result.status === 'fulfilled' ? result.value : null
|
||
).filter(Boolean)
|
||
}
|
||
|
||
// ✅ 好的示例 - 需要时顺序执行
|
||
async function processWithDependencies(request, env) {
|
||
const validation = await validateRequest(request)
|
||
if (!validation.valid) {
|
||
throw new ValidationError(validation.errors.join(', '))
|
||
}
|
||
|
||
const processed = await processRequest(request, env)
|
||
const logged = await logRequest(processed)
|
||
|
||
return processed
|
||
}
|
||
|
||
// ❌ 避免这样写 - 不必要的顺序执行
|
||
async function processMultipleRequests(requests, env) {
|
||
const results = []
|
||
for (const request of requests) {
|
||
const result = await processRequest(request, env) // 阻塞!
|
||
results.push(result)
|
||
}
|
||
return results
|
||
}
|
||
```
|
||
|
||
### 内存管理
|
||
注意内存使用:
|
||
|
||
```javascript
|
||
// ✅ 好的示例 - 清理资源
|
||
async function processLargeRequest(request, env) {
|
||
let reader = null
|
||
try {
|
||
reader = request.body.getReader()
|
||
const chunks = []
|
||
|
||
while (true) {
|
||
const { done, value } = await reader.read()
|
||
if (done) break
|
||
chunks.push(value)
|
||
}
|
||
|
||
return await processChunks(chunks)
|
||
|
||
} finally {
|
||
// 清理资源
|
||
if (reader) {
|
||
reader.releaseLock()
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🧪 测试指南
|
||
|
||
### 测试结构
|
||
编写测试时(未来实现):
|
||
|
||
```javascript
|
||
// ✅ 好的示例 - 清晰的测试结构
|
||
describe('聊天请求处理器', () => {
|
||
describe('validateChatRequest', () => {
|
||
it('应该接受有效的聊天请求', () => {
|
||
const validRequest = {
|
||
messages: [{ role: 'user', content: '你好' }],
|
||
model: 'deepseek-chat'
|
||
}
|
||
|
||
const result = validateChatRequest(validRequest)
|
||
expect(result.valid).toBe(true)
|
||
})
|
||
|
||
it('应该拒绝没有消息的请求', () => {
|
||
const invalidRequest = { model: 'deepseek-chat' }
|
||
|
||
const result = validateChatRequest(invalidRequest)
|
||
expect(result.valid).toBe(false)
|
||
expect(result.errors).toContain('messages 必须是数组')
|
||
})
|
||
})
|
||
})
|
||
```
|
||
|
||
## 📋 代码审查清单
|
||
|
||
提交代码前,确保:
|
||
|
||
### 功能性
|
||
- [ ] 代码按预期工作
|
||
- [ ] 处理边界情况
|
||
- [ ] 正确管理错误条件
|
||
- [ ] 考虑性能影响
|
||
|
||
### 代码质量
|
||
- [ ] 遵循项目命名约定
|
||
- [ ] 函数大小合理(< 50 行)
|
||
- [ ] 代码自文档化
|
||
- [ ] 复杂逻辑有注释
|
||
- [ ] 没有遗留调试代码
|
||
|
||
### 安全性
|
||
- [ ] 实现输入验证
|
||
- [ ] 日志中无敏感数据
|
||
- [ ] 适当的错误处理不泄露信息
|
||
- [ ] 适当设置安全头
|
||
|
||
### 文档
|
||
- [ ] 公共函数有 JSDoc 注释
|
||
- [ ] 需要时更新 README
|
||
- [ ] 为新功能提供示例
|
||
- [ ] 记录重大变更
|
||
|
||
---
|
||
|
||
**一致的代码风格让协作更容易** ✨
|
||
|
||
遵循这些指南有助于维护高质量、可维护的代码库,让所有贡献者都能轻松使用。
|