# 后端管理开发文档 ## 1. 项目概述 ### 1.1 项目简介 解班客后端管理系统是一个基于Node.js的企业级后端服务,为管理后台、小程序和官网提供统一的API服务和数据管理功能。 ### 1.2 技术栈 - **运行环境**:Node.js 18.x - **开发框架**:Express.js 4.x - **开发语言**:TypeScript 5.x - **数据库**:MySQL 8.0 + Redis 7.x - **ORM框架**:TypeORM 0.3.x - **认证授权**:JWT + RBAC - **文件存储**:阿里云OSS - **消息队列**:Redis + Bull - **日志系统**:Winston - **API文档**:Swagger/OpenAPI - **测试框架**:Jest + Supertest - **代码规范**:ESLint + Prettier ### 1.3 项目结构 ``` backend/ ├── src/ │ ├── controllers/ # 控制器 │ │ ├── admin/ # 管理后台控制器 │ │ ├── app/ # 小程序控制器 │ │ └── common/ # 通用控制器 │ ├── services/ # 业务服务层 │ │ ├── user/ # 用户服务 │ │ ├── travel/ # 旅行服务 │ │ ├── animal/ # 动物服务 │ │ ├── order/ # 订单服务 │ │ └── common/ # 通用服务 │ ├── models/ # 数据模型 │ │ ├── entities/ # 实体定义 │ │ ├── dto/ # 数据传输对象 │ │ └── vo/ # 视图对象 │ ├── middleware/ # 中间件 │ │ ├── auth/ # 认证中间件 │ │ ├── validation/ # 验证中间件 │ │ └── common/ # 通用中间件 │ ├── utils/ # 工具函数 │ │ ├── database/ # 数据库工具 │ │ ├── cache/ # 缓存工具 │ │ ├── file/ # 文件处理 │ │ └── common/ # 通用工具 │ ├── config/ # 配置文件 │ │ ├── database.ts # 数据库配置 │ │ ├── redis.ts # Redis配置 │ │ └── app.ts # 应用配置 │ ├── routes/ # 路由定义 │ │ ├── admin/ # 管理后台路由 │ │ ├── app/ # 小程序路由 │ │ └── common/ # 通用路由 │ ├── jobs/ # 定时任务 │ ├── migrations/ # 数据库迁移 │ ├── seeds/ # 数据种子 │ ├── app.ts # 应用入口 │ └── server.ts # 服务器启动 ├── tests/ # 测试文件 │ ├── unit/ # 单元测试 │ ├── integration/ # 集成测试 │ └── e2e/ # 端到端测试 ├── docs/ # 文档 ├── scripts/ # 脚本文件 ├── .env.example # 环境变量示例 ├── package.json ├── tsconfig.json ├── jest.config.js └── README.md ``` ## 2. 开发环境搭建 ### 2.1 环境要求 - Node.js >= 18.0.0 - npm >= 9.0.0 或 yarn >= 1.22.0 - MySQL >= 8.0.0 - Redis >= 7.0.0 - Git >= 2.0.0 ### 2.2 环境搭建步骤 #### 2.2.1 克隆项目 ```bash git clone https://github.com/jiebanke/backend.git cd backend ``` #### 2.2.2 安装依赖 ```bash npm install # 或 yarn install ``` #### 2.2.3 配置环境变量 ```bash # 复制环境配置文件 cp .env.example .env.development cp .env.example .env.production # 编辑配置文件 vim .env.development ``` #### 2.2.4 数据库初始化 ```bash # 创建数据库 npm run db:create # 运行迁移 npm run migration:run # 运行种子数据 npm run seed:run ``` #### 2.2.5 启动开发服务器 ```bash npm run dev # 或 yarn dev ``` ### 2.3 开发工具配置 #### 2.3.1 VSCode配置 推荐安装以下插件: - TypeScript Importer - ESLint - Prettier - REST Client - MySQL - Redis #### 2.3.2 数据库工具 - MySQL Workbench - Navicat - DBeaver - Redis Desktop Manager ## 3. 开发计划与任务分解 ### 3.1 开发阶段划分 #### 阶段一:基础框架搭建(预计8个工作日) - 项目初始化和环境配置 - 数据库设计和迁移 - 基础中间件开发 - 认证授权系统 #### 阶段二:核心业务开发(预计25个工作日) - 用户管理系统 - 旅行结伴功能 - 动物认领功能 - 订单支付系统 #### 阶段三:管理功能开发(预计15个工作日) - 管理后台API - 数据统计分析 - 系统配置管理 - 日志审计功能 #### 阶段四:优化和部署(预计10个工作日) - 性能优化 - 安全加固 - 测试和修复 - 部署和监控 ### 3.2 详细任务分解 #### 3.2.1 阶段一:基础框架搭建 ##### 任务1.1:项目初始化(2个工作日) **负责人**:后端架构师 + 后端开发工程师 **工时估算**:16人时 **任务描述**: - 创建Express.js项目结构 - 配置TypeScript和构建工具 - 设置代码规范和Git hooks - 配置开发环境 **具体子任务**: 1. 创建Express + TypeScript项目(4人时) 2. 配置ESLint、Prettier和Git hooks(4人时) 3. 设置环境变量和配置管理(4人时) 4. 配置开发和构建脚本(4人时) **验收标准**: - 项目可以正常启动和热重载 - 代码规范检查通过 - TypeScript编译无错误 ##### 任务1.2:数据库设计和迁移(3个工作日) **负责人**:后端开发工程师 + 数据库工程师 **工时估算**:24人时 **任务描述**: - 设计数据库表结构 - 创建TypeORM实体 - 编写数据库迁移 - 创建种子数据 **具体子任务**: 1. 设计核心业务表结构(8人时) 2. 创建TypeORM实体和关系(8人时) 3. 编写数据库迁移脚本(4人时) 4. 创建测试和演示数据(4人时) **验收标准**: - 数据库表结构完整 - 实体关系正确 - 迁移脚本可正常执行 ##### 任务1.3:基础中间件开发(2个工作日) **负责人**:后端开发工程师 **工时估算**:16人时 **任务描述**: - 请求日志中间件 - 错误处理中间件 - 参数验证中间件 - 跨域处理中间件 **具体子任务**: 1. 请求日志和响应时间中间件(4人时) 2. 全局错误处理中间件(4人时) 3. 请求参数验证中间件(4人时) 4. CORS和安全头中间件(4人时) **验收标准**: - 中间件功能完整 - 错误处理规范 - 日志记录准确 ##### 任务1.4:认证授权系统(1个工作日) **负责人**:后端开发工程师 **工时估算**:8人时 **任务描述**: - JWT认证实现 - RBAC权限控制 - 用户会话管理 - 权限验证中间件 **具体子任务**: 1. JWT生成和验证(3人时) 2. RBAC权限模型实现(3人时) 3. 权限验证中间件(2人时) **验收标准**: - JWT认证正常 - 权限控制精确 - 会话管理安全 #### 3.2.2 阶段二:核心业务开发 ##### 任务2.1:用户管理系统(6个工作日) **负责人**:后端开发工程师 **工时估算**:48人时 **任务描述**: - 用户注册登录 - 用户信息管理 - 实名认证功能 - 用户行为记录 **具体子任务**: 1. 微信登录和手机号登录(12人时) 2. 用户信息CRUD操作(10人时) 3. 实名认证流程和验证(12人时) 4. 用户行为日志记录(8人时) 5. 用户状态管理(6人时) **验收标准**: - 登录流程完整 - 用户信息管理功能齐全 - 实名认证流程正确 ##### 任务2.2:旅行结伴功能(7个工作日) **负责人**:后端开发工程师 **工时估算**:56人时 **任务描述**: - 旅行活动管理 - 参与申请处理 - 活动状态管理 - 地理位置服务 **具体子任务**: 1. 旅行活动CRUD操作(14人时) 2. 参与申请和审核流程(12人时) 3. 活动状态和生命周期管理(10人时) 4. 地理位置搜索和推荐(10人时) 5. 活动评价和反馈(10人时) **验收标准**: - 活动管理功能完整 - 申请流程顺畅 - 地理位置服务准确 ##### 任务2.3:动物认领功能(6个工作日) **负责人**:后端开发工程师 **工时估算**:48人时 **任务描述**: - 动物信息管理 - 认领申请处理 - 认领记录跟踪 - 动物状态更新 **具体子任务**: 1. 动物信息CRUD操作(12人时) 2. 认领申请和审核流程(12人时) 3. 认领记录和历史跟踪(10人时) 4. 动物状态和健康记录(8人时) 5. 认领动态和通知(6人时) **验收标准**: - 动物信息管理完整 - 认领流程清晰 - 状态跟踪准确 ##### 任务2.4:订单支付系统(6个工作日) **负责人**:后端开发工程师 **工时估算**:48人时 **任务描述**: - 订单创建和管理 - 支付接口集成 - 订单状态跟踪 - 退款处理功能 **具体子任务**: 1. 订单创建和状态管理(12人时) 2. 微信支付接口集成(15人时) 3. 支付回调和状态同步(10人时) 4. 退款申请和处理(8人时) 5. 订单查询和统计(3人时) **验收标准**: - 订单管理功能完整 - 支付流程稳定 - 退款处理正确 #### 3.2.3 阶段三:管理功能开发 ##### 任务3.1:管理后台API(5个工作日) **负责人**:后端开发工程师 **工时估算**:40人时 **任务描述**: - 管理员认证授权 - 用户管理接口 - 内容管理接口 - 系统配置接口 **具体子任务**: 1. 管理员登录和权限管理(10人时) 2. 用户管理相关接口(10人时) 3. 内容审核和管理接口(10人时) 4. 系统配置和参数管理(10人时) **验收标准**: - 管理接口功能完整 - 权限控制严格 - 数据操作安全 ##### 任务3.2:数据统计分析(4个工作日) **负责人**:后端开发工程师 **工时估算**:32人时 **任务描述**: - 用户数据统计 - 业务数据分析 - 财务数据报表 - 实时数据监控 **具体子任务**: 1. 用户增长和活跃度统计(10人时) 2. 业务数据分析和报表(10人时) 3. 财务收支统计(8人时) 4. 实时数据监控接口(4人时) **验收标准**: - 统计数据准确 - 报表生成正确 - 实时监控有效 ##### 任务3.3:系统配置管理(3个工作日) **负责人**:后端开发工程师 **工时估算**:24人时 **任务描述**: - 系统参数配置 - 字典数据管理 - 配置缓存机制 - 配置变更通知 **具体子任务**: 1. 系统参数CRUD操作(8人时) 2. 字典数据管理接口(6人时) 3. 配置缓存和更新机制(6人时) 4. 配置变更通知和日志(4人时) **验收标准**: - 配置管理功能完整 - 缓存机制有效 - 变更通知及时 ##### 任务3.4:日志审计功能(3个工作日) **负责人**:后端开发工程师 **工时估算**:24人时 **任务描述**: - 操作日志记录 - 审计日志查询 - 日志分析统计 - 日志归档清理 **具体子任务**: 1. 操作日志记录和存储(8人时) 2. 审计日志查询接口(8人时) 3. 日志分析和统计(4人时) 4. 日志归档和清理机制(4人时) **验收标准**: - 日志记录完整 - 查询功能强大 - 归档机制有效 #### 3.2.4 阶段四:优化和部署 ##### 任务4.1:性能优化(3个工作日) **负责人**:后端开发工程师 **工时估算**:24人时 **任务描述**: - 数据库查询优化 - 缓存策略优化 - 接口性能优化 - 并发处理优化 **具体子任务**: 1. SQL查询优化和索引调整(8人时) 2. Redis缓存策略优化(6人时) 3. 接口响应时间优化(6人时) 4. 并发处理和连接池优化(4人时) **验收标准**: - 查询性能提升50% - 缓存命中率>80% - 接口响应时间<500ms ##### 任务4.2:安全加固(2个工作日) **负责人**:后端开发工程师 **工时估算**:16人时 **任务描述**: - 输入验证加强 - SQL注入防护 - XSS攻击防护 - 接口限流保护 **具体子任务**: 1. 输入参数验证和过滤(6人时) 2. SQL注入和XSS防护(4人时) 3. 接口限流和防刷机制(4人时) 4. 敏感数据加密存储(2人时) **验收标准**: - 安全漏洞修复 - 防护机制有效 - 敏感数据安全 ##### 任务4.3:测试和修复(3个工作日) **负责人**:后端开发工程师 + 测试工程师 **工时估算**:24人时 **任务描述**: - 单元测试编写 - 集成测试执行 - 性能测试验证 - Bug修复和优化 **具体子任务**: 1. 单元测试编写和执行(10人时) 2. 集成测试和API测试(8人时) 3. 性能测试和压力测试(4人时) 4. Bug修复和代码优化(2人时) **验收标准**: - 测试覆盖率>80% - 集成测试通过 - 性能指标达标 ##### 任务4.4:部署和监控(2个工作日) **负责人**:后端开发工程师 + 运维工程师 **工时估算**:16人时 **任务描述**: - 生产环境部署 - 监控系统配置 - 日志收集配置 - 备份恢复机制 **具体子任务**: 1. Docker容器化和部署(6人时) 2. 监控和告警配置(4人时) 3. 日志收集和分析配置(4人时) 4. 数据备份和恢复测试(2人时) **验收标准**: - 部署流程自动化 - 监控系统正常 - 备份机制有效 ## 4. 开发规范 ### 4.1 代码规范 #### 4.1.1 项目结构规范 ```typescript // src/controllers/admin/user.controller.ts import { Request, Response } from 'express' import { UserService } from '@/services/user/user.service' import { CreateUserDto, UpdateUserDto, UserQueryDto } from '@/models/dto/user.dto' import { ApiResponse } from '@/utils/response' import { validateDto } from '@/middleware/validation' export class AdminUserController { private userService: UserService constructor() { this.userService = new UserService() } /** * 获取用户列表 */ async getUsers(req: Request, res: Response) { try { const query = req.query as UserQueryDto const result = await this.userService.getUsers(query) return ApiResponse.success(res, result, '获取用户列表成功') } catch (error) { return ApiResponse.error(res, error.message) } } /** * 创建用户 */ async createUser(req: Request, res: Response) { try { const createUserDto = await validateDto(CreateUserDto, req.body) const user = await this.userService.createUser(createUserDto) return ApiResponse.success(res, user, '创建用户成功') } catch (error) { return ApiResponse.error(res, error.message) } } /** * 更新用户 */ async updateUser(req: Request, res: Response) { try { const { id } = req.params const updateUserDto = await validateDto(UpdateUserDto, req.body) const user = await this.userService.updateUser(Number(id), updateUserDto) return ApiResponse.success(res, user, '更新用户成功') } catch (error) { return ApiResponse.error(res, error.message) } } /** * 删除用户 */ async deleteUser(req: Request, res: Response) { try { const { id } = req.params await this.userService.deleteUser(Number(id)) return ApiResponse.success(res, null, '删除用户成功') } catch (error) { return ApiResponse.error(res, error.message) } } } ``` #### 4.1.2 服务层规范 ```typescript // src/services/user/user.service.ts import { Repository } from 'typeorm' import { AppDataSource } from '@/config/database' import { User } from '@/models/entities/user.entity' import { CreateUserDto, UpdateUserDto, UserQueryDto } from '@/models/dto/user.dto' import { UserVo } from '@/models/vo/user.vo' import { PageResult } from '@/types/common' import { CacheService } from '@/utils/cache' import { LoggerService } from '@/utils/logger' export class UserService { private userRepository: Repository private cacheService: CacheService private logger: LoggerService constructor() { this.userRepository = AppDataSource.getRepository(User) this.cacheService = new CacheService() this.logger = new LoggerService('UserService') } /** * 获取用户列表 */ async getUsers(query: UserQueryDto): Promise> { try { const { page = 1, limit = 10, keyword, status } = query const queryBuilder = this.userRepository.createQueryBuilder('user') // 关键词搜索 if (keyword) { queryBuilder.andWhere( '(user.username LIKE :keyword OR user.email LIKE :keyword OR user.phone LIKE :keyword)', { keyword: `%${keyword}%` } ) } // 状态筛选 if (status !== undefined) { queryBuilder.andWhere('user.status = :status', { status }) } // 分页 queryBuilder .skip((page - 1) * limit) .take(limit) .orderBy('user.created_at', 'DESC') const [users, total] = await queryBuilder.getManyAndCount() // 转换为VO const userVos = users.map(user => new UserVo(user)) return { data: userVos, total, page, limit, pages: Math.ceil(total / limit) } } catch (error) { this.logger.error('获取用户列表失败', error) throw error } } /** * 根据ID获取用户 */ async getUserById(id: number): Promise { try { // 先从缓存获取 const cacheKey = `user:${id}` let user = await this.cacheService.get(cacheKey) if (!user) { // 缓存未命中,从数据库获取 user = await this.userRepository.findOne({ where: { id }, relations: ['profile', 'roles'] }) if (user) { // 存入缓存,过期时间30分钟 await this.cacheService.set(cacheKey, user, 1800) } } return user ? new UserVo(user) : null } catch (error) { this.logger.error('获取用户详情失败', error) throw error } } /** * 创建用户 */ async createUser(createUserDto: CreateUserDto): Promise { try { // 检查用户名是否已存在 const existingUser = await this.userRepository.findOne({ where: [ { username: createUserDto.username }, { email: createUserDto.email } ] }) if (existingUser) { throw new Error('用户名或邮箱已存在') } // 创建用户 const user = this.userRepository.create(createUserDto) const savedUser = await this.userRepository.save(user) this.logger.info('创建用户成功', { userId: savedUser.id }) return new UserVo(savedUser) } catch (error) { this.logger.error('创建用户失败', error) throw error } } /** * 更新用户 */ async updateUser(id: number, updateUserDto: UpdateUserDto): Promise { try { const user = await this.userRepository.findOne({ where: { id } }) if (!user) { throw new Error('用户不存在') } // 更新用户信息 Object.assign(user, updateUserDto) const updatedUser = await this.userRepository.save(user) // 清除缓存 await this.cacheService.del(`user:${id}`) this.logger.info('更新用户成功', { userId: id }) return new UserVo(updatedUser) } catch (error) { this.logger.error('更新用户失败', error) throw error } } /** * 删除用户 */ async deleteUser(id: number): Promise { try { const user = await this.userRepository.findOne({ where: { id } }) if (!user) { throw new Error('用户不存在') } // 软删除 await this.userRepository.softDelete(id) // 清除缓存 await this.cacheService.del(`user:${id}`) this.logger.info('删除用户成功', { userId: id }) } catch (error) { this.logger.error('删除用户失败', error) throw error } } } ``` #### 4.1.3 数据模型规范 ```typescript // src/models/entities/user.entity.ts import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, DeleteDateColumn, OneToOne, ManyToMany, JoinTable } from 'typeorm' import { UserProfile } from './user-profile.entity' import { Role } from './role.entity' @Entity('users') export class User { @PrimaryGeneratedColumn() id: number @Column({ unique: true, length: 50 }) username: string @Column({ unique: true, length: 100 }) email: string @Column({ length: 20, nullable: true }) phone: string @Column({ length: 255 }) password: string @Column({ type: 'tinyint', default: 1, comment: '状态:0-禁用,1-启用' }) status: number @Column({ type: 'datetime', nullable: true, comment: '最后登录时间' }) last_login_at: Date @CreateDateColumn() created_at: Date @UpdateDateColumn() updated_at: Date @DeleteDateColumn() deleted_at: Date // 关联关系 @OneToOne(() => UserProfile, profile => profile.user) profile: UserProfile @ManyToMany(() => Role, role => role.users) @JoinTable({ name: 'user_roles', joinColumn: { name: 'user_id', referencedColumnName: 'id' }, inverseJoinColumn: { name: 'role_id', referencedColumnName: 'id' } }) roles: Role[] } ``` ```typescript // src/models/dto/user.dto.ts import { IsString, IsEmail, IsOptional, IsNumber, MinLength, MaxLength } from 'class-validator' export class CreateUserDto { @IsString() @MinLength(3) @MaxLength(50) username: string @IsEmail() email: string @IsOptional() @IsString() @MaxLength(20) phone?: string @IsString() @MinLength(6) password: string } export class UpdateUserDto { @IsOptional() @IsString() @MinLength(3) @MaxLength(50) username?: string @IsOptional() @IsEmail() email?: string @IsOptional() @IsString() @MaxLength(20) phone?: string @IsOptional() @IsNumber() status?: number } export class UserQueryDto { @IsOptional() @IsNumber() page?: number @IsOptional() @IsNumber() limit?: number @IsOptional() @IsString() keyword?: string @IsOptional() @IsNumber() status?: number } ``` ```typescript // src/models/vo/user.vo.ts import { User } from '@/models/entities/user.entity' export class UserVo { id: number username: string email: string phone: string status: number last_login_at: Date created_at: Date updated_at: Date constructor(user: User) { this.id = user.id this.username = user.username this.email = user.email this.phone = user.phone this.status = user.status this.last_login_at = user.last_login_at this.created_at = user.created_at this.updated_at = user.updated_at } } ``` ### 4.2 API设计规范 #### 4.2.1 路由规范 ```typescript // src/routes/admin/user.routes.ts import { Router } from 'express' import { AdminUserController } from '@/controllers/admin/user.controller' import { authMiddleware } from '@/middleware/auth' import { permissionMiddleware } from '@/middleware/permission' const router = Router() const userController = new AdminUserController() // 用户管理路由 router.get( '/users', authMiddleware, permissionMiddleware('user:view'), userController.getUsers.bind(userController) ) router.get( '/users/:id', authMiddleware, permissionMiddleware('user:view'), userController.getUserById.bind(userController) ) router.post( '/users', authMiddleware, permissionMiddleware('user:create'), userController.createUser.bind(userController) ) router.put( '/users/:id', authMiddleware, permissionMiddleware('user:update'), userController.updateUser.bind(userController) ) router.delete( '/users/:id', authMiddleware, permissionMiddleware('user:delete'), userController.deleteUser.bind(userController) ) export default router ``` #### 4.2.2 响应格式规范 ```typescript // src/utils/response.ts import { Response } from 'express' export interface ApiResponseData { code: number message: string data: T timestamp: number } export class ApiResponse { /** * 成功响应 */ static success(res: Response, data: T, message = '操作成功'): Response { return res.json({ code: 200, message, data, timestamp: Date.now() }) } /** * 错误响应 */ static error(res: Response, message = '操作失败', code = 500): Response { return res.status(code).json({ code, message, data: null, timestamp: Date.now() }) } /** * 参数错误响应 */ static badRequest(res: Response, message = '参数错误'): Response { return this.error(res, message, 400) } /** * 未授权响应 */ static unauthorized(res: Response, message = '未授权访问'): Response { return this.error(res, message, 401) } /** * 禁止访问响应 */ static forbidden(res: Response, message = '禁止访问'): Response { return this.error(res, message, 403) } /** * 资源不存在响应 */ static notFound(res: Response, message = '资源不存在'): Response { return this.error(res, message, 404) } } ``` ### 4.3 数据库操作规范 #### 4.3.1 查询优化规范 ```typescript // src/services/travel/travel.service.ts export class TravelService { /** * 获取旅行活动列表(优化版本) */ async getTravelActivities(query: TravelQueryDto): Promise> { const { page = 1, limit = 10, city, start_date, end_date, status } = query // 使用QueryBuilder进行复杂查询 const queryBuilder = this.travelRepository .createQueryBuilder('travel') .leftJoinAndSelect('travel.creator', 'creator') .leftJoinAndSelect('travel.participants', 'participants') .select([ 'travel.id', 'travel.title', 'travel.description', 'travel.city', 'travel.start_date', 'travel.end_date', 'travel.max_participants', 'travel.status', 'travel.created_at', 'creator.id', 'creator.username', 'creator.avatar_url', 'participants.id' ]) // 城市筛选 if (city) { queryBuilder.andWhere('travel.city = :city', { city }) } // 日期范围筛选 if (start_date) { queryBuilder.andWhere('travel.start_date >= :start_date', { start_date }) } if (end_date) { queryBuilder.andWhere('travel.end_date <= :end_date', { end_date }) } // 状态筛选 if (status !== undefined) { queryBuilder.andWhere('travel.status = :status', { status }) } // 分页和排序 queryBuilder .skip((page - 1) * limit) .take(limit) .orderBy('travel.created_at', 'DESC') const [travels, total] = await queryBuilder.getManyAndCount() return { data: travels.map(travel => new TravelVo(travel)), total, page, limit, pages: Math.ceil(total / limit) } } /** * 批量更新操作 */ async batchUpdateTravelStatus(ids: number[], status: number): Promise { await this.travelRepository .createQueryBuilder() .update(Travel) .set({ status, updated_at: new Date() }) .where('id IN (:...ids)', { ids }) .execute() } } ``` #### 4.3.2 事务处理规范 ```typescript // src/services/order/order.service.ts import { DataSource } from 'typeorm' export class OrderService { constructor(private dataSource: DataSource) {} /** * 创建订单(事务处理) */ async createOrder(createOrderDto: CreateOrderDto): Promise { const queryRunner = this.dataSource.createQueryRunner() await queryRunner.connect() await queryRunner.startTransaction() try { // 1. 创建订单 const order = queryRunner.manager.create(Order, { ...createOrderDto, order_no: this.generateOrderNo(), status: OrderStatus.PENDING }) const savedOrder = await queryRunner.manager.save(order) // 2. 创建订单项 const orderItems = createOrderDto.items.map(item => queryRunner.manager.create(OrderItem, { ...item, order_id: savedOrder.id }) ) await queryRunner.manager.save(orderItems) // 3. 更新库存 for (const item of createOrderDto.items) { await queryRunner.manager.decrement( Product, { id: item.product_id }, 'stock', item.quantity ) } // 4. 记录操作日志 const log = queryRunner.manager.create(OperationLog, { user_id: createOrderDto.user_id, action: 'CREATE_ORDER', target_type: 'ORDER', target_id: savedOrder.id, details: JSON.stringify(createOrderDto) }) await queryRunner.manager.save(log) await queryRunner.commitTransaction() return new OrderVo(savedOrder) } catch (error) { await queryRunner.rollbackTransaction() throw error } finally { await queryRunner.release() } } } ``` ## 5. 质量保证 ### 5.1 测试策略 #### 5.1.1 单元测试 ```typescript // tests/unit/services/user.service.spec.ts import { UserService } from '@/services/user/user.service' import { User } from '@/models/entities/user.entity' import { CreateUserDto } from '@/models/dto/user.dto' describe('UserService', () => { let userService: UserService let mockUserRepository: any beforeEach(() => { mockUserRepository = { findOne: jest.fn(), create: jest.fn(), save: jest.fn(), softDelete: jest.fn(), createQueryBuilder: jest.fn() } userService = new UserService() userService['userRepository'] = mockUserRepository }) describe('createUser', () => { it('should create user successfully', async () => { const createUserDto: CreateUserDto = { username: 'testuser', email: 'test@example.com', password: 'password123' } const mockUser = { id: 1, ...createUserDto } mockUserRepository.findOne.mockResolvedValue(null) mockUserRepository.create.mockReturnValue(mockUser) mockUserRepository.save.mockResolvedValue(mockUser) const result = await userService.createUser(createUserDto) expect(result.username).toBe(createUserDto.username) expect(result.email).toBe(createUserDto.email) expect(mockUserRepository.save).toHaveBeenCalledWith(mockUser) }) it('should throw error when user already exists', async () => { const createUserDto: CreateUserDto = { username: 'testuser', email: 'test@example.com', password: 'password123' } mockUserRepository.findOne.mockResolvedValue({ id: 1 }) await expect(userService.createUser(createUserDto)) .rejects.toThrow('用户名或邮箱已存在') }) }) }) ``` #### 5.1.2 集成测试 ```typescript // tests/integration/controllers/user.controller.spec.ts import request from 'supertest' import { app } from '@/app' import { AppDataSource } from '@/config/database' describe('User Controller Integration Tests', () => { let authToken: string beforeAll(async () => { await AppDataSource.initialize() // 获取认证token const loginResponse = await request(app) .post('/api/auth/login') .send({ username: 'admin', password: 'admin123' }) authToken = loginResponse.body.data.access_token }) afterAll(async () => { await AppDataSource.destroy() }) describe('GET /api/admin/users', () => { it('should return user list', async () => { const response = await request(app) .get('/api/admin/users') .set('Authorization', `Bearer ${authToken}`) .expect(200) expect(response.body.code).toBe(200) expect(response.body.data).toHaveProperty('data') expect(response.body.data).toHaveProperty('total') expect(Array.isArray(response.body.data.data)).toBe(true) }) it('should return 401 without auth token', async () => { await request(app) .get('/api/admin/users') .expect(401) }) }) describe('POST /api/admin/users', () => { it('should create user successfully', async () => { const newUser = { username: 'newuser', email: 'newuser@example.com', password: 'password123' } const response = await request(app) .post('/api/admin/users') .set('Authorization', `Bearer ${authToken}`) .send(newUser) .expect(200) expect(response.body.code).toBe(200) expect(response.body.data.username).toBe(newUser.username) expect(response.body.data.email).toBe(newUser.email) }) }) }) ``` ### 5.2 代码质量检查 #### 5.2.1 ESLint配置 ```javascript // .eslintrc.js module.exports = { parser: '@typescript-eslint/parser', extends: [ '@typescript-eslint/recommended', 'prettier' ], plugins: ['@typescript-eslint'], rules: { '@typescript-eslint/no-unused-vars': 'error', '@typescript-eslint/explicit-function-return-type': 'warn', '@typescript-eslint/no-explicit-any': 'warn', 'prefer-const': 'error', 'no-var': 'error' } } ``` #### 5.2.2 代码审查流程 1. 开发者提交Pull Request 2. 自动化检查(ESLint、TypeScript、测试) 3. 同行代码审查 4. 技术负责人审查 5. 合并到主分支 ## 6. 部署和运维 ### 6.1 Docker配置 #### 6.1.1 Dockerfile ```dockerfile # Dockerfile FROM node:18-alpine WORKDIR /app # 复制package文件 COPY package*.json ./ # 安装依赖 RUN npm ci --only=production # 复制源代码 COPY . . # 构建应用 RUN npm run build # 暴露端口 EXPOSE 3000 # 启动应用 CMD ["npm", "start"] ``` #### 6.1.2 Docker Compose ```yaml # docker-compose.yml version: '3.8' services: app: build: . ports: - "3000:3000" environment: - NODE_ENV=production - DB_HOST=mysql - REDIS_HOST=redis depends_on: - mysql - redis volumes: - ./logs:/app/logs mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: rootpassword MYSQL_DATABASE: jiebanke MYSQL_USER: jiebanke MYSQL_PASSWORD: password ports: - "3306:3306" volumes: - mysql_data:/var/lib/mysql redis: image: redis:7-alpine ports: - "6379:6379" volumes: - redis_data:/data volumes: mysql_data: redis_data: ``` ### 6.2 CI/CD配置 #### 6.2.1 GitHub Actions ```yaml # .github/workflows/deploy.yml name: Deploy to Production on: push: branches: [ main ] jobs: test: runs-on: ubuntu-latest services: mysql: image: mysql:8.0 env: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: test options: >- --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 redis: image: redis:7-alpine options: >- --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' cache: 'npm' - name: Install dependencies run: npm ci - name: Run linting run: npm run lint - name: Run tests run: npm run test env: DB_HOST: localhost DB_PORT: 3306 DB_USERNAME: root DB_PASSWORD: root DB_DATABASE: test REDIS_HOST: localhost REDIS_PORT: 6379 deploy: needs: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build Docker image run: docker build -t jiebanke/backend:${{ github.sha }} . - name: Push to registry run: | echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin docker push jiebanke/backend:${{ github.sha }} - name: Deploy to server uses: appleboy/ssh-action@v0.1.5 with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_KEY }} script: | docker pull jiebanke/backend:${{ github.sha }} docker stop jiebanke-backend || true docker rm jiebanke-backend || true docker run -d --name jiebanke-backend \ -p 3000:3000 \ --env-file /opt/jiebanke/.env \ jiebanke/backend:${{ github.sha }} ``` ### 6.3 监控和日志 #### 6.3.1 日志配置 ```typescript // src/utils/logger.ts import winston from 'winston' import DailyRotateFile from 'winston-daily-rotate-file' export class LoggerService { private logger: winston.Logger constructor(private context: string) { this.logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ), defaultMeta: { service: 'jiebanke-backend', context: this.context }, transports: [ // 控制台输出 new winston.transports.Console({ format: winston.format.combine( winston.format.colorize(), winston.format.simple() ) }), // 错误日志文件 new DailyRotateFile({ filename: 'logs/error-%DATE%.log', datePattern: 'YYYY-MM-DD', level: 'error', maxSize: '20m', maxFiles: '14d' }), // 应用日志文件 new DailyRotateFile({ filename: 'logs/app-%DATE%.log', datePattern: 'YYYY-MM-DD', maxSize: '20m', maxFiles: '14d' }) ] }) } info(message: string, meta?: any) { this.logger.info(message, meta) } error(message: string, error?: any) { this.logger.error(message, { error: error?.stack || error }) } warn(message: string, meta?: any) { this.logger.warn(message, meta) } debug(message: string, meta?: any) { this.logger.debug(message, meta) } } ``` #### 6.3.2 性能监控 ```typescript // src/middleware/monitor.ts import { Request, Response, NextFunction } from 'express' import { LoggerService } from '@/utils/logger' const logger = new LoggerService('Monitor') export const performanceMonitor = (req: Request, res: Response, next: NextFunction) => { const startTime = Date.now() res.on('finish', () => { const duration = Date.now() - startTime const { method, originalUrl, ip } = req const { statusCode } = res // 记录请求日志 logger.info('HTTP Request', { method, url: originalUrl, statusCode, duration, ip, userAgent: req.get('User-Agent') }) // 慢查询告警 if (duration > 2000) { logger.warn('Slow Request', { method, url: originalUrl, duration }) } // 错误状态告警 if (statusCode >= 500) { logger.error('Server Error', { method, url: originalUrl, statusCode }) } }) next() } ``` ## 7. 总结 本开发文档详细规划了解班客后端管理系统的开发计划,包括: ### 7.1 开发计划 - **总工期**:58个工作日 - **团队规模**:2-3名后端开发工程师 - **关键里程碑**:基础框架、核心业务、管理功能、优化部署 ### 7.2 技术架构 - **后端框架**:Express.js + TypeScript - **数据库**:MySQL + Redis - **ORM框架**:TypeORM - **认证授权**:JWT + RBAC ### 7.3 质量保证 - **代码规范**:ESLint + Prettier - **测试策略**:单元测试 + 集成测试 - **性能优化**:查询优化、缓存策略 - **监控体系**:日志监控 + 性能监控 ### 7.4 部署运维 - **容器化**:Docker + Docker Compose - **CI/CD**:GitHub Actions自动化部署 - **监控日志**:Winston日志系统 - **性能监控**:请求监控和告警 通过严格按照本开发文档执行,可以确保后端管理系统的高质量交付和稳定运行。