# 云音乐平台搭建

serverless 函数即服务

  • 云函数:云端运行的代码,微信私有的协议天然鉴权

  • 云数据库: JSON数据库

  • 云存储:存储文件

  • 云调用:基于云函数免鉴权使用小程序开放接口

  • HTTP API:和原有项目打通

// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init()
//数据库连接操作
const db =cloud.database()
//从外界获取数据
const rp =require("request-promise")
//对接的具体数据库
const playlistCollection = db.collection("playlist")

const URL= "http://musicapi.xiecheng.live/personalized"
// 云函数入口函数
exports.main = async (event, context) => {
  //数据库获取数据
  const list = await playlistCollection.get()
  console.log(list)
  //第三方平台获取数据
  const playlist=await rp(URL).then(res=>{
      return JSON.parse(res).result
  })
  console.log(playlist)
  const newData=[]

  // if(list.data.length&list.data.length>0){
    for (let i = 0, len1 = playlist.length; i < len1; i++) {
      let flag = true
      for (let j = 0, len2 = list.data.length; j < len2; j++) {
        if (playlist[i].id === list.data[j].id) {
          flag = false
          break
        }
      }
      if (flag) {
        newData.push(playlist[i])
      }
    }
  // }else{
  //   newData=playlist
  // }
  console.log(newData)
  
  //填写数据到数据库,只支持一条条添加
  for (let i=0,len=newData.length;i<len;i++){
   await playlistCollection.add({
      data:{
        ...newData[i],
        createTime:db.serverDate()
      }
    }).then(res=>{
      console.log("success")
    }).catch(err=>{
      console.log("fail")
    })
  }
  console.log(playlist)
  return newData.length

}

# 微信小程序数据库操作

# 增加和查询

const cloud = require('wx-server-sdk') //云函数入口文件

const db =cloud.database() //获取数据库的引用

const playlistCollection = db.collection("playlist")//连接数据库playlist

const list = await playlistCollection.get()//获取数据

//添加数据
playlistCollection.add({
      data:{
        ...newData[i],
        createTime:db.serverDate()
      }
}).then(res=>{
      console.log("success")
}).catch(err=>{
      console.log("fail")
})

# 总条数

const MAX_LIMIT = 10
const countResult = await playlistCollection.count()
const total = countResult.total//获取数据库总条数

# 分页查询

const batchTimes = Math.ceil(total / MAX_LIMIT)
  const tasks = []
  for (let i = 0; i < batchTimes; i++) {
	//分页查询
    let promise = playlistCollection.skip(i * MAX_LIMIT).limit(MAX_LIMIT).get()
    tasks.push(promise)
  }
  let list = {
    data: []
  }
  if (tasks.length > 0) {
    list = (await Promise.all(tasks)).reduce((acc, cur) => {
      return {
        data: acc.data.concat(cur.data)
      }
    })
  }

# 排序方式

cloud.database().collection("playlist")
                .skip(event.start)
                .limit(event.count)
                .orderBy("createTime","desc")//逆序
                .get()
                .then(res=>{
                  return res
                })

# 云函数触发器config.json

{
  "triggers": [
    {
      "name": "myTrigger",//名称
      "type": "timer",//类型
      "config": "0 0 10,14,16,23 * * * *"//时间
    }
  ]
}

# 请求云函数 wx.cloud.callFunction

    wx.showLoading({
      title: '加载中',
    })
    wx.cloud.callFunction({
      name:"music",
      data:{
        start:this.data.playlist.length,
        count:MAX_LIMIT
      }
    }).then(res=>{
     this.setData({
       playlist:this.data.playlist.concat(res.result.data)
     })
     wx.stopPullDownRefresh()//当下拉刷新数据请求完成时取消最上方得三个加载点
     wx.hideLoading()
    })

# 云函数路由 tcb-router

云函数数量有限,可以把相似的放在同一个云函数进行处理 return app.serve()要记住加在末尾返回当前服务

// 云函数入口文件
const cloud = require('wx-server-sdk')

const TcbRouter = require('tcb-router')

cloud.init()

// 云函数入口函数
exports.main = async(event, context) => {
  const app = new TcbRouter({
    event
  })

  app.use(async(ctx, next) => {
    console.log('进入全局中间件')
    ctx.data = {}
    ctx.data.openId = event.userInfo.openId
    await next()
    console.log('退出全局中间件')
  })

  app.router('music', async(ctx, next) => {
    console.log('进入音乐名称中间件')
    ctx.data.musicName = '数鸭子'
    await next()
    console.log('退出音乐名称中间件')
  }, async(ctx, next) => {
    console.log('进入音乐类型中间件')
    ctx.data.musicType = '儿歌'
    ctx.body = {
      data: ctx.data
    }
    console.log('退出音乐类型中间件')
  })

  app.router('movie', async(ctx, next) => {
    console.log('进入电影名称中间件')
    ctx.data.movieName = '千与千寻'
    await next()
    console.log('退出电影名称中间件')
  }, async(ctx, next) => {
    console.log('进入电影类型中间件')
    ctx.data.movieType = '日本动画片'
    ctx.body = {
      data: ctx.data
    }
    console.log('退出电影类型中间件')
  })

  return app.serve()

}
//调用云函数部分
//$url是云函数内部走的路由名称
 getMusicInfo() {
    wx.cloud.callFunction({
      name: 'tcbRouter',
      data: {
        $url: 'music'
      },
    }).then((res) => {
      console.log(res)
    })
  },
  getMovieInfo() {
    wx.cloud.callFunction({
      name: 'tcbRouter',
      data: {
        $url: 'movie'
      }
    }).then((res) => {
      console.log(res)
    })
  }

# wx where条件查询

//小程序端
  _loadBlogList(start = 0) {
    wx.showLoading({
      title: '拼命加载中',
    })
    wx.cloud.callFunction({
      name: 'blog',
      data: {
        keyword,
        start,
        count: 10,
        $url: 'list',
      }
    }).then((res) => {
      console.log(res)
      this.setData({
        blogList: this.data.blogList.concat(res.result)
      })
      wx.hideLoading()
      wx.stopPullDownRefresh()
    })
  },
// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const TcbRouter = require('tcb-router')
const db = cloud.database()
const blogCollection = db.collection('blog')
const MAX_LIMIT = 100
// 云函数入口函数
exports.main = async(event, context) => {
  const app = new TcbRouter({
    event
  })

  app.router('list', async(ctx, next) => {
    const keyword = event.keyword
    let w = {}
    if (keyword.trim() != '') {
      w = {
        content: new db.RegExp({
          regexp: keyword,
          options: 'i'
        })
      }
    }

    let blogList = await blogCollection.where(w).skip(event.start).limit(event.count)
      .orderBy('createTime', 'desc').get().then((res) => {
        return res.data
      })
    ctx.body = blogList
  })
  return app.serve()
}

小程序端 也可以直接访问数据库,需要注意,云函数端cloud.database()不需要加wx,小程序端需要加

  const db = wx.cloud.database()
     db.collection('blog').orderBy('createTime', 'desc').get().then((res)=>{
       console.log(res)
       const data = res.data
       for (let i = 0, len = data.length; i<len; i++){
         data[i].createTime = data[i].createTime.toString()
       }
       this.setData({
         blogList: data
       })
     }) 
// 初始化
wx.cloud.init({
  env: 'tianqi-xxx'
})
// 获取数据库实例
const db = wx.cloud.database()
// 增
db.collection('集合名称').add({
  data: {} // 插入的数据
}).then(res => {
  // 可以通过 res._id 获取创建的记录的 id
  console.log(res._id)
})
// 删
db.collection('集合名称').doc('文档 ID').remove().then(res => {
  console.log('removed')
})
// 改
db.collection('集合名称').doc('文档 ID').update({
  data: {
    title: '我的第 1 篇文章', // 只更新 title 字段,其他不更新
  }
}).then(res => {
  // 可以通过 res._id 获取创建的记录的 id
  console.log(res._id)
})
// 查
db.collection('集合名称').doc('文档 ID').get().then(res => {
  // 打印结果,res.data 即为记录的数据
  console.log(res)
})
const _ = db.command // 取指令
db.collection('集合名称').where({
  // 查找条件
  category: 'computer',
  properties: {
    memory: _.gt(8), // 表示大于 8
  }
})

# Cron 表达式

Cron 表达式有七个必需字段,按空格分隔。

第一位 第二位 第三位 第四位 第五位 第六位 第七位
分钟 小时 星期
通配符	含义
, (逗号)	代表取用逗号隔开的字符的并集。例如:在“小时”字段中 1,2,3 表示1点、2点和3点
- (破折号)	包含指定范围的所有值。例如:在“日”字段中,1-15 包含指定月份的 1 号到 15 号
* (星号)	表示所有值。在“小时”字段中,* 表示每个小时
/ (正斜杠)	指定增量。在“分钟”字段中,输入 1/10 以指定从第一分钟开始的每隔十分钟重复。例如,第 11 分钟、第 21 分钟和第 31 分钟,依此类推
*/5 * * * * * * 表示每5秒触发一次
0 0 2 1 * * * 表示在每月的1日的凌晨2点触发
0 15 10 * * MON-FRI * 表示在周一到周五每天上午10:15触发
0 0 10,14,16 * * * * 表示在每天上午10点,下午2点,4点触发
0 */30 9-17 * * * * 表示在每天上午9点到下午5点内每半小时触发
0 0 12 * * WED * 表示在每个星期三中午12点触发

# 云调用

小程序端获取openid const { OPENID } = cloud.getWXContext()

# 模板消息(但是已经废除)

<form slot="modal-content" report-submit="true" bind:submit="onSend">
    <textarea name="content" class="comment-content"
		placeholder="写评论" value="{{content}}" fixed="true">
	</textarea>
    <button class="send" form-type="submit">发送</button>
</form>
// 推送模板消息
wx.cloud.callFunction({
  name: 'sendMessage',
  data: {
	content,
	formId,
	blogId: this.properties.blogId
  }
}).then((res) => {
  console.log(res)
})
// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init()

// 云函数入口函数
exports.main = async(event, context) => {
  const {
    OPENID
  } = cloud.getWXContext()

  const result = await cloud.openapi.templateMessage.send({
    touser: OPENID,
    page: `/pages/blog-comment/blog-comment?blogId=${event.blogId}`,
    data: {
      keyword1: {
        value: '评价完成'
      },
      keyword2: {
        value: event.content
      }
    },
    templateId: 'PjUkFDsOsC3ktzUATsIVy0t1D4RlL-aKbuhGUb7TLS0',//配置好的模板ID
    formId: event.formId
  })
  return result
}

# 微信小程序订阅功能

在微信小程序官网选择好订阅消息,新建一个模板,然后把模板ID复制.

<!-- bindsubmit会报错,可以改成tap -->
<x-bottom-modal modalShow="{{modalShow}}">
  <form slot="modal-content" report-submit="true" bind:submit="onSend">
    <textarea name="content" class="comment-content"
		placeholder="写评论" value="{{content}}" fixed="true">
	 </textarea>
    <button class="send" form-type="submit">发送</button>
  </form>
</x-bottom-modal>
//onsend请求后端操作
//小程序端先询问是否需要订阅
 wx.requestSubscribeMessage({
	  tmplIds: ['BSlLhaQ6Bm1USUtdFfwpuc3_xxxx'],
	  success (res) {
		console.log(res)
	   },
	   fail(err){
		console.log(err)
	   }
	})
	// 推送模板消息
	wx.cloud.callFunction({
	  name: 'sendMessage',
	  data: {
		content,
		formId,
		blogId: this.properties.blogId
	  }
	}).then((res) => {
	  console.log(res)
	}).catch(err=>{
	  console.log(err)
	})
// 云函数入口文件
const cloud = require('wx-server-sdk')

cloud.init()

// 云函数入口函数
exports.main = async(event, context) => {
  const {
    OPENID
  } = cloud.getWXContext()
  console.log(OPENID)
  const result = await cloud.openapi.subscribeMessage.send({
    touser: OPENID,
    page: `/pages/blog-comment/blog-comment?blogId=${event.blogId}`,
    data: {
      phrase1: {
        value: '评价完成'
      },
      thing2: {
        value: event.content
      }
    },
    templateId: 'BSlLhaQ6Bm1USUtdFfwpuc3_xxxx',
    formId: event.formId
  })
  return result
}
//config.json
{
  "permissions": {
    "openapi": [
      "subscribeMessage.send"
    ]
  }
}
最后更新: 4/27/2020, 4:14:57 PM