一、超管端设计优化 - 文档管理SOP体系建立,docs目录重组 - 统一用户管理:跨租户全局视角,合并用户管理+公众用户 - 活动监管全模块重构:全部活动(统计卡片+阶段筛选+SuperDetail详情页)、报名数据/作品数据/评审进度(两层合一扁平列表)、成果发布(去Tab+统计+隐藏写操作) - 菜单精简:移除评委管理/评审规则/通知管理 - Bug修复:租户编辑丢失隐藏菜单、pageSize限制、主色统一 二、UGC绘本创作社区P0 - 数据库:10张新表(user_works/user_work_pages/work_tags等) - 子女账号独立化:Child升级为独立User,家长切换+独立登录 - 用户作品库:CRUD+发布审核,8个API - AI创作流程:提交→生成→保存到作品库,4个API - 作品广场:首页改造为推荐流,标签+搜索+排序 - 内容审核(超管端):作品审核+作品管理+标签管理 - 活动联动:WorkSelector作品选择器 - 布局改造:底部5Tab(发现/创作/活动/作品库/我的) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
222 lines
4.6 KiB
Plaintext
222 lines
4.6 KiB
Plaintext
---
|
||
description: NestJS 后端架构规范和模块结构
|
||
globs:
|
||
- "backend/**/*.ts"
|
||
alwaysApply: false
|
||
---
|
||
|
||
# 后端架构规范
|
||
|
||
## 模块结构
|
||
|
||
每个功能模块应包含:
|
||
|
||
- `module.ts` - 模块定义
|
||
- `controller.ts` - 控制器
|
||
- `service.ts` - 服务层
|
||
- `dto/` - 数据传输对象目录
|
||
|
||
### 命名规范
|
||
|
||
- 模块命名使用复数形式:`users`, `roles`, `contests`
|
||
- 子模块放在父模块目录下:`contests/works/`, `contests/teams/`
|
||
|
||
### 目录结构示例
|
||
|
||
```
|
||
src/
|
||
├── contests/
|
||
│ ├── contests.module.ts
|
||
│ ├── contests/
|
||
│ │ ├── contests.module.ts
|
||
│ │ ├── contests.controller.ts
|
||
│ │ ├── contests.service.ts
|
||
│ │ └── dto/
|
||
│ ├── works/
|
||
│ │ ├── works.module.ts
|
||
│ │ ├── works.controller.ts
|
||
│ │ ├── works.service.ts
|
||
│ │ └── dto/
|
||
│ └── teams/
|
||
│ └── ...
|
||
```
|
||
|
||
## 服务层 (Service)
|
||
|
||
### 基本规范
|
||
|
||
- 使用 `@Injectable()` 装饰器
|
||
- 构造函数注入依赖,使用 `private readonly`
|
||
- 所有数据库操作通过 PrismaService
|
||
- **禁止直接使用 SQL**
|
||
|
||
### 标准方法命名
|
||
|
||
- `create` - 创建
|
||
- `findAll` - 查询列表(支持分页)
|
||
- `findOne` - 查询单个
|
||
- `update` - 更新
|
||
- `remove` - 删除(软删除或级联)
|
||
|
||
### 示例
|
||
|
||
```typescript
|
||
@Injectable()
|
||
export class UsersService {
|
||
constructor(private readonly prisma: PrismaService) {}
|
||
|
||
async create(createDto: CreateUserDto, tenantId: number) {
|
||
return this.prisma.user.create({
|
||
data: {
|
||
...createDto,
|
||
tenantId,
|
||
},
|
||
});
|
||
}
|
||
|
||
async findAll(tenantId: number, skip?: number, take?: number) {
|
||
return this.prisma.user.findMany({
|
||
where: { tenantId, validState: 1 },
|
||
skip,
|
||
take,
|
||
include: {
|
||
roles: {
|
||
include: { role: true },
|
||
},
|
||
},
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
## 控制器层 (Controller)
|
||
|
||
### 基本规范
|
||
|
||
- 使用 `@Controller()` 装饰器,路径使用复数形式
|
||
- 所有路由默认需要认证(除非使用 `@Public()` 装饰器)
|
||
- 使用 REST 风格的 HTTP 方法装饰器
|
||
|
||
### 装饰器使用
|
||
|
||
```typescript
|
||
@Controller("users")
|
||
export class UsersController {
|
||
constructor(private readonly usersService: UsersService) {}
|
||
|
||
@Post()
|
||
@RequirePermission("user:create")
|
||
async create(
|
||
@Body() createDto: CreateUserDto,
|
||
@CurrentTenantId() tenantId: number,
|
||
@CurrentUser() user: any
|
||
) {
|
||
return this.usersService.create(createDto, tenantId);
|
||
}
|
||
|
||
@Get()
|
||
@RequirePermission("user:read")
|
||
async findAll(
|
||
@CurrentTenantId() tenantId: number,
|
||
@Query("skip") skip?: number,
|
||
@Query("take") take?: number
|
||
) {
|
||
return this.usersService.findAll(tenantId, skip, take);
|
||
}
|
||
|
||
@Public()
|
||
@Get("public-info")
|
||
async getPublicInfo() {
|
||
return { version: "1.0.0" };
|
||
}
|
||
}
|
||
```
|
||
|
||
### 常用装饰器
|
||
|
||
- `@CurrentTenantId()` - 获取当前租户ID
|
||
- `@CurrentUser()` - 获取当前用户信息
|
||
- `@RequirePermission('module:action')` - 权限检查
|
||
- `@Public()` - 公开接口,无需认证
|
||
|
||
## DTO 规范
|
||
|
||
### 命名规范
|
||
|
||
- 创建:`CreateXxxDto`
|
||
- 更新:`UpdateXxxDto`
|
||
- 查询:`QueryXxxDto`
|
||
|
||
### 验证规则
|
||
|
||
使用 `class-validator` 装饰器:
|
||
|
||
```typescript
|
||
import {
|
||
IsString,
|
||
IsEmail,
|
||
IsOptional,
|
||
IsArray,
|
||
IsNumber,
|
||
} from "class-validator";
|
||
|
||
export class CreateUserDto {
|
||
@IsString()
|
||
username: string;
|
||
|
||
@IsString()
|
||
password: string;
|
||
|
||
@IsString()
|
||
nickname: string;
|
||
|
||
@IsEmail()
|
||
@IsOptional()
|
||
email?: string;
|
||
|
||
@IsArray()
|
||
@IsNumber({}, { each: true })
|
||
@IsOptional()
|
||
roleIds?: number[];
|
||
}
|
||
```
|
||
|
||
## 错误处理
|
||
|
||
使用 NestJS 内置异常,消息使用中文:
|
||
|
||
```typescript
|
||
import {
|
||
NotFoundException,
|
||
BadRequestException,
|
||
UnauthorizedException,
|
||
ForbiddenException,
|
||
} from "@nestjs/common";
|
||
|
||
// 示例
|
||
if (!user) {
|
||
throw new NotFoundException("用户不存在");
|
||
}
|
||
|
||
if (!isValid) {
|
||
throw new BadRequestException("数据验证失败");
|
||
}
|
||
```
|
||
|
||
## 权限控制
|
||
|
||
权限字符串格式:`模块:操作`
|
||
|
||
```typescript
|
||
@RequirePermission('contest:create') // 创建竞赛
|
||
@RequirePermission('user:update') // 更新用户
|
||
@RequirePermission('role:delete') // 删除角色
|
||
```
|
||
|
||
## 代码风格
|
||
|
||
- 导入顺序:NestJS 核心 → 第三方库 → 本地模块
|
||
- 使用 async/await,避免 Promise.then()
|
||
- 使用解构赋值提高代码可读性
|
||
- 复杂逻辑必须添加注释
|