Typescript网络爬虫微服务实战系列
相关视频:

特点与内容

  • 多个成熟项目经验,经过总结、提炼、精简、重构,带领大家实现一个个完整的实用项目。
  • 课程内容与结构经过精心准备,设计合理、节奏紧凑、内容翔实。
  • 系列课程真实再现了思考、编码、调试、除错的整个开发过程。
  • Typescript核心知识与编程思想的锤炼。
  • 一种ajax动态内页内容的抓取的讲解。
  • 正则表达式与内页解析工具的实战。
  • 一个微服务快速开发框架,让你只需关注核心业务开发。

适合人群

  • Typescript初学者与中级者,带你逐步进入后端开发奇妙世界
  • 适合从实战角度来学习编程的人,带你一步步实现一个完整的项目
  • node.js平台开发者,与你分享个人的设计思想与编程技巧
  • 熟练前端开发者,想步入后端编程的行列,从实战出发吧
  • 后端开发方式探讨,对现有框架不满意的同道之人
  • 项目实践学习开发语言的一种快速方式

Typescript训练营

以实战编程为主线,来讲解typescript语言的要点。项目需求:统一处理不同图形(圆形、长方形、矩形等)的面积计算。

语言基础内容

  • 开发运行环境的配置
  • 以hello world程序讲解

    • 自动编译与自动运行监测
    • tslint规则配置
  • 以统一计算各种图形面积实例讲解

    • 各种静态类型说明
    • vscode中进行程序跟踪与调试
    • 全局变量的声明与初始化
    • 如何用bluebird替换内置的Promise对象
    • 类的编写
    • 面向对象三个原则(封装、继承、多态)的实例说明
    • 接口的使用与实战
  • 模板的知识讲解
  • 函数式编程思想初步探讨
  • 异步处理流程中的一个经验讲解

面向对象三大原则

1.Circle类讲解数据封装概念,将半径与名称封装在类内部,并提供访问方法

export default class Circle {
    private r: number
    private name: string
    constructor(r: number) {
        this.r = r
        this.name = 'Circle'
    }
    getName(): string {
        return this.name
    }
    area(): number {
        return Math.pow(this.r, 2) * PI
    }
}

2.长方形与矩形讲解继承概念

//rectangle.ts
export default class Rectangle {
    private a: number
    private b: number
    private name: string
    constructor(a: number, b: number, name?: string) {
        this.a = a
        this.b = b
        if (name === undefined)
            this.name = 'Rectangle'
        else
            this.name = name
    }
    getName(): string {
        return this.name
    }
    area(): number {
        return this.a * this.b
    }
}
//square.ts
export default class Square extends Rectangle {
    constructor(a: number) {
        super(a, a, 'Square')
    }
}

3.实例统一处理不同的形状一起计算面积,讲解多态概念

let shapes = Array<any>()
shapes.push(new Circle(2))
shapes.push(new Rectangle(5, 4))
shapes.push(new Square(3))
shapes.forEach((element) => {
    console.log(`shape name: ${element.getName()}; shape area: ${element.area()}`)
})

接口概念阐述

加入接口,规范形状对外部分操作要求,让错误提早到编译阶段被发现

export default interface IShape {
    getName(): string;
    area(): number
}

函数式编程讲解

用实例来说明怎样理解函数是一等公民,去掉我们习以为常的函数外层包裹

let printData = function(err: any, data: string): void {
    if (err)
        console.log(err)
    else
        console.log(data)
}
let doAjax = function (data: string, callback: Function): void {
    callback(null, data)
}
//我们习以为常的使用方式
doAjax('hello', function(err, result){
    printData(null, result)
})
//真正理解了函数是一等公民后,你会这样用
doAjax('hello', printData)

异步处理中的经验分享

在实践过程,处理异步调用容易误解的一个重要概念,异步函数执行的具体流程是什么样的?

let pf = function(data: string, n: number, callback: Function) {
    console.log(`begin run ${data}`)
    setTimeout(() => {
        console.log(`end run ${data}`)
        callback(null, data)
    }, n)
}
let p = Promise.promisify(pf);

(async () => {
    let ps = Array<any>()
    ps.push(p('1111', 2000))
    ps.push(p('2222', 1000))
    ps.push(p('3333', 3000))
    await Promise.all(ps)
})()

实战训练营

安装运行

  • 运行数据脚本
    users表

        CREATE TABLE `books` (
        `id` int(11) NOT NULL AUTO_INCREMENT,
        `isbn` varchar(32) NOT NULL DEFAULT '',
        `book_name` varchar(1024) DEFAULT '',
        `author_name` varchar(128) DEFAULT '',
        `publisher` varchar(1024) DEFAULT '',
        `publish_day` varchar(64) DEFAULT NULL,
        `details_json` json DEFAULT NULL,
        `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
        `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        PRIMARY KEY (`id`),
        UNIQUE KEY `isbn` (`isbn`)
        ) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8;

    books表

    SET NAMES utf8;
    SET FOREIGN_KEY_CHECKS = 0;
    
    -- ----------------------------
    --  Table structure for `users`
    -- ----------------------------
    DROP TABLE IF EXISTS `users`;
    CREATE TABLE `users` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `username` varchar(255) DEFAULT NULL,
    `password` varchar(255) DEFAULT NULL,
    `age` int(11) DEFAULT NULL,
    `power` json DEFAULT NULL,
    PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4;
    
    -- ----------------------------
    --  Records of `users`
    -- ----------------------------
    BEGIN;
    INSERT INTO `users` VALUES ('1', 'white', '123', '22', null), ('2', 'john', '456i', '25', null), ('3', 'marry', null, '22', null), ('4', 'bill', '123', '11', null), ('5', 'alice', '122', '16', null), ('6', 'zhoutk', '123456', '26', null);
    COMMIT;
    
    SET FOREIGN_KEY_CHECKS = 1;
  • 配置文件示例,./src/config/configs.ts

    export default {
        inits: {
            directory: {
                run: false,
                dirs: ['public/upload', 'public/temp']
            }
        },
        port: 5000,
        db_dialect: 'mysql',
        dbconfig: {
            db_host: 'localhost',
            db_port: 3306,
            db_name: 'strest',
            db_user: 'root',
            db_pass: '123456',
            db_char: 'utf8mb4',
            db_conn: 10,
        },
        jwt: {
            secret: 'zh-tf2Gp4SFU>a4bh_$3#46d0e85W10aGMkE5xKQ',
            expires_max: 36000      //10小时,单位:秒
        },
    }
  • 在终端(Terminal)中依次运行如下命令

    git clone https://github.com/zhoutk/gels
    cd gels
    npm i -g yarn
    yarn global install typescript tslint nodemon
    yarn install
    tsc -w          //或 command + shift + B,选 tsc:监视
    yarn start      //或 node ./dist/index.js

    项目结构

├── package.json
├── src                              //源代码目录
│   ├── app.ts                       //koa配置及启动
│   ├── common                       //通用函数或元素目录
│   │   ├── globUtils.ts             
│   ├── config                       //配置文件目录
│   │   ├── configs.ts
│   ├── db                           //数据封装目录
│   │   ├── baseDao.ts
│   ├── globals.d.ts                 //全局声明定义文件
│   ├── index.ts                     //运行入口
│   ├── inits                        //启动初始化配置目录
│   │   ├── global.ts
│   │   ├── index.ts
│   │   ├── initDirectory.ts
│   ├── middlewares                  //中间件目录
│   │   ├── globalError.ts
│   │   ├── logger.ts
│   │   ├── router
│   │   └── session.ts
│   └── routers                      //路由配置目录
│       ├── index.ts
│       └── router_rs.ts
├── tsconfig.json
└── tslint.json

数据库接口设计

  • 数据库操作接口,包括基本CURD,两个执行手写sql接口,一个批量插入与更新二合一接口,一个事务操作接口。实践证明,下面八个接口,在绝大部分情况下已经足够。

    export default interface IDao {
        select(tablename: string, params: object, fields?: Array<string>): Promise<any>;
        insert(tablename: string, params: object): Promise<any>;
        update(tablename: string, params: object, id: string|number): Promise<any>;
        delete(tablename: string, id: string|number): Promise<any>;
        querySql(sql: string, values: Array<any>, params: object, fields?: Array<string>): Promise<any>;
        execSql(sql: string, values: Array<any>): Promise<any>;
        insertBatch(tablename: string, elements: Array<any>): Promise<any>;
        transGo(elements: Array<TransElement>, isAsync?: boolean): Promise<any>;
    }
  • BaseDao,为业务层提供标准数据库访问的基类,是自动提供标准rest微服务的关键

    import IDao from './idao'
    let dialect = G.CONFIGS.db_dialect                    //依赖注入
    let Dao = require(`./${dialect}Dao`).default    
    
    export default class BaseDao {
        private table: string
        static dao: IDao                               //以组合的模式,解耦业务层与数据库访问层
        constructor(table?: string) {
            this.table = table || ''
            if (!BaseDao.dao) {
                BaseDao.dao = new Dao()
            }
        }
        async retrieve(params = {}, fields = [], session = {userid: ''}): Promise<any> {
            let rs
            try {
                rs = await BaseDao.dao.select(this.table, params, fields)
            } catch (err) {
                err.message = `data query fail: ${err.message}`
                return err
            }
            return rs
        }
        async create(params = {}, fields = [], session = {userid: ''}): Promise<any> {
            let rs
            try {
                rs = await BaseDao.dao.insert(this.table, params)
            } catch (err) {
                err.message = `data insert fail: ${err.message}`
                return err
            }
            let { affectedRows } = rs
            return G.jsResponse(200, 'data insert success.', { affectedRows, id: rs.insertId })
        }
        async update(params, fields = [], session = { userid: '' }): Promise<any> {
            params = params || {}
            const { id, ...restParams } = params
            let rs
            try {
                rs = await BaseDao.dao.update(this.table, restParams, id)
            } catch (err) {
                err.message = `data update fail: ${err.message}`
                return err
            }
            let { affectedRows } = rs
            return G.jsResponse(200, 'data update success.', { affectedRows, id })
        }
        async delete(params = {}, fields = [], session = {userid: ''}): Promise<any> {
            let id = params['id']
            let rs
            try {
                rs = await BaseDao.dao.delete(this.table, id)
            } catch (err) {
                err.message = `data delete fail: ${err.message}`
                return err
            }
            let {affectedRows} = rs
            return G.jsResponse(200, 'data delete success.', { affectedRows, id })
        }
    }

默认路由

  • /op/:command,只支持POST请求,不鉴权,提供登录等特定服务支持

    • login,登录接口;输入参数{username, password};登录成功返回参数:{status:200, token}
  • /rs/:table[/:id],支持四种restful请求,GET, POST, PUT, DELELTE,除GET外,其它请求检测是否授权

中间件

在app.ts中,实现了一个自动加载中间件的流程,可以方便的引入第三方或自定义中间件。
  • router,路由中间件
  • logger,日志,集成log4js,输出系统日志
  • session,使用jsonwebtoken,实现鉴权;同时,为通过的鉴权的用户生成对应的session

    • 用户登录成功后得到的token,在以后的ajax调用时,需要在header头中加入token key

restful_api

/rs/users
  • [GET] /rs/users[?key=value&...], 列表查询,支持各种智能查询
  • [GET] /rs/users/{id}, 单条查询
  • [POST] /rs/users, 新增记录
  • [PUT] /rs/users/{id}, 修改记录
  • [DELETE] /rs/users/{id}, 删除记录
/rs/books
  • [GET] /rs/books[?key=value&...], 列表查询,支持各种智能查询
  • [GET] /rs/books/{isbn}, 单条查询,若缓存及库中没有,从网络爬取图书信息
  • [POST] /rs/books, 新增图书信息
  • [PUT] /rs/books/{id}, 修改图书信息
  • [DELETE] /rs/books/{id}, 删除图书信息

智能查询

查询保留字:fields, page, size, sort, search, lks, ins, ors, count, sum, group
  • fields, 定义查询结果字段,支持数组和逗号分隔字符串两种形式

    查询示例:  /rs/users?username=white&age=22&fields=["username","age"]
    生成sql:   SELECT username,age FROM users  WHERE username = ?  and age = ?
  • page, 分页参数,第几页
  • size, 分页参数,每页行数
  • sort, 查询结果排序参数

    查询示例:  /rs/users?page=1&size=10&sort=age desc
    生成sql:   SELECT * FROM users  ORDER BY age desc LIMIT 0,10
  • search, 模糊查询切换参数,不提供时为精确匹配

    查询示例:  /rs/users?username=i&password=1&search
    生成sql:   SELECT * FROM users  WHERE username like ?  and password like ?
  • ins, 数据库表单字段in查询,一字段对多个值,例:

    查询示例:  /rs/users?ins=["age",11,22,26]
    生成sql:   SELECT * FROM users  WHERE age in ( ? )
  • ors, 数据库表多字段精确查询,or连接,多个字段对多个值,支持null值查询,例:

    查询示例:  /rs/users?ors=["age",1,"age",22,"password",null]
    生成sql:   SELECT * FROM users  WHERE  ( age = ?  or age = ?  or password is null )
  • lks, 数据库表多字段模糊查询,or连接,多个字段对多个值,支持null值查询,例:

    查询示例:  /rs/users?lks=["username","i","password",null]
    生成sql:   SELECT * FROM users  WHERE  ( username like ?  or password is null  )
  • count, 数据库查询函数count,行统计,例:

    查询示例:  /rs/users?count=["1","total"]&fields=["username"]
    生成sql:   SELECT username,count(1) as total  FROM users
  • sum, 数据库查询函数sum,字段求和,例:

    查询示例:  /rs/users?sum=["age","ageSum"]&fields=["username"]
    生成sql:   SELECT username,sum(age) as ageSum  FROM users
  • group, 数据库分组函数group,例:

    查询示例:  /rs/users?group=age&count=["*","total"]&fields=["age"]
    生成sql:   SELECT age,count(*) as total  FROM users  GROUP BY age
不等操作符查询支持

支持的不等操作符有:>, >=, <, <=, <>, =;逗号符为分隔符,一个字段支持一或二个操作。
特殊处:使用"="可以使某个字段跳过search影响,让模糊匹配与精确匹配同时出现在一个查询语句中

  • 一个字段一个操作,示例:

    查询示例:  /rs/users?age=>,10
    生成sql:   SELECT * FROM users  WHERE age> ?
  • 一个字段二个操作,示例:

    查询示例:  /rs/users?age=>,10,<=,35
    生成sql:   SELECT * FROM users  WHERE age> ? and age<= ?
  • 使用"="去除字段的search影响,示例:

    查询示例:  /rs/users?age==,22&username=i&search
    生成sql:   SELECT * FROM users  WHERE age= ?  and username like ?

高级操作

  • 新增一条记录

    • url

      [POST]/rs/users
    • header

      Content-Type: application/json
      token: eyJhbGciOiJIUzI1NiIsInR...
    • 输入参数

      {
          "username":"bill",
          "password":"abcd",
          "age":46,
          "power": "[\"admin\",\"data\"]"
      }
    • 返回参数

      {
          "affectedRows": 1,
          "id": 7,
          "status": 200,
          "message": "data insert success."
      }
  • execSql执行手写sql语句,供后端内部调用

    • 使用示例

      await new BaseDao().execSql("update users set username = ?, age = ? where id = ? ", ["gels","99","6"])
    • 返回参数

      {
          "affectedRows": 1,
          "status": 200,
          "message": "data execSql success."
      }
  • insertBatch批量插入与更新二合一接口,供后端内部调用

    • 使用示例

      let params = [
                      {
                          "username":"bill2",
                          "password":"523",
                          "age":4
                      },
                      {
                          "username":"bill3",
                          "password":"4",
                          "age":44
                      },
                      {
                          "username":"bill6",
                          "password":"46",
                          "age":46
                      }
                  ]
      await new BaseDao().insertBatch('users', params)
    • 返回参数

      {
          "affectedRows": 3,
          "status": 200,
          "message": "data batch success."
      }
  • tranGo事务处理接口,供后端内部调用

    • 使用示例

      let trs = [
                  {
                      table: 'users',
                      method: 'Insert',
                      params: {
                          username: 'zhou1',
                          password: '1',
                          age: 1
                      }
                  },
                  {
                      table: 'users',
                      method: 'Insert',
                      params: {
                          username: 'zhou2',
                          password: '2',
                          age: 2
                      }
                  },
                  {
                      table: 'users',
                      method: 'Insert',
                      params: {
                          username: 'zhou3',
                          password: '3',
                          age: 3
                      }
                  }
              ]
      await new BaseDao().transGo(trs, true)          //true,异步执行;false,同步执行
    • 返回参数

      {
          "affectedRows": 3,
          "status": 200,
          "message": "data trans success."
      }

相关视频课程

运用typescript进行node.js后端开发精要
nodejs实战之智能微服务快速开发框架
JSON-ORM(对象关系映射)设计与实现
Koa2封装数据库高级操作
蜘蛛实时爬取数据提供图书信息微服务

资源地址

凝胶(gels)项目: https://github.com/zhoutk/gels
视频讲座资料: https://github.com/zhoutk/sifou
个人博客: https://segmentfault.com/blog...

购买须知:
1、本课程为虚拟产品,一经购买,概不退款(讲师特别声明除外)
2、在使用过程中,遇到任何问题,请邮件联系:pr@sifou.com
3、划线价说明:商品展示的划横线价格为参考价,并非原价。该价格仅供您参考。

版权声明:讲者在本产品上发表的全部原创内容(包括但不限于文字、视频、图片等)著作权均归讲师本人所有。未经讲师授权许可,观众用户不得以任何载体或形式使用讲师的内容。

5.0 暂无评价