服务端: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'