亿级电商微信小程序流量,如何优雅的使用云开发进行订单发货?

使用场景

电商小程序使用云开发里的数据库时,需要用到该发货功能。对大量的订单进行批量发货,即修改云数据库里的字段值。
本篇文章的使用前提是已经有订单数据了,进行修改数据发货。导出订单,我想大家应该都会我就不详细描述了,注意云函数中限制读取数量是100,需要分批次获取就分批次获取数据即可。

解决思路

  1. 通过小程序端将excel文件(xlsx类型)上传至云存储里
  2. 通过云函数(事先配置好excel文件读取的node.js包)读取上述的excel文件
  3. 使用xlsx.parse()函数解析上述读取到的文件内容
  4. 遍历数据,使用数据库修改函数,进行相应的修改字段值

开发准备

  1. 微信开发者工具
  2. 云数据库里留有需要被修改的数据
  3. 创建相应的云函数,并配置好云函数的JS第三方包

开发过程

小程序端上传文件至云存储,并将返回的fileID传递给云函数


  /**
   * 读取文件 并将路径传值给 上传文件至云存储的函数
   */
  selectFile() {
    let that = this
    wx.chooseMessageFile({
      count: 1,
      type: 'file',   //可以选择除了图片和视频之外的其它的文件 
      success(res) {
        // tempFilePath可以作为img标签的src属性显示图片
        const path = res.tempFiles[0].path
        // console.log(res)
        that.uploadFile(path)

      }
    })
  },

  /**
   * 上传文件至云存储,并拿到返回的fileID 传值给 解析函数
   */
  uploadFile(path) {
    let that = this
    wx.cloud.uploadFile({
      cloudPath: '快递单号/'+new Date().getTime() + '.x1s', 

      filePath: path,//本地文件路径
      success: res => {
        //显示提示
        wx.showLoading({
          title: '批量发货中',
          icon: 'loading'
        })
        that.jiexi(res.fileID)
      },
      fail: err => {
        wx.showToast({
          icon: 'none',
          title: '上传文件至云存储失败。错误代码:'+err,    //数据更新操作失败导致的
          duration: 2000
        })
        // console.log('上传失败', err)
      }
    })
  },

  /**
   * //3,解析exce1数据并上传到云数据库
   * fileID 为云文件id
   */
  jiexi(fileID) {
    var that = this;
    
    //调用云函数进行解析 并且修改数据库
    wx.cloud.callFunction({
      name: "excel",
      data: {
        action: 'automaticDelivery',
        fileID: fileID
      },
      success(res) {
        // 关闭 批量发货提示
        wx.hideLoading();
        // console.log("解析并上传成功", res.result)
        // console.log(res.result[1]['stats']['updated'])
        
        var walletList = []  //首次载入,无需设置初值,但类型需要设置为数组型
        try {
          // 字符处理代码段
          for (var i = 0; i < res.result.length; i += 2) {
            if (res.result[i + 1].stats.updated==0){  //失败时的显示
              walletList.push(res.result[i] +' 失败')
            }
          }
          

          if (walletList == '' || walletList == undefined || walletList == []){
            wx.showToast({
              icon: 'success',
              title: '全部发货成功!',    //数据更新操作失败导致的
              duration: 2000
            })
            that.setData({
              walletList: walletList,
            })
            //发货成功后更新待发数量详细
            that.loadsteamQuantity()  //聚合显示待发件数 
          }else{

            // 将错误信息复制到剪贴板
            wx.setClipboardData({
              data: walletList.join(''),    //功能:使用分隔符将一个数组合并为一个字符串。不填默认为\n。但是''不能省略。
              success(res) {
                wx.getClipboardData({
                  success(res) {
                    // console.log("复制成功", res.data) // data
                    wx.showToast({
                      icon: 'none',
                      title: '错误信息,已复制到剪贴板',
                      duration: 4000
                    })
                  }
                })
              }
            })
            that.setData({
              walletList: walletList,
            })

          }

         

        } catch (e) {

          wx.showToast({
            icon: 'none',
            title: '字符串处理时发送异常,但可能发货已经成功,请将该错误信息截图自行查询。错误信息:'+e,
            duration: 10000
          })
          that.setData({
            walletList:'Error',
            Error:e.toString()
          })

        }


      },
      fail(res) {
        wx.showToast({
          icon: 'none',
          title: '发货失败,请联系管理员。错误代码:' + res,    //数据更新操作失败导致的
          duration: 2000
        })
        // console.log('解析失败', res)
      }
    })
  },

云函数进行相应的解析,并修改数据库字段值

注意: 云函数有自己的运行时长限制,现在好像可以设置到60秒的运行时长了,默认是运行3秒内没有结束会报异常提示,停止函数运行。另外,注意“高并发”的事情,免费版只给20个连接允许同一秒钟运行。笔者经常遇到高并发,故采用了运行一段时间,休息一下,紧接着再继续运行。这样操作可以避免并发操作,导致修改失败。
大家可以在字段值里面留个flag,修改过的改变状态值,这样,万一真的失败了,再次修改的时候会判断已经修改过了不再进行修改。1来节省性能,单有查询函数运行时间短,查到并修改这个运行时间长。2来如果字段值涉及时间的,可以不用再去改一遍已经发货的时间。

/**
 * 批量自动发货修改数据库,提醒客户已经发货了
 */
async function automaticDelivery(event) {

  try { 

    let {
      fileID
    } = event
    //1,通过fileID下载云存储里的excel文件
    const res = await cloud.downloadFile({
      fileID: fileID,
    })
    const buffer = res.fileContent

    var courierNumber = []  //将一个订单多个运单号合并到这个变量里 读写结束后清空这个变量  后边要是有清空 声明要用 var  不要用 const 否则会报错

    let _id =''  //对上回的业务单号进行缓存,对比与本次业务单号的异同,如果不同开始进行对上次的写入操作

    let currentName ='' //记录当前更新人的数据 好返回给前台

    var countTimes = 0  //控制并发数使用 计次

    const tasks = [] //用来存储所有的添加数据操作

    //构造发货时间   Date.now()是时间戳
    let curDate = new Date();
    let deliveryTime = `${curDate.getFullYear()}/${curDate.getMonth() + 1}/${curDate.getDate()} ${curDate.getHours() + 8}:${curDate.getMinutes()}:${curDate.getSeconds()}`;
    /构造明文下单日期代码结束///

    //2,解析excel文件里的数据
    var sheets = xlsx.parse(buffer); //获取到所有sheets
    sheets.forEach(function (sheet) {
      // console.log(sheet['name']);
      for (var rowId in sheet['data']) {  //for循环读取数据开始 27  26 27 判断最后一个业务单号到了

        // console.log(rowId);
        var row = sheet['data'][rowId]; //第几行数据      //rowId 这个变量是从第0行开始的 一直往大变

        //console.log(row)//读取数据库修改代码块  开始//
        if (rowId > 0 && row) { //第一行是表格标题,所有我们要从第2行开始读

          //使用分割函数,切割字符串 提取出业务单号 split() 功能:使用一个指定的分隔符把一个字符串分割存储到数组
          str = row[1]; //改造过的表数据id 可能有-0 -1 -2 业务单号
          arr = str.split("-");  //切割字符串 提取出业务单号  读取后的数组示例:[ '72527ac65df855b3041864f87d4083a3', '0' ]

          //合并相同订单的运单号到一个数组变量里 当第二次读取的业务单号不同时 将合并好的 运单号写入上一个的数据记录里
    
          if( _id==arr[0]){                   //相同时进行合并运单号 1

            courierNumber.push(String(row[0])) //追加   运单号普通数组 往这个字段的后边按顺序追加 运单号,这样就刚好跟excel表格的运单号对应  存为字符串类型
          } else if (_id) {                   //排除首次启动这个值为空  存在 且不与本次对比的值相等 则进入 2 

              console.log(_id)  //打印即将修改的数据
              console.log(courierNumber)

              //3,把解析到的数据存到excelList数据表里
              const promise = db.collection('Shopping').where({
                _id: _id, //表数据唯一 id,上回id
                delivery_status: _.or(_.eq(null), _.eq(0)),//0待发货 1已发货  
              }).update({
                data: {
                  deliveryTime: deliveryTime, //发货时间
                  delivery_status: 1,     //0待发货 1已发货
                  expressType: row[7] || '百世快递', //快递类型  例如:百世快递。  如果发货表的H列没有填写则为百世快递
                  courierNumber: courierNumber, // 刚才合并好的数组变量
                }
              })
              // console.log('还能执行到这里吗?')
              //等待数据更新完毕
            tasks.push(_id + '-' + currentName, promise)  //row[2]的记录是 收货人姓名  _id才是本次更新的主谋
              //更新完毕次数+1
              countTimes+=1
              /**避免并发数量超出限制,进行休息 */
              if (countTimes % 15 == 0) {  //每n 16 20个连接 休息一次
                sleep(3000)  //休息2秒钟后继续
                console.log('我休息了3秒钟')
                countTimes = 0 //计数清0
              }
              /*********休息代码块结束******* */

              //发货完毕后,将本次的id 与运单号存下来
              _id = arr[0]
            currentName = row[2]
              courierNumber = []  //先清空变量
              courierNumber.push(String(row[0])) //再次重新压入新的值

            }else{                                //首次启动,将第一行的业务单号传给缓存变量 _id  3  将运单号也传入
              _id = arr[0]
            currentName = row[2]
              courierNumber.push(String(row[0])) //首次压入
            }
            

        }  //if 判断值存在的

        /读取数据并修改数据库代码块  结束

      }  //for 循环结束

      //写入最后一个业务单号的 数据  //由于上面的for循环在第二次将数据缓存到变量时,就结束循环了,会导致结尾的数据无法更新到位,特在此单独列到
      console.log(_id)  //打印即将修改的数据
      console.log(courierNumber)
      const promise_end = db.collection('order').where({
        _id: _id, //表数据唯一 id,上回id
        delivery_status: _.or(_.eq(null), _.eq(0)),     //0待发货 1已发货 
      }).update({
        data: {
          deliveryTime: deliveryTime, //发货时间
          delivery_status: 1,     //0待发货 1已发货
          expressType: row[7] || '百世快递', //快递类型  例如:百世快递。  如果发货表的H列没有填写则为百世快递
          courierNumber: courierNumber, // 刚才合并好的数组变量
        }
      })
      tasks.push(_id + '-' + currentName, promise_end)  //row[2]的记录是 收货人姓名

    });   //sheets.forEach 结束

    console.log(tasks) //控制台打印日志
    // 等待所有数据添加完成
    let result = await Promise.all(tasks).then(res => {
      return res
    }).catch(function (err) {
      return '返回任务过程发生错误'+err
    })
    // console.log('调用了批量自动发货函数:'+result)
    return result

  } catch (e) {
    console.error('try批量发货函数里发生错误' +e)
    return e
  }

}
************批量发货************ */

总结

只要开发的思路清晰,后端代码完成应该不难。在调试的过程中,注意观察每个变量值,不要造成逻辑错误即可。云开发也已经支持http进行访问修改了,以后有机会再出一篇通过服务器进行修改云数据库值的文章。其实,发货顾名思义就只是改变个状态,新增个运单号完全就是对数据进行写或改的过程,属于基本的数据库操作。

老规矩,先看本节效果图我们实现这个支付功能完全是借助小程序云开发实现的,不用搭建自己的服务器,不用买域名,不用备案域名,不用支持https。只需要一个简单的云函数,就可以轻松的实现微信小程序支付功能。核心代码就下面这些一,创建一个云开发小程序关于如何创建云开发小程序,这里我就不再做具体讲解。不知道怎么创建云开发小程序的同学,可以去翻看我之前的文章,或者看下我录制的视频:https://edu.csdn.net/course/play/9604/204528创建云开发小程序有几点注意的1,一定不要忘记在app.js里初始化云开发环境。2,创建完云函数后,一定要记得上传二, 创建支付的云函数1,创建云函数pay三,引入三方依赖tenpay我们这里引入三方依赖的目的,是创建我们支付时需要的一些参数。我们安装依赖是使用里npm 而npm必须安装node,关于如何安装node,我这里不做讲解,百度一下,网上一大堆。1,首先右键pay,然后选择在终端中打开2,我们使用npm来安装这个依赖。在命令行里执行 npm i tenpay安装完成后,我们的pay云函数会多出一个package.json 文件到这里我们的tenpay依赖就安装好了。四,编写云函数pay完整代码如下//云开发实现支付 const cloud = require('wx-server-sdk')cloud.init() //1,引入支付的三方依赖 const tenpay = require('tenpay'); //2,配置支付信息 const config = ;exports.main = async(event, context) => 一定要注意把appid,mchid,partnerKey换成你自己的。到这里我们获取小程序支付所需参数的云函数代码就编写完成了。不要忘记上传这个云函数。出现下图就代表上传成功五,写一个简单的页面,用来提交订单调用pay云函数。这个页面很简单,1,自己随便编写一个订单号(这个订单号要大于6位)2,自己随便填写一个订单价(单位是分)3,点击按钮,调用pay云函数。获取支付所需参数。下图是官方支付api所需要的一些必须参数。下图是我们调用pay云函数获取的参数,和上图所需要的是不是一样。六,调用wx.requestPayment实现支付下图是官方的示例代码这里不在做具体讲解了,完整的可以看视频。实现效果1,调起支付键盘2,支付完成3,log日志,可以看出不同支付状态的回调上图是支付成功的回调,我们可以在支付成功回调时,改变订单支付状态。下图是支付失败的回调,下图是支付完成的状态。到这里我们就轻松的实现了微信小程序的支付功能了。是不是很简单啊,完整的讲解可以看视频。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

白天到处飞

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值