微信小程序后台管理系统(后端)笔记

传送门

一、前期框架的搭建

后台采用的框架为Koa2框架,编程软件为vs code,node环境

  • 步骤一:创建一个空文件
  • 步骤二:在vs code打开该文件,并按ctrl+`打开终端
  • 步骤三:输入命令npm init(初始化package.json),也可以用npm init -y(直接默认初始化package.json)
    在这里插入图片描述
  • 步骤四:输入命令npm install koa(下载koa)
    在这里插入图片描述
二、获取AcessToken(接口调用凭证)

      获取小程序全局唯一后台接口调用凭据(access_token)。调用绝大多数后台接口时都需使用 access_token,开发者需要进行妥善保存。
      由于,我是调用云函数,所以必须需要接口调用凭证。

注意:
在这里插入图片描述
中控服务器是自己搭建的后台

请求地址及参数
GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

在这里插入图片描述
因为要不断使用接口调用凭证,所以将其封装成一个方法,再向外抛出接口。

代码实现:

  • 在实现前,因为需要向第三方请求数据(请求接口调用凭证),故需要下载第三方库,我使用的是request-promise(依赖于request)
  • 在终端输入先输入命令npm install request 下载成功后再输入命令npm install request-promise
  • 也可以使用axios第三方库进行请求数据
const rp = require('request-promise')
const fs = require('fs')//自带的
const path = require('path')//自带的
const fileName = path.resolve(__dirname, './access_token.json')//创建文件并获取绝对路径
const APPID = '您的小程序ID'
const APPSECRET = '您的小程序密钥'//不能泄露
const URL = `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${APPSECRET}`

//更新AccessToken
const updateAccessToken = async () => {
    //发送请求获取AccessToken
    const resStr = await rp(URL)
    const res = JSON.parse(resStr)
    
    //  将AccessToken写入文件
    if (res.access_token) {
    //JSON.stringify()将对象转化成字符串
        fs.writeFileSync(fileName, JSON.stringify(
            {
                access_token: res.access_token,
                createTime: new Date()
            }
        ))
    } else {//如果AccessToken不存在,再次更新AccessToken
        await updateAccessToken()
    }
}

//获取AccessToken
const getAccessToken = async () => {
    //读取文件获取AccessToken
    try {
    //读取文件,编码为utf-8
        const readRes = fs.readFileSync(fileName, 'utf8')
        const readobj = JSON.parse(readRes)
        const createTime=new Date(readobj.createTime).getTime()
        const nowTime=new Date().getTime()
       if((nowTime-createTime)/1000/60/60>=2){
       //判断是否已经超过俩个小时,超过俩个小时再次调用updateAccessToken方法
           await updateAccessToken()
       }
        return readobj.access_token
    } catch (error) {//文件不存在,说明AccessToken没有被更新
       await updateAccessToken()
       getAccessToken()
    }
}

// 定时器(定时获取AccessToken 提前5分钟)
setInterval(async () =>{
    await updateAccessToken()
},6900*1000)

//导出接口(给调用方使用)
module.exports=getAccessToken

运行测试

  • 在终端进入到该文件夹中(cd …返回到上级目录或者cd 目标目录)
  • 输入命令编译文件 node 文件名
    在这里插入图片描述
三、搭建后台,并返回数据到前端(重点)

前期准备
(1)由于我前端和后台都是在本地运行,但端口号不同,此时会产生跨域问题。

  • 解决方法:
    我采用的方法时用cors来解决,首先在项目中打开终端,输入命令npm install koa2-cors
    在这里插入图片描述
    然后在项目的入口文件(app.js)中,导入该第三方库,解决跨域问题
const cors=require('koa2-cors')
//解决跨域问题
app.use(cors({
    //配置允许访问后台的URL地址(前端地址)
    origin:['http://localhost:9528'],
    credentials:true
}))

在实现前后端分离,都会出现跨域问题,有以下几种形式

  • http://www.a.com http://www.b.com 域名不一样
  • http://www.a.com:8080 http://www.a.com:8081 端口不一样
  • http://www.a.com http://news.a.com 主域和子域之间
  • http://www.a.com https://www.a.com 协议不一样

(2)配置后端路由,我使用的是koa-router,打开项目终端,输入命令npm install koa-router
在这里插入图片描述

(3)下载第三请求库request-promise,由于前面部分已经下载了,所以直接导入即可。

代码实现

知识点一:调用云函数
我这里是通过调用云函数实现从云数据库中获取歌单信息,下面是我部署的云函数(云函数名为music,路由名(地址)为playlist)
app.router('playlist', async (ctx, next) => {
    ctx.body = await cloud.database().collection('playlist')
      .skip(event.start)
      .limit(event.count)
      .orderBy('createTime', 'desc').get()
      .then((res) => {
        return res
      })
  })

后台调用云函数

const Router=require('koa-router')
const router=new Router()

const getAccessToken=require('../utils/getAccessToken.js')
const rp=require('request-promise')

// /list是后台路由地址
router.get('/list',async(ctx,next)=>{
    // 获取调用接口凭证
    const access_token= await getAccessToken()
    //云数据库运行环境
    const ENV='您的云开发环境ID'
    //调用云函数地址(参数name是您的云函数名字)
    const URL=`https://api.weixin.qq.com/tcb/invokecloudfunction?access_token=${access_token}&env=${ENV}&name=music`
    //获取前端的发送过来的请求数据
    const query=ctx.request.query
    //调用云函数的请求参数(封装成对象)
    const option={
        method:'POST',//必须是POST请求
        url:URL,
        body:{
           $url:'playlist',//云函数的路由名(地址)
           start:parseInt(query.start),
           count:parseInt(query.count)
        },
        json:true//返回格式为JSON格式
    };
    //发送请求
   const data=await rp(option).then((res)=>{
        //console.log(res)
        return JSON.parse(res.resp_data).data
    }).catch((err)=>{
        console.log(err)
    })
    //返回数据给前端
    ctx.body={data,code:20000}
})
module.exports=router//将路由抛出

在返回数据中,一个数据code:20000 目的是:因为我前端采用的是vue-admin-template搭建的前端管理系统,必须返回code:20000。

优化代码
由于后台大部分功能是调用云函数,所以说调用云函数的代码内容基本是一样的。可以将调用云函数的方法进行封装,下面是封装的代码

const getAccessToken = require('./getAccessToken.js')
const rp = require('request-promise')

const callCloudFn = async (ctx, fnName, params) => {
    const ACCESS_TOKEN = await getAccessToken()
    const options = {
        method: 'POST',
        uri: `https://api.weixin.qq.com/tcb/invokecloudfunction?access_token=${ACCESS_TOKEN}&env=${ctx.state.env}&name=${fnName}`,
        //ctx.state.env通过全局中间件获取云开发环境,在入口文件(app.js)有配置
        body: {
            ...params
        },
        json: true 
    }

    return await rp(options)
        .then((res) => {
            return res//注:返回的格式是字符串
        })
        .catch(function (err) {
        })
}

module.exports = callCloudFn

此时需要修改路由list中的代码

const Router=require('koa-router')
const router=new Router()
const rp=require('request-promise')
const callCloudFun=require('../utils/callCloudFn')

router.get('/list',async(ctx,next)=>{
    //获取前端请求的数据
    const query=ctx.request.query
    //发送请求
    const res=await callCloudFun(ctx,'music',{
        $url:'playlist',
        start:parseInt(query.start),
        count:parseInt(query.count)
    })
    let data=[]
    if(res.resp_data){
        data=JSON.parse(res.resp_data).data
    }
    ctx.body={
        data,
        code:20000,
    }
})
module.exports=router

这样的好处就是减少代码的重复率,增加代码的利用率。

知识点二:API调用云数据库

前期准备
(1)下载koa-body,获取post请求数据
在这里插入图片描述
在app.js配置以下代码

//接收post参数解析
app.use(koaBody({
    multipart: true,
}))

(2)创建功能模块

const rp=require('request-promise')
const getAccessToken = require('./getAccessToken.js')
const callCloudDB=async(ctx,fnName,query={})=>{
//fnName数据操作的类型 query数据操作语句
     const access_token=await getAccessToken()
     const options={
         method:'POST',
         uri:`https://api.weixin.qq.com/tcb/${fnName}?access_token=${access_token}`,
         body:{
             query,
             env:ctx.state.env,
         },
         json:true
     }
     return await rp(options).then((res)=>{
           return res
     }).catch((err)=>{
         console.log(err)
     })
}
module.exports=callCloudDB

(3)更新数据
因为前端提交更新数据的形式是post,所以后端代码应该为post

router.post('/updatePlaylist',async(ctx,next)=>{
    //获取请求体
    const params=ctx.request.body
    //更新语句
    const query=`db.collection('playlist')
                 .doc('${params._id}').update({
                     data:{
                         name:'${params.name}',
                         copywriter:'${params.copywriter}'
                     }
                 })
                 `
     const res= await callCloudDB(ctx,'databaseupdate',query)
     ctx.body={
         code:20000,
         data:res
     }
})
module.exports=router

(4)删除数据

router.get('/deletePlaylist',async(ctx,next)=>{
    const query=`db.collection('playlist').doc('${ctx.request.query.id}').remove()`
    const res=await callCloudDB(ctx,'databasedelete',query)
    ctx.body={
        code:20000,
        data:res
    }
})

(5)增加数据

 const query=`db.collection('swiper').add({
        data:{
            fileId:'${fileid}'
        }
    })`
    const res=await callCloudDB(ctx,'databaseadd',query)

(6)查询数据

router.get('/getById',async(ctx,next)=>{
    const query=`db.collection('playlist').doc('${ctx.request.query.id}').get()`
    const res=await callCloudDB(ctx,'databasequery',query)
    //console.log(res)
    ctx.body={
        code:20000,
        data:JSON.parse(res.data)
    }
})
知识点三:文件的上传、下载和删除

创建功能模块

const getAccessToken = require('./getAccessToken.js')
const callCloudDB = require('./callCloudDB.js')

const rp =require('request-promise')
const fs=require('fs')

const cloudStore={

    //下载图片
    async download(ctx,fileList){
        const ACCESSTOKEN=await getAccessToken()
        const options={
            method:'POST',
            uri:`https://api.weixin.qq.com/tcb/batchdownloadfile?access_token=${ACCESSTOKEN}`,
            body:{
               env:ctx.state.env,
               file_list:fileList
            },
            json:true
        }
        return await rp(options).then((res)=>{
              return res
        }).catch((err)=>{
            console.log(err)
        })
    },
    
    //上传图片到云存储
    async uploadfile(ctx){
        //步骤一:发送上传文件的请求,获取相应信息
        const ACCESSTOKEN=await getAccessToken()
        const file=ctx.request.files.file//上传文件信息
        //文件路径(随机)
        const path=`swiper/${Date.now()}-${Math.random}-${file.name}`
        const options={
            method:'POST',
            uri:`https://api.weixin.qq.com/tcb/uploadfile?access_token=${ACCESSTOKEN}`,
            body:{
                env:ctx.state.env,
                path:path
            },
            json:true
        }
        const info= await rp(options).then((res)=>{
            return res
        }).catch((err)=>{
            console.log(err)
        })
        //console.log(info)
        
        //步骤二:上传图片
        const params={
            method:"POST",
            headers:{
                "content-type":'multipart/form-data',                
            },
            uri:info.url,
            formData:{
                key:path,
                Signature:info.authorization,
                'x-cos-security-token':info.token,
                'x-cos-meta-fileid':info.cos_file_id,
                file:fs.createReadStream(file.path)//二进制文件
            },
            json:true
        }
         await rp(params)
         return info.file_id//返回文件id到调用方,将其存储在云数据库
    }

    //删除文件
    async delFile(ctx,fileid_list){
        const ACCESSTOKEN=await getAccessToken()
        const options={
            method:'POST',
            uri:` https://api.weixin.qq.com/tcb/batchdeletefile?access_token=${ACCESSTOKEN}`,
            body:{
                fileid_list,
                env:ctx.state.env,
                
            },
            json:true
        }
        return await rp(options).then((res)=>{
            return res
        }).catch((err)=>{
            console.log(err)
        })
    }
}

module.exports= cloudStore

(2)调用功能模板

//读取图片(获取歌曲链接)
router.get('/list',async(ctx,next)=>{
   //读取数据库文件信息
   const query=`db.collection('swiper').get()`
   const res=await callCloudDB(ctx,'databasequery',query)
   
   //获取图片链接(http形式)
   let fileList=[]
   const data=res.data
   //遍历文件信息,封装请求对象
   for(let i=0,len=data.length;i<len;i++){
       fileList.push({
           fileid:JSON.parse(data[i]).fileId,
           max_age:7200
       })
   }
   //获取下载链接(http形式)
   const dlRes=await cloudStore.download(ctx,fileList)
   
   //封装对象,将所需要的信息返回到前端
   let returnData=[]
   for(let i=0,len=dlRes.file_list.length;i<len;i++){
         returnData.push({
            download_url:dlRes.file_list[i].download_url,//文件下载链接
            fileId : dlRes.file_list[i].fileid,
            _id:JSON.parse(data[i])._id
        })
   }
   ctx.body={
       code:20000,
       data:returnData
   }
})
//上传图片
router.post('/upload',async(ctx,next)=>{
    //上传图片 返回文件id
    const fileid=await cloudStore.uploadfile(ctx)
    //console.log(fileid)
    const query=`db.collection('swiper').add({
        data:{
            fileId:'${fileid}'
        }
    })`
    //写入数据库
    const res=await callCloudDB(ctx,'databaseadd',query)
    ctx.body={
        code:20000,
        id_list:res.id_list
    }
})
//删除文件
export function del(params){
    return request({
        params,
        url:`${baseURL}/swiper/del`,
        method:'get'
    })
}
四、配置入口文件(app.js)
const Koa=require('koa')
const Router=require('koa-router')
const cors=require('koa2-cors')
const koaBody=require('koa-body')
const ENV='haocloud-xagb6'
const app=new Koa()
const router=new Router()

//解决跨域问题
app.use(cors({
    //配置允许访问后台的URL地址
    origin:['http://localhost:9528'],
    credentials:true
}))

//接收post参数解析
app.use(koaBody({
    multipart: true,
}))

//全局中间键
app.use(async(ctx,next)=>{
    ctx.state.env=ENV
    await next()
})

// http://localhost:3000/playlist/list(调用获取歌单的URL)
const playlist=require('./controller/playlist.js')
router.use('/playlist',playlist.routes())

const swiperList=require('./controller/swiper.js')
router.use('/swiper',swiperList.routes())

const blogList=require('./controller/blog.js')
router.use('/blog',blogList.routes())

//声明router
app.use(router.routes())
//允许router下的所有方法
app.use(router.allowedMethods())



app.listen(8080,()=>{
    console.log('服务开启在端口为8080')
})

此时,如果前端要获取歌单信息,则需要向后端地址为http://localhost:8080/playlist/list发送请求

  • playlist指的是 routers.use(’/playlist’,playlist.routes())中的playlist
  • list指的是router.get(’/list’,async(ctx,next)=>{}中的list(就是一个叫playlist的router的路由下的名为list的路由 个人理解)

最后启动后端服务,在终端输入命令 node app.js(编译入口文件)

结束编译:ctrl+C即可

  • 4
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
项目描述 本系统是一套极速开发微信小程序的商城系统,主要包括用户管理、角色管理、部门管理、菜单管 理、定时任务、文件上传、数据权限、Redis 缓存、前后台统一异常处理等系统通用功能,还拥有一套完整的商城后台管理系统微信小程序源码、小程序接口服务、以及完善的支付流程,极大缩短项目的开发周期。 项目特点 ◆ shop-wechat-mall 采用 Spring、MyBatis、Shiro、swagger 框架开发。 ◆ 灵活的权限控制,可控制到页面或按钮,满足绝大部分的权限需求。 ◆ 完善的部门管理及数据权限,通过注解实现数据权限的控制。 ◆ 支持 MySQL 数据库。◆ 多个团队协作开发,有效降低核心代码泄露。 ◆ 推荐使用阿里云服务器部署本系统项目介绍 shop-admin 后台模块,也是系统的核心,用来开发后台管理系统。 shop-api 接口模块,是小程序商城的接口开发模块。实现了微信用户登录、接口权限认证、获取登录用户、商城首页、专题、分类、 购物车、个人中心等功能,为小程序商城接口的安全调用,提供一套完整的解决方案。 shop-common 公共模块,其他模块以 jar 包的形式引入进去,主要提供些工具类,以 及 shop-admin、shop-api 模块公共的 entity、mapper、dao、service 服务,防止一个功能重复多次编代码。 shop-framework 系统 web 合并模块,最终项目打包部署模块。最后会介绍为什么会设计此模块,以及设计此模块的意图。 shop-gen 代码生成器模块,只需在数据库里,创建好表结构,就可以生成增、删、改、查等操作的代码,包括 entity、mapper、 dao、service、controller、页面等所有代码,项目开发神器。 shop-schedule 定时任务模块,使用开源框架 quartz 实现分布式定时任务,动态添加、修改、删除、暂停、恢复、立即执行定时任务。 shop-shop 商城后台管理系统实现了商城的后台管理功能。 wx-mall 商城小程序端源码 开发使用到的软件和工具 Xshell6、Xftp6、Tomcat8.0.33、jdk1.8、MySQL5.7、redis4.0.1 本地部署 ◆ 配置环境(推荐 jdk1.8、maven3.3、tomcat8、mysql5.5+、redis4.0.1) 本机启动 redis 服务、mysql 数据库初始化项目 ◆ 创建数据库 shop-shop,数据库编码为 UTF-8,执行数据库脚本_sql/shop.sql、sys_region.sql、更新脚本.sq ◆ 启动项目之前修改 dev/shop.properties,修改数据库账号和密码,wx.appId、wx.secret、wx.mchId、wx.paySignKey ◆ 修改 j2cache.propertie 配置 redis.hosts 和 redis.password 使用 IDEA 启动项目 配置 tomcat启动成功,访问 http://localhost账号密码:admin/admin Swagger 路径 http://localhost/swagger-ui.html 小程序接口路径 http://localhost/api/ 使用微信 web 开发者工具启动 wx-mall 导入 wx-mall 到微信 web 开发者工具修改 config/api.js 配置开发模式设置     
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海面有风

您的鼓励将是我前进的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值