# NestJS 后端项目分析报告 > 项目路径:`backend/` > 分析日期:2026-03-28 > 项目类型:多租户竞赛/活动管理系统 --- ## 一、技术栈概览 | 组件 | 技术选型 | 版本 | |------|----------|------| | **框架** | NestJS | 10.3.3 | | **语言** | TypeScript | 5.3.3 | | **数据库** | MySQL | 8.0+ | | **ORM** | Prisma | 6.19.0 | | **认证** | JWT + Passport | - | | **加密** | bcrypt | 6.0.0 | | **文件存储** | 腾讯云 COS | - | | **部署** | PM2 | - | | **AI 集成** | 腾讯混元 | - | ### 核心依赖 ```json { "@nestjs/common": "^10.3.3", "@nestjs/core": "^10.3.3", "@nestjs/jwt": "^10.2.0", "@nestjs/passport": "^10.0.3", "@prisma/client": "^6.19.0", "passport-jwt": "^4.0.1", "bcrypt": "^6.0.0", "class-validator": "^0.14.1", "class-transformer": "^0.5.1", "cos-nodejs-sdk-v5": "^2.15.4" } ``` --- ## 二、项目架构 ### 目录结构 ``` backend/ ├── src/ │ ├── ai-3d/ # AI 3D 生成模块 │ ├── auth/ # 认证授权模块 │ ├── common/ # 公共模块(过滤器、拦截器) │ ├── config/ # 系统配置模块 │ ├── contests/ # 竞赛管理模块(核心) │ │ ├── contests/ # 活动管理 │ │ ├── attachments/ # 附件管理 │ │ ├── judges/ # 评委管理 │ │ ├── notices/ # 公告管理 │ │ ├── preset-comments/ # 预设评语库 │ │ ├── registrations/ # 报名管理 │ │ ├── results/ # 结果管理 │ │ ├── review-rules/ # 评审规则 │ │ ├── reviews/ # 评审管理 │ │ ├── teams/ # 团队管理 │ │ └── works/ # 作品管理 │ ├── dict/ # 数据字典模块 │ ├── homework/ # 作业模块 │ │ ├── homeworks/ # 作业管理 │ │ ├── review-rules/ # 评审规则 │ │ ├── scores/ # 评分管理 │ │ └── submissions/ # 提交管理 │ ├── judges-management/ # 评委管理模块 │ ├── logs/ # 日志模块 │ ├── menus/ # 菜单管理模块 │ ├── oss/ # 对象存储模块 │ ├── permissions/ # 权限管理模块 │ ├── prisma/ # Prisma 服务 │ ├── public/ # 公共接口模块 │ ├── roles/ # 角色管理模块 │ ├── school/ # 学校管理模块 │ │ ├── schools/ # 学校管理 │ │ ├── grades/ # 年级管理 │ │ ├── classes/ # 班级管理 │ │ ├── departments/ # 部门管理 │ │ ├── teachers/ # 教师管理 │ │ └── students/ # 学生管理 │ ├── tenants/ # 租户管理模块 │ ├── upload/ # 文件上传模块 │ ├── users/ # 用户管理模块 │ ├── app.module.ts # 根模块 │ └── main.ts # 入口文件 ├── prisma/ │ ├── schema.prisma # 数据库模型定义 │ └── migrations/ # 数据库迁移文件 ├── scripts/ # 初始化脚本 ├── test/ # 测试文件 └── package.json ``` --- ## 三、核心功能模块详解 ### 1. 认证授权模块 (`auth/`) **文件结构:** ``` auth/ ├── auth.controller.ts # 认证控制器 ├── auth.service.ts # 认证服务 ├── auth.module.ts # 认证模块 ├── decorators/ # 装饰器 │ ├── current-tenant-id.decorator.ts # 当前租户 ID 装饰器 │ ├── public.decorator.ts # 公开接口装饰器 │ ├── require-permission.decorator.ts # 权限装饰器 │ └── roles.decorator.ts # 角色装饰器 ├── guards/ # 守卫 │ ├── jwt-auth.guard.ts # JWT 认证守卫 │ ├── roles.guard.ts # 角色守卫 │ └── permissions.guard.ts # 权限守卫 └── strategies/ # Passport 策略 ├── jwt.strategy.ts # JWT 策略 └── local.strategy.ts # 本地策略 ``` **核心功能:** - 用户名密码登录 - JWT Token 签发与验证 - 权限验证(基于装饰器) - 角色验证 - 多租户识别 **核心代码示例:** ```typescript // auth.service.ts - 登录逻辑 async login(user: any, tenantId?: number) { // 验证租户有效 const tenant = await this.prisma.tenant.findUnique({ where: { id: finalTenantId }, }); // 签发 JWT Token const payload = { username: user.username, sub: user.id, tenantId: finalTenantId, }; return { token: this.jwtService.sign(payload), user: { id: user.id, username: user.username, roles: user.roles?.map((ur: any) => ur.role.code) || [], permissions: await this.getUserPermissions(user.id), }, }; } ``` --- ### 2. 用户管理模块 (`users/`) **核心功能:** - 用户 CRUD 操作 - 用户状态管理(启用/禁用) - 关键字搜索(用户名、昵称、邮箱、手机号) - 超级租户跨租户查询 - 用户统计分析 **接口列表:** | 接口 | 方法 | 权限 | 说明 | |------|------|------|------| | `POST /users` | POST | `user:create` | 创建用户 | | `GET /users` | GET | `user:view` | 用户列表(分页) | | `GET /users/stats` | GET | `super_admin` | 用户统计 | | `GET /users/:id` | GET | `user:view` | 用户详情 | | `PUT /users/:id` | PUT | `user:update` | 更新用户 | | `PUT /users/:id/status` | PUT | `user:manage` | 切换状态 | | `DELETE /users/:id` | DELETE | `user:delete` | 删除用户 | **特色功能:** ```typescript // 用户类型统计(仅超管) async getStats() { const superTenantIds = ...; // 平台租户 ID const orgTenantIds = ...; // 机构租户 ID const judgeTenantIds = ...; // 评委租户 ID return { total, // 总用户数 platform, // 平台用户数 org, // 机构用户数 judge, // 评委用户数 public: publicCount, // 公共用户数 }; } ``` --- ### 3. 竞赛/活动管理模块 (`contests/`) ⭐核心业务 **模块结构:** ``` contests/ ├── contests/ # 活动主体管理 ├── registrations/ # 报名管理 ├── teams/ # 团队管理 ├── works/ # 作品管理 ├── reviews/ # 评审管理 ├── judges/ # 评委管理 ├── results/ # 结果管理 ├── review-rules/ # 评审规则 ├── preset-comments/ # 预设评语库 ├── notices/ # 公告管理 └── attachments/ # 附件管理 ``` #### 3.1 活动管理 (`contests/contests/`) **活动生命周期:** ``` ┌─────────────┐ │ 未发布 │ │ unpublished │ └──────┬──────┘ │ 发布 ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ 已发布 │ ──► │ 报名中 │ ──► │ 作品提交中 │ ──► │ 评审中 │ │ published │ │ registering │ │ submitting │ │ reviewing │ └─────────────┘ └─────────────┘ └─────────────┘ └──────┬──────┘ │ ┌─────────────┐ │ │ 已完结 │ ◄────────────────┘ │ finished │ └─────────────┘ ``` **核心服务方法:** ```typescript // 判断活动当前所处阶段 getContestStage(contest: any): string { const now = new Date(); const regStart = new Date(contest.registerStartTime); const regEnd = new Date(contest.registerEndTime); const subStart = new Date(contest.submitStartTime); const subEnd = new Date(contest.submitEndTime); const revStart = new Date(contest.reviewStartTime); const revEnd = new Date(contest.reviewEndTime); if (now >= regStart && now <= regEnd) return 'registering'; if (now >= subStart && now <= subEnd) return 'submitting'; if (now >= revStart && now <= revEnd) return 'reviewing'; if (revEnd && now > revEnd) return 'finished'; return 'published'; } ``` #### 3.2 报名管理 (`registrations/`) **功能:** - 个人/团队报名 - 报名审核(需要审核的活动) - 报名状态管理 #### 3.3 作品管理 (`works/`) **功能:** - 作品提交(支持多次提交配置) - 作品版本管理(`isLatest` 标识最新版本) - 作品状态流转 #### 3.4 评审管理 (`reviews/`) **功能:** - 评委分配(手动/批量/自动) - 作品评分 - 分数统计 **评分 DTO:** ```typescript // create-score.dto.ts export class CreateScoreDto { @IsInt() workId: number; @IsInt() judgeId: number; @IsNumber() score: number; @IsString() @IsOptional() comment: string; // 评语 } ``` #### 3.5 结果管理 (`results/`) **功能:** - 手动设置奖项 - 批量设置奖项 - 自动设置奖项(按分数排名) ```typescript // 自动设置奖项 DTO export class AutoSetAwardsDto { @IsInt() contestId: number; @IsInt() firstPrizeCount: number; // 一等奖数量 @IsInt() secondPrizeCount: number; // 二等奖数量 @IsInt() thirdPrizeCount: number; // 三等奖数量 } ``` --- ### 4. 学校管理模块 (`school/`) **模块结构:** ``` school/ ├── schools/ # 学校信息 ├── grades/ # 年级管理 ├── classes/ # 班级管理 ├── departments/ # 部门管理 ├── teachers/ # 教师管理 └── students/ # 学生管理 ``` **数据库关系:** ``` Tenant (1) ── (1) School Tenant (1) ── (N) Grade Tenant (1) ── (N) Class Tenant (1) ── (N) Teacher Tenant (1) ── (N) Student ``` --- ### 5. AI 3D 生成模块 (`ai-3d/`) ⭐特色功能 **文件结构:** ``` ai-3d/ ├── ai-3d.controller.ts ├── ai-3d.service.ts ├── ai-3d.module.ts ├── providers/ │ ├── ai-3d-provider.interface.ts # AI 提供商接口 │ ├── hunyuan.provider.ts # 腾讯混元实现 │ └── mock.provider.ts # Mock 实现 └── utils/ ├── tencent-cloud-sign.ts # 腾讯云签名 └── zip-handler.ts # ZIP 处理 ``` **功能:** - AI 生成 3D 模型(腾讯混元 API 集成) - 任务创建与状态查询 - 模型文件压缩与下载 **DTO:** ```typescript // create-task.dto.ts export class CreateTaskDto { @IsString() prompt: string; // 生成提示词 @IsString() @IsOptional() style?: string; // 风格(写实/卡通/低多边形) @IsString() @IsOptional() negativePrompt?: string; // 负面提示词 } ``` --- ### 6. 作业模块 (`homework/`) **功能:** - 作业发布与提交 - 作业评审 - 评分管理 **与竞赛模块的区别:** - 作业更轻量化,适合日常教学场景 - 评审流程简化 - 支持班级维度布置 --- ### 7. 系统管理模块 #### 7.1 角色权限 (`roles/`, `permissions/`) **RBAC 模型:** ``` User ──(N)── UserRole ──(1)── Role Role ──(N)── RolePermission ──(1)── Permission ``` **权限装饰器:** ```typescript @RequirePermission('user:create') async createUser(...) { // 只有拥有 user:create 权限的用户才能访问 } ``` #### 7.2 菜单管理 (`menus/`) **功能:** - 动态菜单配置 - 菜单权限绑定 - 租户菜单定制(`TenantMenu`) #### 7.3 数据字典 (`dict/`) **功能:** - 常用数据字典配置 - 支持租户自定义字典 #### 7.4 系统配置 (`config/`) **功能:** - 系统参数配置 - 租户级配置隔离 - 配置验证接口 --- ### 8. 文件存储模块 (`upload/`, `oss/`) **腾讯云 COS 集成:** ```typescript // oss.service.ts async uploadFile(file: Express.Multer.File, dir: string) { const client = new COS({ SecretId: this.secretId, SecretKey: this.secretKey, }); const key = `${dir}/${Date.now()}-${file.originalname}`; await client.putObject({ Bucket: this.bucket, Region: this.region, Key: key, Body: file.buffer, }); return `https://${this.bucket}.cos.${this.region}.myqcloud.com/${key}`; } ``` --- ## 四、数据库设计 ### 核心数据表 #### 1. 租户表 (`Tenant`) ```prisma model Tenant { id Int @id @default(autoincrement()) name String // 租户名称 code String @unique // 租户编码 domain String? @unique // 租户域名 description String? isSuper Int @default(0) // 是否超级租户 tenantType String @default("other") validState Int @default(1) createTime DateTime @default(now()) modifyTime DateTime @updatedAt users User[] roles Role[] contests Contest[] school School? // ... } ``` #### 2. 用户表 (`User`) ```prisma model User { id Int @id @default(autoincrement()) tenantId Int username String password String nickname String? email String? phone String? avatar String? status String @default("enabled") validState Int @default(1) createTime DateTime @default(now()) tenant Tenant @relation(fields: [tenantId], references: [id]) roles UserRole[] children User[] // 家长 - 孩子关联 parent User? @relation("ParentChild", fields: [parentId], references: [id]) contestRegistrations ContestRegistration[] contestWorks ContestWork[] // ... } ``` #### 3. 活动表 (`Contest`) ```prisma model Contest { id Int @id @default(autoincrement()) contestName String contestType String // 竞赛类型 contestState String @default("unpublished") status String @default("ongoing") visibility String @default("designated") contestTenants String? // JSON: 可见租户 ID 列表 registerStartTime DateTime registerEndTime DateTime submitStartTime DateTime submitEndTime DateTime reviewStartTime DateTime reviewEndTime DateTime resultPublishTime DateTime? teamMinMembers Int @default(1) teamMaxMembers Int @default(1) requireAudit Boolean @default(true) attachments ContestAttachment[] registrations ContestRegistration[] teams ContestTeam[] works ContestWork[] judges ContestJudge[] reviewRule ContestReviewRule? notices ContestNotice[] // ... } ``` #### 4. 作品表 (`ContestWork`) ```prisma model ContestWork { id Int @id @default(autoincrement()) contestId Int userId Int teamId Int? title String description String? coverUrl String? workUrl String? workType String // 作品类型 version Int @default(1) isLatest Boolean @default(true) status String @default("submitted") contest Contest @relation(fields: [contestId], references: [id]) author User @relation(fields: [userId], references: [id]) team ContestTeam? scores ContestWorkScore[] attachments ContestWorkAttachment[] // ... } ``` #### 5. 评分表 (`ContestWorkScore`) ```prisma model ContestWorkScore { id Int @id @default(autoincrement()) workId Int judgeId Int contestId Int score Decimal comment String? work ContestWork @relation(fields: [workId], references: [id]) judge User @relation(fields: [judgeId], references: [id]) contest Contest @relation(fields: [contestId], references: [id]) @@unique([workId, judgeId]) // 同一评委对同一作品只能评分一次 } ``` ### 完整 ER 图(核心部分) ``` ┌─────────────┐ │ Tenant │ │ (租户表) │ └──────┬──────┘ │ ├───► ┌─────────────┐ ┌─────────────┐ │ │ User │──────►│ Role │ │ │ (用户表) │ │ (角色表) │ │ └──────┬──────┘ └─────────────┘ │ │ │ ├───► ┌─────────────┐ ┌─────────────┐ │ │ │ Contest │──────►│ContestWork │ │ │ │ (活动表) │ │ (作品表) │ │ │ └──────┬──────┘ └──────┬──────┘ │ │ │ │ │ │ ├───► ┌─────────────┐ │ │ │ │ │ContestTeam │◄┘ │ │ │ │ (团队表) │ │ │ │ └─────────────┘ │ │ │ │ │ ├───► ┌─────────────┐ │ │ │ │ContestJudge │ │ │ │ │ (评委表) │ │ │ │ └─────────────┘ │ │ │ │ │ └───► ┌─────────────┐ │ │ │ContestWork │ │ │ │ Score │ │ │ │ (评分表) │ │ │ └─────────────┘ │ │ │ └───► ┌─────────────┐ │ │ School │ │ │ (学校表) │ │ └──────┬──────┘ │ │ │ ├───► ┌─────────────┐ │ │ │ Teacher │ │ │ │ (教师表) │ │ │ └─────────────┘ │ │ │ └───► ┌─────────────┐ │ │ Student │ │ │ (学生表) │ │ └─────────────┘ │ └───► ┌─────────────┐ │ Permission │ │ (权限表) │ └─────────────┘ ``` --- ## 五、核心业务流程 ### 1. 活动创建流程 ``` 1. 管理员创建活动 ↓ 2. 配置报名参数(时间、人数限制、需审核等) ↓ 3. 配置评审规则(评分维度、权重) ↓ 4. 设置可见范围(公开/指定租户) ↓ 5. 发布活动 ``` ### 2. 用户参赛流程 ``` 1. 用户查看活动列表 ↓ 2. 个人报名 / 创建团队 ↓ 3. 等待审核(如需) ↓ 4. 提交作品(可多次提交,保留最新版本) ↓ 5. 等待评审 ↓ 6. 查看结果 ``` ### 3. 作品评审流程 ``` 1. 系统/管理员分配评委到作品 ↓ 2. 评委登录系统查看分配的作品 ↓ 3. 评委评分(填写分数 + 评语) ↓ 4. 系统统计总分/平均分 ↓ 5. 自动生成获奖名单 ``` ### 4. AI 3D 生成流程 ``` 1. 用户提交生成请求(提示词) ↓ 2. 系统调用腾讯混元 API ↓ 3. 轮询任务状态 ↓ 4. 下载生成的 3D 模型 ↓ 5. 可选:压缩为 ZIP 格式 ``` --- ## 六、多租户架构 ### 租户类型 | 类型 | 说明 | 权限 | |------|------|------| | **超级租户** | 平台管理员 | 访问所有数据 | | **机构租户** | 学校/图书馆等 | 访问本机构数据 | | **评委租户** | 评委专用 | 访问评审相关数据 | | **公共租户** | 公共资源 | 访问公开数据 | ### 数据隔离实现 ```typescript // 所有业务查询必须包含租户 ID const contests = await prisma.contest.findMany({ where: { tenantId: currentTenantId, validState: 1, }, }); // 超级租户跨租户查询 const allContests = await prisma.contest.findMany({ where: isSuperTenant ? { validState: 1 } // 超管查询全部 : { tenantId: currentTenantId, validState: 1 }, // 普通租户仅查询本租户 }); ``` --- ## 七、中间件与拦截器 ### 1. JWT 认证守卫 (`JwtAuthGuard`) ```typescript @Injectable() export class JwtAuthGuard implements CanActivate { canActivate(context: ExecutionContext): boolean { const request = context.switchToHttp().getRequest(); const token = this.extractTokenFromHeader(request); if (!token) throw new UnauthorizedException(); try { const payload = this.jwtService.verify(token); request['user'] = payload; } catch { throw new UnauthorizedException(); } return true; } } ``` ### 2. 权限守卫 (`PermissionsGuard`) ```typescript @Injectable() export class PermissionsGuard implements CanActivate { async canActivate(context: ExecutionContext): Promise { const requiredPermission = this.reflector.get( REQUIRE_PERMISSION_KEY, context.getHandler() ); if (!requiredPermission) return true; const { user } = context.switchToHttp().getRequest(); const permissions = await this.authService.getUserPermissions(user.sub); return permissions.some(p => requiredPermission.includes(p)); } } ``` ### 3. 响应转换拦截器 (`TransformInterceptor`) ```typescript @Injectable() export class TransformInterceptor implements NestInterceptor> { intercept( context: ExecutionContext, next: CallHandler, ): Observable> { return next.handle().pipe( map(data => ({ code: 200, message: '操作成功', data: data.data || data, total: data.total, })), ); } } ``` --- ## 八、初始化脚本 项目提供了丰富的初始化脚本: ```bash # 初始化管理员账户 pnpm init:admin # 初始化菜单 pnpm init:menus # 初始化超级租户 pnpm init:super-tenant # 初始化租户管理员 pnpm init:tenant-admin # 初始化角色权限 pnpm init:roles:all # 数据库迁移 pnpm prisma:migrate # Prisma Studio(数据库可视化) pnpm prisma:studio ``` --- ## 九、API 接口概览 ### 认证相关 | 接口 | 方法 | 说明 | |------|------|------| | `POST /auth/login` | POST | 用户登录 | | `POST /auth/logout` | POST | 用户登出 | | `GET /auth/info` | GET | 获取当前用户信息 | ### 用户管理 | 接口 | 方法 | 说明 | |------|------|------| | `GET /users` | GET | 用户列表 | | `POST /users` | POST | 创建用户 | | `GET /users/:id` | GET | 用户详情 | | `PUT /users/:id` | PUT | 更新用户 | | `PUT /users/:id/status` | PUT | 切换状态 | | `DELETE /users/:id` | DELETE | 删除用户 | ### 活动管理 | 接口 | 方法 | 说明 | |------|------|------| | `GET /contests` | GET | 活动列表 | | `POST /contests` | POST | 创建活动 | | `GET /contests/:id` | GET | 活动详情 | | `PUT /contests/:id` | PUT | 更新活动 | | `POST /contests/:id/publish` | POST | 发布/取消发布 | | `DELETE /contests/:id` | DELETE | 删除活动 | ### 报名管理 | 接口 | 方法 | 说明 | |------|------|------| | `GET /contests/:contestId/registrations` | GET | 报名列表 | | `POST /contests/:contestId/registrations` | POST | 创建报名 | | `PUT /registrations/:id/review` | PUT | 审核报名 | ### 作品管理 | 接口 | 方法 | 说明 | |------|------|------| | `GET /contests/:contestId/works` | GET | 作品列表 | | `POST /contests/:contestId/works` | POST | 提交作品 | | `PUT /works/:id` | PUT | 更新作品 | ### 评审管理 | 接口 | 方法 | 说明 | |------|------|------| | `POST /contests/:contestId/reviews/assign` | POST | 分配评审 | | `POST /reviews/scores` | POST | 提交评分 | | `GET /contests/:contestId/reviews/stats` | GET | 评审统计 | ### 结果管理 | 接口 | 方法 | 说明 | |------|------|------| | `GET /contests/:contestId/results` | GET | 结果列表 | | `POST /contests/:contestId/results/set-awards` | POST | 设置奖项 | | `POST /contests/:contestId/results/auto-set-awards` | POST | 自动设置奖项 | ### AI 3D 生成 | 接口 | 方法 | 说明 | |------|------|------| | `POST /ai-3d/tasks` | POST | 创建生成任务 | | `GET /ai-3d/tasks/:id` | GET | 查询任务状态 | --- ## 十、总结 ### 项目特点 1. **完整的多租户 SaaS 架构** - 支持平台、机构、评委等多角色 2. **丰富的竞赛管理功能** - 从活动创建到评审颁奖全流程覆盖 3. **灵活的 RBAC 权限系统** - 支持细粒度权限控制 4. **AI 能力集成** - 腾讯混元 3D 模型生成 5. **完善的初始化脚本** - 快速部署和配置 ### 适用场景 - 📚 图书馆绘本创作比赛 - 🏫 学校各类竞赛活动 - 🎨 艺术创作比赛 - 📖 作文/阅读比赛 - 🤖 科技创新大赛 ### 技术亮点 - NestJS 模块化架构 - Prisma ORM 类型安全 - JWT 无状态认证 - 事务处理保证数据一致性 - 软删除保留数据追溯 --- > 文档生成时间:2026-03-28 > 分析人:AI Assistant