Nest.js毕业设计

249 阅读2分钟

前言

萌新做的一个毕设,边学边做,使用Nest.js的原因是他原生支持TypeScript,仿写的BBBUG 音乐聊天室,肯定没人家原版写的好,大部分功能已经完成 个人gitee地址后端地址 前端地址

  • 文件上传模块
  • 使用中间件对用户身份验证
  • 数据库:TypeORM x Mysql
  • 暴露静态资源目录
  • websocket实现数据推送
  • 管道dto验证

前端

前端部分采用React来实现,大部分都有TS类型提示

后端

业务接口

  • 文件上传:multer 静态资源暴露静态资源目录
  • 用户:注册 修改密码 邮箱发送验证码
  • 聊天室:WebSocket
  • 音乐数据:axios 请求酷我接口

基础模块

  • 鉴权:设置middware相应的处理
  • 数据库:TypeORM
  • 参数校验:管道-类验证器,进行请求参数校验

ws验证

import { WebSocketGateway, WebSocketServer } from "@nestjs/websockets";
import { Socket, Server } from "socket.io";
import { SongService } from "src/song/song.service";
import { decrypt, getTimeStamp, queryString } from "src/utils/common";
import { CacheRedisService } from "src/cache-redis/cache-redis.service";

@WebSocketGateway({
  allowEIO3: true,
  cors: {
    origin: "*",
    credentials: true,
  },
  transports: ["websocket"],
})
export class WsGateway {
  constructor(private song: SongService, private redis: CacheRedisService) {
    setInterval(async () => {
      const res = await song.execute();
      //如果接收到的值是空,那么就不会广播
      for (let i = 0; i < res.length; i++) {
        this.server.to(res[i].room_id.toString()).emit("playSong", {
          song: res[i].song,
          sysTime: getTimeStamp(),
        });
      }
    }, 6000);
  }

  @WebSocketServer()
  server: Server;

  async send(room_id: string, data: any) {
    this.server.to(room_id).emit("text", data);
  }
  async handleConnection(client: Socket, ...args: any[]) {
    const str = decrypt(client.handshake.query.tick as string);
    const obj = queryString(str) as any;
    if (obj.timeStamp) {
      if (getTimeStamp() > Number(obj.timeStamp) + 300) {
        client.disconnect();
      } else {
        client.join(obj.room_id as any);
        const res = await this.song.getNowPlayingSong(obj.room_id);
        if (res) {
          client.emit("playSong", { sysTime: getTimeStamp(), song: res });
        }
      }
    }
  }
}

文件上传

我这里只是进行了一个图片上传,为了获取到图片的真实类型,用了一个image-type进行一个类型的判断(原理的文件的数据,在前多少位来着有一个标记,具体可以自己搜索一下)

  @Post("upload")
  @UseInterceptors(FileInterceptor("file"))
  uploadImage(
    @UploadedFile() file: Express.Multer.File,
    @Req() request: Request
  ) {
    const ret = imageType(file.buffer);
    //限制大小
    if (file.size > 1024 * 1024) {
      throw new HttpException({ code: 601, msg: "文件大小最大为1MB" }, 200);
    }
    //限制文件类型
    if (!ret || !mimeTypes.has(ret.ext)) {
      throw new HttpException(
        { code: 602, msg: "只允许上传jpg、png、jpeg" },
        200
      );
    }

    const data = this.userService.uploadHead(
      file.buffer,
      `${v4()}.${ret.ext}`,
      request.headers.token as string
    );
    if (data) {
      return success("上传成功", { path: data });
    }
    throw new HttpException({ code: 603, msg: "文件生成失败" }, 200);
  }

中间件

为了省事不再二次查询,就将数据挂载到body上了

import {
  HttpException,
  Inject,
  Injectable,
  NestMiddleware,
} from "@nestjs/common";
import { Request, Response, NextFunction } from "express";
import { CacheRedisService } from "src/cache-redis/cache-redis.service";
@Injectable()
export class UserMiddleware implements NestMiddleware {
  constructor(
    @Inject(CacheRedisService) private readonly redis: CacheRedisService
  ) {}
  async use(req: Request, res: Response, next: NextFunction) {
    const token = req.headers.token ?? "";
    if (!token) {
      throw new HttpException({ code: 501, msg: "令牌不存在" }, 200);
    }
    const data = await this.redis.get(token.toString());
    if (data === null) {
      throw new HttpException({ code: 502, msg: "令牌异常" }, 200);
    }
    const user = data;
    Object.assign(req.body, { user });
    next();
  }
}