koa开发api

178 阅读12分钟

微信小程序中使用npm

  • 在微信开发者工具中的设置菜单下找到“项目设置”,勾选“使用npm模块”。
  • 在微信开发者工具中找到page文件,然后右键选择-在终端打开。在终端打开后,因为是在page文件下右键打开的终端,所以当前的文件是page,使用"cd .."命令回到上一层,也就是当前项目的根目录层,使用" npm init -y "命令创建项目,其实创建的是package.json这个文件。
  • 这时可以使用npm安装需要的组件。注意的是安装完需要的组件后,微信开发者工具的目录文件中并没有node_modules这个目录。这时需要选择微信开发者工具栏菜单中的工具-构建npm。
  • 当前项目的根目录下会显示一个miniprogram_npm的目录,这个目录代替了node_modules这个目录。

使用lin-ui

lin-ui只是一个ui组件库。 lin-ui在小程序中的使用。 lin-ui在小程序中的注册是如下,是在index.json的文件里边进行注册的,如下代码:

{
  "usingComponents": {
    "li-button":"/miniprogram_npm/lin-ui/button/index"
  }
}

注册组件后就可以直接使用这个<lin-button>的这个组件了。

lin-ui的点击事件

小程序正常的点击事件是bind的,但是我们使用的是lin-ui这个框架,所以点击事件是bindtap。如下代码显示:

<li-button size="long" bindtap="onLogin">
  获取token按钮
</li-button>

小程序登录的代码

    success(res){
        if (res.code) {//表示微信登录小程序没问题
          //发起网络请求,使用的方式与jq调用ajax特别的像
          wx.request({
            url: 'http://localhost:3000/v1/token',//设置-->项目设置--https证书的勾选需要勾上,否则访问的协议是https协议。
            method:"post",
            data:{
              account:res.code,
              type:101
            },
            //请求成功返回的结果,失败也会返回结果
            success(r){
              //r.data是返回来的数据,返回来的数据必须是2开头才是正确的token
              if(r.statusCode.toString().startsWith("2")){
                wx.setStorageSync({
                  key: 'token',
                  data: r.data.token
                });
              }
            }
          })

        }else{
          console.log("登录失败:"+res.errMsg);
        }
    }

小程序中本地保存的方法

wx.setStorageSync({key:value,data:value}); wx.getStorageSync(key);//获取本地储存的方法

sequelize创建表

在创建表的时候如果携带了tableName的字段,表示的是表的名字。

生成了3个表,分别是:movie music setence 还有 flow表,这些表都是需要我自己创建的。手动创建,哭死。

sequelize中排序的使用

如下代码:

//取出最新的一条数据,是不需要用where条件的默认就是取出第一条数据
    const resOneFlow=await Flow.findOne({
        //是可以对很多的字段进行多次排序所以会是多个数组的形式
        order:[
            ["index_id","desc"]
        ]
    });

sql语句

创建数据表的语句:

小程序的Authorization的实现

在微信小程序中使用header携带令牌

Authorization是Basic:+base64处理token的格式,所以我们需要在小程序端实现这个格式。然后通过header中的Authorization传递给服务端进行验证。 在小程序中使用第三方插件js-base64。 如下代码:

//在微信小程序中使用header携带令牌
  verifyAuthToken(){
    wx.request({
      url: 'http://localhost:3000/v1/classic/auth',
      method:"post",
      header:{
        Authorization:this._encode()
      },
      success(res){
        console.log(res);
      }
    })
  },
  //auth验证中的一个方法
  _encode(){
    const token=wx.getStorageSync("token");
    //这里是固定的写法,只有这样才是和其他的authorization的请求方式是一样的。
    return "Basic "+Base64.encode(token+":");
  }

mysql中的第3个表的作用

在mysql数据表中有一种现象是,第三个表来存储两个表之间的关系。 在这里的第三个表是存储其他表的索引信息,然后通过第三个表来获取其他两表的关系。

点赞功能的逻辑与model的实现

首先确定点赞的模型Favor,如下代码:

/**
 * 这个表示是存储用户是否对这个期刊进行了点赞,没点赞一次就往这里边添加一条数据,否则删除一条数据。
 * 所以是用户字典
 * type 和 art_id 才能够确定是哪个期刊
 */
Favor.init({
    uid:Sequelize.INTEGER,
    art_id:Sequelize.INTEGER,
    type:Sequelize.INTEGER
},{
    sequelize,
    tableName:"favor"
});

点赞功能的实现代码和注释

这个大概是点赞表的逻辑。点赞表的表名是favor。

表的结构是uid,type,art_id; 判断uid、type、art_id这样的数据是否存在。如果存在 还有就是添加时,那么会往相对应的在期刊表里边的fav_nums字段增加1。 如果不存在则是取消点赞,则虚拟删除该记录,同时相对应的期刊也减少fav_nums数字的值。

使用了sequelize的事务transaction。

    点赞,如果没点添加到favor数据到表
    并且还要往该期刊中的fav_num属性中增加个数
    这两个需要同时进行,所以需要用到数据库的事务
    sequelize.transation(t=>{
        //这个里边的方法要同时执行完毕才可以 
    });
    sequelize的模型的increment("字段",{by:num,transaction:t});表示的是某个字段加num
    如果是减少是decrement

看下具体的代码:


//返回的变量
        let resArtData=null;
        if(fav){//如果存在则是取消操作
            console.log("favId:"+fav.id);
            await sequelize.transaction(async t=>{
                await fav.destroy(
                   {
                       "where":{
                           id:fav.id
                       }
                   },
                {transaction:t});
            });
            const artData=await Art.getData(art_id,type);
            await sequelize.transaction(async t=>{
                resArtData=
                await artData.decrement("fav_nums",{by:1,transaction:t});
            });
            return resArtData;

        }else{//否则是增加操作
            console.log("no exit");
            await sequelize.transaction(async t=>{
                //往favor表里边添加数据
                await Favor.create({
                    uid,
                    art_id,
                    type
                },{transaction:t});
               
            });
             //同时往对应的期刊里边增加一条数据,返回的是sequelize的model数据表模型
            const artData=await Art.getData(art_id,type);
            //记住transation和transaction t的区别。单词是不一样的。
            await sequelize.transaction(async t=>{
                resArtData=
                await artData.increment("fav_nums",{by:1,transaction:t});
            });
            return resArtData;

        }
    }

like的路由接口:

router.post("/",new Auth().m,async ctx=>{
    let tokenData=ctx.auth.uid;
    
    
    //验证参数是否符合标准
    let v=await new LikeValidate().validate(ctx);

   
    const type=v.get("body.type");
    const art_id=v.get("body.art_id");

    //通过用户表获取用户id
    const resUser=await User.findOpenId(tokenData.uid);
    // console.log("::::"+typeof resUser.id,typeof art_id,typeof type);
    let resFavor=await Favor.Makefavor(resUser.id,art_id,type);
    ctx.body={
        data:resFavor
    }

});

用户查询所有点过赞的数据

逻辑: 首先是两个表,一个是fvaor表,通过这个表可以查询出用户点过赞的所有的期刊,然后通过期刊表,查询到具体被点赞的期刊。

期刊只包括,music movie sentence 不包括book。所以要把type等于400的book去掉。

切记:不能够使用for循环,在数据库进行查询,如果使用for循环进行数据库的连接和查询,对数据库来说效率是非常低的。

问题:squelize物理删除

desctroy不是物理删除,只是在deletedAt上填充时间而已,在虚拟删除之前是NULL; 目前没有找到物理删除。

找到的是虚拟删除。一旦deletedAt有时间则表示已经删除,则查询语句是查不出的。

数据库的效率

1,循环查询数据库是非常危险的行为,对于数据库的性能是特别不好的,一定要避免。

关于json的key和switch的case的数据类型

json的key,切记不管填写的是什么,它的类型都是字符串类型。 switch进行判断是===号判断,所以判断前最好做一个类型的转换。

获取当前用户所有点过赞的期刊

这里要涉及到3个表。

当点赞表里获取到的所有数据和期刊表是一条一条对应的,如果不用for循环一条一套查询。那么这里有一个更好的办法,就是通过type分类进行查询比较好。 type分成3类,那么通过找到它,然后用in,先满足type,然后art_id在in里边符合的就查找出来。因为type只有3类,所以只要向数据库查询3次。

重要代码如下:

 //获取用户点赞过的所有数据的方法
    static async getUserAllLike(uid){
        //获取用户所有点过赞的所有的数据的art_id 和 type
        const resAllFavor=await Favor.findAll({
            where:{
                //关于Op和model相对应的使用可以参考文档,表示的是type不等于400的所有值。book是不属于期刊的。Op是需要从sequelize里边引入的。
                type:{
                    [Op.not]:400
                },
                uid
            }
        });

        //通过type把数据进行分类,组成一个集合
        let arts=await Favor._classifycationLike(resAllFavor);
        //分类好的数据进行遍历查询数据库
        let artData=[];
        for(let key in arts){
            let type=parseInt(key);
            console.log("arts[key]",arts[key]);
            let where_={
                where:{
                    type:type,
                    id:{
                        [Op.in]:arts[key]
                    }
                }  
            };
            let r=null;
            if(arts[key].length!=0){
               
                switch(type){
                    case 100:
                        r=await Music.findAll(where_);
                        artData.push(r);
                        break;
                    case 101:
                        r=await Movie.findAll(where_);
                        // artData.push(r);
                        // console.log(r);
                        if(r.id==undefined){
                            artData.push(r);
                        }
                        break;
                    case 102:
                        r=await Sentence.findAll(where_);
                        artData.push(r);
                        break;
                    default:
                        new Error("没有符合的type类型");
                        break;
                }
            }else{
                continue;
            }
            
        }

        // console.log(resAllFavor);
        return artData;
    }

    //数据分类的内部函数
    static async _classifycationLike(allLikeFavor){
        //定义一个已经分类好的数据结构
        let arts={
            "100":[],
            "101":[],
            "102":[]
        }
        //通过for循环遍历每天数据然后进行分类
        for(let key in allLikeFavor){
            // console.log(allLikeFavor[key].type);
            switch(allLikeFavor[key].type){
                case 100:
                    arts["100"].push(allLikeFavor[key].art_id);
                    break;
                case 101:
                    console.log("000");
                    arts["101"].push(allLikeFavor[key].art_id);

                    break;
                case 102:
                    arts["102"].push(allLikeFavor[key].art_id);
                    break;
                default:
                    break;
            }
        }
        return arts;
        
    }


技巧 :如果数据多并且数据是一一对应的,那么分类,先满足某个分类,然后用in集合一次性获取出来。 当然也可以用分类和where条件配合使用。

JavaScript循环导入的坑

在文件的头部进行引用其他文件,如果A.js使用require引入B.js,而B.js又引入了A.js很容易会出现其中一个文件引入后出现undefined的效果。所以尽量避免在文件头部进行引用,而是哪里使用这个文件里的返回结果就在哪里引入。

为什么会出现这种情况呢?

是因为A.js在引入B.js时,要执行B.js里边的代码,但是B.js里边又引入了A.js,但是是A.js先引入B.js的,那么B.js再引入就不合逻辑,因为A.js正在执行,所以就会出现坑。

如下是A.js代码:

const{B}=require("./b");
class A{


}
console.log(B);
module.exports={
    A
}

如下是B.js的代码:

const{A}=require("./A");

class B{

}
console.log("A",A);

module.exports={B}

热门书籍的相关的接口设计

热门书籍的modle的设置

    const {Sequelize,Model}=require("sequelize");
    const {sequelize}=require("./../core/db");
    
    
    class HotBook extends Model{
    
    }
    
    HotBook.init({
    
        index:Sequelize.INTEGER,
        title:Sequelize.STRING,
        author:Sequelize.STRING,
        img:Sequelize.STRING
    
    },{
        sequelize,
        tableName:"hotbook"
    });
    
    sequelize.sync();//自动创建表
    
    module.exports={
        HotBook
    }

forEach在遍历异步操作的坑

数组的forEach在遍历异步的时候我们使用async和await也不会如愿的操作。所以只能是尽量去避免。

并行和并发

并行:一个时间执行多个进程。真正的多线程。

并发:看起来像并行,但是其实是cpu足够快,并不是真正的一个时间内执行多个进程。nodejs之所以高并发是因为他模拟并行的很好(宏观任务和围观任务)和事件池有关的。

nodejs的优势,对服务器cpu的要求低。单线程解决并发的问题。

热门书的查询结果

先条件查询再分组。

然后将统计好的数据给这个对象做属性。

具体的代码:

    
    //返回热门书籍表里边的所有的书籍并且进行排序。将热门书籍里边的书籍的id取出组成一个数组。
        let resOrder=await HotBook.findAll({
            order:["index_"]
        });

        //把所有的id都放入到一个数组里边,方便进行查询
        let arrId=[];
        for(let key in resOrder){
            arrId.push(resOrder[key].id);
        }
    

    //原生mysql解决这查询结构后分组的问题
        let resFavor=await sequelize.query(`select art_id,count(id) as count from favor where type=104 and art_id in(${arrId}) group by art_id`,{model:Favor,mapToModel:true});
        
        //把返回的每本书对象添加一个count的属性
        resOrder.forEach(b=>{
            //每本书添加属性的方法
            HotBook.setBookToArr(b,resFavor);
        })
        
    //

另外一个方法:

    //把循环数组是否将数组中的某个元素的某个值放入到对对象中
    //将数组中与book相等的元素的count增加属性给book这个对象
    static setBookToArr(book,favors){
        favors.forEach(favor=>{
            if(book.id==favor.art_id){
                book.setDataValue("count",favor.get("count"));
            }
        })
        return book;
    }

两个获取数据的网站

t.yushu.im/v2/book/id/…

t.yushu.im/v2/book/id/…

使用util.format(xxx,id);即可以进行格式化。

"t.yushu.im/v2/book/sea…"

使用axios向服务器获取详细的数据


    async getDetail(){
        //第三房服务器获取数据的网址是配置到config的文件里边的
        let url=util.format(config.yushu.detailUrl,this.id);
        let resData=await axios.get(url);
        return resData.data;
    }

前端+后端 叫做大前端。但是我们的数据一般是放在服务器上的。 所以叫服务器端。

微服务其实就是把服务器分成一个小的服务,这个服务器与其他的服务存在的不一样的地方,这样去处理的话比人性化些。如有些业务就是访问量很高,那么我们优化负载均衡等都可以对这个服务操作就可以。

服务端其实把数据抛出来,但是如何返回给前端,就是我们后端操作。这样也就减少服务端的工作。 例如后端,把各个服务器上查询到的数据进行综合返回给前端,或者是筛选后返回给前端。

搜索的接口

借用第三方的接口实现的。

用户的所有点赞的一个接口

    //用户获取图书点赞情况的接口
    static async getBookFavorNumber(userid){
        //通过用户登录的账号名获取用户的id
        let user=await User.findOne({
            where:{
                openid:userid.uid
            }
        });
        //通过用户的id来查询出用户的点赞情况
        let count=await Favor.count({
            where:{
                uid:user.id,
                type:104
            }
        });
        return {user,count};
    }

图书点赞情况和用户是否点赞的一个接口

这个图书被点赞的次数和是否被当前用户点赞。

//这个图书被点赞的次数,和是否被当前用户点赞
    static async getBookFavor(userid,bookid){
        console.log(userid);
        //通过用户登录的账号名获取用户的id
        let user=await User.findOne({
            where:{
                openid:userid.uid
            }
        });

        //查询book被点赞的次数
        let count=await Favor.count({
            where:{
                type:104,
                art_id:bookid
            }
        });
        //是否当前用户也点过赞
        let favor=await Favor.findOne({
            where:{
                art_id:bookid,
                type:104,
                uid:user.id
            }
        })
        //如果存在则是点过赞,否则是没有点赞的
        let like_status=true;
        if(favor.id===undefined){
            like_status=false;
        }
        return {count,like_status}
    }

数据序列化

就是在返回给前端数据的时候,在父亲的原生连或者是公用的地方写上一个方法,这个方法是用来返回给前端数据时,只显示某些数据或者不显示某些数据。

sequealize中Model使用构造函数的坑

在sequelize中尽量不要使用construcotr,否则的话会出现有些字段是无法查询出来的。