
项目规则 project_rules
# 个人网站项目开发规范 (Project Rules)
> **版本**: v1.8
> **更新时间**: 2025年6月23日
> **适用范围**: 个人网站Web应用全栈开发
这是飞书地址,里面有md文档可下载。https://nvraic4kff.feishu.cn/wiki/NgCxwT9W2iW4d9kbvcgcuh0TnRh?from=from_copylink
## 📋 目录
- @项目概述
- @团队角色定义
- @目标用户画像
- @技术架构规范
- @设计规范
- @开发流程
- @代码规范
- @数据库规范
- @API设计规范
- @测试规范
- @部署规范
- @数据规范
- @UI交互规范
- @性能优化规范
- @安全规范
- @国际化规范
- @错误处理规范
- @版本控制规范
---
## 🎯 项目概述
### 项目定位
基于Flask和Astro的个人网站Web应用,主要为作者提供管理文章、音频、视频博客的功能,也能提供其他的其如个人经历展示,作品集展示等能力。粉丝定位为对前沿科技感兴趣的人,面向大众,主要包括人工智能、互联网、智能硬件等前沿科技领域。
### 核心功能
- **管理博客**: 为作者管理文章、音频、视频博客,允许粉丝登录注册并互动
- **个人经历展示**: 作者可以在网站上展示个人经历和作品集
- **数据分析**: 分析粉丝的点击、阅读、评论、转发等行为
- **用户系统**: 登录注册点赞评论等
- **其他**: 其他AI相关的能力尝试
---
## 👥 团队角色定义
### 🎨 产品经理 (20年经验)
- **职责**: 产品规划、需求分析、用户体验设计
- **目标**: 不断地洞察目标用户和市场,以用户为中心,确保产品具有竞争力。确保业务架构和流程合理,追求极致的用户体验
- **原则**: 站在用户角度思考,主动完成所有工作
### 🎨 UI/UX设计师 (20年经验)
- **职责**: 界面设计、交互设计、视觉规范
- **目标**: 符合目标人群使用习惯,现代化UI元素,追求极致的用户体验
- **原则**: 注重产品体验和视觉效果,移动端优先
### 🏗️ 后端架构师 (20年经验)
- **职责**: 系统架构、数据库设计、API设计
- **目标**: 简洁优雅的程序设计,良好的一致性和性能
- **原则**: 面向对象设计思维,高内聚低耦合
### 💻 全栈工程师 (20年经验)
- **职责**: 前后端开发、系统集成、功能实现
- **目标**: 帮助用户完成产品前后端开发
- **原则**: 代码简洁高效,注重可维护性
### 🧪 测试工程师 (20年经验)
- **职责**: 测试策略、质量保证、性能优化
- **目标**: 确保产品质量和用户体验,追求产品无bug
- **原则**: 功能测试、非功能测试、专项测试全覆盖
---
## 👤 目标用户画像
### 主要用户群体
- **年龄**: 对人工智能、互联网、智能硬件领域感兴趣的人,大多数在15岁到50岁之间
- **特征**: 有活力,有梦想,又干劲,愿意观看和消费科技内容
- **工作经历**: 他们往往在科技公司担任重要职位,如人工智能、互联网领域
### 用户需求层次
1. **核心需求**: 管理博客,包括图文、音频、视频博客,粉丝可登录注册,点赞评论转发
2. **进阶需求**: 展示作者的个人经历、作品集
3. **高级需求**: 做人工智能相关功能
### 产品调性
- **科技风**: 符合科技工作者审美和使用习惯
- **活力感**: 色彩丰富,动画效果生动
- **专业性**: 数据准确,功能实用
---
## 🛠️ 技术架构规范
### 技术栈选择
```
前端技术栈:
- 主网站: Astro v5.9.4+原生HTML+第三方CSS库+原生JS+第三方JS库
- 管理后台: React 18 + TypeScript + Ant Design v5 + Vite
- 响应式设计: CSS Media Queries + Flexbox + Grid
- 图标字体: Font Awesome / Ant Design Icons
- 动画效果: CSS3 Transitions + Animations
后端技术栈:
- Python 3.8+ + Flask 2.0+
- 数据库: MySQL 5.7+ / 8.0+
- ORM: 原生SQL + 自定义模型类
- API设计: RESTful API
数据存储:
- 主数据库: MySQL (个人网站数据、用户数据)
- 缓存: Redis (可选,API响应缓存)
- 文件存储: 本地文件系统
- 配置存储: JSON文件 + 环境变量
图片和资源管理:
- 图床方案: 本地存储优先
- 个人网站图片: 本地 `/images/` 目录
- 图标: 本地 `/images/icons/` 目录
- 默认图片: `/images/Default.svg`
- 外部图床:
- 备用方案: 腾讯云对象存储 / 七牛云
- CDN加速: 可选配置
- 访问域名: 自定义域名 + HTTPS
- 存储桶: 按资源类型分类存储
- 图片格式: PNG (高质量) + SVG (图标)
- 图片优化: WebP格式支持 (渐进式)
开发工具:
- 代码编辑器: Cursor
- 版本控制: Git + GitHub
- API测试: Postman / Thunder Client
- 数据库管理: MySQL Workbench / phpMyAdmin
- 包管理: pip (Python) + npm (Node.js可选)
部署和运维:
- 容器化: Docker (可选)
- Web服务器: Nginx (生产环境)
- 应用服务器: Gunicorn / uWSGI
- 进程管理: systemd / PM2
- 监控日志: 自定义日志系统
- 备份策略: 定时数据库备份
性能优化:
- 前端缓存: 浏览器缓存 + Service Worker (可选)
- 后端缓存: Redis缓存 + 内存缓存
- 数据库优化: 索引优化 + 查询优化
- 图片优化: 懒加载 + 压缩 + WebP
安全措施:
- 输入验证: 参数校验 + SQL注入防护
- 跨域处理: CORS配置
- 错误处理: 统一异常处理
- 日志记录: 操作日志 + 错误日志
```
### 项目结构
#### 主项目架构(已实现)
```
personal_website/ # 主项目根目录(Astro前端 + Flask后端)
├── src/ # Astro前端源代码
│ ├── pages/ # 页面文件
│ │ ├── index.astro # 首页
│ │ ├── page-about.astro # 关于页面
│ │ ├── page-contact.astro # 联系页面
│ │ ├── blog/ # 博客页面
│ │ │ └── [...slug].astro # 动态博客详情页
│ │ ├── en/ # 英文版页面
│ │ └── api/ # API路由(Astro)
│ │ ├── posts/ # 文章API
│ │ ├── categories.ts # 分类API
│ │ └── users.ts # 用户API
│ ├── components/ # 组件库
│ │ ├── layout/ # 布局组件
│ │ ├── blog/ # 博客相关组件
│ │ └── ui/ # UI组件
│ ├── layouts/ # 页面布局
│ ├── content/ # 内容文件
│ │ ├── blog/ # Markdown博客文章
│ │ └── portfolio/ # 作品集
│ ├── data/ # 静态数据
│ ├── i18n/ # 国际化文件
│ └── utils/ # 工具函数
├── public/ # 静态资源
│ ├── images/ # 图片资源
│ ├── css/ # 样式文件
│ ├── js/ # JavaScript文件
│ └── resume/ # 个人简历静态子站(私人访问,求职专用)
│ ├── index.html # 简历主页
│ ├── style.css # 简历样式
│ ├── assets/ # 简历静态资源
│ │ ├── css/ # Bootstrap等样式库
│ │ ├── js/ # 简历JavaScript文件
│ │ └── webfonts/ # 字体文件
│ └── img/ # 简历图片资源
│ ├── profile-avatar.jpg # 个人头像
│ ├── about-me.jpg # 关于我图片
│ ├── wechat.jpg # 微信二维码
│ ├── blog/ # 博客缩略图
│ ├── portfolio/ # 作品集图片
│ └── client/ # 客户案例图片
├── scripts/ # 脚本工具
├── sql/ # 数据库脚本
├── docs/ # 项目文档
├── astro.config.mjs # Astro配置
├── package.json # 前端依赖
└── README.md # 项目说明
```
#### 独立管理后台架构(推荐方案)
```
personal_website_admin/ # 独立管理后台项目
├── public/ # 静态资源
│ ├── index.html # 入口HTML
│ ├── favicon.ico # 图标
│ └── assets/ # 静态资源
├── src/ # 源代码目录
│ ├── components/ # React组件
│ │ ├── layout/ # 布局组件
│ │ │ ├── AdminLayout.tsx # 主布局组件
│ │ │ ├── Header.tsx # 顶部导航
│ │ │ ├── Sidebar.tsx # 侧边栏
│ │ │ └── Footer.tsx # 底部
│ │ ├── pages/ # 页面组件
│ │ │ ├── Dashboard.tsx # 仪表盘
│ │ │ ├── ArticleList.tsx # 文章列表
│ │ │ ├── ArticleEditor.tsx # 文章编辑器
│ │ │ ├── CategoryManagement.tsx # 分类管理
│ │ │ ├── UserManagement.tsx # 用户管理
│ │ │ ├── MediaLibrary.tsx # 媒体库
│ │ │ └── Settings.tsx # 系统设置
│ │ ├── common/ # 通用组件
│ │ │ ├── Loading.tsx # 加载组件
│ │ │ ├── ErrorBoundary.tsx # 错误边界
│ │ │ ├── ConfirmModal.tsx # 确认对话框
│ │ │ └── ImageUpload.tsx # 图片上传
│ │ └── forms/ # 表单组件
│ │ ├── ArticleForm.tsx # 文章表单
│ │ ├── CategoryForm.tsx # 分类表单
│ │ └── UserForm.tsx # 用户表单
│ ├── services/ # 服务层
│ │ ├── api.ts # API配置
│ │ ├── auth.ts # 认证服务
│ │ ├── articleService.ts # 文章服务
│ │ ├── categoryService.ts # 分类服务
│ │ ├── userService.ts # 用户服务
│ │ └── uploadService.ts # 上传服务
│ ├── hooks/ # 自定义Hooks
│ │ ├── useAuth.ts # 认证Hook
│ │ ├── useApi.ts # API Hook
│ │ └── useLocalStorage.ts # 本地存储Hook
│ ├── utils/ # 工具函数
│ │ ├── constants.ts # 常量定义
│ │ ├── helpers.ts # 辅助函数
│ │ ├── validators.ts # 验证函数
│ │ └── formatters.ts # 格式化函数
│ ├── styles/ # 样式文件
│ │ ├── globals.css # 全局样式
│ │ ├── variables.css # CSS变量
│ │ └── components/ # 组件样式
│ ├── types/ # TypeScript类型定义
│ │ ├── api.ts # API类型
│ │ ├── user.ts # 用户类型
│ │ ├── article.ts # 文章类型
│ │ └── common.ts # 通用类型
│ ├── context/ # React Context
│ │ ├── AuthContext.tsx # 认证上下文
│ │ └── ThemeContext.tsx # 主题上下文
│ ├── router/ # 路由配置
│ │ ├── index.tsx # 路由主文件
│ │ ├── PrivateRoute.tsx # 私有路由
│ │ └── routes.ts # 路由配置
│ ├── App.tsx # 主应用组件
│ ├── main.tsx # 应用入口
│ └── vite-env.d.ts # Vite类型声明
├── .env # 环境变量
├── .env.example # 环境变量示例
├── .gitignore # Git忽略文件
├── package.json # 项目依赖
├── tsconfig.json # TypeScript配置
├── vite.config.ts # Vite配置
├── tailwind.config.js # Tailwind配置(可选)
├── README.md # 项目说明
└── docs/ # 文档目录
├── API.md # API文档
├── DEPLOYMENT.md # 部署说明
└── DEVELOPMENT.md # 开发指南
```
#### 技术选型说明
```typescript
// package.json 依赖推荐
{
"name": "personal-website-admin",
"version": "1.0.0",
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.8.0",
"antd": "^5.12.0",
"@ant-design/icons": "^5.2.0",
"axios": "^1.6.0",
"dayjs": "^1.11.0",
"react-query": "^3.39.0",
"zustand": "^4.4.0", // 状态管理(轻量级)
"react-hook-form": "^7.48.0", // 表单处理
"zod": "^3.22.0", // 数据验证
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.0"
},
"devDependencies": {
"vite": "^5.0.0",
"@vitejs/plugin-react": "^4.2.0",
"typescript": "^5.0.0",
"eslint": "^8.55.0",
"@typescript-eslint/parser": "^6.14.0",
"@typescript-eslint/eslint-plugin": "^6.14.0",
"prettier": "^3.1.0"
}
}
```
#### Flask后端API结构(继续使用)
```
Flask API 服务器 (端口: 8000)
├── app/ # Flask应用目录
│ ├── __init__.py # 应用初始化
│ ├── models/ # 数据模型
│ │ ├── user.py # 用户模型
│ │ ├── article.py # 文章模型
│ │ ├── category.py # 分类模型
│ │ └── base.py # 基础模型
│ ├── routes/ # 路由定义
│ │ ├── auth.py # 认证路由
│ │ ├── articles.py # 文章路由
│ │ ├── categories.py # 分类路由
│ │ ├── users.py # 用户路由
│ │ └── upload.py # 上传路由
│ ├── services/ # 业务逻辑
│ │ ├── auth_service.py # 认证服务
│ │ ├── article_service.py # 文章服务
│ │ └── upload_service.py # 上传服务
│ ├── utils/ # 工具函数
│ │ ├── database.py # 数据库工具
│ │ ├── auth.py # 认证工具
│ │ └── validators.py # 验证器
│ └── config.py # 配置文件
├── migrations/ # 数据库迁移
├── requirements.txt # Python依赖
└── run.py # 启动文件
```
### 文件夹功能说明
#### 核心应用文件夹
#### 服务器和配置
- **server/**: 服务器启动和管理脚本
- `run.py`: 主要的服务器启动脚本
- `app_flask.py`: Flask应用的详细配置
- `start_server.py`: 服务器启动管理和监控
- **config/**: 项目配置文件
- `.my.cnf`: MySQL数据库连接配置
- `package.json`: Node.js依赖管理
- `app.js`: 应用级配置
#### 数据和数据库
- **data/**: 静态数据文件和配置
- `json/`: 结构化JSON数据文件
- **sql/**: 数据库相关SQL脚本
- 表结构创建、数据迁移、性能优化脚本
- **backups/**: 数据库定期备份文件
- 按时间戳命名的SQL备份文件
#### 工具和日志
- **tools/**: 开发和维护工具
- `check_progress.py`: 数据导入进度检查
- `update_personal_website_db.sh`: 数据库更新自动化脚本
- `init_personal_website_db.sh`: 数据库初始化脚本
- **logs/**: 应用和操作日志
- `app.log`: 应用运行日志
- `server_*.log`: 服务器启动和运行日志
- `import_*.log`: 数据导入操作日志
#### 开发环境
- **tests/**: 测试文件和演示页面
- **docs/**: 项目文档和说明
- **venv/**: Python虚拟环境
- **node_modules/**: Node.js依赖包
- **__pycache__/**: Python编译缓存
- **instance/**: Flask实例特定文件
#### 版本控制和IDE
- **.git/**: Git版本控制数据
- **.Cursor/**: Cursor编辑器配置
- **.cursor/**: Cursor IDE配置和规则
### 文件夹管理规范
### 开发环境
#### 主项目环境
- **前端**: Astro v5.9.4,端口3000
- **后端**: Flask,端口8000
- **Python**: 3.8+
- **Node.js**: 18.0+
- **数据库**: MySQL,使用 `config/my.cnf` 配置文件自动认证
- **版本控制**: Git + GitHub
#### 管理后台环境
- **框架**: React 18 + TypeScript + Vite
- **UI库**: Ant Design v5
- **端口**: 5173 (Vite默认开发端口)
- **状态管理**: Zustand (轻量级)
- **表单处理**: React Hook Form + Zod
- **HTTP请求**: Axios + React Query
- **构建工具**: Vite
- **代码质量**: ESLint + Prettier
### 端口管理规范
```bash
# 端口分配
- 3000: Astro前端开发服务器
- 主站访问: http://localhost:3000
- 5173: React管理后台开发服务器 (Vite)
- 8000: Flask后端API服务器
- 3306: MySQL数据库服务器
# 端口占用检查和释放
lsof -i :3000 # 查找Astro前端端口
lsof -i :5173 # 查找React管理后台端口
lsof -i :8000 # 查找Flask后端端口
kill PID # 正常终止进程 (替换PID为实际进程号)
kill -9 PID # 强制终止进程
# 服务启动命令
npm run dev # 启动Astro前端 (在主项目目录)
npm run dev # 启动React管理后台 (在管理后台目录)
python run.py # 启动Flask后端服务
```
### 数据库配置
```ini
# config/my.cnf 配置文件示例
[client]
host = localhost
user = your_username
password = your_password
database = personal_website_wiki
port = 3306
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
```
---
## 🎨 设计规范
### 整体设计原则
#### 科技感现代化设计
- **设计风格**: 简洁现代,突出科技感
- **色彩搭配**: 以黑白灰为主,黄色作为强调色
- **字体选择**: 系统默认字体栈,确保跨平台兼容性
- **布局理念**: 内容优先,减少视觉干扰
#### 文章列表设计规范
**核心设计决策:单列布局**
文章列表页面采用单列布局设计,这是基于用户体验和现代设计趋势的重要决策:
##### 设计理念
1. **内容为王**: 单列布局让用户专注于文章内容本身
2. **阅读体验**: 提供类似于书籍的线性阅读体验
3. **视觉统一**: 与移动端体验保持一致
4. **现代化**: 符合当前主流博客和内容网站的设计趋势
##### 视觉规范
- **卡片设计**: 简洁的卡片式设计,突出内容层次
- **间距控制**: 合理的垂直间距,营造舒适的视觉节奏
- **颜色运用**: 黑色文字,灰色辅助信息,黄色链接强调
- **图片处理**: 统一的图片比例和圆角设计
##### 响应式设计
- **大屏幕**: 666px固定宽度,居中显示
- **中屏幕**: 自适应宽度,保持合理边距
- **小屏幕**: 全宽显示,优化移动端体验
---
## 🔄 开发流程
### 第一步: 项目初始化
1. **需求理解**: 浏览README.md和项目文档
2. **架构分析**: 理解项目目标和实现方式
3. **文档更新**: 确保README.md描述准确完整
### 第二步: 需求分析和开发
1. **需求分析**: 充分理解用户需求,站在用户角度思考
2. **方案设计**: 选择最合适的解决方案
3. **原型设计**: 创建页面原型和交互流程
4. **开发实现**: 按照规范进行编码
### 第三步: 测试和优化
1. **功能测试**: 确保所有功能正常工作
2. **性能优化**: 优化加载速度和用户体验
3. **兼容性测试**: 确保跨浏览器兼容
4. **文档更新**: 更新相关文档和说明
---
## 💻 代码规范
按照Astro框架和Flask框架的规范执行
### 设计原则
1. **第一范式**: 所有字段都是不可分解的原子值
2. **第二范式**: 消除非主属性对主键的部分依赖
3. **第三范式**: 消除非主属性之间的传递依赖
### 命名规范
```sql
-- 表名使用小写+下划线
CREATE TABLE personal_website_type (
id INT PRIMARY KEY AUTO_INCREMENT,
personal_website_id INT NOT NULL, -- 统一使用personal_website_id作为个人网站标识
type_id INT NOT NULL,
-- 多语言支持
name_zh VARCHAR(50) COMMENT '中文名称',
name_ja VARCHAR(50) COMMENT '日文名称',
name_en VARCHAR(50) COMMENT '英文名称'
);
```
### 数据完整性
- **外键约束**: 确保数据关联完整性
- **索引优化**: 为常用查询字段添加索引
- **数据验证**: 在应用层和数据库层双重验证
---
## 🔌 API设计规范
### RESTful设计
```
GET /api/personal_website # 获取个人网站列表
GET /api/personal_website/{id} # 获取单个个人网站详情
POST /api/personal_website # 创建个人网站记录
PUT /api/personal_website/{id} # 更新个人网站信息
DELETE /api/personal_website/{id} # 删除个人网站记录
```
### 响应格式
### 错误处理
```json
{
"success": false,
"error": {
"code": "personal_website_NOT_FOUND",
"message": "未找到指定的个人网站",
"details": "personal_website_id: 9999 不存在"
},
"timestamp": "2024-12-19T10:30:00Z"
}
```
---
## 🧪 测试规范
### 测试层级
1. **单元测试**: 测试单个函数和方法
2. **集成测试**: 测试模块间的交互
3. **端到端测试**: 测试完整的用户流程
### 测试覆盖
- **功能测试**: 所有核心功能正常工作
- **性能测试**: 页面加载时间 < 3秒
- **兼容性测试**: 支持主流浏览器
- **响应式测试**: 适配不同屏幕尺寸
### 测试工具
```python
# 使用pytest进行单元测试
def test_get_personal_website_by_id():
personal_website = personal_website.get_by_personal_website_id(1)
assert personal_website.name_zh == "妙蛙种子"
assert personal_website.personal_website_id == 1
```
---
## 🚀 部署规范
### 环境配置
- **开发环境**: 本地开发,端口8000
- **测试环境**: 功能测试,数据隔离
- **生产环境**: 正式部署,性能优化
### 配置管理规范 🔧
#### Nginx配置文件管理
- **唯一配置文件**: `config/nginx-luyu668-complete.conf`
- **服务器路径**: `/etc/nginx/sites-available/luyu668.com`
- **禁止事项**:
- ❌ 不能创建新的nginx配置文件
- ❌ 不能直接在服务器上修改配置
- ❌ 不能保留多个版本的配置文件
#### 配置修改流程
1. 本地修改 `config/nginx-luyu668-complete.conf`
2. 上传: `scp config/nginx-luyu668-complete.conf personal-website:/var/www/personal-website/config/`
3. 应用: `sudo cp /var/www/personal-website/config/nginx-luyu668-complete.conf /etc/nginx/sites-available/luyu668.com`
4. 测试: `sudo nginx -t`
5. 重载: `sudo systemctl reload nginx`
#### 域名架构
- `luyu668.com` + `www.luyu668.com` → xxx端口 (Astro主站)
- `luyu668.com/xxx` → xxx端口 (React管理后台)
- `/api/*` → 8000端口 (Flask后端)
#### CDN配置标准
- 回源端口: 80 (不能用端口范围)
- 回源协议: HTTP
- 源站地址: xxx.xxx.xxx.xxx
### 部署流程
1. **代码审查**: 确保代码质量
2. **自动化测试**: 运行完整测试套件
3. **构建部署**: 自动化部署流程
4. **监控告警**: 实时监控系统状态
### 性能优化
- **图片优化**: 使用WebP格式,懒加载
- **CSS压缩**: 生产环境压缩CSS文件
- **JavaScript优化**: 代码分割,按需加载
- **缓存策略**: 合理设置缓存头
### 技术选型说明
#### 数据库连接配置
#### API缓存策略
```python
# Redis缓存配置
import redis
import json
from functools import wraps
redis_client = redis.Redis(
host=os.getenv('REDIS_HOST', 'localhost'),
port=int(os.getenv('REDIS_PORT', 6379)),
db=0,
decode_responses=True
)
def cache_result(expire_time=3600):
"""API结果缓存装饰器"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# 生成缓存键
cache_key = f"{func.__name__}:{hash(str(args) + str(kwargs))}"
# 尝试从缓存获取
cached_result = redis_client.get(cache_key)
if cached_result:
return json.loads(cached_result)
# 执行函数并缓存结果
result = func(*args, **kwargs)
redis_client.setex(cache_key, expire_time, json.dumps(result))
return result
return wrapper
return decorator
# 使用示例
@cache_result(expire_time=1800) # 缓存30分钟
def get_personal_website_list(page=1, limit=20):
# API逻辑
pass
```
#### 监控和日志配置
```python
# 日志配置
import logging
from logging.handlers import RotatingFileHandler
import os
def setup_logging(app):
"""配置应用日志"""
if not app.debug:
# 文件日志处理器
file_handler = RotatingFileHandler(
'logs/app.log',
maxBytes=10240000, # 10MB
backupCount=10
)
file_handler.setFormatter(logging.Formatter(
'%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'
))
file_handler.setLevel(logging.INFO)
app.logger.addHandler(file_handler)
# 错误日志处理器
error_handler = RotatingFileHandler(
'logs/error.log',
maxBytes=10240000,
backupCount=5
)
error_handler.setLevel(logging.ERROR)
app.logger.addHandler(error_handler)
app.logger.setLevel(logging.INFO)
app.logger.info('personal_website Wiki 启动')
# 性能监控
import time
from functools import wraps
def monitor_performance(func):
"""性能监控装饰器"""
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
execution_time = end_time - start_time
if execution_time > 1.0: # 超过1秒记录警告
app.logger.warning(
f"慢查询警告: {func.__name__} 执行时间: {execution_time:.2f}秒"
)
return result
return wrapper
```
## 📊 数据规范
### 数据源优先级
1. **本地数据库**: 优先从本地数据库获取数据
2. **本地仓库**: 从 `/Users/lewi/Documents/博客/personal_website` 获取图片资源
3. **外部API**: 暂无
### 媒体文件存储规范
#### 🗄️ 核心存储架构
项目采用 **"数据库记录 + 本地文件存储"** 的混合架构:
##### 1. 媒体文件表 (media)
```sql
CREATE TABLE `media` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '媒体ID',
`uuid` CHAR(36) NOT NULL COMMENT '媒体UUID',
`title` VARCHAR(200) NOT NULL COMMENT '媒体标题',
`filename` VARCHAR(255) NOT NULL COMMENT '文件名',
`file_path` VARCHAR(500) NOT NULL COMMENT '文件路径',
`file_url` VARCHAR(500) NOT NULL COMMENT '访问URL',
`file_type` VARCHAR(50) NOT NULL COMMENT '文件类型',
`mime_type` VARCHAR(100) NOT NULL COMMENT 'MIME类型',
`file_size` BIGINT UNSIGNED NOT NULL COMMENT '文件大小(字节)',
`dimensions` VARCHAR(20) COMMENT '图片尺寸(宽x高)',
`duration` INT UNSIGNED COMMENT '音视频时长(秒)',
`alt_text` VARCHAR(255) COMMENT '替代文本',
`caption` TEXT COMMENT '说明文字',
`author_id` INT UNSIGNED NOT NULL COMMENT '上传者ID',
`upload_ip` VARCHAR(45) COMMENT '上传IP地址',
`storage_type` ENUM('local', 'oss', 'cdn') DEFAULT 'local' COMMENT '存储类型',
`status` ENUM('active', 'deleted', 'processing') DEFAULT 'active' COMMENT '文件状态',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_uuid` (`uuid`),
KEY `idx_file_type` (`file_type`),
KEY `idx_author_id` (`author_id`),
KEY `idx_created_at` (`created_at`),
FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='媒体文件表';
```
##### 2. 文章媒体关联表 (post_media)
```sql
CREATE TABLE `post_media` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '关联ID',
`post_id` INT UNSIGNED NOT NULL COMMENT '文章ID',
`media_id` INT UNSIGNED NOT NULL COMMENT '媒体ID',
`position` INT UNSIGNED DEFAULT 0 COMMENT '在文章中的位置',
`usage_type` ENUM('featured', 'content', 'gallery', 'thumbnail') DEFAULT 'content' COMMENT '使用类型',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_post_media` (`post_id`, `media_id`),
KEY `idx_post_id` (`post_id`),
KEY `idx_media_id` (`media_id`),
KEY `idx_usage_type` (`usage_type`),
FOREIGN KEY (`post_id`) REFERENCES `posts`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`media_id`) REFERENCES `media`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章媒体关联表';
```
##### 3. 媒体标签表 (media_tags)
```sql
CREATE TABLE `media_tags` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '标签ID',
`media_id` INT UNSIGNED NOT NULL COMMENT '媒体ID',
`tag_name` VARCHAR(50) NOT NULL COMMENT '标签名称',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_media_tag` (`media_id`, `tag_name`),
KEY `idx_tag_name` (`tag_name`),
FOREIGN KEY (`media_id`) REFERENCES `media`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='媒体标签表';
```
#### 📁 文件存储结构
##### 本地存储目录规范
```
public/images/
├── uploads/ # 用户上传的媒体文件
│ ├── 2024/ # 按年份分类
│ │ ├── 01/ # 按月份分类
│ │ │ ├── images/ # 图片文件
│ │ │ │ ├── original/ # 原始图片
│ │ │ │ ├── large/ # 大尺寸 (1200px)
│ │ │ │ ├── medium/ # 中尺寸 (800px)
│ │ │ │ ├── small/ # 小尺寸 (400px)
│ │ │ │ └── thumb/ # 缩略图 (150px)
│ │ │ ├── videos/ # 视频文件
│ │ │ │ ├── original/ # 原始视频
│ │ │ │ ├── compressed/ # 压缩视频
│ │ │ │ └── thumbnails/ # 视频封面
│ │ │ └── audios/ # 音频文件
│ │ │ ├── original/ # 原始音频
│ │ │ └── compressed/ # 压缩音频
│ │ └── 02/ # 其他月份...
│ └── 2025/ # 其他年份...
├── thumbs/ # 系统默认缩略图
│ ├── masonry/ # 瀑布流缩略图
│ ├── about/ # 关于页面图片
│ └── contact/ # 联系页面图片
└── default/ # 默认图片
├── avatar.png # 默认头像
├── cover.jpg # 默认封面
└── placeholder.svg # 占位图
```
#### 🔧 媒体处理规范
##### 图片处理流程
```python
class ImageProcessor:
"""图片处理器"""
SIZES = {
'original': None, # 保持原尺寸
'large': 1200, # 大尺寸
'medium': 800, # 中尺寸
'small': 400, # 小尺寸
'thumb': 150 # 缩略图
}
QUALITY = {
'original': 95, # 原图高质量
'large': 85, # 大图中等质量
'medium': 80, # 中图标准质量
'small': 75, # 小图压缩质量
'thumb': 70 # 缩略图压缩质量
}
@staticmethod
def process_image(image_path, output_dir):
"""处理图片生成多个尺寸"""
for size_name, width in ImageProcessor.SIZES.items():
if width:
# 调整尺寸并保存
resized_image = resize_image(image_path, width)
save_path = f"{output_dir}/{size_name}/{filename}"
save_image(resized_image, save_path,
quality=ImageProcessor.QUALITY[size_name])
```
##### 视频处理流程
```python
class VideoProcessor:
"""视频处理器"""
@staticmethod
def process_video(video_path, output_dir):
"""处理视频文件"""
# 1. 生成视频封面
thumbnail_path = f"{output_dir}/thumbnails/{filename}.jpg"
extract_video_thumbnail(video_path, thumbnail_path)
# 2. 压缩视频文件
compressed_path = f"{output_dir}/compressed/{filename}"
compress_video(video_path, compressed_path,
bitrate='1000k', resolution='720p')
# 3. 获取视频信息
duration = get_video_duration(video_path)
dimensions = get_video_dimensions(video_path)
return {
'duration': duration,
'dimensions': dimensions,
'thumbnail': thumbnail_path,
'compressed': compressed_path
}
```
#### 🌐 访问URL规范
##### URL生成策略
```python
class MediaURLGenerator:
"""媒体URL生成器"""
@staticmethod
def generate_image_url(media_record, size='medium'):
"""生成图片URL"""
if media_record['storage_type'] == 'local':
return f"/images/uploads/{media_record['file_path']}/{size}/{media_record['filename']}"
elif media_record['storage_type'] == 'oss':
return f"https://cdn.example.com/{media_record['file_path']}"
@staticmethod
def generate_video_url(media_record, quality='compressed'):
"""生成视频URL"""
if quality == 'original':
return f"/images/uploads/{media_record['file_path']}/original/{media_record['filename']}"
else:
return f"/images/uploads/{media_record['file_path']}/compressed/{media_record['filename']}"
```
#### 📝 数据模型使用示例
##### 文章中插入媒体
```python
def insert_media_to_post(post_id, media_file, usage_type='content'):
"""在文章中插入媒体文件"""
# 1. 处理上传的文件
processed_media = process_uploaded_file(media_file)
# 2. 插入媒体记录
media_id = insert_media_record({
'uuid': str(uuid.uuid4()),
'title': processed_media['title'],
'filename': processed_media['filename'],
'file_path': processed_media['path'],
'file_url': processed_media['url'],
'file_type': processed_media['type'],
'mime_type': processed_media['mime'],
'file_size': processed_media['size'],
'dimensions': processed_media.get('dimensions'),
'duration': processed_media.get('duration'),
'author_id': get_current_user_id(),
'storage_type': 'local'
})
# 3. 建立文章媒体关联
insert_post_media_relation({
'post_id': post_id,
'media_id': media_id,
'usage_type': usage_type,
'position': get_next_position(post_id)
})
return media_id
```
##### 获取文章媒体
```python
def get_post_media(post_id, usage_type=None):
"""获取文章关联的媒体文件"""
query = """
SELECT m.*, pm.usage_type, pm.position
FROM media m
JOIN post_media pm ON m.id = pm.media_id
WHERE pm.post_id = %s
"""
params = [post_id]
if usage_type:
query += " AND pm.usage_type = %s"
params.append(usage_type)
query += " ORDER BY pm.position ASC"
return execute_query(query, params)
```
#### 🚀 性能优化规范
##### 图片懒加载实现
```javascript
// 响应式图片组件
function createResponsiveImage(media) {
return `
`;
}
```
##### 媒体缓存策略
```python
@cache_result(expire_time=3600) # 缓存1小时
def get_media_info(media_id):
"""获取媒体信息(带缓存)"""
return fetch_media_from_database(media_id)
@cache_result(expire_time=7200) # 缓存2小时
def get_post_featured_image(post_id):
"""获取文章特色图片(带缓存)"""
return get_post_media(post_id, usage_type='featured')
```
#### 🔒 安全规范
##### 文件上传安全
```python
ALLOWED_IMAGE_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg'}
ALLOWED_VIDEO_EXTENSIONS = {'.mp4', '.avi', '.mov', '.wmv', '.flv', '.webm'}
ALLOWED_AUDIO_EXTENSIONS = {'.mp3', '.wav', '.flac', '.aac', '.ogg'}
MAX_FILE_SIZE = {
'image': 10 * 1024 * 1024, # 10MB
'video': 500 * 1024 * 1024, # 500MB
'audio': 50 * 1024 * 1024 # 50MB
}
def validate_uploaded_file(file):
"""验证上传文件安全性"""
# 1. 检查文件扩展名
ext = get_file_extension(file.filename).lower()
if ext not in ALLOWED_EXTENSIONS:
raise ValueError(f"不支持的文件类型: {ext}")
# 2. 检查文件大小
file_type = get_file_type(ext)
if file.content_length > MAX_FILE_SIZE[file_type]:
raise ValueError(f"文件过大,最大允许{MAX_FILE_SIZE[file_type]}字节")
# 3. 检查文件内容
if not validate_file_content(file):
raise ValueError("文件内容验证失败")
return True
```
### 图片资源规范
### 数据标识规范
### 数据完整性要求
---
## 🎯 UI交互规范
### 页面结构规范
#### 文章列表布局规范
**设计原则:单列布局优先**
个人网站的文章列表页面采用**单列布局设计**,这是经过深入分析和用户体验考虑后的设计决策:
##### 🎯 单列布局的优势
1. **专注阅读体验**:单列布局让用户专注于内容本身,减少视觉干扰
2. **现代设计趋势**:符合现代博客和内容网站的设计潮流
3. **内容聚焦**:避免多列布局可能带来的注意力分散
4. **移动端友好**:与移动端体验保持一致,提供统一的用户体验
##### 📐 布局技术规范
```javascript
// Masonry配置 - 单列布局优化
function getMasonryConfig() {
var screenWidth = $(window).width();
if (screenWidth > 1200) {
// 大屏幕:单列居中布局
return {
itemSelector: '.masonry__brick',
columnWidth: 666, // 固定卡片宽度
percentPosition: false,
resize: true,
horizontalOrder: true, // 保持水平顺序
fitWidth: true, // 容器宽度适应内容
transitionDuration: '0.2s'
};
}
// 中小屏幕配置...
}
```
##### 🎨 视觉设计规范
- **卡片宽度**:666px(大屏幕)- 提供最佳阅读宽度
- **卡片间距**:垂直间距保持一致,营造整洁的视觉效果
- **居中对齐**:在大屏幕上居中显示,充分利用空间
- **响应式适配**:小屏幕自动适应为单列全宽布局
##### 🚫 明确禁止的布局方式
- **多列瀑布流布局**:不采用传统的多列Masonry布局
- **网格布局**:不使用固定网格系统
- **卡片跳跃动画**:避免从底部弹出等干扰性动画
##### 📱 响应式布局策略
```css
/* 大屏幕:单列居中 */
@media screen and (min-width: 1200px) {
.masonry {
max-width: 666px;
margin: 0 auto;
}
}
/* 中屏幕:单列适应 */
@media screen and (max-width: 1199px) and (min-width: 768px) {
.masonry__brick {
width: calc(100% - 40px);
margin: 0 20px;
}
}
/* 小屏幕:全宽单列 */
@media screen and (max-width: 767px) {
.masonry__brick {
width: 100%;
margin: 0;
}
}
```
### 主题无关组件规范
#### 其他主题无关组件
#### CSS实现规范
### 交互设计原则
- **一致性**:
- **反馈性**: 所有交互操作提供视觉反馈
- **容错性**: 提供友好的错误提示和恢复机制
- **可访问性**: 支持键盘导航和屏幕阅读器
### 加载状态管理
- **统一加载动画**: 使用现有的动画
- **分页加载**: 每次加载20条数据,支持无限滚动
- **防抖处理**: 搜索输入500ms防抖
- **加载提示**: 显示"已加载全部XXX"结束标记
---
## ⚡ 性能优化规范
### 前端性能优化
```javascript
// 图片懒加载
const lazyLoadImages = () => {
const images = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.removeAttribute('data-src');
imageObserver.unobserve(img);
}
});
});
images.forEach(img => imageObserver.observe(img));
};
// API响应缓存
const apiCache = new Map();
const cacheCapacity = 50;
const fetchWithCache = async (url) => {
if (apiCache.has(url)) {
return apiCache.get(url);
}
const response = await fetch(url);
const data = await response.json();
if (apiCache.size >= cacheCapacity) {
const firstKey = apiCache.keys().next().value;
apiCache.delete(firstKey);
}
apiCache.set(url, data);
return data;
};
```
### 数据库性能优化
```sql
-- 为常用查询字段添加索引
CREATE INDEX idx_personal_website_name_zh ON personal_website(name_zh);
CREATE INDEX idx_personal_website_type_personal_website_id ON personal_website_type(personal_website_id);
CREATE INDEX idx_move_meta_category ON move(meta_category);
-- 复合索引优化复杂查询
CREATE INDEX idx_personal_website_search_composite ON personal_website(name_zh, generation_id, type_id);
```
### 加载性能目标
- **首屏加载**: < 2秒
- **页面切换**: < 1秒
- **搜索响应**: < 500ms
- **滚动加载**: < 300ms
---
## 🔒 安全规范
### 输入验证
```python
# 参数验证示例
def validate_personal_website_id(personal_website_id):
"""验证个人网站ID的有效性"""
if not isinstance(personal_website_id, int):
raise ValueError("personal_website_id必须为整数")
if personal_website_id < 1 or personal_website_id > 1010:
raise ValueError("personal_website_id超出有效范围")
return True
# SQL注入防护
def safe_query(query, params):
"""安全的数据库查询"""
cursor = get_db_connection().cursor()
cursor.execute(query, params) # 使用参数化查询
return cursor.fetchall()
```
### 数据安全
- **参数验证**: 所有用户输入必须验证
- **SQL注入防护**: 使用参数化查询
- **XSS防护**: 对输出内容进行转义
- **CSRF防护**: 重要操作添加CSRF令牌
### 访问控制
- **API限流**: 防止恶意请求
- **错误信息**: 不暴露敏感系统信息
- **日志记录**: 记录重要操作和异常
---
## 🌍 国际化规范
### 多语言支持
```javascript
// 语言切换功能
const i18n = {
'zh-CN': {
'personal_website': '个人网站',
'type': '属性',
'ability': '特性',
'loading': '加载中...'
},
'en-US': {
'personal_website': 'personal_website',
'type': 'Type',
'ability': 'Ability',
'loading': 'Loading...'
}
};
const t = (key, lang = 'zh-CN') => {
return i18n[lang][key] || key;
};
```
### 数据库多语言设计
```sql
-- 多语言字段命名规范
CREATE TABLE personal_website (
id INT PRIMARY KEY AUTO_INCREMENT,
personal_website_id INT UNIQUE NOT NULL,
name_zh VARCHAR(50) NOT NULL COMMENT '中文名称',
name_en VARCHAR(50) COMMENT '英文名称',
description_zh TEXT COMMENT '中文描述',
description_en TEXT COMMENT '英文描述'
);
```
### 本地化要求
- **默认语言**: 简体中文
- **支持语言**: 中文、英文
- **字体支持**: 确保多语言字体正确显示
- **日期格式**: 根据语言环境调整日期格式
---
## 🚨 错误处理规范
### 前端错误处理
```javascript
// 统一错误处理函数
const handleError = (error, context = '') => {
console.error(`${context}错误:`, error);
// 显示用户友好的错误信息
showErrorMessage(getErrorMessage(error));
// 记录错误日志
logError(error, context);
};
// 错误信息映射
const getErrorMessage = (error) => {
const errorMessages = {
'NETWORK_ERROR': '网络连接失败,请检查网络设置',
'personal_website_NOT_FOUND': '未找到指定的个人网站',
'API_TIMEOUT': '请求超时,请稍后重试',
'UNKNOWN_ERROR': '发生未知错误,请稍后重试'
};
return errorMessages[error.code] || errorMessages['UNKNOWN_ERROR'];
};
```
### 后端错误处理
```python
# 统一异常处理
@app.errorhandler(404)
def not_found(error):
return jsonify({
'success': False,
'error': {
'code': 'NOT_FOUND',
'message': '请求的资源不存在'
}
}), 404
@app.errorhandler(500)
def internal_error(error):
return jsonify({
'success': False,
'error': {
'code': 'INTERNAL_ERROR',
'message': '服务器内部错误'
}
}), 500
```
### 错误处理原则
- **用户友好**: 提供易懂的错误信息
- **不暴露敏感信息**: 不显示系统内部错误详情
- **提供解决方案**: 告诉用户如何解决问题
- **日志记录**: 记录详细错误信息用于调试
---
## 📝 版本控制规范
### Git工作流程
```bash
# 分支命名规范
feature/personal_website-detail-page # 功能分支
bugfix/fix-search-issue # 修复分支
hotfix/critical-security-fix # 热修复分支
release/v1.2.0 # 发布分支
# 提交信息规范
feat: 添加文章详情页面
fix: 修复搜索功能的分页问题
docs: 更新API文档
style: 优化卡片样式
refactor: 重构数据库查询逻辑
test: 添加单元测试
chore: 更新依赖包版本
```
### 代码审查规范
- **必须审查**: 所有代码变更必须经过审查
- **审查要点**: 功能正确性、代码质量、性能影响、安全性
- **审查工具**: GitHub Pull Request
- **审查标准**: 至少一人审查通过才能合并
### 发布管理
- **版本号规范**: 使用语义化版本 (Semantic Versioning)
- **发布说明**: 每个版本包含详细的变更说明
- **回滚计划**: 准备快速回滚方案
- **发布验证**: 发布后进行功能验证
### 文件管理规范
- **测试文件**: 统一放在 `tests/` 目录
- **文档文件**: 统一放在 `docs/` 目录的子文件夹
- **临时文件**: 不提交临时文件到版本控制
- **配置文件**: 敏感配置使用环境变量或配置文件
---
## 🔧 工具和资源
### 开发工具
- **IDE**: VS Code / PyCharm
- **版本控制**: Git + GitHub
- **API测试**: Postman / Insomnia
- **数据库管理**: MySQL Workbench
### 设计资源
- **图片素材**: Unsplash, Pexels
- **图标库**: AweFont
- **字体**: 系统默认字体栈
- **颜色工具**: Coolors.co
---
## 📞 联系和支持
### 项目维护
- **项目负责人**: [Lewi]
- **技术支持**: [技术支持联系方式]
- **问题反馈**: GitHub Issues
### 更新日志
- **v1.8** (2024-12): 初始版本,建立基础开发规范
- **v1.1** (2025-01): 补充数据规范、UI交互规范、性能优化规范
- **v1.2** (2025-02): 添加安全规范、国际化规范、错误处理规范
- **v1.3** (2025-03): 完善版本控制规范,优化开发流程
- **v1.4** (2025-04): 增强测试规范,添加自动化测试指南
- **v1.5** (2025-05): 优化部署规范,添加Docker容器化部署
- **v1.6** (2025-06): 完善文档规范,建立完整的项目规范体系
- **v1.7** (2025-06): **重要更新**:确立文章列表单列布局设计规范,明确禁止多列瀑布流布局,完善UI设计原则和响应式布局策略
- **v1.8** (2025-06-23): **项目结构重组**:完成项目文件重组工作,新增tests/、tools/、templates/、config/四个功能目录,将根目录从70+个文件精简为约20个核心文件,更新项目架构文档,优化数据库配置文件路径为config/my.cnf,大幅提升项目结构清晰度和可维护性
- **后续版本**: 根据项目发展持续更新
---
*本文档是个人网站项目的核心开发规范,所有团队成员都应严格遵循。如有疑问或建议,请及时沟通讨论。*