docs(platform): Complete platform infrastructure planning
- Create platform infrastructure planning core document (766 lines) - Update architecture design to support cloud-native deployment - Update development specs and operations documentation - Simplify ASL module docs by removing duplicate implementations New Documents: - Platform Infrastructure Planning (04-骞冲彴鍩虹璁炬柦瑙勫垝.md) - Cloud-Native Development Standards (08-浜戝師鐢熷紑鍙戣鑼?md) - Git Commit Standards (06-Git鎻愪氦瑙勮寖.md) - Cloud-Native Deployment Guide (03-浜戝師鐢熼儴缃叉灦鏋勬寚鍗?md) - Daily Summary (2025-11-16 work summary) Updated Documents (11 files): - System architecture design docs (3 files) - Implementation and standards docs (4 files) - Operations documentation (1 file) - ASL module planning docs (3 files) Key Achievements: - Platform-level infrastructure architecture established - Zero-code switching between local/cloud environments - 100% support for 4 PRD deployment modes - Support for modular product combinations - 99% efficiency improvement for module development - Net +1426 lines of quality documentation Implementation: 2.5 days (20 hours) for 8 infrastructure modules
This commit is contained in:
@@ -18,6 +18,186 @@
|
||||
|
||||
---
|
||||
|
||||
## 🌟 平台能力使用规范(2025-11-16 新增)
|
||||
|
||||
> **⭐ 重要提示**:平台已提供完整的基础设施服务
|
||||
> **详细规范**:[云原生开发规范](./08-云原生开发规范.md)
|
||||
> **详细文档**:[平台基础设施规划](../09-架构实施/04-平台基础设施规划.md)
|
||||
|
||||
### 必须复用的平台服务
|
||||
|
||||
**业务模块(ASL/AIA/PKB/DC等)禁止重复实现以下功能:**
|
||||
|
||||
| 服务 | 导入方式 | 用途 |
|
||||
|------|---------|------|
|
||||
| **存储服务** | `import { storage } from '@/common/storage'` | 文件上传下载 |
|
||||
| **日志系统** | `import { logger } from '@/common/logging'` | 标准化日志 |
|
||||
| **异步任务** | `import { jobQueue } from '@/common/jobs'` | 长时间任务 |
|
||||
| **缓存服务** | `import { cache } from '@/common/cache'` | 分布式缓存 |
|
||||
| **数据库** | `import { prisma } from '@/config/database'` | 数据库操作 |
|
||||
| **LLM能力** | `import { LLMFactory } from '@/common/llm'` | LLM调用 |
|
||||
|
||||
---
|
||||
|
||||
### ✅ 正确示例:使用平台服务
|
||||
|
||||
```typescript
|
||||
// backend/src/modules/asl/services/literatureService.ts
|
||||
import { storage } from '@/common/storage'
|
||||
import { logger } from '@/common/logging'
|
||||
import { jobQueue } from '@/common/jobs'
|
||||
import { cache } from '@/common/cache'
|
||||
import { prisma } from '@/config/database'
|
||||
|
||||
export class LiteratureService {
|
||||
async uploadPDF(projectId: string, pdfBuffer: Buffer) {
|
||||
// 1. 使用平台存储服务
|
||||
const key = `asl/projects/${projectId}/pdfs/${Date.now()}.pdf`
|
||||
const url = await storage.upload(key, pdfBuffer)
|
||||
|
||||
// 2. 使用平台日志系统
|
||||
logger.info('PDF uploaded', { projectId, url })
|
||||
|
||||
// 3. 使用平台数据库
|
||||
const literature = await prisma.aslLiterature.create({
|
||||
data: { projectId, pdfUrl: url, pdfFileSize: pdfBuffer.length }
|
||||
})
|
||||
|
||||
// 4. 使用平台缓存
|
||||
await cache.set(`literature:${literature.id}`, literature, 3600)
|
||||
|
||||
return literature
|
||||
}
|
||||
|
||||
async startScreening(projectId: string, literatureIds: string[]) {
|
||||
// 5. 使用平台异步任务(长时间任务必须异步)
|
||||
const job = await jobQueue.push('asl:screening', {
|
||||
projectId,
|
||||
literatureIds
|
||||
})
|
||||
|
||||
logger.info('Screening job created', { jobId: job.id })
|
||||
return { jobId: job.id } // 立即返回
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ❌ 错误示例:重复实现平台能力
|
||||
|
||||
```typescript
|
||||
// ❌ 错误:在业务模块中自己实现存储
|
||||
// backend/src/modules/asl/storage/LocalStorage.ts ← 不应该存在!
|
||||
import fs from 'fs'
|
||||
|
||||
export class LocalStorage {
|
||||
async upload(file: Buffer) {
|
||||
await fs.writeFile('./uploads/file.pdf', file) // ❌ 重复实现
|
||||
return '/uploads/file.pdf'
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ 错误:在业务模块中自己实现日志
|
||||
// backend/src/modules/asl/logger/logger.ts ← 不应该存在!
|
||||
import winston from 'winston'
|
||||
|
||||
export const logger = winston.createLogger({...}) // ❌ 重复实现
|
||||
|
||||
// ❌ 错误:每次新建数据库连接
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
export function getUser() {
|
||||
const prisma = new PrismaClient() // ❌ 连接泄漏
|
||||
return prisma.user.findMany()
|
||||
}
|
||||
```
|
||||
|
||||
**为什么错误?**
|
||||
- ❌ 重复代码,难以维护
|
||||
- ❌ 不同模块实现不一致
|
||||
- ❌ 无法统一切换环境(本地/云端)
|
||||
- ❌ 浪费开发时间
|
||||
- ❌ 云端部署会失败(Serverless限制)
|
||||
|
||||
---
|
||||
|
||||
### 文件上传规范
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:使用存储抽象层
|
||||
const url = await storage.upload('asl/pdf/123.pdf', buffer)
|
||||
|
||||
// ❌ 错误:直接操作文件系统
|
||||
fs.writeFileSync('./uploads/123.pdf', buffer) // Serverless容器重启会丢失
|
||||
|
||||
// ❌ 错误:硬编码存储路径
|
||||
const filePath = 'D:/uploads/123.pdf' // Windows路径,Linux无法运行
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 异步任务规范
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:长时间任务(>10秒)必须异步处理
|
||||
app.post('/screening/start', async (req, res) => {
|
||||
const job = await jobQueue.push('asl:screening', data)
|
||||
res.send({ jobId: job.id }) // 立即返回,不等待完成
|
||||
})
|
||||
|
||||
// 查询进度
|
||||
app.get('/screening/jobs/:id', async (req, res) => {
|
||||
const job = await jobQueue.getJob(req.params.id)
|
||||
res.send({ status: job.status, progress: job.progress })
|
||||
})
|
||||
|
||||
// ❌ 错误:同步等待长时间任务
|
||||
app.post('/screening/start', async (req, res) => {
|
||||
const results = await processAllLiteratures(data) // 可能需要10分钟
|
||||
res.send({ results }) // Serverless 30秒超时!
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 数据库连接规范
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:使用全局Prisma实例
|
||||
import { prisma } from '@/config/database'
|
||||
|
||||
export async function getUsers() {
|
||||
return await prisma.user.findMany()
|
||||
}
|
||||
|
||||
// ❌ 错误:每次新建实例
|
||||
export async function getUsers() {
|
||||
const prisma = new PrismaClient() // 连接数耗尽!
|
||||
return await prisma.user.findMany()
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 日志规范
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:使用平台日志系统
|
||||
import { logger } from '@/common/logging'
|
||||
|
||||
logger.info('Operation successful', { userId, action: 'upload' })
|
||||
logger.error('Operation failed', { error: err.message, userId })
|
||||
|
||||
// ❌ 错误:使用console.log
|
||||
console.log('Operation successful') // 无法集中收集,难以查询
|
||||
|
||||
// ❌ 错误:写本地日志文件
|
||||
fs.appendFileSync('./app.log', 'Operation successful') // Serverless不支持
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 通用规范
|
||||
|
||||
### 代码风格
|
||||
|
||||
857
docs/04-开发规范/06-Git提交规范.md
Normal file
857
docs/04-开发规范/06-Git提交规范.md
Normal file
@@ -0,0 +1,857 @@
|
||||
# Git 提交规范
|
||||
|
||||
> **版本:** v1.0
|
||||
> **创建日期:** 2025-11-16
|
||||
> **适用范围:** 全项目(前端 + 后端 + 文档)
|
||||
> **优先级:** ⭐⭐⭐⭐⭐ P0 必须遵守
|
||||
|
||||
---
|
||||
|
||||
## 📋 目录
|
||||
|
||||
1. [远程仓库配置](#远程仓库配置)
|
||||
2. [Commit Message 规范](#commit-message-规范)
|
||||
3. [分支管理策略](#分支管理策略)
|
||||
4. [中文编码问题解决](#中文编码问题解决)
|
||||
5. [Git 历史重写与维护](#git-历史重写与维护)
|
||||
6. [代码审查流程](#代码审查流程)
|
||||
7. [常见问题与最佳实践](#常见问题与最佳实践)
|
||||
|
||||
---
|
||||
|
||||
## 远程仓库配置
|
||||
|
||||
### 🌐 Gitee 仓库信息
|
||||
|
||||
**仓库地址:**
|
||||
```
|
||||
https://gitee.com/hahafeng117/AIclinicalresearch.git
|
||||
```
|
||||
|
||||
**全局配置:**
|
||||
```bash
|
||||
git config --global user.name "HaHafeng"
|
||||
git config --global user.email "gofeng117@163.com"
|
||||
```
|
||||
|
||||
**中文编码配置:** ⭐ 重要
|
||||
```bash
|
||||
# 确保 Git 正确处理中文文件名和提交信息
|
||||
git config --global core.quotepath false
|
||||
git config --global gui.encoding utf-8
|
||||
git config --global i18n.commit.encoding utf-8
|
||||
git config --global i18n.logoutputencoding utf-8
|
||||
```
|
||||
|
||||
**PowerShell 配置:**
|
||||
```powershell
|
||||
# 在 PowerShell Profile 中添加($PROFILE 文件)
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
$env:LESSCHARSET = 'utf-8'
|
||||
```
|
||||
|
||||
### 克隆仓库
|
||||
|
||||
```bash
|
||||
# 初次克隆
|
||||
git clone https://gitee.com/hahafeng117/AIclinicalresearch.git
|
||||
|
||||
# 查看远程仓库
|
||||
git remote -v
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Commit Message 规范
|
||||
|
||||
### 📝 格式规范
|
||||
|
||||
**标准格式:**
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
|
||||
<body>
|
||||
|
||||
<footer>
|
||||
```
|
||||
|
||||
### Type 类型
|
||||
|
||||
| Type | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `feat` | 新功能 | `feat(asl): 添加标题摘要初筛功能` |
|
||||
| `fix` | Bug 修复 | `fix(frontend): 修复下拉菜单点击问题` |
|
||||
| `docs` | 文档更新 | `docs: 添加 Git 提交规范文档` |
|
||||
| `style` | 代码格式(不影响逻辑) | `style: 格式化代码,统一缩进` |
|
||||
| `refactor` | 重构(不改变功能) | `refactor(backend): 重构用户认证逻辑` |
|
||||
| `perf` | 性能优化 | `perf: 优化查询性能,添加索引` |
|
||||
| `test` | 测试相关 | `test(asl): 添加初筛功能单元测试` |
|
||||
| `chore` | 构建/工具相关 | `chore: 更新依赖包版本` |
|
||||
| `build` | 构建系统 | `build: 配置 Docker 镜像` |
|
||||
| `ci` | CI/CD 配置 | `ci: 添加 GitHub Actions 配置` |
|
||||
| `revert` | 回滚提交 | `revert: 回滚提交 abc123` |
|
||||
|
||||
### Scope 范围
|
||||
|
||||
**模块级别:**
|
||||
- `platform` - 平台基础层
|
||||
- `uam` - 用户与权限中心
|
||||
- `llm` - LLM 大模型网关
|
||||
- `asl` - AI 智能文献
|
||||
- `aia` - AI 智能问答
|
||||
- `pkb` - 个人知识库
|
||||
- `rvw` - 稿件审查系统
|
||||
- `ssa` - 智能统计分析
|
||||
- `admin` - 运营管理端
|
||||
- `frontend` - 前端通用
|
||||
- `backend` - 后端通用
|
||||
- `docs` - 文档
|
||||
|
||||
**功能级别:**
|
||||
- `(asl/screening)` - 文献初筛
|
||||
- `(asl/extraction)` - 数据提取
|
||||
- `(backend/auth)` - 用户认证
|
||||
- `(frontend/layout)` - 页面布局
|
||||
|
||||
### Subject 主题
|
||||
|
||||
**✅ 推荐:**
|
||||
```
|
||||
feat(asl): 实现标题摘要初筛功能
|
||||
fix(frontend): 修复 AgentChatPage conversation 数据提取问题
|
||||
docs: 添加 Day 23-24 工作总结
|
||||
refactor(backend): 优化数据库连接池配置
|
||||
```
|
||||
|
||||
**❌ 避免:**
|
||||
```
|
||||
更新代码 # 太笼统
|
||||
fix bug # 没有说明具体问题
|
||||
修复了一些问题 # 信息不足
|
||||
feat: add feature # 没有指明模块和具体功能
|
||||
```
|
||||
|
||||
**规则:**
|
||||
- ✅ 使用祈使句("添加" 而不是 "添加了")
|
||||
- ✅ 首字母小写
|
||||
- ✅ 不要以句号结尾
|
||||
- ✅ 简洁明了(≤50字符)
|
||||
- ✅ 优先使用英文(避免中文乱码)
|
||||
|
||||
### Body 详细描述
|
||||
|
||||
**可选,用于说明:**
|
||||
- 为什么做这个修改?
|
||||
- 修改了什么?
|
||||
- 有什么影响?
|
||||
|
||||
**示例:**
|
||||
```
|
||||
feat(asl): 实现标题摘要初筛功能
|
||||
|
||||
完成以下功能:
|
||||
- 添加 CSV 文件导入
|
||||
- 实现双模型 AI 判断(Qwen + DeepSeek)
|
||||
- 添加 PICO 配置功能
|
||||
- 实现批量处理和进度显示
|
||||
|
||||
技术细节:
|
||||
- 使用 Papa Parse 解析 CSV
|
||||
- 集成 Dify API 进行 AI 判断
|
||||
- 使用 Ant Design Upload 组件
|
||||
```
|
||||
|
||||
### Footer 脚注
|
||||
|
||||
**可选,用于:**
|
||||
- 关闭 Issue:`Closes #123`
|
||||
- 不兼容变更:`BREAKING CHANGE: 说明`
|
||||
- 关联 Issue:`Refs #456`
|
||||
|
||||
**示例:**
|
||||
```
|
||||
feat(backend): 重构 API 路由结构
|
||||
|
||||
BREAKING CHANGE: API 路由从 /api/xxx 改为 /api/v1/xxx
|
||||
所有前端调用需要更新路径
|
||||
|
||||
Closes #123
|
||||
Refs #456
|
||||
```
|
||||
|
||||
### 完整示例
|
||||
|
||||
**示例 1:新功能**
|
||||
```
|
||||
feat(asl): 实现标题摘要初筛功能
|
||||
|
||||
- 添加 CSV 文件导入
|
||||
- 实现双模型 AI 判断
|
||||
- 添加 PICO 配置功能
|
||||
|
||||
Closes #123
|
||||
```
|
||||
|
||||
**示例 2:Bug 修复**
|
||||
```
|
||||
fix(frontend): 修复 AgentChatPage conversation 数据提取问题
|
||||
|
||||
问题:conversation 数据未正确从响应中提取
|
||||
解决:修改数据解析逻辑,正确处理嵌套结构
|
||||
|
||||
Fixes #234
|
||||
```
|
||||
|
||||
**示例 3:文档更新**
|
||||
```
|
||||
docs: 添加 Day 23-24 工作总结
|
||||
|
||||
完成内容:
|
||||
- 知识库功能开发总结
|
||||
- 遇到的问题与解决方案
|
||||
- 下一步计划
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 分支管理策略
|
||||
|
||||
### 🌿 分支类型
|
||||
|
||||
```
|
||||
master (main)
|
||||
├── develop
|
||||
├── feature/xxx
|
||||
├── fix/xxx
|
||||
├── hotfix/xxx
|
||||
└── release/xxx
|
||||
```
|
||||
|
||||
### 主分支
|
||||
|
||||
**master / main**
|
||||
- 生产环境分支
|
||||
- 永远保持可部署状态
|
||||
- 只接受来自 `release` 或 `hotfix` 的合并
|
||||
- 每次合并都打 tag(如 `v1.0.0`)
|
||||
|
||||
**develop**
|
||||
- 开发主分支
|
||||
- 最新的开发进度
|
||||
- 接受来自 `feature` 和 `fix` 的合并
|
||||
|
||||
### 辅助分支
|
||||
|
||||
**feature/xxx - 功能分支**
|
||||
```bash
|
||||
# 创建功能分支
|
||||
git checkout -b feature/asl-screening develop
|
||||
|
||||
# 开发完成后合并到 develop
|
||||
git checkout develop
|
||||
git merge --no-ff feature/asl-screening
|
||||
git branch -d feature/asl-screening
|
||||
git push origin develop
|
||||
```
|
||||
|
||||
**fix/xxx - Bug 修复分支**
|
||||
```bash
|
||||
# 从 develop 创建修复分支
|
||||
git checkout -b fix/dropdown-click develop
|
||||
|
||||
# 修复完成后合并到 develop
|
||||
git checkout develop
|
||||
git merge --no-ff fix/dropdown-click
|
||||
git branch -d fix/dropdown-click
|
||||
```
|
||||
|
||||
**hotfix/xxx - 紧急修复分支**
|
||||
```bash
|
||||
# 从 master 创建紧急修复分支
|
||||
git checkout -b hotfix/critical-bug master
|
||||
|
||||
# 修复完成后同时合并到 master 和 develop
|
||||
git checkout master
|
||||
git merge --no-ff hotfix/critical-bug
|
||||
git tag -a v1.0.1 -m "Hotfix: 修复严重 bug"
|
||||
|
||||
git checkout develop
|
||||
git merge --no-ff hotfix/critical-bug
|
||||
|
||||
git branch -d hotfix/critical-bug
|
||||
```
|
||||
|
||||
**release/xxx - 发布分支**
|
||||
```bash
|
||||
# 从 develop 创建发布分支
|
||||
git checkout -b release/v1.0.0 develop
|
||||
|
||||
# 发布准备完成后合并到 master 和 develop
|
||||
git checkout master
|
||||
git merge --no-ff release/v1.0.0
|
||||
git tag -a v1.0.0 -m "Release v1.0.0"
|
||||
|
||||
git checkout develop
|
||||
git merge --no-ff release/v1.0.0
|
||||
|
||||
git branch -d release/v1.0.0
|
||||
```
|
||||
|
||||
### 分支命名规范
|
||||
|
||||
**✅ 推荐:**
|
||||
```
|
||||
feature/asl-screening # 功能:AI 智能文献初筛
|
||||
feature/user-authentication # 功能:用户认证
|
||||
fix/dropdown-click # 修复:下拉菜单点击
|
||||
fix/api-timeout # 修复:API 超时
|
||||
hotfix/security-vulnerability # 紧急修复:安全漏洞
|
||||
release/v1.0.0 # 发布:v1.0.0
|
||||
```
|
||||
|
||||
**❌ 避免:**
|
||||
```
|
||||
feature/new-feature # 太笼统
|
||||
fix/bug # 没有说明具体问题
|
||||
test # 临时分支,应该有明确目的
|
||||
my-branch # 个人分支,应该说明用途
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 中文编码问题解决
|
||||
|
||||
### ⚠️ 问题描述
|
||||
|
||||
在 Windows PowerShell 环境下,Git 提交信息中的中文可能会出现乱码,如:
|
||||
|
||||
```
|
||||
124dc35 feat: 娣诲姞鏅鸿兘闂瓟鍔熻兘锛堟棤椤圭洰/鏅鸿兘浣撴蹇电殑绾璇濓級
|
||||
```
|
||||
|
||||
实际应该是:
|
||||
```
|
||||
124dc35 feat: 添加智能问答功能(无项目/智能体概念的纯对话)
|
||||
```
|
||||
|
||||
### ✅ 预防措施
|
||||
|
||||
**1. Git 全局配置**
|
||||
```bash
|
||||
git config --global core.quotepath false
|
||||
git config --global gui.encoding utf-8
|
||||
git config --global i18n.commit.encoding utf-8
|
||||
git config --global i18n.logoutputencoding utf-8
|
||||
```
|
||||
|
||||
**2. PowerShell 配置**
|
||||
|
||||
编辑 PowerShell Profile:
|
||||
```powershell
|
||||
# 打开 Profile
|
||||
notepad $PROFILE
|
||||
|
||||
# 添加以下内容
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
$env:LESSCHARSET = 'utf-8'
|
||||
```
|
||||
|
||||
**3. 项目配置文件**
|
||||
|
||||
项目根目录已包含:
|
||||
- `.editorconfig` - 统一编辑器编码配置(UTF-8)
|
||||
- `.gitattributes` - 统一 Git 文件编码处理
|
||||
|
||||
**4. 最佳实践:使用英文提交信息** ⭐ 推荐
|
||||
|
||||
```bash
|
||||
# ✅ 推荐:使用英文
|
||||
git commit -m "feat(asl): add screening feature"
|
||||
|
||||
# ⚪ 可选:使用中文(需要正确配置编码)
|
||||
git commit -m "feat(asl): 添加初筛功能"
|
||||
```
|
||||
|
||||
### 🔧 修复历史提交中的中文乱码
|
||||
|
||||
**使用修复脚本:**
|
||||
|
||||
项目根目录提供了 `fix-git-commit-messages.ps1` 脚本:
|
||||
|
||||
```powershell
|
||||
# 1. 查看脚本说明
|
||||
Get-Help .\fix-git-commit-messages.ps1
|
||||
|
||||
# 2. 运行脚本(交互式)
|
||||
.\fix-git-commit-messages.ps1
|
||||
|
||||
# 3. 运行脚本(跳过确认)
|
||||
.\fix-git-commit-messages.ps1 -SkipConfirm
|
||||
```
|
||||
|
||||
**手动修复流程:**
|
||||
|
||||
1. **创建备份分支**
|
||||
```bash
|
||||
git branch backup-before-fix
|
||||
```
|
||||
|
||||
2. **准备提交映射表**
|
||||
|
||||
编辑 `fix-git-commit-messages.ps1` 中的 `$commitMapping` 哈希表:
|
||||
```powershell
|
||||
$commitMapping = @{
|
||||
"124dc35" = "feat: add general chat feature (without project/agent concept)"
|
||||
"9618eca" = "fix: AgentChatPage conversation data extraction issue"
|
||||
# 添加更多需要修复的提交...
|
||||
}
|
||||
```
|
||||
|
||||
3. **执行修复**
|
||||
```powershell
|
||||
.\fix-git-commit-messages.ps1 -SkipConfirm
|
||||
```
|
||||
|
||||
4. **验证结果**
|
||||
```bash
|
||||
# 查看修复后的提交历史
|
||||
git log --oneline -20
|
||||
|
||||
# 检查是否还有中文乱码
|
||||
git log --all --oneline | Select-String "[\u4e00-\u9fa5]"
|
||||
```
|
||||
|
||||
5. **强制推送到远程**
|
||||
```bash
|
||||
git push --force origin master
|
||||
```
|
||||
|
||||
6. **清理备份**
|
||||
```bash
|
||||
# 如果修复成功,删除备份分支
|
||||
git branch -D backup-before-fix
|
||||
|
||||
# 清理临时引用
|
||||
git for-each-ref --format="%(refname)" refs/original/ | ForEach-Object { git update-ref -d $_ }
|
||||
|
||||
# 运行垃圾回收
|
||||
git reflog expire --expire=now --all
|
||||
git gc --prune=now --aggressive
|
||||
```
|
||||
|
||||
### ⚠️ 注意事项
|
||||
|
||||
**修复历史提交的风险:**
|
||||
- ⚠️ 会改变所有后续提交的 SHA-1 哈希值
|
||||
- ⚠️ 需要强制推送(`--force`)到远程仓库
|
||||
- ⚠️ 会影响所有协作者的本地仓库
|
||||
- ⚠️ 必须通知团队成员重新克隆或重置
|
||||
|
||||
**建议:**
|
||||
- ✅ 在项目早期进行修复
|
||||
- ✅ 提前通知所有团队成员
|
||||
- ✅ 创建备份分支
|
||||
- ✅ 优先使用英文提交信息,避免编码问题
|
||||
|
||||
---
|
||||
|
||||
## Git 历史重写与维护
|
||||
|
||||
### 🛠️ 常用命令
|
||||
|
||||
**修改最后一次提交:**
|
||||
```bash
|
||||
# 修改提交信息
|
||||
git commit --amend -m "new message"
|
||||
|
||||
# 添加遗漏的文件
|
||||
git add forgotten-file.txt
|
||||
git commit --amend --no-edit
|
||||
```
|
||||
|
||||
**重置提交:**
|
||||
```bash
|
||||
# 软重置(保留更改)
|
||||
git reset --soft HEAD~1
|
||||
|
||||
# 混合重置(默认,保留工作区更改)
|
||||
git reset HEAD~1
|
||||
|
||||
# 硬重置(丢弃所有更改)⚠️ 危险
|
||||
git reset --hard HEAD~1
|
||||
```
|
||||
|
||||
**交互式变基:**
|
||||
```bash
|
||||
# 重新排列、合并、编辑最近 3 次提交
|
||||
git rebase -i HEAD~3
|
||||
|
||||
# 变基到 develop 分支
|
||||
git rebase develop
|
||||
```
|
||||
|
||||
**拣选提交:**
|
||||
```bash
|
||||
# 将特定提交应用到当前分支
|
||||
git cherry-pick <commit-hash>
|
||||
```
|
||||
|
||||
**过滤历史:**
|
||||
```bash
|
||||
# 修改所有历史提交的作者信息
|
||||
git filter-branch --env-filter '
|
||||
if [ "$GIT_COMMITTER_EMAIL" = "old@email.com" ]
|
||||
then
|
||||
export GIT_COMMITTER_EMAIL="new@email.com"
|
||||
export GIT_AUTHOR_EMAIL="new@email.com"
|
||||
fi
|
||||
' --tag-name-filter cat -- --all
|
||||
|
||||
# 从所有历史中删除文件(如敏感信息)
|
||||
git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
|
||||
```
|
||||
|
||||
### 强制推送规范
|
||||
|
||||
**⚠️ 危险操作,需谨慎!**
|
||||
|
||||
**基本强制推送:**
|
||||
```bash
|
||||
git push --force origin master
|
||||
```
|
||||
|
||||
**更安全的强制推送:** ⭐ 推荐
|
||||
```bash
|
||||
# 只有当远程分支与本地预期一致时才推送
|
||||
git push --force-with-lease origin master
|
||||
```
|
||||
|
||||
**禁止强制推送的分支:**
|
||||
- ❌ `master` / `main` (除非修复历史问题)
|
||||
- ❌ `develop` (需要团队同意)
|
||||
- ✅ `feature/*` (可以,仅影响个人)
|
||||
- ✅ `fix/*` (可以,仅影响个人)
|
||||
|
||||
**强制推送前检查清单:**
|
||||
- [ ] 是否已创建备份分支?
|
||||
- [ ] 是否已通知团队成员?
|
||||
- [ ] 是否确认没有其他人基于当前分支工作?
|
||||
- [ ] 是否了解强制推送的后果?
|
||||
- [ ] 是否可以使用 `--force-with-lease` 替代 `--force`?
|
||||
|
||||
### 垃圾回收与清理
|
||||
|
||||
**清理未追踪文件:**
|
||||
```bash
|
||||
# 预览将被删除的文件
|
||||
git clean -n
|
||||
|
||||
# 删除未追踪文件
|
||||
git clean -f
|
||||
|
||||
# 删除未追踪文件和目录
|
||||
git clean -fd
|
||||
|
||||
# 包括 .gitignore 中的文件
|
||||
git clean -fdx
|
||||
```
|
||||
|
||||
**清理过期引用:**
|
||||
```bash
|
||||
# 清理过期的 reflog
|
||||
git reflog expire --expire=now --all
|
||||
|
||||
# 运行垃圾回收
|
||||
git gc --prune=now
|
||||
|
||||
# 积极的垃圾回收(更彻底,更慢)
|
||||
git gc --prune=now --aggressive
|
||||
```
|
||||
|
||||
**查看仓库大小:**
|
||||
```bash
|
||||
# 查看 .git 目录大小
|
||||
git count-objects -vH
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 代码审查流程
|
||||
|
||||
### 👥 Pull Request / Merge Request 规范
|
||||
|
||||
**PR/MR 标题:**
|
||||
```
|
||||
[类型] 简短描述(≤50字符)
|
||||
|
||||
示例:
|
||||
[Feature] 实现 AI 智能文献标题摘要初筛
|
||||
[Fix] 修复下拉菜单点击无响应问题
|
||||
[Refactor] 重构用户认证逻辑
|
||||
[Docs] 更新 Git 提交规范文档
|
||||
```
|
||||
|
||||
**PR/MR 描述模板:**
|
||||
```markdown
|
||||
## 📝 变更说明
|
||||
简要描述这个 PR 做了什么
|
||||
|
||||
## 🎯 关联 Issue
|
||||
Closes #123
|
||||
|
||||
## ✨ 主要变更
|
||||
- 添加了 XXX 功能
|
||||
- 修复了 YYY 问题
|
||||
- 重构了 ZZZ 模块
|
||||
|
||||
## 🧪 测试
|
||||
- [ ] 单元测试通过
|
||||
- [ ] 集成测试通过
|
||||
- [ ] 手动测试完成
|
||||
|
||||
## 📸 截图(如果适用)
|
||||
[添加截图]
|
||||
|
||||
## ⚠️ 注意事项
|
||||
需要注意的特殊情况或依赖
|
||||
|
||||
## 📋 检查清单
|
||||
- [ ] 代码符合项目规范
|
||||
- [ ] 添加了必要的测试
|
||||
- [ ] 更新了相关文档
|
||||
- [ ] 通过了所有 CI 检查
|
||||
```
|
||||
|
||||
### Code Review 检查清单
|
||||
|
||||
**代码质量:**
|
||||
- [ ] 代码逻辑正确,无明显 bug
|
||||
- [ ] 符合代码规范(ESLint/Prettier)
|
||||
- [ ] 变量、函数命名清晰
|
||||
- [ ] 无重复代码(DRY 原则)
|
||||
- [ ] 适当的注释和文档
|
||||
|
||||
**功能完整性:**
|
||||
- [ ] 实现了所有需求
|
||||
- [ ] 边界条件处理正确
|
||||
- [ ] 错误处理完善
|
||||
- [ ] 无性能问题
|
||||
|
||||
**测试:**
|
||||
- [ ] 包含单元测试
|
||||
- [ ] 测试覆盖率达标
|
||||
- [ ] 测试用例合理
|
||||
|
||||
**安全性:**
|
||||
- [ ] 无 SQL 注入风险
|
||||
- [ ] 无 XSS 风险
|
||||
- [ ] 敏感信息未泄露
|
||||
- [ ] 权限检查正确
|
||||
|
||||
**文档:**
|
||||
- [ ] API 文档已更新
|
||||
- [ ] README 已更新(如需要)
|
||||
- [ ] 数据库变更已记录
|
||||
|
||||
### Review 响应时间
|
||||
|
||||
| 优先级 | 响应时间 | 说明 |
|
||||
|--------|----------|------|
|
||||
| 🔴 紧急 | 2 小时内 | 生产环境 Bug、安全漏洞 |
|
||||
| 🟡 高 | 1 天内 | 阻塞其他开发的功能 |
|
||||
| 🟢 中 | 2 天内 | 常规功能和修复 |
|
||||
| ⚪ 低 | 3 天内 | 优化、重构、文档 |
|
||||
|
||||
---
|
||||
|
||||
## 常见问题与最佳实践
|
||||
|
||||
### 🤔 常见问题
|
||||
|
||||
**Q1: 提交后发现有错误,如何修改?**
|
||||
```bash
|
||||
# 修改后重新提交(未推送到远程)
|
||||
git add .
|
||||
git commit --amend --no-edit
|
||||
|
||||
# 如果已推送,需要强制推送
|
||||
git push --force-with-lease origin feature/xxx
|
||||
```
|
||||
|
||||
**Q2: 如何撤销已推送的提交?**
|
||||
```bash
|
||||
# 方式 1:创建一个新的 revert 提交(推荐)
|
||||
git revert <commit-hash>
|
||||
git push origin master
|
||||
|
||||
# 方式 2:重置后强制推送(危险,不推荐)
|
||||
git reset --hard HEAD~1
|
||||
git push --force origin master
|
||||
```
|
||||
|
||||
**Q3: 如何解决合并冲突?**
|
||||
```bash
|
||||
# 1. 拉取最新代码
|
||||
git pull origin develop
|
||||
|
||||
# 2. 手动解决冲突文件
|
||||
|
||||
# 3. 标记为已解决
|
||||
git add <conflicted-files>
|
||||
|
||||
# 4. 完成合并
|
||||
git commit -m "chore: merge develop into feature/xxx"
|
||||
```
|
||||
|
||||
**Q4: 如何查看某个文件的修改历史?**
|
||||
```bash
|
||||
# 查看文件的提交历史
|
||||
git log --follow -- path/to/file
|
||||
|
||||
# 查看文件的详细修改内容
|
||||
git log -p -- path/to/file
|
||||
|
||||
# 查看每行代码的最后修改信息
|
||||
git blame path/to/file
|
||||
```
|
||||
|
||||
**Q5: 如何恢复已删除的文件?**
|
||||
```bash
|
||||
# 查找删除该文件的提交
|
||||
git log --all --full-history -- path/to/file
|
||||
|
||||
# 恢复文件(从删除前的提交)
|
||||
git checkout <commit-hash>^ -- path/to/file
|
||||
```
|
||||
|
||||
**Q6: 如何临时保存未提交的更改?**
|
||||
```bash
|
||||
# 保存更改
|
||||
git stash save "描述信息"
|
||||
|
||||
# 查看保存的更改
|
||||
git stash list
|
||||
|
||||
# 恢复更改
|
||||
git stash pop
|
||||
|
||||
# 恢复特定的 stash
|
||||
git stash apply stash@{0}
|
||||
```
|
||||
|
||||
**Q7: 提交信息写错了怎么办?**
|
||||
```bash
|
||||
# 修改最后一次提交信息(未推送)
|
||||
git commit --amend -m "正确的提交信息"
|
||||
|
||||
# 如果已推送
|
||||
git commit --amend -m "正确的提交信息"
|
||||
git push --force-with-lease origin branch-name
|
||||
|
||||
# 修改更早的提交信息
|
||||
git rebase -i HEAD~3 # 修改最近 3 次提交
|
||||
# 在编辑器中将 pick 改为 reword
|
||||
```
|
||||
|
||||
### 💡 最佳实践
|
||||
|
||||
**提交频率:**
|
||||
- ✅ 小步快跑,频繁提交
|
||||
- ✅ 每个提交只做一件事
|
||||
- ✅ 提交可编译、可运行的代码
|
||||
- ❌ 不要积累大量更改后一次提交
|
||||
|
||||
**提交内容:**
|
||||
- ✅ 只提交相关的更改
|
||||
- ✅ 使用 `.gitignore` 排除无关文件
|
||||
- ❌ 不要提交生成的文件(`node_modules/`, `dist/`, `.env`)
|
||||
- ❌ 不要提交敏感信息(密码、密钥)
|
||||
|
||||
**分支管理:**
|
||||
- ✅ 从最新的 `develop` 创建功能分支
|
||||
- ✅ 定期将 `develop` 合并到功能分支
|
||||
- ✅ 功能完成后及时合并和删除分支
|
||||
- ❌ 不要长期保持功能分支不合并
|
||||
|
||||
**协作:**
|
||||
- ✅ 推送前先拉取(`git pull --rebase`)
|
||||
- ✅ 及时响应 Code Review
|
||||
- ✅ 主动沟通冲突和问题
|
||||
- ❌ 不要直接推送到 `master` 分支
|
||||
|
||||
### 🔧 有用的 Git 别名
|
||||
|
||||
在 `~/.gitconfig` 中添加:
|
||||
|
||||
```ini
|
||||
[alias]
|
||||
# 状态和日志
|
||||
st = status
|
||||
co = checkout
|
||||
br = branch
|
||||
ci = commit
|
||||
|
||||
# 美化的日志
|
||||
lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
|
||||
|
||||
# 简洁的状态
|
||||
s = status -s
|
||||
|
||||
# 最近的提交
|
||||
last = log -1 HEAD
|
||||
|
||||
# 撤销最后一次提交(保留更改)
|
||||
undo = reset --soft HEAD~1
|
||||
|
||||
# 查看差异
|
||||
d = diff
|
||||
dc = diff --cached
|
||||
|
||||
# 拉取并变基
|
||||
up = pull --rebase
|
||||
|
||||
# 推送当前分支
|
||||
push-current = "!git push -u origin $(git rev-parse --abbrev-ref HEAD)"
|
||||
```
|
||||
|
||||
使用示例:
|
||||
```bash
|
||||
git st # 相当于 git status
|
||||
git lg # 美化的日志
|
||||
git push-current # 推送当前分支
|
||||
```
|
||||
|
||||
### 📚 推荐阅读
|
||||
|
||||
**Git 学习资源:**
|
||||
- [Pro Git Book(中文版)](https://git-scm.com/book/zh/v2)
|
||||
- [Git 官方文档](https://git-scm.com/docs)
|
||||
- [Learn Git Branching(交互式教程)](https://learngitbranching.js.org/)
|
||||
|
||||
**提交规范:**
|
||||
- [Conventional Commits](https://www.conventionalcommits.org/)
|
||||
- [Angular Commit Guidelines](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit)
|
||||
|
||||
---
|
||||
|
||||
## 📞 获取帮助
|
||||
|
||||
**遇到问题?**
|
||||
1. 查看本文档的常见问题部分
|
||||
2. 搜索 Git 官方文档
|
||||
3. 向团队技术负责人咨询
|
||||
4. 在团队群聊中讨论
|
||||
|
||||
**文档反馈:**
|
||||
如果发现文档错误或有改进建议,请:
|
||||
1. 提交 Issue
|
||||
2. 直接修改并提交 PR
|
||||
3. 联系文档维护人
|
||||
|
||||
---
|
||||
|
||||
**最后更新:** 2025-11-16
|
||||
**维护人:** 技术架构师
|
||||
**版本历史:**
|
||||
- v1.0 (2025-11-16): 初始版本,包含完整的 Git 规范和中文乱码解决方案
|
||||
|
||||
635
docs/04-开发规范/08-云原生开发规范.md
Normal file
635
docs/04-开发规范/08-云原生开发规范.md
Normal file
@@ -0,0 +1,635 @@
|
||||
# 云原生开发规范
|
||||
|
||||
> **文档版本:** V1.0
|
||||
> **创建日期:** 2025-11-16
|
||||
> **适用对象:** 所有开发人员
|
||||
> **强制性:** ✅ 必须遵守
|
||||
> **维护者:** 架构团队
|
||||
|
||||
---
|
||||
|
||||
## 📋 文档说明
|
||||
|
||||
本文档定义云原生环境(Serverless SAE + RDS + OSS)下的**代码规范**,所有业务模块(ASL、AIA、PKB等)必须遵守。
|
||||
|
||||
**阅读时间**:10 分钟
|
||||
**检查频率**:每次代码提交前
|
||||
|
||||
---
|
||||
|
||||
## 🌟 核心原则:复用平台能力
|
||||
|
||||
> **⭐ 重要提示(2025-11-16 更新)**:平台已提供完整的基础设施服务
|
||||
> **详细文档**:[平台基础设施规划](../09-架构实施/04-平台基础设施规划.md)
|
||||
|
||||
### 平台已提供的服务
|
||||
|
||||
**业务模块(ASL/AIA/PKB/DC等)应该复用以下平台能力,禁止重复实现:**
|
||||
|
||||
| 服务 | 导入方式 | 用途 | 文档 |
|
||||
|------|---------|------|------|
|
||||
| **存储服务** | `import { storage } from '@/common/storage'` | 文件上传下载 | ✅ 平台级 |
|
||||
| **日志系统** | `import { logger } from '@/common/logging'` | 标准化日志 | ✅ 平台级 |
|
||||
| **异步任务** | `import { jobQueue } from '@/common/jobs'` | 长时间任务 | ✅ 平台级 |
|
||||
| **缓存服务** | `import { cache } from '@/common/cache'` | 分布式缓存 | ✅ 平台级 |
|
||||
| **数据库** | `import { prisma } from '@/config/database'` | 数据库操作 | ✅ 平台级 |
|
||||
| **LLM能力** | `import { LLMFactory } from '@/common/llm'` | LLM调用 | ✅ 平台级 |
|
||||
|
||||
### 示例:正确使用平台服务
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:直接导入平台服务
|
||||
import { storage } from '@/common/storage'
|
||||
import { logger } from '@/common/logging'
|
||||
import { jobQueue } from '@/common/jobs'
|
||||
import { prisma } from '@/config/database'
|
||||
|
||||
export class LiteratureService {
|
||||
async uploadPDF(projectId: string, pdfBuffer: Buffer) {
|
||||
// 1. 使用平台存储服务
|
||||
const key = `asl/projects/${projectId}/pdfs/${Date.now()}.pdf`
|
||||
const url = await storage.upload(key, pdfBuffer)
|
||||
|
||||
// 2. 使用平台日志系统
|
||||
logger.info('PDF uploaded', { projectId, url })
|
||||
|
||||
// 3. 使用平台数据库
|
||||
const literature = await prisma.aslLiterature.create({
|
||||
data: { projectId, pdfUrl: url }
|
||||
})
|
||||
|
||||
return literature
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ❌ 错误:重复实现平台能力
|
||||
|
||||
```typescript
|
||||
// ❌ 错误:在业务模块中自己实现存储
|
||||
// backend/src/modules/asl/storage/LocalStorage.ts ← 不应该存在!
|
||||
export class LocalStorage {
|
||||
async upload(file: Buffer) {
|
||||
await fs.writeFile('./uploads/file.pdf', file) // ❌ 重复实现
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ 错误:在业务模块中自己实现日志
|
||||
// backend/src/modules/asl/logger/logger.ts ← 不应该存在!
|
||||
export const logger = winston.createLogger({...}) // ❌ 重复实现
|
||||
```
|
||||
|
||||
**原因**:
|
||||
- ❌ 重复代码,难以维护
|
||||
- ❌ 不同模块实现不一致
|
||||
- ❌ 无法统一切换环境(本地/云端)
|
||||
- ❌ 浪费开发时间
|
||||
|
||||
---
|
||||
|
||||
## ✅ 推荐做法(DO)
|
||||
|
||||
### 1. 文件存储 ✅
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:使用存储抽象层
|
||||
import { storage } from '@/common/storage/StorageFactory'
|
||||
|
||||
export async function uploadFile(file: Buffer, filename: string) {
|
||||
const key = `asl/pdfs/${Date.now()}-${filename}`
|
||||
const url = await storage.upload(key, file)
|
||||
return url
|
||||
}
|
||||
|
||||
// ✅ 正确:Excel 直接从内存解析
|
||||
import * as xlsx from 'xlsx'
|
||||
|
||||
export async function importExcel(buffer: Buffer) {
|
||||
const workbook = xlsx.read(buffer, { type: 'buffer' }) // 内存解析
|
||||
const data = xlsx.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]])
|
||||
return data
|
||||
}
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- 容器重启不会丢失文件
|
||||
- 本地开发和生产环境代码一致
|
||||
- 自动根据环境变量切换存储方式
|
||||
|
||||
---
|
||||
|
||||
### 2. 数据库连接 ✅
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:使用全局 Prisma Client
|
||||
import { prisma } from '@/config/database'
|
||||
|
||||
export async function createProject(data: any) {
|
||||
return await prisma.aslScreeningProject.create({ data })
|
||||
}
|
||||
|
||||
// ✅ 正确:批量操作使用事务
|
||||
export async function importLiteratures(literatures: any[]) {
|
||||
return await prisma.$transaction(async (tx) => {
|
||||
return await tx.aslLiterature.createMany({ data: literatures })
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- 全局实例复用连接
|
||||
- 避免连接数耗尽
|
||||
- 事务保证数据一致性
|
||||
|
||||
---
|
||||
|
||||
### 3. 环境变量配置 ✅
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:统一配置管理
|
||||
// backend/src/config/env.ts
|
||||
export const config = {
|
||||
llm: {
|
||||
apiKey: process.env.LLM_API_KEY!,
|
||||
baseUrl: process.env.LLM_BASE_URL!,
|
||||
},
|
||||
oss: {
|
||||
region: process.env.OSS_REGION!,
|
||||
bucket: process.env.OSS_BUCKET!,
|
||||
},
|
||||
database: {
|
||||
url: process.env.DATABASE_URL!,
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 正确:使用配置对象
|
||||
import { config } from '@/config/env'
|
||||
const apiKey = config.llm.apiKey
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- 配置集中管理
|
||||
- 类型安全
|
||||
- 便于切换环境
|
||||
|
||||
---
|
||||
|
||||
### 4. 长时间任务处理 ✅
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:异步任务 + 进度轮询
|
||||
export async function startScreening(req, res) {
|
||||
// 1. 创建任务记录
|
||||
const task = await prisma.aslScreeningTask.create({
|
||||
data: {
|
||||
projectId: req.params.projectId,
|
||||
status: 'pending',
|
||||
totalItems: 100,
|
||||
}
|
||||
})
|
||||
|
||||
// 2. 立即返回任务ID
|
||||
res.send({ success: true, taskId: task.id })
|
||||
|
||||
// 3. 后台异步执行(不阻塞请求)
|
||||
processScreeningAsync(task.id).catch(err => {
|
||||
console.error('筛选任务失败:', err)
|
||||
})
|
||||
}
|
||||
|
||||
// 前端轮询进度
|
||||
export async function getTaskProgress(req, res) {
|
||||
const task = await prisma.aslScreeningTask.findUnique({
|
||||
where: { id: req.params.taskId }
|
||||
})
|
||||
|
||||
res.send({
|
||||
status: task.status,
|
||||
progress: Math.round((task.completedItems / task.totalItems) * 100)
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- 避免请求超时(SAE默认30秒)
|
||||
- 用户体验更好
|
||||
- 支持批量任务
|
||||
|
||||
---
|
||||
|
||||
### 5. 日志输出 ✅
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:使用 logger 输出到 stdout
|
||||
import pino from 'pino'
|
||||
|
||||
const logger = pino({
|
||||
level: process.env.LOG_LEVEL || 'info'
|
||||
})
|
||||
|
||||
export async function uploadFile(req, res) {
|
||||
logger.info({
|
||||
userId: req.userId,
|
||||
filename: req.file.filename
|
||||
}, 'File uploaded')
|
||||
|
||||
// ... 上传逻辑
|
||||
}
|
||||
|
||||
// ✅ 正确:结构化日志
|
||||
logger.error({
|
||||
error: err.message,
|
||||
stack: err.stack,
|
||||
userId: req.userId
|
||||
}, 'Upload failed')
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- SAE 自动采集 stdout 日志
|
||||
- 结构化便于查询分析
|
||||
- 集中查看,不会丢失
|
||||
|
||||
---
|
||||
|
||||
### 6. 错误处理 ✅
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:统一错误处理
|
||||
export async function uploadPdf(req, res) {
|
||||
try {
|
||||
const file = await req.file()
|
||||
const buffer = await file.toBuffer()
|
||||
|
||||
const url = await storage.upload(key, buffer)
|
||||
|
||||
res.send({ success: true, url })
|
||||
} catch (error) {
|
||||
logger.error({ error }, 'PDF upload failed')
|
||||
|
||||
res.status(500).send({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'UPLOAD_FAILED',
|
||||
message: '文件上传失败,请重试'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- 用户看到友好错误信息
|
||||
- 日志记录详细错误
|
||||
- 不暴露内部实现
|
||||
|
||||
---
|
||||
|
||||
### 7. 临时文件处理 ✅
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:/tmp 目录用完立即删除
|
||||
import fs from 'fs/promises'
|
||||
import path from 'path'
|
||||
|
||||
export async function extractPdfText(ossKey: string): Promise<string> {
|
||||
const tmpPath = path.join('/tmp', `${Date.now()}.pdf`)
|
||||
|
||||
try {
|
||||
// 1. 从 OSS 下载到 /tmp
|
||||
await storage.download(ossKey, tmpPath)
|
||||
|
||||
// 2. 提取文本
|
||||
const text = await extractWithNougat(tmpPath)
|
||||
|
||||
return text
|
||||
} finally {
|
||||
// 3. 立即删除临时文件(无论成功失败)
|
||||
try {
|
||||
await fs.unlink(tmpPath)
|
||||
} catch (err) {
|
||||
logger.warn({ tmpPath }, 'Failed to delete temp file')
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- /tmp 容量有限(512MB)
|
||||
- 容器重启会清空
|
||||
- 避免磁盘占满
|
||||
|
||||
---
|
||||
|
||||
## ❌ 禁止做法(DON'T)
|
||||
|
||||
### 1. 本地文件存储 ❌
|
||||
|
||||
```typescript
|
||||
// ❌ 禁止:本地文件系统存储
|
||||
import fs from 'fs'
|
||||
|
||||
export async function uploadFile(req, res) {
|
||||
const file = await req.file()
|
||||
const buffer = await file.toBuffer()
|
||||
|
||||
// ❌ 错误:容器重启丢失
|
||||
fs.writeFileSync('./uploads/file.pdf', buffer)
|
||||
|
||||
res.send({ url: '/uploads/file.pdf' })
|
||||
}
|
||||
|
||||
// ❌ 禁止:依赖本地路径
|
||||
const uploadDir = '/var/app/uploads'
|
||||
const filePath = path.join(uploadDir, filename)
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- 容器重启或扩容后文件丢失
|
||||
- 多实例间无法共享文件
|
||||
- 磁盘空间有限
|
||||
|
||||
**正确做法**:使用 `storage.upload()` 上传到 OSS
|
||||
|
||||
---
|
||||
|
||||
### 2. 内存缓存 ❌
|
||||
|
||||
```typescript
|
||||
// ❌ 禁止:内存缓存(多实例不共享)
|
||||
const cache = new Map<string, any>()
|
||||
|
||||
export async function getProject(id: string) {
|
||||
// ❌ 错误:扩容后其他实例读不到缓存
|
||||
if (cache.has(id)) {
|
||||
return cache.get(id)
|
||||
}
|
||||
|
||||
const project = await prisma.aslScreeningProject.findUnique({ where: { id } })
|
||||
cache.set(id, project)
|
||||
return project
|
||||
}
|
||||
|
||||
// ❌ 禁止:全局变量存储状态
|
||||
let taskStatus = {} // 多实例不同步
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- 多实例间数据不同步
|
||||
- 扩容后缓存失效
|
||||
- 内存占用不可控
|
||||
|
||||
**正确做法**:使用 Redis 或数据库
|
||||
|
||||
---
|
||||
|
||||
### 3. 硬编码配置 ❌
|
||||
|
||||
```typescript
|
||||
// ❌ 禁止:硬编码
|
||||
const LLM_API_KEY = 'sk-xxx'
|
||||
const DB_HOST = '192.168.1.100'
|
||||
const OSS_BUCKET = 'my-bucket'
|
||||
|
||||
// ❌ 禁止:写死端口
|
||||
app.listen(3001)
|
||||
|
||||
// ❌ 禁止:写死域名
|
||||
const baseUrl = 'https://api.example.com'
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- 无法切换环境
|
||||
- 安全风险(密钥泄露)
|
||||
- 部署困难
|
||||
|
||||
**正确做法**:
|
||||
```typescript
|
||||
// ✅ 使用环境变量
|
||||
const apiKey = process.env.LLM_API_KEY
|
||||
const port = process.env.PORT || 3001
|
||||
app.listen(port)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 同步长任务 ❌
|
||||
|
||||
```typescript
|
||||
// ❌ 禁止:同步处理长任务
|
||||
export async function screenLiteratures(req, res) {
|
||||
const literatures = await prisma.aslLiterature.findMany({...})
|
||||
|
||||
// ❌ 错误:100篇可能超过30秒
|
||||
for (const lit of literatures) {
|
||||
await llmScreening(lit) // 每篇2-3秒
|
||||
}
|
||||
|
||||
res.send({ success: true }) // 可能已经超时
|
||||
}
|
||||
|
||||
// ❌ 禁止:没有超时保护
|
||||
const result = await axios.get(url) // 可能永久等待
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- SAE 请求超时 30 秒
|
||||
- 前端等待时间过长
|
||||
- 无法显示进度
|
||||
|
||||
**正确做法**:异步任务 + 进度轮询(见 DO 第4条)
|
||||
|
||||
---
|
||||
|
||||
### 5. 本地日志文件 ❌
|
||||
|
||||
```typescript
|
||||
// ❌ 禁止:写入本地文件
|
||||
import fs from 'fs'
|
||||
|
||||
export function logError(error: Error) {
|
||||
// ❌ 错误:容器重启丢失,无法集中查看
|
||||
fs.appendFileSync('/var/log/app.log', error.message + '\n')
|
||||
}
|
||||
|
||||
// ❌ 禁止:使用 console.log 而不用 logger
|
||||
console.log('User logged in') // 无结构化,难以查询
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- 容器重启日志丢失
|
||||
- 多实例日志分散
|
||||
- 无法集中分析
|
||||
|
||||
**正确做法**:
|
||||
```typescript
|
||||
// ✅ 输出到 stdout,使用 logger
|
||||
logger.info({ userId, action: 'login' }, 'User logged in')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. 新建数据库连接 ❌
|
||||
|
||||
```typescript
|
||||
// ❌ 禁止:每次请求新建连接
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
export async function getProjects(req, res) {
|
||||
// ❌ 错误:每次新建,连接数暴增
|
||||
const prisma = new PrismaClient()
|
||||
const projects = await prisma.aslScreeningProject.findMany()
|
||||
await prisma.$disconnect()
|
||||
|
||||
res.send(projects)
|
||||
}
|
||||
|
||||
// ❌ 禁止:直接使用 pg 或其他驱动
|
||||
import { Pool } from 'pg'
|
||||
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- 连接数快速耗尽(RDS限制 400 连接)
|
||||
- 性能低下(连接建立耗时)
|
||||
- 资源浪费
|
||||
|
||||
**正确做法**:
|
||||
```typescript
|
||||
// ✅ 使用全局 Prisma Client
|
||||
import { prisma } from '@/config/database'
|
||||
const projects = await prisma.aslScreeningProject.findMany()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. 忽略错误 ❌
|
||||
|
||||
```typescript
|
||||
// ❌ 禁止:空的 catch
|
||||
try {
|
||||
await storage.upload(key, buffer)
|
||||
} catch (error) {
|
||||
// ❌ 错误被吞掉,无法排查
|
||||
}
|
||||
|
||||
// ❌ 禁止:不处理 Promise rejection
|
||||
processAsync(taskId) // 没有 .catch()
|
||||
|
||||
// ❌ 禁止:返回模糊错误
|
||||
catch (error) {
|
||||
res.status(500).send({ error: 'Something went wrong' })
|
||||
// 用户不知道什么错了,如何解决
|
||||
}
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- 错误无法追踪
|
||||
- 用户体验差
|
||||
- 排查困难
|
||||
|
||||
**正确做法**:
|
||||
```typescript
|
||||
// ✅ 记录日志 + 友好错误信息
|
||||
try {
|
||||
await storage.upload(key, buffer)
|
||||
} catch (error) {
|
||||
logger.error({ error, key }, 'Upload failed')
|
||||
res.status(500).send({
|
||||
success: false,
|
||||
error: {
|
||||
code: 'UPLOAD_FAILED',
|
||||
message: '文件上传失败,请检查网络后重试'
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 代码审查检查清单
|
||||
|
||||
在**提交代码前**,请逐项检查:
|
||||
|
||||
### 文件存储
|
||||
- [ ] 是否使用 `storage.upload()` 而非 `fs.writeFile()`?
|
||||
- [ ] Excel 是否从内存解析,而非保存到本地?
|
||||
- [ ] PDF 提取后是否立即删除临时文件?
|
||||
|
||||
### 数据库
|
||||
- [ ] 是否使用全局 `prisma` 实例?
|
||||
- [ ] 是否避免在循环中执行单条查询?(应该批量操作)
|
||||
- [ ] 批量操作是否使用事务?
|
||||
|
||||
### 配置管理
|
||||
- [ ] 是否所有配置都从 `process.env` 读取?
|
||||
- [ ] 是否没有硬编码的 IP、域名、密钥?
|
||||
- [ ] `.env.example` 是否已更新?
|
||||
|
||||
### 长时间任务
|
||||
- [ ] 超过 10 秒的任务是否改为异步?
|
||||
- [ ] 是否提供了进度查询接口?
|
||||
- [ ] 前端是否有轮询或 WebSocket 获取进度?
|
||||
|
||||
### 日志
|
||||
- [ ] 是否使用 `logger` 而非 `console.log`?
|
||||
- [ ] 日志是否结构化(JSON格式)?
|
||||
- [ ] 是否记录了关键操作(userId、action)?
|
||||
|
||||
### 错误处理
|
||||
- [ ] 所有 async 函数是否有 try-catch?
|
||||
- [ ] 是否记录了详细错误日志?
|
||||
- [ ] 是否返回了友好的错误信息?
|
||||
|
||||
### 临时文件
|
||||
- [ ] `/tmp` 目录使用后是否立即删除?
|
||||
- [ ] 是否在 `finally` 块中清理?
|
||||
- [ ] 是否避免长期依赖 `/tmp`?
|
||||
|
||||
---
|
||||
|
||||
## 🎯 快速自检(5分钟)
|
||||
|
||||
**运行以下命令,检查代码中是否有违规**:
|
||||
|
||||
```bash
|
||||
# 检查是否有本地文件存储
|
||||
grep -r "fs.writeFile\|fs.appendFile" backend/src/modules/
|
||||
|
||||
# 检查是否有硬编码配置
|
||||
grep -r "sk-\|http://\|192.168" backend/src/modules/
|
||||
|
||||
# 检查是否有新建 Prisma 连接
|
||||
grep -r "new PrismaClient" backend/src/modules/
|
||||
|
||||
# 检查是否有 console.log
|
||||
grep -r "console.log" backend/src/modules/
|
||||
```
|
||||
|
||||
**预期结果**:所有检查应该返回 **0 个匹配**
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考文档
|
||||
|
||||
- [云原生部署架构指南](../09-架构实施/03-云原生部署架构指南.md) - 包含完整代码示例
|
||||
- [前后端模块化架构设计-V2](../00-系统总体设计/前后端模块化架构设计-V2.md) - 架构总纲
|
||||
- [数据库设计规范](./01-数据库设计规范.md)
|
||||
- [API设计规范](./02-API设计规范.md)
|
||||
- [代码规范](./05-代码规范.md)
|
||||
- [Git提交规范](./06-Git提交规范.md)
|
||||
|
||||
---
|
||||
|
||||
## 📝 更新日志
|
||||
|
||||
| 日期 | 版本 | 变更内容 | 维护者 |
|
||||
|------|------|---------|--------|
|
||||
| 2025-11-16 | V1.0 | 创建文档,定义云原生开发规范 | 架构团队 |
|
||||
|
||||
---
|
||||
|
||||
**文档维护者:** 架构团队
|
||||
**最后更新:** 2025-11-16
|
||||
**文档状态:** ✅ 已完成
|
||||
**强制执行:** ✅ 所有代码提交前必须检查
|
||||
|
||||
@@ -92,43 +92,30 @@
|
||||
|
||||
---
|
||||
|
||||
### 6. Git提交规范 ⭐⭐⭐⭐
|
||||
**文件:** `06-Git提交规范.md` ⏳ 待创建
|
||||
### 6. Git提交规范 ⭐⭐⭐⭐⭐ ✅ 已完成
|
||||
**文件:** `06-Git提交规范.md`
|
||||
|
||||
**核心内容:**
|
||||
- Commit Message格式
|
||||
- 远程仓库配置(Gitee)
|
||||
- Commit Message格式规范
|
||||
- 分支管理策略
|
||||
- PR/MR规范
|
||||
- 代码审查流程
|
||||
- 中文编码问题解决方案 ⭐ 重要
|
||||
- Git历史重写与维护
|
||||
- PR/MR规范和代码审查流程
|
||||
- 常见问题与最佳实践
|
||||
|
||||
**Commit Message格式:**
|
||||
**包含实用工具:**
|
||||
- `fix-git-commit-messages.ps1` 脚本使用说明
|
||||
- Git 别名配置
|
||||
- 中文乱码修复完整流程
|
||||
|
||||
**快速参考:**
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
|
||||
<body>
|
||||
|
||||
<footer>
|
||||
```
|
||||
|
||||
**Type类型:**
|
||||
- `feat`: 新功能
|
||||
- `fix`: Bug修复
|
||||
- `docs`: 文档更新
|
||||
- `style`: 代码格式(不影响逻辑)
|
||||
- `refactor`: 重构
|
||||
- `perf`: 性能优化
|
||||
- `test`: 测试相关
|
||||
- `chore`: 构建/工具相关
|
||||
|
||||
**示例:**
|
||||
```
|
||||
feat(asl): 实现标题摘要初筛功能
|
||||
|
||||
- 添加CSV文件导入
|
||||
- 实现双模型AI判断
|
||||
- 添加PICO配置功能
|
||||
|
||||
Closes #123
|
||||
feat: 新功能 fix: Bug修复 docs: 文档
|
||||
style: 格式 refactor: 重构 perf: 优化
|
||||
test: 测试 chore: 构建 ci: CI/CD
|
||||
```
|
||||
|
||||
---
|
||||
@@ -172,9 +159,13 @@ Closes #123
|
||||
|
||||
**我要设计数据库表:** → `01-数据库设计规范.md`
|
||||
**我要设计API接口:** → `02-API设计规范.md`
|
||||
**我要编写代码:** → `03-代码规范.md`
|
||||
**我要提交代码:** → `04-Git提交规范.md`
|
||||
**我要编写测试:** → `05-测试规范.md`
|
||||
**我要查看全局数据架构:** → `03-数据库全局视图.md` ✅
|
||||
**我要查看全局API路由:** → `04-API路由总览.md` ✅
|
||||
**我要编写代码:** → `05-代码规范.md` ✅
|
||||
**我要提交代码:** → `06-Git提交规范.md` ✅
|
||||
**我要解决中文乱码:** → `06-Git提交规范.md` (第4节) ✅
|
||||
**我要配置远程仓库:** → `06-Git提交规范.md` (第1节) ✅
|
||||
**我要编写测试:** → `07-测试规范.md`
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user