添加后端todo-list
This commit is contained in:
791
docs/DETAILED_TODOLIST.md
Normal file
791
docs/DETAILED_TODOLIST.md
Normal file
@@ -0,0 +1,791 @@
|
|||||||
|
# 🚀 网站现代化重构 - 详尽 TodoList
|
||||||
|
|
||||||
|
**项目周期**:4-6 周 | **总任务数**:87 项 | **优先级分布**:P0(22) | P1(35) | P2(20) | P3(10)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 任务约定
|
||||||
|
|
||||||
|
- 🔴 **P0 - 必须**:项目核心,必须完成
|
||||||
|
- 🟠 **P1 - 重要**:功能完整性,应该完成
|
||||||
|
- 🟡 **P2 - 优化**:用户体验,可选完成
|
||||||
|
- 🟢 **P3 - 未来**:后续功能,后期实现
|
||||||
|
|
||||||
|
**状态说明**:
|
||||||
|
- ⬜ `not-started` - 未开始
|
||||||
|
- 🟦 `in-progress` - 进行中
|
||||||
|
- ✅ `completed` - 已完成
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 第一阶段:环境准备 & 项目规划(第 1 周)
|
||||||
|
|
||||||
|
### 🔴 P0 任务:项目初始化
|
||||||
|
|
||||||
|
#### 1.1 后端项目创建
|
||||||
|
- [ ] 创建 `/backend` 目录结构
|
||||||
|
```
|
||||||
|
backend/
|
||||||
|
├── src/
|
||||||
|
│ ├── server.ts
|
||||||
|
│ ├── config/
|
||||||
|
│ ├── routes/
|
||||||
|
│ ├── controllers/
|
||||||
|
│ ├── middleware/
|
||||||
|
│ ├── database/
|
||||||
|
│ └── types/
|
||||||
|
├── tests/
|
||||||
|
├── .env.example
|
||||||
|
├── package.json
|
||||||
|
└── tsconfig.json
|
||||||
|
```
|
||||||
|
- [ ] 初始化 Node.js 项目:`npm init -y`
|
||||||
|
- [ ] 安装基础依赖:`npm install express cors dotenv pg`
|
||||||
|
- [ ] 安装开发依赖:`npm install -D typescript @types/node @types/express ts-node nodemon`
|
||||||
|
- [ ] 创建 `.env` 和 `.env.example` 文件
|
||||||
|
- [ ] 创建 `tsconfig.json` 配置文件
|
||||||
|
- [ ] 创建 `package.json` 脚本配置
|
||||||
|
|
||||||
|
#### 1.2 前端项目升级
|
||||||
|
- [ ] 升级 Astro 到最新版本:`npm install astro@latest`
|
||||||
|
- [ ] 安装 React 集成:`npm install @astrojs/react react react-dom`
|
||||||
|
- [ ] 安装 API 相关依赖:`npm install axios zod isomorphic-dompurify`
|
||||||
|
- [ ] 验证项目能正常构建
|
||||||
|
|
||||||
|
#### 1.3 数据库规划
|
||||||
|
- [ ] 创建数据库初始化脚本目录 `backend/sql/`
|
||||||
|
- [ ] 制定数据库连接策略
|
||||||
|
- [ ] 准备 PostgreSQL 本地开发环境
|
||||||
|
- [ ] 创建数据库连接池配置
|
||||||
|
|
||||||
|
### 🟠 P1 任务:文档编写
|
||||||
|
|
||||||
|
#### 1.4 API 文档
|
||||||
|
- [ ] 编写完整的 API 规范文档
|
||||||
|
- [ ] 定义评论 API 端点
|
||||||
|
- [ ] 定义统计 API 端点
|
||||||
|
- [ ] 定义用户 API 端点
|
||||||
|
- [ ] 定义错误响应格式
|
||||||
|
|
||||||
|
#### 1.5 架构文档
|
||||||
|
- [ ] 绘制系统架构图
|
||||||
|
- [ ] 编写数据流说明
|
||||||
|
- [ ] 列出安全考虑点
|
||||||
|
- [ ] 记录性能优化策略
|
||||||
|
|
||||||
|
### 🟡 P2 任务:设计文档
|
||||||
|
|
||||||
|
#### 1.6 UI/UX 设计
|
||||||
|
- [ ] 设计评论区域样式
|
||||||
|
- [ ] 设计统计卡片样式
|
||||||
|
- [ ] 制定色系和排版规范
|
||||||
|
- [ ] 创建组件设计稿
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 第二阶段:后端开发(第 2-3 周)
|
||||||
|
|
||||||
|
### 🔴 P0 任务:核心基础设施
|
||||||
|
|
||||||
|
#### 2.1 Server 启动和基础配置
|
||||||
|
- [ ] 创建 `src/server.ts` 主文件
|
||||||
|
- [ ] 配置 Express 应用基础
|
||||||
|
- [ ] 配置 CORS 中间件
|
||||||
|
- [ ] 配置 JSON 解析中间件
|
||||||
|
- [ ] 实现环境变量加载
|
||||||
|
- [ ] 设置错误处理中间件
|
||||||
|
- [ ] 实现 404 路由处理
|
||||||
|
- [ ] 测试服务器启动成功
|
||||||
|
|
||||||
|
#### 2.2 数据库连接
|
||||||
|
- [ ] 创建 `src/database/connection.ts`
|
||||||
|
- [ ] 配置 PostgreSQL 连接池
|
||||||
|
- [ ] 实现连接错误处理
|
||||||
|
- [ ] 实现连接超时处理
|
||||||
|
- [ ] 添加连接测试函数
|
||||||
|
- [ ] 验证连接可用性
|
||||||
|
|
||||||
|
#### 2.3 数据库 Schema
|
||||||
|
- [ ] 创建 `backend/sql/001_init_schema.sql`
|
||||||
|
- [ ] 定义 `comments` 表
|
||||||
|
```sql
|
||||||
|
- id: SERIAL PRIMARY KEY
|
||||||
|
- report_id: VARCHAR(100)
|
||||||
|
- author: VARCHAR(100)
|
||||||
|
- email: VARCHAR(100)
|
||||||
|
- content: TEXT
|
||||||
|
- status: VARCHAR(20)
|
||||||
|
- created_at: TIMESTAMP
|
||||||
|
- updated_at: TIMESTAMP
|
||||||
|
```
|
||||||
|
- [ ] 定义 `report_stats` 表
|
||||||
|
```sql
|
||||||
|
- id: SERIAL PRIMARY KEY
|
||||||
|
- report_id: VARCHAR(100) UNIQUE
|
||||||
|
- views: INTEGER
|
||||||
|
- comments: INTEGER
|
||||||
|
- likes: INTEGER
|
||||||
|
- shares: INTEGER
|
||||||
|
```
|
||||||
|
- [ ] 定义 `likes` 表
|
||||||
|
```sql
|
||||||
|
- id: SERIAL PRIMARY KEY
|
||||||
|
- report_id: VARCHAR(100)
|
||||||
|
- client_id: VARCHAR(100)
|
||||||
|
- created_at: TIMESTAMP
|
||||||
|
```
|
||||||
|
- [ ] 创建必要的索引
|
||||||
|
- `idx_comments_report_id`
|
||||||
|
- `idx_stats_report_id`
|
||||||
|
- `idx_likes_report_id`
|
||||||
|
- [ ] 创建 `backend/sql/002_add_constraints.sql` (外键约束)
|
||||||
|
- [ ] 创建迁移脚本运行器
|
||||||
|
|
||||||
|
#### 2.4 TypeScript 类型定义
|
||||||
|
- [ ] 创建 `src/types/comment.ts`
|
||||||
|
- [ ] 创建 `src/types/stats.ts`
|
||||||
|
- [ ] 创建 `src/types/like.ts`
|
||||||
|
- [ ] 创建 `src/types/error.ts`
|
||||||
|
- [ ] 创建 `src/types/api-response.ts`
|
||||||
|
|
||||||
|
### 🔴 P0 任务:评论系统 API
|
||||||
|
|
||||||
|
#### 2.5 评论模块 - 路由层
|
||||||
|
- [ ] 创建 `src/routes/comments.ts`
|
||||||
|
- [ ] `GET /api/comments` - 获取评论列表
|
||||||
|
- [ ] 支持分页(page, limit)
|
||||||
|
- [ ] 支持排序(created_at, likes)
|
||||||
|
- [ ] 支持过滤(status, reportId)
|
||||||
|
- [ ] `GET /api/comments/:id` - 获取单条评论
|
||||||
|
- [ ] `POST /api/comments` - 创建评论
|
||||||
|
- [ ] `PUT /api/comments/:id` - 更新评论(仅作者或管理员)
|
||||||
|
- [ ] `DELETE /api/comments/:id` - 删除评论(仅作者或管理员)
|
||||||
|
|
||||||
|
#### 2.6 评论模块 - 控制器层
|
||||||
|
- [ ] 创建 `src/controllers/commentController.ts`
|
||||||
|
- [ ] 实现 `getAllComments()` 逻辑
|
||||||
|
- [ ] 实现 `getCommentById()` 逻辑
|
||||||
|
- [ ] 实现 `createComment()` 逻辑
|
||||||
|
- [ ] 实现 `updateComment()` 逻辑
|
||||||
|
- [ ] 实现 `deleteComment()` 逻辑
|
||||||
|
- [ ] 添加数据验证逻辑
|
||||||
|
- [ ] 添加错误处理
|
||||||
|
|
||||||
|
#### 2.7 评论模块 - 数据库层
|
||||||
|
- [ ] 创建 `src/database/commentQueries.ts`
|
||||||
|
- [ ] 实现 `fetchCommentsByReport()`
|
||||||
|
- [ ] 实现 `fetchCommentById()`
|
||||||
|
- [ ] 实现 `insertComment()`
|
||||||
|
- [ ] 实现 `updateComment()`
|
||||||
|
- [ ] 实现 `deleteComment()`
|
||||||
|
- [ ] 实现 `countCommentsByReport()`
|
||||||
|
- [ ] 添加事务处理
|
||||||
|
|
||||||
|
### 🔴 P0 任务:统计系统 API
|
||||||
|
|
||||||
|
#### 2.8 统计模块 - 路由层
|
||||||
|
- [ ] 创建 `src/routes/stats.ts`
|
||||||
|
- [ ] `GET /api/stats/:reportId` - 获取统计数据
|
||||||
|
- [ ] `POST /api/stats/:reportId/view` - 记录浏览
|
||||||
|
- [ ] `POST /api/stats/:reportId/like` - 点赞
|
||||||
|
- [ ] `DELETE /api/stats/:reportId/like` - 取消点赞
|
||||||
|
- [ ] `POST /api/stats/:reportId/share` - 记录分享
|
||||||
|
- [ ] `GET /api/stats/trending` - 获取热门
|
||||||
|
|
||||||
|
#### 2.9 统计模块 - 控制器层
|
||||||
|
- [ ] 创建 `src/controllers/statsController.ts`
|
||||||
|
- [ ] 实现 `getStats()` 逻辑
|
||||||
|
- [ ] 实现 `recordView()` 逻辑
|
||||||
|
- [ ] 实现 `recordLike()` 逻辑
|
||||||
|
- [ ] 实现 `removeLike()` 逻辑
|
||||||
|
- [ ] 实现 `recordShare()` 逻辑
|
||||||
|
- [ ] 实现 `getTrending()` 逻辑
|
||||||
|
- [ ] 添加重复检查
|
||||||
|
|
||||||
|
#### 2.10 统计模块 - 数据库层
|
||||||
|
- [ ] 创建 `src/database/statsQueries.ts`
|
||||||
|
- [ ] 实现 `fetchStats()`
|
||||||
|
- [ ] 实现 `updateViews()`
|
||||||
|
- [ ] 实现 `addLike()`
|
||||||
|
- [ ] 实现 `removeLike()`
|
||||||
|
- [ ] 实现 `checkLikeExists()`
|
||||||
|
- [ ] 实现 `updateShares()`
|
||||||
|
- [ ] 实现 `fetchTrending()`
|
||||||
|
|
||||||
|
### 🟠 P1 任务:中间件和工具
|
||||||
|
|
||||||
|
#### 2.11 验证中间件
|
||||||
|
- [ ] 创建 `src/middleware/validation.ts`
|
||||||
|
- [ ] 实现评论数据验证 (Zod schema)
|
||||||
|
- [ ] 实现统计数据验证
|
||||||
|
- [ ] 实现错误转换中间件
|
||||||
|
- [ ] 添加日志中间件
|
||||||
|
|
||||||
|
#### 2.12 安全中间件
|
||||||
|
- [ ] 创建 `src/middleware/security.ts`
|
||||||
|
- [ ] 实现速率限制(express-rate-limit)
|
||||||
|
- [ ] 实现 CSRF 保护
|
||||||
|
- [ ] 实现 XSS 防护 (DOMPurify)
|
||||||
|
- [ ] 实现输入清理函数
|
||||||
|
|
||||||
|
#### 2.13 认证中间件(可选基础)
|
||||||
|
- [ ] 创建 `src/middleware/auth.ts`
|
||||||
|
- [ ] 实现 JWT 验证(预留)
|
||||||
|
- [ ] 实现权限检查函数
|
||||||
|
- [ ] 实现管理员标识
|
||||||
|
|
||||||
|
### 🟡 P2 任务:辅助功能
|
||||||
|
|
||||||
|
#### 2.14 工具函数
|
||||||
|
- [ ] 创建 `src/utils/helpers.ts`
|
||||||
|
- [ ] 实现分页计算函数
|
||||||
|
- [ ] 实现日期格式化
|
||||||
|
- [ ] 实现 ID 生成器
|
||||||
|
- [ ] 实现数据转换函数
|
||||||
|
|
||||||
|
#### 2.15 缓存策略
|
||||||
|
- [ ] 创建 `src/cache/cacheManager.ts`(预留 Redis)
|
||||||
|
- [ ] 实现内存缓存方案
|
||||||
|
- [ ] 实现缓存失效策略
|
||||||
|
- [ ] 实现缓存预热
|
||||||
|
|
||||||
|
#### 2.16 日志系统
|
||||||
|
- [ ] 创建 `src/logger/logger.ts`
|
||||||
|
- [ ] 配置日志级别
|
||||||
|
- [ ] 实现日志文件轮转
|
||||||
|
- [ ] 实现错误日志记录
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 第三阶段:前端开发(第 3-4 周)
|
||||||
|
|
||||||
|
### 🔴 P0 任务:Astro 混合渲染配置
|
||||||
|
|
||||||
|
#### 3.1 更新 Astro 配置
|
||||||
|
- [ ] 修改 `astro.config.mjs` 启用混合渲染
|
||||||
|
```javascript
|
||||||
|
output: 'hybrid'
|
||||||
|
adapter: node({ mode: 'standalone' })
|
||||||
|
```
|
||||||
|
- [ ] 配置 React 集成
|
||||||
|
- [ ] 验证混合渲染配置正确
|
||||||
|
- [ ] 测试静态和动态页面生成
|
||||||
|
|
||||||
|
#### 3.2 创建 API 客户端
|
||||||
|
- [ ] 创建 `src/lib/api-client.ts`
|
||||||
|
- [ ] 实现 HTTP 请求拦截器
|
||||||
|
- [ ] 实现错误处理
|
||||||
|
- [ ] 实现请求重试逻辑
|
||||||
|
- [ ] 配置 API 基础 URL
|
||||||
|
|
||||||
|
#### 3.3 创建 React 上下文(可选)
|
||||||
|
- [ ] 创建 `src/contexts/ReportContext.tsx`
|
||||||
|
- [ ] 实现统计数据共享
|
||||||
|
- [ ] 实现用户状态管理
|
||||||
|
- [ ] 实现通知管理
|
||||||
|
|
||||||
|
### 🔴 P0 任务:评论组件开发
|
||||||
|
|
||||||
|
#### 3.4 评论列表组件
|
||||||
|
- [ ] 创建 `src/components/comments/CommentList.tsx`
|
||||||
|
- [ ] 实现分页显示
|
||||||
|
- [ ] 实现排序切换
|
||||||
|
- [ ] 实现加载状态
|
||||||
|
- [ ] 实现空状态提示
|
||||||
|
- [ ] 实现错误状态提示
|
||||||
|
|
||||||
|
#### 3.5 评论表单组件
|
||||||
|
- [ ] 创建 `src/components/comments/CommentForm.tsx`
|
||||||
|
- [ ] 实现表单输入验证
|
||||||
|
- [ ] 实现字符计数
|
||||||
|
- [ ] 实现提交加载状态
|
||||||
|
- [ ] 实现成功/失败提示
|
||||||
|
- [ ] 实现防重复提交
|
||||||
|
|
||||||
|
#### 3.6 单条评论组件
|
||||||
|
- [ ] 创建 `src/components/comments/CommentItem.tsx`
|
||||||
|
- [ ] 实现用户信息展示
|
||||||
|
- [ ] 实现时间格式化
|
||||||
|
- [ ] 实现评论内容渲染(XSS 防护)
|
||||||
|
- [ ] 实现编辑/删除按钮(权限检查)
|
||||||
|
- [ ] 实现回复按钮(预留)
|
||||||
|
|
||||||
|
#### 3.7 完整评论区域组件
|
||||||
|
- [ ] 创建 `src/components/comments/CommentSection.tsx`
|
||||||
|
- [ ] 组合 List、Form、Item 组件
|
||||||
|
- [ ] 实现数据获取逻辑
|
||||||
|
- [ ] 实现实时更新
|
||||||
|
- [ ] 实现错误边界
|
||||||
|
- [ ] 实现加载骨架屏
|
||||||
|
|
||||||
|
### 🔴 P0 任务:统计组件开发
|
||||||
|
|
||||||
|
#### 3.8 统计卡片组件
|
||||||
|
- [ ] 创建 `src/components/stats/StatCard.tsx`
|
||||||
|
- [ ] 实现数字动画展示
|
||||||
|
- [ ] 实现图标展示
|
||||||
|
- [ ] 实现趋势指示
|
||||||
|
- [ ] 实现加载状态
|
||||||
|
|
||||||
|
#### 3.9 统计面板组件
|
||||||
|
- [ ] 创建 `src/components/stats/StatsPanel.tsx`
|
||||||
|
- [ ] 组合多个 StatCard
|
||||||
|
- [ ] 实现数据获取
|
||||||
|
- [ ] 实现自动刷新
|
||||||
|
- [ ] 实现响应式布局
|
||||||
|
|
||||||
|
#### 3.10 点赞按钮组件
|
||||||
|
- [ ] 创建 `src/components/interactions/LikeButton.tsx`
|
||||||
|
- [ ] 实现点赞/取消点赞逻辑
|
||||||
|
- [ ] 实现动画效果
|
||||||
|
- [ ] 实现重复点赞防护
|
||||||
|
- [ ] 实现点赞数显示
|
||||||
|
|
||||||
|
#### 3.11 分享按钮组件
|
||||||
|
- [ ] 创建 `src/components/interactions/ShareButton.tsx`
|
||||||
|
- [ ] 实现复制链接功能
|
||||||
|
- [ ] 实现社交分享(可选)
|
||||||
|
- [ ] 实现分享统计
|
||||||
|
- [ ] 实现分享提示
|
||||||
|
|
||||||
|
### 🟠 P1 任务:页面集成
|
||||||
|
|
||||||
|
#### 3.12 报告页面更新
|
||||||
|
- [ ] 修改 `src/pages/report/ai-eda-paper-report/index.astro`
|
||||||
|
- [ ] 导入统计组件
|
||||||
|
- [ ] 导入评论组件
|
||||||
|
- [ ] 添加统计展示区域
|
||||||
|
- [ ] 添加评论区域
|
||||||
|
- [ ] 实现客户端加载指令
|
||||||
|
|
||||||
|
#### 3.13 其他报告页面
|
||||||
|
- [ ] 为其他报告页面添加统计功能
|
||||||
|
- [ ] 为其他报告页面添加评论功能
|
||||||
|
- [ ] 统一样式风格
|
||||||
|
- [ ] 验证功能一致性
|
||||||
|
|
||||||
|
### 🟡 P2 任务:交互优化
|
||||||
|
|
||||||
|
#### 3.14 加载状态
|
||||||
|
- [ ] 创建 `src/components/common/Skeleton.tsx` (骨架屏)
|
||||||
|
- [ ] 创建 `src/components/common/LoadingSpinner.tsx`
|
||||||
|
- [ ] 创建 `src/components/common/ErrorBoundary.tsx`
|
||||||
|
- [ ] 应用到所有异步组件
|
||||||
|
|
||||||
|
#### 3.15 通知系统
|
||||||
|
- [ ] 创建 `src/components/common/Toast.tsx`
|
||||||
|
- [ ] 实现成功提示
|
||||||
|
- [ ] 实现错误提示
|
||||||
|
- [ ] 实现警告提示
|
||||||
|
- [ ] 实现通知队列
|
||||||
|
|
||||||
|
#### 3.16 动画效果
|
||||||
|
- [ ] 实现数字动画库集成(可选)
|
||||||
|
- [ ] 添加评论出现动画
|
||||||
|
- [ ] 添加统计数字动画
|
||||||
|
- [ ] 添加过渡效果
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 第四阶段:集成与测试(第 4 周)
|
||||||
|
|
||||||
|
### 🔴 P0 任务:后端测试
|
||||||
|
|
||||||
|
#### 4.1 单元测试
|
||||||
|
- [ ] 安装测试框架:`npm install -D jest @types/jest ts-jest`
|
||||||
|
- [ ] 创建 `backend/tests/` 目录
|
||||||
|
- [ ] 编写评论 API 单元测试
|
||||||
|
- [ ] 测试 GET /api/comments
|
||||||
|
- [ ] 测试 POST /api/comments
|
||||||
|
- [ ] 测试数据验证
|
||||||
|
- [ ] 测试错误处理
|
||||||
|
- [ ] 编写统计 API 单元测试
|
||||||
|
- [ ] 测试 GET /api/stats/:reportId
|
||||||
|
- [ ] 测试 POST /api/stats/:reportId/view
|
||||||
|
- [ ] 测试 POST /api/stats/:reportId/like
|
||||||
|
- [ ] 运行测试,确保覆盖率 > 80%
|
||||||
|
|
||||||
|
#### 4.2 集成测试
|
||||||
|
- [ ] 创建测试数据库配置
|
||||||
|
- [ ] 编写评论完整流程测试
|
||||||
|
- [ ] 编写统计完整流程测试
|
||||||
|
- [ ] 编写并发场景测试
|
||||||
|
- [ ] 验证所有 API 端点
|
||||||
|
|
||||||
|
#### 4.3 API 文档测试
|
||||||
|
- [ ] 使用 Postman/Insomnia 测试所有端点
|
||||||
|
- [ ] 验证请求/响应格式
|
||||||
|
- [ ] 验证错误响应
|
||||||
|
- [ ] 记录实际的 API 示例
|
||||||
|
|
||||||
|
### 🔴 P0 任务:前端测试
|
||||||
|
|
||||||
|
#### 4.4 组件测试
|
||||||
|
- [ ] 安装测试库:`npm install -D vitest @testing-library/react`
|
||||||
|
- [ ] 编写 CommentForm 测试
|
||||||
|
- [ ] 测试表单提交
|
||||||
|
- [ ] 测试验证逻辑
|
||||||
|
- [ ] 测试错误显示
|
||||||
|
- [ ] 编写 StatsPanel 测试
|
||||||
|
- [ ] 测试数据加载
|
||||||
|
- [ ] 测试数据展示
|
||||||
|
- [ ] 编写 LikeButton 测试
|
||||||
|
- [ ] 测试点赞逻辑
|
||||||
|
- [ ] 测试防重复点赞
|
||||||
|
|
||||||
|
#### 4.5 集成测试
|
||||||
|
- [ ] 测试 Astro 页面 + React 组件集成
|
||||||
|
- [ ] 测试客户端加载指令
|
||||||
|
- [ ] 测试数据流
|
||||||
|
- [ ] 测试错误恢复
|
||||||
|
|
||||||
|
### 🟠 P1 任务:E2E 测试
|
||||||
|
|
||||||
|
#### 4.6 用户场景测试
|
||||||
|
- [ ] 安装 E2E 框架:`npm install -D playwright`
|
||||||
|
- [ ] 测试查看报告流程
|
||||||
|
- [ ] 测试发布评论流程
|
||||||
|
- [ ] 测试点赞流程
|
||||||
|
- [ ] 测试分享流程
|
||||||
|
- [ ] 测试移动端响应
|
||||||
|
|
||||||
|
#### 4.7 性能测试
|
||||||
|
- [ ] 测试页面加载时间
|
||||||
|
- [ ] 测试 API 响应时间
|
||||||
|
- [ ] 测试并发用户压力
|
||||||
|
- [ ] 记录性能基线
|
||||||
|
|
||||||
|
### 🟡 P2 任务:安全测试
|
||||||
|
|
||||||
|
#### 4.8 安全审查
|
||||||
|
- [ ] 审查 XSS 防护
|
||||||
|
- [ ] 审查 SQL 注入防护
|
||||||
|
- [ ] 审查认证逻辑
|
||||||
|
- [ ] 审查 CORS 配置
|
||||||
|
- [ ] 测试速率限制
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 第五阶段:部署与优化(第 5 周)
|
||||||
|
|
||||||
|
### 🔴 P0 任务:Docker 配置
|
||||||
|
|
||||||
|
#### 5.1 后端 Docker 化
|
||||||
|
- [ ] 创建 `Dockerfile`
|
||||||
|
- [ ] 创建 `.dockerignore`
|
||||||
|
- [ ] 定义构建阶段
|
||||||
|
- [ ] 定义运行阶段
|
||||||
|
- [ ] 优化镜像大小
|
||||||
|
- [ ] 本地测试 Docker 镜像
|
||||||
|
|
||||||
|
#### 5.2 前端 Docker 化
|
||||||
|
- [ ] 为前端创建 `Dockerfile`
|
||||||
|
- [ ] 多阶段构建
|
||||||
|
- [ ] 静态文件优化
|
||||||
|
- [ ] 本地测试
|
||||||
|
|
||||||
|
#### 5.3 Docker Compose
|
||||||
|
- [ ] 创建 `docker-compose.yml`
|
||||||
|
- [ ] 配置后端服务
|
||||||
|
- [ ] 配置前端服务
|
||||||
|
- [ ] 配置数据库服务
|
||||||
|
- [ ] 配置网络
|
||||||
|
- [ ] 测试本地完整运行
|
||||||
|
|
||||||
|
#### 5.4 数据库容器
|
||||||
|
- [ ] 配置 PostgreSQL 容器
|
||||||
|
- [ ] 持久化数据卷
|
||||||
|
- [ ] 初始化脚本
|
||||||
|
- [ ] 备份策略
|
||||||
|
|
||||||
|
### 🔴 P0 任务:环境配置
|
||||||
|
|
||||||
|
#### 5.5 生产环境变量
|
||||||
|
- [ ] 创建 `.env.production`
|
||||||
|
- [ ] 配置数据库 URL
|
||||||
|
- [ ] 配置 API 基础 URL
|
||||||
|
- [ ] 配置日志级别
|
||||||
|
- [ ] 配置 CORS 源
|
||||||
|
- [ ] 配置密钥
|
||||||
|
|
||||||
|
#### 5.6 环境管理
|
||||||
|
- [ ] 创建环境配置管理器
|
||||||
|
- [ ] 验证必需变量
|
||||||
|
- [ ] 文档化所有变量
|
||||||
|
|
||||||
|
### 🟠 P1 任务:部署配置
|
||||||
|
|
||||||
|
#### 5.7 Nginx 反向代理
|
||||||
|
- [ ] 创建 `nginx.conf`
|
||||||
|
- [ ] 配置前端路由
|
||||||
|
- [ ] 配置后端 API 反向代理
|
||||||
|
- [ ] 配置 Gzip 压缩
|
||||||
|
- [ ] 配置缓存策略
|
||||||
|
- [ ] 配置 SSL(可选)
|
||||||
|
|
||||||
|
#### 5.8 PM2 进程管理
|
||||||
|
- [ ] 创建 `ecosystem.config.js`
|
||||||
|
- [ ] 配置后端进程
|
||||||
|
- [ ] 配置前端构建进程
|
||||||
|
- [ ] 实现自动重启
|
||||||
|
- [ ] 配置日志轮转
|
||||||
|
|
||||||
|
#### 5.9 部署脚本
|
||||||
|
- [ ] 创建 `deploy.sh` 脚本
|
||||||
|
- [ ] 拉取最新代码
|
||||||
|
- [ ] 安装依赖
|
||||||
|
- [ ] 构建项目
|
||||||
|
- [ ] 运行数据库迁移
|
||||||
|
- [ ] 重启服务
|
||||||
|
- [ ] 健康检查
|
||||||
|
|
||||||
|
### 🟠 P1 任务:性能优化
|
||||||
|
|
||||||
|
#### 5.10 数据库优化
|
||||||
|
- [ ] 分析查询性能
|
||||||
|
- [ ] 添加必要的索引
|
||||||
|
- [ ] 优化 N+1 查询
|
||||||
|
- [ ] 实现查询缓存
|
||||||
|
- [ ] 监控连接池
|
||||||
|
|
||||||
|
#### 5.11 API 优化
|
||||||
|
- [ ] 实现 API 分页
|
||||||
|
- [ ] 实现字段选择(select)
|
||||||
|
- [ ] 实现 API 压缩
|
||||||
|
- [ ] 实现响应缓存
|
||||||
|
- [ ] 实现 CDN 集成
|
||||||
|
|
||||||
|
#### 5.12 前端优化
|
||||||
|
- [ ] 代码分割优化
|
||||||
|
- [ ] 图片优化
|
||||||
|
- [ ] 字体加载优化
|
||||||
|
- [ ] 缓存策略
|
||||||
|
- [ ] 预加载/预连接
|
||||||
|
|
||||||
|
### 🟡 P2 任务:监控与日志
|
||||||
|
|
||||||
|
#### 5.13 应用监控
|
||||||
|
- [ ] 配置应用性能监控 (APM)
|
||||||
|
- [ ] 配置错误追踪
|
||||||
|
- [ ] 配置日志聚合
|
||||||
|
- [ ] 设置告警规则
|
||||||
|
|
||||||
|
#### 5.14 基础设施监控
|
||||||
|
- [ ] 配置 CPU 监控
|
||||||
|
- [ ] 配置内存监控
|
||||||
|
- [ ] 配置磁盘监控
|
||||||
|
- [ ] 配置网络监控
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 第六阶段:上线与维护(第 6 周)
|
||||||
|
|
||||||
|
### 🔴 P0 任务:上线前准备
|
||||||
|
|
||||||
|
#### 6.1 最终检查清单
|
||||||
|
- [ ] 所有测试通过
|
||||||
|
- [ ] 代码审查完成
|
||||||
|
- [ ] 文档完整
|
||||||
|
- [ ] 安全审计通过
|
||||||
|
- [ ] 性能测试通过
|
||||||
|
- [ ] 备份策略就位
|
||||||
|
- [ ] 回滚计划准备
|
||||||
|
|
||||||
|
#### 6.2 数据迁移
|
||||||
|
- [ ] 备份现有数据
|
||||||
|
- [ ] 准备数据迁移脚本
|
||||||
|
- [ ] 测试数据迁移
|
||||||
|
- [ ] 准备回滚方案
|
||||||
|
|
||||||
|
#### 6.3 DNS 配置
|
||||||
|
- [ ] 配置域名解析
|
||||||
|
- [ ] 配置 SSL 证书
|
||||||
|
- [ ] 测试 HTTPS 连接
|
||||||
|
- [ ] 配置 CDN(可选)
|
||||||
|
|
||||||
|
### 🟠 P1 任务:灰度发布
|
||||||
|
|
||||||
|
#### 6.4 金丝雀发布
|
||||||
|
- [ ] 发布到测试服务器
|
||||||
|
- [ ] 在小部分用户中测试
|
||||||
|
- [ ] 监控错误率
|
||||||
|
- [ ] 逐步增加流量
|
||||||
|
- [ ] 完整上线
|
||||||
|
|
||||||
|
#### 6.5 发布公告
|
||||||
|
- [ ] 准备发布说明
|
||||||
|
- [ ] 通知用户新功能
|
||||||
|
- [ ] 准备常见问题解答
|
||||||
|
- [ ] 监控用户反馈
|
||||||
|
|
||||||
|
### 🟠 P1 任务:上线后维护
|
||||||
|
|
||||||
|
#### 6.6 监控第一周
|
||||||
|
- [ ] 日常监控指标
|
||||||
|
- [ ] 应对用户反馈
|
||||||
|
- [ ] 快速修复 bug
|
||||||
|
- [ ] 记录问题和解决方案
|
||||||
|
|
||||||
|
#### 6.7 长期维护计划
|
||||||
|
- [ ] 建立定期备份
|
||||||
|
- [ ] 定期安全更新
|
||||||
|
- [ ] 定期依赖更新
|
||||||
|
- [ ] 性能优化迭代
|
||||||
|
|
||||||
|
### 🟡 P2 任务:用户支持
|
||||||
|
|
||||||
|
#### 6.8 文档完善
|
||||||
|
- [ ] 完善用户指南
|
||||||
|
- [ ] 编写 FAQ
|
||||||
|
- [ ] 编写故障排查指南
|
||||||
|
- [ ] 建立反馈渠道
|
||||||
|
|
||||||
|
#### 6.9 持续改进
|
||||||
|
- [ ] 收集用户反馈
|
||||||
|
- [ ] 分析使用数据
|
||||||
|
- [ ] 规划功能迭代
|
||||||
|
- [ ] 优化用户体验
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 可选增强功能(P3 任务)
|
||||||
|
|
||||||
|
### 🟢 P3:用户系统
|
||||||
|
|
||||||
|
#### 7.1 用户认证
|
||||||
|
- [ ] 实现用户注册
|
||||||
|
- [ ] 实现用户登录
|
||||||
|
- [ ] 实现密码重置
|
||||||
|
- [ ] 实现社交登录
|
||||||
|
|
||||||
|
#### 7.2 用户资料
|
||||||
|
- [ ] 用户头像上传
|
||||||
|
- [ ] 用户信息编辑
|
||||||
|
- [ ] 用户偏好设置
|
||||||
|
- [ ] 用户隐私控制
|
||||||
|
|
||||||
|
### 🟢 P3:高级评论功能
|
||||||
|
|
||||||
|
#### 7.3 评论增强
|
||||||
|
- [ ] 评论回复功能
|
||||||
|
- [ ] 评论点赞功能
|
||||||
|
- [ ] 评论举报功能
|
||||||
|
- [ ] 评论标签功能
|
||||||
|
- [ ] 评论搜索功能
|
||||||
|
|
||||||
|
### 🟢 P3:AI 增强
|
||||||
|
|
||||||
|
#### 7.4 智能功能
|
||||||
|
- [ ] 评论内容审核(AI)
|
||||||
|
- [ ] 自动回复建议
|
||||||
|
- [ ] 评论总结
|
||||||
|
- [ ] 智能分类
|
||||||
|
|
||||||
|
### 🟢 P3:社区功能
|
||||||
|
|
||||||
|
#### 7.5 社区建设
|
||||||
|
- [ ] 排行榜系统
|
||||||
|
- [ ] 徽章系统
|
||||||
|
- [ ] 积分系统
|
||||||
|
- [ ] 社区讨论区
|
||||||
|
- [ ] 用户关注
|
||||||
|
|
||||||
|
### 🟢 P3:数据分析
|
||||||
|
|
||||||
|
#### 7.6 深度分析
|
||||||
|
- [ ] 用户行为分析
|
||||||
|
- [ ] 评论情感分析
|
||||||
|
- [ ] 内容热力分析
|
||||||
|
- [ ] 用户转化分析
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 时间和资源预估
|
||||||
|
|
||||||
|
### 时间分配
|
||||||
|
```
|
||||||
|
第一周:环境和规划 - 40 小时
|
||||||
|
第二周:后端开发 - 60 小时
|
||||||
|
第三周:前端开发 - 60 小时
|
||||||
|
第四周:测试和集成 - 50 小时
|
||||||
|
第五周:部署和优化 - 50 小时
|
||||||
|
第六周:上线和维护 - 40 小时
|
||||||
|
|
||||||
|
总计:约 300 小时 ≈ 7-8 周(单人开发)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 资源需求
|
||||||
|
- **开发者**:1-2 人(可并行)
|
||||||
|
- **测试**:可通过自动化完成
|
||||||
|
- **运维**:1 人(兼职)
|
||||||
|
- **服务器**:
|
||||||
|
- VPS:¥30-50/月
|
||||||
|
- 数据库:¥10-20/月
|
||||||
|
- 存储:¥5-10/月
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 优先级执行策略
|
||||||
|
|
||||||
|
### 如果时间紧张(2 周完成)
|
||||||
|
**优先完成 P0 任务**:
|
||||||
|
1. 后端基础 + 数据库
|
||||||
|
2. 评论和统计 API
|
||||||
|
3. 前端组件集成
|
||||||
|
4. 基础部署
|
||||||
|
|
||||||
|
**跳过**:
|
||||||
|
- 完整测试 → 手动测试即可
|
||||||
|
- 性能优化 → 后期迭代
|
||||||
|
- 用户系统 → 第二期实现
|
||||||
|
|
||||||
|
### 正常周期(4-6 周)
|
||||||
|
**完成所有 P0 + P1 任务**:
|
||||||
|
- 完整功能实现
|
||||||
|
- 充分测试
|
||||||
|
- 完善文档
|
||||||
|
- 生产级部署
|
||||||
|
|
||||||
|
### 完全版本(8-12 周)
|
||||||
|
**完成所有任务**:
|
||||||
|
- 包含所有 P2 优化
|
||||||
|
- 包含 P3 高级功能
|
||||||
|
- 完整的监控和维护体系
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 检查清单模板
|
||||||
|
|
||||||
|
### 每周完成检查
|
||||||
|
- [ ] 所有 P0 任务完成
|
||||||
|
- [ ] 代码审查通过
|
||||||
|
- [ ] 测试覆盖率 > 80%
|
||||||
|
- [ ] 文档更新
|
||||||
|
- [ ] 部署到测试环境
|
||||||
|
- [ ] 演示给利益相关者
|
||||||
|
|
||||||
|
### 上线前最终检查
|
||||||
|
- [ ] 所有单元测试通过
|
||||||
|
- [ ] 所有集成测试通过
|
||||||
|
- [ ] 所有 E2E 测试通过
|
||||||
|
- [ ] 性能测试通过(<2s 加载)
|
||||||
|
- [ ] 安全审计通过
|
||||||
|
- [ ] 备份和回滚计划准备好
|
||||||
|
- [ ] 监控和告警配置完成
|
||||||
|
- [ ] 团队培训完成
|
||||||
|
- [ ] 用户文档完备
|
||||||
|
- [ ] 发布公告准备好
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 下一步行动
|
||||||
|
|
||||||
|
现在可以开始执行这个 TodoList:
|
||||||
|
|
||||||
|
1. **立即开始**:第一阶段任务(环境准备)
|
||||||
|
2. **并行进行**:后端和前端开发(第二、三阶段)
|
||||||
|
3. **定期检查**:每周检查进度和质量
|
||||||
|
4. **灵活调整**:根据实际情况调整优先级
|
||||||
|
|
||||||
|
**建议**:建立一个项目管理工具(如 Trello、Notion、Linear)来追踪这些任务的进度。
|
||||||
|
|
||||||
1145
docs/SINGLE_VPS_DEPLOYMENT_PLAN.md
Normal file
1145
docs/SINGLE_VPS_DEPLOYMENT_PLAN.md
Normal file
File diff suppressed because it is too large
Load Diff
681
docs/WEBSITE_MODERNIZATION_PLAN.md
Normal file
681
docs/WEBSITE_MODERNIZATION_PLAN.md
Normal file
@@ -0,0 +1,681 @@
|
|||||||
|
# 🎯 网站现代化重构方案
|
||||||
|
|
||||||
|
**目标**:将静态 Astro 网站升级为具有评论系统、数据统计、用户交互的动态网站
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 方案对比
|
||||||
|
|
||||||
|
### 方案 A:Astro + Node.js 后端(推荐)⭐⭐⭐⭐⭐
|
||||||
|
|
||||||
|
**最适合当前项目!**
|
||||||
|
|
||||||
|
#### 技术栈
|
||||||
|
- **前端**:Astro + React/Vue 组件(动态部分)
|
||||||
|
- **后端**:Node.js + Express/Fastify
|
||||||
|
- **数据库**:PostgreSQL / MongoDB
|
||||||
|
- **缓存**:Redis
|
||||||
|
- **API**:REST / GraphQL
|
||||||
|
- **部署**:Docker + PM2
|
||||||
|
|
||||||
|
#### 优势
|
||||||
|
✅ 前后端分离,灵活可维护
|
||||||
|
✅ Astro 保留静态生成的性能优势
|
||||||
|
✅ 支持实时数据更新
|
||||||
|
✅ 可扩展性强
|
||||||
|
✅ 成熟的技术生态
|
||||||
|
|
||||||
|
#### 架构图
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ 用户浏览器 (静态部分) │
|
||||||
|
├─────────────────────────────────────┤
|
||||||
|
│ Astro 静态生成 │
|
||||||
|
│ - 首页、报告页面 │
|
||||||
|
│ - SEO 优化 │
|
||||||
|
└────────────────┬────────────────────┘
|
||||||
|
│
|
||||||
|
┌────────▼────────┐
|
||||||
|
│ 动态组件加载 │
|
||||||
|
│ (React/Vue) │
|
||||||
|
├─────────────────┤
|
||||||
|
│ - 评论系统 │
|
||||||
|
│ - 数据统计 │
|
||||||
|
│ - 用户互动 │
|
||||||
|
└────────┬────────┘
|
||||||
|
│ API 调用
|
||||||
|
┌────────▼────────┐
|
||||||
|
│ Node.js 后端 │
|
||||||
|
├─────────────────┤
|
||||||
|
│ - 用户管理 │
|
||||||
|
│ - 评论处理 │
|
||||||
|
│ - 数据分析 │
|
||||||
|
│ - 权限控制 │
|
||||||
|
└────────┬────────┘
|
||||||
|
│
|
||||||
|
┌────────▼────────┐
|
||||||
|
│ 数据库 │
|
||||||
|
├─────────────────┤
|
||||||
|
│ - PostgreSQL │
|
||||||
|
│ - Redis Cache │
|
||||||
|
└─────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 方案 B:Astro + 第三方服务(快速启动)⭐⭐⭐⭐
|
||||||
|
|
||||||
|
**无需后端维护,快速上线**
|
||||||
|
|
||||||
|
#### 技术栈
|
||||||
|
- **前端**:Astro(保持不变)
|
||||||
|
- **评论**:Disqus / Giscus / Utterances
|
||||||
|
- **数据统计**:Google Analytics / Plausible
|
||||||
|
- **用户系统**:Supabase / Firebase
|
||||||
|
- **内容管理**:Contentful / Sanity
|
||||||
|
- **部署**:Vercel / Netlify
|
||||||
|
|
||||||
|
#### 优势
|
||||||
|
✅ 零后端维护
|
||||||
|
✅ 快速集成
|
||||||
|
✅ 自动扩展
|
||||||
|
✅ 成本低
|
||||||
|
✅ 部署简单
|
||||||
|
|
||||||
|
#### 缺点
|
||||||
|
❌ 功能受限
|
||||||
|
❌ 数据隐私性
|
||||||
|
❌ 成本可能增加
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 方案 C:Astro + Hybrid Rendering(终极方案)⭐⭐⭐⭐⭐
|
||||||
|
|
||||||
|
**Astro 4.0+ 的新特性:静态和动态混合**
|
||||||
|
|
||||||
|
#### 技术栈
|
||||||
|
- **Astro Hybrid SSR**:部分页面服务端渲染
|
||||||
|
- **后端**:Node.js
|
||||||
|
- **数据库**:PostgreSQL
|
||||||
|
- **实时**:WebSocket
|
||||||
|
- **部署**:自托管或 Vercel
|
||||||
|
|
||||||
|
#### 优势
|
||||||
|
✅ 性能最优
|
||||||
|
✅ SEO 完美
|
||||||
|
✅ 实时交互
|
||||||
|
✅ 完全控制
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ 推荐方案详细实现
|
||||||
|
|
||||||
|
### 方案 A:完整实现步骤
|
||||||
|
|
||||||
|
#### 第一阶段:前端重构(1-2周)
|
||||||
|
|
||||||
|
**1. 更新 Astro 配置支持混合渲染**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// astro.config.mjs
|
||||||
|
import { defineConfig } from 'astro/config';
|
||||||
|
import react from '@astrojs/react';
|
||||||
|
import tailwind from '@astrojs/tailwind';
|
||||||
|
import node from '@astrojs/node';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
integrations: [
|
||||||
|
react(),
|
||||||
|
tailwind(),
|
||||||
|
],
|
||||||
|
// 启用混合渲染
|
||||||
|
output: 'hybrid',
|
||||||
|
adapter: node({
|
||||||
|
mode: 'standalone'
|
||||||
|
}),
|
||||||
|
vite: {
|
||||||
|
define: {
|
||||||
|
'process.env.NODE_ENV': JSON.stringify('production')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. 创建动态 React 组件**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/components/comments/CommentSection.tsx
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import type { Comment } from '../../types/comment';
|
||||||
|
|
||||||
|
export default function CommentSection({ reportId }: { reportId: string }) {
|
||||||
|
const [comments, setComments] = useState<Comment[]>([]);
|
||||||
|
const [newComment, setNewComment] = useState('');
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchComments();
|
||||||
|
}, [reportId]);
|
||||||
|
|
||||||
|
const fetchComments = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/comments?reportId=${reportId}`);
|
||||||
|
const data = await response.json();
|
||||||
|
setComments(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch comments:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/comments', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
reportId,
|
||||||
|
content: newComment,
|
||||||
|
author: 'Anonymous', // 需要用户系统
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const comment = await response.json();
|
||||||
|
setComments([...comments, comment]);
|
||||||
|
setNewComment('');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to post comment:', error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mt-8 p-6 bg-white rounded-lg shadow">
|
||||||
|
<h2 className="text-2xl font-bold mb-4">评论 ({comments.length})</h2>
|
||||||
|
|
||||||
|
{/* 评论列表 */}
|
||||||
|
<div className="space-y-4 mb-6">
|
||||||
|
{comments.map((comment) => (
|
||||||
|
<div key={comment.id} className="p-4 bg-gray-50 rounded-lg">
|
||||||
|
<div className="flex justify-between items-start">
|
||||||
|
<strong className="text-gray-800">{comment.author}</strong>
|
||||||
|
<time className="text-sm text-gray-500">
|
||||||
|
{new Date(comment.createdAt).toLocaleDateString('zh-CN')}
|
||||||
|
</time>
|
||||||
|
</div>
|
||||||
|
<p className="text-gray-600 mt-2">{comment.content}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 评论表单 */}
|
||||||
|
<form onSubmit={handleSubmit} className="pt-6 border-t">
|
||||||
|
<textarea
|
||||||
|
value={newComment}
|
||||||
|
onChange={(e) => setNewComment(e.target.value)}
|
||||||
|
placeholder="分享你的想法..."
|
||||||
|
className="w-full p-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
rows={4}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={loading}
|
||||||
|
className="mt-3 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{loading ? '发布中...' : '发布评论'}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. 创建数据统计组件**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// src/components/stats/ReportStats.tsx
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import type { ReportStats as Stats } from '../../types/stats';
|
||||||
|
|
||||||
|
export default function ReportStats({ reportId }: { reportId: string }) {
|
||||||
|
const [stats, setStats] = useState<Stats | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch(`/api/stats/${reportId}`)
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(setStats);
|
||||||
|
}, [reportId]);
|
||||||
|
|
||||||
|
if (!stats) return <div>加载中...</div>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 p-6 bg-gradient-to-r from-blue-50 to-purple-50 rounded-lg">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="text-3xl font-bold text-blue-600">{stats.views}</div>
|
||||||
|
<div className="text-sm text-gray-600">浏览量</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="text-3xl font-bold text-green-600">{stats.comments}</div>
|
||||||
|
<div className="text-sm text-gray-600">评论数</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="text-3xl font-bold text-yellow-600">{stats.likes}</div>
|
||||||
|
<div className="text-sm text-gray-600">点赞数</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="text-3xl font-bold text-red-600">{stats.shares}</div>
|
||||||
|
<div className="text-sm text-gray-600">分享数</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 第二阶段:后端开发(2-3周)
|
||||||
|
|
||||||
|
**1. 创建 Node.js 后端**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// server/index.ts
|
||||||
|
import express from 'express';
|
||||||
|
import cors from 'cors';
|
||||||
|
import { Pool } from 'pg';
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
app.use(cors());
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
// 数据库连接
|
||||||
|
const pool = new Pool({
|
||||||
|
connectionString: process.env.DATABASE_URL
|
||||||
|
});
|
||||||
|
|
||||||
|
// ==================== 评论 API ====================
|
||||||
|
|
||||||
|
// 获取评论
|
||||||
|
app.get('/api/comments', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { reportId } = req.query;
|
||||||
|
const result = await pool.query(
|
||||||
|
`SELECT * FROM comments
|
||||||
|
WHERE report_id = $1
|
||||||
|
ORDER BY created_at DESC`,
|
||||||
|
[reportId]
|
||||||
|
);
|
||||||
|
res.json(result.rows);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 发布评论
|
||||||
|
app.post('/api/comments', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { reportId, content, author, email } = req.body;
|
||||||
|
|
||||||
|
// 数据验证
|
||||||
|
if (!content || content.length > 5000) {
|
||||||
|
return res.status(400).json({ error: '评论内容无效' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await pool.query(
|
||||||
|
`INSERT INTO comments (report_id, content, author, email, created_at)
|
||||||
|
VALUES ($1, $2, $3, $4, NOW())
|
||||||
|
RETURNING *`,
|
||||||
|
[reportId, content, author, email]
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json(result.rows[0]);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ==================== 统计 API ====================
|
||||||
|
|
||||||
|
// 获取报告统计
|
||||||
|
app.get('/api/stats/:reportId', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { reportId } = req.params;
|
||||||
|
|
||||||
|
const result = await pool.query(
|
||||||
|
`SELECT
|
||||||
|
COALESCE(views, 0) as views,
|
||||||
|
COALESCE(comments, 0) as comments,
|
||||||
|
COALESCE(likes, 0) as likes,
|
||||||
|
COALESCE(shares, 0) as shares
|
||||||
|
FROM report_stats
|
||||||
|
WHERE report_id = $1`,
|
||||||
|
[reportId]
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json(result.rows[0] || {
|
||||||
|
views: 0,
|
||||||
|
comments: 0,
|
||||||
|
likes: 0,
|
||||||
|
shares: 0
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 记录浏览
|
||||||
|
app.post('/api/stats/:reportId/view', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { reportId } = req.params;
|
||||||
|
await pool.query(
|
||||||
|
`INSERT INTO report_stats (report_id, views)
|
||||||
|
VALUES ($1, 1)
|
||||||
|
ON CONFLICT (report_id)
|
||||||
|
DO UPDATE SET views = views + 1`,
|
||||||
|
[reportId]
|
||||||
|
);
|
||||||
|
res.json({ success: true });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ==================== 点赞 API ====================
|
||||||
|
|
||||||
|
app.post('/api/stats/:reportId/like', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { reportId } = req.params;
|
||||||
|
const clientId = req.body.clientId; // 使用客户端 ID 避免重复点赞
|
||||||
|
|
||||||
|
const checkResult = await pool.query(
|
||||||
|
`SELECT * FROM likes WHERE report_id = $1 AND client_id = $2`,
|
||||||
|
[reportId, clientId]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (checkResult.rows.length > 0) {
|
||||||
|
return res.status(400).json({ error: '已点赞' });
|
||||||
|
}
|
||||||
|
|
||||||
|
await pool.query(
|
||||||
|
`INSERT INTO likes (report_id, client_id) VALUES ($1, $2)`,
|
||||||
|
[reportId, clientId]
|
||||||
|
);
|
||||||
|
|
||||||
|
await pool.query(
|
||||||
|
`UPDATE report_stats SET likes = likes + 1 WHERE report_id = $1`,
|
||||||
|
[reportId]
|
||||||
|
);
|
||||||
|
|
||||||
|
res.json({ success: true });
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const PORT = process.env.PORT || 3000;
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Server running on port ${PORT}`);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. 数据库 schema**
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 评论表
|
||||||
|
CREATE TABLE comments (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
report_id VARCHAR(100) NOT NULL,
|
||||||
|
author VARCHAR(100) NOT NULL,
|
||||||
|
email VARCHAR(100),
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
status VARCHAR(20) DEFAULT 'pending', -- pending, approved, rejected
|
||||||
|
created_at TIMESTAMP DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMP DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 报告统计表
|
||||||
|
CREATE TABLE report_stats (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
report_id VARCHAR(100) UNIQUE NOT NULL,
|
||||||
|
views INTEGER DEFAULT 0,
|
||||||
|
comments INTEGER DEFAULT 0,
|
||||||
|
likes INTEGER DEFAULT 0,
|
||||||
|
shares INTEGER DEFAULT 0,
|
||||||
|
created_at TIMESTAMP DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMP DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 点赞表
|
||||||
|
CREATE TABLE likes (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
report_id VARCHAR(100) NOT NULL,
|
||||||
|
client_id VARCHAR(100) NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT NOW(),
|
||||||
|
UNIQUE(report_id, client_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 索引优化
|
||||||
|
CREATE INDEX idx_comments_report ON comments(report_id);
|
||||||
|
CREATE INDEX idx_stats_report ON report_stats(report_id);
|
||||||
|
CREATE INDEX idx_likes_report ON likes(report_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 第三阶段:集成与部署(1-2周)
|
||||||
|
|
||||||
|
**1. 在 Astro 页面中使用新组件**
|
||||||
|
|
||||||
|
```astro
|
||||||
|
---
|
||||||
|
// src/pages/report/ai-eda-paper-report/index.astro
|
||||||
|
// ... 其他导入
|
||||||
|
import CommentSection from '../../../components/comments/CommentSection';
|
||||||
|
import ReportStats from '../../../components/stats/ReportStats';
|
||||||
|
|
||||||
|
const reportId = 'ai-eda-paper-report';
|
||||||
|
|
||||||
|
// 记录浏览
|
||||||
|
if (Astro.request.method === 'GET') {
|
||||||
|
await fetch('http://localhost:3000/api/stats/ai-eda-paper-report/view', {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout ...>
|
||||||
|
<!-- 统计卡片 -->
|
||||||
|
<ReportStats client:load reportId={reportId} />
|
||||||
|
|
||||||
|
<!-- ... 其他内容 -->
|
||||||
|
|
||||||
|
<!-- 评论系统 -->
|
||||||
|
<CommentSection client:load reportId={reportId} />
|
||||||
|
</BaseLayout>
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Docker 部署**
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
# Dockerfile
|
||||||
|
FROM node:18-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm ci --only=production
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
CMD ["npm", "start"]
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Docker Compose**
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
api:
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgresql://user:password@postgres:5432/jiao77
|
||||||
|
NODE_ENV: production
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
postgres:
|
||||||
|
image: postgres:15
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: user
|
||||||
|
POSTGRES_PASSWORD: password
|
||||||
|
POSTGRES_DB: jiao77
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
image: nginx:alpine
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
volumes:
|
||||||
|
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||||
|
depends_on:
|
||||||
|
- api
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
postgres_data:
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 实施路线图
|
||||||
|
|
||||||
|
| 阶段 | 任务 | 时间 | 优先级 |
|
||||||
|
|-----|------|------|--------|
|
||||||
|
| **P0** | 前端:Astro 混合渲染配置 | 3天 | 🔴 必须 |
|
||||||
|
| **P0** | 前端:React 评论组件 | 4天 | 🔴 必须 |
|
||||||
|
| **P0** | 后端:Express 基础项目 | 2天 | 🔴 必须 |
|
||||||
|
| **P1** | 后端:评论 API | 3天 | 🟠 重要 |
|
||||||
|
| **P1** | 后端:统计 API | 2天 | 🟠 重要 |
|
||||||
|
| **P1** | 数据库设计与优化 | 2天 | 🟠 重要 |
|
||||||
|
| **P2** | 前端:统计组件 | 2天 | 🟡 可选 |
|
||||||
|
| **P2** | 用户认证系统 | 5天 | 🟡 可选 |
|
||||||
|
| **P3** | 测试与优化 | 3天 | 🟢 后续 |
|
||||||
|
| **P3** | Docker 部署 | 2天 | 🟢 后续 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💰 成本对比
|
||||||
|
|
||||||
|
### 方案 A:自托管(年成本)
|
||||||
|
- VPS:¥360-1200
|
||||||
|
- 域名:¥69
|
||||||
|
- SSL:免费 (Let's Encrypt)
|
||||||
|
- 总计:≈ ¥500/年
|
||||||
|
|
||||||
|
### 方案 B:第三方服务(年成本)
|
||||||
|
- Disqus Pro:$120
|
||||||
|
- Firebase:≈ $50-500
|
||||||
|
- Google Analytics:免费
|
||||||
|
- 总计:≈ ¥1000-4000/年
|
||||||
|
|
||||||
|
### 方案 C:Vercel 部署(年成本)
|
||||||
|
- 高级计划:$20/月
|
||||||
|
- 数据库:¥500/年
|
||||||
|
- 总计:≈ ¥3000/年
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 安全建议
|
||||||
|
|
||||||
|
1. **数据验证**
|
||||||
|
```typescript
|
||||||
|
// 使用 zod 进行数据验证
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const CommentSchema = z.object({
|
||||||
|
reportId: z.string().min(1),
|
||||||
|
content: z.string().min(1).max(5000),
|
||||||
|
author: z.string().min(1).max(100),
|
||||||
|
email: z.string().email().optional()
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **XSS 防护**
|
||||||
|
```typescript
|
||||||
|
import DOMPurify from 'isomorphic-dompurify';
|
||||||
|
|
||||||
|
const sanitized = DOMPurify.sanitize(userInput);
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **速率限制**
|
||||||
|
```typescript
|
||||||
|
import rateLimit from 'express-rate-limit';
|
||||||
|
|
||||||
|
const limiter = rateLimit({
|
||||||
|
windowMs: 15 * 60 * 1000,
|
||||||
|
max: 100
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/comments', limiter, (req, res) => {
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **CORS 配置**
|
||||||
|
```typescript
|
||||||
|
app.use(cors({
|
||||||
|
origin: process.env.ALLOWED_ORIGINS?.split(','),
|
||||||
|
credentials: true
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 性能优化
|
||||||
|
|
||||||
|
1. **查询优化**
|
||||||
|
- 添加数据库索引
|
||||||
|
- 使用分页加载评论
|
||||||
|
- Redis 缓存热门数据
|
||||||
|
|
||||||
|
2. **前端优化**
|
||||||
|
- 代码分割
|
||||||
|
- 懒加载评论组件
|
||||||
|
- 虚拟滚动(大量评论)
|
||||||
|
|
||||||
|
3. **CDN 加速**
|
||||||
|
- 静态资源 CDN
|
||||||
|
- API 响应缓存
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 总结
|
||||||
|
|
||||||
|
**最推荐方案:方案 A(Astro + Node.js + PostgreSQL)**
|
||||||
|
|
||||||
|
✅ **完全控制**:所有功能自主开发
|
||||||
|
✅ **成本低**:仅需便宜 VPS
|
||||||
|
✅ **性能好**:Astro 保留优势
|
||||||
|
✅ **可扩展**:微服务架构
|
||||||
|
✅ **学习价值**:深入技术栈
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 快速开始
|
||||||
|
|
||||||
|
你想我帮你立即开始哪个方面?
|
||||||
|
|
||||||
|
1. **立即创建后端项目** → 生成 Express 项目结构
|
||||||
|
2. **更新 Astro 配置** → 启用混合渲染
|
||||||
|
3. **创建 React 组件** → 评论和统计组件
|
||||||
|
4. **设计数据库** → SQL 脚本和迁移
|
||||||
|
5. **Docker 配置** → 完整部署方案
|
||||||
|
|
||||||
Reference in New Issue
Block a user