使用场景
电商小程序使用云开发里的数据库时,需要用到该发货功能。对大量的订单进行批量发货,即修改云数据库里的字段值。
本篇文章的使用前提是已经有订单数据了,进行修改数据发货。导出订单,我想大家应该都会我就不详细描述了,注意云函数中限制读取数量是100,需要分批次获取就分批次获取数据即可。
解决思路
- 通过小程序端将excel文件(xlsx类型)上传至云存储里
- 通过云函数(事先配置好excel文件读取的node.js包)读取上述的excel文件
- 使用xlsx.parse()函数解析上述读取到的文件内容
- 遍历数据,使用数据库修改函数,进行相应的修改字段值
开发准备
- 微信开发者工具
- 云数据库里留有需要被修改的数据
- 创建相应的云函数,并配置好云函数的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进行访问修改了,以后有机会再出一篇通过服务器进行修改云数据库值的文章。其实,发货顾名思义就只是改变个状态,新增个运单号完全就是对数据进行写或改的过程,属于基本的数据库操作。