Skip to content

Nest 短链

  • 短链就是 将长链接转换为我自己的短链人为的记个数之类的,方便用户记忆和传播,比如微博的短链,微信的短链等。

流程

注意

  1. 利用唯一加密算法 生成压缩码

  2. 利用一个随机数 随机生成 0-61 的数字,然后转成字符。 62 的 6 次方,范围有 580 亿 然后生成的时候去数据库查询一下有没有,没有就存入数据库,有就重新生成

  3. 将压缩码和长链接存入数据库

  4. 利用 302 重定向到长连接

建表

两张表 unique_code表和``short_long_map`表

unique_code 表

  • id 主键
  • code 压缩码
  • status 状态 0 未使用 1 已使用
ts
model short_long_map {
  id         Int      @id @default(autoincrement())
  shortUrl   String   @db.VarChar(10)
  longUrl    String   @db.VarChar(255)
  createTime DateTime @default(now()) @db.Timestamp(0)
}

short_long_map 表

  • id 主键
  • shortUrl 短链
  • longUrl 长链接
  • create_time 创建时间
ts
model unique_code {
  id     Int    @id @default(autoincrement())
  status Int
  code   String @unique(map: "unique") @db.VarChar(255)
}

使用

安装依赖

bash
npm install base62

代码

    1. 生成短链压缩码
    1. 存到短链状态表
    1. 存到短链和长链关联表并且把短链状态置为 1
    1. 重定向

提供者代码

ts
import { Inject, Injectable } from "@nestjs/common";
import { RedisService } from "../redis/redis.service";
import { ConfigService } from "@nestjs/config";
import { JwtServiceAll } from "../jwt/jwt.service";
// 数据库
import { PrismadbService } from "../prisma/prisma.service";
// 生成短链base62
const base62 = require("base62/lib/ascii");
@Injectable()
export class DuanLianService {
  // 注入配置文件
  @Inject()
  private readonly configService: ConfigService;
  // 注入Redis
  @Inject()
  private readonly redisService: RedisService;
  // 注入jwt
  @Inject()
  private readonly jwtService: JwtServiceAll;
  // 注入数据库
  @Inject()
  private readonly prismadbService: PrismadbService;

  // 1. 生成短链压缩码
  async setduanliancode(len) {
    let str = "";
    for (let i = 0; i < len; i++) {
      const num = Math.floor(Math.random() * 62);
      str += base62.encode(num);
    }
    return str;
  }

  // 2. 存到短链状态表
  async setduanlian(len) {
    // 生成压缩码
    const res = await this.setduanliancode(len);
    // 查询
    const uniqueId = await this.prismadbService.unique_code.findFirst({
      where: {
        code: res,
      },
    });
    if (uniqueId) {
      return this.setduanlian(len);
    } else {
      const result = await this.prismadbService.unique_code.create({
        data: {
          code: res,
          status: 0, // 0:未使用 1:已使用
        },
      });
      return result.code; // 返回短链
    }
  }

  //3 存到短链和长链关联表并且把短链状态置为1
  async setduanlianlong(len, long_link) {
    // 存到短链状态表
    const result = await this.setduanlian(len);
    // 插入
    await this.prismadbService.short_long_map.create({
      data: {
        shortUrl: result,
        longUrl: long_link,
      },
    });
    // 更新短链状态表 0 变1
    await this.prismadbService.unique_code.update({
      where: {
        code: result,
      },
      data: {
        status: 1,
      },
    });
    return result;
  }

  // 4 重定向
  async getLongUrl(code) {
    const result = await this.prismadbService.short_long_map.findFirst({
      where: {
        shortUrl: code,
      },
    });
    if (!result) {
      return null;
    } else {
      return result.longUrl;
    }
  }
}

控制器代码

ts
import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
  Query,
  Redirect,
  Res,
} from "@nestjs/common";
import { UserService } from "./user.service";
import { GlobalCheckException } from "src/common/globalcheck.exception";

@Controller("user")
export class UserController {
  constructor(private readonly userService: UserService) {}

  // 短链服务
  @Get("setduanlian")
  async setduanlian(@Query() query: any) {
    const result = await this.userService.setduanlianlong(6, query.longUrl);
    return {
      data: "http://2.jsopy.com:5000/user/" + result, // 你的域名和端口号
    };
  }

  // 增加重定向接口,这里要么返回的是二级域名+压缩码,把二级域名放到白名单。或者自己新建一个路径,放到白名单
  // 这样全局拦截器不会拦截
  @Get(":code")
  @Redirect("./", 302)
  async jump(@Param("code") code, @Res() res: Response) {
    const longUrl = await this.userService.getLongUrl(code);
    return {
      url: longUrl,
    };
  }
}