1. 排行榜悬浮窗关闭后小按钮消失问题 - 修复面板居中模式关闭时按钮位置错误 - 保存并恢复按钮的居中状态 2. 首页tag按钮按素材数量排序 - 添加按素材数量降序排序逻辑 3. 上传弹窗输入框浅色模式文字不可见 - 添加 text-slate-900 确保浅色模式下文字可见 - 添加 placeholder 样式确保占位符正确显示
797 lines
20 KiB
Markdown
797 lines
20 KiB
Markdown
# 架构设计与编码规范
|
||
|
||
## 📋 项目目标
|
||
|
||
**最终目的**:让其他公司能够**一键部署**或**一条命令**从Docker部署到自己的NAS或服务器上。
|
||
|
||
**核心原则**:
|
||
1. **性能开销优先** - 最小化资源消耗,确保在低配置设备上也能稳定运行
|
||
2. **稳定性优先** - 确保长期稳定运行,减少崩溃和错误
|
||
3. **易于部署** - 零配置或最小配置即可运行
|
||
4. **可维护性** - 代码清晰,易于理解和维护
|
||
5. **国内镜像源优先** - 所有依赖、库等外部资源必须使用国内镜像源,确保国内用户快速部署和稳定运行
|
||
|
||
---
|
||
|
||
## 🏗️ 架构设计原则
|
||
|
||
### 1. 资源消耗最小化
|
||
|
||
#### 1.1 内存管理
|
||
- ✅ **禁止内存泄漏**
|
||
- 所有事件监听器必须正确清理
|
||
- 定时器必须清理
|
||
- 数据库连接池大小限制
|
||
- 文件流必须关闭
|
||
|
||
- ✅ **避免大对象常驻内存**
|
||
- 大文件使用流式处理,不全部加载到内存
|
||
- 图片处理使用流式API
|
||
- 数据库查询使用分页,避免一次性加载大量数据
|
||
|
||
- ✅ **缓存策略**
|
||
- 使用内存缓存时设置大小限制和TTL
|
||
- 优先使用Redis等外部缓存(如果可用)
|
||
- 避免缓存大对象
|
||
|
||
#### 1.2 CPU优化
|
||
- ✅ **避免阻塞主线程**
|
||
- 所有I/O操作使用异步
|
||
- 图片处理使用Worker线程或子进程
|
||
- 数据库查询使用连接池
|
||
|
||
- ✅ **减少不必要的计算**
|
||
- 禁用Next.js Image Optimizer(图片已在上传时优化)
|
||
- 避免重复的图片处理
|
||
- 使用数据库索引优化查询
|
||
|
||
#### 1.3 网络优化
|
||
- ✅ **减少网络请求**
|
||
- 合并API请求
|
||
- 使用HTTP/2多路复用
|
||
- 静态资源使用CDN或本地缓存
|
||
|
||
- ✅ **超时和重试机制**
|
||
- 所有外部API调用设置合理的超时时间
|
||
- 实现指数退避重试机制
|
||
- 避免无限重试导致资源耗尽
|
||
|
||
### 2. 稳定性保障
|
||
|
||
#### 2.1 错误处理
|
||
```typescript
|
||
// ✅ 好的做法:完整的错误处理
|
||
try {
|
||
const result = await someOperation()
|
||
return NextResponse.json({ success: true, data: result })
|
||
} catch (error) {
|
||
// 记录详细错误信息(用于诊断)
|
||
console.error('[Operation] Error:', {
|
||
message: error instanceof Error ? error.message : String(error),
|
||
stack: error instanceof Error ? error.stack : undefined,
|
||
context: { /* 相关上下文信息 */ }
|
||
})
|
||
|
||
// 返回用户友好的错误信息(不暴露内部细节)
|
||
return NextResponse.json(
|
||
{ error: '操作失败,请稍后重试' },
|
||
{ status: 500 }
|
||
)
|
||
}
|
||
|
||
// ❌ 不好的做法:忽略错误或暴露内部细节
|
||
try {
|
||
await someOperation()
|
||
} catch (error) {
|
||
// 错误被忽略,无法诊断问题
|
||
}
|
||
```
|
||
|
||
#### 2.2 资源清理
|
||
```typescript
|
||
// ✅ 好的做法:确保资源清理
|
||
async function processFile(file: File) {
|
||
let stream: ReadableStream | null = null
|
||
try {
|
||
stream = file.stream()
|
||
// 处理文件
|
||
} finally {
|
||
// 确保流被关闭
|
||
if (stream) {
|
||
await stream.cancel()
|
||
}
|
||
}
|
||
}
|
||
|
||
// ❌ 不好的做法:资源未清理
|
||
async function processFile(file: File) {
|
||
const stream = file.stream()
|
||
// 处理文件,但流可能未关闭
|
||
}
|
||
```
|
||
|
||
#### 2.3 数据库连接管理
|
||
```typescript
|
||
// ✅ 好的做法:使用连接池,设置合理的限制
|
||
const prisma = new PrismaClient({
|
||
log: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'],
|
||
datasources: {
|
||
db: {
|
||
url: process.env.DATABASE_URL,
|
||
},
|
||
},
|
||
})
|
||
|
||
// 设置连接池大小(根据服务器配置调整)
|
||
// 在DATABASE_URL中设置:?connection_limit=10&pool_timeout=20
|
||
|
||
// ❌ 不好的做法:无限制的连接
|
||
// 可能导致数据库连接耗尽
|
||
```
|
||
|
||
#### 2.4 文件描述符管理
|
||
```typescript
|
||
// ✅ 好的做法:限制并发操作
|
||
const MAX_CONCURRENT_UPLOADS = 5
|
||
const uploadSemaphore = new Semaphore(MAX_CONCURRENT_UPLOADS)
|
||
|
||
async function uploadFile(file: File) {
|
||
await uploadSemaphore.acquire()
|
||
try {
|
||
// 上传文件
|
||
} finally {
|
||
uploadSemaphore.release()
|
||
}
|
||
}
|
||
|
||
// ❌ 不好的做法:无限制并发
|
||
// 可能导致文件描述符耗尽
|
||
```
|
||
|
||
### 3. 部署友好性
|
||
|
||
#### 3.1 环境变量管理
|
||
```typescript
|
||
// ✅ 好的做法:提供合理的默认值
|
||
const config = {
|
||
port: parseInt(process.env.PORT || '3000', 10),
|
||
databaseUrl: process.env.DATABASE_URL || 'postgresql://localhost:5432/app',
|
||
// 所有配置都有默认值,可以零配置运行
|
||
}
|
||
|
||
// ❌ 不好的做法:强制要求环境变量
|
||
if (!process.env.DATABASE_URL) {
|
||
throw new Error('DATABASE_URL is required') // 部署时容易出错
|
||
}
|
||
```
|
||
|
||
#### 3.2 健康检查
|
||
```typescript
|
||
// ✅ 好的做法:提供健康检查端点
|
||
export async function GET() {
|
||
try {
|
||
// 检查数据库连接
|
||
await prisma.$queryRaw`SELECT 1`
|
||
|
||
// 检查S3连接(如果配置了)
|
||
if (process.env.S3_ENDPOINT) {
|
||
// 简单的连接测试
|
||
}
|
||
|
||
return NextResponse.json({
|
||
status: 'ok',
|
||
timestamp: new Date().toISOString(),
|
||
version: process.env.APP_VERSION || 'unknown'
|
||
})
|
||
} catch (error) {
|
||
return NextResponse.json(
|
||
{ status: 'error', message: 'Health check failed' },
|
||
{ status: 503 }
|
||
)
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3.3 优雅关闭
|
||
```typescript
|
||
// ✅ 好的做法:处理关闭信号
|
||
process.on('SIGTERM', async () => {
|
||
console.log('SIGTERM received, shutting down gracefully...')
|
||
|
||
// 停止接受新请求
|
||
server.close(() => {
|
||
console.log('HTTP server closed')
|
||
})
|
||
|
||
// 关闭数据库连接
|
||
await prisma.$disconnect()
|
||
|
||
// 清理其他资源
|
||
process.exit(0)
|
||
})
|
||
```
|
||
|
||
### 4. 监控和诊断
|
||
|
||
#### 4.1 日志记录
|
||
```typescript
|
||
// ✅ 好的做法:结构化日志
|
||
function logOperation(operation: string, data: any) {
|
||
console.log(JSON.stringify({
|
||
timestamp: new Date().toISOString(),
|
||
level: 'info',
|
||
operation,
|
||
...data
|
||
}))
|
||
}
|
||
|
||
// 使用示例
|
||
logOperation('upload', {
|
||
userId: user.id,
|
||
fileSize: file.size,
|
||
duration: Date.now() - startTime
|
||
})
|
||
```
|
||
|
||
#### 4.2 性能监控
|
||
```typescript
|
||
// ✅ 好的做法:记录操作耗时
|
||
async function monitoredOperation() {
|
||
const startTime = Date.now()
|
||
try {
|
||
const result = await operation()
|
||
const duration = Date.now() - startTime
|
||
|
||
if (duration > 1000) {
|
||
console.warn(`[Performance] Operation took ${duration}ms`)
|
||
}
|
||
|
||
return result
|
||
} catch (error) {
|
||
const duration = Date.now() - startTime
|
||
console.error(`[Performance] Operation failed after ${duration}ms:`, error)
|
||
throw error
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 编码规范
|
||
|
||
### 1. API路由设计
|
||
|
||
#### 1.1 统一响应格式
|
||
```typescript
|
||
// ✅ 好的做法:统一的响应格式
|
||
export async function GET() {
|
||
try {
|
||
const data = await fetchData()
|
||
return NextResponse.json({
|
||
success: true,
|
||
data,
|
||
timestamp: new Date().toISOString()
|
||
})
|
||
} catch (error) {
|
||
return NextResponse.json({
|
||
success: false,
|
||
error: error instanceof Error ? error.message : 'Unknown error',
|
||
timestamp: new Date().toISOString()
|
||
}, { status: 500 })
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 1.2 请求验证
|
||
```typescript
|
||
// ✅ 好的做法:验证和清理输入
|
||
import { z } from 'zod'
|
||
|
||
const uploadSchema = z.object({
|
||
title: z.string().min(1).max(100),
|
||
file: z.instanceof(File)
|
||
})
|
||
|
||
export async function POST(request: Request) {
|
||
try {
|
||
const formData = await request.formData()
|
||
const data = uploadSchema.parse({
|
||
title: formData.get('title'),
|
||
file: formData.get('file')
|
||
})
|
||
|
||
// 处理数据
|
||
} catch (error) {
|
||
if (error instanceof z.ZodError) {
|
||
return NextResponse.json(
|
||
{ error: 'Invalid input', details: error.errors },
|
||
{ status: 400 }
|
||
)
|
||
}
|
||
throw error
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 1.3 权限检查
|
||
```typescript
|
||
// ✅ 好的做法:统一的权限检查
|
||
async function requireAdmin(session: Session | null) {
|
||
if (!session) {
|
||
throw new Error('Unauthorized')
|
||
}
|
||
|
||
const isAdmin = session.user.role === 'ADMIN' || (session.user as any).isSuperAdmin
|
||
if (!isAdmin) {
|
||
throw new Error('Forbidden')
|
||
}
|
||
|
||
return session
|
||
}
|
||
|
||
export async function POST(request: Request) {
|
||
const session = await getServerSession(authOptions)
|
||
await requireAdmin(session)
|
||
|
||
// 管理员操作
|
||
}
|
||
```
|
||
|
||
### 2. 数据库操作
|
||
|
||
#### 2.1 使用事务
|
||
```typescript
|
||
// ✅ 好的做法:关键操作使用事务
|
||
await prisma.$transaction(async (tx) => {
|
||
// 检查条件
|
||
const count = await tx.user.count()
|
||
|
||
// 执行操作
|
||
const user = await tx.user.create({
|
||
data: { /* ... */ }
|
||
})
|
||
|
||
return user
|
||
})
|
||
```
|
||
|
||
#### 2.2 查询优化
|
||
```typescript
|
||
// ✅ 好的做法:只查询需要的字段
|
||
const users = await prisma.user.findMany({
|
||
select: {
|
||
id: true,
|
||
email: true,
|
||
username: true,
|
||
// 不查询password等大字段
|
||
},
|
||
take: 20, // 分页
|
||
skip: 0
|
||
})
|
||
|
||
// ❌ 不好的做法:查询所有字段
|
||
const users = await prisma.user.findMany() // 可能包含大字段
|
||
```
|
||
|
||
#### 2.3 错误处理
|
||
```typescript
|
||
// ✅ 好的做法:处理数据库错误
|
||
try {
|
||
await prisma.user.create({ data })
|
||
} catch (error) {
|
||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||
if (error.code === 'P2002') {
|
||
// 唯一约束冲突
|
||
return NextResponse.json(
|
||
{ error: '用户已存在' },
|
||
{ status: 409 }
|
||
)
|
||
}
|
||
}
|
||
throw error
|
||
}
|
||
```
|
||
|
||
### 3. 文件处理
|
||
|
||
#### 3.1 流式处理
|
||
```typescript
|
||
// ✅ 好的做法:使用流处理大文件
|
||
import { pipeline } from 'stream/promises'
|
||
import { createReadStream } from 'fs'
|
||
|
||
async function uploadLargeFile(filePath: string) {
|
||
const fileStream = createReadStream(filePath)
|
||
const uploadStream = createUploadStream()
|
||
|
||
await pipeline(fileStream, uploadStream)
|
||
// 文件不会全部加载到内存
|
||
}
|
||
|
||
// ❌ 不好的做法:一次性读取整个文件
|
||
const fileContent = await fs.readFile(filePath) // 大文件会占用大量内存
|
||
```
|
||
|
||
#### 3.2 文件大小限制
|
||
```typescript
|
||
// ✅ 好的做法:检查文件大小
|
||
const MAX_FILE_SIZE = 10 * 1024 * 1024 * 1024 // 10GB
|
||
|
||
if (file.size > MAX_FILE_SIZE) {
|
||
return NextResponse.json(
|
||
{ error: '文件大小超过限制' },
|
||
{ status: 413 }
|
||
)
|
||
}
|
||
```
|
||
|
||
### 4. 前端组件
|
||
|
||
#### 4.1 性能优化
|
||
```typescript
|
||
// ✅ 好的做法:使用React.memo和useMemo
|
||
import { memo, useMemo } from 'react'
|
||
|
||
const MaterialCard = memo(({ material }: { material: Material }) => {
|
||
const previewUrl = useMemo(() => {
|
||
return material.previewUrl || '/placeholder.png'
|
||
}, [material.previewUrl])
|
||
|
||
return (
|
||
<Image
|
||
src={previewUrl}
|
||
alt={material.title}
|
||
unoptimized // 图片已在上传时优化,不需要Next.js再次优化
|
||
width={300}
|
||
height={200}
|
||
/>
|
||
)
|
||
})
|
||
```
|
||
|
||
#### 4.2 错误边界
|
||
```typescript
|
||
// ✅ 好的做法:错误边界处理
|
||
'use client'
|
||
|
||
import { ErrorBoundary } from 'react-error-boundary'
|
||
|
||
function ErrorFallback({ error }: { error: Error }) {
|
||
return (
|
||
<div className="error-container">
|
||
<h2>出现错误</h2>
|
||
<p>{error.message}</p>
|
||
<button onClick={() => window.location.reload()}>
|
||
刷新页面
|
||
</button>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default function Page() {
|
||
return (
|
||
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
||
{/* 页面内容 */}
|
||
</ErrorBoundary>
|
||
)
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🚀 部署优化
|
||
|
||
### 1. Docker优化
|
||
|
||
#### 1.1 多阶段构建
|
||
```dockerfile
|
||
# ✅ 好的做法:多阶段构建,最小化镜像大小
|
||
FROM node:18-alpine AS builder
|
||
# 构建阶段
|
||
|
||
FROM node:18-alpine AS runner
|
||
# 运行阶段,只包含必要的文件
|
||
COPY --from=builder /app/.next/standalone ./
|
||
COPY --from=builder /app/.next/static ./.next/static
|
||
```
|
||
|
||
#### 1.2 层缓存优化
|
||
```dockerfile
|
||
# ✅ 好的做法:依赖文件放在前面,利用Docker缓存
|
||
COPY package.json package-lock.json ./
|
||
RUN npm ci --production
|
||
|
||
COPY . .
|
||
RUN npm run build
|
||
```
|
||
|
||
### 2. 环境配置
|
||
|
||
#### 2.1 配置优先级
|
||
```
|
||
1. 系统环境变量(最高优先级)
|
||
2. .env.local(本地开发)
|
||
3. .env(项目默认)
|
||
4. config-bianliang.env(可迁移配置,最低优先级)
|
||
```
|
||
|
||
#### 2.2 默认值设计
|
||
```typescript
|
||
// ✅ 好的做法:所有配置都有合理的默认值
|
||
const config = {
|
||
// 数据库:默认使用本地PostgreSQL
|
||
databaseUrl: process.env.DATABASE_URL || 'postgresql://postgres:postgres@postgres:5432/flnxi',
|
||
|
||
// S3:默认使用MinIO(Docker Compose中已配置)
|
||
s3Endpoint: process.env.S3_ENDPOINT || 'http://minio:9000',
|
||
s3Bucket: process.env.S3_BUCKET || 'youyidian3d',
|
||
|
||
// 端口:默认3000
|
||
port: parseInt(process.env.PORT || '3000', 10),
|
||
}
|
||
```
|
||
|
||
### 3. 国内镜像源配置(必须)
|
||
|
||
#### 3.1 核心要求
|
||
**所有外部资源必须使用国内镜像源**,包括但不限于:
|
||
- npm 包(Node.js 依赖)
|
||
- Docker 基础镜像(Alpine、Node.js 等)
|
||
- 系统包管理器(apk、apt、yum 等)
|
||
- 其他第三方服务(如 CDN、API 等)
|
||
|
||
#### 3.2 Docker 构建镜像源配置
|
||
|
||
##### 3.2.1 Alpine Linux 镜像源
|
||
```dockerfile
|
||
# ✅ 好的做法:使用阿里云 Alpine 镜像源
|
||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
|
||
|
||
# 其他可选镜像源:
|
||
# - 清华大学:mirrors.tuna.tsinghua.edu.cn
|
||
# - 中科大:mirrors.ustc.edu.cn
|
||
```
|
||
|
||
##### 3.2.2 npm 镜像源
|
||
```dockerfile
|
||
# ✅ 好的做法:构建时使用国内 npm 镜像源
|
||
RUN npm config set registry https://registry.npmmirror.com/
|
||
|
||
# 安装依赖
|
||
RUN npm ci --prefer-offline --no-audit
|
||
|
||
# 构建完成后清理配置(不在运行镜像中保留)
|
||
RUN npm config delete registry
|
||
```
|
||
|
||
**推荐的国内 npm 镜像源**:
|
||
- **npmmirror.com**(原淘宝镜像,推荐)
|
||
- **registry.npmmirror.com**(新域名)
|
||
- **cnpmjs.org**(备选)
|
||
|
||
##### 3.2.3 Docker Hub 镜像源(必须配置)
|
||
**重要**:所有Docker镜像(包括MinIO、PostgreSQL等)必须通过国内镜像加速器拉取。
|
||
|
||
**配置方法**(`/etc/docker/daemon.json`):
|
||
```json
|
||
{
|
||
"registry-mirrors": [
|
||
"https://docker.mirrors.ustc.edu.cn",
|
||
"https://hub-mirror.c.163.com",
|
||
"https://mirror.baidubce.com"
|
||
]
|
||
}
|
||
```
|
||
|
||
**配置后重启Docker**:
|
||
```bash
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl restart docker
|
||
```
|
||
|
||
**验证配置**:
|
||
```bash
|
||
docker info | grep -A 10 "Registry Mirrors"
|
||
```
|
||
|
||
**版本管理要求**:
|
||
- ✅ 使用特定版本号(如 `minio/minio:RELEASE.2025-04-22T22-12-26Z`)而不是 `latest`
|
||
- ✅ 确保版本号在文档中明确记录
|
||
- ✅ 版本更新前必须在测试环境验证
|
||
|
||
#### 3.3 本地开发环境配置
|
||
|
||
##### 3.3.1 npm 镜像源(本地开发)
|
||
```bash
|
||
# 永久配置(推荐)
|
||
npm config set registry https://registry.npmmirror.com/
|
||
|
||
# 或使用 .npmrc 文件(项目级别)
|
||
echo "registry=https://registry.npmmirror.com/" > .npmrc
|
||
```
|
||
|
||
##### 3.3.2 验证镜像源配置
|
||
```bash
|
||
# 检查 npm 镜像源
|
||
npm config get registry
|
||
|
||
# 检查 Docker 镜像源(如果配置了)
|
||
docker info | grep -A 10 "Registry Mirrors"
|
||
```
|
||
|
||
#### 3.4 代码中的外部资源引用
|
||
|
||
##### 3.4.1 CDN 资源
|
||
```typescript
|
||
// ✅ 好的做法:使用国内 CDN
|
||
const CDN_BASE_URL = process.env.CDN_BASE_URL || 'https://cdn.jsdelivr.net'
|
||
|
||
// 推荐使用:
|
||
// - jsDelivr(有国内节点)
|
||
// - unpkg(有国内节点)
|
||
// - 自建 CDN 或使用阿里云/腾讯云 CDN
|
||
```
|
||
|
||
##### 3.4.2 外部 API
|
||
```typescript
|
||
// ✅ 好的做法:优先使用国内 API 服务
|
||
// 如果必须使用国外服务,考虑:
|
||
// 1. 使用代理
|
||
// 2. 使用国内替代方案
|
||
// 3. 实现缓存机制减少请求
|
||
```
|
||
|
||
#### 3.5 部署脚本中的镜像源配置
|
||
|
||
##### 3.5.1 PowerShell 部署脚本
|
||
```powershell
|
||
# ✅ 好的做法:部署脚本中配置镜像源
|
||
# 在 Dockerfile 中已配置,部署脚本无需额外配置
|
||
# 但如果需要,可以在构建前设置环境变量
|
||
$env:NPM_CONFIG_REGISTRY = "https://registry.npmmirror.com/"
|
||
```
|
||
|
||
##### 3.5.2 服务器端配置
|
||
```bash
|
||
# 如果需要在服务器上直接安装依赖,配置 npm 镜像源
|
||
ssh user@server "npm config set registry https://registry.npmmirror.com/"
|
||
```
|
||
|
||
#### 3.6 镜像源配置检查清单
|
||
|
||
在提交代码前,确保:
|
||
- [ ] Dockerfile 中配置了 Alpine 镜像源
|
||
- [ ] Dockerfile 中配置了 npm 镜像源(构建阶段)
|
||
- [ ] 本地开发环境配置了 npm 镜像源
|
||
- [ ] 服务器上配置了 Docker 镜像加速器
|
||
- [ ] docker-compose.yml 中使用特定版本号(不使用 `latest`)
|
||
- [ ] 所有外部 CDN 资源优先使用国内服务
|
||
- [ ] 部署文档中说明了镜像源配置要求
|
||
|
||
#### 3.7 Docker镜像版本管理
|
||
|
||
##### 3.7.1 使用特定版本号(必须)
|
||
```yaml
|
||
# ✅ 好的做法:使用特定版本号
|
||
services:
|
||
minio:
|
||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||
|
||
postgres:
|
||
image: postgres:15-alpine
|
||
|
||
# ❌ 不好的做法:使用latest(可能导致不稳定)
|
||
services:
|
||
minio:
|
||
image: minio/minio:latest
|
||
```
|
||
|
||
##### 3.7.2 版本更新流程
|
||
1. **测试环境验证**:在测试环境部署新版本,验证兼容性
|
||
2. **文档记录**:在CHANGELOG中记录版本更新和变更
|
||
3. **逐步部署**:先在部分环境部署,确认稳定后再全面更新
|
||
4. **回滚准备**:保留旧版本配置,确保可以快速回滚
|
||
|
||
##### 3.7.3 版本号管理建议
|
||
```yaml
|
||
# 使用环境变量管理版本(推荐)
|
||
MINIO_VERSION=${MINIO_VERSION:-RELEASE.2025-04-22T22-12-26Z}
|
||
|
||
services:
|
||
minio:
|
||
image: minio/minio:${MINIO_VERSION}
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 性能指标
|
||
|
||
### 目标指标
|
||
|
||
1. **内存使用**
|
||
- 空闲时:< 200MB
|
||
- 正常运行时:< 500MB
|
||
- 峰值:< 1GB
|
||
|
||
2. **CPU使用**
|
||
- 空闲时:< 5%
|
||
- 正常运行时:< 30%
|
||
- 峰值:< 80%
|
||
|
||
3. **响应时间**
|
||
- API响应:< 200ms(95分位)
|
||
- 页面加载:< 2s(首次加载)
|
||
- 数据库查询:< 100ms(95分位)
|
||
|
||
4. **稳定性**
|
||
- 正常运行时间:> 99.9%
|
||
- 错误率:< 0.1%
|
||
- 崩溃恢复时间:< 30s
|
||
|
||
---
|
||
|
||
## 🔍 代码审查清单
|
||
|
||
在提交代码前,检查以下项目:
|
||
|
||
### 性能
|
||
- [ ] 是否避免了内存泄漏?
|
||
- [ ] 是否使用了流式处理大文件?
|
||
- [ ] 是否限制了并发操作?
|
||
- [ ] 是否优化了数据库查询?
|
||
- [ ] 是否禁用了不必要的优化(如Next.js Image Optimizer)?
|
||
|
||
### 稳定性
|
||
- [ ] 是否处理了所有可能的错误?
|
||
- [ ] 是否清理了所有资源(文件流、数据库连接等)?
|
||
- [ ] 是否设置了合理的超时时间?
|
||
- [ ] 是否实现了重试机制?
|
||
- [ ] 是否添加了健康检查?
|
||
|
||
### 部署友好性
|
||
- [ ] 是否提供了合理的默认值?
|
||
- [ ] 是否支持零配置运行?
|
||
- [ ] 是否添加了必要的日志?
|
||
- [ ] 是否处理了优雅关闭?
|
||
- [ ] 是否配置了国内镜像源(npm、Docker、系统包管理器)?
|
||
- [ ] 是否避免了直接引用国外 CDN 或 API?
|
||
|
||
---
|
||
|
||
## 📚 参考资源
|
||
|
||
- [Next.js性能优化最佳实践](https://nextjs.org/docs/app/building-your-application/optimizing)
|
||
- [Prisma性能优化指南](https://www.prisma.io/docs/guides/performance-and-optimization)
|
||
- [Node.js最佳实践](https://github.com/goldbergyoni/nodebestpractices)
|
||
- [Docker最佳实践](https://docs.docker.com/develop/dev-best-practices/)
|
||
|
||
---
|
||
|
||
**最后更新**:2025-11-15
|
||
**维护者**:开发团队
|
||
|
||
---
|
||
|
||
## 📌 重要提醒
|
||
|
||
### 国内镜像源配置(必须遵守)
|
||
|
||
本项目主要面向国内用户,**所有外部资源必须使用国内镜像源**:
|
||
|
||
1. ✅ **Dockerfile 中已配置**:
|
||
- Alpine Linux:使用阿里云镜像源
|
||
- npm:使用 npmmirror.com 镜像源
|
||
|
||
2. ✅ **本地开发环境**:
|
||
- 建议配置 npm 镜像源:`npm config set registry https://registry.npmmirror.com/`
|
||
|
||
3. ⚠️ **新增依赖或修改配置时**:
|
||
- 必须确保使用国内镜像源
|
||
- 禁止直接使用国外源(npmjs.org、docker.io 等)
|
||
- 代码审查时必须检查镜像源配置
|
||
|
||
4. 📝 **文档要求**:
|
||
- 所有部署文档必须说明镜像源配置
|
||
- 新加入的开发者必须了解镜像源要求
|
||
|