服务端:https://github.com/jrainlau/t...
经过为期两个晚上下班时间的努力,终于把我第一个小程序开发完成并发布上线了。整个过程还算顺利,由于使用了mpvue方案进行开发,故可以享受和vue一致的流畅开发体验;后台系统使用了python3+flask框架进行,使用最少的代码完成了小程序的后台逻辑。除了开发之外,还实实在在地体验了一把微信小程序的开发流程,包括开发者工具的使用、体验版的发布、上线的申请等等。这些开发体验都非常值得被记录下来,于是便趁热打铁,写下这篇文章。
由于公司里有相当多的同事都住在同一个小区,所以上下班的时候经常会在公司群里组织拼车。但是由于完全依赖聊天记录,且上下班拼车的同事也很多,依赖群聊很容易把消息刷走,而且容易造成信息错乱。既然如此,那么完全可以开发一个小工具把这些问题解决。
发起拼车的人把出发地点、目的地点、打车信息以卡片的形式分享出来,参与拼车的人点击卡片就能选择参加拼车,并且能看到同车拼友是谁,拼单的信息等等内容。
交互流程如下:

可以看到,逻辑是非常简单的,我们只需要保证生成拼单、分享拼单、进入拼单和退出拼单这四个功能就好。
需求和功能已经确定好,首先按照小程序官网的介绍,注册好小程序并拿到appId,接下来可以开始进行后台逻辑的开发。
由于时间仓促,功能又简单,所以并没有考虑任何高并发等复杂场景,仅仅考虑功能的实现。从需求的逻辑可以知道,其实后台只需要维护两个列表,分别存储当前所有拼车单以及当前所有参与了拼车的用户即可,其数据结构如下:


当用户确定并分享了一个拼单之后,会直接新建一个拼单,同时把该用户添加到当前所有参与了拼车的用户列表列表里面,并且添加到该拼单的成员列表当中:

只要维护好这两个列表,接下来就是具体的业务逻辑了。
为了快速开发,这里我使用了python3+flask框架的方案。不懂python的读者看到这里也不用紧张,代码非常简单且直白,看看也无妨。
首先新建一个BillController类:
class BillController:
billsList = []
inBillUsers = []
接下来会在这个类的内部添加创建拼单、获取拼单、参与拼单、退出拼单、判断用户是否在某一拼单中、图片上传的功能。
该方法接收客户端传来的拼单ID,然后拿这个ID去检索是否存在对应的拼单。若存在则返回对应的拼单,否则报错给客户端。
def getBill(self, ctx):
ctxBody = ctx.form
billId = ctxBody['billId']
try:
return response([item for item in self.billsList if item['billId'] == billId][0])
except IndexError:
return response({
'errMsg': '拼单不存在!',
'billsList': self.billsList,
}, 1)
该方法会接收来自客户端的用户信息和拼单信息,分别添加到billsList和inBillUsers当中。
def createBill(self, ctx):
ctxBody = ctx.form
user = {
'userId': ctxBody['userId'],
'billId': ctxBody['billId'],
'name': ctxBody['name'],
'avatar': ctxBody['avatar']
}
bill = {
'billId': ctxBody['billId'],
'from': ctxBody['from'],
'to': ctxBody['to'],
'time': ctxBody['time'],
'members': [user]
}
if ctxBody['userId'] in [item['userId'] for item in self.inBillUsers]:
return response({
'errMsg': '用户已经在拼单中!'
}, 1)
self.billsList.append(bill)
self.inBillUsers.append(user)
return response({
'billsList': self.billsList,
'inBillUsers': self.inBillUsers
})
创建完成后,会返回当前的billsList和inBillUsers到客户端。
接收客户端传来的用户信息和拼单ID,把用户添加到拼单和inBillUsers列表中。
def joinBill(self, ctx):
ctxBody = ctx.form
billId = ctxBody['billId']
user = {
'userId': ctxBody['userId'],
'name': ctxBody['name'],
'avatar': ctxBody['avatar'],
'billId': ctxBody['billId']
}
if ctxBody['userId'] in [item['userId'] for item in self.inBillUsers]:
return response({
'errMsg': '用户已经在拼单中!'
}, 1)
theBill = [item for item in self.billsList if item['billId'] == billId]
if not theBill:
return response({
'errMsg': '拼单不存在'
}, 1)
theBill[0]['members'].append(user)
self.inBillUsers.append(user)
return response({
'billsList': self.billsList,
'inBillUsers': self.inBillUsers
})
接收客户端传来的用户ID和拼单ID,然后删除掉两个列表里面的该用户。
这个函数还有一个功能,如果判断到这个拼单ID所对应的拼单成员为空,会认为该拼单已经作废,会直接删除掉这个拼单以及所对应的车辆信息图片。
def leaveBill(self, ctx):
ctxBody = ctx.form
billId = ctxBody['billId']
userId = ctxBody['userId']
indexOfUser = [i for i, member in enumerate(self.inBillUsers) if member['userId'] == userId][0]
indexOfTheBill = [i for i, bill in enumerate(self.billsList) if bill['billId'] == billId][0]
indexOfUserInBill = [i for i, member in enumerate(self.billsList[indexOfTheBill]['members']) if member['userId'] == userId][0]
# 删除拼单里面的该用户
self.billsList[indexOfTheBill]['members'].pop(indexOfUserInBill)
# 删除用户列表里面的该用户
self.inBillUsers.pop(indexOfUser)
# 如果拼单里面用户为空,则直接删除这笔拼单
if len(self.billsList[indexOfTheBill]['members']) == 0:
imgPath = './imgs/' + self.billsList[indexOfTheBill]['img'].split('/getImg')[1]
if os.path.exists(imgPath):
os.remove(imgPath)
self.billsList.pop(indexOfTheBill)
return response({
'billsList': self.billsList,
'inBillUsers': self.inBillUsers
})
接收客户端传来的用户ID,接下来会根据这个用户ID去inBillUsers里面去检索该用户所对应的拼单,如果能检索到,会返回其所在的拼单。
def inBill(self, ctx):
ctxBody = ctx.form
userId = ctxBody['userId']
if ctxBody['userId'] in [item['userId'] for item in self.inBillUsers]:
return response({
'inBill': [item for item in self.inBillUsers if ctxBody['userId'] == item['userId']][0],
'billsList': self.billsList,
'inBillUsers': self.inBillUsers
})
return response({
'inBill': False,
'billsList': self.billsList,
'inBillUsers': self.inBillUsers
})
接收客户端传来的拼单ID和图片资源,先存储图片,然后把该图片的路径写入对应拼单ID的拼单当中。
def uploadImg(self, ctx):
billId = ctx.form['billId']
file = ctx.files['file']
filename = file.filename
file.save(os.path.join('./imgs', filename))
# 把图片信息挂载到对应的拼单
indexOfTheBill = [i for i, bill in enumerate(self.billsList) if bill['billId'] == billId][0]
self.billsList[indexOfTheBill]['img'