library-picturebook-activity/.claude/skills/backend-api.md

262 lines
5.6 KiB
Markdown
Raw Normal View History

2026-01-15 16:35:00 +08:00
# 后端接口生成规范
## 概述
本规范用于快速生成 NestJS 后端接口,包含:
- Controller路由定义
- Service业务逻辑
- DTO参数校验
- 前端 API 调用
## 快速生成指令格式
```
请生成后端接口:
模块名称xxx
接口路径GET/POST /api/xxx
功能描述xxx
查询参数:
- 参数1类型必填/选填)
- 参数2类型必填/选填)
返回字段:
- 字段1来源表.字段)
- 字段2关联表.字段)
关联查询:
- 表1 -> 表2关联字段
排序:字段名 + 排序方式
分页:是/否
```
## 文件结构
```
backend/src/模块名/
├── 模块名.module.ts # 模块定义
├── 模块名.controller.ts # 路由控制器
├── 模块名.service.ts # 业务逻辑
└── dto/
├── query-xxx.dto.ts # 查询参数 DTO
└── create-xxx.dto.ts # 创建参数 DTO
frontend/src/api/
└── 模块名.ts # 前端 API 调用
```
## Controller 模板
```typescript
import { Controller, Get, Query, Param, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { XxxService } from './xxx.service';
import { QueryXxxDto } from './dto/query-xxx.dto';
@Controller('api/xxx')
@UseGuards(JwtAuthGuard)
export class XxxController {
constructor(private readonly xxxService: XxxService) {}
@Get(':id/list')
async getList(
@Param('id') id: string,
@Query() queryDto: QueryXxxDto,
) {
return this.xxxService.findAll(+id, queryDto);
}
}
```
## Service 模板
```typescript
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { QueryXxxDto } from './dto/query-xxx.dto';
@Injectable()
export class XxxService {
constructor(private prisma: PrismaService) {}
async findAll(parentId: number, queryDto: QueryXxxDto) {
const { page = 1, pageSize = 10, ...filters } = queryDto;
const skip = (page - 1) * pageSize;
// 构建查询条件
const where: any = { parentId };
if (filters.field1) {
where.field1 = { contains: filters.field1 };
}
const [list, total] = await Promise.all([
this.prisma.table.findMany({
where,
skip,
take: pageSize,
orderBy: { sortField: 'desc' },
include: {
// 关联查询
},
}),
this.prisma.table.count({ where }),
]);
return { list, total, page, pageSize };
}
}
```
## DTO 模板
```typescript
import { IsOptional, IsString, IsInt, Min } from 'class-validator';
import { Type } from 'class-transformer';
export class QueryXxxDto {
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
page?: number = 1;
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
pageSize?: number = 10;
@IsOptional()
@IsString()
field1?: string;
@IsOptional()
@IsString()
field2?: string;
}
```
## 前端 API 模板
```typescript
import request from '@/utils/request'
export interface QueryXxxParams {
page?: number
pageSize?: number
field1?: string
field2?: string
}
export interface XxxItem {
id: number
// ... 其他字段
}
export interface XxxListResponse {
list: XxxItem[]
total: number
page: number
pageSize: number
}
export const xxxApi = {
getList(parentId: number, params: QueryXxxParams): Promise<XxxListResponse> {
return request.get(`/api/xxx/${parentId}/list`, { params })
},
}
```
## Prisma 关联查询示例
### 常用关联模式
```typescript
// 用户信息 + 租户 + 学生班级年级
user: {
include: {
tenant: {
select: { id: true, name: true },
},
student: {
include: {
class: {
include: {
grade: {
select: { id: true, name: true },
},
},
},
},
},
},
}
// 指导老师列表
teachers: {
include: {
user: {
select: { id: true, username: true, nickname: true },
},
},
}
// 报名信息 + 用户 + 团队
registration: {
include: {
user: {
select: { id: true, username: true, nickname: true },
},
team: {
select: { id: true, teamName: true },
},
},
}
```
## 示例:赛果发布详情接口
```
请生成后端接口:
模块名称results已存在需修改
接口路径GET /api/contests/:id/results/works
功能描述:获取赛事的作品列表(用于赛果发布)
查询参数:
- pagenumber选填默认1
- pageSizenumber选填默认10
- workNostring选填作品编号模糊搜索
- accountNostring选填报名账号模糊搜索
返回字段:
- id, workNo, title, finalScore作品表
- registration.user.nickname, username用户表
- registration.user.tenant.name租户表
- registration.user.student.class.name, grade.name班级年级
- registration.teachers[].user.nickname指导老师
关联查询:
- ContestWork -> ContestRegistrationregistrationId
- ContestRegistration -> UseruserId
- User -> TenanttenantId
- User -> Student -> Class -> Grade
- ContestRegistration -> ContestRegistrationTeacher -> User
排序finalScore DESC
分页:是
```
## 注意事项
1. **权限控制**Controller 需要添加 `@UseGuards(JwtAuthGuard)` 和权限装饰器
2. **参数校验**DTO 使用 class-validator 进行校验
3. **分页处理**:统一使用 page/pageSize 参数
4. **空值处理**:查询条件为空时不添加到 where 条件
5. **关联数据**:使用 Prisma 的 include 进行关联查询
6. **性能优化**:只 select 需要的字段,避免查询过多数据
7. **错误处理**Service 层抛出 NestJS 内置异常