一、背景
用户在参与公司的促销活动,比如邀请用户下单支付,可以获得虚拟货币。它可以用于解锁学习课程。
商品在定价的时候,需支持虚拟货币,用户在购买该商品的时候,可使用虚拟货币进行支付。
二、目标
1、支撑公司的商城业务在营销活动过程中,所衍生出来的多种虚拟货币的发放和消费。
2、商品的价格除默认的人民币方式外,还需支持虚拟货币以及人民币加虚拟货币的组合方式。
三、范围
1、积分按日、按月和按年的维度统计,暂时不实现。
2、不做历史发放的积分回收:长期未使用,在到了过期时间,不做回收。
3、由于订单的退货退款等原因,不对已发放的积分进行回收。
四、数模设计

1、积分表points
用于抽象各种虚拟货币,并通过类别来区分不同的账户体系。
包括主键ID、积分类别type、积分名称name、创建时间、更新时间、创建人员、更新人员、备注
` CREATE TABLE `points` (`
``id` bigint(``20``) NOT NULL AUTO_INCREMENT COMMENT ``'主键ID'``,`
``type` varchar(``32``) NOT NULL COMMENT ``'积分类别'``,`
``name` varchar(``128``) NOT NULL COMMENT ``'积分名称'``,`
``created_date` datetime DEFAULT NULL COMMENT ``'创建时间'``,`
``modified_date` datetime DEFAULT NULL COMMENT ``'更新时间'``,`
``created_by` varchar(``64``) DEFAULT NULL COMMENT ``'创建人员'``,`
``modified_by` varchar(``64``) DEFAULT NULL COMMENT ``'更新人员'``,`
``remark` varchar(``128``) DEFAULT NULL COMMENT ``'备注'``,`
`PRIMARY KEY (`id`) USING BTREE,`
`UNIQUE KEY `uk_type` (`type`)`
`) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=``'积分表'``;`
2、积分渠道表points_channel
通过有效期的时间区间,控制积分的发放、消耗和可能产生的风险。
包括主键ID、渠道编号code、渠道名称name、积分类别(渠道和积分的关联关系)、奖励的积分数reward_points、有效期起始时间、有效期截止时间、创建时间、更新时间、创建人员、更新人员、备注
` CREATE TABLE `points_channel` (`
``id` bigint(``20``) NOT NULL AUTO_INCREMENT COMMENT ``'主键ID'``,`
``code` varchar(``32``) NOT NULL COMMENT ``'渠道编码'``,`
``name` varchar(``128``) NOT NULL COMMENT ``'渠道名称'``,`
``points_type` varchar(``32``) NOT NULL COMMENT ``'积分类别'``,`
``reward_points` ``int``(``10``) DEFAULT ``'0'` `COMMENT ``'奖励的积分数'``,`
``begin_date` datetime DEFAULT NULL COMMENT ``'起始时间'``,`
``end_date` datetime DEFAULT NULL COMMENT ``'截止时间'``, `
``created_date` datetime DEFAULT NULL COMMENT ``'创建时间'``,`
``modified_date` datetime DEFAULT NULL COMMENT ``'更新时间'``,`
``created_by` varchar(``64``) DEFAULT NULL COMMENT ``'创建人员'``,`
``modified_by` varchar(``64``) DEFAULT NULL COMMENT ``'更新人员'``,`
``remark` varchar(``128``) DEFAULT NULL COMMENT ``'备注'``,`
`PRIMARY KEY (`id`) USING BTREE,`
`UNIQUE KEY `uk_code_points_type` (`code`,`points_type`)`
`) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=``'积分渠道表,一类积分对应多个渠道'``;`
3、积分账户表points_account
包括主键ID、用户ID、积分类别points_type、可用积分数available_points、被锁定积分数lock_points、创建时间、更新时间
` CREATE TABLE `points_account` (`
``id` bigint(``20``) NOT NULL AUTO_INCREMENT COMMENT ``'主键ID'``,`
``user_id` bigint(``20``) NOT NULL COMMENT ``'用户ID'``,`
``points_type` varchar(``32``) NOT NULL COMMENT ``'积分类别'``,`
``available_points` ``int``(``10``) DEFAULT ``'0'` `COMMENT ``'可用的积分数'``,`
``lock_points` ``int``(``10``) DEFAULT ``'0'` `COMMENT ``'锁定的积分数'``,`
``created_date` datetime DEFAULT NULL COMMENT ``'创建时间'``,`
``modified_date` datetime DEFAULT NULL COMMENT ``'更新时间'``,`
``version` bigint(``20``) DEFAULT ``'0'` `COMMENT ``'版本号'``,`
`PRIMARY KEY (`id`) USING BTREE,`
`UNIQUE KEY `uk_user_id_points_type` (`user_id`,`points_type`)`
`) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=``'积分账户表'``;`
4、积分账户的流水表points_account_flow
包括主键ID、积分类别、积分数、类型(增/减)、用户ID、积分渠道编号、积分渠道名称、orderNo、创建时间、更新时间
` CREATE TABLE `points_account_flow` (`
``id` bigint(``20``) NOT NULL AUTO_INCREMENT COMMENT ``'主键ID'``,`
``type` smallint(``1``) DEFAULT ``'0'` `COMMENT ``'类型:0-增加;1-减少'``,`
``user_id` bigint(``20``) NOT NULL COMMENT ``'用户ID'``,`
``points_type` varchar(``32``) NOT NULL COMMENT ``'积分类别'``,`
``points` ``int``(``10``) DEFAULT ``'0'` `COMMENT ``'积分数'``,`
``channel_code` varchar(``32``) DEFAULT NULL COMMENT ``'渠道编码'``,`
``channel_name` varchar(``128``) DEFAULT NULL COMMENT ``'渠道名称'``,`
``order_no` varchar(``32``) DEFAULT NULL COMMENT ``'订单号'``,`
``created_date` datetime DEFAULT NULL COMMENT ``'创建时间'``,`
``modified_date` datetime DEFAULT NULL COMMENT ``'更新时间'``,`
``created_by` varchar(``64``) DEFAULT NULL COMMENT ``'创建人员'``,`
``modified_by` varchar(``64``) DEFAULT NULL COMMENT ``'更新人员'``,`
``remark` varchar(``128``) DEFAULT NULL COMMENT ``'备注'``,`
`PRIMARY KEY (`id`) USING BTREE`
`) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=``'积分账户的流水表'``;`
5、积分使用表points_use_record
用于锁定、回退、消费等操作的状态变更。
包括主键ID、status、orderNo、用户ID、积分类别、积分数、创建时间、更新时间
` CREATE TABLE `points_use_record` (`
``id` bigint(``20``) NOT NULL AUTO_INCREMENT COMMENT ``'主键ID'``,`
``order_no` varchar(``32``) NOT NULL COMMENT ``'订单号'``,`
``status` smallint(``1``) DEFAULT ``'0'` `COMMENT ``'状态:0-锁定;1-解锁;2-完成'``,`
``user_id` bigint(``20``) NOT NULL COMMENT ``'用户ID'``,`
``points_type` varchar(``32``) NOT NULL COMMENT ``'积分类别'``,`
``points` ``int``(``10``) NOT NULL COMMENT ``'积分数'``,`
``created_date` datetime DEFAULT NULL COMMENT ``'创建时间'``,`
``modified_date` datetime DEFAULT NULL COMMENT ``'更新时间'``,`
``remark` varchar(``128``) DEFAULT NULL COMMENT ``'备注'``,`
`PRIMARY KEY (`id`) USING BTREE,`
`UNIQUE KEY `uk_order_no` (`order_no`)`
`) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=``'积分使用记录表'``;`
6、积分订单
记录每个订单的已使用数、已结算和可结算等。这里会涉及比较多的更新,所以增加了版本号字段。
CREATE TABLE `points_order` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`order_no` varchar(32) NOT NULL COMMENT '订单号',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`school_id` int(10) NOT NULL COMMENT '学校ID',
`points_type` varchar(32) NOT NULL COMMENT '积分类别',
`points` int(10) DEFAULT 0 COMMENT '积分数',
`used_points` int(10) DEFAULT 0 COMMENT '已使用积分数',
`available_points` int(10) DEFAULT 0 COMMENT '可用积分数',
`refunded_points` int(10) DEFAULT 0 COMMENT '已退积分数',
`settled_points` int(10) DEFAULT 0 COMMENT '已结算积分数',
`available_settle_points` int(10) DEFAULT 0 COMMENT '可结算积分数',
`created_date` datetime DEFAULT NULL COMMENT '创建时间',
`modified_date` datetime DEFAULT NULL COMMENT '更新时间',
`version` bigint(20) DEFAULT 0 COMMENT '版本号',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `uk_order_no` (`order_no`)
) ENGINE=InnoDB AUTO_INCREMENT=58 DEFAULT CHARSET=utf8mb4 COMMENT='积分结算表:积分数=已使用积分数+可用积分数+已退积分数。';
五、接口设计
分为管理端和面向业务端、积分订单。

1、管理端
1.1、分页查询用户积分账户列表


1.2、分页查询账户的流水记录


1.3、手动发放积分


2、面向业务端
2.1、查询我的积分账户


2.2、购买积分
用户获得积分的方式,除了上面的后台手动发放外, 还可以购买积分。既然是购买所得,入参就必须包含购买的订单编号orderNo。


2.3、发放积分
- 用户获得积分的第三种方式。
用户参加活动、做任务等奖励机制,业务方调用本接口给用户发放相应的积分。token: 防止重复调用, 每次调用都不相等。发放渠道channelCode和积分类别必填,业务方不传入奖励积分数,在积分系统中配置,查询得到应发放的积分值。


2.4、消费积分
当商品的付款方式是积分时,用户可以抵扣已有的积分来付款订单。


2.5、回退积分
用户可以使用积分对商品进行付款,当用户对订单取消或退款后,我们需要把扣除了的积分返还给用户。这是消费积分的逆向过程。所以它也必须提供付款时的订单编号。同时,它为了支持部分回退积分的场景,接口的入参增加了积分数points。


3、积分订单
这里是针对购买积分的订单而已, 不是用户的积分本身了。 既然是购买,就涉及到退款和结算,结算是以已使用为准。
3.1、分页查询积分订单列表


3.2、积分订单退款
规定退款所有,不支持部分退。

3.3、积分订单结算
支持部分结算,所以会指定结算哪个订单的多少积分。

六、总结
- 接口大多增加了token字段,用以防重。
- 数据库的更新操作,基于版本号的乐观锁实现。
- 在设计接口的时候,基于单一原则,不使用发放类别这样的字段来兼容,而是拆分为多个接口。(用户获得积分的三种方式:后台手动发放、购买和奖励发放。他们的必传参数不一,后台手动发放必须有操作人,也可以是验证码,总之需要对这种方式增加必要的验证,提高接口的安全性;购买则必须有购买的订单信息,积分值由订单系统负责;奖励发放则需要渠道编码,至于发放多少积分,当然由积分系统控制了)
- 为了支持积分的抽象性,每个接口基本都传入了积分类别。抽象的积分可以是金币、不同系统的积分、全平台的积分、使用权益、虚拟账户等等。但是,他们又都是围绕上面的那些领域来流转。
网友评论