Appearance
新增模块
⭐⭐⭐⭐⭐
提供了一个标准模块的各页面基础代码模版供一键复制
module.ts
生成一个新模块
执行以下命令自动生成相关文件
- 生成模块:
nest g mo modules/website/XXX
说明:此操作将生成XXX.module.ts文件 - 生成控制器:
nest g co modules/website/XXX
说明:此操作将生成XXX.controller.ts文件,同时在XXX.module.ts中自动引入controllers - 生成服务类:
nest g s modules/website/XXX
说明:此操作将生成XXX.service.ts文件,同时在XXX.module.ts中自动引入providers - 手动创建其他文件:
XXX.entity.ts、XXX.dto.ts、XXX.vo.ts - 在
XXX.module.ts中注册 entity
ts
import { Banner } from './banner.entity'
import { TypeOrmModule } from '@nestjs/typeorm'
@Module({
controllers: [BannerController],
providers: [BannerService],
imports: [
TypeOrmModule.forFeature([Banner])
]
})DANGER
- 一定要先生成模块再生成其他部分
- 如果不想要单元测试可以加上
--no-specnest g mo modules/website/XXX --no-spec - 这里没有采用
Nest提供的一键 CURD 生成器是因为生成的代码跟我想要的不同。
entity.ts
ts
import { Entity, Column } from 'typeorm';
import { Common } from 'src/common/common.entity'
@Entity()
export class Banner extends Common {
@Column({
type: 'varchar',
comment: '标题',
length: 128
})
title: string;
@Column({
type: 'int',
comment: '排序'
})
sort: number;
@Column({
type: 'varchar',
comment: '图片',
default: '',
length: 1024
})
pic: string;
}dto.ts
ts
import { IsNotEmpty, Matches } from "class-validator";
import { regPositive } from "src/utils/validator";
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from 'class-transformer'
import { QueryDTO } from 'src/common/common.dto'
export class BannerQueryDTO extends QueryDTO {
@ApiPropertyOptional({
description: '标题',
example: '标题'
})
readonly title: string;
}
export class BannerAddNewDTO {
@ApiProperty({
description: '标题',
example: '标题'
})
@IsNotEmpty({ message: '标题不能为空' })
readonly title: string;
@ApiPropertyOptional({
description: '是否启用',
example: true
})
readonly isStart: boolean;
@ApiPropertyOptional({
description: '排序',
example: 1
})
readonly sort: number;
@ApiProperty({
description: '图片',
example: 'https://gq-blog.oss-cn-hangzhou.aliyuncs.com/dev/articleType/myLogo1677201314389.jpg'
})
@IsNotEmpty({ message: '图片不能为空' })
readonly pic: string;
}
export class BannerUpdateDTO extends BannerAddNewDTO {
@ApiProperty({
description: 'id',
example: 1
})
@Type(() => String)
@Matches(regPositive, { message: '请输入有效 id' })
@IsNotEmpty({ message: 'id 不能为空' })
readonly id: number;
}vo.ts
ts
import { ApiPropertyOptional } from "@nestjs/swagger";
import { commonResponse, commonField, Pagination } from "src/common/common.vo"
class BannerInfo extends commonField {
@ApiPropertyOptional({ description: '标题', example: '标题' })
title: string;
@ApiPropertyOptional({ description: '排序', example: 0 })
sort: number;
@ApiPropertyOptional({ description: '图片', example: 'https://gq-blog.oss-cn-hangzhou.aliyuncs.com/dev/articleType/myLogo1677201314389.jpg' })
pic: string;
}
// 定义返回数据类型
export class BannerListVO {
@ApiPropertyOptional({ type: BannerInfo, isArray: true })
list: Array<BannerInfo>
@ApiPropertyOptional({ type: Pagination })
pagination: Pagination
}
export class BannerListResponse extends commonResponse {
@ApiPropertyOptional({ description: '数据', type: () => BannerListVO, example: () => BannerListVO })
data: BannerListVO
}service.ts
ts
import { Injectable, InternalServerErrorException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { In, Like, Repository } from 'typeorm';
import { Banner } from './banner.entity';
import { BannerAddNewDTO, BannerQueryDTO, BannerUpdateDTO } from './banner.dto'
import { IdDTO, IdsDTO, getPagination, ChangeStartDTO } from 'src/common/common.dto'
@Injectable()
export class BannerService {
list: any[];
constructor(
@InjectRepository(Banner)
private readonly bannerRepository: Repository<Banner>
) {
this.list = []
}
async queryList(queryDTO: BannerQueryDTO) {
const { pageIndex = 1, pageSize = 15, title } = queryDTO
const condition = {
isDelete: false
}
title && (condition['title'] = Like(`%${title}%`))
const getList = this.bannerRepository
.createQueryBuilder('banner')
.leftJoinAndSelect("banner.article", "article", 'banner.articleId = article.id')
.where(condition)
.select([
'banner.id',
'banner.title',
'banner.articleId',
'banner.pic',
'banner.sort',
'banner.createTime',
'banner.isStart',
'article.title'
])
.skip((pageIndex - 1) * pageSize)
.take(pageSize)
.orderBy("banner.sort", "ASC")
.getManyAndCount()
const [list, total] = await getList
const pagination = getPagination(total, pageSize, pageIndex)
return {
list,
pagination
}
}
async addNew(addNewDTO: BannerAddNewDTO) {
const { title } = addNewDTO
const hasName = await this.bannerRepository.findOneBy({ title, isDelete: false })
if (hasName) {
throw new InternalServerErrorException('该标题已存在')
}
const addNewData = new Banner();
addNewData.title = addNewDTO.title
addNewData.pic = addNewDTO.pic
addNewData.sort = addNewDTO.sort
addNewData.isStart = addNewDTO.isStart
addNewData.articleId = addNewDTO.articleId
await this.bannerRepository.save(addNewData);
return true
}
async update(updateDTO: BannerUpdateDTO) {
const { id } = updateDTO
let updateData = await this.bannerRepository.findOneBy({ id })
updateData.title = updateDTO.title
updateData.pic = updateDTO.pic
updateData.sort = updateDTO.sort
updateData.isStart = updateDTO.isStart
await this.bannerRepository.save(updateData)
return true
}
async delete(idsDTO: IdsDTO) {
let updateData = await this.bannerRepository.findBy({id: In(idsDTO.ids)})
updateData.map(item => {
item.isDelete = true
})
await this.bannerRepository.save(updateData)
return true
}
async changeStart(changeStartDTO: ChangeStartDTO) {
const { id, isStart } = changeStartDTO
let updateData = await this.bannerRepository.findOneBy({ id })
updateData.isStart = isStart
await this.bannerRepository.save(updateData)
return true
}
}controller.ts
ts
import { Controller, Post, Body } from '@nestjs/common';
import { BannerService } from './banner.service'
import { ApiTags, ApiOperation, ApiOkResponse } from '@nestjs/swagger';
import { BannerAddNewDTO, BannerQueryDTO, BannerUpdateDTO } from './banner.dto'
import { BannerListResponse, BannerListVO } from './banner.vo'
import { IdsDTO, ChangeStartDTO } from 'src/common/common.dto'
import { Admin } from 'src/decorator/admin.decorator';
@ApiTags('轮播图设置')
@Controller('api/banner')
export class BannerController {
constructor(private BannerService: BannerService) {}
@Post('queryList')
@Admin('list_banner')
@ApiOperation({ summary: '轮播列表' })
@ApiOkResponse({ type: BannerListResponse })
queryList(@Body() queryDTO: BannerQueryDTO): Promise<BannerListVO> {
return this.BannerService.queryList(queryDTO);
}
@Post('addNew')
@Admin('list_banner:addNew')
@ApiOperation({ summary: '新建轮播' })
@ApiOkResponse({ type: Boolean })
addNew(@Body() addNewDTO: BannerAddNewDTO): Promise<Boolean> {
return this.BannerService.addNew(addNewDTO);
}
@Post('update')
@Admin('list_banner:update')
@ApiOperation({ summary: '修改轮播' })
@ApiOkResponse({ type: Boolean })
update(@Body() updateDTO: BannerUpdateDTO): Promise<Boolean> {
return this.BannerService.update(updateDTO);
}
@Post('delete')
@Admin('list_banner:delete')
@ApiOperation({ summary: '删除轮播' })
@ApiOkResponse({ type: Boolean })
delete(@Body() idsDto: IdsDTO): Promise<Boolean> {
return this.BannerService.delete(idsDto);
}
@Post('changeStart')
@Admin('list_banner:changeStart')
@ApiOperation({ summary: '是否启用' })
@ApiOkResponse({ type: Boolean })
changeStart(@Body() changeStartDTO: ChangeStartDTO): Promise<Boolean> {
return this.BannerService.changeStart(changeStartDTO);
}
}