diff --git a/README.md b/README.md new file mode 100644 index 0000000..3244ba3 --- /dev/null +++ b/README.md @@ -0,0 +1,152 @@ +# 项目管理系统 + +## 项目简介 + +项目管理系统是一个基于BS架构的企业级项目管理平台,旨在帮助企业实现项目全生命周期的数字化管理,包括项目创建、编辑、跟踪、统计等功能。 + +## 功能特性 + +### 1. 用户管理 +- **管理员**:可以创建用户,修改用户密码,权限 +- **市场部**:可以创建项目 +- **其他部门**:可以修改项目内容 + +### 2. 项目创建 +- 项目只能由市场部创建 +- 支持完整的项目信息录入,包括基本信息、项目描述、项目团队、项目时间、项目预算、项目里程碑、项目风险等 + +### 3. 项目编辑 +- 所有用户可以修改项目 +- 自动记录修改历史记录,便于追溯 + +### 4. 项目统计 +- 管理员可以通过各种条件筛选和统计项目 +- 支持多维度项目统计分析 + +## 技术栈 + +### 前端技术 +- **框架**:Vue 3 +- **状态管理**:Pinia +- **UI组件库**:Element Plus +- **路由**:Vue Router +- **网络请求**:Axios + +### 后端技术 +- **框架**:FastAPI 0.104.0+ +- **ORM框架**:SQLAlchemy 2.0.0+ +- **数据库驱动**:PyMySQL 1.1.0+ +- **数据库**:MySQL 8.0+ +- **认证**:JWT 2.8.0+ +- **密码加密**:Passlib 1.7.4+ +- **数据验证**:Pydantic 2.5.0+ +- **API文档**:Swagger (FastAPI自动生成) +- **构建工具**:Poetry 1.7.0+ + +## 项目结构 + +### 前端结构 +``` +├── public/ # 静态资源 +├── src/ # 源代码 +│ ├── assets/ # 资源文件 +│ ├── components/ # 公共组件 +│ ├── views/ # 页面组件 +│ ├── router/ # 路由配置 +│ ├── store/ # 状态管理 +│ ├── api/ # API请求 +│ ├── utils/ # 工具函数 +│ ├── constants/ # 常量定义 +│ ├── hooks/ # 自定义Hooks +│ ├── App.vue # 根组件 +│ └── main.js # 入口文件 +├── .env # 环境变量 +├── vite.config.js # Vite配置 +├── package.json # 项目依赖 +└── README.md # 项目说明 +``` + +### 后端结构 +``` +├── app/ # 应用代码 +│ ├── api/ # API路由 +│ ├── models/ # 数据模型 +│ ├── schemas/ # Pydantic模式 +│ ├── services/ # 业务逻辑 +│ ├── database/ # 数据库配置 +│ ├── common/ # 公共模块 +│ ├── config.py # 应用配置 +│ └── main.py # 应用入口 +├── tests/ # 测试代码 +├── alembic/ # 数据库迁移 +├── pyproject.toml # Poetry配置 +├── requirements.txt # 依赖列表 +└── README.md # 项目说明 +``` + +## 开发环境搭建 + +### 前端环境 +1. 克隆代码库: `git clone ` +2. 安装依赖: `npm install` +3. 启动开发服务器: `npm run dev` +4. 访问: `http://localhost:5173` + +### 后端环境 +1. 克隆代码库: `git clone ` +2. 安装依赖: `poetry install` 或 `pip install -r requirements.txt` +3. 配置数据库连接 (config.py) +4. 初始化数据库: `alembic upgrade head` +5. 启动应用: `uvicorn app.main:app --reload` +6. 访问: `http://localhost:8000` +7. API文档: `http://localhost:8000/docs` + +## 数据库配置 + +1. 创建数据库: `CREATE DATABASE project_management CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;` +2. 执行数据库初始化脚本: `docs/init.sql` +3. 配置数据库连接信息 + +## 系统初始化 + +### 初始用户 +- **管理员**:用户名: admin, 密码: 123456 +- **市场部**:用户名: marketing, 密码: 123456 +- **其他部门**:用户名: other, 密码: 123456 + +## 项目文档 + +- **产品需求文档**:docs/产品需求文档.md +- **功能规格说明书**:docs/功能规格说明书.md +- **数据库设计文档**:docs/数据库设计文档.md +- **技术架构文档**:docs/技术架构文档.md +- **项目示例文件**:docs/example.xsl + +## 部署方案 + +### 前端部署 +1. 构建生产版本: `npm run build` +2. 将构建产物复制到 Nginx 静态目录 +3. 配置 Nginx 反向代理 + +### 后端部署 +1. 安装依赖: `poetry install --no-dev` 或 `pip install -r requirements.txt` +2. 配置环境变量和数据库连接 +3. 使用 Gunicorn 启动应用: `gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker` +4. 配置 Nginx 反向代理 + +## 安全注意事项 + +1. 首次登录后请修改初始密码 +2. 定期备份数据库 +3. 避免使用弱密码 +4. 限制系统访问IP +5. 定期更新系统和依赖库版本 + +## 联系方式 + +如有问题或建议,请联系项目管理员。 + +--- + +© 2026 项目管理系统 版权所有 diff --git a/__init__.py b/__init__.py deleted file mode 100644 index 6cf50cb..0000000 --- a/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -import os - -from .xsl_nodes_mask import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS - - -NODE_CLASS_MAPPINGS = { - **NODE_CLASS_MAPPINGS, -} - -NODE_DISPLAY_NAME_MAPPINGS = { - **NODE_DISPLAY_NAME_MAPPINGS, -} - -__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] \ No newline at end of file diff --git a/backend/docs/后端API文档.md b/backend/docs/后端API文档.md new file mode 100644 index 0000000..993faa4 --- /dev/null +++ b/backend/docs/后端API文档.md @@ -0,0 +1,838 @@ +# 后端API文档 + +## 文档信息 +- **文档版本**: V1.0 +- **创建日期**: 2026-01-26 +- **文档类型**: 后端API文档 + +--- + +## 1. API概述 + +### 1.1 基础信息 + +- **API前缀**: `/api` +- **API文档地址**: `/docs` +- **认证方式**: JWT Token (Bearer Token) +- **响应格式**: JSON +- **错误处理**: 统一错误响应格式 + +### 1.2 响应格式 + +#### 成功响应 +```json +{ + "code": 200, + "message": "操作成功", + "data": {...} +} +``` + +#### 错误响应 +```json +{ + "code": 错误码, + "message": "错误信息" +} +``` + +### 1.3 错误码 + +| 错误码 | 说明 | +|--------|------| +| 400 | 请求参数错误 | +| 401 | 未授权 | +| 403 | 禁止访问 | +| 404 | 资源不存在 | +| 500 | 服务器内部错误 | +| 1001 | 用户名或密码错误 | +| 1002 | 用户不存在 | +| 1003 | 用户名已存在 | +| 1004 | 密码不一致 | +| 2001 | 项目不存在 | +| 2002 | 无权限操作 | + +--- + +## 2. 认证API + +### 2.1 登录 + +**接口地址**: `/api/auth/login` + +**请求方法**: `POST` + +**请求参数**: + +| 参数名 | 类型 | 位置 | 必填 | 说明 | +|--------|------|------|------|------| +| username | string | body | 是 | 用户名 | +| password | string | body | 是 | 密码 | + +**请求示例**: +```json +{ + "username": "admin", + "password": "123456" +} +``` + +**响应示例**: +```json +{ + "code": 200, + "message": "登录成功", + "data": { + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "userInfo": { + "userId": "admin001", + "username": "admin", + "realName": "系统管理员", + "department": "ADMIN", + "role": "ADMIN" + } + } +} +``` + +### 2.2 退出登录 + +**接口地址**: `/api/auth/logout` + +**请求方法**: `POST` + +**请求参数**: 无 + +**响应示例**: +```json +{ + "code": 200, + "message": "退出成功" +} +``` + +### 2.3 获取用户信息 + +**接口地址**: `/api/auth/userInfo` + +**请求方法**: `GET` + +**请求参数**: 无 + +**响应示例**: +```json +{ + "code": 200, + "message": "查询成功", + "data": { + "userId": "admin001", + "username": "admin", + "realName": "系统管理员", + "department": "ADMIN", + "role": "ADMIN" + } +} +``` + +--- + +## 3. 用户API + +### 3.1 创建用户 + +**接口地址**: `/api/user` + +**请求方法**: `POST` + +**请求参数**: + +| 参数名 | 类型 | 位置 | 必填 | 说明 | +|--------|------|------|------|------| +| username | string | body | 是 | 用户名 | +| password | string | body | 是 | 密码 | +| realName | string | body | 是 | 真实姓名 | +| department | string | body | 是 | 部门 | +| phone | string | body | 否 | 联系电话 | +| email | string | body | 否 | 邮箱 | +| role | string | body | 是 | 角色 | + +**请求示例**: +```json +{ + "username": "test", + "password": "123456", + "realName": "测试用户", + "department": "技术部", + "phone": "13800138000", + "email": "test@example.com", + "role": "OTHER" +} +``` + +**响应示例**: +```json +{ + "code": 200, + "message": "创建成功" +} +``` + +### 3.2 更新用户 + +**接口地址**: `/api/user/{id}` + +**请求方法**: `PUT` + +**请求参数**: + +| 参数名 | 类型 | 位置 | 必填 | 说明 | +|--------|------|------|------|------| +| id | string | path | 是 | 用户ID | +| realName | string | body | 否 | 真实姓名 | +| department | string | body | 否 | 部门 | +| phone | string | body | 否 | 联系电话 | +| email | string | body | 否 | 邮箱 | +| role | string | body | 否 | 角色 | + +**请求示例**: +```json +{ + "realName": "测试用户1", + "department": "市场部", + "role": "MARKETING" +} +``` + +**响应示例**: +```json +{ + "code": 200, + "message": "更新成功" +} +``` + +### 3.3 删除用户 + +**接口地址**: `/api/user/{id}` + +**请求方法**: `DELETE` + +**请求参数**: + +| 参数名 | 类型 | 位置 | 必填 | 说明 | +|--------|------|------|------|------| +| id | string | path | 是 | 用户ID | + +**响应示例**: +```json +{ + "code": 200, + "message": "删除成功" +} +``` + +### 3.4 获取用户列表 + +**接口地址**: `/api/user` + +**请求方法**: `GET` + +**请求参数**: + +| 参数名 | 类型 | 位置 | 必填 | 说明 | +|--------|------|------|------|------| +| page | integer | query | 否 | 页码,默认1 | +| size | integer | query | 否 | 每页数量,默认10 | +| username | string | query | 否 | 用户名(模糊查询) | +| department | string | query | 否 | 部门 | +| role | string | query | 否 | 角色 | + +**响应示例**: +```json +{ + "code": 200, + "message": "查询成功", + "data": [ + { + "userId": "admin001", + "username": "admin", + "realName": "系统管理员", + "department": "ADMIN", + "role": "ADMIN", + "status": 1, + "createTime": "2025-01-01 00:00:00", + "lastLoginTime": "2025-01-15 10:00:00" + } + ], + "total": 1 +} +``` + +### 3.5 重置密码 + +**接口地址**: `/api/user/resetPassword/{id}` + +**请求方法**: `PUT` + +**请求参数**: + +| 参数名 | 类型 | 位置 | 必填 | 说明 | +|--------|------|------|------|------| +| id | string | path | 是 | 用户ID | +| password | string | body | 是 | 新密码 | + +**请求示例**: +```json +{ + "password": "123456" +} +``` + +**响应示例**: +```json +{ + "code": 200, + "message": "密码重置成功" +} +``` + +--- + +## 4. 项目API + +### 4.1 创建项目 + +**接口地址**: `/api/project` + +**请求方法**: `POST` + +**请求参数**: + +| 参数名 | 类型 | 位置 | 必填 | 说明 | +|--------|------|------|------|------| +| projectName | string | body | 是 | 项目名称 | +| status | string | body | 是 | 项目状态 | +| leader | string | body | 是 | 负责人 | +| phone | string | body | 否 | 联系电话 | +| email | string | body | 否 | 邮箱 | +| background | string | body | 否 | 项目背景 | +| goal | string | body | 否 | 项目目标 | +| scope | string | body | 否 | 项目范围 | +| startDate | string | body | 是 | 开始日期 | +| plannedEndDate | string | body | 是 | 预计结束日期 | +| actualEndDate | string | body | 否 | 实际结束日期 | +| totalBudget | number | body | 是 | 总预算 | +| usedBudget | number | body | 否 | 已使用预算 | +| members | array | body | 否 | 项目成员 | +| milestones | array | body | 否 | 项目里程碑 | +| risks | array | body | 否 | 项目风险 | +| remarks | string | body | 否 | 备注 | + +**请求示例**: +```json +{ + "projectName": "测试项目", + "status": "NOT_STARTED", + "leader": "张三", + "phone": "13800138000", + "email": "zhangsan@example.com", + "background": "为了提升企业运营效率", + "goal": "完成企业内部管理系统的全面数字化改造", + "scope": "包括人力资源、财务管理、供应链管理等模块", + "startDate": "2025-01-15", + "plannedEndDate": "2025-06-30", + "totalBudget": 500000, + "usedBudget": 0, + "members": [ + { + "name": "张三", + "role": "项目经理", + "department": "市场部" + }, + { + "name": "李四", + "role": "技术负责人", + "department": "技术部" + } + ], + "milestones": [ + { + "name": "需求分析完成", + "plannedDate": "2025-02-15", + "status": "NOT_STARTED" + }, + { + "name": "系统设计完成", + "plannedDate": "2025-03-15", + "status": "NOT_STARTED" + } + ], + "risks": [ + { + "description": "技术难度较高,可能影响进度", + "level": "HIGH", + "measure": "增加技术团队人员投入" + } + ], + "remarks": "项目需要与现有系统进行数据对接" +} +``` + +**响应示例**: +```json +{ + "code": 200, + "message": "创建成功", + "data": { + "projectId": "PRJ2025001" + } +} +``` + +### 4.2 更新项目 + +**接口地址**: `/api/project/{id}` + +**请求方法**: `PUT` + +**请求参数**: + +| 参数名 | 类型 | 位置 | 必填 | 说明 | +|--------|------|------|------|------| +| id | string | path | 是 | 项目ID | +| projectName | string | body | 否 | 项目名称 | +| status | string | body | 否 | 项目状态 | +| leader | string | body | 否 | 负责人 | +| phone | string | body | 否 | 联系电话 | +| email | string | body | 否 | 邮箱 | +| background | string | body | 否 | 项目背景 | +| goal | string | body | 否 | 项目目标 | +| scope | string | body | 否 | 项目范围 | +| startDate | string | body | 否 | 开始日期 | +| plannedEndDate | string | body | 否 | 预计结束日期 | +| actualEndDate | string | body | 否 | 实际结束日期 | +| totalBudget | number | body | 否 | 总预算 | +| usedBudget | number | body | 否 | 已使用预算 | +| members | array | body | 否 | 项目成员 | +| milestones | array | body | 否 | 项目里程碑 | +| risks | array | body | 否 | 项目风险 | +| remarks | string | body | 否 | 备注 | + +**请求示例**: +```json +{ + "projectName": "测试项目1", + "status": "IN_PROGRESS", + "usedBudget": 100000 +} +``` + +**响应示例**: +```json +{ + "code": 200, + "message": "更新成功" +} +``` + +### 4.3 删除项目 + +**接口地址**: `/api/project/{id}` + +**请求方法**: `DELETE` + +**请求参数**: + +| 参数名 | 类型 | 位置 | 必填 | 说明 | +|--------|------|------|------|------| +| id | string | path | 是 | 项目ID | + +**响应示例**: +```json +{ + "code": 200, + "message": "删除成功" +} +``` + +### 4.4 获取项目列表 + +**接口地址**: `/api/project` + +**请求方法**: `GET` + +**请求参数**: + +| 参数名 | 类型 | 位置 | 必填 | 说明 | +|--------|------|------|------|------| +| page | integer | query | 否 | 页码,默认1 | +| size | integer | query | 否 | 每页数量,默认10 | +| projectName | string | query | 否 | 项目名称(模糊查询) | +| status | string | query | 否 | 项目状态 | +| leader | string | query | 否 | 负责人(模糊查询) | + +**响应示例**: +```json +{ + "code": 200, + "message": "查询成功", + "data": [ + { + "projectId": "PRJ2025001", + "projectNo": "PRJ2025001", + "projectName": "测试项目", + "status": "IN_PROGRESS", + "leader": "张三", + "createTime": "2025-01-15 00:00:00", + "updateTime": "2025-01-20 10:00:00" + } + ], + "total": 1 +} +``` + +### 4.5 获取项目详情 + +**接口地址**: `/api/project/{id}` + +**请求方法**: `GET` + +**请求参数**: + +| 参数名 | 类型 | 位置 | 必填 | 说明 | +|--------|------|------|------|------| +| id | string | path | 是 | 项目ID | + +**响应示例**: +```json +{ + "code": 200, + "message": "查询成功", + "data": { + "projectId": "PRJ2025001", + "projectNo": "PRJ2025001", + "projectName": "测试项目", + "status": "IN_PROGRESS", + "leader": "张三", + "phone": "13800138000", + "email": "zhangsan@example.com", + "background": "为了提升企业运营效率", + "goal": "完成企业内部管理系统的全面数字化改造", + "scope": "包括人力资源、财务管理、供应链管理等模块", + "startDate": "2025-01-15", + "plannedEndDate": "2025-06-30", + "actualEndDate": null, + "totalBudget": 500000, + "usedBudget": 100000, + "remainingBudget": 400000, + "members": [ + { + "name": "张三", + "role": "项目经理", + "department": "市场部" + }, + { + "name": "李四", + "role": "技术负责人", + "department": "技术部" + } + ], + "milestones": [ + { + "name": "需求分析完成", + "plannedDate": "2025-02-15", + "actualDate": null, + "status": "NOT_STARTED" + }, + { + "name": "系统设计完成", + "plannedDate": "2025-03-15", + "actualDate": null, + "status": "NOT_STARTED" + } + ], + "risks": [ + { + "description": "技术难度较高,可能影响进度", + "level": "HIGH", + "measure": "增加技术团队人员投入" + } + ], + "remarks": "项目需要与现有系统进行数据对接", + "creator": "marketing", + "createTime": "2025-01-15 00:00:00", + "lastModifier": "admin", + "updateTime": "2025-01-20 10:00:00" + } +} +``` + +--- + +## 5. 历史记录API + +### 5.1 获取项目历史 + +**接口地址**: `/api/history/{projectId}` + +**请求方法**: `GET` + +**请求参数**: + +| 参数名 | 类型 | 位置 | 必填 | 说明 | +|--------|------|------|------|------| +| projectId | string | path | 是 | 项目ID | +| page | integer | query | 否 | 页码,默认1 | +| size | integer | query | 否 | 每页数量,默认10 | +| operator | string | query | 否 | 操作人(模糊查询) | +| startDate | string | query | 否 | 开始日期 | +| endDate | string | query | 否 | 结束日期 | + +**响应示例**: +```json +{ + "code": 200, + "message": "查询成功", + "data": [ + { + "historyId": "HIS2025001", + "projectId": "PRJ2025001", + "projectNo": "PRJ2025001", + "operationType": "UPDATE", + "operator": "admin", + "operationTime": "2025-01-20 10:00:00", + "fieldName": "status", + "oldValue": "NOT_STARTED", + "newValue": "IN_PROGRESS" + } + ], + "total": 1 +} +``` + +--- + +## 6. 统计API + +### 6.1 筛选项目 + +**接口地址**: `/api/statistics/filter` + +**请求方法**: `POST` + +**请求参数**: + +| 参数名 | 类型 | 位置 | 必填 | 说明 | +|--------|------|------|------|------| +| projectNo | string | body | 否 | 项目编号 | +| projectName | string | body | 否 | 项目名称(模糊查询) | +| status | string | body | 否 | 项目状态 | +| leader | string | body | 否 | 负责人(模糊查询) | +| creator | string | body | 否 | 创建人(模糊查询) | +| createStartDate | string | body | 否 | 创建开始日期 | +| createEndDate | string | body | 否 | 创建结束日期 | +| updateStartDate | string | body | 否 | 更新开始日期 | +| updateEndDate | string | body | 否 | 更新结束日期 | +| page | integer | body | 否 | 页码,默认1 | +| size | integer | body | 否 | 每页数量,默认10 | + +**请求示例**: +```json +{ + "projectName": "测试", + "status": "IN_PROGRESS", + "createStartDate": "2025-01-01", + "createEndDate": "2025-12-31" +} +``` + +**响应示例**: +```json +{ + "code": 200, + "message": "查询成功", + "data": [ + { + "projectId": "PRJ2025001", + "projectNo": "PRJ2025001", + "projectName": "测试项目", + "status": "IN_PROGRESS", + "leader": "张三", + "createTime": "2025-01-15 00:00:00", + "updateTime": "2025-01-20 10:00:00" + } + ], + "total": 1 +} +``` + +### 6.2 统计项目 + +**接口地址**: `/api/statistics/statistics` + +**请求方法**: `POST` + +**请求参数**: + +| 参数名 | 类型 | 位置 | 必填 | 说明 | +|--------|------|------|------|------| +| dimension | string | body | 是 | 统计维度:status, department, leader, month | +| startDate | string | body | 否 | 开始日期 | +| endDate | string | body | 否 | 结束日期 | + +**请求示例**: +```json +{ + "dimension": "status", + "startDate": "2025-01-01", + "endDate": "2025-12-31" +} +``` + +**响应示例**: +```json +{ + "code": 200, + "message": "统计成功", + "data": { + "dimension": "status", + "total": 10, + "details": [ + { + "name": "NOT_STARTED", + "value": 2 + }, + { + "name": "IN_PROGRESS", + "value": 5 + }, + { + "name": "COMPLETED", + "value": 2 + }, + { + "name": "PAUSED", + "value": 1 + } + ] + } +} +``` + +--- + +## 7. 系统API + +### 7.1 获取操作日志 + +**接口地址**: `/api/system/log` + +**请求方法**: `GET` + +**请求参数**: + +| 参数名 | 类型 | 位置 | 必填 | 说明 | +|--------|------|------|------|------| +| page | integer | query | 否 | 页码,默认1 | +| size | integer | query | 否 | 每页数量,默认10 | +| username | string | query | 否 | 用户名(模糊查询) | +| operation | string | query | 否 | 操作内容(模糊查询) | +| startDate | string | query | 否 | 开始日期 | +| endDate | string | query | 否 | 结束日期 | + +**响应示例**: +```json +{ + "code": 200, + "message": "查询成功", + "data": [ + { + "logId": "LOG2025001", + "username": "admin", + "operation": "登录系统", + "ipAddress": "127.0.0.1", + "userAgent": "Mozilla/5.0...", + "operationTime": "2025-01-20 10:00:00" + } + ], + "total": 1 +} +``` + +--- + +## 8. 认证与授权 + +### 8.1 认证流程 + +1. 客户端发送登录请求到 `/api/auth/login` +2. 服务端验证用户名和密码 +3. 验证成功后生成 JWT Token +4. 客户端保存 Token 到 localStorage +5. 后续请求在请求头中携带 Token: `Authorization: Bearer ` +6. 服务端验证 Token 的有效性 +7. 验证通过后处理请求 + +### 8.2 授权流程 + +1. 服务端从 Token 中解析出用户信息 +2. 根据用户角色和权限判断是否有权限访问请求的资源 +3. 有权限则继续处理请求,无权限则返回 403 错误 + +### 8.3 角色权限 + +| 角色 | 权限 | +|------|------| +| ADMIN | 所有权限 | +| MARKETING | 创建项目、编辑项目、查看项目、查看历史记录 | +| OTHER | 编辑项目、查看项目、查看历史记录 | + +--- + +## 9. 附录 + +### 9.1 数据字典 + +#### 项目状态 +| 值 | 说明 | +|----|------| +| NOT_STARTED | 未开始 | +| IN_PROGRESS | 进行中 | +| COMPLETED | 已完成 | +| PAUSED | 已暂停 | +| CANCELLED | 已取消 | + +#### 角色 +| 值 | 说明 | +|----|------| +| ADMIN | 管理员 | +| MARKETING | 市场部 | +| OTHER | 其他部门 | + +#### 风险等级 +| 值 | 说明 | +|----|------| +| HIGH | 高 | +| MEDIUM | 中 | +| LOW | 低 | + +#### 操作类型 +| 值 | 说明 | +|----|------| +| CREATE | 创建 | +| UPDATE | 更新 | +| DELETE | 删除 | + +### 9.2 开发规范 + +- **API设计**: 遵循 RESTful API 设计规范 +- **参数验证**: 使用 Pydantic 进行参数验证 +- **错误处理**: 使用统一的错误处理机制 +- **日志记录**: 记录重要操作的日志 +- **测试**: 为每个 API 编写单元测试 + +### 9.3 变更记录 + +| 版本 | 日期 | 修改人 | 修改内容 | +|------|------|--------|----------| +| V1.0 | 2026-01-26 | - | 初始版本创建 | diff --git a/backend/docs/后端依赖管理文档.md b/backend/docs/后端依赖管理文档.md new file mode 100644 index 0000000..d032738 --- /dev/null +++ b/backend/docs/后端依赖管理文档.md @@ -0,0 +1,381 @@ +# 后端依赖管理文档 + +## 文档信息 +- **文档版本**: V1.0 +- **创建日期**: 2026-01-26 +- **文档类型**: 后端依赖管理文档 + +--- + +## 1. 依赖管理工具 + +本项目使用以下工具管理依赖: + +### 1.1 Poetry (推荐) + +Poetry 是一个现代化的 Python 依赖管理和打包工具,提供了更简洁、更一致的依赖管理体验。 + +**安装 Poetry**: +```bash +# 使用 pip 安装 +pip install poetry + +# 或使用官方安装脚本 +curl -sSL https://install.python-poetry.org | python3 - +``` + +### 1.2 pip + +如果您不使用 Poetry,也可以使用 pip 管理依赖。 + +--- + +## 2. 核心依赖 + +### 2.1 主要依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| fastapi | ^0.104.0 | 后端 Web 框架 | +| uvicorn[standard] | ^0.24.0 | ASGI 服务器 | +| sqlalchemy | ^2.0.0 | ORM 框架 | +| pymysql | ^1.1.0 | MySQL 数据库驱动 | +| python-jose[cryptography] | ^3.3.0 | JWT 库 | +| passlib[bcrypt] | ^1.7.4 | 密码哈希库 | +| pydantic | ^2.5.0 | 数据验证库 | +| pydantic-settings | ^2.1.0 | 配置管理库 | +| alembic | ^1.12.0 | 数据库迁移工具 | +| python-multipart | ^0.0.6 | 表单数据处理 | +| python-dotenv | ^1.0.0 | 环境变量管理 | +| aiomysql | ^0.2.0 | 异步 MySQL 驱动 | + +### 2.2 开发依赖 + +| 依赖 | 版本 | 用途 | +|------|------|------| +| pytest | ^7.4.0 | 测试框架 | +| pytest-asyncio | ^0.21.0 | 异步测试支持 | +| pytest-cov | ^4.1.0 | 测试覆盖率 | +| flake8 | ^6.1.0 | 代码风格检查 | +| black | ^23.11.0 | 代码格式化 | +| isort | ^5.12.0 | 导入排序 | +| mypy | ^1.7.0 | 类型检查 | + +--- + +## 3. 配置文件 + +### 3.1 pyproject.toml (Poetry) + +```toml +[tool.poetry] +name = "project-management-backend" +version = "0.1.0" +description = "项目管理系统后端" +authors = [{ name = "Developer", email = "developer@example.com" }] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.9" +fastapi = "^0.104.0" +uvicorn = { version = "^0.24.0", extras = ["standard"] } +sqlalchemy = "^2.0.0" +pymysql = "^1.1.0" +python-jose = { version = "^3.3.0", extras = ["cryptography"] } +passlib = { version = "^1.7.4", extras = ["bcrypt"] } +pydantic = "^2.5.0" +pydantic-settings = "^2.1.0" +alembic = "^1.12.0" +python-multipart = "^0.0.6" +python-dotenv = "^1.0.0" +aiomysql = "^0.2.0" + +[tool.poetry.group.dev.dependencies] +pytest = "^7.4.0" +pytest-asyncio = "^0.21.0" +pytest-cov = "^4.1.0" +flake8 = "^6.1.0" +black = "^23.11.0" +isort = "^5.12.0" +mypy = "^1.7.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" +``` + +### 3.2 requirements.txt (pip) + +``` +fastapi==0.104.0 +uvicorn[standard]==0.24.0 +sqlalchemy==2.0.0 +pymysql==1.1.0 +python-jose[cryptography]==3.3.0 +passlib[bcrypt]==1.7.4 +pydantic==2.5.0 +pydantic-settings==2.1.0 +alembic==1.12.0 +python-multipart==0.0.6 +python-dotenv==1.0.0 +aiomysql==0.2.0 + +# 开发依赖 +pytest==7.4.0 +pytest-asyncio==0.21.0 +pytest-cov==4.1.0 +flake8==6.1.0 +black==23.11.0 +isort==5.12.0 +mypy==1.7.0 +``` + +--- + +## 4. 依赖管理命令 + +### 4.1 使用 Poetry + +#### 安装依赖 +```bash +# 安装所有依赖(包括开发依赖) +poetry install + +# 仅安装生产依赖 +poetry install --no-dev +``` + +#### 添加依赖 +```bash +# 添加生产依赖 +poetry add + +# 添加开发依赖 +poetry add --group dev +``` + +#### 更新依赖 +```bash +# 更新所有依赖 +poetry update + +# 更新特定依赖 +poetry update +``` + +#### 导出依赖 +```bash +# 导出到 requirements.txt +poetry export --output requirements.txt + +# 导出生产依赖 +poetry export --output requirements.txt --without dev +``` + +#### 运行命令 +```bash +# 在虚拟环境中运行命令 +poetry run + +# 例如运行开发服务器 +poetry run uvicorn app.main:app --reload + +# 例如运行测试 +poetry run pytest +``` + +### 4.2 使用 pip + +#### 安装依赖 +```bash +# 安装所有依赖 +pip install -r requirements.txt + +# 安装生产依赖(需要单独创建生产依赖文件) +pip install -r requirements-prod.txt +``` + +#### 添加依赖 +```bash +# 安装并添加到 requirements.txt +pip install && pip freeze | grep >> requirements.txt +``` + +#### 更新依赖 +```bash +# 更新所有依赖 +pip install --upgrade -r requirements.txt + +# 更新特定依赖 +pip install --upgrade +``` + +--- + +## 5. 环境配置 + +### 5.1 环境变量 + +项目使用 `.env` 文件管理环境变量,示例配置如下: + +```env +# 数据库连接信息 +DATABASE_URL="mysql+pymysql://username:password@localhost:3306/project_management" + +# JWT 配置 +SECRET_KEY="your-secret-key" +ALGORITHM="HS256" +ACCESS_TOKEN_EXPIRE_MINUTES=30 + +# 应用配置 +APP_NAME="Project Management System" +DEBUG=True +``` + +### 5.2 配置文件 + +项目使用 `app/config.py` 管理应用配置: + +```python +from pydantic_settings import BaseSettings +from functools import lru_cache + + +class Settings(BaseSettings): + """应用配置""" + # 应用配置 + app_name: str = "Project Management System" + debug: bool = True + + # 数据库配置 + database_url: str + + # JWT 配置 + secret_key: str + algorithm: str = "HS256" + access_token_expire_minutes: int = 30 + + class Config: + env_file = ".env" + case_sensitive = False + + +@lru_cache() +def get_settings() -> Settings: + """获取配置实例""" + return Settings() + + +settings = get_settings() +``` + +--- + +## 6. 依赖版本管理 + +### 6.1 版本约束 + +Poetry 使用语义化版本约束,常见的版本约束格式: + +| 格式 | 说明 | 示例 | +|------|------|------| +| ^1.0.0 | 兼容 1.x.x 版本 | ^2.0.0 匹配 2.0.0, 2.1.0 但不匹配 3.0.0 | +| ~1.0.0 | 兼容 1.0.x 版本 | ~1.0.0 匹配 1.0.0, 1.0.1 但不匹配 1.1.0 | +| >=1.0.0 | 大于等于 1.0.0 | >=2.0.0 匹配 2.0.0, 3.0.0 | +| ==1.0.0 | 精确匹配 1.0.0 | ==1.0.0 只匹配 1.0.0 | + +### 6.2 依赖锁定 + +Poetry 使用 `poetry.lock` 文件锁定依赖版本,确保在不同环境中安装相同版本的依赖: + +```bash +# 生成锁定文件 +poetry lock + +# 使用锁定文件安装依赖 +poetry install +``` + +### 6.3 依赖解析 + +如果遇到依赖冲突,可以使用以下方法解决: + +1. **更新依赖版本**:尝试更新冲突的依赖版本 +2. **指定版本**:为冲突的依赖指定兼容的版本 +3. **使用虚拟环境**:为不同项目使用独立的虚拟环境 + +--- + +## 7. 开发最佳实践 + +### 7.1 依赖管理建议 + +1. **定期更新依赖**:定期更新依赖以获取安全补丁和新特性 +2. **使用锁定文件**:在生产环境中使用锁定文件确保依赖版本一致 +3. **分离开发和生产依赖**:只在生产环境中安装必要的依赖 +4. **使用虚拟环境**:为每个项目使用独立的虚拟环境 +5. **记录依赖变更**:在提交代码时同时提交依赖变更 + +### 7.2 安全注意事项 + +1. **检查依赖安全**:使用安全扫描工具检查依赖的安全漏洞 +2. **使用可信依赖**:只使用来自可信源的依赖 +3. **固定依赖版本**:在生产环境中固定依赖版本,避免自动更新引入问题 +4. **定期审计依赖**:定期审计项目依赖,移除不再使用的依赖 + +--- + +## 8. 常见问题 + +### 8.1 依赖安装失败 + +**问题**:依赖安装失败,出现版本冲突 + +**解决方法**: +1. 检查 Python 版本是否兼容 +2. 尝试使用 `poetry update` 更新依赖 +3. 检查网络连接是否正常 +4. 尝试清理缓存:`poetry cache clear --all pypi` + +### 8.2 数据库连接失败 + +**问题**:无法连接到数据库 + +**解决方法**: +1. 检查数据库服务是否运行 +2. 检查数据库连接字符串是否正确 +3. 检查数据库用户权限是否正确 +4. 检查网络连接是否正常 + +### 8.3 JWT 认证失败 + +**问题**:JWT 认证失败 + +**解决方法**: +1. 检查 `SECRET_KEY` 是否正确设置 +2. 检查 Token 是否过期 +3. 检查 Token 格式是否正确 +4. 检查认证中间件配置是否正确 + +--- + +## 9. 附录 + +### 9.1 Python 版本要求 + +本项目要求 Python 3.9 或更高版本。 + +### 9.2 开发工具推荐 + +| 工具 | 用途 | 安装命令 | +|------|------|----------| +| Poetry | 依赖管理 | `pip install poetry` | +| PyCharm | IDE | [官网下载](https://www.jetbrains.com/pycharm/) | +| VS Code | 编辑器 | [官网下载](https://code.visualstudio.com/) | +| MySQL Workbench | 数据库管理 | [官网下载](https://www.mysql.com/products/workbench/) | + +### 9.3 变更记录 + +| 版本 | 日期 | 修改人 | 修改内容 | +|------|------|--------|----------| +| V1.0 | 2026-01-26 | - | 初始版本创建 | diff --git a/backend/docs/后端技术架构文档.md b/backend/docs/后端技术架构文档.md new file mode 100644 index 0000000..33c6f88 --- /dev/null +++ b/backend/docs/后端技术架构文档.md @@ -0,0 +1,479 @@ +# 后端技术架构文档 + +## 文档信息 +- **文档版本**: V1.0 +- **创建日期**: 2026-01-26 +- **文档类型**: 后端技术架构文档 + +--- + +## 1. 技术选型 + +| 分类 | 技术 | 版本 | 选型理由 | +|------|------|------|----------| +| 后端框架 | FastAPI | 0.104.0+ | 高性能Python Web框架,自动生成API文档,支持异步 | +| ORM框架 | SQLAlchemy | 2.0.0+ | Python最流行的ORM框架,支持多种数据库 | +| 数据库驱动 | PyMySQL | 1.1.0+ | 纯Python实现的MySQL客户端 | +| 数据库 | MySQL | 8.0+ | 关系型数据库,稳定可靠,适合企业级应用 | +| 认证 | JWT | 2.8.0+ | JSON Web Token,用于用户认证和授权 | +| 密码加密 | Passlib | 1.7.4+ | 密码哈希库,支持多种加密算法 | +| 数据验证 | Pydantic | 2.5.0+ | 数据验证和设置库,与FastAPI完美集成 | +| API文档 | Swagger | - | FastAPI自动生成,提供交互式API文档 | +| 构建工具 | Poetry | 1.7.0+ | Python依赖管理和打包工具 | +| 数据库迁移 | Alembic | 1.12.0+ | SQLAlchemy的数据库迁移工具 | +| 测试框架 | Pytest | 7.4.0+ | Python最流行的测试框架 | +| 异步支持 | asyncio | - | Python内置的异步IO库 | + +--- + +## 2. 架构设计 + +### 2.1 分层架构 + +``` +┌─────────────────────────────────────────────────────────┐ +│ 路由层 (API) │ +│ (Router) │ +├─────────────────────────────────────────────────────────┤ +│ 业务逻辑层 (Service) │ +│ (Service) │ +├─────────────────────────────────────────────────────────┤ +│ 数据访问层 (Model) │ +│ (ORM Model) │ +├─────────────────────────────────────────────────────────┤ +│ 数据层 │ +│ (MySQL) │ +└─────────────────────────────────────────────────────────┘ +``` + +### 2.2 核心模块 + +| 模块名称 | 主要职责 | 文件路径 | 依赖关系 | +|---------|---------|---------|----------| +| 认证模块 | 用户登录、退出、认证 | app/api/auth.py | 依赖用户模型、JWT | +| 用户模块 | 用户管理、权限管理 | app/api/user.py | 依赖用户模型、认证模块 | +| 项目模块 | 项目管理、创建、编辑 | app/api/project.py | 依赖项目模型、历史记录模块 | +| 历史记录模块 | 项目修改历史记录 | app/api/history.py | 依赖历史记录模型 | +| 统计模块 | 项目统计、数据分析 | app/api/statistics.py | 依赖项目模型 | +| 系统模块 | 系统配置、操作日志 | app/api/system.py | 依赖日志模型 | + +### 2.3 数据流 + +1. **请求处理流程**: + - 客户端发送请求到API路由 + - 路由层验证请求参数和用户权限 + - 调用服务层处理业务逻辑 + - 服务层调用数据访问层操作数据库 + - 数据访问层返回数据给服务层 + - 服务层处理数据并返回给路由层 + - 路由层返回响应给客户端 + +2. **项目创建流程**: + - 验证用户权限(必须是市场部或管理员) + - 生成项目编号 + - 创建项目记录 + - 创建项目成员记录 + - 创建项目里程碑记录 + - 创建项目风险记录 + - 创建项目历史记录 + +3. **项目编辑流程**: + - 验证用户权限 + - 获取项目当前信息 + - 对比修改前后的数据 + - 更新项目记录 + - 更新相关表数据 + - 创建项目历史记录 + +--- + +## 3. 目录结构 + +``` +├── app/ # 应用代码 +│ ├── api/ # API路由 +│ │ ├── auth.py # 认证API +│ │ ├── user.py # 用户API +│ │ ├── project.py # 项目API +│ │ ├── history.py # 历史记录API +│ │ ├── statistics.py # 统计API +│ │ ├── system.py # 系统API +│ │ └── __init__.py # 导出API +│ ├── models/ # 数据模型 +│ │ ├── __init__.py # 导出模型 +│ │ ├── user.py # 用户模型 +│ │ ├── project.py # 项目模型 +│ │ ├── member.py # 成员模型 +│ │ ├── milestone.py # 里程碑模型 +│ │ ├── risk.py # 风险模型 +│ │ ├── history.py # 历史记录模型 +│ │ └── log.py # 日志模型 +│ ├── schemas/ # Pydantic模式 +│ │ ├── __init__.py # 导出模式 +│ │ ├── auth.py # 认证模式 +│ │ ├── user.py # 用户模式 +│ │ ├── project.py # 项目模式 +│ │ ├── history.py # 历史记录模式 +│ │ ├── statistics.py # 统计模式 +│ │ ├── system.py # 系统模式 +│ │ └── common.py # 通用模式 +│ ├── services/ # 业务逻辑 +│ │ ├── __init__.py # 导出服务 +│ │ ├── auth_service.py # 认证服务 +│ │ ├── user_service.py # 用户服务 +│ │ ├── project_service.py # 项目服务 +│ │ ├── history_service.py # 历史记录服务 +│ │ ├── statistics_service.py # 统计服务 +│ │ └── system_service.py # 系统服务 +│ ├── database/ # 数据库配置 +│ │ ├── __init__.py # 导出配置 +│ │ ├── config.py # 数据库配置 +│ │ └── session.py # 数据库会话 +│ ├── common/ # 公共模块 +│ │ ├── __init__.py # 导出公共模块 +│ │ ├── constants.py # 常量定义 +│ │ ├── utils.py # 工具函数 +│ │ ├── dependencies.py # 依赖注入 +│ │ └── exceptions.py # 异常处理 +│ ├── config.py # 应用配置 +│ └── main.py # 应用入口 +├── tests/ # 测试代码 +│ ├── __init__.py # 测试初始化 +│ ├── test_auth.py # 认证测试 +│ ├── test_user.py # 用户测试 +│ ├── test_project.py # 项目测试 +│ ├── test_history.py # 历史记录测试 +│ ├── test_statistics.py # 统计测试 +│ └── conftest.py # 测试配置 +├── alembic/ # 数据库迁移 +│ ├── versions/ # 迁移版本 +│ ├── env.py # 迁移环境 +│ └── script.py.mako # 迁移脚本模板 +├── pyproject.toml # Poetry配置 +├── poetry.lock # Poetry锁文件 +├── requirements.txt # 依赖列表 +├── README.md # 项目说明 +├── .env.example # 环境变量示例 +└── .gitignore # Git忽略文件 +``` + +--- + +## 4. 核心类与函数 + +### 4.1 认证模块 + +| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 | +|-----------|------|-----------------|-----------------|-----------------|--------------| +| `AuthRouter.login()` | 用户登录 | LoginRequest 登录请求 | `AuthResponse` | `HTTPException` | app/api/auth.py | +| `AuthRouter.logout()` | 用户退出 | 无 | `BaseResponse` | `HTTPException` | app/api/auth.py | +| `AuthRouter.get_user_info()` | 获取用户信息 | 无 | `UserInfoResponse` | `HTTPException` | app/api/auth.py | +| `AuthService.authenticate()` | 认证用户 | str username, str password | `AuthResponse` | `AuthenticationException` | app/services/auth_service.py | +| `AuthService.create_token()` | 创建Token | str user_id | `str` Token | `Exception` | app/services/auth_service.py | +| `AuthService.verify_token()` | 验证Token | str token | `dict` Token载荷 | `JWTError` | app/services/auth_service.py | + +### 4.2 用户模块 + +| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 | +|-----------|------|-----------------|-----------------|-----------------|--------------| +| `UserRouter.create()` | 创建用户 | UserCreate 用户创建请求 | `BaseResponse` | `HTTPException` | app/api/user.py | +| `UserRouter.update()` | 更新用户 | str id, UserUpdate 用户更新请求 | `BaseResponse` | `HTTPException` | app/api/user.py | +| `UserRouter.delete()` | 删除用户 | str id | `BaseResponse` | `HTTPException` | app/api/user.py | +| `UserRouter.list()` | 获取用户列表 | UserQuery 查询参数 | `PageResponse[User]` | `HTTPException` | app/api/user.py | +| `UserRouter.reset_password()` | 重置密码 | str id, PasswordReset 密码重置请求 | `BaseResponse` | `HTTPException` | app/api/user.py | +| `UserService.create_user()` | 创建用户 | UserCreate 用户创建请求 | `User` | `ServiceException` | app/services/user_service.py | +| `UserService.update_user()` | 更新用户 | str id, UserUpdate 用户更新请求 | `User` | `ServiceException` | app/services/user_service.py | +| `UserService.delete_user()` | 删除用户 | str id | `bool` | `ServiceException` | app/services/user_service.py | +| `UserService.get_user_list()` | 获取用户列表 | UserQuery 查询参数 | `List[User]` | `ServiceException` | app/services/user_service.py | +| `UserService.reset_password()` | 重置密码 | str id, str password | `bool` | `ServiceException` | app/services/user_service.py | + +### 4.3 项目模块 + +| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 | +|-----------|------|-----------------|-----------------|-----------------|--------------| +| `ProjectRouter.create()` | 创建项目 | ProjectCreate 项目创建请求 | `BaseResponse` | `HTTPException` | app/api/project.py | +| `ProjectRouter.update()` | 更新项目 | str id, ProjectUpdate 项目更新请求 | `BaseResponse` | `HTTPException` | app/api/project.py | +| `ProjectRouter.delete()` | 删除项目 | str id | `BaseResponse` | `HTTPException` | app/api/project.py | +| `ProjectRouter.list()` | 获取项目列表 | ProjectQuery 查询参数 | `PageResponse[Project]` | `HTTPException` | app/api/project.py | +| `ProjectRouter.detail()` | 获取项目详情 | str id | `ProjectDetailResponse` | `HTTPException` | app/api/project.py | +| `ProjectService.create_project()` | 创建项目 | ProjectCreate 项目创建请求, str username | `Project` | `ServiceException` | app/services/project_service.py | +| `ProjectService.update_project()` | 更新项目 | str id, ProjectUpdate 项目更新请求, str username | `Project` | `ServiceException` | app/services/project_service.py | +| `ProjectService.delete_project()` | 删除项目 | str id | `bool` | `ServiceException` | app/services/project_service.py | +| `ProjectService.get_project_list()` | 获取项目列表 | ProjectQuery 查询参数 | `List[Project]` | `ServiceException` | app/services/project_service.py | +| `ProjectService.get_project_detail()` | 获取项目详情 | str id | `ProjectDetail` | `ServiceException` | app/services/project_service.py | + +### 4.4 历史记录模块 + +| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 | +|-----------|------|-----------------|-----------------|-----------------|--------------| +| `HistoryRouter.list()` | 获取项目历史 | str project_id, HistoryQuery 查询参数 | `PageResponse[ProjectHistory]` | `HTTPException` | app/api/history.py | +| `HistoryService.record_history()` | 记录历史 | str project_id, str operation_type, str operator, dict changes | `ProjectHistory` | `ServiceException` | app/services/history_service.py | +| `HistoryService.get_project_history()` | 获取项目历史 | str project_id, HistoryQuery 查询参数 | `List[ProjectHistory]` | `ServiceException` | app/services/history_service.py | + +### 4.5 统计模块 + +| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 | +|-----------|------|-----------------|-----------------|-----------------|--------------| +| `StatisticsRouter.filter()` | 筛选项目 | ProjectFilter 筛选条件 | `PageResponse[Project]` | `HTTPException` | app/api/statistics.py | +| `StatisticsRouter.statistics()` | 统计项目 | StatisticsRequest 统计请求 | `StatisticsResponse` | `HTTPException` | app/api/statistics.py | +| `StatisticsService.filter_projects()` | 筛选项目 | ProjectFilter 筛选条件 | `List[Project]` | `ServiceException` | app/services/statistics_service.py | +| `StatisticsService.get_project_statistics()` | 获取项目统计 | StatisticsRequest 统计请求 | `StatisticsResult` | `ServiceException` | app/services/statistics_service.py | + +--- + +## 5. 数据库设计 + +### 5.1 数据库表结构 + +详细的数据库表结构见《数据库设计文档》。 + +### 5.2 数据模型关系 + +``` +User (用户) + | + | 1 + | + | N + | +OperationLog (操作日志) + +Project (项目) + | + | 1 + | + | N + | + +-- ProjectMember (项目成员) + | + | 1 + | + | N + | + +-- ProjectMilestone (项目里程碑) + | + | 1 + | + | N + | + +-- ProjectRisk (项目风险) + | + | 1 + | + | N + | + +-- ProjectHistory (项目历史) +``` + +### 5.3 数据库连接配置 + +- **开发环境**:使用本地MySQL数据库 +- **测试环境**:使用测试MySQL数据库 +- **生产环境**:使用生产MySQL数据库 + +--- + +## 6. API设计 + +### 6.1 认证API + +| API路径 | 方法 | 模块/文件 | 功能描述 | 请求体 (JSON) | 成功响应 (200 OK) | +|---------|------|-----------|----------|--------------|------------------| +| `/api/auth/login` | `POST` | `app/api/auth.py` | 用户登录 | `{"username": "admin", "password": "123456"}` | `{"code": 200, "data": {"token": "...", "userInfo": {...}}, "message": "登录成功"}` | +| `/api/auth/logout` | `POST` | `app/api/auth.py` | 用户退出 | N/A | `{"code": 200, "message": "退出成功"}` | +| `/api/auth/userInfo` | `GET` | `app/api/auth.py` | 获取用户信息 | N/A | `{"code": 200, "data": {"userId": "...", "username": "...", "realName": "...", "department": "...", "role": "..."}}` | + +### 6.2 用户API + +| API路径 | 方法 | 模块/文件 | 功能描述 | 请求体 (JSON) | 成功响应 (200 OK) | +|---------|------|-----------|----------|--------------|------------------| +| `/api/user` | `GET` | `app/api/user.py` | 获取用户列表 | N/A (Query参数: page, size, username, department, role) | `{"code": 200, "data": [{...}], "total": 10, "message": "查询成功"}` | +| `/api/user` | `POST` | `app/api/user.py` | 创建用户 | `{"username": "test", "password": "123456", "realName": "测试用户", "department": "技术部", "role": "OTHER"}` | `{"code": 200, "message": "创建成功"}` | +| `/api/user/{id}` | `PUT` | `app/api/user.py` | 更新用户 | `{"realName": "测试用户1", "department": "市场部", "role": "MARKETING"}` | `{"code": 200, "message": "更新成功"}` | +| `/api/user/{id}` | `DELETE` | `app/api/user.py` | 删除用户 | N/A | `{"code": 200, "message": "删除成功"}` | +| `/api/user/resetPassword/{id}` | `PUT` | `app/api/user.py` | 重置密码 | `{"password": "123456"}` | `{"code": 200, "message": "密码重置成功"}` | + +### 6.3 项目API + +| API路径 | 方法 | 模块/文件 | 功能描述 | 请求体 (JSON) | 成功响应 (200 OK) | +|---------|------|-----------|----------|--------------|------------------| +| `/api/project` | `GET` | `app/api/project.py` | 获取项目列表 | N/A (Query参数: page, size, projectName, status, leader) | `{"code": 200, "data": [{...}], "total": 10, "message": "查询成功"}` | +| `/api/project` | `POST` | `app/api/project.py` | 创建项目 | `{"projectName": "测试项目", "status": "NOT_STARTED", "leader": "张三", ...}` | `{"code": 200, "data": {"projectId": "..."}, "message": "创建成功"}` | +| `/api/project/{id}` | `GET` | `app/api/project.py` | 获取项目详情 | N/A | `{"code": 200, "data": {...}, "message": "查询成功"}` | +| `/api/project/{id}` | `PUT` | `app/api/project.py` | 更新项目 | `{"projectName": "测试项目1", "status": "IN_PROGRESS", ...}` | `{"code": 200, "message": "更新成功"}` | +| `/api/project/{id}` | `DELETE` | `app/api/project.py` | 删除项目 | N/A | `{"code": 200, "message": "删除成功"}` | + +### 6.4 历史记录API + +| API路径 | 方法 | 模块/文件 | 功能描述 | 请求体 (JSON) | 成功响应 (200 OK) | +|---------|------|-----------|----------|--------------|------------------| +| `/api/history/{projectId}` | `GET` | `app/api/history.py` | 获取项目历史 | N/A (Query参数: page, size, operator, startDate, endDate) | `{"code": 200, "data": [{...}], "total": 10, "message": "查询成功"}` | + +### 6.5 统计API + +| API路径 | 方法 | 模块/文件 | 功能描述 | 请求体 (JSON) | 成功响应 (200 OK) | +|---------|------|-----------|----------|--------------|------------------| +| `/api/statistics/filter` | `POST` | `app/api/statistics.py` | 筛选项目 | `{"projectNo": "PRJ2025001", "projectName": "测试", "status": "IN_PROGRESS", ...}` | `{"code": 200, "data": [{...}], "total": 10, "message": "查询成功"}` | +| `/api/statistics/statistics` | `POST` | `app/api/statistics.py` | 统计项目 | `{"dimension": "status", "startDate": "2025-01-01", "endDate": "2025-12-31"}` | `{"code": 200, "data": {...}, "message": "统计成功"}` | + +--- + +## 7. 安全设计 + +### 7.1 认证与授权 + +- **认证方式**: JWT (JSON Web Token) 认证 +- **授权方式**: 基于角色的访问控制 (RBAC) +- **密码加密**: 使用 Passlib 的 bcrypt 算法加密存储密码 +- **会话管理**: JWT Token 存储在前端 localStorage 中,后端验证 Token 签名和有效期 + +### 7.2 接口安全 + +- **统一认证拦截**: 所有接口(除登录接口外)都需要验证 Token +- **权限验证**: 基于用户角色和权限进行接口访问控制 +- **请求参数验证**: 使用 Pydantic 验证请求参数的合法性 +- **防止SQL注入**: 使用 SQLAlchemy 的参数化查询,避免SQL注入 +- **防止XSS攻击**: 前端使用 v-html 时进行转义,后端对输入进行过滤 +- **防止CSRF攻击**: 使用 Token 验证,确保请求来自合法来源 + +### 7.3 数据安全 + +- **敏感数据加密**: 敏感数据(如密码)加密存储 +- **数据备份**: 定期备份数据库,防止数据丢失 +- **数据恢复**: 制定数据恢复计划,确保数据可恢复 +- **日志审计**: 记录用户操作日志,便于追溯操作行为 + +--- + +## 8. 部署与集成 + +### 8.1 开发环境部署 + +1. 克隆代码库: `git clone ` +2. 安装依赖: `poetry install` 或 `pip install -r requirements.txt` +3. 配置环境变量: 复制 `.env.example` 为 `.env` 并修改配置 +4. 初始化数据库: `alembic upgrade head` +5. 启动应用: `uvicorn app.main:app --reload` +6. 访问: `http://localhost:8000` +7. API文档: `http://localhost:8000/docs` + +### 8.2 生产环境部署 + +1. 克隆代码库: `git clone ` +2. 安装依赖: `poetry install --no-dev` 或 `pip install -r requirements.txt` +3. 配置环境变量: 复制 `.env.example` 为 `.env` 并修改配置 +4. 初始化数据库: `alembic upgrade head` +5. 构建应用: 无需构建,直接运行 +6. 启动应用: `gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker` +7. 配置 Nginx 反向代理 + +### 8.3 集成方案 + +- **前端与后端集成**: 通过 RESTful API 进行数据交互 +- **数据库集成**: 使用 SQLAlchemy ORM 操作数据库 +- **日志集成**: 使用 Python logging 模块记录系统日志 +- **监控集成**: 可集成 Prometheus + Grafana 监控系统性能 + +--- + +## 9. 性能优化 + +### 9.1 代码优化 + +- **异步处理**: 使用 FastAPI 的异步特性,提高并发处理能力 +- **批量操作**: 对批量数据操作使用批量处理,减少数据库交互次数 +- **缓存**: 对热点数据使用内存缓存,减少数据库查询 +- **分页查询**: 使用分页查询,避免一次性加载大量数据 + +### 9.2 数据库优化 + +- **索引优化**: 为常用查询字段创建索引,提高查询性能 +- **连接池**: 使用 SQLAlchemy 的连接池,提高数据库连接效率 +- **查询优化**: 使用 SQLAlchemy 的 lazy loading 和 eager loading 优化查询 +- **数据库配置**: 优化 MySQL 配置参数,提高数据库性能 + +### 9.3 部署优化 + +- **负载均衡**: 对多实例部署使用负载均衡 +- **水平扩展**: 根据业务需求进行水平扩展 +- **资源限制**: 合理设置应用的资源限制(CPU、内存等) + +--- + +## 10. 监控与维护 + +### 10.1 系统监控 + +- **应用监控**: 监控应用的运行状态、CPU、内存使用情况 +- **数据库监控**: 监控数据库的连接数、查询性能、存储空间 +- **API监控**: 监控 API 的响应时间、调用次数、错误率 +- **日志监控**: 监控系统日志,及时发现异常情况 + +### 10.2 故障处理 + +- **故障定位**: 通过日志和监控工具定位故障原因 +- **故障恢复**: 制定故障恢复方案,确保系统快速恢复 +- **故障预防**: 定期进行系统检查,预防故障发生 + +### 10.3 系统维护 + +- **定期更新**: 定期更新依赖库和框架版本,修复安全漏洞 +- **数据备份**: 定期备份数据库,防止数据丢失 +- **性能调优**: 定期分析系统性能,进行性能调优 +- **文档更新**: 及时更新系统文档,保持文档与系统同步 + +--- + +## 11. 开发规范 + +### 11.1 代码规范 + +- **代码风格**: 遵循 PEP 8 规范 +- **命名规范**: + - 类名: 大驼峰命名法 (PascalCase) + - 函数名: 小驼峰命名法 (camelCase) + - 变量名: 小写下划线分隔 (snake_case) + - 常量名: 全大写下划线分隔 (SNAKE_CASE) +- **注释规范**: 为所有公共函数和类添加文档字符串 +- **导入规范**: 分组导入,按标准库、第三方库、本地模块顺序 + +### 11.2 版本控制 + +- **分支管理**: 使用 Git Flow 分支管理策略 +- **提交规范**: 提交信息使用英文,格式为 `[类型]: 描述` +- **版本号**: 使用语义化版本号 (Semantic Versioning) + +### 11.3 测试规范 + +- **测试覆盖率**: 核心功能测试覆盖率不低于 80% +- **测试类型**: 单元测试、集成测试、端到端测试 +- **测试框架**: 使用 Pytest 进行测试 + +--- + +## 12. 附录 + +### 12.1 技术选型对比 + +| 技术 | 对比方案 | 最终选择理由 | +|------|----------|--------------| +| 后端框架 | FastAPI vs Flask vs Django | FastAPI 高性能,自动生成API文档,支持异步 | +| ORM框架 | SQLAlchemy vs Django ORM | SQLAlchemy 灵活强大,支持多种数据库 | +| 数据库 | MySQL vs PostgreSQL | MySQL 社区活跃,生态成熟,适合企业级应用 | +| 认证 | JWT vs Session | JWT 无状态,便于水平扩展 | +| 构建工具 | Poetry vs pip | Poetry 提供更好的依赖管理和版本控制 | + +### 12.2 常用命令 + +- **安装依赖**: `poetry install` 或 `pip install -r requirements.txt` +- **添加依赖**: `poetry add ` 或 `pip install ` +- **启动开发服务器**: `uvicorn app.main:app --reload` +- **运行测试**: `pytest` +- **生成数据库迁移**: `alembic revision --autogenerate -m "描述"` +- **执行数据库迁移**: `alembic upgrade head` + +### 12.3 变更记录 + +| 版本 | 日期 | 修改人 | 修改内容 | +|------|------|--------|----------| +| V1.0 | 2026-01-26 | - | 初始版本创建 | diff --git a/backend/docs/后端数据库迁移文档.md b/backend/docs/后端数据库迁移文档.md new file mode 100644 index 0000000..f0b2e18 --- /dev/null +++ b/backend/docs/后端数据库迁移文档.md @@ -0,0 +1,789 @@ +# 后端数据库迁移文档 + +## 文档信息 +- **文档版本**: V1.0 +- **创建日期**: 2026-01-26 +- **文档类型**: 后端数据库迁移文档 + +--- + +## 1. 数据库迁移概述 + +### 1.1 什么是数据库迁移 + +数据库迁移是一种管理数据库模式变更的方法,它允许您: + +- 版本化数据库模式 +- 跟踪数据库变更历史 +- 在不同环境之间一致地应用数据库变更 +- 回滚错误的数据库变更 +- 协作开发数据库模式 + +### 1.2 为什么使用数据库迁移 + +- **可追溯性**: 记录所有数据库变更的历史 +- **一致性**: 在开发、测试和生产环境中应用相同的变更 +- **安全性**: 提供回滚机制,防止错误变更导致的数据丢失 +- **协作**: 团队成员可以共享和审查数据库变更 +- **自动化**: 简化数据库部署流程 + +--- + +## 2. 迁移工具 + +### 2.1 Alembic + +本项目使用 **Alembic** 作为数据库迁移工具。Alembic 是 SQLAlchemy 的官方数据库迁移工具,提供了以下功能: + +- 自动生成迁移脚本 +- 管理迁移版本 +- 应用和回滚迁移 +- 支持多种数据库 +- 与 SQLAlchemy 无缝集成 + +### 2.2 安装 Alembic + +Alembic 已经在项目依赖中包含,使用以下命令安装: + +```bash +# 使用 Poetry +poetry install + +# 使用 pip +pip install -r requirements.txt +``` + +--- + +## 3. 迁移配置 + +### 3.1 初始化 Alembic + +如果项目还没有初始化 Alembic,使用以下命令: + +```bash +# 在项目根目录执行 +alembic init alembic +``` + +### 3.2 配置文件 + +Alembic 的主要配置文件是 `alembic.ini`,位于项目根目录: + +```ini +# alembic.ini + +# A generic, single database configuration. + +[alembic] +# path to migration scripts +script_location = alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# file_template = %%(year)d%%(month).2d%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. +prepend_sys_path = . + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the python-dateutil library +# timezone = + +# max length of characters to apply to the +# "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to alembic/versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "version_path_separator" below. +# version_locations = %(here)s/bar:%(here)s/bat:alembic/versions + +# version path separator; As mentioned above, this is the character used to split +# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. +# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. +# Valid values for version_path_separator are: +# +# version_path_separator = : +# version_path_separator = ; +# version_path_separator = space +version_path_separator = os # Use os.pathsep. +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +sqlalchemy.url = mysql+pymysql://username:password@localhost:3306/project_management + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S +``` + +### 3.3 环境配置 + +Alembic 的环境配置文件是 `alembic/env.py`,用于配置数据库连接和迁移行为: + +```python +# alembic/env.py + +from logging.config import fileConfig +from sqlalchemy import engine_from_config +from sqlalchemy import pool +from alembic import context +import os +import sys +from pathlib import Path + +# 将项目根目录添加到 Python 路径 +sys.path.append(str(Path(__file__).parent.parent)) + +# 导入模型和配置 +from app.config import settings +from app.database.base import Base +from app.models import * # 导入所有模型 + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# 使用环境变量中的数据库 URL +config.set_main_option('sqlalchemy.url', settings.database_url) + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() +``` + +### 3.4 脚本模板 + +Alembic 使用 `script.py.mako` 作为迁移脚本的模板,位于 `alembic` 目录: + +```mako +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade() -> None: + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + ${downgrades if downgrades else "pass"} +``` + +--- + +## 4. 迁移操作 + +### 4.1 生成迁移脚本 + +#### 自动生成迁移脚本 + +当您修改了模型定义后,使用以下命令自动生成迁移脚本: + +```bash +# 在项目根目录执行 +alembic revision --autogenerate -m "描述迁移内容" + +# 示例 +alembic revision --autogenerate -m "创建用户表" +``` + +#### 手动创建迁移脚本 + +如果需要手动创建迁移脚本,使用以下命令: + +```bash +# 在项目根目录执行 +alembic revision -m "描述迁移内容" + +# 示例 +alembic revision -m "添加用户状态字段" +``` + +然后编辑生成的迁移脚本,添加具体的迁移操作。 + +### 4.2 应用迁移 + +#### 应用所有迁移 + +使用以下命令将所有未应用的迁移应用到数据库: + +```bash +# 在项目根目录执行 +alembic upgrade head +``` + +#### 应用特定迁移 + +使用以下命令将迁移应用到特定版本: + +```bash +# 在项目根目录执行 +alembic upgrade + +# 示例 +alembic upgrade 1234abcd +``` + +### 4.3 回滚迁移 + +#### 回滚到上一个版本 + +使用以下命令回滚到上一个迁移版本: + +```bash +# 在项目根目录执行 +alembic downgrade -1 +``` + +#### 回滚到特定版本 + +使用以下命令回滚到特定迁移版本: + +```bash +# 在项目根目录执行 +alembic downgrade + +# 示例 +alembic downgrade 1234abcd +``` + +#### 回滚到初始状态 + +使用以下命令回滚到初始状态: + +```bash +# 在项目根目录执行 +alembic downgrade base +``` + +### 4.4 查看迁移状态 + +使用以下命令查看当前的迁移状态: + +```bash +# 在项目根目录执行 +alembic current +``` + +使用以下命令查看所有迁移版本: + +```bash +# 在项目根目录执行 +alembic history + +# 显示更详细的信息 +alembic history --verbose + +# 显示图形化的迁移树 +alembic history --graph +``` + +--- + +## 5. 迁移最佳实践 + +### 5.1 迁移脚本管理 + +1. **清晰的迁移消息**: 使用描述性的迁移消息,说明迁移的目的 +2. **版本控制**: 将迁移脚本纳入版本控制 +3. **测试迁移**: 在应用到生产环境之前,在测试环境中测试迁移 +4. **备份数据**: 在应用迁移之前,备份数据库 +5. **逐步迁移**: 对于大型迁移,考虑分步骤进行 + +### 5.2 迁移脚本编写 + +1. **保持迁移脚本简单**: 每个迁移脚本只包含一个逻辑变更 +2. **处理默认值**: 为新添加的列提供合理的默认值 +3. **处理数据迁移**: 如果需要迁移数据,在迁移脚本中添加相应的代码 +4. **处理约束**: 注意外键约束和唯一约束的处理 +5. **编写回滚脚本**: 确保每个迁移都有对应的回滚操作 + +### 5.3 常见迁移操作 + +#### 添加表 + +```python +def upgrade() -> None: + op.create_table( + 'users', + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('username', sa.String(length=50), nullable=False), + sa.Column('password', sa.String(length=128), nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('username') + ) + +def downgrade() -> None: + op.drop_table('users') +``` + +#### 添加列 + +```python +def upgrade() -> None: + op.add_column( + 'users', + sa.Column('email', sa.String(length=100), nullable=True) + ) + +def downgrade() -> None: + op.drop_column('users', 'email') +``` + +#### 修改列 + +```python +def upgrade() -> None: + op.alter_column( + 'users', + 'email', + existing_type=sa.String(length=100), + nullable=False + ) + +def downgrade() -> None: + op.alter_column( + 'users', + 'email', + existing_type=sa.String(length=100), + nullable=True + ) +``` + +#### 添加索引 + +```python +def upgrade() -> None: + op.create_index( + op.f('ix_users_email'), + 'users', + ['email'], + unique=True + ) + +def downgrade() -> None: + op.drop_index(op.f('ix_users_email'), table_name='users') +``` + +--- + +## 6. 数据库初始化 + +### 6.1 初始迁移 + +当创建新项目时,需要执行初始迁移: + +1. **创建模型**: 定义所有数据库模型 +2. **生成初始迁移**: `alembic revision --autogenerate -m "初始迁移"` +3. **应用初始迁移**: `alembic upgrade head` + +### 6.2 数据种子 + +在应用初始迁移后,可能需要添加一些初始数据,例如管理员用户: + +```python +# 在 app/database/seed.py 中 + +from sqlalchemy.orm import Session +from app.models.user import User +from app.database.session import get_db +from passlib.context import CryptContext + +pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") + +def seed_data(db: Session) -> None: + """添加初始数据""" + # 检查是否已有管理员用户 + admin_user = db.query(User).filter(User.username == "admin").first() + if not admin_user: + # 创建管理员用户 + admin_user = User( + username="admin", + password=pwd_context.hash("123456"), + real_name="系统管理员", + department="ADMIN", + role="ADMIN", + status=1 + ) + db.add(admin_user) + + # 检查是否已有市场部用户 + marketing_user = db.query(User).filter(User.username == "marketing").first() + if not marketing_user: + # 创建市场部用户 + marketing_user = User( + username="marketing", + password=pwd_context.hash("123456"), + real_name="市场部经理", + department="MARKETING", + role="MARKETING", + status=1 + ) + db.add(marketing_user) + + # 检查是否已有其他部门用户 + other_user = db.query(User).filter(User.username == "other").first() + if not other_user: + # 创建其他部门用户 + other_user = User( + username="other", + password=pwd_context.hash("123456"), + real_name="技术部经理", + department="TECHNOLOGY", + role="OTHER", + status=1 + ) + db.add(other_user) + + db.commit() + +if __name__ == "__main__": + db = next(get_db()) + try: + seed_data(db) + print("数据种子添加成功") + finally: + db.close() +``` + +执行数据种子脚本: + +```bash +# 在项目根目录执行 +python -m app.database.seed +``` + +--- + +## 7. 迁移示例 + +### 7.1 示例 1: 创建用户表 + +**迁移脚本**: `alembic/versions/1234abcd_create_user_table.py` + +```python +"""创建用户表 + +Revision ID: 1234abcd +Revises: +Create Date: 2025-01-01 00:00:00.000000 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '1234abcd' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.create_table('sys_user', + sa.Column('user_id', sa.String(length=32), nullable=False), + sa.Column('username', sa.String(length=50), nullable=False), + sa.Column('password', sa.String(length=128), nullable=False), + sa.Column('real_name', sa.String(length=50), nullable=False), + sa.Column('department', sa.String(length=50), nullable=False), + sa.Column('phone', sa.String(length=20), nullable=True), + sa.Column('email', sa.String(length=100), nullable=True), + sa.Column('role', sa.String(length=20), nullable=False), + sa.Column('status', sa.Integer(), nullable=False), + sa.Column('create_time', sa.DateTime(), nullable=False), + sa.Column('update_time', sa.DateTime(), nullable=False), + sa.Column('last_login_time', sa.DateTime(), nullable=True), + sa.PrimaryKeyConstraint('user_id'), + sa.UniqueConstraint('username') + ) + op.create_index(op.f('ix_sys_user_department'), 'sys_user', ['department'], unique=False) + op.create_index(op.f('ix_sys_user_role'), 'sys_user', ['role'], unique=False) + op.create_index(op.f('ix_sys_user_status'), 'sys_user', ['status'], unique=False) + + +def downgrade() -> None: + op.drop_index(op.f('ix_sys_user_status'), table_name='sys_user') + op.drop_index(op.f('ix_sys_user_role'), table_name='sys_user') + op.drop_index(op.f('ix_sys_user_department'), table_name='sys_user') + op.drop_table('sys_user') +``` + +### 7.2 示例 2: 创建项目表 + +**迁移脚本**: `alembic/versions/5678efgh_create_project_table.py` + +```python +"""创建项目表 + +Revision ID: 5678efgh +Revises: 1234abcd +Create Date: 2025-01-02 00:00:00.000000 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '5678efgh' +down_revision = '1234abcd' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.create_table('project', + sa.Column('project_id', sa.String(length=32), nullable=False), + sa.Column('project_no', sa.String(length=20), nullable=False), + sa.Column('project_name', sa.String(length=200), nullable=False), + sa.Column('status', sa.String(length=20), nullable=False), + sa.Column('create_time', sa.DateTime(), nullable=False), + sa.Column('creator', sa.String(length=50), nullable=False), + sa.Column('update_time', sa.DateTime(), nullable=False), + sa.Column('last_modifier', sa.String(length=50), nullable=True), + sa.Column('leader', sa.String(length=50), nullable=False), + sa.Column('phone', sa.String(length=20), nullable=True), + sa.Column('email', sa.String(length=100), nullable=True), + sa.Column('background', sa.Text(), nullable=True), + sa.Column('goal', sa.Text(), nullable=True), + sa.Column('scope', sa.Text(), nullable=True), + sa.Column('start_date', sa.Date(), nullable=False), + sa.Column('planned_end_date', sa.Date(), nullable=False), + sa.Column('actual_end_date', sa.Date(), nullable=True), + sa.Column('total_budget', sa.Numeric(precision=15, scale=2), nullable=False), + sa.Column('used_budget', sa.Numeric(precision=15, scale=2), nullable=False), + sa.Column('remaining_budget', sa.Numeric(precision=15, scale=2), nullable=False), + sa.Column('remarks', sa.Text(), nullable=True), + sa.PrimaryKeyConstraint('project_id'), + sa.UniqueConstraint('project_no') + ) + op.create_index(op.f('ix_project_create_time'), 'project', ['create_time'], unique=False) + op.create_index(op.f('ix_project_creator'), 'project', ['creator'], unique=False) + op.create_index(op.f('ix_project_leader'), 'project', ['leader'], unique=False) + op.create_index(op.f('ix_project_status'), 'project', ['status'], unique=False) + op.create_index(op.f('ix_project_update_time'), 'project', ['update_time'], unique=False) + + +def downgrade() -> None: + op.drop_index(op.f('ix_project_update_time'), table_name='project') + op.drop_index(op.f('ix_project_status'), table_name='project') + op.drop_index(op.f('ix_project_leader'), table_name='project') + op.drop_index(op.f('ix_project_creator'), table_name='project') + op.drop_index(op.f('ix_project_create_time'), table_name='project') + op.drop_table('project') +``` + +--- + +## 8. 常见问题 + +### 8.1 迁移失败 + +**问题**:迁移应用失败,出现错误 + +**解决方法**: +1. 查看错误信息,了解失败原因 +2. 检查迁移脚本是否正确 +3. 检查数据库连接是否正常 +4. 如果需要,回滚到之前的版本并修复问题 + +### 8.2 自动生成的迁移脚本不正确 + +**问题**:Alembic 自动生成的迁移脚本与预期不符 + +**解决方法**: +1. 检查模型定义是否正确 +2. 手动编辑生成的迁移脚本 +3. 确保所有模型都已导入到 `env.py` 中 + +### 8.3 数据库连接错误 + +**问题**:无法连接到数据库进行迁移 + +**解决方法**: +1. 检查数据库服务是否运行 +2. 检查数据库连接字符串是否正确 +3. 检查数据库用户权限是否正确 + +### 8.4 迁移版本冲突 + +**问题**:多个开发者创建了相同版本号的迁移 + +**解决方法**: +1. 使用唯一的迁移版本号 +2. 在提交迁移脚本前检查版本冲突 +3. 如果发生冲突,重新生成迁移脚本 + +--- + +## 9. 附录 + +### 9.1 常用命令汇总 + +| 命令 | 说明 | 示例 | +|------|------|------| +| `alembic init` | 初始化 Alembic | `alembic init alembic` | +| `alembic revision --autogenerate` | 自动生成迁移脚本 | `alembic revision --autogenerate -m "创建用户表"` | +| `alembic revision` | 手动创建迁移脚本 | `alembic revision -m "添加用户状态字段"` | +| `alembic upgrade head` | 应用所有迁移 | `alembic upgrade head` | +| `alembic upgrade ` | 应用到特定版本 | `alembic upgrade 1234abcd` | +| `alembic downgrade -1` | 回滚到上一个版本 | `alembic downgrade -1` | +| `alembic downgrade ` | 回滚到特定版本 | `alembic downgrade 1234abcd` | +| `alembic downgrade base` | 回滚到初始状态 | `alembic downgrade base` | +| `alembic current` | 查看当前版本 | `alembic current` | +| `alembic history` | 查看所有版本 | `alembic history` | +| `alembic history --verbose` | 查看详细版本信息 | `alembic history --verbose` | +| `alembic history --graph` | 查看迁移树 | `alembic history --graph` | + +### 9.2 迁移最佳实践总结 + +1. **版本控制**: 将迁移脚本纳入版本控制 +2. **测试**: 在应用到生产环境之前测试迁移 +3. **备份**: 在应用迁移之前备份数据库 +4. **简单**: 每个迁移只包含一个逻辑变更 +5. **清晰**: 使用描述性的迁移消息 +6. **完整**: 编写正确的升级和降级脚本 +7. **协作**: 与团队成员协调迁移工作 +8. **监控**: 监控迁移执行情况 + +### 9.3 变更记录 + +| 版本 | 日期 | 修改人 | 修改内容 | +|------|------|--------|----------| +| V1.0 | 2026-01-26 | - | 初始版本创建 | diff --git a/docs/example.xls b/docs/example.xls new file mode 100644 index 0000000..eae71b0 Binary files /dev/null and b/docs/example.xls differ diff --git a/docs/example.xsl b/docs/example.xsl new file mode 100644 index 0000000..ad95217 --- /dev/null +++ b/docs/example.xsl @@ -0,0 +1,74 @@ + + + <基本信息> + <项目编号>PRJ2025001 + <项目名称>企业数字化转型系统 + <项目状态>进行中 + <创建日期>2025-01-15 + <负责人>张三 + <联系电话>13800138000 + <邮箱>zhangsan@example.com + + <项目描述> + <项目背景>为了提升企业运营效率,实现业务流程数字化管理 + <项目目标>完成企业内部管理系统的全面数字化改造 + <项目范围>包括人力资源、财务管理、供应链管理等模块 + + <项目团队> + <成员> + <姓名>张三 + <角色>项目经理 + <部门>市场部 + + <成员> + <姓名>李四 + <角色>技术负责人 + <部门>技术部 + + <成员> + <姓名>王五 + <角色>设计师 + <部门>设计部 + + + <项目时间> + <开始日期>2025-01-15 + <预计结束日期>2025-06-30 + <实际结束日期> + + <项目预算> + <总预算>500000 + <已使用>120000 + <剩余>380000 + + <项目里程碑> + <里程碑> + <名称>需求分析完成 + <计划日期>2025-02-15 + <实际日期>2025-02-10 + <状态>已完成 + + <里程碑> + <名称>系统设计完成 + <计划日期>2025-03-15 + <实际日期> + <状态>进行中 + + <里程碑> + <名称>开发完成 + <计划日期>2025-05-30 + <实际日期> + <状态>未开始 + + + <项目风险> + <风险> + <描述>技术难度较高,可能影响进度 + <等级>高 + <应对措施>增加技术团队人员投入 + + + <项目备注> + 项目需要与现有系统进行数据对接,需要提前做好接口设计 + + diff --git a/docs/产品需求文档.md b/docs/产品需求文档.md new file mode 100644 index 0000000..5383b3e --- /dev/null +++ b/docs/产品需求文档.md @@ -0,0 +1,482 @@ +# 项目管理系统产品需求文档 + +## 文档信息 +- **文档版本**: V1.0 +- **创建日期**: 2026-01-26 +- **文档类型**: 产品需求文档 (PRD) + +--- + +## 1. 产品概述 + +### 1.1 产品简介 +项目管理系统是一个基于BS架构的企业级项目管理平台,旨在帮助企业实现项目全生命周期的数字化管理,包括项目创建、编辑、跟踪、统计等功能。 + +### 1.2 产品目标 +- 规范项目管理流程,提高项目执行效率 +- 实现项目信息的集中管理和共享 +- 提供完善的项目修改历史记录,便于追溯 +- 支持多维度项目筛选和统计分析 + +### 1.3 目标用户 +- 管理员:系统最高权限用户,负责用户管理和系统维护 +- 市场部人员:负责项目创建和项目信息维护 +- 其他部门人员:负责项目内容的修改和更新 + +--- + +## 2. 用户角色与权限 + +### 2.1 角色定义 + +#### 2.1.1 管理员 +**权限范围:** +- 用户管理:创建、编辑、删除用户 +- 权限管理:分配用户角色和权限 +- 密码管理:重置用户密码 +- 项目管理:查看、编辑所有项目 +- 统计分析:查看所有项目统计报表 +- 系统配置:管理系统基础配置 + +#### 2.1.2 市场部人员 +**权限范围:** +- 项目创建:创建新项目 +- 项目编辑:编辑项目信息 +- 项目查看:查看所有项目 +- 历史记录:查看项目修改历史 + +#### 2.1.3 其他部门人员 +**权限范围:** +- 项目编辑:编辑项目内容 +- 项目查看:查看所有项目 +- 历史记录:查看项目修改历史 + +### 2.2 权限矩阵 + +| 功能模块 | 管理员 | 市场部 | 其他部门 | +|---------|--------|--------|----------| +| 用户管理 | ✓ | ✗ | ✗ | +| 权限管理 | ✓ | ✗ | ✗ | +| 密码管理 | ✓ | ✗ | ✗ | +| 项目创建 | ✓ | ✓ | ✗ | +| 项目编辑 | ✓ | ✓ | ✓ | +| 项目查看 | ✓ | ✓ | ✓ | +| 历史记录查看 | ✓ | ✓ | ✓ | +| 项目统计 | ✓ | ✗ | ✗ | +| 系统配置 | ✓ | ✗ | ✗ | + +--- + +## 3. 功能需求 + +### 3.1 用户管理模块 + +#### 3.1.1 用户创建 +**功能描述:** 管理员可以创建新用户账号 + +**输入信息:** +- 用户名(必填,唯一) +- 密码(必填,需满足安全策略) +- 真实姓名(必填) +- 部门(必填,可选:市场部、技术部、设计部、财务部等) +- 联系电话(选填) +- 邮箱(选填,需格式验证) +- 角色(必填:管理员、市场部、其他部门) + +**业务规则:** +- 用户名必须唯一,不能重复 +- 密码长度不少于8位,包含字母和数字 +- 邮箱格式必须正确 +- 创建成功后自动分配初始权限 + +#### 3.1.2 用户编辑 +**功能描述:** 管理员可以编辑用户基本信息 + +**可编辑字段:** +- 真实姓名 +- 部门 +- 联系电话 +- 邮箱 +- 角色 + +**业务规则:** +- 用户名不可修改 +- 修改角色会自动更新权限 +- 修改部门会影响项目创建权限 + +#### 3.1.3 密码管理 +**功能描述:** 管理员可以重置用户密码 + +**操作方式:** +- 选择用户 +- 生成新随机密码或手动设置密码 +- 通知用户新密码 + +**业务规则:** +- 新密码需满足安全策略 +- 密码重置后建议用户首次登录修改密码 + +#### 3.1.4 用户列表 +**功能描述:** 展示所有用户信息 + +**显示字段:** +- 用户名 +- 真实姓名 +- 部门 +- 角色 +- 创建时间 +- 最后登录时间 +- 状态(正常/禁用) + +**支持操作:** +- 编辑用户 +- 重置密码 +- 禁用/启用用户 +- 删除用户 + +### 3.2 项目管理模块 + +#### 3.2.1 项目创建 +**功能描述:** 市场部人员可以创建新项目 + +**创建条件:** +- 用户角色为市场部或管理员 + +**项目信息结构:** + +**基本信息** +- 项目编号(自动生成) +- 项目名称 +- 项目状态(未开始、进行中、已完成、已暂停、已取消) +- 创建日期(自动生成) +- 负责人 +- 联系电话 +- 邮箱 + +**项目描述** +- 项目背景 +- 项目目标 +- 项目范围 + +**项目团队** +- 成员列表(姓名、角色、部门) + +**项目时间** +- 开始日期 +- 预计结束日期 +- 实际结束日期 + +**项目预算** +- 总预算 +- 已使用 +- 剩余 + +**项目里程碑** +- 里程碑列表(名称、计划日期、实际日期、状态) + +**项目风险** +- 风险列表(描述、等级、应对措施) + +**项目备注** +- 备注内容 + +**业务规则:** +- 项目编号自动生成,格式:PRJ + 年份 + 序号(如:PRJ2025001) +- 项目状态默认为"未开始" +- 创建者自动记录为创建人 +- 创建时间自动记录 + +#### 3.2.2 项目编辑 +**功能描述:** 所有用户可以编辑项目信息 + +**编辑权限:** +- 管理员:可编辑所有项目 +- 市场部:可编辑所有项目 +- 其他部门:可编辑所有项目 + +**编辑内容:** +- 可编辑项目的所有字段 +- 编辑后自动记录修改历史 + +**业务规则:** +- 项目编号不可修改 +- 创建日期不可修改 +- 创建人不可修改 +- 每次修改都记录修改历史 + +#### 3.2.3 项目查看 +**功能描述:** 所有用户可以查看项目详情 + +**查看内容:** +- 项目完整信息 +- 项目修改历史记录 + +**业务规则:** +- 所有用户都可以查看所有项目 +- 项目修改历史按时间倒序显示 + +#### 3.2.4 项目列表 +**功能描述:** 展示所有项目列表 + +**显示字段:** +- 项目编号 +- 项目名称 +- 项目状态 +- 负责人 +- 创建日期 +- 最后修改时间 + +**支持操作:** +- 查看项目详情 +- 编辑项目 +- 查看修改历史 + +### 3.3 项目历史记录模块 + +#### 3.3.1 历史记录记录 +**功能描述:** 自动记录项目修改历史 + +**记录内容:** +- 修改时间 +- 修改人 +- 修改字段 +- 修改前值 +- 修改后值 +- 修改类型(创建、更新、删除) + +**记录时机:** +- 项目创建时 +- 项目信息修改时 +- 项目状态变更时 + +**业务规则:** +- 历史记录不可删除 +- 历史记录不可修改 +- 历史记录永久保存 + +#### 3.3.2 历史记录查看 +**功能描述:** 查看项目修改历史 + +**显示内容:** +- 修改时间 +- 修改人 +- 修改详情 +- 修改前后的值对比 + +**显示方式:** +- 按时间倒序排列 +- 支持按修改人筛选 +- 支持按时间范围筛选 + +### 3.4 项目统计模块 + +#### 3.4.1 项目筛选 +**功能描述:** 管理员可以通过多种条件筛选项目 + +**筛选条件:** +- 项目编号 +- 项目名称(模糊查询) +- 项目状态 +- 负责人 +- 创建日期范围 +- 修改日期范围 +- 部门 +- 项目状态 + +**业务规则:** +- 支持多条件组合筛选 +- 筛选结果可导出 +- 筛选条件可保存 + +#### 3.4.2 项目统计 +**功能描述:** 管理员可以查看项目统计数据 + +**统计维度:** +- 按项目状态统计 +- 按部门统计 +- 按负责人统计 +- 按创建时间统计 +- 按项目预算统计 + +**统计指标:** +- 项目总数 +- 各状态项目数量 +- 项目完成率 +- 项目平均周期 +- 预算使用率 + +**展示方式:** +- 数据表格 +- 柱状图 +- 饼图 +- 折线图 + +**业务规则:** +- 统计数据实时更新 +- 支持时间范围选择 +- 支持统计结果导出 + +--- + +## 4. 项目数据结构 + +### 4.1 用户数据结构 +```json +{ + "userId": "用户ID", + "username": "用户名", + "password": "密码(加密)", + "realName": "真实姓名", + "department": "部门", + "phone": "联系电话", + "email": "邮箱", + "role": "角色", + "status": "状态", + "createTime": "创建时间", + "lastLoginTime": "最后登录时间" +} +``` + +### 4.2 项目数据结构 +```json +{ + "projectId": "项目ID", + "projectNo": "项目编号", + "projectName": "项目名称", + "status": "项目状态", + "createTime": "创建时间", + "creator": "创建人", + "basicInfo": { + "leader": "负责人", + "phone": "联系电话", + "email": "邮箱" + }, + "description": { + "background": "项目背景", + "goal": "项目目标", + "scope": "项目范围" + }, + "team": [ + { + "name": "姓名", + "role": "角色", + "department": "部门" + } + ], + "timeInfo": { + "startDate": "开始日期", + "plannedEndDate": "预计结束日期", + "actualEndDate": "实际结束日期" + }, + "budget": { + "total": "总预算", + "used": "已使用", + "remaining": "剩余" + }, + "milestones": [ + { + "name": "名称", + "plannedDate": "计划日期", + "actualDate": "实际日期", + "status": "状态" + } + ], + "risks": [ + { + "description": "描述", + "level": "等级", + "measure": "应对措施" + } + ], + "remarks": "备注", + "updateTime": "最后修改时间", + "lastModifier": "最后修改人" +} +``` + +### 4.3 项目历史记录数据结构 +```json +{ + "historyId": "历史记录ID", + "projectId": "项目ID", + "projectNo": "项目编号", + "operationType": "操作类型", + "operator": "操作人", + "operationTime": "操作时间", + "changes": [ + { + "field": "字段名", + "oldValue": "修改前值", + "newValue": "修改后值" + } + ] +} +``` + +--- + +## 5. 系统架构 + +### 5.1 技术架构 +- **架构模式**: BS架构(Browser/Server) +- **前端技术**: 待定 +- **后端技术**: 待定 +- **数据库**: 待定 + +### 5.2 部署架构 +- **前端**: Web浏览器访问 +- **后端**: 应用服务器部署 +- **数据库**: 数据库服务器部署 + +### 5.3 安全架构 +- 用户认证:用户名密码登录 +- 权限控制:基于角色的访问控制(RBAC) +- 数据加密:敏感数据加密存储 +- 操作审计:记录用户操作日志 + +--- + +## 6. 非功能性需求 + +### 6.1 性能要求 +- 系统响应时间:页面加载时间 < 3秒 +- 并发用户数:支持至少100个并发用户 +- 数据查询:复杂查询响应时间 < 5秒 + +### 6.2 可用性要求 +- 系统可用性:99.5% +- 系统恢复时间:故障恢复时间 < 1小时 + +### 6.3 安全性要求 +- 密码加密:使用安全的加密算法 +- 会话管理:会话超时时间30分钟 +- 数据备份:每日自动备份数据 +- 权限控制:严格的权限验证机制 + +### 6.4 可扩展性要求 +- 支持模块化扩展 +- 支持新增用户角色 +- 支持自定义项目字段 + +### 6.5 易用性要求 +- 界面简洁直观 +- 操作流程清晰 +- 提供操作提示 +- 支持快捷键操作 + +--- + +## 7. 附录 + +### 7.1 术语表 +- **BS架构**: Browser/Server架构,浏览器/服务器架构 +- **RBAC**: Role-Based Access Control,基于角色的访问控制 +- **PRD**: Product Requirements Document,产品需求文档 + +### 7.2 参考资料 +- 项目示例文件:docs/example.xsl + +### 7.3 变更记录 +| 版本 | 日期 | 修改人 | 修改内容 | +|------|------|--------|----------| +| V1.0 | 2026-01-26 | - | 初始版本创建 | diff --git a/docs/功能规格说明书.md b/docs/功能规格说明书.md new file mode 100644 index 0000000..51a86ed --- /dev/null +++ b/docs/功能规格说明书.md @@ -0,0 +1,533 @@ +# 项目管理系统功能规格说明书 + +## 文档信息 +- **文档版本**: V1.0 +- **创建日期**: 2026-01-26 +- **文档类型**: 功能规格说明书 (FSD) + +--- + +## 1. 引言 + +### 1.1 文档目的 +本文档详细描述项目管理系统的各项功能规格,为系统设计和开发提供详细的功能说明。 + +### 1.2 适用范围 +本文档适用于项目管理系统开发团队,包括前端开发、后端开发、测试人员等。 + +### 1.3 参考文档 +- 《项目管理系统产品需求文档》 + +--- + +## 2. 用户管理功能规格 + +### 2.1 用户登录功能 + +#### 2.1.1 功能描述 +用户通过用户名和密码登录系统。 + +#### 2.1.2 输入参数 +- 用户名:字符串,长度3-20字符 +- 密码:字符串,长度8-20字符 + +#### 2.1.3 处理逻辑 +1. 验证用户名和密码是否为空 +2. 验证用户名是否存在 +3. 验证密码是否正确 +4. 验证用户状态是否正常 +5. 登录成功后创建会话 +6. 记录登录日志 + +#### 2.1.4 输出结果 +- 登录成功:跳转到系统首页 +- 登录失败:显示错误提示信息 + +#### 2.1.5 异常处理 +- 用户名不存在:提示"用户名或密码错误" +- 密码错误:提示"用户名或密码错误" +- 用户被禁用:提示"账号已被禁用,请联系管理员" +- 系统异常:提示"系统异常,请稍后重试" + +### 2.2 用户创建功能 + +#### 2.2.1 功能描述 +管理员创建新用户账号。 + +#### 2.2.2 输入参数 +- 用户名:字符串,长度3-20字符,必填 +- 密码:字符串,长度8-20字符,必填 +- 确认密码:字符串,必填 +- 真实姓名:字符串,长度2-20字符,必填 +- 部门:枚举值,必填 +- 联系电话:字符串,选填 +- 邮箱:字符串,选填 +- 角色:枚举值,必填 + +#### 2.2.3 处理逻辑 +1. 验证输入参数的完整性和格式 +2. 验证用户名是否已存在 +3. 验证密码和确认密码是否一致 +4. 验证邮箱格式是否正确 +5. 对密码进行加密处理 +6. 生成用户ID +7. 保存用户信息到数据库 +8. 记录操作日志 + +#### 2.2.4 输出结果 +- 创建成功:提示"用户创建成功",清空表单 +- 创建失败:显示错误提示信息 + +#### 2.2.5 异常处理 +- 用户名已存在:提示"用户名已存在" +- 密码不一致:提示"两次输入的密码不一致" +- 邮箱格式错误:提示"邮箱格式不正确" +- 数据库异常:提示"系统异常,请稍后重试" + +### 2.3 用户编辑功能 + +#### 2.3.1 功能描述 +管理员编辑用户基本信息。 + +#### 2.3.2 输入参数 +- 用户ID:字符串,必填 +- 真实姓名:字符串,必填 +- 部门:枚举值,必填 +- 联系电话:字符串,选填 +- 邮箱:字符串,选填 +- 角色:枚举值,必填 + +#### 2.3.3 处理逻辑 +1. 验证输入参数的完整性和格式 +2. 验证用户ID是否存在 +3. 验证邮箱格式是否正确 +4. 更新用户信息到数据库 +5. 如果角色发生变化,更新用户权限 +6. 记录操作日志 + +#### 2.3.4 输出结果 +- 编辑成功:提示"用户信息更新成功" +- 编辑失败:显示错误提示信息 + +#### 2.3.5 异常处理 +- 用户不存在:提示"用户不存在" +- 邮箱格式错误:提示"邮箱格式不正确" +- 数据库异常:提示"系统异常,请稍后重试" + +### 2.4 密码重置功能 + +#### 2.4.1 功能描述 +管理员重置用户密码。 + +#### 2.4.2 输入参数 +- 用户ID:字符串,必填 +- 新密码:字符串,长度8-20字符,必填 +- 确认密码:字符串,必填 + +#### 2.4.3 处理逻辑 +1. 验证输入参数的完整性 +2. 验证用户ID是否存在 +3. 验证新密码和确认密码是否一致 +4. 对新密码进行加密处理 +5. 更新用户密码到数据库 +6. 记录操作日志 + +#### 2.4.4 输出结果 +- 重置成功:提示"密码重置成功" +- 重置失败:显示错误提示信息 + +#### 2.4.5 异常处理 +- 用户不存在:提示"用户不存在" +- 密码不一致:提示"两次输入的密码不一致" +- 数据库异常:提示"系统异常,请稍后重试" + +### 2.5 用户列表功能 + +#### 2.5.1 功能描述 +展示所有用户列表。 + +#### 2.5.2 输入参数 +- 页码:整数,默认1 +- 每页数量:整数,默认10 +- 搜索关键词:字符串,选填 + +#### 2.5.3 处理逻辑 +1. 验证分页参数 +2. 根据搜索关键词查询用户列表 +3. 计算总记录数和总页数 +4. 返回用户列表数据 + +#### 2.5.4 输出结果 +- 用户列表数据 +- 分页信息 + +#### 2.5.5 异常处理 +- 参数错误:提示"参数错误" +- 数据库异常:提示"系统异常,请稍后重试" + +--- + +## 3. 项目管理功能规格 + +### 3.1 项目创建功能 + +#### 3.1.1 功能描述 +市场部人员创建新项目。 + +#### 3.1.2 输入参数 + +**基本信息** +- 项目名称:字符串,长度2-100字符,必填 +- 项目状态:枚举值,默认"未开始" +- 负责人:字符串,必填 +- 联系电话:字符串,选填 +- 邮箱:字符串,选填 + +**项目描述** +- 项目背景:字符串,选填 +- 项目目标:字符串,选填 +- 项目范围:字符串,选填 + +**项目团队** +- 成员列表:数组,每个成员包含姓名、角色、部门 + +**项目时间** +- 开始日期:日期,必填 +- 预计结束日期:日期,必填 +- 实际结束日期:日期,选填 + +**项目预算** +- 总预算:数字,必填 +- 已使用:数字,默认0 +- 剩余:数字,自动计算 + +**项目里程碑** +- 里程碑列表:数组,每个里程碑包含名称、计划日期、实际日期、状态 + +**项目风险** +- 风险列表:数组,每个风险包含描述、等级、应对措施 + +**项目备注** +- 备注:字符串,选填 + +#### 3.1.3 处理逻辑 +1. 验证用户是否有创建项目权限 +2. 验证必填参数是否完整 +3. 验证日期格式是否正确 +4. 验证预算数据是否合法 +5. 生成项目编号 +6. 生成项目ID +7. 计算剩余预算 +8. 保存项目信息到数据库 +9. 创建项目创建历史记录 +10. 记录操作日志 + +#### 3.1.4 输出结果 +- 创建成功:提示"项目创建成功",跳转到项目列表 +- 创建失败:显示错误提示信息 + +#### 3.1.5 异常处理 +- 无权限:提示"您没有创建项目的权限" +- 参数错误:提示"请填写必填项" +- 日期错误:提示"日期格式不正确" +- 数据库异常:提示"系统异常,请稍后重试" + +### 3.2 项目编辑功能 + +#### 3.2.1 功能描述 +用户编辑项目信息。 + +#### 3.2.2 输入参数 +- 项目ID:字符串,必填 +- 项目信息:项目完整信息(同创建参数) + +#### 3.2.3 处理逻辑 +1. 验证用户是否有编辑项目权限 +2. 验证项目ID是否存在 +3. 获取项目当前信息 +4. 对比修改前后的数据 +5. 验证必填参数是否完整 +6. 验证日期格式是否正确 +7. 验证预算数据是否合法 +8. 更新项目信息到数据库 +9. 记录修改历史(只记录有变化的字段) +10. 更新最后修改时间和修改人 +11. 记录操作日志 + +#### 3.2.4 输出结果 +- 编辑成功:提示"项目信息更新成功" +- 编辑失败:显示错误提示信息 + +#### 3.2.5 异常处理 +- 项目不存在:提示"项目不存在" +- 参数错误:提示"请填写必填项" +- 日期错误:提示"日期格式不正确" +- 数据库异常:提示"系统异常,请稍后重试" + +### 3.3 项目查看功能 + +#### 3.3.1 功能描述 +用户查看项目详情。 + +#### 3.3.2 输入参数 +- 项目ID:字符串,必填 + +#### 3.3.3 处理逻辑 +1. 验证项目ID是否存在 +2. 查询项目详细信息 +3. 查询项目修改历史记录 +4. 返回项目详情和历史记录 + +#### 3.3.4 输出结果 +- 项目详细信息 +- 项目修改历史记录 + +#### 3.3.5 异常处理 +- 项目不存在:提示"项目不存在" +- 数据库异常:提示"系统异常,请稍后重试" + +### 3.4 项目列表功能 + +#### 3.4.1 功能描述 +展示所有项目列表。 + +#### 3.4.2 输入参数 +- 页码:整数,默认1 +- 每页数量:整数,默认10 +- 搜索关键词:字符串,选填 +- 项目状态:枚举值,选填 +- 负责人:字符串,选填 + +#### 3.4.3 处理逻辑 +1. 验证分页参数 +2. 根据筛选条件查询项目列表 +3. 计算总记录数和总页数 +4. 返回项目列表数据 + +#### 3.4.4 输出结果 +- 项目列表数据 +- 分页信息 + +#### 3.4.5 异常处理 +- 参数错误:提示"参数错误" +- 数据库异常:提示"系统异常,请稍后重试" + +--- + +## 4. 项目历史记录功能规格 + +### 4.1 历史记录记录功能 + +#### 4.1.1 功能描述 +自动记录项目修改历史。 + +#### 4.1.2 输入参数 +- 项目ID:字符串,必填 +- 操作类型:枚举值,必填 +- 操作人:字符串,必填 +- 修改数据:对象,必填 + +#### 4.1.3 处理逻辑 +1. 生成历史记录ID +2. 记录操作时间 +3. 对比修改前后的数据 +4. 提取变化的字段 +5. 保存历史记录到数据库 + +#### 4.1.4 输出结果 +- 记录成功:无返回 +- 记录失败:记录错误日志 + +#### 4.1.5 异常处理 +- 数据库异常:记录错误日志,不影响主流程 + +### 4.2 历史记录查看功能 + +#### 4.2.1 功能描述 +查看项目修改历史。 + +#### 4.2.2 输入参数 +- 项目ID:字符串,必填 +- 页码:整数,默认1 +- 每页数量:整数,默认10 +- 修改人:字符串,选填 +- 开始时间:日期,选填 +- 结束时间:日期,选填 + +#### 4.2.3 处理逻辑 +1. 验证项目ID是否存在 +2. 根据筛选条件查询历史记录 +3. 按时间倒序排列 +4. 计算总记录数和总页数 +5. 返回历史记录数据 + +#### 4.2.4 输出结果 +- 历史记录列表 +- 分页信息 + +#### 4.2.5 异常处理 +- 项目不存在:提示"项目不存在" +- 参数错误:提示"参数错误" +- 数据库异常:提示"系统异常,请稍后重试" + +--- + +## 5. 项目统计功能规格 + +### 5.1 项目筛选功能 + +#### 5.1.1 功能描述 +管理员通过多种条件筛选项目。 + +#### 5.1.2 输入参数 +- 项目编号:字符串,选填 +- 项目名称:字符串,选填 +- 项目状态:枚举值,选填 +- 负责人:字符串,选填 +- 创建开始日期:日期,选填 +- 创建结束日期:日期,选填 +- 修改开始日期:日期,选填 +- 修改结束日期:日期,选填 +- 部门:字符串,选填 + +#### 5.1.3 处理逻辑 +1. 验证用户是否有筛选权限 +2. 验证日期格式是否正确 +3. 构建查询条件 +4. 执行查询 +5. 返回筛选结果 + +#### 5.1.4 输出结果 +- 筛选后的项目列表 + +#### 5.1.5 异常处理 +- 无权限:提示"您没有筛选项目的权限" +- 参数错误:提示"参数错误" +- 数据库异常:提示"系统异常,请稍后重试" + +### 5.2 项目统计功能 + +#### 5.2.1 功能描述 +管理员查看项目统计数据。 + +#### 5.2.2 输入参数 +- 统计维度:枚举值,必填 +- 时间范围:开始日期、结束日期,选填 + +#### 5.2.3 处理逻辑 +1. 验证用户是否有统计权限 +2. 根据统计维度查询数据 +3. 根据时间范围过滤数据 +4. 计算统计指标 +5. 返回统计结果 + +#### 5.2.4 输出结果 +- 统计数据 +- 统计图表数据 + +#### 5.2.5 异常处理 +- 无权限:提示"您没有查看统计的权限" +- 参数错误:提示"参数错误" +- 数据库异常:提示"系统异常,请稍后重试" + +--- + +## 6. 数据验证规则 + +### 6.1 用户数据验证 +- 用户名:3-20字符,只能包含字母、数字、下划线 +- 密码:8-20字符,必须包含字母和数字 +- 真实姓名:2-20字符 +- 邮箱:标准邮箱格式 +- 联系电话:11位数字 + +### 6.2 项目数据验证 +- 项目名称:2-100字符 +- 项目编号:自动生成,格式PRJ+年份+序号 +- 项目状态:枚举值(未开始、进行中、已完成、已暂停、已取消) +- 预算:非负数 +- 日期:YYYY-MM-DD格式 + +--- + +## 7. 业务流程图 + +### 7.1 用户创建流程 +1. 管理员登录系统 +2. 进入用户管理页面 +3. 点击"创建用户"按钮 +4. 填写用户信息 +5. 提交表单 +6. 系统验证数据 +7. 保存用户信息 +8. 提示创建成功 + +### 7.2 项目创建流程 +1. 市场部人员登录系统 +2. 进入项目管理页面 +3. 点击"创建项目"按钮 +4. 填写项目信息 +5. 提交表单 +6. 系统验证数据 +7. 生成项目编号 +8. 保存项目信息 +9. 记录创建历史 +10. 提示创建成功 + +### 7.3 项目编辑流程 +1. 用户登录系统 +2. 进入项目列表页面 +3. 选择要编辑的项目 +4. 点击"编辑"按钮 +5. 修改项目信息 +6. 提交表单 +7. 系统验证数据 +8. 对比修改前后的数据 +9. 更新项目信息 +10. 记录修改历史 +11. 提示更新成功 + +--- + +## 8. 附录 + +### 8.1 枚举值定义 + +#### 用户角色 +- ADMIN:管理员 +- MARKETING:市场部 +- OTHER:其他部门 + +#### 项目状态 +- NOT_STARTED:未开始 +- IN_PROGRESS:进行中 +- COMPLETED:已完成 +- PAUSED:已暂停 +- CANCELLED:已取消 + +#### 操作类型 +- CREATE:创建 +- UPDATE:更新 +- DELETE:删除 + +#### 部门 +- MARKETING:市场部 +- TECHNOLOGY:技术部 +- DESIGN:设计部 +- FINANCE:财务部 +- HR:人力资源部 + +### 8.2 错误码定义 +- 1001:用户名或密码错误 +- 1002:用户不存在 +- 1003:用户名已存在 +- 1004:密码不一致 +- 2001:项目不存在 +- 2002:无权限操作 +- 3001:参数错误 +- 9999:系统异常 + +### 8.3 变更记录 +| 版本 | 日期 | 修改人 | 修改内容 | +|------|------|--------|----------| +| V1.0 | 2026-01-26 | - | 初始版本创建 | diff --git a/docs/技术架构文档.md b/docs/技术架构文档.md new file mode 100644 index 0000000..ce21a77 --- /dev/null +++ b/docs/技术架构文档.md @@ -0,0 +1,634 @@ +# 项目管理系统技术架构文档 + +## 文档信息 +- **文档版本**: V2.0 +- **创建日期**: 2026-01-26 +- **文档类型**: 技术架构文档 + +--- + +## 1. 技术架构概述 + +### 1.1 架构风格 +本项目采用BS(Browser/Server)架构,即浏览器/服务器架构。前端通过浏览器访问系统,后端通过服务器提供服务,数据存储在数据库中。由于系统并发量较小,采用直接操作数据库的方式,无需引入缓存层。 + +### 1.2 技术选型 + +| 分类 | 技术 | 版本 | 选型理由 | +|------|------|------|----------| +| 前端框架 | Vue 3 | 3.3.0+ | 轻量级、响应式、组件化开发,适合构建现代化Web应用 | +| 状态管理 | Pinia | 2.1.0+ | Vue 3官方推荐的状态管理库,比Vuex更轻量、更易用 | +| UI组件库 | Element Plus | 2.4.0+ | 基于Vue 3的企业级UI组件库,组件丰富,文档完善 | +| 前端路由 | Vue Router | 4.2.0+ | Vue官方路由库,支持嵌套路由、动态路由等 | +| 网络请求 | Axios | 1.6.0+ | 轻量级HTTP客户端,支持拦截器、取消请求等特性 | +| 后端框架 | FastAPI | 0.104.0+ | 高性能Python Web框架,自动生成API文档,支持异步 | +| ORM框架 | SQLAlchemy | 2.0.0+ | Python最流行的ORM框架,支持多种数据库 | +| 数据库驱动 | PyMySQL | 1.1.0+ | 纯Python实现的MySQL客户端 | +| 数据库 | MySQL | 8.0+ | 关系型数据库,稳定可靠,适合企业级应用 | +| 认证 | JWT | 2.8.0+ | JSON Web Token,用于用户认证和授权 | +| 密码加密 | Passlib | 1.7.4+ | 密码哈希库,支持多种加密算法 | +| 数据验证 | Pydantic | 2.5.0+ | 数据验证和设置库,与FastAPI完美集成 | +| API文档 | Swagger | - | FastAPI自动生成,提供交互式API文档 | +| 构建工具 | Poetry | 1.7.0+ | Python依赖管理和打包工具 | +| 版本控制 | Git | 2.40.0+ | 分布式版本控制系统,便于团队协作开发 | + +--- + +## 2. 系统架构设计 + +### 2.1 架构分层 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ 前端层 │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ 视图层 │ │ 业务逻辑层 │ │ 数据层 │ │ +│ │ (Vue组件) │ │ (Pinia Store) │ │ (Axios) │ │ +│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ +├─────────────────────────────────────────────────────────────────┤ +│ API网关 │ +├─────────────────────────────────────────────────────────────────┤ +│ 后端层 │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ 路由层 │ │ 服务层 │ │ 数据访问层 │ │ +│ │ (Router) │ │ (Service) │ │ (Model) │ │ +│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ +├─────────────────────────────────────────────────────────────────┤ +│ 数据层 │ +│ ┌─────────────────┐ ┌─────────────────┐ │ +│ │ MySQL数据库 │ │ 文件存储 │ │ +│ └─────────────────┘ └─────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 2.2 核心流程图 + +#### 2.2.1 用户登录流程 + +```mermaid +sequenceDiagram + participant 前端 as 浏览器 + participant 后端 as FastAPI服务 + participant 数据库 as MySQL + + 前端->>后端: POST /api/auth/login (username, password) + 后端->>数据库: 查询用户信息 + alt 用户存在且密码正确 + 数据库-->>后端: 返回用户信息 + 后端->>后端: 生成JWT Token + 后端-->>前端: 200 OK { "token": "...", "userInfo": {...} } + else 用户不存在或密码错误 + 数据库-->>后端: 返回空 + 后端-->>前端: 401 Unauthorized { "message": "用户名或密码错误" } + end +``` + +#### 2.2.2 项目创建流程 + +```mermaid +sequenceDiagram + participant 前端 as 浏览器 + participant 后端 as FastAPI服务 + participant 数据库 as MySQL + + 前端->>后端: POST /api/project (项目信息) + 后端->>后端: 验证用户权限 + alt 有权限 + 后端->>后端: 生成项目编号 + 后端->>数据库: 创建项目记录 + 后端->>数据库: 创建项目成员记录 + 后端->>数据库: 创建项目里程碑记录 + 后端->>数据库: 创建项目风险记录 + 后端->>数据库: 创建项目历史记录 + 后端-->>前端: 200 OK { "message": "项目创建成功", "projectId": "..." } + else 无权限 + 后端-->>前端: 403 Forbidden { "message": "您没有创建项目的权限" } + end +``` + +#### 2.2.3 项目编辑流程 + +```mermaid +sequenceDiagram + participant 前端 as 浏览器 + participant 后端 as FastAPI服务 + participant 数据库 as MySQL + + 前端->>后端: PUT /api/project/{id} (修改后的项目信息) + 后端->>后端: 验证用户权限 + alt 有权限 + 后端->>数据库: 查询项目信息 + 数据库-->>后端: 返回项目信息 + 后端->>后端: 对比修改前后的数据 + 后端->>数据库: 更新项目信息 + 后端->>数据库: 更新相关表数据 + 后端->>数据库: 创建项目历史记录 + 后端-->>前端: 200 OK { "message": "项目更新成功" } + else 无权限 + 后端-->>前端: 403 Forbidden { "message": "您没有编辑项目的权限" } + end +``` + +### 2.3 模块划分 + +#### 2.3.1 前端模块 + +| 模块名称 | 主要功能 | 对应文件路径 | +|---------|---------|------------| +| 登录模块 | 用户登录、退出登录 | src/views/login/ | +| 首页模块 | 系统首页、项目概览 | src/views/home/ | +| 用户管理模块 | 用户列表、创建用户、编辑用户 | src/views/user/ | +| 项目管理模块 | 项目列表、创建项目、编辑项目 | src/views/project/ | +| 项目详情模块 | 项目详细信息、修改历史 | src/views/project/detail/ | +| 项目统计模块 | 项目筛选、统计分析 | src/views/project/statistics/ | +| 系统设置模块 | 系统配置、个人设置 | src/views/settings/ | + +#### 2.3.2 后端模块 + +| 模块名称 | 主要功能 | 对应包路径 | +|---------|---------|-----------| +| 认证模块 | 用户认证、授权 | app.api.auth | +| 用户模块 | 用户管理、权限管理 | app.api.user | +| 项目模块 | 项目管理、项目编辑 | app.api.project | +| 历史记录模块 | 项目修改历史 | app.api.history | +| 统计模块 | 项目统计、数据分析 | app.api.statistics | +| 系统模块 | 系统配置、操作日志 | app.api.system | +| 数据模型 | 数据库模型定义 | app.models | +| 数据库会话 | 数据库连接管理 | app.database | +| 公共模块 | 工具类、常量定义 | app.common | + +--- + +## 3. 目录结构 + +### 3.1 前端目录结构 + +``` +├── public/ # 静态资源 +│ ├── favicon.ico # 网站图标 +│ └── index.html # HTML模板 +├── src/ # 源代码 +│ ├── assets/ # 资源文件 +│ │ ├── css/ # 样式文件 +│ │ ├── images/ # 图片文件 +│ │ └── icons/ # 图标文件 +│ ├── components/ # 公共组件 +│ ├── views/ # 页面组件 +│ │ ├── login/ # 登录页面 +│ │ ├── home/ # 首页 +│ │ ├── user/ # 用户管理 +│ │ ├── project/ # 项目管理 +│ │ └── settings/ # 系统设置 +│ ├── router/ # 路由配置 +│ ├── store/ # 状态管理 +│ ├── api/ # API请求 +│ ├── utils/ # 工具函数 +│ ├── constants/ # 常量定义 +│ ├── hooks/ # 自定义Hooks +│ ├── App.vue # 根组件 +│ └── main.js # 入口文件 +├── .env # 环境变量 +├── .env.development # 开发环境变量 +├── .env.production # 生产环境变量 +├── vite.config.js # Vite配置 +├── package.json # 项目依赖 +└── README.md # 项目说明 +``` + +### 3.2 后端目录结构 + +``` +├── app/ # 应用代码 +│ ├── api/ # API路由 +│ │ ├── auth.py # 认证API +│ │ ├── user.py # 用户API +│ │ ├── project.py # 项目API +│ │ ├── history.py # 历史记录API +│ │ ├── statistics.py # 统计API +│ │ └── system.py # 系统API +│ ├── models/ # 数据模型 +│ │ ├── user.py # 用户模型 +│ │ ├── project.py # 项目模型 +│ │ ├── member.py # 成员模型 +│ │ ├── milestone.py # 里程碑模型 +│ │ ├── risk.py # 风险模型 +│ │ ├── history.py # 历史记录模型 +│ │ └── log.py # 日志模型 +│ ├── schemas/ # Pydantic模式 +│ │ ├── auth.py # 认证模式 +│ │ ├── user.py # 用户模式 +│ │ ├── project.py # 项目模式 +│ │ └── common.py # 通用模式 +│ ├── services/ # 业务逻辑 +│ │ ├── auth_service.py # 认证服务 +│ │ ├── user_service.py # 用户服务 +│ │ ├── project_service.py # 项目服务 +│ │ ├── history_service.py # 历史记录服务 +│ │ └── statistics_service.py # 统计服务 +│ ├── database/ # 数据库配置 +│ │ ├── config.py # 数据库配置 +│ │ └── session.py # 数据库会话 +│ ├── common/ # 公共模块 +│ │ ├── constants.py # 常量定义 +│ │ ├── utils.py # 工具函数 +│ │ ├── dependencies.py # 依赖注入 +│ │ └── exceptions.py # 异常处理 +│ ├── config.py # 应用配置 +│ └── main.py # 应用入口 +├── tests/ # 测试代码 +│ ├── test_auth.py # 认证测试 +│ ├── test_user.py # 用户测试 +│ └── test_project.py # 项目测试 +├── alembic/ # 数据库迁移 +│ ├── versions/ # 迁移版本 +│ └── env.py # 迁移环境 +├── pyproject.toml # Poetry配置 +├── poetry.lock # Poetry锁文件 +├── requirements.txt # 依赖列表 +└── README.md # 项目说明 +``` + +--- + +## 4. 关键类与函数设计 + +### 4.1 前端关键类与函数 + +#### 4.1.1 认证相关 + +| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 | +|-----------|------|-----------------|-----------------|-----------------|--------------| +| `login()` | 用户登录 | username: String 用户名
password: String 密码 | `{ token: String, userInfo: Object }` | `{ code: Number, message: String }` | src/api/auth.js | +| `logout()` | 用户退出 | 无 | `{ code: 200, message: String }` | `{ code: Number, message: String }` | src/api/auth.js | +| `getUserInfo()` | 获取用户信息 | 无 | `{ userInfo: Object }` | `{ code: Number, message: String }` | src/api/auth.js | + +#### 4.1.2 项目相关 + +| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 | +|-----------|------|-----------------|-----------------|-----------------|--------------| +| `createProject()` | 创建项目 | project: Object 项目信息 | `{ code: 200, message: String, projectId: String }` | `{ code: Number, message: String }` | src/api/project.js | +| `updateProject()` | 更新项目 | id: String 项目ID
project: Object 项目信息 | `{ code: 200, message: String }` | `{ code: Number, message: String }` | src/api/project.js | +| `getProjectList()` | 获取项目列表 | params: Object 查询参数 | `{ code: 200, data: Array, total: Number }` | `{ code: Number, message: String }` | src/api/project.js | +| `getProjectDetail()` | 获取项目详情 | id: String 项目ID | `{ code: 200, data: Object }` | `{ code: Number, message: String }` | src/api/project.js | +| `getProjectHistory()` | 获取项目历史 | id: String 项目ID
params: Object 查询参数 | `{ code: 200, data: Array, total: Number }` | `{ code: Number, message: String }` | src/api/project.js | + +#### 4.1.3 用户相关 + +| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 | +|-----------|------|-----------------|-----------------|-----------------|--------------| +| `createUser()` | 创建用户 | user: Object 用户信息 | `{ code: 200, message: String }` | `{ code: Number, message: String }` | src/api/user.js | +| `updateUser()` | 更新用户 | id: String 用户ID
user: Object 用户信息 | `{ code: 200, message: String }` | `{ code: Number, message: String }` | src/api/user.js | +| `getUserList()` | 获取用户列表 | params: Object 查询参数 | `{ code: 200, data: Array, total: Number }` | `{ code: Number, message: String }` | src/api/user.js | +| `resetPassword()` | 重置密码 | id: String 用户ID
password: String 新密码 | `{ code: 200, message: String }` | `{ code: Number, message: String }` | src/api/user.js | + +### 4.2 后端关键类与函数 + +#### 4.2.1 认证相关 + +| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 | +|-----------|------|-----------------|-----------------|-----------------|--------------| +| `AuthRouter.login()` | 用户登录 | LoginRequest 登录请求 | `AuthResponse` | `HTTPException` | app.api.auth | +| `AuthRouter.logout()` | 用户退出 | 无 | `BaseResponse` | `HTTPException` | app.api.auth | +| `AuthService.authenticate()` | 认证用户 | str username, str password | `AuthResponse` | `AuthenticationException` | app.services.auth_service | +| `AuthService.create_token()` | 创建Token | str user_id | `str` Token | `Exception` | app.services.auth_service | + +#### 4.2.2 项目相关 + +| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 | +|-----------|------|-----------------|-----------------|-----------------|--------------| +| `ProjectRouter.create()` | 创建项目 | ProjectRequest 项目请求 | `BaseResponse` | `HTTPException` | app.api.project | +| `ProjectRouter.update()` | 更新项目 | str id, ProjectRequest 项目请求 | `BaseResponse` | `HTTPException` | app.api.project | +| `ProjectRouter.list()` | 获取项目列表 | ProjectQuery 查询参数 | `PageResponse[Project]` | `HTTPException` | app.api.project | +| `ProjectRouter.detail()` | 获取项目详情 | str id | `BaseResponse[ProjectDetail]` | `HTTPException` | app.api.project | +| `ProjectService.create_project()` | 创建项目 | ProjectRequest 项目请求, str username | `str` 项目ID | `ServiceException` | app.services.project_service | +| `ProjectService.update_project()` | 更新项目 | str id, ProjectRequest 项目请求, str username | `None` | `ServiceException` | app.services.project_service | + +#### 4.2.3 历史记录相关 + +| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 | +|-----------|------|-----------------|-----------------|-----------------|--------------| +| `HistoryRouter.list()` | 获取项目历史 | str project_id, HistoryQuery 查询参数 | `PageResponse[ProjectHistory]` | `HTTPException` | app.api.history | +| `HistoryService.record_history()` | 记录历史 | str project_id, str operation_type, str operator, dict changes | `None` | `ServiceException` | app.services.history_service | + +#### 4.2.4 统计相关 + +| 类/函数名 | 说明 | 参数(类型/含义) | 成功返回结构/类型 | 失败返回结构/类型 | 所属文件/模块 | +|-----------|------|-----------------|-----------------|-----------------|--------------| +| `StatisticsRouter.filter()` | 筛选项目 | ProjectFilter 筛选条件 | `PageResponse[Project]` | `HTTPException` | app.api.statistics | +| `StatisticsRouter.statistics()` | 统计项目 | StatisticsRequest 统计请求 | `BaseResponse[StatisticsResult]` | `HTTPException` | app.api.statistics | +| `StatisticsService.get_project_statistics()` | 获取项目统计 | StatisticsRequest 统计请求 | `StatisticsResult` | `ServiceException` | app.services.statistics_service | + +--- + +## 5. 数据库与数据结构设计 + +### 5.1 数据库表结构 + +详细的数据库表结构见《数据库设计文档》。 + +### 5.2 数据传输对象 (DTOs) + +#### 5.2.1 前端DTOs + +##### 登录请求 +```javascript +{ + username: String, // 用户名 + password: String // 密码 +} +``` + +##### 项目请求 +```javascript +{ + projectName: String, // 项目名称 + status: String, // 项目状态 + leader: String, // 负责人 + phone: String, // 联系电话 + email: String, // 邮箱 + background: String, // 项目背景 + goal: String, // 项目目标 + scope: String, // 项目范围 + startDate: String, // 开始日期 + plannedEndDate: String, // 预计结束日期 + actualEndDate: String, // 实际结束日期 + totalBudget: Number, // 总预算 + usedBudget: Number, // 已使用预算 + members: [ // 项目成员 + { + name: String, // 姓名 + role: String, // 角色 + department: String // 部门 + } + ], + milestones: [ // 项目里程碑 + { + name: String, // 名称 + plannedDate: String, // 计划日期 + actualDate: String, // 实际日期 + status: String // 状态 + } + ], + risks: [ // 项目风险 + { + description: String, // 描述 + level: String, // 等级 + measure: String // 应对措施 + } + ], + remarks: String // 备注 +} +``` + +#### 5.2.2 后端DTOs (Pydantic Models) + +##### 登录请求 +```python +from pydantic import BaseModel + +class LoginRequest(BaseModel): + username: str # 用户名 + password: str # 密码 +``` + +##### 项目请求 +```python +from pydantic import BaseModel +from typing import List, Optional +from datetime import date +from decimal import Decimal + +class MemberRequest(BaseModel): + name: str # 姓名 + role: str # 角色 + department: str # 部门 + +class MilestoneRequest(BaseModel): + name: str # 名称 + planned_date: date # 计划日期 + actual_date: Optional[date] = None # 实际日期 + status: str # 状态 + +class RiskRequest(BaseModel): + description: str # 描述 + level: str # 等级 + measure: str # 应对措施 + +class ProjectRequest(BaseModel): + project_name: str # 项目名称 + status: str # 项目状态 + leader: str # 负责人 + phone: Optional[str] = None # 联系电话 + email: Optional[str] = None # 邮箱 + background: Optional[str] = None # 项目背景 + goal: Optional[str] = None # 项目目标 + scope: Optional[str] = None # 项目范围 + start_date: date # 开始日期 + planned_end_date: date # 预计结束日期 + actual_end_date: Optional[date] = None # 实际结束日期 + total_budget: Decimal # 总预算 + used_budget: Decimal = Decimal('0.00') # 已使用预算 + members: List[MemberRequest] = [] # 项目成员 + milestones: List[MilestoneRequest] = [] # 项目里程碑 + risks: List[RiskRequest] = [] # 项目风险 + remarks: Optional[str] = None # 备注 +``` + +--- + +## 6. 接口设计 + +### 6.1 认证接口 + +| API路径 | 方法 | 模块/文件 | 类型 | 功能描述 | 请求体 (JSON) | 成功响应 (200 OK) | +|---------|------|-----------|------|----------|--------------|------------------| +| `/api/auth/login` | `POST` | `app.api.auth` | `Router` | 用户登录 | `{"username": "admin", "password": "123456"}` | `{"code": 200, "data": {"token": "...", "userInfo": {...}}, "message": "登录成功"}` | +| `/api/auth/logout` | `POST` | `app.api.auth` | `Router` | 用户退出 | N/A | `{"code": 200, "message": "退出成功"}` | +| `/api/auth/userInfo` | `GET` | `app.api.auth` | `Router` | 获取用户信息 | N/A | `{"code": 200, "data": {"userId": "...", "username": "...", "realName": "...", "department": "...", "role": "..."}}` | + +### 6.2 用户接口 + +| API路径 | 方法 | 模块/文件 | 类型 | 功能描述 | 请求体 (JSON) | 成功响应 (200 OK) | +|---------|------|-----------|------|----------|--------------|------------------| +| `/api/user` | `GET` | `app.api.user` | `Router` | 获取用户列表 | N/A (Query参数: page, size, username, department, role) | `{"code": 200, "data": [{...}], "total": 10, "message": "查询成功"}` | +| `/api/user` | `POST` | `app.api.user` | `Router` | 创建用户 | `{"username": "test", "password": "123456", "realName": "测试用户", "department": "技术部", "role": "OTHER"}` | `{"code": 200, "message": "创建成功"}` | +| `/api/user/{id}` | `PUT` | `app.api.user` | `Router` | 更新用户 | `{"realName": "测试用户1", "department": "市场部", "role": "MARKETING"}` | `{"code": 200, "message": "更新成功"}` | +| `/api/user/{id}` | `DELETE` | `app.api.user` | `Router` | 删除用户 | N/A | `{"code": 200, "message": "删除成功"}` | +| `/api/user/resetPassword/{id}` | `PUT` | `app.api.user` | `Router` | 重置密码 | `{"password": "123456"}` | `{"code": 200, "message": "密码重置成功"}` | + +### 6.3 项目接口 + +| API路径 | 方法 | 模块/文件 | 类型 | 功能描述 | 请求体 (JSON) | 成功响应 (200 OK) | +|---------|------|-----------|------|----------|--------------|------------------| +| `/api/project` | `GET` | `app.api.project` | `Router` | 获取项目列表 | N/A (Query参数: page, size, projectName, status, leader) | `{"code": 200, "data": [{...}], "total": 10, "message": "查询成功"}` | +| `/api/project` | `POST` | `app.api.project` | `Router` | 创建项目 | `{"projectName": "测试项目", "status": "NOT_STARTED", "leader": "张三", ...}` | `{"code": 200, "data": {"projectId": "..."}, "message": "创建成功"}` | +| `/api/project/{id}` | `GET` | `app.api.project` | `Router` | 获取项目详情 | N/A | `{"code": 200, "data": {...}, "message": "查询成功"}` | +| `/api/project/{id}` | `PUT` | `app.api.project` | `Router` | 更新项目 | `{"projectName": "测试项目1", "status": "IN_PROGRESS", ...}` | `{"code": 200, "message": "更新成功"}` | +| `/api/project/{id}` | `DELETE` | `app.api.project` | `Router` | 删除项目 | N/A | `{"code": 200, "message": "删除成功"}` | + +### 6.4 历史记录接口 + +| API路径 | 方法 | 模块/文件 | 类型 | 功能描述 | 请求体 (JSON) | 成功响应 (200 OK) | +|---------|------|-----------|------|----------|--------------|------------------| +| `/api/history/{projectId}` | `GET` | `app.api.history` | `Router` | 获取项目历史 | N/A (Query参数: page, size, operator, startDate, endDate) | `{"code": 200, "data": [{...}], "total": 10, "message": "查询成功"}` | + +### 6.5 统计接口 + +| API路径 | 方法 | 模块/文件 | 类型 | 功能描述 | 请求体 (JSON) | 成功响应 (200 OK) | +|---------|------|-----------|------|----------|--------------|------------------| +| `/api/statistics/filter` | `POST` | `app.api.statistics` | `Router` | 筛选项目 | `{"projectNo": "PRJ2025001", "projectName": "测试", "status": "IN_PROGRESS", ...}` | `{"code": 200, "data": [{...}], "total": 10, "message": "查询成功"}` | +| `/api/statistics/statistics` | `POST` | `app.api.statistics` | `Router` | 统计项目 | `{"dimension": "status", "startDate": "2025-01-01", "endDate": "2025-12-31"}` | `{"code": 200, "data": {...}, "message": "统计成功"}` | + +--- + +## 7. 安全设计 + +### 7.1 认证与授权 + +- **认证方式**: JWT (JSON Web Token) 认证 +- **授权方式**: 基于角色的访问控制 (RBAC) +- **密码加密**: 使用 Passlib 的 bcrypt 算法加密存储密码 +- **会话管理**: JWT Token 存储在前端 localStorage 中,后端验证 Token 签名和有效期 + +### 7.2 接口安全 + +- **统一认证拦截**: 所有接口(除登录接口外)都需要验证 Token +- **权限验证**: 基于用户角色和权限进行接口访问控制 +- **请求参数验证**: 使用 Pydantic 验证请求参数的合法性 +- **防止SQL注入**: 使用 SQLAlchemy 的参数化查询,避免SQL注入 +- **防止XSS攻击**: 前端使用 v-html 时进行转义,后端对输入进行过滤 +- **防止CSRF攻击**: 使用 Token 验证,确保请求来自合法来源 + +### 7.3 数据安全 + +- **敏感数据加密**: 敏感数据(如密码)加密存储 +- **数据备份**: 定期备份数据库,防止数据丢失 +- **数据恢复**: 制定数据恢复计划,确保数据可恢复 +- **日志审计**: 记录用户操作日志,便于追溯操作行为 + +--- + +## 8. 部署与集成方案 + +### 8.1 开发环境部署 + +#### 8.1.1 前端部署 +1. 克隆代码库: `git clone ` +2. 安装依赖: `npm install` +3. 启动开发服务器: `npm run dev` +4. 访问: `http://localhost:5173` + +#### 8.1.2 后端部署 +1. 克隆代码库: `git clone ` +2. 安装依赖: `poetry install` 或 `pip install -r requirements.txt` +3. 配置数据库连接 (config.py) +4. 初始化数据库: `alembic upgrade head` +5. 启动应用: `uvicorn app.main:app --reload` +6. 访问: `http://localhost:8000` +7. API文档: `http://localhost:8000/docs` + +### 8.2 生产环境部署 + +#### 8.2.1 前端部署 +1. 构建生产版本: `npm run build` +2. 将构建产物复制到 Nginx 静态目录 +3. 配置 Nginx 反向代理 + +#### 8.2.2 后端部署 +1. 安装依赖: `poetry install --no-dev` 或 `pip install -r requirements.txt` +2. 配置环境变量和数据库连接 +3. 使用 Gunicorn 启动应用: `gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker` +4. 配置 Nginx 反向代理 + +### 8.3 集成方案 + +- **前端与后端集成**: 通过 RESTful API 进行数据交互 +- **数据库集成**: 使用 SQLAlchemy ORM 操作数据库 +- **日志集成**: 使用 Python logging 模块记录系统日志 +- **监控集成**: 可集成 Prometheus + Grafana 监控系统性能 + +--- + +## 9. 性能优化策略 + +### 9.1 前端优化 + +- **代码分割**: 使用 Vue 的路由懒加载,减少初始加载时间 +- **组件缓存**: 使用 keep-alive 缓存频繁访问的组件 +- **图片优化**: 使用适当尺寸的图片,压缩图片大小 +- **网络请求优化**: 使用 Axios 拦截器,统一处理请求和响应 +- **减少 DOM 操作**: 使用虚拟列表处理大数据渲染 + +### 9.2 后端优化 + +- **数据库索引**: 为常用查询字段创建索引,提高查询性能 +- **连接池**: 使用 SQLAlchemy 的连接池,提高数据库连接效率 +- **异步处理**: FastAPI 支持异步处理,提高系统响应速度 +- **批量操作**: 对批量数据操作使用批量处理,减少数据库交互次数 +- **查询优化**: 使用 SQLAlchemy 的 lazy loading 和 eager loading 优化查询 + +### 9.3 数据库优化 + +- **表结构优化**: 合理设计表结构,避免冗余字段 +- **SQL优化**: 优化 SQL 查询语句,避免全表扫描 +- **定期清理**: 定期清理历史数据,避免数据量过大 +- **数据库配置**: 优化 MySQL 配置参数,提高数据库性能 + +--- + +## 10. 监控与维护 + +### 10.1 系统监控 + +- **应用监控**: 监控应用的运行状态、CPU、内存使用情况 +- **数据库监控**: 监控数据库的连接数、查询性能、存储空间 +- **API监控**: 监控 API 的响应时间、调用次数、错误率 +- **日志监控**: 监控系统日志,及时发现异常情况 + +### 10.2 故障处理 + +- **故障定位**: 通过日志和监控工具定位故障原因 +- **故障恢复**: 制定故障恢复方案,确保系统快速恢复 +- **故障预防**: 定期进行系统检查,预防故障发生 + +### 10.3 系统维护 + +- **定期更新**: 定期更新依赖库和框架版本,修复安全漏洞 +- **数据备份**: 定期备份数据库,防止数据丢失 +- **性能调优**: 定期分析系统性能,进行性能调优 +- **文档更新**: 及时更新系统文档,保持文档与系统同步 + +--- + +## 11. 附录 + +### 11.1 技术选型对比 + +| 技术 | 对比方案 | 最终选择理由 | +|------|----------|--------------| +| 前端框架 | Vue 3 vs React | Vue 3 学习曲线平缓,文档完善,适合快速开发 | +| 后端框架 | FastAPI vs Flask vs Django | FastAPI 高性能,自动生成API文档,支持异步 | +| 数据库 | MySQL vs PostgreSQL | MySQL 社区活跃,生态成熟,适合企业级应用 | +| ORM框架 | SQLAlchemy vs Django ORM | SQLAlchemy 灵活强大,支持多种数据库 | + +### 11.2 开发规范 + +#### 前端开发规范 +- 组件命名: 大驼峰命名法 (PascalCase) +- 变量命名: 小驼峰命名法 (camelCase) +- 常量命名: 全大写下划线分隔 (SNAKE_CASE) +- 代码风格: 遵循 ESLint 规范 + +#### 后端开发规范 +- 类命名: 大驼峰命名法 (PascalCase) +- 函数命名: 小驼峰命名法 (camelCase) +- 变量命名: 小写下划线分隔 (snake_case) +- 常量命名: 全大写下划线分隔 (SNAKE_CASE) +- 代码风格: 遵循 PEP 8 规范 + +### 11.3 变更记录 + +| 版本 | 日期 | 修改人 | 修改内容 | +|------|------|--------|----------| +| V1.0 | 2026-01-26 | - | 初始版本创建 | +| V2.0 | 2026-01-26 | - | 后端改为Python FastAPI,去掉Redis | diff --git a/docs/数据库设计文档.md b/docs/数据库设计文档.md new file mode 100644 index 0000000..ffc975f --- /dev/null +++ b/docs/数据库设计文档.md @@ -0,0 +1,529 @@ +# 项目管理系统数据库设计文档 + +## 文档信息 +- **文档版本**: V1.0 +- **创建日期**: 2026-01-26 +- **文档类型**: 数据库设计文档 (DDD) + +--- + +## 1. 数据库概述 + +### 1.1 数据库选型 +- **数据库类型**: 关系型数据库 +- **推荐数据库**: MySQL / PostgreSQL + +### 1.2 数据库设计原则 +- 遵循第三范式(3NF) +- 合理使用索引提高查询性能 +- 考虑数据完整性和一致性 +- 支持事务处理 +- 便于扩展和维护 + +--- + +## 2. 数据库表设计 + +### 2.1 用户表 (sys_user) + +#### 2.1.1 表说明 +存储系统用户的基本信息和认证信息。 + +#### 2.1.2 表结构 + +| 字段名 | 数据类型 | 长度 | 是否必填 | 默认值 | 说明 | +|--------|----------|------|----------|--------|------| +| user_id | VARCHAR | 32 | 是 | - | 用户ID,主键 | +| username | VARCHAR | 50 | 是 | - | 用户名,唯一 | +| password | VARCHAR | 128 | 是 | - | 密码,加密存储 | +| real_name | VARCHAR | 50 | 是 | - | 真实姓名 | +| department | VARCHAR | 50 | 是 | - | 部门 | +| phone | VARCHAR | 20 | 否 | NULL | 联系电话 | +| email | VARCHAR | 100 | 否 | NULL | 邮箱 | +| role | VARCHAR | 20 | 是 | - | 角色 | +| status | TINYINT | 1 | 是 | 1 | 状态:1-正常,0-禁用 | +| create_time | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | +| update_time | DATETIME | - | 是 | CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 更新时间 | +| last_login_time | DATETIME | - | 否 | NULL | 最后登录时间 | + +#### 2.1.3 索引设计 +- PRIMARY KEY: user_id +- UNIQUE KEY: uk_username (username) +- INDEX: idx_department (department) +- INDEX: idx_role (role) +- INDEX: idx_status (status) + +#### 2.1.4 外键约束 +无 + +--- + +### 2.2 项目表 (project) + +#### 2.2.1 表说明 +存储项目的基本信息和详细内容。 + +#### 2.2.2 表结构 + +| 字段名 | 数据类型 | 长度 | 是否必填 | 默认值 | 说明 | +|--------|----------|------|----------|--------|------| +| project_id | VARCHAR | 32 | 是 | - | 项目ID,主键 | +| project_no | VARCHAR | 20 | 是 | - | 项目编号,唯一 | +| project_name | VARCHAR | 200 | 是 | - | 项目名称 | +| status | VARCHAR | 20 | 是 | 'NOT_STARTED' | 项目状态 | +| create_time | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | +| creator | VARCHAR | 50 | 是 | - | 创建人 | +| update_time | DATETIME | - | 是 | CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 最后修改时间 | +| last_modifier | VARCHAR | 50 | 否 | NULL | 最后修改人 | +| leader | VARCHAR | 50 | 是 | - | 负责人 | +| phone | VARCHAR | 20 | 否 | NULL | 联系电话 | +| email | VARCHAR | 100 | 否 | NULL | 邮箱 | +| background | TEXT | - | 否 | NULL | 项目背景 | +| goal | TEXT | - | 否 | NULL | 项目目标 | +| scope | TEXT | - | 否 | NULL | 项目范围 | +| start_date | DATE | - | 是 | - | 开始日期 | +| planned_end_date | DATE | - | 是 | - | 预计结束日期 | +| actual_end_date | DATE | - | 否 | NULL | 实际结束日期 | +| total_budget | DECIMAL | 15,2 | 是 | 0.00 | 总预算 | +| used_budget | DECIMAL | 15,2 | 是 | 0.00 | 已使用预算 | +| remaining_budget | DECIMAL | 15,2 | 是 | 0.00 | 剩余预算 | +| remarks | TEXT | - | 否 | NULL | 备注 | + +#### 2.2.3 索引设计 +- PRIMARY KEY: project_id +- UNIQUE KEY: uk_project_no (project_no) +- INDEX: idx_status (status) +- INDEX: idx_creator (creator) +- INDEX: idx_leader (leader) +- INDEX: idx_create_time (create_time) +- INDEX: idx_update_time (update_time) + +#### 2.2.4 外键约束 +无 + +--- + +### 2.3 项目成员表 (project_member) + +#### 2.3.1 表说明 +存储项目成员信息。 + +#### 2.3.2 表结构 + +| 字段名 | 数据类型 | 长度 | 是否必填 | 默认值 | 说明 | +|--------|----------|------|----------|--------|------| +| member_id | VARCHAR | 32 | 是 | - | 成员ID,主键 | +| project_id | VARCHAR | 32 | 是 | - | 项目ID,外键 | +| name | VARCHAR | 50 | 是 | - | 姓名 | +| role | VARCHAR | 50 | 是 | - | 角色 | +| department | VARCHAR | 50 | 是 | - | 部门 | +| create_time | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | + +#### 2.3.3 索引设计 +- PRIMARY KEY: member_id +- INDEX: idx_project_id (project_id) + +#### 2.3.4 外键约束 +- FOREIGN KEY: project_id -> project(project_id) ON DELETE CASCADE + +--- + +### 2.4 项目里程碑表 (project_milestone) + +#### 2.4.1 表说明 +存储项目里程碑信息。 + +#### 2.4.2 表结构 + +| 字段名 | 数据类型 | 长度 | 是否必填 | 默认值 | 说明 | +|--------|----------|------|----------|--------|------| +| milestone_id | VARCHAR | 32 | 是 | - | 里程碑ID,主键 | +| project_id | VARCHAR | 32 | 是 | - | 项目ID,外键 | +| name | VARCHAR | 200 | 是 | - | 名称 | +| planned_date | DATE | - | 是 | - | 计划日期 | +| actual_date | DATE | - | 否 | NULL | 实际日期 | +| status | VARCHAR | 20 | 是 | 'NOT_STARTED' | 状态 | +| create_time | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | +| update_time | DATETIME | - | 是 | CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 更新时间 | + +#### 2.4.3 索引设计 +- PRIMARY KEY: milestone_id +- INDEX: idx_project_id (project_id) +- INDEX: idx_status (status) + +#### 2.4.4 外键约束 +- FOREIGN KEY: project_id -> project(project_id) ON DELETE CASCADE + +--- + +### 2.5 项目风险表 (project_risk) + +#### 2.5.1 表说明 +存储项目风险信息。 + +#### 2.5.2 表结构 + +| 字段名 | 数据类型 | 长度 | 是否必填 | 默认值 | 说明 | +|--------|----------|------|----------|--------|------| +| risk_id | VARCHAR | 32 | 是 | - | 风险ID,主键 | +| project_id | VARCHAR | 32 | 是 | - | 项目ID,外键 | +| description | TEXT | - | 是 | - | 描述 | +| level | VARCHAR | 20 | 是 | - | 等级 | +| measure | TEXT | - | 是 | - | 应对措施 | +| create_time | DATETIME | - | 是 | CURRENT_TIMESTAMP | 创建时间 | + +#### 2.5.3 索引设计 +- PRIMARY KEY: risk_id +- INDEX: idx_project_id (project_id) +- INDEX: idx_level (level) + +#### 2.5.4 外键约束 +- FOREIGN KEY: project_id -> project(project_id) ON DELETE CASCADE + +--- + +### 2.6 项目历史记录表 (project_history) + +#### 2.6.1 表说明 +存储项目修改历史记录。 + +#### 2.6.2 表结构 + +| 字段名 | 数据类型 | 长度 | 是否必填 | 默认值 | 说明 | +|--------|----------|------|----------|--------|------| +| history_id | VARCHAR | 32 | 是 | - | 历史记录ID,主键 | +| project_id | VARCHAR | 32 | 是 | - | 项目ID,外键 | +| project_no | VARCHAR | 20 | 是 | - | 项目编号 | +| operation_type | VARCHAR | 20 | 是 | - | 操作类型 | +| operator | VARCHAR | 50 | 是 | - | 操作人 | +| operation_time | DATETIME | - | 是 | CURRENT_TIMESTAMP | 操作时间 | +| field_name | VARCHAR | 100 | 是 | - | 字段名 | +| old_value | TEXT | - | 否 | NULL | 修改前值 | +| new_value | TEXT | - | 否 | NULL | 修改后值 | + +#### 2.6.3 索引设计 +- PRIMARY KEY: history_id +- INDEX: idx_project_id (project_id) +- INDEX: idx_project_no (project_no) +- INDEX: idx_operation_time (operation_time) +- INDEX: idx_operator (operator) + +#### 2.6.4 外键约束 +- FOREIGN KEY: project_id -> project(project_id) ON DELETE CASCADE + +--- + +### 2.7 操作日志表 (operation_log) + +#### 2.7.1 表说明 +存储用户操作日志。 + +#### 2.7.2 表结构 + +| 字段名 | 数据类型 | 长度 | 是否必填 | 默认值 | 说明 | +|--------|----------|------|----------|--------|------| +| log_id | VARCHAR | 32 | 是 | - | 日志ID,主键 | +| user_id | VARCHAR | 32 | 是 | - | 用户ID,外键 | +| username | VARCHAR | 50 | 是 | - | 用户名 | +| operation | VARCHAR | 100 | 是 | - | 操作内容 | +| ip_address | VARCHAR | 50 | 否 | NULL | IP地址 | +| user_agent | VARCHAR | 500 | 否 | NULL | 用户代理 | +| operation_time | DATETIME | - | 是 | CURRENT_TIMESTAMP | 操作时间 | + +#### 2.7.3 索引设计 +- PRIMARY KEY: log_id +- INDEX: idx_user_id (user_id) +- INDEX: idx_operation_time (operation_time) + +#### 2.7.4 外键约束 +- FOREIGN KEY: user_id -> sys_user(user_id) ON DELETE CASCADE + +--- + +## 3. 数据库关系图 + +### 3.1 ER图描述 + +``` +sys_user (用户表) + | + | 1 + | + | N + | +operation_log (操作日志表) + +project (项目表) + | + | 1 + | + | N + | + +-- project_member (项目成员表) + | + | 1 + | + | N + | + +-- project_milestone (项目里程碑表) + | + | 1 + | + | N + | + +-- project_risk (项目风险表) + | + | 1 + | + | N + | + +-- project_history (项目历史记录表) +``` + +--- + +## 4. 数据字典 + +### 4.1 用户角色枚举值 +| 值 | 说明 | +|----|------| +| ADMIN | 管理员 | +| MARKETING | 市场部 | +| OTHER | 其他部门 | + +### 4.2 项目状态枚举值 +| 值 | 说明 | +|----|------| +| NOT_STARTED | 未开始 | +| IN_PROGRESS | 进行中 | +| COMPLETED | 已完成 | +| PAUSED | 已暂停 | +| CANCELLED | 已取消 | + +### 4.3 里程碑状态枚举值 +| 值 | 说明 | +|----|------| +| NOT_STARTED | 未开始 | +| IN_PROGRESS | 进行中 | +| COMPLETED | 已完成 | + +### 4.4 风险等级枚举值 +| 值 | 说明 | +|----|------| +| HIGH | 高 | +| MEDIUM | 中 | +| LOW | 低 | + +### 4.5 操作类型枚举值 +| 值 | 说明 | +|----|------| +| CREATE | 创建 | +| UPDATE | 更新 | +| DELETE | 删除 | + +### 4.6 部门枚举值 +| 值 | 说明 | +|----|------| +| MARKETING | 市场部 | +| TECHNOLOGY | 技术部 | +| DESIGN | 设计部 | +| FINANCE | 财务部 | +| HR | 人力资源部 | + +--- + +## 5. 数据库初始化脚本 + +### 5.1 建表脚本 + +```sql +-- 创建用户表 +CREATE TABLE sys_user ( + user_id VARCHAR(32) PRIMARY KEY COMMENT '用户ID', + username VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名', + password VARCHAR(128) NOT NULL COMMENT '密码', + real_name VARCHAR(50) NOT NULL COMMENT '真实姓名', + department VARCHAR(50) NOT NULL COMMENT '部门', + phone VARCHAR(20) COMMENT '联系电话', + email VARCHAR(100) COMMENT '邮箱', + role VARCHAR(20) NOT NULL COMMENT '角色', + status TINYINT(1) NOT NULL DEFAULT 1 COMMENT '状态:1-正常,0-禁用', + create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + last_login_time DATETIME COMMENT '最后登录时间', + INDEX idx_department (department), + INDEX idx_role (role), + INDEX idx_status (status) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; + +-- 创建项目表 +CREATE TABLE project ( + project_id VARCHAR(32) PRIMARY KEY COMMENT '项目ID', + project_no VARCHAR(20) NOT NULL UNIQUE COMMENT '项目编号', + project_name VARCHAR(200) NOT NULL COMMENT '项目名称', + status VARCHAR(20) NOT NULL DEFAULT 'NOT_STARTED' COMMENT '项目状态', + create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + creator VARCHAR(50) NOT NULL COMMENT '创建人', + update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间', + last_modifier VARCHAR(50) COMMENT '最后修改人', + leader VARCHAR(50) NOT NULL COMMENT '负责人', + phone VARCHAR(20) COMMENT '联系电话', + email VARCHAR(100) COMMENT '邮箱', + background TEXT COMMENT '项目背景', + goal TEXT COMMENT '项目目标', + scope TEXT COMMENT '项目范围', + start_date DATE NOT NULL COMMENT '开始日期', + planned_end_date DATE NOT NULL COMMENT '预计结束日期', + actual_end_date DATE COMMENT '实际结束日期', + total_budget DECIMAL(15,2) NOT NULL DEFAULT 0.00 COMMENT '总预算', + used_budget DECIMAL(15,2) NOT NULL DEFAULT 0.00 COMMENT '已使用预算', + remaining_budget DECIMAL(15,2) NOT NULL DEFAULT 0.00 COMMENT '剩余预算', + remarks TEXT COMMENT '备注', + INDEX idx_status (status), + INDEX idx_creator (creator), + INDEX idx_leader (leader), + INDEX idx_create_time (create_time), + INDEX idx_update_time (update_time) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目表'; + +-- 创建项目成员表 +CREATE TABLE project_member ( + member_id VARCHAR(32) PRIMARY KEY COMMENT '成员ID', + project_id VARCHAR(32) NOT NULL COMMENT '项目ID', + name VARCHAR(50) NOT NULL COMMENT '姓名', + role VARCHAR(50) NOT NULL COMMENT '角色', + department VARCHAR(50) NOT NULL COMMENT '部门', + create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + FOREIGN KEY (project_id) REFERENCES project(project_id) ON DELETE CASCADE, + INDEX idx_project_id (project_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目成员表'; + +-- 创建项目里程碑表 +CREATE TABLE project_milestone ( + milestone_id VARCHAR(32) PRIMARY KEY COMMENT '里程碑ID', + project_id VARCHAR(32) NOT NULL COMMENT '项目ID', + name VARCHAR(200) NOT NULL COMMENT '名称', + planned_date DATE NOT NULL COMMENT '计划日期', + actual_date DATE COMMENT '实际日期', + status VARCHAR(20) NOT NULL DEFAULT 'NOT_STARTED' COMMENT '状态', + create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + FOREIGN KEY (project_id) REFERENCES project(project_id) ON DELETE CASCADE, + INDEX idx_project_id (project_id), + INDEX idx_status (status) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目里程碑表'; + +-- 创建项目风险表 +CREATE TABLE project_risk ( + risk_id VARCHAR(32) PRIMARY KEY COMMENT '风险ID', + project_id VARCHAR(32) NOT NULL COMMENT '项目ID', + description TEXT NOT NULL COMMENT '描述', + level VARCHAR(20) NOT NULL COMMENT '等级', + measure TEXT NOT NULL COMMENT '应对措施', + create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + FOREIGN KEY (project_id) REFERENCES project(project_id) ON DELETE CASCADE, + INDEX idx_project_id (project_id), + INDEX idx_level (level) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目风险表'; + +-- 创建项目历史记录表 +CREATE TABLE project_history ( + history_id VARCHAR(32) PRIMARY KEY COMMENT '历史记录ID', + project_id VARCHAR(32) NOT NULL COMMENT '项目ID', + project_no VARCHAR(20) NOT NULL COMMENT '项目编号', + operation_type VARCHAR(20) NOT NULL COMMENT '操作类型', + operator VARCHAR(50) NOT NULL COMMENT '操作人', + operation_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间', + field_name VARCHAR(100) NOT NULL COMMENT '字段名', + old_value TEXT COMMENT '修改前值', + new_value TEXT COMMENT '修改后值', + FOREIGN KEY (project_id) REFERENCES project(project_id) ON DELETE CASCADE, + INDEX idx_project_id (project_id), + INDEX idx_project_no (project_no), + INDEX idx_operation_time (operation_time), + INDEX idx_operator (operator) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目历史记录表'; + +-- 创建操作日志表 +CREATE TABLE operation_log ( + log_id VARCHAR(32) PRIMARY KEY COMMENT '日志ID', + user_id VARCHAR(32) NOT NULL COMMENT '用户ID', + username VARCHAR(50) NOT NULL COMMENT '用户名', + operation VARCHAR(100) NOT NULL COMMENT '操作内容', + ip_address VARCHAR(50) COMMENT 'IP地址', + user_agent VARCHAR(500) COMMENT '用户代理', + operation_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间', + FOREIGN KEY (user_id) REFERENCES sys_user(user_id) ON DELETE CASCADE, + INDEX idx_user_id (user_id), + INDEX idx_operation_time (operation_time) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='操作日志表'; +``` + +### 5.2 初始化数据脚本 + +```sql +-- 插入管理员用户 +INSERT INTO sys_user (user_id, username, password, real_name, department, role, status) +VALUES ('admin001', 'admin', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EH', '系统管理员', 'ADMIN', 'ADMIN', 1); + +-- 插入市场部用户 +INSERT INTO sys_user (user_id, username, password, real_name, department, role, status) +VALUES ('marketing001', 'marketing', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EH', '市场部经理', 'MARKETING', 'MARKETING', 1); + +-- 插入其他部门用户 +INSERT INTO sys_user (user_id, username, password, real_name, department, role, status) +VALUES ('other001', 'other', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iAt6Z5EH', '技术部经理', 'TECHNOLOGY', 'OTHER', 1); +``` + +--- + +## 6. 数据库性能优化建议 + +### 6.1 索引优化 +- 为常用查询字段创建索引 +- 避免在索引列上进行函数操作 +- 定期分析和优化索引 + +### 6.2 查询优化 +- 避免使用SELECT * +- 合理使用JOIN +- 使用分页查询减少数据传输量 +- 对大表进行分区处理 + +### 6.3 存储优化 +- 定期清理历史数据 +- 对大文本字段考虑单独存储 +- 使用合适的数据类型减少存储空间 + +### 6.4 备份策略 +- 每日全量备份 +- 每小时增量备份 +- 定期测试备份恢复 + +--- + +## 7. 数据库安全建议 + +### 7.1 访问控制 +- 使用最小权限原则 +- 定期修改数据库密码 +- 限制数据库访问IP + +### 7.2 数据加密 +- 敏感字段加密存储 +- 使用SSL连接数据库 +- 定期更新加密算法 + +### 7.3 审计日志 +- 记录所有数据库操作 +- 定期审计日志 +- 异常操作及时报警 + +--- + +## 8. 附录 + +### 8.1 变更记录 +| 版本 | 日期 | 修改人 | 修改内容 | +|------|------|--------|----------| +| V1.0 | 2026-01-26 | - | 初始版本创建 | diff --git a/xsl_nodes_mask.py b/xsl_nodes_mask.py deleted file mode 100644 index d491a01..0000000 --- a/xsl_nodes_mask.py +++ /dev/null @@ -1,110 +0,0 @@ -import numpy as np -import scipy.ndimage -import torch -import comfy.utils -import node_helpers -import folder_paths -import random - -import nodes -from nodes import MAX_RESOLUTION - -def composite(destination, source, x, y, mask = None, multiplier = 8, resize_source = False): - source = source.to(destination.device) - if resize_source: - source = torch.nn.functional.interpolate(source, size=(destination.shape[2], destination.shape[3]), mode="bilinear") - - source = comfy.utils.repeat_to_batch_size(source, destination.shape[0]) - - x = max(-source.shape[3] * multiplier, min(x, destination.shape[3] * multiplier)) - y = max(-source.shape[2] * multiplier, min(y, destination.shape[2] * multiplier)) - - left, top = (x // multiplier, y // multiplier) - right, bottom = (left + source.shape[3], top + source.shape[2],) - - if mask is None: - mask = torch.ones_like(source) - else: - mask = mask.to(destination.device, copy=True) - mask = torch.nn.functional.interpolate(mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])), size=(source.shape[2], source.shape[3]), mode="bilinear") - mask = comfy.utils.repeat_to_batch_size(mask, source.shape[0]) - - # calculate the bounds of the source that will be overlapping the destination - # this prevents the source trying to overwrite latent pixels that are out of bounds - # of the destination - visible_width, visible_height = (destination.shape[3] - left + min(0, x), destination.shape[2] - top + min(0, y),) - - mask = mask[:, :, :visible_height, :visible_width] - inverse_mask = torch.ones_like(mask) - mask - - source_portion = mask * source[:, :, :visible_height, :visible_width] - destination_portion = inverse_mask * destination[:, :, top:bottom, left:right] - - destination[:, :, top:bottom, left:right] = source_portion + destination_portion - return destination - - - -class MyMaskComposite: - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "destination": ("MASK",), - "source": ("MASK",), - "x": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}), - "y": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}), - "offset": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}), - "operation": (["multiply", "add", "subtract", "and", "or", "xor"],), - } - } - - CATEGORY = "mask" - - RETURN_TYPES = ("MASK",) - - FUNCTION = "combine" - - def combine(self, destination, source, x, y, offset, operation): - output = destination.reshape((-1, destination.shape[-2], destination.shape[-1])).clone() - source = source.reshape((-1, source.shape[-2], source.shape[-1])) - print(source.shape) # 输出数组的形状 - - left, top = (x, y,) - right, bottom = (min(left + source.shape[-1], destination.shape[-1]), min(top + source.shape[-2], destination.shape[-2])) - visible_width, visible_height = (right - left, bottom - top,) - - source[:, :offset, :] = 0 - - source_portion = source[:, :visible_height, :visible_width] - destination_portion = output[:, top:bottom, left:right] - - print(f"left:{left} top:{top} right:{right} bottom:{bottom} visible_width:{visible_width} visible_height:{visible_height}") - - if operation == "multiply": - output[:, top:bottom, left:right] = destination_portion * source_portion - elif operation == "add": - output[:, top:bottom, left:right] = destination_portion + source_portion - elif operation == "subtract": - output[:, top:bottom, left:right] = destination_portion - source_portion - elif operation == "and": - output[:, top:bottom, left:right] = torch.bitwise_and(destination_portion.round().bool(), source_portion.round().bool()).float() - elif operation == "or": - output[:, top:bottom, left:right] = torch.bitwise_or(destination_portion.round().bool(), source_portion.round().bool()).float() - elif operation == "xor": - output[:, top:bottom, left:right] = torch.bitwise_xor(destination_portion.round().bool(), source_portion.round().bool()).float() - - output = torch.clamp(output, 0.0, 1.0) - - return (output,) - - - - -NODE_CLASS_MAPPINGS = { - "MyMaskComposite": MyMaskComposite, -} - -NODE_DISPLAY_NAME_MAPPINGS = { - "MyMaskComposite": "MyMaskComposite", -}