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黑名单
流程图
系统架构