目录设计routes存放路由 controller存放控制器 services管理model层 router->controller-services->mysql

image.png

3-1 公共配置

错误信息管理

根据接口错误 返回对应错误信息

src/constants/errorInfo.ts

  1. /**
  2. * @description 失败信息集合,包括 code 和 message
  3. * @author 拔都
  4. */
  5. export default {
  6. // 用户名已存在
  7. registerUserNameExistInfo: {
  8. code: 10001,
  9. message: '用户名已存在'
  10. },
  11. // 注册失败
  12. registerFailInfo: {
  13. code: 10002,
  14. message: '注册失败,请重试'
  15. },
  16. // 用户名不存在
  17. registerUserNameNotExistInfo: {
  18. code: 10003,
  19. message: '用户名未存在'
  20. },
  21. // 登录失败
  22. loginFailInfo: {
  23. code: 10004,
  24. message: '登录失败,用户名或密码错误'
  25. },
  26. // 未登录
  27. loginCheckFailInfo: {
  28. code: 10005,
  29. message: '您尚未登录'
  30. },
  31. // 修改密码失败
  32. changePasswordFailInfo: {
  33. code: 10006,
  34. message: '修改密码失败,请重试'
  35. },
  36. // 用户信息失败
  37. getUserInfoFailInfo: {
  38. code: 10007,
  39. message: '用户信息获取失败 token验证无效'
  40. },
  41. getUserListFailInfo: {
  42. code: 10008,
  43. message: '用户列表获取失败, 请重试'
  44. },
  45. editUserInfoFailInfo: {
  46. code: 10009,
  47. message: '用户信息修改失败, 请重试'
  48. },
  49. deleteUserInfoFailInfo: {
  50. code: 10010,
  51. message: '用户删除失败, 请重试'
  52. },
  53. updateUserRoleFailInfo: {
  54. code: 10011,
  55. message: '用户角色修改失败,请重试'
  56. }
  57. }

存放密钥

src/config/auth.ts

  1. // jwt加密用
  2. export const jwtSecret = 'vue3-admin-jwt'
  3. // MD5加密用(比如用户密码 需要加密后再存放到数据库)
  4. export const userSecret = 'vue3-admin-user-password'

响应封装

创建成功和失败两个响应类

src/utils/Response.ts

  1. // 请求响应封装
  2. interface ResponseData {
  3. code: number;
  4. data?: any;
  5. message?: string;
  6. }
  7. class BaseResponse {
  8. public code!: number;
  9. public data: any;
  10. public message!: string;
  11. constructor({ code, data, message }: ResponseData) {
  12. this.code = code
  13. if (data) this.data = data
  14. if (message) this.message = message
  15. }
  16. }
  17. export class SuccessResponse extends BaseResponse {
  18. constructor(data: any) {
  19. super({
  20. code: 0,
  21. data
  22. })
  23. }
  24. }
  25. export class ErrorResponse extends BaseResponse {
  26. constructor(code: number, message: string) {
  27. super({
  28. code,
  29. message
  30. })
  31. }
  32. }

md5加密方法

src/utils/createMD5.ts

  1. import crypto from 'crypto'
  2. import { userSecret } from '../config/auth'
  3. // md5加密
  4. export const createMd5 = (content: any) => {
  5. const md5 = crypto.createHash('md5')
  6. return md5.update(`${content}_${userSecret}`).digest('hex')
  7. }

初始tsconfig.json

  1. tsc --init

3-2 开发注册接口

效果图

user表目前是空

image.png
发送注册请求
image.png
注册后:
image.png
再次点击注册 重复注册
image.png

创建auth路由

auth里的路由负责用户登录注册认证和用户信息获取

image.png
src/routes/auth.ts

route里需要调相应controller

  1. import Router from '@koa/router'
  2. import { registerController } from '../controller/auth'
  3. const router = new Router({
  4. prefix: '/api/auth'
  5. })
  6. /**
  7. * 用户注册接口
  8. * /auth/register
  9. */
  10. router.post('/register', async ctx => {
  11. ctx.body = await registerController(ctx.request.body)
  12. })
  13. export default router

创建controller

src/controller/auth.ts

  1. import { RegisterModel } from '../db/models/user'
  2. import { createUser, getUserInfo } from '../services/auth'
  3. import { ErrorResponse, SuccessResponse } from '../utils/Response'
  4. import errorInfo from '../constants/errorInfo'
  5. import { createMd5 } from '../utils/createMD5'
  6. const {
  7. registerUserNameExistInfo,
  8. registerFailInfo
  9. } = errorInfo
  10. export const registerController = async (params: RegisterModel) => {
  11. const { username, password } = params
  12. // 注册前先看下用户是否已注册 getUserInfo services
  13. const userInfo = await getUserInfo({ username })
  14. if (userInfo) { // 如果已注册
  15. // 用户已注册
  16. const { code, message } = registerUserNameExistInfo
  17. return new ErrorResponse(code, message)
  18. }
  19. // 用户不存在
  20. try {
  21. await createUser({ // 创建用户
  22. ...params,
  23. password: createMd5(password)
  24. })
  25. return new SuccessResponse({})
  26. } catch (err) { // 注册失败
  27. console.log(err.message, err.stack)
  28. const { code, message } = registerFailInfo
  29. return new ErrorResponse(code, message)
  30. }
  31. }

创建services

src/services/types.ts

services下类型管理

  1. export interface UserWhereProps {
  2. username: string;
  3. password?: string;
  4. id?: number;
  5. }

src/services/auth.ts

  1. import UserModel, { RegisterModel, UserModelProps } from '../db/models/user'
  2. import { UserWhereProps } from './types'
  3. import { createMd5 } from '../utils/createMD5'
  4. /**
  5. * 创建用户
  6. */
  7. export const createUser = async ({ username, password, email, mobile, status, avatar }: RegisterModel) => {
  8. const result = await UserModel.create({
  9. username,
  10. password,
  11. email,
  12. mobile,
  13. status
  14. })
  15. return result.toJSON()
  16. }
  17. /**
  18. * 根据用户名 获取用户信息
  19. * @param username 用户名
  20. * @param password 密码
  21. * @param id 用户id
  22. * @returns 用户信息
  23. */
  24. export const getUserInfo = async ({ username, password, id }: UserWhereProps): Promise<UserModelProps | null> => {
  25. const where: UserWhereProps = {
  26. username
  27. }
  28. if (password) {
  29. where.password = createMd5(password)
  30. }
  31. if (typeof id != 'undefined') {
  32. where.id = id
  33. }
  34. const result = await UserModel.findOne({
  35. attributes: {
  36. exclude: ['password', 'createdAt', 'updatedAt']
  37. },
  38. where,
  39. })
  40. if (result == null) return null
  41. return result.toJSON() as UserModelProps
  42. }

models/user.ts 类型声明小修改

注册插入数据时,允许avatar字段可选,后续 我们可以在个人设置里修改上传

image.png

  1. import {
  2. Model,
  3. DataTypes,
  4. Optional
  5. } from 'sequelize'
  6. import seq from '../seq'
  7. // sequelize+typescript 参考文档
  8. // https://sequelize.org/master/manual/typescript.html
  9. // model类型
  10. export interface UserModelProps {
  11. id: number;
  12. username: string;
  13. password: string;
  14. email: string | null;
  15. mobile: string | null;
  16. avatar: string;
  17. isSuper: 0 | 1;
  18. status: 0 | 1;
  19. }
  20. // 注册接口params类型 id 和 isSuper创建时候可以不用定义自动分配
  21. export type RegisterModel = Omit<UserModelProps, 'id'|'isSuper'>
  22. // 在“User.build”和“User.create”调用中,有些属性是可选的
  23. interface UserCreationAttributes extends Optional<UserModelProps, "id" | "isSuper" | "status" | "avatar"> {}
  24. // Model实例接口
  25. interface UserInstance
  26. extends Model<UserModelProps, UserCreationAttributes>,
  27. UserModelProps {}
  28. // 创建User模型 数据表的名字是users
  29. const User = seq.define<UserInstance>('User', {
  30. id: {
  31. primaryKey: true,
  32. type: DataTypes.INTEGER.UNSIGNED,
  33. autoIncrement: true
  34. },
  35. // id会自动创建 并设为主键、自增
  36. username: {
  37. type: DataTypes.STRING,
  38. allowNull: false,
  39. comment: '用户名'
  40. },
  41. password: {
  42. type: DataTypes.STRING,
  43. allowNull: false,
  44. comment: '密码'
  45. },
  46. email: {
  47. type: DataTypes.STRING,
  48. comment: '用户邮箱'
  49. },
  50. mobile: {
  51. type: DataTypes.STRING,
  52. comment: '手机号'
  53. },
  54. avatar: {
  55. type: DataTypes.STRING,
  56. comment: '头像'
  57. },
  58. isSuper: {
  59. type: DataTypes.BOOLEAN, // TINYINT(1)
  60. comment: '超级管理员 1是 0不是',
  61. defaultValue: 0
  62. },
  63. status: {
  64. type: DataTypes.BOOLEAN, // TINYINT(1)
  65. comment: '账户禁用状态 1正常 0禁用',
  66. defaultValue: 1
  67. }
  68. })
  69. export default User

本节参考源码

https://gitee.com/brolly/vue3-admin-server/commit/b5e87e90202de55937fef9ac9140cbc7433ee737