积分
在交互系统中,可以通过看视频、发评论、点赞、签到等操作获取积分,获取的积分又可以参与排行榜、兑换优惠券等,提高用户使用系统的积极性,实现引流。这些功能在很多项目中都很常见,关于功能的实现我的思路如下。
1.1 积分规则
用户在使用我们xx系统时,可以通过各种交互行为产生积分,不同类型的行为获得不等的分数,比如:
1.签到规则
签到1天给1分
连续7天额外奖励10分
连续14天额外奖励20分
连续28天额外奖励40分
每月签到进度当月第一天重置
2.学习规则
每学习一小节积分+10,每天获得上限50分
3.交互规则(有效交互数据参与积分规则,无效数据会被删除)
写评价,积分+10 每日获得上限为50分
写问答,积分+5 每日获得上限为20分
写笔记,积分+3,每次被采集+2 每日获得上限为20分
原型图类似下图所示:
1.2 积分数据表设计
根据产品原型可以得到积分表需要记录信息有:本次得到积分值、积分方式、获取积分时间、
获取积分的人。
所以积分表里需要设计的字段有:积分记录表id、用户id、积分方式(1-课程学习,2-每日签到,3-课程问答, 4-课程笔记,5-课程评价)、积分值、创建时间。
1.3 签到
1.3.1 数据库表设计
签到最核心的包含两个要素:
- 谁签到:用户id
- 什么时候签的:签到日期
同时要考虑一些功能要素,比如:
- 补签功能,所以要有补签标示
- 按照年、月统计的功能:所以签到日期可以按照年、月、日分离保存
所以签到表里需要设计的字段有:主键id、用户id、签到日期、签到月份、签到年份、是否补签。
1.3.2 签到设计优化:Bitmap
但是,通过观察签到表的设计,会发现一条签到记录就会占用22个字节,如果每天有很多用户签到,一个月就会有大量签到记录,这样会非常浪费空间,针对这一问题我们可以采用Redis提供的bitmap(位图)这一数据结构进行优化。
我们可以使用bitmap将用户一个月的签到记录保存为一条数据存入数据库,每一个bit为对应当月的每一天,形成映射关系,用0和1分别标识是否打卡。
在Redis中,bitmap底层还是基于String类型实现的, 存储数据是以二进制(bit位)为单位进行存储的bitmap在处理大量数据统计和判断时,可以只占用非常小的一部分内存,并且计算速度非常高效!
1.3.3 签到的功能实现:查询和新增
- 查询签到:
- 在签到日历中,把当前登录用户本月第一天到今天为止的所有签到过的日期高亮显示;今天若未打卡,则前端显示蓝色“打卡”,若已打卡,则前端显示蓝色“已打卡”。
- 需要返回每日签到记录,0表示未签到,前端显示灰色;1表示已签到,前端高亮显示。
- 最终返回一个由0或1组成的数组,对应从本月第1天到今天为止每一天的签到情况。
- 新增签到:
- 在个人中心的积分页面,用户每天都可以签到一次,签到成功则把BitMap中的与签到日期对应的bit位设置为1。
- 为了便于统计,组装签到记录,保存到redis的bitMap,可以将每个月为每个用户生成一个独立的KEY,KEY中包含用户信息、月份信息,即sign:用户id:日期。
- 获取连续签到的天数,计算连续签到积分,保存到积分表。
1.4 新增积分
用户签到、学习、参与互动问答、提交学习笔记等行为都可以产生积分,并基于积分形成排行榜。积分当月有效,月底清零。
由积分规则可知,获取积分的行为多种多样,而且每一种行为都有自己的独立业务。而这些行为产生的时候需要保存一条积分明细到数据库。
我们显然不能要求其它业务的开发者在开发时帮我们新增一条积分记录,这样会导致原有业务与积分业务耦合。因此必须采用异步方式,将原有业务与积分业务解耦。如果有必要,甚至可以将积分业务抽离,作为独立微服务。
所以我的实现思路是使用MQ实现服务之间的解耦:
- 在所有可以获取积分的业务( 用户签到、学习、参与互动问答、提交学习笔记等)中发送MQ消息
- 根据不同的积分获取方式,编写不同的消息监听器,监听保存积分的消息,让实现积分的添加
- 添加积分:
- 判断当前积分类型是否有上线,如果以达到上限,根据上线确定本次积分保存大小
- 未达到上线,查询今天本类型已经获取了多少分,判断是否已经到达今日分数上线
- 如果积分超出每日上限,不做操作,结束程序
- 如果积分不足每日上限,并且加上本次会超过上限,只保存欠额部分
1.5 查询积分
在个人中心,用户可以查看当天各种不同类型的已获得的积分和积分上限,即需返回的数据有积分类型描述、今日已获取积分值、 积分上限。
总结
1. 设计签到功能时,为什么采用BitMap?
答:使用BitMap是以二进制0 1来表示数据的,0表示未签到,1表示已签到,这样就可以将一个人一个月份的签到记录保存到一条数据中,大大的节省了空间的使用。