类支付宝积分系统设计方案(过期、兑奖)

个人博客:无奈何杨(wnhyang)

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview


声明

本篇文章纯粹抛砖引玉!

需求说明

开门见山,业务背景直接跳过。

类比支付宝会员积分,支付宝APP-我的-支付宝会员。

支付宝会员-XXX积分-积分规则,可以看到具体的积分规则,本篇文章类比于此积分业务场景,做简单的设计。

积分说明

积分不具有货币或现金价值,不可兑现,不可转让。用户可以通过支付、账户服务、金融理财和积分奖励活动等方式来获取积分。

积分可以兑换各类权益、参与各种积分活动等,具体以权益兑换及活动页面展示为准。

积分领取规则

积分发放后,用户可前往“我的”-“支付宝会员”,点击领取积分球,或者在支付成功页面、服务消息提醒、账单点击领取,积分方可到账。积分自产生之时起,领取有效期7天(168小时),逾期不领则作废,不予补发。

积分获取方式

1、支付

2、账户服务

3、金融理财

4、扫一扫

5、等

积分有效期

用户获得的积分有效期为自获得当月起的12个自然月,有效期内未使用的积分到期将自动清零,不予补发。

温馨提示:

1、用户日常使用的积分,将优先使用即将过期的积分。

2、每月,用户可在“我的”-“支付宝会员”-“收支/订单/规则”页查看当月月底即将过期的积分。

3、已使用的积分若发生退还,积分有效期不变,仍以该笔积分原获取时间计算有效期。

积分权益兑换

积分可以兑换各类权益、参与各种积分活动等,具体以权益兑换及活动页面展示为准。

积分与成长体系

需求分析

因为上面几乎都是粘贴于支付宝对外展示的积分规则,而这篇文章涉及范围并没有那么广,所以这里要声明一下需求范围。

积分获取方式

支付、账户服务、金融理财、扫一扫等。

同步方案:这个就涉及到AOP的思想,换言之“广义AOP”。这个不是单个系统能实现的,需要多方系统共同确认,而本系统需要提供可靠的积分累计接口。如:用户支付完成后应该累计积分的,通过什么方式与积分系统交互增加积分呢?这样的接口如何设计呢?除了用户唯一标识、积分值、交易时间、交易类型还有什么必要字段呢?需不需要重试?如何做幂等?分布式事务如何考虑?等等,还有很多要素要考虑。

异步方案:有时我们对于积分累积的时效性并不高,就可以考虑异步方案。异步无非就是消息队列类的异步任务,或是有数据团队来做数据的分析处理。异步任务方式不管是消息队列或是事件驱动都大概可行,当然这中间也有很多地方要注意。数据分析处理有点像是定时任务,描述可能不太准确,就是通过分析用户实际数据来具体分析处理,如:每天2点,数据团队有多个数据模型针对不同的数据表(账户存款、理财等等)对用户前一天所有交易进行分析,计算积分然后累计前一天产生的积分。

关于积分获取方式,本次就讨论到这了。其实上面简单分析已经包含了很多系统设计的必要考虑条件了,而且很多都可以类比的,比如积分累计不就是类似购物产生订单,或是记录操作日志吗。有很多相似之处的,可以类比思考一下。

积分领取规则

从前面积分领取规则可以确定,积分领取涉及消息通知和过期。

消息通知肯定是成体系的,可参考其他消息类系统设计。过期可以参考下面的积分过期。

积分有效期

这个是本篇文章中我想要突出的重点。积分产生、积分兑换、积分过期还是需要好好思考一下的,如若不然,甚至不单单是产生系统bug,甚至会有业务bug,这是难以接受的。

既然上面需求说明了积分有效期是12个自然月,那么就可以考虑设计一张按月存储的表来存储积分。

如5.1-5.31的产生的积分存储为一条可用积分记录,过期时间为5.31 23:59:59,总积分值为最近12个月积分之和。

用户ID 累计积分 可用积分值 过期时间
1 300 300 5.31 23:59:59
1 500 500 6.30 23:59:59
1 800 700 7.31 23:59:59
1 500 400 8.31 23:59:59
1 300 200 9.30 23:59:59
1 300 100 10.31 23:59:59
1 500 500 11.30 23:59:59
1 600 600 12.31 23:59:59

可能有人在接到积分过期需求时就考虑到定时任务扫表的方式,虽然可能也是没问题的,但细节其实挺多的,这里就不做过多讨论了。

积分权益兑换

用户日常使用的积分,将优先使用即将过期的积分。针对于上面的月度积分表,在积分兑奖时从最早的未过期的月开始计算扣减积分。

设计方案

数据表

简单设计如下,月度积分关联多条积分明细记录,这样可以方便查询月度积分获取和消耗明细。

月度积分

  • 月度积分id
  • 用户id
  • 年月(参考2024-01之类的)
  • 累计积分
  • 可用积分
  • 过期时间

积分明细

  • 月度积分id
  • 明细id
  • 用户id
  • 积分值
  • 类型(如:消耗,获取等)
  • 操作
  • 时间

兑奖明细

  • 兑奖明细id
  • 用户id
  • 奖品消耗积分
  • 奖品名称
  • 时间

流程

积分获取

积分由用户操作生成,同时存储于月度积分表和积分明细表。

1708264217470

积分兑奖

1708264217472

其他

除了上面这些,还有

  • 总积分查询,最近12个月的月度积分-可用积分之和
  • 积分明细查询,倒序查询月度积分和月度积分明细
  • 积分兑奖明细,等,简单的就不多提了

参考代码

积分兑奖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.time.LocalDate;
import java.util.*;

class PointRecord {
private int points;
private LocalDate expirationDate;

// 构造方法、getters和setters省略

public boolean canConsume(int requiredPoints) {
return this.points >= requiredPoints;
}

public void consume(int requiredPoints) {
if (canConsume(requiredPoints)) {
this.points -= requiredPoints;
} else {
throw new IllegalArgumentException("Insufficient points to consume.");
}
}
}

public class PointService {
public void redeemReward(User user, int rewardPointCost) {
List<PointRecord> sortedRecords = getSortedUnexpiredPointRecords(user);

for (PointRecord record : sortedRecords) {
if (record.canConsume(rewardPointCost)) {
record.consume(rewardPointCost);
rewardPointCost = 0; // 已经满足消耗条件,跳出循环
break;
} else {
rewardPointCost -= record.getPoints();
record.setPoints(0); // 消耗完该记录所有积分
}
}

// 更新数据库中的积分明细表
updateDatabase(sortedRecords);

// 如果rewardPointCost仍然大于0,说明积分不足,需要在此处处理异常情况
if (rewardPointCost > 0) {
throw new InsufficientPointsException("Not enough valid points to redeem the reward.");
}
}

// 省略了获取用户未过期且已排序的积分记录列表的方法(getSortedUnexpiredPointRecords)以及更新数据库的方法(updateDatabase)
}

在这个示例中,PointRecord类代表单个积分记录,包含积分值和过期日期。PointService中的redeemReward方法首先获取用户的所有未过期且按过期时间排序的积分记录,然后遍历这些记录,优先扣除最早到期的积分。当所需兑换积分数量减为0时停止扣除。如果最后仍有剩余的兑换积分需求,则抛出积分不足的异常。

实际应用中,你需要根据数据库查询结果填充sortedRecords列表,并在扣除积分后更新数据库以反映新的积分状态。

小结

其实还有很多内容没有细究,这部分就留给认真看的你们了。哈哈哈😂

积分补偿、退货等场景,分布式事务如何考虑,留给你们了。

最后的最后,其实在系统设计之前,需求分析是重中之重,一定要讲需求看透,尤其是多考虑一些场景。

下面有一张关于积分过期和兑奖的场景草图,是我在思考时画的,不想多解释了,自己看吧😁

最后四个字:跳出时间。

1708264217473

写在最后

拙作艰辛,字句心血,望诸君垂青,多予支持,不胜感激。


个人博客:无奈何杨(wnhyang)

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview