diff --git a/app/user_center/model/rank_and_score.go b/app/user_center/model/rank_and_score.go index 9374f78..543d0d3 100644 --- a/app/user_center/model/rank_and_score.go +++ b/app/user_center/model/rank_and_score.go @@ -29,6 +29,14 @@ func AllScoreType() []ScoreType { return result } +func AllRankType() []pbVars.RankType { + result := make([]pbVars.RankType, 0, len(rankAndScoreMap)) + for rankType, _ := range rankAndScoreMap { + result = append(result, rankType) + } + return result +} + func addRankScore(rankType pbVars.RankType, scoreType ScoreType) { rankAndScoreMap[rankType] = scoreType scoreAndRankMap[scoreType] = rankType diff --git a/app/user_center/model/rank_pvp_model.go b/app/user_center/model/rank_pvp_model.go index 19334ff..fe67b26 100644 --- a/app/user_center/model/rank_pvp_model.go +++ b/app/user_center/model/rank_pvp_model.go @@ -9,21 +9,6 @@ import ( var _ RankPvpModel = (*customRankPvpModel)(nil) -const ( - RankTypeDamage = iota + 1 - RankTypeDeDamage - RankTypeGeneral - RankTypeDeGeneral - RankTypeKillUnit - RankTypeDeKillUnit - RankTypeKillPlayer - RankTypeDeKillPlayer - RankTypeWin - RankTypeLost - RankTypeFirstBlood - RankTypeDeFirstBlood -) - const MaxRankN = 50 type ( diff --git a/app/user_center/model/statistics_pvp_model.go b/app/user_center/model/statistics_pvp_model.go index 9eb43c3..07eb607 100644 --- a/app/user_center/model/statistics_pvp_model.go +++ b/app/user_center/model/statistics_pvp_model.go @@ -37,6 +37,8 @@ type ( FindGreaterByScore(ctx context.Context, score int64, scoreType ScoreType, limit int) ([]UserAndScore, error) // FindScoreByType 根据类型获取用户分数 FindScoreByType(ctx context.Context, tx *gorm.DB, userId int64, scoreType []ScoreType) (map[string]int64, error) + // CleanByType 清空某字段 + CleanByType(ctx context.Context, tx *gorm.DB, scoreType ScoreType) error } UserAndScore struct { @@ -169,3 +171,10 @@ func (m *customStatisticsPvpModel) FindScoreByType(ctx context.Context, tx *gorm return result, nil } + +func (m *customStatisticsPvpModel) CleanByType(ctx context.Context, tx *gorm.DB, scoreType ScoreType) error { + db := gormx.WithTx(ctx, m.DB, tx).Session(&gorm.Session{AllowGlobalUpdate: true}) + result := db.Table(m.table). + Update(string(scoreType), 0) + return gormx.WrapUpdateErr(result.Error, result.RowsAffected) +} diff --git a/app/user_center/model/user_coin_model.go b/app/user_center/model/user_coin_model.go index 86ec935..afc111a 100644 --- a/app/user_center/model/user_coin_model.go +++ b/app/user_center/model/user_coin_model.go @@ -5,7 +5,6 @@ import ( "git.noahlan.cn/northlan/ntools-go/gorm-zero/gormx" "github.com/pkg/errors" "gorm.io/gorm" - "gorm.io/plugin/optimisticlock" "live-service/common/nerr" ) @@ -17,7 +16,7 @@ type ( UserCoinModel interface { userCoinModel // ChangeCoin 用户弹币变动 - ChangeCoin(ctx context.Context, tx *gorm.DB, userId int64, change int64) (int64, error) + ChangeCoin(ctx context.Context, tx *gorm.DB, userId int64, change int64, ignoreNotEnough bool) (int64, error) } customUserCoinModel struct { @@ -34,15 +33,15 @@ func NewUserCoinModel(conn *gorm.DB) UserCoinModel { func (m *customUserCoinModel) updateCoin(ctx context.Context, tx *gorm.DB, coin *UserCoin) error { if coin.Coin < 0 { - return errors.New("无法将弹币更新至负数") + coin.Coin = 0 } db := gormx.WithTx(ctx, m.DB, tx) - result := db.Model(&coin).Updates(&UserCoin{Coin: coin.Coin, Version: optimisticlock.Version{Int64: 1}}) + result := db.Model(&coin).Update("coin", coin.Coin) return gormx.WrapUpdateErr(result.Error, result.RowsAffected) } -func (m *customUserCoinModel) ChangeCoin(ctx context.Context, tx *gorm.DB, userId int64, change int64) (int64, error) { +func (m *customUserCoinModel) ChangeCoin(ctx context.Context, tx *gorm.DB, userId int64, change int64, ignoreNotEnough bool) (int64, error) { resp := change err := gormx.WithRetry(VersionRetryCount, func() error { return m.Transact(tx, func(tx *gorm.DB) error { @@ -50,7 +49,11 @@ func (m *customUserCoinModel) ChangeCoin(ctx context.Context, tx *gorm.DB, userI if err != nil { if errors.Is(err, ErrNotFound) { if change < 0 { - return nerr.NewError(nerr.UserCoinNotEnoughErr, "用户弹币不足") + if ignoreNotEnough { + change = 0 + } else { + return nerr.NewError(nerr.UserCoinNotEnoughErr, "用户弹币不足") + } } // 用户积分记录不存在,进行插入 if err = m.Insert(ctx, tx, &UserCoin{ @@ -65,7 +68,11 @@ func (m *customUserCoinModel) ChangeCoin(ctx context.Context, tx *gorm.DB, userI } } if data.Coin+change < 0 { - return nerr.NewError(nerr.UserCoinNotEnoughErr, "用户弹币不足") + if ignoreNotEnough { + data.Coin = 0 + } else { + return nerr.NewError(nerr.UserCoinNotEnoughErr, "用户弹币不足") + } } data.Coin += change if err = m.updateCoin(ctx, tx, data); err != nil { diff --git a/app/user_center/model/user_elite_model.go b/app/user_center/model/user_elite_model.go index 0c52f8e..1184ca7 100644 --- a/app/user_center/model/user_elite_model.go +++ b/app/user_center/model/user_elite_model.go @@ -19,11 +19,7 @@ type ( // and implement the added methods in customUserEliteModel. UserEliteModel interface { userEliteModel - // FindMaxSort 寻找当前用户精英单位的最大Sort 最小值2 - FindMaxSort(ctx context.Context, tx *gorm.DB, userId int64) int64 - // FindOneByUserIdSort 通过用户和排序号找寻精英单位 - FindOneByUserIdSort(ctx context.Context, tx *gorm.DB, userId int64, sort int64) (*UserElite, error) - // FindByUserId 查找用户所有列表,按sort升序排列 + // FindByUserId 查找用户所有列表 FindByUserId(ctx context.Context, tx *gorm.DB, userId int64) ([]UserElite, error) // Addon 添加新的或延长时间 Addon(ctx context.Context, tx *gorm.DB, userId, eliteId int64, duration time.Duration, forever bool) error @@ -41,35 +37,10 @@ func NewUserEliteModel(conn *gorm.DB) UserEliteModel { } } -func (m *customUserEliteModel) FindMaxSort(ctx context.Context, tx *gorm.DB, userId int64) int64 { - db := gormx.WithTx(ctx, m.DB, tx) - var resp int64 = 1 - - db.Table(m.table). - Select("MAX(sort)"). - Where("user_id = ?", userId). - Where("end_time > ?", now.BeginningOfDay()).Take(&resp) - - return resp -} - -func (m *customUserEliteModel) FindOneByUserIdSort(ctx context.Context, tx *gorm.DB, userId int64, sort int64) (*UserElite, error) { - db := gormx.WithTx(ctx, m.DB, tx) - var resp UserElite - err := db.Model(&UserElite{}). - Where("user_id = ? and sort = ?", userId, sort).Take(&resp).Error - err = gormx.WrapSelectErr(err) - if err != nil { - return nil, err - } - return &resp, nil -} - func (m *customUserEliteModel) FindByUserId(ctx context.Context, tx *gorm.DB, userId int64) ([]UserElite, error) { var resp []UserElite err := gormx.WithTx(ctx, m.DB, tx).Table(m.table). - Where("user_id = ?", userId). - Order("sort asc").Find(&resp).Error + Where("user_id = ?", userId).Find(&resp).Error err = gormx.WrapSelectErr(err) if err != nil { return nil, err @@ -88,13 +59,10 @@ func (m *customUserEliteModel) Addon(ctx context.Context, tx *gorm.DB, userId, e today := now.BeginningOfDay() if userElite == nil { - // sort - maxSort := m.FindMaxSort(ctx, tx, userId) err = m.Insert(ctx, tx, &UserElite{ Id: uuid.NextId(), UserId: userId, EliteId: eliteId, - Sort: maxSort + 1, Forever: BitBool(forever), StartTime: today, EndTime: today.Add(duration), @@ -117,7 +85,6 @@ func (m *customUserEliteModel) Addon(ctx context.Context, tx *gorm.DB, userId, e Id: userElite.Id, UserId: userId, EliteId: eliteId, - Sort: userElite.Sort, Forever: BitBool(forever), StartTime: today, EndTime: today.Add(duration), diff --git a/app/user_center/model/user_elite_model_gen.go b/app/user_center/model/user_elite_model_gen.go index 892c3ea..6ac0f76 100644 --- a/app/user_center/model/user_elite_model_gen.go +++ b/app/user_center/model/user_elite_model_gen.go @@ -39,7 +39,6 @@ type ( Id int64 `gorm:"column:id;primaryKey"` // 主键ID UserId int64 `gorm:"column:user_id"` // 用户ID EliteId int64 `gorm:"column:elite_id"` // 精英单位ID - Sort int64 `gorm:"column:sort"` // 排序号 Forever BitBool `gorm:"forever"` // 永久 StartTime time.Time `gorm:"column:start_time;default:null"` // 开始时间 EndTime time.Time `gorm:"column:end_time;default:null"` // 结束时间 diff --git a/app/user_center/model/user_integral_model.go b/app/user_center/model/user_integral_model.go index 13d6790..e012b8c 100644 --- a/app/user_center/model/user_integral_model.go +++ b/app/user_center/model/user_integral_model.go @@ -5,7 +5,6 @@ import ( "git.noahlan.cn/northlan/ntools-go/gorm-zero/gormx" "github.com/pkg/errors" "gorm.io/gorm" - "gorm.io/plugin/optimisticlock" "live-service/common/nerr" ) @@ -18,7 +17,7 @@ type ( userIntegralModel UpdateIntegral(ctx context.Context, tx *gorm.DB, integral *UserIntegral) error // ChangeIntegral 用户积分变动 - ChangeIntegral(ctx context.Context, tx *gorm.DB, userId int64, change int64) (int64, error) + ChangeIntegral(ctx context.Context, tx *gorm.DB, userId int64, change int64, ignoreNotEnough bool) (int64, error) } customUserIntegralModel struct { @@ -35,15 +34,15 @@ func NewUserIntegralModel(conn *gorm.DB) UserIntegralModel { func (m *customUserIntegralModel) UpdateIntegral(ctx context.Context, tx *gorm.DB, integral *UserIntegral) error { if integral.Integral < 0 { - return errors.New("无法将积分更新至负数") + integral.Integral = 0 } db := gormx.WithTx(ctx, m.DB, tx) - result := db.Model(&integral).Updates(&UserIntegral{Integral: integral.Integral, Version: optimisticlock.Version{Int64: 1}}) + result := db.Model(&integral).Update("integral", integral.Integral) return gormx.WrapUpdateErr(result.Error, result.RowsAffected) } -func (m *customUserIntegralModel) ChangeIntegral(ctx context.Context, tx *gorm.DB, userId int64, change int64) (int64, error) { +func (m *customUserIntegralModel) ChangeIntegral(ctx context.Context, tx *gorm.DB, userId int64, change int64, ignoreNotEnough bool) (int64, error) { resp := change err := gormx.WithRetry(VersionRetryCount, func() error { return m.Transact(tx, func(tx *gorm.DB) error { @@ -51,7 +50,11 @@ func (m *customUserIntegralModel) ChangeIntegral(ctx context.Context, tx *gorm.D if err != nil { if errors.Is(err, ErrNotFound) { if change < 0 { - return nerr.NewError(nerr.UserIntegralNotEnoughError, "用户积分不足") + if ignoreNotEnough { + change = 0 + } else { + return nerr.NewError(nerr.UserIntegralNotEnoughError, "用户积分不足") + } } // 用户积分记录不存在,进行插入 if err = m.Insert(ctx, tx, &UserIntegral{ @@ -66,7 +69,11 @@ func (m *customUserIntegralModel) ChangeIntegral(ctx context.Context, tx *gorm.D } } if data.Integral+change < 0 { - return nerr.NewError(nerr.UserIntegralNotEnoughError, "用户积分不足") + if ignoreNotEnough { + data.Integral = 0 + } else { + return nerr.NewError(nerr.UserIntegralNotEnoughError, "用户积分不足") + } } data.Integral += change if err = m.UpdateIntegral(ctx, tx, data); err != nil { diff --git a/app/user_center/model/user_title_model.go b/app/user_center/model/user_title_model.go index f9b7dab..17bdd84 100644 --- a/app/user_center/model/user_title_model.go +++ b/app/user_center/model/user_title_model.go @@ -49,12 +49,11 @@ func NewUserTitleModel(conn *gorm.DB) UserTitleModel { func (m *customUserTitleModel) FindMaxSort(ctx context.Context, tx *gorm.DB, userId int64) int64 { db := gormx.WithTx(ctx, m.DB, tx) - var resp int64 = 1 + var resp int64 = 0 db.Table(m.table). Select("MAX(sort)"). - Where("user_id = ?", userId). - Where("end_time > ?", now.BeginningOfDay()).Take(&resp) + Where("user_id = ?", userId).Take(&resp) return resp } diff --git a/app/user_center/rpc/etc/user_center-dev.yaml b/app/user_center/rpc/etc/user_center-dev.yaml index cd05bde..09d1edd 100644 --- a/app/user_center/rpc/etc/user_center-dev.yaml +++ b/app/user_center/rpc/etc/user_center-dev.yaml @@ -10,10 +10,11 @@ Etcd: NonBlock: true Log: Mode: console + Path: logs/live-service KeepDays: 7 Level: info DB: - DataSource: root:root@tcp(127.0.0.1:3306)/dmgame?charset=utf8mb4&loc=Asia%2FShanghai&parseTime=true + DataSource: root:root@tcp(127.0.0.1:3306)/dmgame-dev?charset=utf8mb4&loc=Asia%2FShanghai&parseTime=true Kafka: UserCoinNotify: Addr: [ "127.0.0.1:9093" ] @@ -41,6 +42,41 @@ Rank: Cron: Update: "@every 10s" # 10s一次 Persistence: "@every 10m" # 10min一次 + Submit: + # 未列举的榜 + 0: + Best: [30,10,8,6,5,5] + Consolation: 1 + # 名将 + 3: + TitleId: 1000 + Best: [50,30,20,15,10,8] + Consolation: 1 + # 小兵终结者 + 5: + TitleId: 1001 + Best: [50,30,20,15,10,8] + Consolation: 1 + # 拆迁办 + 7: + TitleId: 1002 + Best: [60,40,25.6,18,10,8] + Consolation: 1 + # 获胜榜 + 9: + TitleId: 1003 + Best: [45.6,23.4,15,10,8,6] + Consolation: 1 + # 一血 + 11: + TitleId: 1004 + Best: [66.6,45.6,28,18,10,6] + Consolation: 2 + # 怒送一血 + 12: + TitleId: 1005 + Best: [66.6,45.6,28,18,10,6] + Consolation: 2 GiftCollector: Enabled: false Platforms: [ "bilibili" ] @@ -62,19 +98,19 @@ Coin: FreeToCoin: bilibili: 0.01 # 价值1毛 Elite: - LiveDict: { "1": "1001", "2": "1002" } - Default: - Id: 0 - Sort: 1 + LiveDict: { "1": "0", "2": "1001", "3": "1002" } + DefaultId: 0 Items: - Id: 1001 - PriceDay: 100 - PriceForever: 10000 + Sort: 2 + PriceDay: 80 + PriceForever: 26660 - Id: 1002 - PriceDay: 100 - PriceForever: 10000 + Sort: 3 + PriceDay: 60 + PriceForever: 18888 Title: - LiveDict: { "1": "3" } + LiveDict: { "1": "2000" } Items: - Id: 99999 Name: 无上意志 @@ -86,12 +122,19 @@ Title: Name: 废物舰长 Type: custom - Id: 3 - Name: 无用之人 - Type: default - PriceDay: 50 - PriceForever: 100000 + Name: Joe王 + Type: custom + - Id: 4 + Name: 我是弟弟 + Type: custom + - Id: 5 + Name: 萌新导师 + Type: custom + - Id: 6 + Name: 群萌新 + Type: custom - Id: 1000 - Name: 三军统帅 # 名将榜首 + Name: 军神 # 名将榜首 Type: rank RankType: 3 - Id: 1001 @@ -114,6 +157,11 @@ Title: Name: 求放过 # 被一血榜首 Type: rank RankType: 12 + - Id: 2000 + Name: 无用之人 + Type: default + PriceDay: 50 + PriceForever: 100000 GiftPackMap: - PackType: starter PackName: 新手礼包 diff --git a/app/user_center/rpc/internal/common/coin_manager/manager.go b/app/user_center/rpc/internal/common/coin_manager/manager.go index ab87b71..5e6fe23 100644 --- a/app/user_center/rpc/internal/common/coin_manager/manager.go +++ b/app/user_center/rpc/internal/common/coin_manager/manager.go @@ -63,11 +63,11 @@ func (m *Manager) TransferCoin(ctx context.Context, req *pb.TransferUserCoinReq) if req.TargetUserId == 0 { return nerr.NewError(nerr.RequestParamError, "目标用户ID为空") } - coin, err := m.userCoinModel.ChangeCoin(ctx, tx, req.UserId, -req.Transfer) + coin, err := m.userCoinModel.ChangeCoin(ctx, tx, req.UserId, -req.Transfer, false) if err != nil { return nerr.NewWithErr(err) } - targetCoin, err := m.userCoinModel.ChangeCoin(ctx, tx, req.TargetUserId, req.Transfer) + targetCoin, err := m.userCoinModel.ChangeCoin(ctx, tx, req.TargetUserId, req.Transfer, false) if err != nil { return nerr.NewWithErr(err) } @@ -98,7 +98,7 @@ func (m *Manager) TransferCoin(ctx context.Context, req *pb.TransferUserCoinReq) } func (m *Manager) ChangeCoin(ctx context.Context, tx *gorm.DB, req *ChangeCoinReq, reason pbVars.UserCoinChangedReason) (*ChangeCoinResp, error) { - coin, err := m.userCoinModel.ChangeCoin(ctx, tx, req.UserId, req.Change) + coin, err := m.userCoinModel.ChangeCoin(ctx, tx, req.UserId, req.Change, false) if err != nil { return nil, err } @@ -122,7 +122,7 @@ func (m *Manager) ChangeCoinBatch(ctx context.Context, req []ChangeCoinReq, reas resp := make(map[int64]ChangeCoinResp) if err := m.userCoinModel.TransactCtx(ctx, nil, func(tx *gorm.DB) error { for _, item := range req { - coin, err := m.userCoinModel.ChangeCoin(ctx, tx, item.UserId, item.Change) + coin, err := m.userCoinModel.ChangeCoin(ctx, tx, item.UserId, item.Change, false) if err != nil { return err } diff --git a/app/user_center/rpc/internal/config/config_game.go b/app/user_center/rpc/internal/config/config_game.go index 6660f3a..90e51e0 100644 --- a/app/user_center/rpc/internal/config/config_game.go +++ b/app/user_center/rpc/internal/config/config_game.go @@ -4,16 +4,14 @@ import "github.com/pkg/errors" type ( Elite struct { - LiveDict map[int32]int64 // 直播间序号对应ID - Default struct { - Id int64 - Sort int32 - } // 默认 - Items []EliteItem + LiveDict map[int32]int64 // 直播间序号对应ID + DefaultId int64 // 默认单位ID + Items []EliteItem } // EliteItem 精英单位 EliteItem struct { Id int64 // 单位ID + Sort int32 // 排序号 PriceDay int64 // 每天价格(弹币) PriceForever int64 // 永久价格 } @@ -52,6 +50,12 @@ type ( } } } + // RankSubmit 排行榜结算配置 + RankSubmit struct { + TitleId int64 // 称号奖励 + Best []float64 // 弹币奖励(前n) 单位: 元 + Consolation float64 // 安慰奖(后max-n) 单位: 元 + } GameConfig struct { UserRetriever struct { Enabled bool // 是否开启 @@ -74,6 +78,7 @@ type ( Update string // 更新榜单 Persistence string // 持久化 } + Submit map[int32]RankSubmit // 排行榜结算 } GiftCollector struct { Enabled bool // 是否开启 diff --git a/app/user_center/rpc/internal/logic/coin/change_coin_logic_test.go b/app/user_center/rpc/internal/logic/coin/change_coin_logic_test.go new file mode 100644 index 0000000..3ecab0a --- /dev/null +++ b/app/user_center/rpc/internal/logic/coin/change_coin_logic_test.go @@ -0,0 +1,172 @@ +package coin + +import ( + "bufio" + "context" + "fmt" + "github.com/shopspring/decimal" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" + "live-service/app/user_center/model" + "log" + "os" + "regexp" + "strconv" + "strings" + "testing" + "time" +) + +func TestA(t *testing.T) { + file, err := os.Open("C:\\Users\\NorthLan\\Desktop\\dmgame v3\\6月.txt") + if err != nil { + fmt.Println("读取文件失败") + return + } + defer file.Close() + + regex, _ := regexp.Compile(`\d+`) + + type db struct { + name string + value int64 + } + jfs := make([]db, 0) + + scanner := bufio.NewScanner(file) + l := 0 + + var tmpJ db + for scanner.Scan() { + l++ + line := scanner.Text() + if l == 3 { + // 名字 + tmpJ.name = strings.TrimSpace(line) + } else if l == 4 { + // 电池 + battery, _ := strconv.ParseInt(regex.FindString(line), 10, 0) + tmpJ.value = battery + jfs = append(jfs, tmpJ) + //fmt.Println(battery) + l = 0 + } + } + gormDb, err := gorm.Open(mysql.Open("root:root@tcp(127.0.0.1:3306)/dmgame-dev?charset=utf8mb4&loc=Asia%2FShanghai&parseTime=true"), &gorm.Config{ + Logger: logger.New( + log.New(os.Stdout, "\r\n", log.LstdFlags), + logger.Config{ + SlowThreshold: 5 * time.Second, + LogLevel: logger.Info, + IgnoreRecordNotFoundError: true, + Colorful: true, + }, + ), + }) + //UserPlatformModel := model.NewUserPlatformModel(gormDb) + //UserIntegralModel := model.NewUserIntegralModel(gormDb) + UserCoinModel := model.NewUserCoinModel(gormDb) + + gormDb.Transaction(func(tx *gorm.DB) error { + for _, tmpJ := range jfs { + var userId int64 + if err := tx.Table("user_platform"). + Select("user_id"). + Where("p_uname = ?", tmpJ.name).Take(&userId).Error; err != nil { + continue + } + //fmt.Println(userId) + + UserCoinModel.ChangeCoin(context.Background(), tx, userId, tmpJ.value, false) + + //tx.Table("user_integral"). + // Where("user_id = ?", userId). + // Update("integral", gorm.Expr("integral + ?", tmpJ.integral)) + } + return nil + }) + + if err := scanner.Err(); err != nil { + fmt.Println("err", err) + } +} + +func TestB(t *testing.T) { + gormDb, _ := gorm.Open(mysql.Open("root:root@tcp(127.0.0.1:3306)/dmgame-dev?charset=utf8mb4&loc=Asia%2FShanghai&parseTime=true"), &gorm.Config{ + Logger: logger.New( + log.New(os.Stdout, "\r\n", log.LstdFlags), + logger.Config{ + SlowThreshold: 5 * time.Second, + LogLevel: logger.Info, + IgnoreRecordNotFoundError: true, + Colorful: true, + }, + ), + }) + //UserPlatformModel := model.NewUserPlatformModel(gormDb) + UserIntegralModel := model.NewUserIntegralModel(gormDb) + //UserCoinModel := model.NewUserCoinModel(gormDb) + + ctx := context.Background() + + var allCoin []model.UserCoin + err := gormDb.Model(&model.UserCoin{}).Find(&allCoin).Error + if err != nil { + panic(err) + } + + err = gormDb.Transaction(func(tx *gorm.DB) error { + for _, coin := range allCoin { + _, err := UserIntegralModel.ChangeIntegral(ctx, tx, coin.UserId, -coin.Coin*100, true) + if err != nil { + return err + } + time.Sleep(10 * time.Millisecond) + } + return nil + }) + if err != nil { + panic(err) + } +} + +func TestC(t *testing.T) { + gormDb, _ := gorm.Open(mysql.Open("root:root@tcp(127.0.0.1:3306)/dmgame-dev?charset=utf8mb4&loc=Asia%2FShanghai&parseTime=true"), &gorm.Config{ + Logger: logger.New( + log.New(os.Stdout, "\r\n", log.LstdFlags), + logger.Config{ + SlowThreshold: 5 * time.Second, + LogLevel: logger.Info, + IgnoreRecordNotFoundError: true, + Colorful: true, + }, + ), + }) + //UserPlatformModel := model.NewUserPlatformModel(gormDb) + //UserIntegralModel := model.NewUserIntegralModel(gormDb) + UserCoinModel := model.NewUserCoinModel(gormDb) + + ctx := context.Background() + + var allIntegral []model.UserIntegral + err := gormDb.Model(&model.UserIntegral{}).Find(&allIntegral).Error + if err != nil { + panic(err) + } + + err = gormDb.Transaction(func(tx *gorm.DB) error { + for _, integral := range allIntegral { + change := decimal.NewFromInt(integral.Integral).Div(decimal.NewFromInt(500)).Round(0).IntPart() + _, err := UserCoinModel.ChangeCoin(ctx, tx, integral.UserId, change, true) + if err != nil { + return err + } + //time.Sleep(10 * time.Millisecond) + } + return nil + }) + if err != nil { + panic(err) + } +} diff --git a/app/user_center/rpc/internal/logic/integral/integral_temp.go b/app/user_center/rpc/internal/logic/integral/integral_temp.go deleted file mode 100644 index a648763..0000000 --- a/app/user_center/rpc/internal/logic/integral/integral_temp.go +++ /dev/null @@ -1,14 +0,0 @@ -package integral - -import ( - "context" - "github.com/zeromicro/go-zero/core/logx" - "live-service/app/user_center/rpc/internal/svc" -) - -type IntegralTemp struct { - ctx context.Context - svcCtx *svc.ServiceContext - - logx.Logger -} diff --git a/app/user_center/rpc/internal/logic/integral/integral_temp_test.go b/app/user_center/rpc/internal/logic/integral/integral_temp_test.go deleted file mode 100644 index 689c4b8..0000000 --- a/app/user_center/rpc/internal/logic/integral/integral_temp_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package integral - -import ( - "bufio" - "context" - "fmt" - "gorm.io/driver/mysql" - "gorm.io/gorm" - "gorm.io/gorm/logger" - "live-service/app/user_center/model" - "log" - "os" - "regexp" - "strconv" - "strings" - "testing" - "time" -) - -func TestA(t *testing.T) { - file, err := os.Open("C:\\Users\\NorthLan\\Desktop\\dmgame v2\\5月.txt") - if err != nil { - fmt.Println("读取文件失败") - return - } - defer file.Close() - - regex, _ := regexp.Compile(`\d+`) - - type jf struct { - name string - integral int64 - } - jfs := make([]jf, 0) - - scanner := bufio.NewScanner(file) - l := 0 - - var tmpJ jf - for scanner.Scan() { - l++ - line := scanner.Text() - if l == 3 { - // 名字 - tmpJ.name = strings.TrimSpace(line) - } else if l == 4 { - // 电池 - battery, _ := strconv.ParseInt(regex.FindString(line), 10, 0) - tmpJ.integral = battery * 100 - jfs = append(jfs, tmpJ) - //fmt.Println(battery) - l = 0 - } - } - gormDb, err := gorm.Open(mysql.Open("root:root@tcp(127.0.0.1:3306)/dmgame?charset=utf8mb4&loc=Asia%2FShanghai&parseTime=true"), &gorm.Config{ - Logger: logger.New( - log.New(os.Stdout, "\r\n", log.LstdFlags), - logger.Config{ - SlowThreshold: 5 * time.Second, - LogLevel: logger.Info, - IgnoreRecordNotFoundError: true, - Colorful: true, - }, - ), - }) - //UserPlatformModel := model.NewUserPlatformModel(gormDb) - UserIntegralModel := model.NewUserIntegralModel(gormDb) - - gormDb.Transaction(func(tx *gorm.DB) error { - for _, tmpJ := range jfs { - var userId int64 - if err := tx.Table("user_platform"). - Select("user_id"). - Where("p_uname = ?", tmpJ.name).Take(&userId).Error; err != nil { - continue - } - //fmt.Println(userId) - - UserIntegralModel.ChangeIntegral(context.Background(), tx, userId, tmpJ.integral) - - //tx.Table("user_integral"). - // Where("user_id = ?", userId). - // Update("integral", gorm.Expr("integral + ?", tmpJ.integral)) - } - return nil - }) - - if err := scanner.Err(); err != nil { - fmt.Println("err", err) - } -} diff --git a/app/user_center/rpc/internal/logic/rank/rank_job.go b/app/user_center/rpc/internal/logic/rank/rank_job.go index 9ab0cd0..c197e86 100644 --- a/app/user_center/rpc/internal/logic/rank/rank_job.go +++ b/app/user_center/rpc/internal/logic/rank/rank_job.go @@ -11,7 +11,7 @@ import ( pbVars "live-service/app/pb/vars" "live-service/app/user_center/model" "live-service/app/user_center/rpc/internal/svc" - "live-service/app/user_center/rpc/pb" + "sync" "time" ) @@ -23,18 +23,38 @@ type ( Username string Avatar string } + RangeItem struct { + UserId int64 + Username string + Avatar string + Score int64 + } + Range struct { + Type pbVars.RankType + Items []RangeItem + } + ZSetWithLock struct { + *zset.ZSetInt + sync.RWMutex + } Job struct { ctx context.Context svcCtx *svc.ServiceContext // 实时排行榜(定期读取,半实时) - rankByTypeMap map[pbVars.RankType]*zset.ZSetInt + rankByTypeMap map[pbVars.RankType]*ZSetWithLock // 用户数据表内存缓存 userCache *lru.Cache } ) +func newZSetWithLock(scoreLessThan func(l, r int32) bool, rankN int) *ZSetWithLock { + return &ZSetWithLock{ + ZSetInt: zset.NewZSetInt(scoreLessThan, rankN), + } +} + func InitRankJob(svcCtx *svc.ServiceContext) { lessFunc := func(l, r int32) bool { return l > r @@ -43,19 +63,19 @@ func InitRankJob(svcCtx *svc.ServiceContext) { Service = &Job{ ctx: context.Background(), svcCtx: svcCtx, - rankByTypeMap: map[pbVars.RankType]*zset.ZSetInt{ - pbVars.RankType_Damage: zset.NewZSetInt(lessFunc, model.MaxRankN), - pbVars.RankType_DeDamage: zset.NewZSetInt(lessFunc, model.MaxRankN), - pbVars.RankType_General: zset.NewZSetInt(lessFunc, model.MaxRankN), - pbVars.RankType_DeGeneral: zset.NewZSetInt(lessFunc, model.MaxRankN), - pbVars.RankType_KillUnit: zset.NewZSetInt(lessFunc, model.MaxRankN), - pbVars.RankType_DeKillUnit: zset.NewZSetInt(lessFunc, model.MaxRankN), - pbVars.RankType_KillPlayer: zset.NewZSetInt(lessFunc, model.MaxRankN), - pbVars.RankType_DeKillPlayer: zset.NewZSetInt(lessFunc, model.MaxRankN), - pbVars.RankType_Win: zset.NewZSetInt(lessFunc, model.MaxRankN), - pbVars.RankType_Lost: zset.NewZSetInt(lessFunc, model.MaxRankN), - pbVars.RankType_FirstBlood: zset.NewZSetInt(lessFunc, model.MaxRankN), - pbVars.RankType_DeFirstBlood: zset.NewZSetInt(lessFunc, model.MaxRankN), + rankByTypeMap: map[pbVars.RankType]*ZSetWithLock{ + pbVars.RankType_Damage: newZSetWithLock(lessFunc, model.MaxRankN), + pbVars.RankType_DeDamage: newZSetWithLock(lessFunc, model.MaxRankN), + pbVars.RankType_General: newZSetWithLock(lessFunc, model.MaxRankN), + pbVars.RankType_DeGeneral: newZSetWithLock(lessFunc, model.MaxRankN), + pbVars.RankType_KillUnit: newZSetWithLock(lessFunc, model.MaxRankN), + pbVars.RankType_DeKillUnit: newZSetWithLock(lessFunc, model.MaxRankN), + pbVars.RankType_KillPlayer: newZSetWithLock(lessFunc, model.MaxRankN), + pbVars.RankType_DeKillPlayer: newZSetWithLock(lessFunc, model.MaxRankN), + pbVars.RankType_Win: newZSetWithLock(lessFunc, model.MaxRankN), + pbVars.RankType_Lost: newZSetWithLock(lessFunc, model.MaxRankN), + pbVars.RankType_FirstBlood: newZSetWithLock(lessFunc, model.MaxRankN), + pbVars.RankType_DeFirstBlood: newZSetWithLock(lessFunc, model.MaxRankN), }, userCache: uc, } @@ -108,11 +128,25 @@ func (j *Job) initJob() { c.Start() } +func (j *Job) CleanByType(rankType pbVars.RankType) bool { + rankZSet, _, err := j.getRankInstanceAndScoreType(rankType) + if err != nil { + return false + } + rankZSet.Lock() + defer rankZSet.Unlock() + + rankZSet.Clear() + return true +} + func (j *Job) RankByScore(rankType pbVars.RankType, score int32) int32 { rankZSet, _, err := j.getRankInstanceAndScoreType(rankType) if err != nil { return 0 } + rankZSet.Lock() + defer rankZSet.Unlock() rank := rankZSet.RangeByScore(score, score) if len(rank) > 0 { @@ -121,51 +155,53 @@ func (j *Job) RankByScore(rankType pbVars.RankType, score int32) int32 { return 0 } -func (j *Job) RangeRankByType(rankType pbVars.RankType, topN int32) *pb.RankPvpResp { - result := &pb.RankPvpResp{ - Type: rankType, - } +func (j *Job) RangeRankByType(rankType pbVars.RankType, topN int32) Range { + result := Range{Type: rankType} rankZSet, _, err := j.getRankInstanceAndScoreType(rankType) if err != nil { return result } + if topN > model.MaxRankN { topN = model.MaxRankN } + + rankZSet.Lock() rank := rankZSet.RangeByRank(1, uint32(topN)) + rankZSet.Unlock() // 这里make 减少扩容次数 - result.Items = make([]*pb.RankPvpResp_Item, 0, len(rank)) + result.Items = make([]RangeItem, 0, len(rank)) for _, r := range rank { uid := r[0] score := r[1] - var item pb.RankPvpResp_Item + var item RangeItem if c, ok := j.userCache.Get(uid); ok { cached := c.(CachedUserInfo) - item = pb.RankPvpResp_Item{ - Uid: cached.UserId, - Uname: cached.Username, - Score: score, - Avatar: cached.Avatar, + item = RangeItem{ + UserId: cached.UserId, + Username: cached.Username, + Score: score, + Avatar: cached.Avatar, } } else { dbUser, err := j.svcCtx.UserPlatformModel.FindDisplayOneByUserId(j.ctx, uid) if err != nil { - item = pb.RankPvpResp_Item{ - Uid: uid, - Score: score, + item = RangeItem{ + UserId: uid, + Score: score, } } else { - item = pb.RankPvpResp_Item{ - Uid: uid, - Uname: dbUser.PUname, - Score: score, - Avatar: dbUser.PAvatar, + item = RangeItem{ + UserId: uid, + Username: dbUser.PUname, + Score: score, + Avatar: dbUser.PAvatar, } } } - result.Items = append(result.Items, &item) + result.Items = append(result.Items, item) } return result } @@ -175,6 +211,9 @@ func (j *Job) readAndUpdate(rankType pbVars.RankType) { if err != nil { return } + rankZSet.Lock() + defer rankZSet.Unlock() + rank := rankZSet.RangeByRank(1, model.MaxRankN) rankLen := len(rank) @@ -219,7 +258,11 @@ func (j *Job) persistence(rankType pbVars.RankType) { if err != nil { return } + + rankZSet.Lock() rank := rankZSet.RangeByRank(1, model.MaxRankN) + rankZSet.Unlock() + dbModel := make([]model.RankPvp, 0, len(rank)) for _, r := range rank { uid := r[0] @@ -241,8 +284,8 @@ func (j *Job) persistence(rankType pbVars.RankType) { } } -func (j *Job) getRankInstanceAndScoreType(rankType pbVars.RankType) (*zset.ZSetInt, model.ScoreType, error) { - var rankZSet *zset.ZSetInt +func (j *Job) getRankInstanceAndScoreType(rankType pbVars.RankType) (*ZSetWithLock, model.ScoreType, error) { + var rankZSet *ZSetWithLock scoreType := model.ScoreTypeByRankType(rankType) rankZSet, _ = j.rankByTypeMap[rankType] if rankZSet == nil { diff --git a/app/user_center/rpc/internal/logic/rank/rank_pvp_logic.go b/app/user_center/rpc/internal/logic/rank/rank_pvp_logic.go index 78313e1..d9b3b3e 100644 --- a/app/user_center/rpc/internal/logic/rank/rank_pvp_logic.go +++ b/app/user_center/rpc/internal/logic/rank/rank_pvp_logic.go @@ -4,6 +4,7 @@ import ( "context" "live-service/app/user_center/rpc/internal/svc" "live-service/app/user_center/rpc/pb" + "live-service/common/nerr" "github.com/zeromicro/go-zero/core/logx" ) @@ -24,5 +25,21 @@ func NewRankPvpLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RankPvpLo // rank func (l *RankPvpLogic) RankPvp(in *pb.RankPvpReq) (*pb.RankPvpResp, error) { - return Service.RangeRankByType(in.Type, in.TopN), nil + if !l.svcCtx.GameConfig.Rank.Enabled { + return nil, nerr.NewError(nerr.RankNotEnabled, "排行榜功能未开启") + } + resp := &pb.RankPvpResp{} + rankRange := Service.RangeRankByType(in.Type, in.TopN) + + resp.Type = rankRange.Type + resp.Items = make([]*pb.RankPvpResp_Item, 0, len(rankRange.Items)) + for _, item := range rankRange.Items { + resp.Items = append(resp.Items, &pb.RankPvpResp_Item{ + Uid: item.UserId, + Uname: item.Username, + Score: item.Score, + Avatar: item.Avatar, + }) + } + return resp, nil } diff --git a/app/user_center/rpc/internal/logic/rank/rank_pvp_submit_logic.go b/app/user_center/rpc/internal/logic/rank/rank_pvp_submit_logic.go index d05cb89..0130f6c 100644 --- a/app/user_center/rpc/internal/logic/rank/rank_pvp_submit_logic.go +++ b/app/user_center/rpc/internal/logic/rank/rank_pvp_submit_logic.go @@ -2,6 +2,16 @@ package rank import ( "context" + "git.noahlan.cn/northlan/ntools-go/gorm-zero/gormx" + "github.com/jinzhu/now" + "github.com/pkg/errors" + "github.com/shopspring/decimal" + "gorm.io/gorm" + pbVars "live-service/app/pb/vars" + "live-service/app/user_center/model" + "live-service/app/user_center/rpc/internal/common/coin_manager" + "live-service/common/nerr" + "time" "live-service/app/user_center/rpc/internal/svc" "live-service/app/user_center/rpc/pb" @@ -24,7 +34,119 @@ func NewRankPvpSubmitLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Ran } func (l *RankPvpSubmitLogic) RankPvpSubmit(in *pb.RankPvpSubmitReq) (*pb.RankPvpSubmitResp, error) { - // todo: add your logic here and delete this line + resp := &pb.RankPvpSubmitResp{ + Items: make([]*pb.RankPvpSubmitResp_Item, 0), + } + if in.AllRankType { + if err := l.svcCtx.Db.Transaction(func(tx *gorm.DB) error { + for _, rankType := range model.AllRankType() { + if item, err := l.rankByType(rankType, tx); err != nil { + return err + } else { + resp.Items = append(resp.Items, item) + } + } + return nil + }); err != nil { + return nil, errors.Wrap(nerr.NewWithErr(err), "结算排行榜失败") + } + } else { + // rankType判断 + var existsRankType bool + for _, rankType := range model.AllRankType() { + if rankType == in.RankType { + existsRankType = true + break + } + } + if !existsRankType { + return nil, errors.Wrap(nerr.NewError(nerr.RankNotExists, "排行榜类型不存在(不对)"), "排行榜类型不存在(不对)") + } + + if item, err := l.rankByType(in.RankType, nil); err != nil { + return nil, errors.Wrap(nerr.NewWithErr(err), "结算排行榜失败") + } else { + resp.Items = append(resp.Items, item) + } + } + return resp, nil +} + +func (l *RankPvpSubmitLogic) rankByType(rankType pbVars.RankType, tx *gorm.DB) (*pb.RankPvpSubmitResp_Item, error) { + if !l.svcCtx.GameConfig.Rank.Enabled { + return nil, nerr.NewError(nerr.RankNotEnabled, "排行榜功能未开启") + } + // . 获取对应榜单列表(用户) + // . 称号奖励+弹币奖励 + // . 清理对应榜的数据(数据库+内存rank) + rankTypeInt := int32(rankType) + resp := &pb.RankPvpSubmitResp_Item{ + RankType: rankType, + Results: make([]*pb.RankPvpSubmitResp_Result, 0), + } + rsCfg := l.svcCtx.GameConfig.Rank.Submit + cfg, ok := rsCfg[rankTypeInt] + if !ok { + cfg = rsCfg[0] + } + titleCfg, _ := l.svcCtx.GameConfig.Title.FindTitleCfg(cfg.TitleId) + titleExists := titleCfg != nil - return &pb.RankPvpSubmitResp{}, nil + rmbToCoin := l.svcCtx.GameConfig.Coin.RMBToCoin + + if err := gormx.WithTx(l.ctx, l.svcCtx.Db, tx).Transaction(func(tx *gorm.DB) error { + rankRange := Service.RangeRankByType(rankType, model.MaxRankN) + if len(rankRange.Items) == 0 { + return nil + } + for i, item := range rankRange.Items { + tmpItem := &pb.RankPvpSubmitResp_Result{ + UserId: item.UserId, + Username: item.Username, + Avatar: item.Avatar, + } + if i == 0 { + // 榜首 称号奖励 从今日持续到本周日早 + if titleExists { + durationHour := now.With(now.Sunday()).BeginningOfDay().Sub(now.BeginningOfDay()).Hours() + err := l.svcCtx.UserTitleModel.Addon(l.ctx, tx, item.UserId, titleCfg.Id, titleCfg.Type, + time.Duration(durationHour)*time.Hour, false) + if err != nil { + return nerr.NewWithErr(err) + } + tmpItem.Title = titleCfg.Id + } + } + var coinAddon int64 + if i < len(cfg.Best) { + // 前N奖励 + coinAddon = decimal.NewFromFloat(rmbToCoin).Mul(decimal.NewFromFloat(cfg.Best[i])).Round(0).IntPart() + } else { + // 安慰奖 + coinAddon = decimal.NewFromFloat(rmbToCoin).Mul(decimal.NewFromFloat(cfg.Consolation)).Round(0).IntPart() + } + _, err := l.svcCtx.CoinManager.ChangeCoin(l.ctx, tx, &coin_manager.ChangeCoinReq{ + UserId: item.UserId, + Change: coinAddon, + }, pbVars.UserCoinChangedReason_EventRankSubmit) + if err != nil { + return nerr.NewWithErr(err) + } + tmpItem.Coin = coinAddon + // . 清理 + if err = l.svcCtx.StatisticsPvpModel.CleanByType(l.ctx, tx, model.ScoreTypeByRankType(rankType)); err != nil { + if !errors.Is(err, gormx.ErrRowsAffectedZero) { + return nerr.NewWithErr(err) + } + } + if !Service.CleanByType(rankType) { + return nerr.NewError(nerr.RankSubmitErr, "清理内存排行榜数据失败") + } + resp.Results = append(resp.Results, tmpItem) + } + return nil + }); err != nil { + return nil, nerr.NewWithErr(err) + } + return resp, nil } diff --git a/app/user_center/rpc/internal/logic/rank/rank_pvp_submit_logic_test.go b/app/user_center/rpc/internal/logic/rank/rank_pvp_submit_logic_test.go new file mode 100644 index 0000000..02d54b1 --- /dev/null +++ b/app/user_center/rpc/internal/logic/rank/rank_pvp_submit_logic_test.go @@ -0,0 +1,11 @@ +package rank + +import ( + "fmt" + "github.com/jinzhu/now" + "testing" +) + +func TestAAA(t *testing.T) { + fmt.Println(now.With(now.Sunday()).BeginningOfDay().Sub(now.With(now.Monday()).BeginningOfDay()).Hours()) +} diff --git a/app/user_center/rpc/internal/logic/statistics/stat_pvp_report_logic.go b/app/user_center/rpc/internal/logic/statistics/stat_pvp_report_logic.go index 0c3594c..f7c80e8 100644 --- a/app/user_center/rpc/internal/logic/statistics/stat_pvp_report_logic.go +++ b/app/user_center/rpc/internal/logic/statistics/stat_pvp_report_logic.go @@ -35,7 +35,7 @@ type ( } statPvPReportItem struct { - *pb.StatPvPReportResp_Item + respItem *pb.StatPvPReportResp_Item uid int64 uname string damage int64 @@ -56,61 +56,50 @@ func (l *StatPvpReportLogic) StatPvpReport(in *pb.StatPvPReportReq) (*pb.StatPvP WinCamp: in.WinCamp, BattleId: in.BattleId, } - winItems := make([]*statPvPReportItem, 0, len(in.WinItems)) - lostItems := make([]*statPvPReportItem, 0, len(in.LostItems)) // 1. 计算评分 - winSum := l.calcSum(in.WinItems, winItems) - lostSum := l.calcSum(in.LostItems, lostItems) + winSum, winItems := l.calcSum(in.WinItems) + lostSum, lostItems := l.calcSum(in.LostItems) l.calcScoreResponse(winSum, winItems) l.calcScoreResponse(lostSum, lostItems) // 2. 评分排序 sort.Slice(winItems, func(i, j int) bool { - return winItems[i].Score > winItems[j].Score + return winItems[i].respItem.Score > winItems[j].respItem.Score }) sort.Slice(lostItems, func(i, j int) bool { - return lostItems[i].Score > lostItems[j].Score + return lostItems[i].respItem.Score > lostItems[j].respItem.Score }) // 3. 添加position + 各项记录(可忽略错误) err = l.reports(true, winItems) if err != nil { - l.Logger.Errorf("获胜PvP记录失败, err:%v", err) + l.Logger.Errorf("获胜PvP记录失败, err:%+v", err) } err = l.reports(false, lostItems) if err != nil { - l.Logger.Errorf("战败PvP记录失败, err:%v", err) + l.Logger.Errorf("战败PvP记录失败, err:%+v", err) } // resp resp.WinItems = make([]*pb.StatPvPReportResp_Item, 0, len(winItems)) resp.LostItems = make([]*pb.StatPvPReportResp_Item, 0, len(lostItems)) for _, item := range winItems { - resp.WinItems = append(resp.WinItems, &pb.StatPvPReportResp_Item{ - Uid: item.uid, - Uname: item.uname, - Position: item.Position, - Score: item.Score, - }) + resp.WinItems = append(resp.WinItems, item.respItem) } for _, item := range lostItems { - resp.LostItems = append(resp.LostItems, &pb.StatPvPReportResp_Item{ - Uid: item.uid, - Uname: item.uname, - Position: item.Position, - Score: item.Score, - }) + resp.LostItems = append(resp.LostItems, item.respItem) } return resp, nil } -func (l *StatPvpReportLogic) calcSum(items []*pb.StatPvPReportReq_Item, out []*statPvPReportItem) sumType { +func (l *StatPvpReportLogic) calcSum(items []*pb.StatPvPReportReq_Item) (sumType, []*statPvPReportItem) { + resp := make([]*statPvPReportItem, 0, len(items)) var sum sumType for _, item := range items { sum.damage += item.Damage sum.deDamage += item.DeDamage sum.killPlayer += item.KillPlayer - out = append(out, &statPvPReportItem{ + resp = append(resp, &statPvPReportItem{ uid: item.Uid, uname: item.Uname, damage: item.Damage, @@ -124,31 +113,46 @@ func (l *StatPvpReportLogic) calcSum(items []*pb.StatPvPReportReq_Item, out []*s isGeneral: item.IsGeneral, }) } - return sum + return sum, resp } func (l *StatPvpReportLogic) calcScoreResponse(sum sumType, items []*statPvPReportItem) { for _, item := range items { itemResp := &pb.StatPvPReportResp_Item{ - Uid: item.Uid, - Uname: item.Uname, + Uid: item.uid, + Uname: item.uname, Score: 0, } - damageScore, _ := decimal.NewFromInt(item.damage). - Div(decimal.NewFromInt(sum.damage)). - Mul(decimal.NewFromFloat32(0.3)). - Mul(decimal.NewFromInt(100)). - Round(1).Float64() - deDamageScore, _ := decimal.NewFromInt(item.deDamage). - Div(decimal.NewFromInt(sum.deDamage)). - Mul(decimal.NewFromFloat32(0.1)). - Mul(decimal.NewFromInt(100)). - Round(1).Float64() - killPlayerScore, _ := decimal.NewFromInt(item.killPlayer). - Div(decimal.NewFromInt(sum.killPlayer)). - Mul(decimal.NewFromFloat32(0.3)). - Mul(decimal.NewFromInt(100)). - Round(1).Float64() + var damageScore float64 + if sum.damage == 0 { + damageScore = 0 + } else { + damageScore, _ = decimal.NewFromInt(item.damage). + Div(decimal.NewFromInt(sum.damage)). + Mul(decimal.NewFromFloat32(0.3)). + Mul(decimal.NewFromInt(100)). + Round(1).Float64() + } + var deDamageScore float64 + if sum.damage == 0 { + deDamageScore = 0 + } else { + deDamageScore, _ = decimal.NewFromInt(item.deDamage). + Div(decimal.NewFromInt(sum.deDamage)). + Mul(decimal.NewFromFloat32(0.1)). + Mul(decimal.NewFromInt(100)). + Round(1).Float64() + } + var killPlayerScore float64 + if sum.killPlayer == 0 { + killPlayerScore = 0 + } else { + killPlayerScore, _ = decimal.NewFromInt(item.killPlayer). + Div(decimal.NewFromInt(sum.killPlayer)). + Mul(decimal.NewFromFloat32(0.3)). + Mul(decimal.NewFromInt(100)). + Round(1).Float64() + } deKillUnit := item.deKillUnit if deKillUnit == 0 { deKillUnit = 1 @@ -175,14 +179,17 @@ func (l *StatPvpReportLogic) calcScoreResponse(sum sumType, items []*statPvPRepo if item.isGeneral { itemResp.Score += 10 } - item.StatPvPReportResp_Item = itemResp + if itemResp.Score < 0 { + itemResp.Score = 0 + } + item.respItem = itemResp } } func (l *StatPvpReportLogic) reports(win bool, items []*statPvPReportItem) error { return l.svcCtx.StatisticsPvpModel.TransactCtx(l.ctx, nil, func(tx *gorm.DB) error { for i, item := range items { - item.Position = int32(i + 1) + item.respItem.Position = int32(i + 1) props := &model.UpdateRecordProps{ Damage: item.damage, DeDamage: item.deDamage, @@ -201,7 +208,7 @@ func (l *StatPvpReportLogic) reports(win bool, items []*statPvPReportItem) error props.General = true } - if err := l.svcCtx.StatisticsPvpModel.UpdateRecord(l.ctx, tx, item.Uid, props); err != nil { + if err := l.svcCtx.StatisticsPvpModel.UpdateRecord(l.ctx, tx, item.uid, props); err != nil { if errors.Is(err, gormx.ErrRowsAffectedZero) { // insert var ( diff --git a/app/user_center/rpc/internal/logic/user/get_user_details_logic.go b/app/user_center/rpc/internal/logic/user/get_user_details_logic.go index 9ffbfce..73829f2 100644 --- a/app/user_center/rpc/internal/logic/user/get_user_details_logic.go +++ b/app/user_center/rpc/internal/logic/user/get_user_details_logic.go @@ -50,9 +50,6 @@ func (l *GetUserDetailsLogic) GetUserDetails(in *pb.UserIdReq) (*pb.UserDetailsR resp.Coin = coin nowTime := time.Now() - // 当前佩戴 称号/精英单位 - var currentTitle pb.UserDetailsResp_TitleItem - var currentElite pb.UserDetailsResp_EliteItem _ = l.svcCtx.Db.Transaction(func(tx *gorm.DB) error { if zhgUserDetails, err := l.svcCtx.ZhgUserDetailsModel.FindOne(l.ctx, tx, in.UserId); err == nil { @@ -62,7 +59,7 @@ func (l *GetUserDetailsLogic) GetUserDetails(in *pb.UserIdReq) (*pb.UserDetailsR if cfg, err := l.svcCtx.GameConfig.Title.FindTitleCfg(dbTitle.TitleId); err == nil { name = cfg.Name } - currentTitle = pb.UserDetailsResp_TitleItem{ + resp.CurrentTitle = &pb.UserDetailsResp_TitleItem{ Id: dbTitle.TitleId, Name: name, Sort: int32(dbTitle.Sort), @@ -72,9 +69,13 @@ func (l *GetUserDetailsLogic) GetUserDetails(in *pb.UserIdReq) (*pb.UserDetailsR } if dbElite, err := l.svcCtx.UserEliteModel.FindOneByUserIdEliteId(l.ctx, tx, in.UserId, zhgUserDetails.Elite); err == nil { if !timex.DayExpire(nowTime, dbElite.EndTime, bool(dbElite.Forever)) { - currentElite = pb.UserDetailsResp_EliteItem{ + var sort int32 = 2 + if cfg, err := l.svcCtx.GameConfig.Elite.FindEliteCfg(dbElite.EliteId); err == nil { + sort = cfg.Sort + } + resp.CurrentElite = &pb.UserDetailsResp_EliteItem{ Id: dbElite.EliteId, - Sort: int32(dbElite.Sort), + Sort: sort, Remain: timex.DayRemain(nowTime, dbElite.EndTime, bool(dbElite.Forever)), } } @@ -83,25 +84,25 @@ func (l *GetUserDetailsLogic) GetUserDetails(in *pb.UserIdReq) (*pb.UserDetailsR return nil }) - resp.CurrentTitle = ¤tTitle - resp.CurrentElite = ¤tElite - // 拥有 称号/精英单位 列表 - elites := make([]*pb.UserDetailsResp_EliteItem, 0) + resp.Elites = make([]*pb.UserDetailsResp_EliteItem, 0) if dbElites, err := l.svcCtx.UserEliteModel.FindByUserId(l.ctx, nil, in.UserId); err == nil { for _, item := range dbElites { if !timex.DayExpire(nowTime, item.EndTime, bool(item.Forever)) { - elites = append(elites, &pb.UserDetailsResp_EliteItem{ + var sort int32 = 2 + if cfg, err := l.svcCtx.GameConfig.Elite.FindEliteCfg(item.EliteId); err == nil { + sort = cfg.Sort + } + resp.Elites = append(resp.Elites, &pb.UserDetailsResp_EliteItem{ Id: item.EliteId, - Sort: int32(item.Sort), + Sort: sort, Remain: timex.DayRemain(nowTime, item.EndTime, bool(item.Forever)), }) } } } - resp.Elites = elites - titles := make([]*pb.UserDetailsResp_TitleItem, 0) + resp.Titles = make([]*pb.UserDetailsResp_TitleItem, 0) if dbTitles, err := l.svcCtx.UserTitleModel.FindByUserId(l.ctx, nil, in.UserId); err == nil { for _, item := range dbTitles { if !timex.DayExpire(nowTime, item.EndTime, bool(item.Forever)) { @@ -109,7 +110,7 @@ func (l *GetUserDetailsLogic) GetUserDetails(in *pb.UserIdReq) (*pb.UserDetailsR if cfg, err := l.svcCtx.GameConfig.Title.FindTitleCfg(item.TitleId); err == nil { name = cfg.Name } - titles = append(titles, &pb.UserDetailsResp_TitleItem{ + resp.Titles = append(resp.Titles, &pb.UserDetailsResp_TitleItem{ Id: item.TitleId, Name: name, Sort: int32(item.Sort), @@ -118,7 +119,5 @@ func (l *GetUserDetailsLogic) GetUserDetails(in *pb.UserIdReq) (*pb.UserDetailsR } } } - resp.Titles = titles - return resp, nil } diff --git a/app/user_center/rpc/internal/logic/zhg/buy_elite_logic.go b/app/user_center/rpc/internal/logic/zhg/buy_elite_logic.go index 28aa5a1..2c2843a 100644 --- a/app/user_center/rpc/internal/logic/zhg/buy_elite_logic.go +++ b/app/user_center/rpc/internal/logic/zhg/buy_elite_logic.go @@ -30,9 +30,12 @@ func NewBuyEliteLogic(ctx context.Context, svcCtx *svc.ServiceContext) *BuyElite func (l *BuyEliteLogic) BuyElite(in *pb.EliteReq) (*pb.BuyEliteResp, error) { elite, err := l.svcCtx.GameConfig.Elite.FindEliteCfgByLive(in.Sort) if err != nil { - // 待购买的精英单位不存在 - return nil, errors.Wrapf(nerr.NewError(nerr.EliteConfigNotFoundErr, err.Error()), err.Error()) + return nil, nerr.NewError(nerr.EliteConfigNotFoundErr, err.Error()) } + if elite.Id == 0 { + return nil, nerr.NewError(nerr.UserBuyDefaultEliteErr, err.Error()) + } + resp := &pb.BuyEliteResp{ UserId: in.UserId, EliteId: elite.Id, diff --git a/app/user_center/rpc/internal/logic/zhg/change_elite_logic.go b/app/user_center/rpc/internal/logic/zhg/change_elite_logic.go index fc6ddb1..e440c4a 100644 --- a/app/user_center/rpc/internal/logic/zhg/change_elite_logic.go +++ b/app/user_center/rpc/internal/logic/zhg/change_elite_logic.go @@ -33,9 +33,13 @@ func (l *ChangeEliteLogic) ChangeElite(in *pb.EliteReq) (*pb.ChangeEliteResp, er resp := &pb.ChangeEliteResp{ UserId: in.UserId, } - eliteId := l.svcCtx.GameConfig.Elite.Default.Id - if in.Sort != l.svcCtx.GameConfig.Elite.Default.Sort { - dbModel, err := l.svcCtx.UserEliteModel.FindOneByUserIdSort(l.ctx, nil, in.UserId, int64(in.Sort)) + eliteCfg, err := l.svcCtx.GameConfig.Elite.FindEliteCfgByLive(in.Sort) + if err != nil { + return nil, nerr.NewError(nerr.EliteConfigNotFoundErr, err.Error()) + } + + if eliteCfg.Id != l.svcCtx.GameConfig.Elite.DefaultId { + dbModel, err := l.svcCtx.UserEliteModel.FindOneByUserIdEliteId(l.ctx, nil, in.UserId, eliteCfg.Id) if err != nil { if errors.Is(err, gormx.ErrNotFound) { return nil, errors.Wrap(nerr.NewError(nerr.UserEliteNotFoundErr, "用户没有此单位"), "用户没有此单位") @@ -46,13 +50,12 @@ func (l *ChangeEliteLogic) ChangeElite(in *pb.EliteReq) (*pb.ChangeEliteResp, er if timex.DayExpire(time.Now(), dbModel.EndTime, bool(dbModel.Forever)) { return nil, errors.Wrap(nerr.NewError(nerr.UserEliteExpiresErr, "待切换的精英单位已过期"), "待切换的精英单位已过期") } - eliteId = dbModel.EliteId } // 更新当前使用精英单位 - err := l.svcCtx.ZhgUserDetailsModel.Upsert(l.ctx, nil, &model.UpsertParam{ + err = l.svcCtx.ZhgUserDetailsModel.Upsert(l.ctx, nil, &model.UpsertParam{ UserId: in.UserId, - Elite: &eliteId, + Elite: &eliteCfg.Id, }) if err != nil { if !errors.Is(err, gormx.ErrRowsAffectedZero) { @@ -60,7 +63,7 @@ func (l *ChangeEliteLogic) ChangeElite(in *pb.EliteReq) (*pb.ChangeEliteResp, er } } - resp.EliteId = eliteId + resp.EliteId = eliteCfg.Id return resp, nil } diff --git a/app/user_center/rpc/pb/user_center.pb.go b/app/user_center/rpc/pb/user_center.pb.go index 17f7690..2991872 100644 --- a/app/user_center/rpc/pb/user_center.pb.go +++ b/app/user_center/rpc/pb/user_center.pb.go @@ -2773,12 +2773,11 @@ type RankPvpSubmitResp_Result struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - UserId int64 `protobuf:"varint,1,opt,name=userId,proto3" json:"userId,omitempty"` - Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` - Avatar string `protobuf:"bytes,3,opt,name=avatar,proto3" json:"avatar,omitempty"` - Coin int64 `protobuf:"varint,4,opt,name=coin,proto3" json:"coin,omitempty"` // 获取到的积分数 - Title string `protobuf:"bytes,5,opt,name=title,proto3" json:"title,omitempty"` // 获取到的称号 - TitleDuration int64 `protobuf:"varint,6,opt,name=titleDuration,proto3" json:"titleDuration,omitempty"` // 称号持续时间(单位: 秒,负数为无限长) + UserId int64 `protobuf:"varint,1,opt,name=userId,proto3" json:"userId,omitempty"` + Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username,omitempty"` + Avatar string `protobuf:"bytes,3,opt,name=avatar,proto3" json:"avatar,omitempty"` + Coin int64 `protobuf:"varint,4,opt,name=coin,proto3" json:"coin,omitempty"` // 获取到的积分数 + Title int64 `protobuf:"varint,5,opt,name=title,proto3" json:"title,omitempty"` // 获取到的称号ID } func (x *RankPvpSubmitResp_Result) Reset() { @@ -2841,17 +2840,10 @@ func (x *RankPvpSubmitResp_Result) GetCoin() int64 { return 0 } -func (x *RankPvpSubmitResp_Result) GetTitle() string { +func (x *RankPvpSubmitResp_Result) GetTitle() int64 { if x != nil { return x.Title } - return "" -} - -func (x *RankPvpSubmitResp_Result) GetTitleDuration() int64 { - if x != nil { - return x.TitleDuration - } return 0 } @@ -3209,166 +3201,164 @@ var file_user_center_proto_rawDesc = []byte{ 0x76, 0x61, 0x72, 0x73, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x72, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x52, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x6c, - 0x6c, 0x52, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0xdb, 0x02, 0x0a, 0x11, 0x52, 0x61, + 0x6c, 0x52, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0xb4, 0x02, 0x0a, 0x11, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x76, 0x70, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x30, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x76, 0x70, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, - 0x73, 0x1a, 0xa4, 0x01, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, - 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, - 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x69, 0x6e, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x63, 0x6f, 0x69, 0x6e, 0x12, 0x14, 0x0a, 0x05, - 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, - 0x6c, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x44, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, 0x69, 0x74, 0x6c, 0x65, - 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x6d, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, - 0x12, 0x2d, 0x0a, 0x08, 0x72, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x76, 0x61, 0x72, 0x73, 0x2e, 0x52, 0x61, 0x6e, - 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x72, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x36, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x76, 0x70, 0x53, 0x75, 0x62, - 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, - 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, - 0x52, 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, - 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x08, 0x72, - 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, - 0x70, 0x62, 0x2e, 0x76, 0x61, 0x72, 0x73, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, - 0x52, 0x08, 0x72, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x6c, - 0x6c, 0x52, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0b, 0x61, 0x6c, 0x6c, 0x52, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0x9a, 0x01, 0x0a, - 0x0c, 0x55, 0x73, 0x65, 0x72, 0x52, 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x12, 0x2b, 0x0a, - 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, - 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x2e, 0x49, - 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x1a, 0x5d, 0x0a, 0x04, 0x49, 0x74, - 0x65, 0x6d, 0x12, 0x2d, 0x0a, 0x08, 0x72, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x76, 0x61, 0x72, 0x73, 0x2e, 0x52, - 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x72, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, - 0x70, 0x6f, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x36, 0x0a, 0x08, 0x45, 0x6c, 0x69, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, - 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x6f, 0x72, - 0x74, 0x22, 0x76, 0x0a, 0x0c, 0x47, 0x69, 0x76, 0x65, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6c, 0x69, - 0x74, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x65, 0x6c, 0x69, 0x74, - 0x65, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x18, 0x0a, 0x07, 0x66, 0x6f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x66, 0x6f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x22, 0x70, 0x0a, 0x0c, 0x42, 0x75, 0x79, - 0x45, 0x6c, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, - 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, - 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6c, 0x69, 0x74, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x07, 0x65, 0x6c, 0x69, 0x74, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, - 0x6f, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x12, - 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x76, 0x0a, 0x0c, 0x47, - 0x69, 0x76, 0x65, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, + 0x73, 0x1a, 0x7e, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, - 0x72, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x49, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1a, 0x0a, - 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x66, 0x6f, 0x72, - 0x65, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x66, 0x6f, 0x72, 0x65, - 0x76, 0x65, 0x72, 0x22, 0x84, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x79, 0x54, 0x69, 0x74, 0x6c, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, + 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x69, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x63, 0x6f, 0x69, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, + 0x69, 0x74, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, + 0x65, 0x1a, 0x6d, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x2d, 0x0a, 0x08, 0x72, 0x61, 0x6e, + 0x6b, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x62, + 0x2e, 0x76, 0x61, 0x72, 0x73, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, + 0x72, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x75, + 0x6c, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x62, 0x2e, 0x52, + 0x61, 0x6e, 0x6b, 0x50, 0x76, 0x70, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x07, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, + 0x22, 0x92, 0x01, 0x0a, 0x0b, 0x55, 0x73, 0x65, 0x72, 0x52, 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x71, + 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x08, 0x72, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x76, 0x61, 0x72, 0x73, + 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x52, 0x08, 0x72, 0x61, 0x6e, 0x6b, 0x54, + 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x6c, 0x6c, 0x52, 0x61, 0x6e, 0x6b, 0x54, 0x79, + 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x61, 0x6c, 0x6c, 0x52, 0x61, 0x6e, + 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22, 0x9a, 0x01, 0x0a, 0x0c, 0x55, 0x73, 0x65, 0x72, 0x52, 0x61, + 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x12, 0x2b, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, + 0x65, 0x6d, 0x73, 0x1a, 0x5d, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x2d, 0x0a, 0x08, 0x72, + 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, + 0x70, 0x62, 0x2e, 0x76, 0x61, 0x72, 0x73, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, + 0x52, 0x08, 0x72, 0x61, 0x6e, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x6f, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x6f, 0x73, 0x12, 0x14, 0x0a, 0x05, + 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x63, 0x6f, + 0x72, 0x65, 0x22, 0x36, 0x0a, 0x08, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, 0x16, + 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, + 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x22, 0x76, 0x0a, 0x0c, 0x47, 0x69, + 0x76, 0x65, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, + 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, + 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6c, 0x69, 0x74, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x07, 0x65, 0x6c, 0x69, 0x74, 0x65, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, + 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x66, 0x6f, 0x72, 0x65, + 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x66, 0x6f, 0x72, 0x65, 0x76, + 0x65, 0x72, 0x22, 0x70, 0x0a, 0x0c, 0x42, 0x75, 0x79, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6c, + 0x69, 0x74, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x65, 0x6c, 0x69, + 0x74, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x76, 0x0a, 0x0c, 0x47, 0x69, 0x76, 0x65, 0x54, 0x69, 0x74, 0x6c, + 0x65, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, - 0x69, 0x74, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, - 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x43, 0x0a, 0x0f, 0x43, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, + 0x69, 0x74, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x66, 0x6f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x66, 0x6f, 0x72, 0x65, 0x76, 0x65, 0x72, 0x22, 0x84, 0x01, 0x0a, + 0x0c, 0x42, 0x75, 0x79, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, - 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6c, 0x69, 0x74, 0x65, 0x49, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x65, 0x6c, 0x69, 0x74, 0x65, 0x49, 0x64, 0x22, - 0x36, 0x0a, 0x08, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, - 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, - 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, 0x22, 0x57, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, - 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x2a, 0x28, 0x0a, 0x08, 0x47, 0x69, 0x66, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x72, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x73, 0x75, 0x62, - 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, 0x65, 0x10, 0x01, 0x32, 0xaf, 0x08, 0x0a, 0x0a, 0x75, - 0x73, 0x65, 0x72, 0x43, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x14, 0x72, 0x65, 0x74, - 0x72, 0x69, 0x65, 0x76, 0x65, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x55, 0x73, 0x65, - 0x72, 0x12, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x55, - 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x6c, 0x61, 0x74, - 0x66, 0x6f, 0x72, 0x6d, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x34, 0x0a, 0x0e, - 0x67, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x0d, - 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, - 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x12, 0x36, 0x0a, 0x0f, 0x67, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x42, - 0x79, 0x50, 0x55, 0x69, 0x64, 0x12, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, - 0x6f, 0x72, 0x6d, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x0e, 0x2e, 0x70, 0x62, 0x2e, - 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x31, 0x0a, 0x0b, 0x75, 0x73, - 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, 0x12, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x55, - 0x73, 0x65, 0x72, 0x49, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, - 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x2a, 0x0a, - 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x12, 0x11, 0x2e, 0x70, 0x62, - 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x09, - 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x31, 0x0a, 0x0b, 0x67, 0x65, 0x74, - 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x69, 0x6e, 0x12, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, - 0x65, 0x72, 0x49, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, - 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x45, 0x0a, 0x10, - 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x69, 0x6e, - 0x12, 0x17, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x55, 0x73, - 0x65, 0x72, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x54, + 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x49, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x49, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x04, 0x63, 0x6f, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0x43, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x6c, 0x69, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x18, + 0x0a, 0x07, 0x65, 0x6c, 0x69, 0x74, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x07, 0x65, 0x6c, 0x69, 0x74, 0x65, 0x49, 0x64, 0x22, 0x36, 0x0a, 0x08, 0x54, 0x69, 0x74, 0x6c, + 0x65, 0x52, 0x65, 0x71, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x73, 0x6f, 0x72, 0x74, + 0x22, 0x57, 0x0a, 0x0f, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74, + 0x69, 0x74, 0x6c, 0x65, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, + 0x74, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x2a, 0x28, 0x0a, 0x08, 0x47, 0x69, 0x66, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x72, + 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x73, 0x75, 0x62, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x63, + 0x65, 0x10, 0x01, 0x32, 0xaf, 0x08, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x72, 0x43, 0x65, 0x6e, 0x74, + 0x65, 0x72, 0x12, 0x41, 0x0a, 0x14, 0x72, 0x65, 0x74, 0x72, 0x69, 0x65, 0x76, 0x65, 0x50, 0x6c, + 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x55, 0x73, 0x65, 0x72, 0x12, 0x13, 0x2e, 0x70, 0x62, 0x2e, + 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x1a, + 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x55, 0x73, 0x65, + 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x34, 0x0a, 0x0e, 0x67, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, + 0x72, 0x49, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x12, 0x36, 0x0a, 0x0f, 0x67, + 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x42, 0x79, 0x50, 0x55, 0x69, 0x64, 0x12, 0x13, + 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x55, 0x73, 0x65, 0x72, + 0x52, 0x65, 0x71, 0x1a, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x12, 0x31, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x49, 0x6e, 0x12, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x52, 0x65, + 0x71, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x49, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x2a, 0x0a, 0x0a, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x43, 0x6f, 0x69, 0x6e, 0x12, 0x11, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x12, 0x31, 0x0a, 0x0b, 0x67, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x69, + 0x6e, 0x12, 0x0d, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x52, 0x65, 0x71, + 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x69, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x45, 0x0a, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, + 0x72, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x69, 0x6e, 0x12, 0x17, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x69, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x12, 0x2e, 0x0a, 0x0c, 0x75, 0x73, 0x65, 0x72, 0x53, 0x65, 0x6e, 0x64, 0x47, - 0x69, 0x66, 0x74, 0x12, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x6e, - 0x64, 0x47, 0x69, 0x66, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x12, 0x34, 0x0a, 0x0f, 0x75, 0x73, 0x65, 0x72, 0x42, 0x75, 0x79, 0x4e, 0x6f, - 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x16, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, - 0x42, 0x75, 0x79, 0x4e, 0x6f, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x09, - 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3c, 0x0a, 0x0d, 0x73, 0x74, 0x61, - 0x74, 0x50, 0x76, 0x70, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x14, 0x2e, 0x70, 0x62, 0x2e, - 0x53, 0x74, 0x61, 0x74, 0x50, 0x76, 0x50, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, - 0x1a, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x50, 0x76, 0x50, 0x52, 0x65, 0x70, - 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x39, 0x0a, 0x0c, 0x64, 0x72, 0x61, 0x77, 0x47, - 0x69, 0x66, 0x74, 0x50, 0x61, 0x63, 0x6b, 0x12, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x72, 0x61, - 0x77, 0x47, 0x69, 0x66, 0x74, 0x50, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x1a, 0x14, 0x2e, 0x70, - 0x62, 0x2e, 0x44, 0x72, 0x61, 0x77, 0x47, 0x69, 0x66, 0x74, 0x50, 0x61, 0x63, 0x6b, 0x52, 0x65, - 0x73, 0x70, 0x12, 0x2a, 0x0a, 0x07, 0x72, 0x61, 0x6e, 0x6b, 0x50, 0x76, 0x70, 0x12, 0x0e, 0x2e, - 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x76, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, - 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x76, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x3c, - 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x6b, 0x50, 0x76, 0x70, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x12, - 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x76, 0x70, 0x53, 0x75, 0x62, 0x6d, - 0x69, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x50, - 0x76, 0x70, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x30, 0x0a, 0x0b, - 0x75, 0x73, 0x65, 0x72, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x76, 0x70, 0x12, 0x0f, 0x2e, 0x70, 0x62, - 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x1a, 0x10, 0x2e, 0x70, - 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x12, 0x2f, - 0x0a, 0x09, 0x67, 0x69, 0x76, 0x65, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x12, 0x10, 0x2e, 0x70, 0x62, - 0x2e, 0x47, 0x69, 0x76, 0x65, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x10, 0x2e, - 0x70, 0x62, 0x2e, 0x42, 0x75, 0x79, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, - 0x2a, 0x0a, 0x08, 0x62, 0x75, 0x79, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x62, - 0x2e, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x42, - 0x75, 0x79, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x2f, 0x0a, 0x09, 0x67, - 0x69, 0x76, 0x65, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x69, - 0x76, 0x65, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, - 0x42, 0x75, 0x79, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x2a, 0x0a, 0x08, - 0x62, 0x75, 0x79, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x69, - 0x74, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x75, 0x79, 0x54, - 0x69, 0x74, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x30, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, - 0x67, 0x65, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6c, 0x69, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x30, 0x0a, 0x0b, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, - 0x69, 0x74, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x42, 0x06, 0x5a, 0x04, - 0x2e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, + 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x2e, 0x0a, 0x0c, + 0x75, 0x73, 0x65, 0x72, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x69, 0x66, 0x74, 0x12, 0x13, 0x2e, 0x70, + 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x69, 0x66, 0x74, 0x52, 0x65, + 0x71, 0x1a, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x34, 0x0a, 0x0f, + 0x75, 0x73, 0x65, 0x72, 0x42, 0x75, 0x79, 0x4e, 0x6f, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, + 0x16, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x42, 0x75, 0x79, 0x4e, 0x6f, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x09, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x12, 0x3c, 0x0a, 0x0d, 0x73, 0x74, 0x61, 0x74, 0x50, 0x76, 0x70, 0x52, 0x65, 0x70, + 0x6f, 0x72, 0x74, 0x12, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x50, 0x76, 0x50, + 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x50, 0x76, 0x50, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x12, 0x39, 0x0a, 0x0c, 0x64, 0x72, 0x61, 0x77, 0x47, 0x69, 0x66, 0x74, 0x50, 0x61, 0x63, 0x6b, + 0x12, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x72, 0x61, 0x77, 0x47, 0x69, 0x66, 0x74, 0x50, 0x61, + 0x63, 0x6b, 0x52, 0x65, 0x71, 0x1a, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x44, 0x72, 0x61, 0x77, 0x47, + 0x69, 0x66, 0x74, 0x50, 0x61, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x12, 0x2a, 0x0a, 0x07, 0x72, + 0x61, 0x6e, 0x6b, 0x50, 0x76, 0x70, 0x12, 0x0e, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x6b, + 0x50, 0x76, 0x70, 0x52, 0x65, 0x71, 0x1a, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x6b, + 0x50, 0x76, 0x70, 0x52, 0x65, 0x73, 0x70, 0x12, 0x3c, 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x6b, 0x50, + 0x76, 0x70, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x12, 0x14, 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, + 0x6e, 0x6b, 0x50, 0x76, 0x70, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x15, + 0x2e, 0x70, 0x62, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x50, 0x76, 0x70, 0x53, 0x75, 0x62, 0x6d, 0x69, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x30, 0x0a, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x52, 0x61, 0x6e, + 0x6b, 0x50, 0x76, 0x70, 0x12, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x61, + 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x12, 0x2f, 0x0a, 0x09, 0x67, 0x69, 0x76, 0x65, 0x45, + 0x6c, 0x69, 0x74, 0x65, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x69, 0x76, 0x65, 0x45, 0x6c, + 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x75, 0x79, 0x45, + 0x6c, 0x69, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x2a, 0x0a, 0x08, 0x62, 0x75, 0x79, 0x45, + 0x6c, 0x69, 0x74, 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x75, 0x79, 0x45, 0x6c, 0x69, 0x74, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x12, 0x2f, 0x0a, 0x09, 0x67, 0x69, 0x76, 0x65, 0x54, 0x69, 0x74, 0x6c, + 0x65, 0x12, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x69, 0x76, 0x65, 0x54, 0x69, 0x74, 0x6c, 0x65, + 0x52, 0x65, 0x71, 0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x75, 0x79, 0x54, 0x69, 0x74, 0x6c, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x2a, 0x0a, 0x08, 0x62, 0x75, 0x79, 0x54, 0x69, 0x74, 0x6c, + 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x1a, + 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x75, 0x79, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x12, 0x30, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x6c, 0x69, 0x74, 0x65, + 0x12, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x52, 0x65, 0x71, 0x1a, 0x13, + 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x45, 0x6c, 0x69, 0x74, 0x65, 0x52, + 0x65, 0x73, 0x70, 0x12, 0x30, 0x0a, 0x0b, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x74, + 0x6c, 0x65, 0x12, 0x0c, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x65, 0x71, + 0x1a, 0x13, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x54, 0x69, 0x74, 0x6c, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x42, 0x06, 0x5a, 0x04, 0x2e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/app/user_center/rpc/pb/user_center.proto b/app/user_center/rpc/pb/user_center.proto index 584c9f8..c677ec4 100644 --- a/app/user_center/rpc/pb/user_center.proto +++ b/app/user_center/rpc/pb/user_center.proto @@ -239,8 +239,7 @@ message RankPvpSubmitResp { string username = 2; string avatar = 3; int64 coin = 4; // 获取到的积分数 - string title = 5; // 获取到的称号 - int64 titleDuration = 6; // 称号持续时间(单位: 秒,负数为无限长) + int64 title = 5; // 获取到的称号ID } message Item { pb.vars.RankType rankType = 1; // 排行榜类型 diff --git a/common/nerr/err_code.go b/common/nerr/err_code.go index 7dad983..832fa6f 100644 --- a/common/nerr/err_code.go +++ b/common/nerr/err_code.go @@ -36,9 +36,10 @@ const ( UpdateUserCoinErr // 更新用户金币失败 // UserEliteExpiresErr 用户精英单位相关 - UserEliteExpiresErr // 用户精英单位过期 - UserEliteNotFoundErr // 用户没有此单位 - UserChangeEliteErr // 用户切换精英单位失败 + UserEliteExpiresErr // 用户精英单位过期 + UserEliteNotFoundErr // 用户没有此单位 + UserChangeEliteErr // 用户切换精英单位失败 + UserBuyDefaultEliteErr // 用户购买默认精英单位(不允许的事) // UserTitleExpiresErr 用户称号相关 UserTitleExpiresErr // 用户称号过期 @@ -59,6 +60,9 @@ const ( // RankStatisticsSelectError 排行相关 RankStatisticsSelectError // 获取统计数据失败 + RankSubmitErr // 结算失败 + RankNotEnabled // 排行榜功能未开启 + RankNotExists // 排行榜不存在 // EliteConfigNotFoundErr 精英单位 EliteConfigNotFoundErr // 精英单位配置不存在