• TS项目实战三:Express实现登录注册功能后端


      使用express实现用户登录注册功能,使用ts进行代码开发,使用mysql作为数据库,实现用户登录、登录状态检测、验证码获取接口及用户注册相关接口功能的实现。
    源码下载:[点击下载]
    (https://download.csdn.net/download/m0_37631110/88909002)
    讲解视频:

    1. 开发视频:

      TS实战项目十四:Express项目创建

    2. B站视频:

      TS实战项目十四:Express项目创建

    3. 西瓜视频:
      https://www.ixigua.com/7331627299491709477
      在这里插入图片描述

    一、知识点

    1. tsc编译
    2. tsconfig.json配置项
    3. 模块定义及导入导出
    4. 类定义
    5. 参数属性
    6. 存取器
    7. 继承
    8. 抽象类
    9. 抽象方法
    10. 接口
    11. expres对接mysql数据库
    12. express-session
    13. bcryptjs
    14. express-jwt
    15. jsonwebtoken
    16. svg-captcha
    17. 枚举
    18. 静态变量
    19. 接口定义
    20. async/await

    二、功能规划

      使用express框架搭建web服务器,切换js为ts方式开发,使用svg-captcha库提供图片验证码信息,使用express-jwt+jsonwebtoken实现登录状态检测及token生成。使用bcryptjs实现用户登录密码的加密及验证处理。
      实现三层分离,controller层进行接口控制,service进行业务逻辑处理,dao层进行数据库操作,使用mysql数据库。

    三、项目创建

    1. 安装express生成器:npm install -g express-generator
      在这里插入图片描述

    2. 创建express项目:express tsexpress
      在这里插入图片描述

    3. 安装ts:npm install typescript --save
      在这里插入图片描述

    4. 安装ts相关依赖:
      (1) npm install @types/node --save-dev
      请添加图片描述
      (2) npm install @types/express --save-dev
      请添加图片描述

    5. 安装svg-captcha:npm install svg-captcha --save
      请添加图片描述

    6. 安装rotating-file-stream:npm install rotating-file-stream --save
      请添加图片描述

    7. 安装mysql:npm install mysql --save
      请添加图片描述
      npm install @types/mysql --save-dev
      请添加图片描述

    8. 安装morgan:npm install morgan --save
      请添加图片描述
      npm install @types/morgan --save-dev
      请添加图片描述

    9. 安装jsonwebtoken:npm install jsonwebtoken --save
      请添加图片描述
      npm install @types/jsonwebtoken --save-dev
      请添加图片描述

    10. 安装express-session:npm install express-session --save
      请添加图片描述
      npm install @types/express-session --save-dev
      请添加图片描述

    11. 安装express-jwt:npm install express-jwt --save
      请添加图片描述

    12. 安装bcryptjs:npm install bcryptjs --save
      请添加图片描述
      npm install @types/bcryptjs --save-dev
      请添加图片描述

    13. 安装cookie-parser:npm install cookie-parser --save
      请添加图片描述
      npm install @types/cookie-parser --save-dev
      请添加图片描述

    14. 使用ts-node ./bin/www启动项目,启动会报错,需要将app等文件中的代码进行调整,写成ts的写法。

    15. 项目目录结构:
      请添加图片描述

    四、代码实现

    1. 项目配置文件package.json
    {
      "name": "demo3",
      "version": "0.0.0",
      "private": true,
      "scripts": {
        "start": "ts-node ./bin/www"
      },
      "dependencies": {
        "@types/bcryptjs": "^2.4.6",
        "@types/express-session": "^1.17.10",
        "@types/jsonwebtoken": "^9.0.5",
        "@types/mysql": "^2.15.25",
        "bcryptjs": "^2.4.3",
        "cookie-parser": "~1.4.4",
        "debug": "~2.6.9",
        "express": "~4.16.1",
        "express-jwt": "^8.4.1",
        "express-session": "^1.18.0",
        "http-errors": "~1.6.3",
        "jade": "~1.11.0",
        "jsonwebtoken": "^9.0.2",
        "morgan": "~1.9.1",
        "mysql": "^2.18.1",
        "rotating-file-stream": "^3.2.1",
        "svg-captcha": "^1.4.0",
        "typescript": "^5.3.3"
      },
      "devDependencies": {
        "@types/cookie-parser": "^1.4.6",
        "@types/express": "^4.17.21",
        "@types/morgan": "^1.9.9",
        "@types/node": "^20.11.16"
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    1. app.ts
    import cookieParser from 'cookie-parser';
    import express, {
      NextFunction,
      Request,
      Response,
    } from 'express';
    import session from 'express-session';
    import createError from 'http-errors';
    import jwt from 'jsonwebtoken';
    import logger from 'morgan';
    import path from 'path';
    import * as rfs from 'rotating-file-stream'; // version 2.x
    
    import { SYSTEM } from './src/config/Config';
    import indexRouter from './src/routes/index';
    import jwtToken, { lessPaths } from './src/security/jwtToken';
    
    const app = express();
    
    // view engine setup
    app.set('views', path.join(__dirname, './src/views'));
    app.set('view engine', 'jade');
    
    //系统日志
    var accessLogStream = rfs.createStream('access.log', {
      interval: '1d', // rotate daily
      path: path.join(__dirname, './logs/log')
    })
    app.use(logger(':method :url :status :res[content-length] - :response-time ms', { stream: accessLogStream }));
    
    app.use(express.json());
    app.use(express.urlencoded({ extended: false }));
    app.use(cookieParser());
    
    
    app.use(session({
      secret: SYSTEM.privateKey,
      resave: false,
      saveUninitialized: true,
      cookie: { secure: false }
    }))
    
    app.use(express.static(path.join(__dirname, 'public')));
    
    
    //加登陆的拦截
    app.use(/.*/, function (err: any, req: Request, res: Response, next: NextFunction) {
      let less = lessPaths.find(item => {
        return req.url.startsWith(item);
      })
      if (less) {
        next();
        return;
      }
      if (!req.headers.authorization) {
        res.status(401).send({
          code: 401,
          msg: '请先登录!'
        });
      } else {
        jwt.verify(req.headers.authorization.split(" ")[1], SYSTEM.privateKey, (error, decoded) => {
          if (error) {
            res.status(401).send({
              code: 401,
              msg: 'token错误或已过期'
            });
          } else {
            req.headers.user = JSON.stringify(decoded);
            next();
          }
        })
      }
    });
    
    //是否登陆的判断
    app.use(jwtToken);
    
    //注册路由
    app.use('/', indexRouter);
    
    // catch 404 and forward to error handler
    app.use(function (req: Request, res: Response, next: NextFunction) {
      next(createError(404));
    });
    
    // error handler
    app.use(function (err: any, req: Request, res: Response, next: NextFunction) {
      if (err.name === "UnauthorizedError") {
        res.status(401).send({
          code: 401,
          msg: '请先登录!'
        });
      }
      // set locals, only providing error in development
      res.locals.message = err.message;
      res.locals.error = req.app.get('env') === 'development' ? err : {};
    
      // render the error page
      res.status(err.status || 500);
      res.render('error');
    });
    console.log('启动成功');
    
    module.exports = app;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    1. utils/DbUtils.ts
    // const mysql = require('mysql')
    import mysql, {
      Connection,
      ConnectionConfig,
    } from 'mysql';
    
    /**
     * 初始化链接
     * @param config 数据库的配置
     */
    function init(config: ConnectionConfig): Connection {
        const connection = mysql.createConnection(config)
        connection.connect();
        return connection;
    }
    
    // connection.query('SELECT 1 + 1 AS solution', (err, rows, fields) => {
    //     if (err) throw err
    
    //     console.log('The solution is: ', rows[0].solution)
    // })
    
    // connection.end()
    
    export default {
        init
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    1. service/BaseService.ts
    import BaseDao from '../dao/BaseDao';
    import BaseEntity from '../entity/BaseEntity';
    import DataEntity from '../entity/DataEntity';
    import DelFlag from '../entity/DelFlag';
    
    /**
     * 业务处理的基础类
     */
    export default abstract class BaseService<T extends BaseEntity> {
        constructor(protected _dao: BaseDao<T>) {
        }
    
        /**
         * 保存数据
         * @param data  要保存的数据
         */
        save(data: T): void {
            if (data instanceof DataEntity) {
                (<DataEntity>data).createData = new Date();
                (<DataEntity>data).updateData = new Date();
                (<DataEntity>data).delflag = DelFlag.NOMAL;
            }
            this.beforeSave(data);
            this._dao.save(data).then(() => {
                //TODO 记录日志
            }).catch((err => {
                throw err;
            }));
        }
        /**
         * 数据保存之前,进行数据默认值的填充
         */
        protected abstract beforeSave(data: T): void;
        /**
         * 更新数据
         * @param data  要保存的数据
         */
        update<K extends DataEntity>(data: K): void {
            if (data instanceof DataEntity) {
                (<DataEntity>data).updateData = new Date();
            }
            this.beforeUpdate(data);
            let fileds: string[] = this.getFileds();
            this._dao.update(data, fileds).then(() => {
                //TODO 记录日志
            }).catch((err => {
                throw err;
            }));
        }
        /**
         * 获取需要更新的字段
         */
        protected abstract getFileds(): string[];
        /**
         * 数据保存之前,进行数据默认值的填充
         */
        protected abstract beforeUpdate<K extends DataEntity>(data: K): void;
        /**
         * 删除数据
         * @param data  要保存的数据
         */
        deleteData(id: number): void {
            this._dao.deleteData(id).then(() => {
                //TODO 记录日志
            }).catch((err => {
                throw err;
            }));
        }
        /**
         * 删除数据
         * @param data  要保存的数据
         */
        async get<K extends DataEntity>(id: number): Promise<K | null> {
            return this._dao.get(id);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    1. service/system/UserService.ts
    import bcrypt from 'bcryptjs';
    
    import UserDao from '../../dao/system/UserDao';
    import DataEntity from '../../entity/DataEntity';
    import User, { UserStatus } from '../../entity/system/User';
    import BaseService from '../BaseService';
    
    export default class UserService extends BaseService<User> {
        protected getFileds(): string[] {
            return ['_account', '_name', '_phone', '_sex', '_status', '_remork'];
        }
        protected beforeSave(data: User) {
            data.status = UserStatus.NOMAL;
            let salt = bcrypt.genSaltSync(10);
            data.password = bcrypt.hashSync(data.password, salt);
        }
        protected beforeUpdate<K extends DataEntity>(data: K) {
        }
        constructor() {
            super(new UserDao())
        }
        /**
         * 通过账号查找对应的用户
         * @param account  账号信息
         */
        async findByAccount(account: string): Promise<User | null> {
            return (<UserDao>this._dao).findByAccount(account);
        }
        /**
        * 通过电话号码查找对应的用户
        * @param account  电话号码
        */
        async findByPhone(phone: string): Promise<User | null> {
            return (<UserDao>this._dao).findByPhone(phone);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    1. security/jwtToken.ts
    import express from 'express';
    import { expressjwt } from 'express-jwt';
    
    import { SYSTEM } from '../config/Config';
    
    /**
     * 不需要鉴权的地址
     */
    export const lessPaths = ["/register", "/register/captcha", "/login", "/login/captcha", "/public/"];
    
    export default expressjwt({
        secret: SYSTEM.privateKey,
        algorithms: ['HS256'],
        getToken: (req: express.Request): string | Promise<string> | undefined => {
            if (
                req.headers.authorization &&
                req.headers.authorization.split(" ")[0] === "Bearer"
            ) {
                return req.headers.authorization.split(" ")[1];
            } else if (req.query && req.query.token) {
                return <string>req.query.token;
            }
            return undefined;
        }
    }).unless({ path: lessPaths });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    1. routes/index.ts
    // var express = require('express');
    import express from 'express';
    
    import index from '../controller/index';
    
    var router = express.Router();
    router.use(index)
    
    // module.exports = router;
    export default router;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. entity/DelFlag.ts
    /**
     * 数据的删除状态标识
     */
    enum DelFlag {
        //正常
        NOMAL = 1,
        //删除
        DELETE = 2
    }
    export default DelFlag;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    1. entity/DataEntity.ts
    import BaseEntity from './BaseEntity';
    import DelFlag from './DelFlag';
    
    /**
     * 基础实体
     */
    export default abstract class DataEntity extends BaseEntity {
        /**
         * 主键
         */
        private _id: number = 0;
        /**
         * 数据的创建时间
         */
        private _createDate: Date = new Date();
        /**
         * 数据的更新时间
         */
        private _updateDate: Date = new Date();
        /**
         * 备注
         */
        private _remork: string = "";
        /**
         * 状体:
         */
        private _delflag: DelFlag = 1;
    
        set id(id: number) {
            this._id = id;
        }
        get id() {
            return this._id;
        }
        set createData(createData: Date) {
            this._createDate = createData;
        }
        get createData() {
            return this._createDate;
        }
        set updateData(updateData: Date) {
            this._updateDate = updateData;
        }
        get updateData() {
            return this._updateDate;
        }
        set remork(remork: string) {
            this._remork = remork;
        }
        get remork() {
            return this._remork;
        }
        set delflag(delflag: DelFlag) {
            this._delflag = delflag;
        }
        get delflag() {
            return this._delflag;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    1. entity/BaseEntity.ts
    export default abstract class BaseEntity {
    }
    
    • 1
    • 2
    1. entity/system/User.ts
    import DataEntity from '../DataEntity';
    
    /**
     * 用户的状态
     */
    export enum UserStatus {
        NOMAL = 1,
        LOCK
    }
    
    /**
     * 用户实体
     */
    export default class User extends DataEntity {
        /**
         * 账号
         */
        private _account: string = '';
        /**
         * 姓名
         */
        private _name: string = '';
        /**
         * 电话
         */
        private _phone: string = '';
        /**
         * 性别
         */
        private _sex: string = '';
        /**
         * 密码
         */
        private _password: string = '';
        /**
         * 状态1正常、2锁定
         */
        private _status: number = UserStatus.NOMAL;
    
        set account(account: string) {
            this._account = account;
        }
        get account() {
            return this._account;
        }
        set name(name: string) {
            this._name = name;
        }
        get name() {
            return this._name;
        }
        set phone(phone: string) {
            this._phone = phone;
        }
        get phone() {
            return this._phone;
        }
        set sex(sex: string) {
            this._sex = sex;
        }
        get sex() {
            return this._sex;
        }
        set password(password: string) {
            this._password = password;
        }
        get password() {
            return this._password;
        }
        set status(status: UserStatus) {
            this._status = status;
        }
        get status() {
            return this._status;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    1. dao/BaseDao.ts
    import {
        Connection,
        FieldInfo,
        MysqlError,
    } from 'mysql';
    
    import { DB } from '../config/Config';
    import BaseEntity from '../entity/BaseEntity';
    import DataEntity from '../entity/DataEntity';
    import DbUtils from '../utils/DbUtils';
    
    /**
     * dao基础类
     */
    export default abstract class BaseDao<T extends BaseEntity> {
        /**
         * 获取数据库链接
         */
        protected getConn(): Connection {
            return DbUtils.init(DB);
        }
        /**
         * 获取要操作的实体表名
         */
        protected abstract getTableName(): string;
        /**
         * 创建新的实体
         */
        protected abstract newInsance(): T;
        /**
         * 保存数据
         * @param data 要保存的数据
         */
        save(data: T): Promise<unknown> {
            let conn = this.getConn();
            return new Promise((resolve, reject) => {
                conn.query('INSERT INTO ' + this.getTableName() + ' SET ?', data, function (err: MysqlError | null, results?: any, fields?: FieldInfo[]) {
                    if (err) {
                        throw err
                    };
                    resolve({
                        err, results, fields
                    });
                });
            }).finally(() => {
                conn.end();
            })
        }
        /**
         * 更新数据
         */
        update<K extends DataEntity>(data: K, fileds: string[]): Promise<unknown> {
            let conn = this.getConn();
            return new Promise((resolve, reject) => {
                let sql = 'UPDATE ' + this.getTableName() + ' SET ';
                let values: any[] = [];
                fileds.forEach((filed, index) => {
                    if (index > 0) {
                        sql += ',';
                    }
                    sql += filed + '=?';
                    let value: any = null;
                    for (const key in data) {
                        if (key === filed) {
                            value = data[key];
                        }
                    }
                    values.push(value);
                })
                values.push(data.id);
                conn.query(sql + ' where _id=?', values, function (err: MysqlError | null, results?: any, fields?: FieldInfo[]) {
                    if (err) {
                        throw err
                    };
                    resolve({
                        err, results, fields
                    });
                });
            }).finally(() => {
                conn.end();
            })
        }
        /**
         * 删除数据
         * @param data 
         * @returns 
         */
        deleteData<K extends DataEntity>(id: Number): Promise<unknown> {
            let conn = this.getConn();
            return new Promise((resolve, reject) => {
                conn.query('DELETE FROM  ' + this.getTableName() + ' where _id=?', [id], function (err: MysqlError | null, results?: any, fields?: FieldInfo[]) {
                    if (err) {
                        throw err
                    };
                    resolve({
                        err, results, fields
                    });
                });
            }).finally(() => {
                conn.end();
            })
        }
        /**
         * 查询数据
         */
        async get<K extends DataEntity>(id: Number): Promise<K | null> {
            let conn = this.getConn();
            return await new Promise<K | null>((resolve, reject) => {
                conn.query('select * FROM  ' + this.getTableName() + ' where _id=?', [id], (err: MysqlError | null, results?: any, fields?: FieldInfo[]) => {
                    if (err) {
                        throw err
                    };
                    if (!results || results === null || results.length <= 0) {
                        resolve(null);
                    } else {
                        resolve(Object.assign(this.newInsance(), results[0]));
                    }
                });
            }).then().finally(() => {
                conn.end();
            })
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    1. dao/system/UserDao.ts
    import {
        FieldInfo,
        MysqlError,
    } from 'mysql';
    
    import User from '../../entity/system/User';
    import BaseDao from '../BaseDao';
    
    /**
     * 用户dao的处理
     */
    export default class UserDao extends BaseDao<User> {
        protected newInsance(): User {
            return new User();
        }
        protected getTableName(): string {
            return 'user';
        }
        /**
         * 通过账号查找对应的用户信息
         */
        async findByAccount(account: string): Promise<User | null> {
            let conn = this.getConn();
            return await new Promise<User | null>((resolve, reject) => {
                conn.query('select * FROM  ' + this.getTableName() + ' where _account=?', [account], (err: MysqlError | null, results?: any, fields?: FieldInfo[]) => {
                    if (err) {
                        throw err
                    };
                    if (!results || results === null || results.length <= 0) {
                        resolve(null);
                    } else {
                        resolve(Object.assign(this.newInsance(), results[0]));
                    }
                });
            }).then().finally(() => {
                conn.end();
            })
        }
        /**
        * 通过电话号码查找对应的用户信息
        */
        async findByPhone(phone: string): Promise<User | null> {
            let conn = this.getConn();
            return await new Promise<User | null>((resolve, reject) => {
                conn.query('select * FROM  ' + this.getTableName() + ' where _phone=?', [phone], (err: MysqlError | null, results?: any, fields?: FieldInfo[]) => {
                    if (err) {
                        throw err
                    };
                    if (!results || results === null || results.length <= 0) {
                        resolve(null);
                    } else {
                        resolve(Object.assign(this.newInsance(), results[0]));
                    }
                });
            }).then().finally(() => {
                conn.end();
            })
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    1. controller/index.ts
    // var express = require('express');
    import express from 'express';
    
    import LoginController from './login/LoginController';
    import RegisterController from './register/RegisterController';
    import UserController from './system/UserController';
    
    var router = express.Router();
    
    router.use(UserController);
    router.use(LoginController);
    router.use(RegisterController);
    
    // module.exports = router;
    export default router;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    1. controller/BaseViewController.ts
    import {
        NextFunction,
        Request,
        Response,
    } from 'express';
    
    import DataEntity from '../entity/DataEntity';
    import BaseService from '../service/BaseService';
    import BaseController from './BaseController';
    
    /**
     * 数据查看
     */
    export default abstract class BaseViewContorller<T extends DataEntity> extends BaseController<T> {
    
        constructor(protected service: BaseService<T>) {
            super();
        }
        /**
         * 创建实体实例
         */
        protected abstract newInstance(): T;
        /**
         * 获取数据详情
         * @param req 请求
         * @param res 返回
         * @param next 
         */
        get(req: Request, res: Response, next: NextFunction) {
            let id = req.query.id;
            if (!id) {
                res.status(400).send({
                    code: 400,
                    msg: '参数为空'
                });
                return;
            }
            this.service.get(parseInt(<string>id)).then((value: DataEntity | null) => {
                res.send({
                    code: 200,
                    msg: '',
                    data: value
                })
            }).catch((err: Error) => {
                res.status(500).send({
                    code: 500,
                    msg: '处理失败'
                });
            });
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    1. controller/BaseEditController.ts
    import {
        NextFunction,
        Request,
        Response,
    } from 'express';
    
    import DataEntity from '../entity/DataEntity';
    import BaseService from '../service/BaseService';
    import BaseViewContorller from './BaseViewController';
    
    /**
     * 数据查看
     */
    export default abstract class BaseEntityContorller<T extends DataEntity> extends BaseViewContorller<T> {
        constructor(service: BaseService<T>) {
            super(service);
        }
        /**
         * 校验数据的合法性
         */
        protected abstract validate(data: T): boolean;
        /**
         * 创建实体实例
         */
        protected abstract newInstance(): T;
    
        /**
         * 添加数据
         * @param req 请求
         * @param res 返回
         * @param next 
         */
        save(req: Request, res: Response, next: NextFunction) {
            let param = req.body;
            if (!param) {
                res.status(400).send({
                    code: 400,
                    msg: '参数为空'
                });
                return;
            }
            //做数据的具体校验
            let entity: T = this.newInstance();
            if (!this.validate(Object.assign(entity, param))) {
                res.status(400).send({
                    code: 400,
                    msg: '参数不合法'
                });
                return;
            }
            //数据权限校验
            try {
                this.service.save(entity);
                res.send({
                    code: 200,
                    msg: ''
                })
            } catch (error) {
                console.error(error);
                res.status(500).send({
                    code: 500,
                    msg: '处理失败'
                });
            }
        }
        /**
         * 更新数据
         * @param req 请求
         * @param res 返回
         * @param next 
         */
        update(req: Request, res: Response, next: NextFunction) {
            let param = req.body;
            if (!param) {
                res.status(400).send({
                    code: 400,
                    msg: '参数为空'
                });
                return;
            }
            //做数据的具体校验
            let entity: T = this.newInstance();
            if (!this.validate(Object.assign(entity, param))) {
                res.status(400).send({
                    code: 400,
                    msg: '参数不合法'
                });
                return;
            }
            //数据权限校验
            try {
                this.service.update(entity);
                res.send({
                    code: 200,
                    msg: ''
                })
            } catch (error) {
                console.error(error);
                res.status(500).send({
                    code: 500,
                    msg: '处理失败'
                });
            }
        }
    
        /**
         * 删除数据
         * @param req 
         * @param res 
         * @param next 
         */
        deleteData(req: Request, res: Response, next: NextFunction) {
            let id = req.query.id;
            if (!id) {
                res.status(400).send({
                    code: 400,
                    msg: '参数为空'
                });
                return;
            }
            try {
                this.service.deleteData(parseInt(<string>id));
                res.send({
                    code: 200,
                    msg: ''
                })
            } catch (error) {
                console.error(error);
                res.status(500).send({
                    code: 500,
                    msg: '处理失败'
                });
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    1. controller/BaseController.ts
    /**
     * 接口的基类
     */
    export default abstract class BaseController<T> {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. controller/system/UserController.ts
    // var express = require('express');
    import express from 'express';
    
    import User from '../../entity/system/User';
    import UserService from '../../service/system/UserService';
    import BaseEntityContorller from '../BaseEditController';
    
    /**
     * 用户的接口
     */
    export class UserController extends BaseEntityContorller<User> {
        constructor() {
            super(new UserService());
        }
        protected validate(data: User): boolean {
            if (!data.account) {
                return false;
            }
            if (!data.name) {
                return false;
            }
            return true;
        }
        protected newInstance(): User {
            return new User();
        }
    }
    
    var router = express.Router();
    const userController = new UserController();
    
    //获取
    router.get('/get', userController.get.bind(userController));
    //保存
    router.post('/save', userController.save.bind(userController));
    //更新
    router.post('/update', userController.update.bind(userController));
    //删除
    router.get('/delete', userController.deleteData.bind(userController));
    
    // module.exports = router;
    export default router;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    1. controller/register/RegisterController.ts
    // var express = require('express');
    import express from 'express';
    import svgCaptcha from 'svg-captcha';
    
    import { SYSTEM } from '../../config/Config';
    import User from '../../entity/system/User';
    import UserService from '../../service/system/UserService';
    import BaseController from '../BaseController';
    
    /**
     * 登陆接口
     */
    export class RegisterController extends BaseController<null> {
        /**
         * 用户服务
         */
        useService: UserService;
        constructor() {
            super();
            this.useService = new UserService();
        }
        /**
         * 生成验证码
         */
        captcha(req: express.Request, res: express.Response, next: express.NextFunction) {
            var captcha = svgCaptcha.create();
            req.session.captcha = captcha.text;
            req.session.captchaData = new Date().getTime();
            res.type('svg');
            res.status(200).send(captcha.data);
        }
        /**
         * 用户注册功能
         */
        async register(req: express.Request, res: express.Response, next: express.NextFunction) {
            /**
             * 1.检测参数是否合法,账号、密码、验证码等不能为空
             * 2.校验验证码是否合法:验证码是否正确、验证码是否过期
             * 3.检测账号是否重复
             * 4.保存账号信息
             */
            try {
                //验证码校验
                let captcha = req.body.captcha;
                if (!captcha) {
                    res.status(400).send({
                        code: 400,
                        msg: '验证码不能为空'
                    })
                    return;
                }
                //参数校验
                let user: User = new User();
                user.account = req.body.account;
                user.name = req.body.name;
                user.password = req.body.password;
                user.sex = req.body.sex;
                user.phone = req.body.phone;
                if (!user || !user.account || !user.name || !user.password || !user.phone) {
                    res.status(400).send({
                        code: 400,
                        msg: '参数不合法'
                    })
                    return;
                }
                //验证码校验
                let systemCaptcha = req.session.captcha;
                let systemCaptchaDate = req.session.captchaData;
                if (!systemCaptcha || !systemCaptchaDate) {
                    res.status(500).send({
                        code: 500,
                        msg: '验证码不合法'
                    })
                    return;
                }
                if (systemCaptcha !== captcha) {
                    res.status(500).send({
                        code: 500,
                        msg: '验证码错误'
                    })
                    return;
                }
                if (new Date().getTime() - systemCaptchaDate > SYSTEM.captchaTimeOut) {
                    res.status(500).send({
                        code: 500,
                        msg: '验证码已失效'
                    })
                    return;
                }
                //账号校验
                let systemUser = await this.useService.findByAccount(user.account);
                if (systemUser && systemUser.id) {
                    res.status(500).send({
                        code: 500,
                        msg: '改账号已注册,无法重复注册'
                    })
                    return;
                }
                //电话号码检测
                systemUser = await this.useService.findByPhone(user.phone);
                if (systemUser && systemUser.id) {
                    res.status(500).send({
                        code: 500,
                        msg: '改账号已注册,无法重复注册'
                    })
                    return;
                }
                this.useService.save(user);
                res.send({
                    code: 200,
                    msg: '注册成功'
                });
            } catch (error) {
                console.error(error);
                res.status(503).send({
                    code: 503,
                    msg: error
                })
                return;
            }
        }
    }
    
    var router = express.Router();
    const registerController = new RegisterController();
    
    //注册接口
    router.post('/register', registerController.register.bind(registerController));
    
    //获取验证码
    router.get('/register/captcha', registerController.captcha.bind(registerController));
    // module.exports = router;
    export default router;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    1. controller/login/LoginController.ts
    import bcrypt from 'bcryptjs';
    // var express = require('express');
    import express from 'express';
    import jwt from 'jsonwebtoken';
    import svgCaptcha from 'svg-captcha';
    
    import { SYSTEM } from '../../config/Config';
    import UserService from '../../service/system/UserService';
    import BaseController from '../BaseController';
    
    /**
     * 登陆接口
     */
    export class LoginController extends BaseController<null> {
        /**
         * 用户服务
         */
        useService: UserService;
        constructor() {
            super();
            this.useService = new UserService();
        }
        /**
         * 生成验证码
         */
        captcha(req: express.Request, res: express.Response, next: express.NextFunction) {
            var captcha = svgCaptcha.create();
            req.session.captcha = captcha.text;
            req.session.captchaData = new Date().getTime();
            res.type('svg');
            res.status(200).send(captcha.data);
        }
        /**
         * 登陆操作
         */
        async login(req: express.Request, res: express.Response, next: express.NextFunction) {
            /**
             * 1.检测参数是否合法,账号、密码、验证码等不能为空
             * 2.校验验证码是否合法:验证码是否正确、验证码是否过期
             * 3.账号密码是否合法的校验
             */
            let captcha = req.body.captcha;
            let account = req.body.account;
            let password = req.body.password;
            if (!captcha || !account || !password) {
                res.status(400).send({
                    code: 400,
                    msg: '参数为空'
                })
                return;
            }
            //验证码合法性的判断"wBxqQKlIOWlecyawht2W7dJTCI9DD_qC"
            let systemCaptcha = req.session.captcha;
            let systemCaptchaDate = req.session.captchaData;
            if (!systemCaptcha || !systemCaptchaDate) {
                res.status(500).send({
                    code: 500,
                    msg: '验证码不合法'
                })
                return;
            }
            if (systemCaptcha !== captcha) {
                res.status(500).send({
                    code: 500,
                    msg: '验证码错误'
                })
                return;
            }
            if (new Date().getTime() - systemCaptchaDate > SYSTEM.captchaTimeOut) {
                res.status(500).send({
                    code: 500,
                    msg: '验证码已失效'
                })
                return;
            }
            //账号密码合法性的判断
            let systemUser = await this.useService.findByAccount(account).catch(error => {
                console.error(error);
                res.status(503).send({
                    code: 503,
                    msg: '系统错误:' + error.message
                })
            });
            if (!systemUser) {
                res.status(500).send({
                    code: 500,
                    msg: '账号错误'
                })
                return;
            }
            if (!bcrypt.compareSync(password, systemUser.password)) {//TODO 需进行加密判断
                res.status(500).send({
                    code: 500,
                    msg: '密码错误'
                })
                return;
            }
            //生成token,并且返回登陆成功信息
            let token = jwt.sign({
                id: systemUser.id,
                name: systemUser.name
            }, SYSTEM.privateKey, {
                expiresIn: '1d' // 1天 
            });
            res.header("authorization", token);
            res.send({
                code: 200,
                msg: ''
            })
        }
    }
    
    var router = express.Router();
    const loginController = new LoginController();
    
    //登陆接口
    router.post('/login', loginController.login.bind(loginController));
    
    //获取验证码
    router.get('/login/captcha', loginController.captcha.bind(loginController));
    // module.exports = router;
    export default router;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    1. config/Config.ts
    /**
     * 数据库配置
     */
    export class DB {
        static host = 'localhost';
        static port = 3306;
        static user = "root";
        static password = 'Root123456*';
        static database = 'expressdemo';
        static charset = 'utf8mb4'
    }
    /**
     * 系统的一些配置项
     */
    export class SYSTEM {
        /**
         * 验证码的超时时间
         */
        static captchaTimeOut = 3 * 60 * 1000;
        /**
         * token加密的密钥
         */
        static privateKey = "junjunjun006"
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    五、接口列表

    序号接口请求方式备注
    1/loginPOST登录接口
    2/login/captchaGET获取登录验证码接口
    3/registerPOST用户注册接口
    4/register/captchaGET获取用户注册验证码接口
    5/getGET加载用户详情
    6/savePOST保存用户信息
    7/updatePOST更新用户信息
    8/deleteGET删除用户信息

    六、问题汇总

    问题一: session设置中的cookie的secure设置为true时,cookie只有在https时生效,导致postman测试接口时出现session不一致的问题。将cookie中的secure设置为false即可。
    问题二: 数据库保存时id设置,可以设置为数据库自增长或者使用uuid,也可以手动查询最新id,并且进行id的更新,但这种方式不建议。
    问题三: 使用async及await时,需要返回Promise类型才可以,不然ts编译器会报错。

  • 相关阅读:
    简述MASM宏汇编
    差值结构顺序的稳定性
    伦敦银代码什么意思
    HAProxy理论+实验
    那些年白上了的线代课
    docker挂载目录权限问题
    抖音矩阵系统源码,抖音矩阵系统源码,抖音矩阵系统源码,抖音矩阵系统源码,抖音矩阵系统源码,抖音矩阵系统源码。
    JavaEE之HTTP协议(1)_HTTP基础知识,HTTP 请求、响应格式,方法,状态码
    3.zigbee开发,OSAL原理及使用(类似操作系统)
    工业自动化控制通信协议Profinet系列-1、了解Profinet协议及收集资料
  • 原文地址:https://blog.csdn.net/m0_37631110/article/details/136295408