# portal-server **Repository Path**: smart-portal/portal-server ## Basic Information - **Project Name**: portal-server - **Description**: 服务端 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2025-11-10 - **Last Updated**: 2026-02-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Portal Server - 会员管理系统 基于 Koa + TypeScript + MySQL + Redis 的会员管理系统后端服务。 **前端架构**:单页应用(SPA),包含 Web 端(前台用户)和 Admin 端(后台管理)两个路由模块。 ## 技术栈 - **框架**: Koa 2.x - **语言**: TypeScript - **数据库**: MySQL - **缓存**: Redis - **认证**: JWT + Redis Token管理 ## 功能特性 - ✅ 完整的用户认证和授权系统,支持 Redis Token 管理 - 登录验证码功能:SVG图片验证码,5分钟有效期,一次性使用 - ✅ 登录用户管理(增删改查) - 默认管理员用户:`admin`(不可删除、不可改名、不可禁用) - 密码修改功能:支持用户修改密码,权限控制 - admin用户只能修改自己的密码 - 系统管理员权限用户可以修改任何用户的密码 - 普通用户只能修改自己的密码 - ✅ 权限管理(增删改查) - 默认权限:系统管理员(不可删除、代码不可修改) - ✅ 会员管理(增删改查) - 会员数据隔离(按用户,通过 `user_id` 字段实现) - ✅ 会员等级管理(增删改查) - 等级数据隔离(按用户,通过 `user_id` 字段实现) - 每个用户只能管理自己的等级 - 等级列表不分页,直接返回当前用户的所有等级 - ✅ 通知管理(增删改查) - 后台管理:管理员创建、编辑、删除通知 - 推送功能:管理员可以推送通知给所有用户或指定用户 - 前台用户:用户只能查看推送给自己的通知 - 已读状态管理(通过关系表维护) - 支持查询未读通知 - 支持标记单个或全部通知为已读 - 通知推送记录表(`notification_pushes`):记录推送历史 - 通知已读关系表(`notification_reads`):记录已读状态 - ✅ 博客管理(增删改查) - 博客数据隔离(按用户,通过 `user_id` 字段实现) - 支持博客发布/取消发布功能 - 支持分类和标签管理(通过统一关系表 `blog_relations` 维护多对多关系) - 支持封面图和摘要 - 支持按标题、分类、状态等条件搜索和过滤 - **性能优化**:提供初始化接口 `/api/blogs/init`,一次请求获取博客列表、分类列表、标签列表,减少前端请求次数 - ✅ 数据库备份管理(基于 mysql2,无需外部工具) - 创建备份、删除备份、查询备份列表 - 下载备份文件 - ✅ 日志管理(查询、统计、删除旧日志) - ✅ 服务健康检查(Koa、MySQL、Redis) - ✅ 统一的API响应格式 - ✅ 完整的日志系统(持久化到MySQL) - API请求日志(成功、失败、超时) - Koa服务日志(启动、关闭、异常) - MySQL操作日志(连接、查询、错误) - Redis操作日志(连接、命令、错误) - 错误日志(未捕获异常、Promise拒绝) - ✅ 基于日志的超频请求检测和IP冷却限制(检测到超频请求后,对IP进行1分钟冷却限制) - ✅ 自动注入 X-Request-Id,支持全链路追踪 - ✅ 模块化中间件设计 - ✅ 所有API使用POST方法和JSON格式 - ✅ 自动路由注册(基于文件命名) ## 项目结构 ``` portal-server/ ├── src/ │ ├── config/ # 配置文件 │ │ ├── index.ts # 主配置 │ │ └── responseCode.ts # 响应码配置 │ ├── database/ # 数据库相关 │ │ ├── mysql.ts # MySQL连接 │ │ ├── redis.ts # Redis连接 │ │ ├── models.ts # 数据模型 │ │ └── schema.sql # 数据库表结构 │ ├── middleware/ # 中间件 │ │ ├── auth.ts # 认证中间件 │ │ ├── staticFiles.ts # 静态文件服务中间件(路由处理、SPA fallback、404处理) │ │ ├── errorHandler.ts # 错误处理 │ │ ├── jsonBody.ts # JSON Body验证 │ │ ├── logger.ts # 日志记录 │ │ ├── rateLimit.ts # 速率限制(已关闭) │ │ ├── abnormalDetection.ts # 异常检测和IP冷却限制 │ │ └── requestId.ts # Request ID注入 │ ├── controllers/ # 控制器 │ │ ├── auth.controller.ts │ │ ├── user.controller.ts │ │ ├── permission.controller.ts │ │ ├── member.controller.ts │ │ ├── memberLevel.controller.ts │ │ ├── backup.controller.ts │ │ ├── health.controller.ts │ │ ├── log.controller.ts │ │ ├── notification.controller.ts │ │ └── blog.controller.ts │ ├── routes/ # 路由 │ │ ├── auth.routes.ts │ │ ├── user.routes.ts │ │ ├── permission.routes.ts │ │ ├── member.routes.ts │ │ ├── memberLevel.routes.ts │ │ ├── backup.routes.ts │ │ ├── health.routes.ts │ │ ├── log.routes.ts │ │ ├── notification.routes.ts │ │ ├── blog.routes.ts │ │ ├── tag.routes.ts │ │ ├── category.routes.ts │ │ └── index.ts │ ├── utils/ # 工具函数 │ │ ├── logger.ts # 日志工具 │ │ ├── password.ts # 密码加密 │ │ ├── response.ts # 响应工具 │ │ ├── utils.ts # 统一工具函数(cross-env-plugins) │ │ └── captcha.ts # 验证码生成和验证 │ └── app.ts # 应用入口 ├── doc/ # 文档目录 ├── public/ # 静态文件目录(前端SPA) │ ├── web/ # Web端路由(前台用户,访问路径:/web/) │ └── admin/ # Admin端路由(后台管理,访问路径:/admin/) ├── .env # 默认环境配置 ├── .env.dev # 开发环境配置 ├── .env.prd # 生产环境配置 ├── package.json ├── tsconfig.json └── README.md ``` ## 快速开始 ### 1. 安装依赖 ```bash npm install ``` ### 2. 配置环境变量 创建环境配置文件并根据实际情况修改: **开发环境配置** (`.env.dev`): ```env NODE_ENV=dev PORT=3000 MYSQL_HOST=localhost MYSQL_PORT=3306 MYSQL_USER=root MYSQL_PASSWORD=your_password MYSQL_DATABASE=member_management_dev REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASSWORD= JWT_SECRET=dev-secret-key ``` **生产环境配置** (`.env.prd`): ```env NODE_ENV=prd PORT=3000 MYSQL_HOST=localhost MYSQL_PORT=3306 MYSQL_USER=root MYSQL_PASSWORD=your_password MYSQL_DATABASE=member_management REDIS_HOST=localhost REDIS_PORT=6379 REDIS_PASSWORD= JWT_SECRET=change-this-in-production ``` **注意**: 生产环境必须修改 `JWT_SECRET` 为强密钥! ### 3. 初始化数据库 使用以下命令初始化数据库(会自动创建数据库、表结构和默认管理员用户): **开发环境:** ```bash # 正常初始化(如果表已存在则跳过) npm run init-db # 强制初始化(删除所有表后重新创建) npm run init-db:force ``` **生产环境:** ```bash # 正常初始化 npm run init-db:prd # 强制初始化 npm run init-db:prd:force ``` **默认管理员账号:** - 用户名: `admin` - 密码: `Admin123.` - 权限: 系统管理员(自动关联) **默认权限:** - 名称: 系统管理员 - 代码: `SYSTEM_ADMIN` - 状态: 不可删除、代码不可修改 **保护机制:** - `admin` 用户:不可删除、不可改名、不可禁用 - `SYSTEM_ADMIN` 权限:不可删除、代码不可修改 - `admin` 用户始终拥有系统管理员权限 **注意**: - 初始化脚本会自动创建数据库(如果不存在) - 正常模式下,如果表已存在会跳过创建 - 强制模式下,会删除所有现有表后重新创建 - 如果admin用户已存在,正常模式会跳过创建,强制模式会更新用户信息 - 首次登录后建议立即修改管理员密码 - 结构同步与导出:运行 init-db 时会先根据 `src/database/schema.sql` 同步数据库(补齐缺失字段和索引),随后从“数据库的最终结构”反向导出并覆盖写回 `src/database/schema.sql`,以确保文件与实际结构一致;请避免同时在数据库和文件里各自修改导致冲突,建议以 `schema.sql` 为唯一源头修改。 ### 4. 启动项目 **开发环境:** ```bash npm run dev ``` **生产环境:** ```bash npm run dev:prd ``` ### 5. 打包项目 **生产环境打包:** ```bash npm run build ``` **开发环境打包:** ```bash npm run build:dev ``` ### 6. 启动构建后的项目 **生产环境启动:** ```bash # 先打包 npm run build # 再启动 npm start ``` **开发环境启动:** ```bash # 先打包 npm run build:dev # 再启动 npm run start:dev ``` **注意**: - `npm start` 默认使用生产环境配置(NODE_ENV=prd) - `npm run start:dev` 使用开发环境配置(NODE_ENV=dev) - 启动前确保已执行 `npm run build` 或 `npm run build:dev` ## API 文档 详细的API文档请查看 [doc/API文档.md](./doc/API文档.md) ## 数据库设计 所有表的ID都使用UUID生成,不使用数据库外键约束,通过应用层维护数据完整性。 ### 表关系维护方式 - **用户 ↔ 会员**:通过 `members.user_id` 字段直接维护(一对多关系) - **用户 ↔ 权限**:通过 `user_permissions` 关系表维护(多对多关系) - **用户 ↔ 会员等级**:通过 `member_levels.user_id` 字段直接维护(一对多关系) - **会员 ↔ 等级**:通过 `member_level_relations` 关系表维护(多对多关系) - **用户 ↔ 通知推送**:通过 `notification_pushes` 关系表维护(多对多关系) - **用户 ↔ 通知已读**:通过 `notification_reads` 关系表维护(多对多关系) - **用户 ↔ 博客**:通过 `blogs.user_id` 字段直接维护(一对多关系) - **用户 ↔ 标签**:通过 `tags.user_id` 字段直接维护(一对多关系) - **用户 ↔ 分类**:通过 `categories.user_id` 字段直接维护(一对多关系) - **博客 ↔ 标签/分类**:通过 `blog_relations` 统一关系表维护(多对多关系) ### 主要表结构 - `users` - 登录用户表 - `permissions` - 权限表 - `user_permissions` - 用户权限关系表 - `members` - 会员表(包含 `user_id` 字段关联用户) - `member_levels` - 会员等级表(包含 `user_id` 字段关联用户) - `member_level_relations` - 会员等级关系表 - `notifications` - 通知表 - `notification_pushes` - 通知推送记录表 - `notification_reads` - 通知已读关系表 - `blogs` - 博客表(包含 `user_id` 字段关联用户) - `tags` - 标签表(包含 `user_id` 字段关联用户) - `categories` - 分类表(包含 `user_id` 字段关联用户) - `blog_relations` - 博客关系表(统一维护博客与标签、分类的多对多关系) - `database_backups` - 数据库备份表(记录相对路径) - `logs` - 日志表(优化索引,支持快速查询和多条件组合查询) 详细表结构请查看 `src/database/schema.sql` ### 数据库迁移 项目提供了数据库迁移脚本,用于优化现有数据库的性能: **索引优化迁移**: - `src/database/migrations/optimize_logs_indexes.sql` - 优化 logs 表的索引(已包含在 schema.sql 中) - `src/database/migrations/add_logs_search_indexes.sql` - 为 logs 表添加基于搜索条件的复合索引 **执行迁移**: ```bash # 在 MySQL 中执行迁移脚本 mysql -u root -p your_database < src/database/migrations/add_logs_search_indexes.sql ``` **注意**: - 迁移脚本会为 logs 表添加多个复合索引,提升多条件查询性能 - 建议在业务低峰期执行,因为创建索引会锁定表 - 如果索引已存在,执行会报错,可以忽略或手动注释掉对应的语句 - 新建数据库时,schema.sql 已包含所有索引,无需执行迁移 ### 数据库备份 备份功能完全基于 `mysql2` 依赖,无需外部 MySQL 客户端工具: - 自动获取所有表结构和数据 - 生成标准 SQL 备份文件 - 支持跨平台(Windows/Linux/Mac) - 备份文件记录相对路径,便于项目迁移 - 支持下载备份文件(通过API接口) ## 业务逻辑 - **登录验证码**: - 登录前必须先获取验证码 - 验证码有效期5分钟,过期需重新获取 - 验证码一次性使用,验证成功后自动删除 - 验证码忽略大小写 - **数据隔离**: - 登录用户A和登录用户B的会员数据完全隔离,即使有重复的会员,ID也不一样 - **关系维护**: - 用户和会员:通过 `members` 表中的 `user_id` 字段直接维护(一对多关系,一个用户可以有多个会员) - 用户和权限:通过 `user_permissions` 关系表维护(多对多关系,默认一对一) - 会员和会员等级:通过 `member_level_relations` 关系表维护(多对多关系,默认一对一) - **安全机制**: - 所有用户密码都经过bcrypt加密存储 - `admin` 用户和 `SYSTEM_ADMIN` 权限受系统保护,不可删除或修改关键属性 - **密码修改权限规则**: - admin用户只能修改自己的密码 - 系统管理员权限用户可以修改任何用户的密码(包括admin用户) - 普通用户只能修改自己的密码 - **日志记录**: - 登录后的所有API请求自动记录用户ID到日志系统 ## 响应格式 所有API响应统一格式: ```json { "code": 200, "msg": "成功", "data": {}, "requestId": "uuid" } ``` 响应码定义在 `src/config/responseCode.ts` ## 认证方式 ### 登录流程 1. **获取验证码**:调用 `POST /api/auth/captcha` 获取验证码ID和SVG图片 2. **用户登录**:调用 `POST /api/auth/login`,传递用户名、密码、验证码ID和验证码 3. **获取Token**:登录成功后返回Token,后续请求需要携带此Token ### Token使用 所有需要认证的API需要在请求头中携带Token: ``` Authorization: Bearer ``` 或者在请求体中传递: ```json { "token": "" } ``` ### 验证码说明 - **类型**:SVG图片验证码,4位字符 - **有效期**:5分钟 - **使用方式**:一次性使用,验证成功后自动删除 - **验证规则**:忽略大小写 - **存储**:使用Redis存储,支持分布式部署 ## 超频请求检测和IP冷却限制 系统基于日志自动检测超频请求,并对超频IP进行冷却限制: ### 超频请求定义 - 同一个IP在同一个API路径下,60秒内请求超过60次 ### 检测机制 - **实时计数**:系统在请求完成后实时记录每个IP+API路径的请求计数 - **滑动窗口**:使用60秒滑动时间窗口进行计数 - **独立计数**:每个IP+API路径组合独立计数,互不影响 - **Redis存储**:使用Redis存储计数和冷却状态,支持分布式部署 - **自动触发**:当检测到超频请求时,自动对该IP进行1分钟冷却限制 ### 冷却机制 - **触发条件**:当同一IP在同一API路径下,60秒内请求超过60次时触发 - **冷却时间**:检测到超频请求后,自动对该IP进行1分钟(60秒)冷却限制 - **冷却范围**:冷却限制针对整个IP,而非单个API路径 - **拒绝响应**:冷却期内的IP请求会被拒绝,返回 `429 Too Many Requests` 错误 - **自动恢复**:冷却期结束后,IP可正常访问,计数重新开始 ### 优势 - 更智能:只限制超频请求的IP,不影响正常用户 - 更灵活:基于实际请求频率动态调整 - 更安全:有效防止恶意高频请求和API滥用 ## 日志系统 日志持久化到MySQL的`logs`表中,支持: ### 日志类型 - **API日志**:记录所有API请求(成功、失败、超时),包含请求/响应信息、用户ID、IP地址等 - **Koa服务日志**:记录服务启动、关闭、未捕获异常等 - **MySQL日志**:记录数据库连接、查询执行、错误等 - **Redis日志**:记录缓存连接、命令执行、错误等 - **错误日志**:记录所有错误信息 - **其他日志**:其他类型的日志 ### 日志特性 - 所有日志都关联`requestId`,支持全链路追踪 - 登录后的API请求自动记录用户ID - 支持按类型、级别、用户、请求ID、时间范围等条件查询 - 支持日志统计和旧日志清理 - 优化的数据库索引,支持快速查询 ### 日志管理接口 - `POST /api/logs/list` - 查询日志列表(支持分页和多条件筛选) - `POST /api/logs/detail` - 查询日志详情 - `POST /api/logs/delete-old` - 删除旧日志(按天数) - `POST /api/logs/stats` - 获取日志统计信息 - `POST /api/logs/abnormal` - 筛选超频请求(支持按IP、用户ID、时间范围、URL等条件筛选) ### 性能优化 - 复合索引优化常见查询场景(支持 type、level、userId、requestId 等条件组合) - 分页限制(每页最多100条) - 智能计数(只在需要时查询总数) - 建议定期清理旧日志,保持表大小合理 - 数据库索引优化:为 logs 表添加了基于搜索条件的复合索引,提升多条件查询性能 ## 开发规范 - 所有API都使用POST方法 - 所有API请求和响应都使用JSON格式 - 所有表ID使用UUID生成 - 所有表关系通过关系表维护,不使用外键 - 业务模块化,每个模块独立路由维护 - **所有源代码文件使用 UTF-8 编码(无 BOM)** ## 文件编码 项目所有源代码文件均使用 **UTF-8 编码(无 BOM)**,确保跨平台兼容性。 ### 检查文件编码 使用 PowerShell 脚本检查文件编码: ```powershell .\scripts\checkEncoding.ps1 ``` ### 转换文件为 UTF-8 如果发现非 UTF-8 编码的文件,可以使用脚本转换: ```powershell .\scripts\convertToUtf8.ps1 ``` ## 许可证 MIT