每日商店
在大世界中与流浪商人对话,点击对应的按钮即可进入商城购买道具或进入交易界面出售道具。购买的道具要求每天凌晨刷新,每个玩家有五次手动刷新。
需求分析
从需求描述可以分析出,需要三个接口:
- 获取商城:需要返回的结构体,主要有商城的道具,倒计时,刷新次数。
- 刷新商城:需要返回的结构体,主要有商城的道具,倒计时,刷新次数。
- 购买商城:需要返回的结构体,主要有购买的道具是否成功。
- 出售商城:需要返回的结构体,主要有出售的道具是否成功,获得得金币,已经放入的背包位置。
设计数据库
- 背包数据库:
type BagData struct {
Id int `json:"id"` //背包主键
Uid int64 `json:"uuid" bson:"uuid"` //用户ID
ItemId string `json:"iname" bson:"iname"` //物品名
Num int `json:"num" bson:"num"` //数量
Options string `json:"options" bson:"options"`
Durability float32 `json:"durability" bson:"durability"` //耐久度
F32 float32 `json:"f32" bson:"f32"` //float32传值用
TimeStamp int64 `json:"tm" bson:"tm"` //时间戳
Param int `json:"param" bson:"param"` //暂留字段
}
- 商城数据库:
type MallItemDetails struct {
Id int `json:"id"` //背包主键
Uid int64 `json:"uuid" bson:"uuid"` //用户ID
ItemId string `json:"iname" bson:"iname"` //物品名
Num int `json:"num"`
Oil int `json:"oil"` //金币
IsBuy bool `json:"isBuy" bson:"isBuy"` //是否购买
RefreshName int `json:"rn"` //刷新次数
RefreshTime int64 `json:"rt"` //刷新时间
}
功能逻辑分析
-
获取商城:
- 客户端请求获取商城的接口。
- 查询数据库找到用户商城信息。
- 如果没有则执行刷新物品信息存入数据库,同时返回给客户端。
- 如果有则执行判断是否今天有刷新,如果有刷新则返回给客户端,如果没有刷新则执行刷新物品信息存入数据库,同时返回给客户端。
-
刷新商城:
- 客户端请求刷新商城的接口。
- 查询数据库找到用户商城信息。
- 判断今天手动刷新还是自动刷新。
- 手动刷新去判断是否还有次数,自动刷新去判断当天是否刷新。
方法
1.获取商城
a. 计算当前时间到今天凌晨的时间.
func (slf *RealmSer) GetCountdownSecond() (countdown int64) {
now := time.Now() // 获取当前时间
addDate := now.AddDate(0, 0, 1)
today := time.Date(addDate.Year(), addDate.Month(), addDate.Day(), 0, 0, 0, 0, now.Location()) // 获取今天凌晨时间
timestamp := today.Unix() - now.Unix()// 计算当前时间到今天凌晨的时间戳
return timestamp
}
b. 获取公共的每日商店
func (slf *RealmSer) GetMallLimitedDetails() []MallLimitedDetails {
limited := slf.Csvhandle.MallLimited
var mallLimitedDetails []MallLimitedDetails
//具体代码省略
return mallLimitedDetails
}
c. 获取用户每日商店
func () DoGetMallLimited(ctx context.Context, roleId int64) () {
//倒计时
timeStamp = slf.GetCountdownSecond()
//用户用户数据库
mallLimited, err := slf.getRoleMallLimited(ctx, roleId)
if err != nil {
log.Error(ctx, "getRoleMallLimited|%s", err)
return nil, timeStamp, err
}
//如果商城没有,则给他加载
if mallLimited == nil {
limitedDetails := slf.GetMallLimitedDetails()
limitedMall := LimitedMall{
RoleId: roleId,
MallLimitedDetails: limitedDetails,
TimeStamp: time.Now().Unix(),
}
//写入数据库
err := slf.createRoleLimitedMall(ctx, limitedMall)
if err != nil {
log.Error(ctx, "createRoleLimitedMall MongoDB is err=%s", err)
return nil, timeStamp, err
}
return limitedDetails, timeStamp, nil
}
year, month, day := time.Now().Date()
// 获得自动刷新的记录
t := time.Unix(mallLimited.TimeStamp, 0)
y, m, d := t.Date()
//判断是否到了第二天还没有给他刷新
if y != year || m != month || d != day {
log.Debug(ctx, "y=%d,m=%d,d=%d", y, m, d)
limitedDetails := slf.GetMallLimitedDetails()
limitedMall := LimitedMall{
RoleId: roleId,
MallLimitedDetails: limitedDetails,
TimeStamp: time.Now().Unix(),
}
//把新生成的商城道具修改到数据库
err := slf.updateRoleLimitedMall(ctx, roleId, &limitedMall)
if err != nil {
log.Error(ctx, "updateRoleLimitedMall MongoDB is err=%s", err)
return nil, timeStamp, err
}
return limitedDetails, timeStamp, nil
}
return mallLimited.MallLimitedDetails, timeStamp, err
}
2.刷新商城
func () refreshMallItemRandom(ctx context.Context, roleId int64, isAuto bool) () {
//倒计时
getCountdown := slf.GetCountdownSecond()
//用户用户数据库
mallLimited, err := slf.getRoleMallLimited(ctx, roleId)
if err != nil {
log.Error(ctx, "getRoleMallLimited|%s", err)
return nil, timeStamp, err
}
//校验是否是自动刷新
if isAuto {
newUnix := time.Now().Unix()
currentTime := time.Now()
year, month, day := currentTime.Date()
time00 := time.Date(year, month, day, 0, 0, 0, 0, currentTime.Location())
noonTime00stampmin := time00.Unix() - 3
noonTime00stampmax := time00.Unix() + 3
if noonTime00stampmin <= newUnix && newUnix <= noonTime00stampmax {
limitedDetails := slf.GetMallLimitedDetails()
limitedMall := LimitedMall{
RoleId: roleId,
MallLimitedDetails: limitedDetails,
TimeStamp: time.Now().Unix(),
}
//把新生成的商城道具修改到数据库
err := slf.updateRoleLimitedMall(ctx, roleId, &limitedMall)
if err != nil {
log.Error(ctx, "updateRoleLimitedMall MongoDB is err=%s", err)
return nil, timeStamp, err
}
return limitedDetails, timeStamp, nil
}
//返回错误
return limitedDetails, timeStamp, myErr
} else {
refreshTimes := mallLimited.RefreshTimes
if refreshTimes <= 0 {
//刷新次数不够了
return limitedDetails, timeStamp, myErr
}
mallLimited.RefreshTimes-=1
limitedDetails := slf.GetMallLimitedDetails()
limitedMall := LimitedMall{
RoleId: roleId,
MallLimitedDetails: limitedDetails,
TimeStamp: time.Now().Unix(),
}
//把新生成的商城道具修改到数据库,同时刷新次数减一
err := slf.updateRoleLimitedMall(ctx, roleId, &limitedMall)
if err != nil {
log.Error(ctx, "updateRoleLimitedMall MongoDB is err=%s", err)
return nil, timeStamp, err
}
return limitedDetails, timeStamp, nil
}
return limitedDetails, timeStamp, err
}
存在问题
目前我感觉会存在几个问题,想听听大佬的见解,希望可以提升自己的业务能力。
- 这个获取商城信息,我用记录的时间戳去判断今天是否已经刷新,如果正好是凌晨我这个逻辑有问题吗?
- 在购买的接口里面我要去验证上次刷新的时间戳是今天吗?
- 我在自动刷新的判断里面,给了6秒的延迟,因为我把倒计时给客户端,他那边再向我请求会存在延迟,他可能会利用这个bug返回请求自动刷新。有没有什么好的解决方案,或者给多少秒的缓冲比较好。