功能介绍
用户加入群组之后,会在签到群组所设的签到地点进行签到和签退,并限制同一个设备只能签到一个用户,签到成功之后。会获取一定的限制在该群组使用的积分。该群组可以设置一些抽奖活动,用户可使用该群组内的积分来进行该群组的抽奖活动。
项目功能
前台的主要功能
功能项 | 功能说明 |
---|---|
基本的用户操作 | 用户邮箱注册、登陆、修改头像、修改用户地址、修改用户密码 |
群组操作 | 创建群组、用户加入群组、改变群组头像、获取群组成员、群组的解散 |
群组打卡 | 用户签到、签退,获取用户和群组成员的打卡记录,打卡记录的生成,请假 |
打卡配置 | 群组打卡地点的增加、删除,获取群组打卡位置,打卡ip限制 |
群组抽奖活动 | 抽奖活动的增加、删除、更新,获取群组的抽奖活动。用户抽奖 |
群组抽奖奖品 | 抽奖活动奖品的增加、删除、更新,获取活动的奖品列表 |
用户的抽奖记录 | 用户抽奖记录的生成、获取 |
后台的主要功能
管理员操作 | 管理员的登陆 |
---|---|
用户管理 | 用户列表的获取,用户的删除 |
群组管理 | 群组的删除 |
活动管理 | 活动列表的获取,活动的删除 |
用户订单管理 | 用户订单的获取、删除 |
打卡管理 | 获取群组和用户的打卡记录 |
项目实现
技术选型
技术 | 选型 |
---|---|
语言 | Go1.20 |
存储 | Redis、Mysql |
rpc框架 | Kitex |
http框架 | Hertz |
orm框架 | Gorm、Gen |
消息队列 | Rabbitmq |
日志框架 | Logrus、File-rotatelogs |
容器技术 | Docker |
服务注册与发现 | Etcd |
开发设计
1.JWT鉴权:
基于golang-jwt包实现了token的颁发与解析
token的自动续期
2.密码安全:
长度限制:为了保证密码的可记忆性以及安全性,限制密码长度为6~30
加密:使用md5加盐进行加密。
3.接口防刷:
基于Redis构建了一个通过检测用户IP地址限制统一IP多次刷赞的中间件,服务器可配置每分钟用户访问
点赞接口的上限量,当用户访问接口次数超过此上限时,将返回“操作频繁,请稍后重试”信息提醒。
4.Redis缓存:
对近期访问的信息进行缓存,对访问的不存在的数据也在redis当中缓存空值来防止缓存穿透。
对于数据更新操作,为了保证数据库与缓存的数据一致性,采用先更新数据库再更新缓存,然后延时双
删的策略
5.消息队列:
引入了消息队列来应对一些高并发场景下的并发安全问题
对服务之间进行解耦
对一些重要的存储失败的信息进行暂存再次尝试存储来避免重要数据的丢失
6.高并发设计
对于抽奖场景的高并发,先通过查缓存判断当前时间是否在活动时间内,活动奖品是否有剩余,用户的
积分是否够用。满足所有条件才会把这次抽奖请求放入到rabbitmq中。lottery服务处理完这次请求之后,
通过redis异步返回抽奖的结果。
高并发下如果生成用户抽奖记录失败,会把这次订单存到rabbitmq中,再次尝试生成订单,这样保证了
重要信息的不丢失。
对于发送邮件验证码,用户签到记录的生成这种可能需要高并发的场景,也采用了rabbitmq做中间缓存
7.Docker部署:
使用dokcer-compose整合项目依赖,快速部署所需要的依赖并根据init.sql文件建表。
8.OSS存储:
使用七牛云服务器进行OSS存储,来减少本地磁盘IO
架构设计
微服务设计
数据库设计
1.活动的奖品总数、群组的人数、奖品的数量、用户在群组的签到天数
2.每张表添加了deleted_at作软删除,并记录更新创建时间
3.索引:
1.查询时deleted_at常做条件使用,所有的deleted_at建立索引
2.对user表的email和deleted_at作联合唯一索引
3.对user_group表,对gid建立索引,对uid,gid,deleted_at建立联合唯一索引
4.对sign_month表,对gid建立索引,对uid,month,gid建立联合唯一索引
5.对sign_record表,对gid建立索引,对uid,gid,deleted_at建立联合唯一索引
6.对sign_group_pos表,对gid,name,longtitude,latitude,deleted_at建立联合唯一索引
7.对ask_leave表,对gid,uid,time,deleted_at建立联合唯一索引
8.对admin表,对name,password,deleted_at建立联合唯一索引
9.对activity表,对uid建立索引,对gid建立索引
10.对prize表,对aid建立索引
11.对user_order表,对uid,pid建立联合唯一索引
缓存设计
key | value | 相关操作 |
---|---|---|
user:{uid} | hash类型 | 存储用户的基本信息 |
{email}:{code} | string类型 | 存储用户注册时的验证码 |
group:{gid} | hash类型 | 存储群组的基本信息 |
sign:user:start:{gid} | set类型 | 存储群组中签到的用户的id |
sign:user:end:{gid} | set类型 | 存储群组中签退的用户的id |
sign:month:{gid}:{uid} | setbit类型 | 存储群组用户的月签到信息 |
sign:ip:{ip} | string类型 | 存储操作请求的ip地址和用户的的映射 |
sign:location:{gid} | geo类型 | 存储群组签到的地理位置 |
record:{uid} | hash类型 | 存储用户的签到记录 |
activity:{aid} | hash类型 | 存储活动的信息 |
prize:{pid} | hash类型 | 存储奖品的信息 |
prize:aid:{aid} | set类型 | 存储活动的所有奖品id |
order:user:{uid}:{offset}:{limit} | set类型 | 存储用户奖品单页的订单id列表 |
order:{oid} | hash类型 | 存储用户奖品单的信息 |
email:return:{email} | string类型 | 从rabbitmq读取email并发送邮寄后的异步返回处理结果的code |
sign:return:{uid}:{gid} | string类型 | 从rabbitmq读取签到请求信息后异步返回处理结果的code |
choose:return:{uid}:{gid} | string类型 | 从rabbitmq读取抽奖请求信息后异步返回处理结果的code |
ip:count:{ip} | string类型 | 用于存储ip地址在2分钟内的请求次数 |
token:{token} | string类型 | 存储初始token指向的真实token |
消息队列设计
代码目录
├── cmd
│ ├── api
│ │ └── biz
│ │ ├── handler
│ │ │ ├── api
│ │ │ │ ├── base
│ │ │ │ │ ├── admin.go
│ │ │ │ │ ├── group.go
│ │ │ │ │ └── user.go
│ │ │ │ ├── lottery
│ │ │ │ │ ├── activity.go
│ │ │ │ │ ├── choose.go
│ │ │ │ │ ├── prize.go
│ │ │ │ │ └── record.go
│ │ │ │ └── sign
│ │ │ │ ├── record.go
│ │ │ │ ├── sign.go
│ │ │ │ └── signPos.go
│ │ │ ├── common
│ │ │ │ └── handle_biz.go
│ │ │ └── ping.go
│ │ ├── middleware
│ │ │ ├── auth.go
│ │ │ └── ipLimit.go
│ │ ├── model
│ │ │ ├── api
│ │ │ │ └── api.go
│ │ │ ├── lottery
│ │ │ │ └── lottery.go
│ │ │ ├── sign
│ │ │ │ └── sign.go
│ │ │ └── user
│ │ │ └── base.go
│ │ └── router
│ │ ├── api
│ │ │ ├── api.go
│ │ │ └── middleware.go
│ │ └── register.go
│ ├── generate
│ │ └── main.go
│ ├── main
│ │ └── main.go
│ └── rpc
│ ├── base
│ │ └── base.go
│ ├── lottery
│ │ └── lottery.go
│ └── sign
│ └── sign.go
├── dao
│ ├── cache
│ │ ├── activity.go
│ │ ├── group.go
│ │ ├── handlerErr.go
│ │ ├── init.go
│ │ ├── ip.go
│ │ ├── order.go
│ │ ├── prize.go
│ │ ├── sign.go
│ │ └── user.go
│ └── db
│ ├── activity.go
│ ├── admin.go
│ ├── group.go
│ ├── init.go
│ ├── model
│ │ ├── activity.gen.go
│ │ ├── admin.gen.go
│ │ ├── ask_leave.gen.go
│ │ ├── prize.gen.go
│ │ ├── sign_group.gen.go
│ │ ├── sign_group_pos.gen.go
│ │ ├── sign_list.gen.go
│ │ ├── sign_month.gen.go
│ │ ├── sign_record.gen.go
│ │ ├── user.gen.go
│ │ ├── user_group.gen.go
│ │ └── user_order.gen.go
│ ├── order.go
│ ├── prize.go
│ ├── record.go
│ ├── sign.go
│ └── user.go
├── docker-compose.yml
├── go.mod
├── go.sum
├── idl
│ ├── api.thrift
│ ├── base.thrift
│ ├── lottery.thrift
│ └── sign.thrift
├── kitex_gen
│ ├── lottery
│ │ ├── k-consts.go
│ │ ├── k-lottery.go
│ │ ├── lottery.go
│ │ └── lotteryservice
│ │ ├── client.go
│ │ ├── invoker.go
│ │ ├── lotteryservice.go
│ │ └── server.go
│ ├── sign
│ │ ├── k-consts.go
│ │ ├── k-sign.go
│ │ ├── sign.go
│ │ └── signservice
│ │ ├── client.go
│ │ ├── invoker.go
│ │ ├── server.go
│ │ └── signservice.go
│ └── user
│ ├── base.go
│ ├── baseservice
│ │ ├── baseservice.go
│ │ ├── client.go
│ │ ├── invoker.go
│ │ └── server.go
│ ├── k-base.go
│ └── k-consts.go
├── pkg
│ ├── config
│ │ └── mysql
│ │ └── init.sql
│ ├── constants
│ │ └── const.go
│ ├── errmsg
│ │ └── error.go
│ ├── jwt
│ │ └── jwt.go
│ └── log
│ ├── log.go
│ └── logs
├── rabbitmq
│ ├── comm
│ │ └── comm.go
│ ├── consumer
│ │ ├── chooseConsumer.go
│ │ ├── emailConsumer.go
│ │ ├── init.go
│ │ ├── orderConsumer.go
│ │ ├── recordConsumer.go
│ │ ├── regularConsumer.go
│ │ └── signConsumer.go
│ ├── model
│ │ ├── choose.go
│ │ ├── order.go
│ │ ├── record.go
│ │ └── sign.go
│ └── producer
│ ├── chooseProducer.go
│ ├── emailProducer.go
│ ├── init.go
│ ├── orderProducer.go
│ ├── recordProducer.go
│ ├── regularProducer.go
│ └── signProducer.go
├── script
│ └── bootstrap.sh
├── service
│ ├── base
│ ├── pos.go
│ ├── record.go
│ └── sign.go
└── utils
├── email.go
└── rand.go
源码地址