会员积分功能模型设计
最近在开发中遇上一个会员(用户)积分的场景,我在网上查找相关设计文章,并没有发现合适的方案,所以在此写下我思考和设计的过程,供程序猿们参考。
积分管理这一功能,已经算是十分常见,夸张点说是烂大街了的场景,但仔细分析后,发现要做好也不容易,得考量多个细节,对初级程序员来说,也算是一个很有意义的练习题目。以下内容,没有一行代码,没有一张图表,也没有设计文档参见的模式套路,对一些人来说,读之枯燥,但是,如果您对“设计”工作有兴趣,相信我,不妨一读。
因为不是专业做CRM或销售领域的,对“会员积分”的需求了解并不深,这里仅仅是对一般的需求场景进行分析设计,权当抛砖引玉,如果有更多复杂需求,还请留言,不吝赐教。
需求概要
假设某企业引入会员积分业务。会员有多种渠道获得积分,另外还可以兑换积分。此外,积分有有效期,每项积分6个月不兑换,则自动失效。
请设计一下数据模型结构和基本业务逻辑。要求能计算会员的总积分、当前有效积分、最近7天内要到期的积分,能统计每月、每季度、每年的积分发放、消费、剩余情况。
接下来,我们来将需求细化,一步步完成对应的设计。
需求一 记录积分获取和积分消费
创建一个对象,里面记录用户id,获得积分,原因等等,再记录时间,就可以了。所以我们有了第一个实体模型:【积分记录】
同样的,积分消费,用一个对象记录消费情况,也记录消费积分、内容、时间等。然后我们发现积分消费可以用积分记录模型,只是分数为负数就好。
需求二 积分消费校验和积分统计
要统计某用户积分,将他的积分记录查询出来,对积分字段求和就好。
在做积分消费时,查询用户之前积分合计,如果分数小于要消费的积分,就返回不允许消费。
需求三 积分过期
接下来考虑积分的有效期需求,比如积分以年为固定周期,每年将去年积分失效,或者每笔积分的有效期都是一年。我们先按后面一种情况考虑。
首先积分过期可以使用定期执行的策略,查询积分记录,进行过期处理。
很直观地,在积分记录里面,增加一个字段,记录过期时间。定期检查过期时候,判断过期就号。但是,如果这笔积分被消费了,那么就不存在过期的概念了,如果继续按过期处理,那么积分统计时,无法区分这笔是否被消费过,即需求2不能满足。
一种方案,将消费时消费了之前哪些积分记录下来,但是这种方式的计算量大,额外增加不少存储空间,并不好。
另一种方案,在积分记录里面增减一个“剩余积分”字段,用户的有效积分改为“剩余积分”字段的合计。积分消费时,按时间顺序取积分记录,逐条扣减剩余积分。积分过期时,简单地把剩余积分清零就好。
积分统计就改为统计【积分记录】的“剩余积分”字段。
但是这仍然有个问题,我们看下一条需求。
需求四 求截止上月的积分统计
之前的方案,能解决当前实时的积分统计,但是假设某用户上个月有条记录未过期,但这个月过期,剩余积分为0,这时候我们就统计不出上个月的积分情况了。这是因为“剩余积分”里面记录了被消费扣减的积分和过期的积分,无法区分,“过期时间”这个信息还不足够,除非我们再增加字段,将消费扣除的积分和过期扣除的积分区分开来,算上个月积分时,排除过期时间晚于1号的过期积分记录。
但是这样一来,积分的业务规则就相当复杂了。为此我们调整一下方案。
首先,将积分过期也视为一条【积分记录】,积分过期就增加一笔“过期”的【积分记录】。之前引入的“剩余积分”字段取消。然后增加一个【积分账户】实体模型,里面有一个指针,指向最后一条被扣减的积分记录,以及该记录的剩余积分。比如有某用户有10条积分获取记录,每条积分都是10分(id从1-10),然后消费一笔35积分,那么 记录最后一次消费积分时,积分指针id=4,该记录剩余积分等于5。然后,假设第二天前5条都过期了,那么【积分账户】里的指针指向id=5,剩余积分0。
同时,我们将用户的总积分、剩余积分也存在【积分账户】里,获取这个信息时候就不用再从【积分记录】里面去计算了。
要统计截止上个月的积分,只需要查询【积分记录】里截止上个月的记录,汇总积分字段即可。
要统计近期过期积分,查询【积分账户】里指针之后的记录,并且满足近期过期条件的即可。
假设数据有异常,需要调整某个用户很久以前的一笔积分,那么【积分账户】是能被重新计算出来的,取出所有积分获取记录,积分消费记录,按时间顺序,逐条执行积分账户消费处理和积分过期处理就行。
最后,罗列一下需求,如果你有其他设计思路,看看是否满足以下需求:
能记录用户获取积分的明细信息并展示;
能记录用户获取积分和消费积分的明细信息并展示;
消费积分时,要检查积分余额是否大于等于要消费的积分;
积分有有效期,过期失效;
能统计用户的总积分,剩余积分,能提醒用户近期过期积分;
能统计之前某个时间点比如上个月的积分情况;
能支持调整之前的积分数据;
能满足数据量较大或巨大的场景;
补充,高并发大数据量如何处理
高并发场景,可能要引入负载均衡部署,这时候一般的处理逻辑会引入并发问题,对此业内有很多对应方案了,简单的处理办法,使用消息队列,将并发请求转换为单一队列(串行)。这里给出一个简单方案。
首先,所有增加和消费积分的请求,都发送到 redis 里(把 redis 当作消息队列用)。然后,部署一个应用专门处理积分检查和持久化操作,按顺序执行请求即可,这样就不会有并发冲突了。如果一个应用处理还是不够,那么也可以部署多个应用,在处理积分请求时,针对这个用户id加锁(把 redis 当作分布式锁),完成后解锁。
大数据量场景,可以考虑按年/月将积分记录转移到历史记录表,降低【积分记录】表的数量量即可。
最新评论