会员积分系统设计

1,112 阅读3分钟

功能

  • 增加用户积分
  • 消耗用户积分
  • 查询用户积分
  • 到期积分清理
  • 查询用户的积分明细

积分分为

  • 有过期时间
  • 无过期时间的

用户可以通过做任务获得积分,也可以通过充值会员获得积分。通过充会员获得的积分就是有期限的。 对于前端需要提供一个查询用户积分的接口

表结构设计

用户积分表

iduidscore
123260
223470

积分明细表

CREATE TABLE `user_score_record` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `record_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '积分记录id',
  `uid` bigint(20) NOT NULL DEFAULT '0' COMMENT '用户id',
  `in_out` tinyint(4) NOT NULL DEFAULT '0' COMMENT '积分操作类型 1/添加, 2/减少',
  `deal_time` int(10) NOT NULL DEFAULT '0' COMMENT '交易时间:充值或消费时间',
  `event_id` varchar(128) NOT NULL DEFAULT '' COMMENT '事件ID,任务id、订单ID、签到ID、聊天msgID等',
  `expired_time` bigint(20) NOT NULL DEFAULT '0' COMMENT '积分过期时间:0/不限制时间,大于0是对应的过期时间',
  `score_code` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '积分编码',
  `score` int(10) NOT NULL DEFAULT '0' COMMENT '积分',
  `create_time` int(10) NOT NULL DEFAULT '0' COMMENT '赚取或消费创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_code_eventid` (`uid`,`score_code`,`event_id`),
  KEY `idx_record_id` (`record_id`),
  KEY `idx_channel_id` (`uid`,`deal_time`),
  KEY `idx_deal_time` (`deal_time`)
) ENGINE=InnoDB AUTO_INCREMENT=115 DEFAULT CHARSET=utf8mb4 COMMENT='积分明细表';

userUsable 表

CREATE TABLE `userUsableScore` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `uid` bigint(20) NOT NULL DEFAULT '0' COMMENT '用户id',
  `event_id` varchar(128) NOT NULL DEFAULT '' COMMENT '事件ID,任务id、订单ID、签到ID、聊天msgID等',
  `score_code` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '积分编码',
  `score` int(10) NOT NULL DEFAULT '0' COMMENT '积分',
  `score_usable` int(10) NOT NULL DEFAULT '0' COMMENT '可以用积分',
  `deal_time` int(10) NOT NULL DEFAULT '0' COMMENT '交易时间:充值',
  `expired_time` bigint(20) NOT NULL DEFAULT '0' COMMENT '积分过期时间',
  `deleted` int(10) NOT NULL DEFAULT '0' COMMENT '删除状态:0正常,大于0删除时间',
  `update_time` int(10) NOT NULL DEFAULT '0' COMMENT '修改时间',
  `create_time` int(10) NOT NULL DEFAULT '0' COMMENT '创建时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uniq_user_cuid` (`uid`,`score_code`,`event_id`,`deleted`),
  KEY `idx_usable_id` (`usable_id`),
  KEY `idx_deal_time` (`deal_time`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='可用积分表';

由于积分是有过期时间的属性。会抽象一个用户可用表来便于对积分的处理,方便控制表的大小 。usableScore它是明细表的聚合,无过期时间的积分会聚合为一条数据,且他的score_code=1001, 1001表示的是无过期时间的积分 有过期时间的会在这个表里有多条数据。

对外接口

GET /user/score 返回用户的积分余额和即将到期的积分,即将到期的积分可以自定义时间,比如1天,3天。 查询用户余额:查用户积分表,可以使用redis提高查询速度,设置超时时间。 查询快要到期的积分:查询明细表,未做缓存,指定读库,加了for update

增加积分

流程图

增加积分.drawio.png

消耗积分

for _, row := range UsableRows {
    if unallocScore > 0 {
       //当前记录够扣除
       if row.ScoreUsable-unallocScore >= 0 {
          row.ScoreUsable = row.ScoreUsable - unallocScore
          unallocScore = 0

          _, err := e.DaoUserUsableScore.Update(row)
          if err != nil {
             return false, score.ErrorDbUpdate
          }
          break
       }
       //当前记录不够扣除
       unallocScore = unallocScore - row.ScoreUsable
       row.ScoreUsable = 0
       _, err := e.DaoUserUsableScore.Update(row)
       if err != nil {
          return false, score.ErrorDbUpdate
       }
    }
}

积分到期清理

目前只有充值vip给的积分才有过期时间 积分提供了一个接口

func CleanByCodeEvent(req ScoreConsumeReq) error 

大致逻辑如下

row = getUsableScoreByCodeEventId(uid,req.ConsumeScoreCode.Int(), req.ConsumeEventId)
req.Score = tmpRow.ScoreUsable
req.IsConsumeScoreCode = true
consumeScore(req)

参考

juejin.cn/post/716400…