[mini-blog][v2.1.0]博客小程序签到功能实现
- 2020 年 3 月 11 日
- 筆記
昨天抽空将mini-blog的签到功能基本上实现了,这里分享下。
很早之前就想实现签到功能,昨天终于花时间去变现了,先来看下效果图。
效果

个人中心
的排版进行了一定的改造,将关于个人相关的信息整合,留些位置给到签到。

截图1
签到页还是常规的日历组件,为了保证一页填满,加了一个广告组件。

截图2
后端实现

页面样式定好之后,就看怎么设计了,首先是数据库,在云数据库中我又新增了三个集合。
首先是mini_member
,用于记录会员相关的汇总数据,以openId
作为唯一索引,其中也预留了一些属性,比如积分,未读消息等,用于后期的功能迭代中。
{ "openId": "oyMiZ5aClPsDJ_2LqWNXWXMolyhs", "continueSignedCount": 1,//连续签到数量 "lastSignedDate": "2020-03-05",//最近签到日 "level": 1,//会员等级(预留) "totalPoints": 1,//总的积分(预留) "totalSignedCount": 1,//总的签到数 "unreadMessgeCount": 0,//未读消息(预留) "modifyTime": 1583410585920 }
然后是两张明细表,mini_point_detail
和mini_sign_detail
,用于记录用户的签到明细和积分明细。
基本上这三个集合可以基本满足签到+积分的一些简单功能了。
接着就是云函数的编写了,在这里我也新增了一个云函数memberService
,用于处理会员维度相关的操作。
这次核心的方法就是签到,要操作的集合还是比较多的,除了要操作上面新增的三个集合之外,还要加一个mini_subcribute
,用于接入签到提醒的订阅消息。核心代码如下:
async function addSign(event) { console.info("addSign") try { let memberInfos = await db.collection('mini_member').where({ openId: event.info.openId }).get(); const tasks = [] let date = new Date().toFormat("YYYY-MM-DD") if (memberInfos.data.length === 0) { let task1 = db.collection('mini_member').add({ data: { openId: event.info.openId, totalSignedCount: 1,//累计签到数 continueSignedCount: 1,//持续签到 totalPoints: 1,//积分 lastSignedDate: date,//最后一次签到日期 level: 1,//会员等级(预留) unreadMessgeCount: 0,//未读消息(预留) modifyTime: new Date().getTime() } }) tasks.push(task1) } else { let continueSignedCount = 1 let memberInfo = memberInfos.data[0] if (new Date().addDays(-1).toFormat("YYYY-MM-DD") == memberInfo.lastSignedDate) { continueSignedCount = memberInfo.continueSignedCount + 1 } let task2 = db.collection('mini_member').doc(memberInfo._id).update({ data: { totalSignedCount: _.inc(1), continueSignedCount: continueSignedCount, totalPoints: _.inc(1), lastSignedDate: date, modifyTime: new Date().getTime() } }); tasks.push(task2) } //签到明细 let date1 = new Date().toFormat("YYYY-M-D").split("-") let task3 = db.collection('mini_sign_detail').add({ data: { openId: event.info.openId, year: date1[0], month: date1[1], day: date1[2], createTime: new Date().getTime() } }) tasks.push(task3) //如果统一订阅签到通知 if (event.info.accept == 'accept') { let task4 = await db.collection("mini_subcribute").add({ data: { templateId: event.info.templateId, openId: event.info.openId, timestamp: new Date().getTime() } }); tasks.push(task4) } //积分明细 let task5 = db.collection('mini_point_detail').add({ data: { openId: event.info.openId, operateType: 0,//0:获得 1:使用 2:过期 count: 1, desc: "签到得积分", date: (new Date()).toFormat("YYYY-MM-DD HH:MI:SS"), createTime: new Date().getTime() } }) tasks.push(task5) await Promise.all(tasks) return true } catch (e) { console.error(e) return false } }
当然这里还是不够严谨的,理论上最好使用事务「云开发目前已经支持了,后面我优化下」,保证这几个集合的数据一致性。
其他用于界面展示的数据,可以绕过云函数,直接在小程序端查询云数据库即可,代码较简单,简单的查询。
交互实现

在前端交互上,本着不重复造轮子的目的,找了个开源日历组件「wx_calendar
」,样式上基本能接受,功能也满足,所以直接拿来用了。详细的文档可以看下他的github,文档还是比较详细的。
主要在初始化日历组件之后,需要获取历史已经签到的明细,来渲染选中状态:
/** * 日历组件渲染之后 */ afterCalendarRender: async function (e) { let year=util.getYear(new Date()) let month=util.getMonth(new Date()) let res = await api.getSignedDetail(app.globalData.appid, year.toString(), month.toString()) console.info(res) let toSet = []; res.result.forEach(function (item) { let set = { year: item.year, month: item.month, day: item.day } toSet.push(set) }) this.calendar.setSelectedDays(toSet); }
另外就是在用户点击签到前,唤起下订阅消息授权,接受提醒则每天可以定时提醒用户签到。
/** * 签到 */ bindSignFn: function (e) { let that = this let tempalteId = 'J-MZ6Zrd08TobUgWPbjQcnJt9BHbc9M-nOOxirC8nWA' wx.requestSubscribeMessage({ tmplIds: [tempalteId], success(res) { console.info(res) that.submitSign(res[tempalteId], tempalteId,that).then((res) => { console.info(res) }) }, fail(res) { console.info(res) wx.showToast({ title: '程序有一点点小异常,操作失败啦', icon: 'none', duration: 1500 }) } }) },
最后

签到功能已经发版上线拉,有兴趣的小伙伴可以体验下哦。
至于一些细节上交互和实现可以直接获取源码查看,有好的建议也欢迎留言。
下个迭代将完善积分模块,增加一些积分获取的渠道,同时想下有什么福利可以利用积分系统来分享给大家。