go抽奖系统

分6个数据库表:

黑名单表(黑名单限制到期时间、id、ip地址、创建时间、修改时间)

不同编码的虚拟券表(编码、id、奖品id、创建时间、更新时间、状态0正常,1作废,2已发放)

奖品表(位置序号、奖品类型0虚拟币,1虚拟券,2实物小奖,3实物大奖,id,图片地址、剩余数量、奖品总数、中奖编码、发奖周期、发奖计划、奖品状态0正常1删除、奖品开始时间、结束时间、奖品名称)

获奖表(id、奖品id、奖品名称、奖品类型、抽奖编号、用户ip、用户id、用户名、状态0正常1删除2作弊)

用户表(地址、id、手机号、联系人、抽奖时间、黑名单到期时间)

用户每日抽奖情况表(id、日期、抽奖次数、用户id、创建时间、修改时间)

功能:

中奖记录管理,可以根据用户id或奖品id或全部显示,后台可以将用户的中奖纪录删除或标记作弊(修改状态位)

奖品管理:显示名称数量更新时间等信息,可以修改奖品库存、中奖概率等信息,可以删除奖品(修改位)

优惠券管理:增加优惠券、删除、恢复

用户管理:显示用户名、黑名单到期时间、手机地址、创建时间、更新时间,可以黑一周一年一月洗白

ip黑名单:显示ip、黑名单到期时间、更新时间等,可以黑一周一年一月洗白

显示奖品列表:

从缓存获得全部奖品步骤:

从缓存中获取,是条json数据,将json反序列化到一个map切片,map键为string值为空接口。

再对空的数据库对象赋值,返回过去。

将奖品数据更新到缓存:

将数据库对象传入,make一个map切片,对该切片用数据库对象赋值,然后再json序列化,设置键set进redis中。

显示中奖记录:

将所有奖品的id放到切片中传到dao层,查询中奖记录表中奖品的id是不是在此切片中。

哪些用了redis:

奖品信息(奖品池)、不同编码优惠券信息(用set存储)、今日抽奖次数信息(分段的hash)、用户ip黑名单(分段的hash)用了redis缓存

而且redis单进程单线程的特点达到原子性操作的要求,避免并发操作中线程安全性问题,数据库+分布式锁在并发能力方面将会受到极大的限制。如果redis和数据库的数据不一致,以数据库为准并更新redis,因为可能redis重启了

抽奖过程:

验证是否登录,从cookie中得到当前登录的用户

用户抽奖分布式锁定,避免一个用户并发多次抽奖(redis设置3秒到期,键不存在时设置)

验证用户今日抽奖次数,每日重置,整点归零,用hincrby记录抽奖的次数,将hash结构散列多段,提高执行效率

验证ip今日抽奖次数

验证ip黑名单

验证用户黑名单

获得抽奖编码 一个随机数

匹配奖品是否中奖并直接发放不限量奖品(虚拟币)

限量奖品发放,先从奖品池获得奖品数量判断是否可以发奖,可以的话更新缓存和数据库

不同编码优惠券发放

记录中奖纪录,若中大奖需要设置黑名单

难点:合理分配抽奖

设置奖品池,作为奖品库存的中间缓冲地带,避免一开始就把奖品发完,导致后面的时间没有奖品可以发放

发奖计划:让奖品数量合理分配到发奖周期内,每天内不同的时间段设置的概率也不一样。

精确到分钟,发奖周期内每天、每分钟发奖数量的概率相同,一天内不同时间点概率不同 。比如下午6/7/8/9的概率稍大些

如果没有发奖周期,全部填充到奖品池。当奖品数量发生变化、奖品周期发生变化、每5钟都要更新一次发奖计划,发奖计划的格式【时间:数量】时间是20190101的格式

填充奖品的服务:如果过了发奖计划的时间,就需要将这些奖品放到奖品池中,并更新发奖计划,每分钟执行一次

注意并发问题,有了二次补发的逻辑

同一个ip或用户防止得到多次大奖,添加了用户黑名单和ip黑名单

流程图



系统架构