项目规则 project_rules

项目规则 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 ` ${media.alt_text} `; } ``` ##### 媒体缓存策略 ```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,大幅提升项目结构清晰度和可维护性 - **后续版本**: 根据项目发展持续更新 --- *本文档是个人网站项目的核心开发规范,所有团队成员都应严格遵循。如有疑问或建议,请及时沟通讨论。*