|
|
|
@ -0,0 +1,253 @@
|
|
|
|
|
package grade
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"git.noahlan.cn/northlan/ntools-go/gorm-zero/gormx"
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
"live-service/app/user_center/model"
|
|
|
|
|
"live-service/app/user_center/rpc/internal/config"
|
|
|
|
|
"live-service/app/user_center/rpc/pb"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Manager 排位管理器
|
|
|
|
|
// 段/二级星/星等级(5) 二级段顶级后,开始晋级赛(1次),获胜后升段。
|
|
|
|
|
// 战斗积分(累计),抵扣掉段,每个大段抵扣值不同。
|
|
|
|
|
// 获胜+星,失败-星
|
|
|
|
|
type (
|
|
|
|
|
Manager struct {
|
|
|
|
|
config *config.GameConfig
|
|
|
|
|
userGradeModel model.UserGradeModel
|
|
|
|
|
}
|
|
|
|
|
ReportReq struct {
|
|
|
|
|
UserId int64
|
|
|
|
|
Username string
|
|
|
|
|
Position int32
|
|
|
|
|
Score float32
|
|
|
|
|
}
|
|
|
|
|
ReportResp struct {
|
|
|
|
|
ReportReq
|
|
|
|
|
*pb.Grade
|
|
|
|
|
Result pb.StatPvPReportResp_GradeResult
|
|
|
|
|
Reason pb.StatPvPReportResp_GradeReason
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func NewUserGradeManager(config *config.GameConfig, userGradeModel model.UserGradeModel) *Manager {
|
|
|
|
|
return &Manager{
|
|
|
|
|
config: config,
|
|
|
|
|
userGradeModel: userGradeModel,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// QueryUser 查询用户段位
|
|
|
|
|
func (m *Manager) QueryUser(ctx context.Context, userId int64) (*model.UserGrade, error) {
|
|
|
|
|
var resp *model.UserGrade
|
|
|
|
|
if err := m.userGradeModel.TransactCtx(ctx, nil, func(tx *gorm.DB) error {
|
|
|
|
|
dbModel, err := m.userGradeModel.FindOne(ctx, nil, userId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if errors.Is(err, gormx.ErrNotFound) {
|
|
|
|
|
dbModel = &model.UserGrade{
|
|
|
|
|
UserId: userId,
|
|
|
|
|
Grade: model.DefaultUserGrade,
|
|
|
|
|
Level: model.DefaultUserGradeLevel,
|
|
|
|
|
Star: model.DefaultUserGradeStar,
|
|
|
|
|
BravePoint: 0,
|
|
|
|
|
}
|
|
|
|
|
if err = m.userGradeModel.Insert(ctx, tx, dbModel); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
resp = dbModel
|
|
|
|
|
}
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
resp = dbModel
|
|
|
|
|
return nil
|
|
|
|
|
}); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return resp, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Report 战斗记录
|
|
|
|
|
func (m *Manager) Report(ctx context.Context, winItems, lostItems []ReportReq) ([]ReportResp, error) {
|
|
|
|
|
// 1. 查询现有段位信息
|
|
|
|
|
// 2. 计算段位结果
|
|
|
|
|
// 3. 更新并返回
|
|
|
|
|
userCountSatisfied := len(winItems)+len(lostItems) >= m.config.Grade.MinUserCount
|
|
|
|
|
resp := make([]ReportResp, 0, len(winItems)+len(lostItems))
|
|
|
|
|
if err := m.userGradeModel.TransactCtx(ctx, nil, func(tx *gorm.DB) error {
|
|
|
|
|
for _, item := range winItems {
|
|
|
|
|
if itemResp, err := m.reportItem(ctx, tx, item, true, userCountSatisfied); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
} else {
|
|
|
|
|
resp = append(resp, *itemResp)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, item := range lostItems {
|
|
|
|
|
if itemResp, err := m.reportItem(ctx, tx, item, false, userCountSatisfied); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
} else {
|
|
|
|
|
resp = append(resp, *itemResp)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return resp, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *Manager) reportItem(ctx context.Context, tx *gorm.DB, item ReportReq, win bool, userCountSatisfied bool) (*ReportResp, error) {
|
|
|
|
|
var resp *ReportResp
|
|
|
|
|
dbModel, err := m.userGradeModel.FindOne(ctx, tx, item.UserId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if !errors.Is(err, gormx.ErrNotFound) {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if dbModel == nil {
|
|
|
|
|
// insert
|
|
|
|
|
dbModel = &model.UserGrade{
|
|
|
|
|
UserId: item.UserId,
|
|
|
|
|
}
|
|
|
|
|
grade, result, reason := m.calcRank(win, userCountSatisfied,
|
|
|
|
|
model.DefaultUserGrade,
|
|
|
|
|
model.DefaultUserGradeLevel,
|
|
|
|
|
model.DefaultUserGradeStar,
|
|
|
|
|
0,
|
|
|
|
|
item.Score)
|
|
|
|
|
dbModel.Grade = int64(grade.Grade)
|
|
|
|
|
dbModel.Level = int64(grade.Level)
|
|
|
|
|
dbModel.Star = int64(grade.Star)
|
|
|
|
|
dbModel.BravePoint = grade.BravePoint
|
|
|
|
|
if err = m.userGradeModel.Insert(ctx, tx, dbModel); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
resp = &ReportResp{
|
|
|
|
|
ReportReq: ReportReq{
|
|
|
|
|
UserId: dbModel.UserId,
|
|
|
|
|
Username: item.Username,
|
|
|
|
|
Position: item.Position,
|
|
|
|
|
Score: item.Score,
|
|
|
|
|
},
|
|
|
|
|
Grade: &grade,
|
|
|
|
|
Result: result,
|
|
|
|
|
Reason: reason,
|
|
|
|
|
}
|
|
|
|
|
return resp, nil
|
|
|
|
|
}
|
|
|
|
|
grade, result, reason := m.calcRank(win, userCountSatisfied,
|
|
|
|
|
dbModel.Grade,
|
|
|
|
|
dbModel.Level,
|
|
|
|
|
dbModel.Star,
|
|
|
|
|
dbModel.BravePoint,
|
|
|
|
|
item.Score)
|
|
|
|
|
dbModel.Grade = int64(grade.Grade)
|
|
|
|
|
dbModel.Level = int64(grade.Level)
|
|
|
|
|
dbModel.Star = int64(grade.Star)
|
|
|
|
|
dbModel.BravePoint = grade.BravePoint
|
|
|
|
|
if err = m.userGradeModel.UpdateOptimistic(ctx, tx, dbModel); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
resp = &ReportResp{
|
|
|
|
|
ReportReq: ReportReq{
|
|
|
|
|
UserId: dbModel.UserId,
|
|
|
|
|
Username: item.Username,
|
|
|
|
|
Position: item.Position,
|
|
|
|
|
Score: item.Score,
|
|
|
|
|
},
|
|
|
|
|
Grade: &grade,
|
|
|
|
|
Result: result,
|
|
|
|
|
Reason: reason,
|
|
|
|
|
}
|
|
|
|
|
return resp, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (m *Manager) calcRank(win bool, userCountSatisfied bool, grade, level, star, bravePoint int64, score float32) (g pb.Grade, result pb.StatPvPReportResp_GradeResult, reason pb.StatPvPReportResp_GradeReason) {
|
|
|
|
|
g.Grade = int32(grade)
|
|
|
|
|
g.Level = int32(level)
|
|
|
|
|
g.Star = int32(star)
|
|
|
|
|
g.BravePoint = bravePoint + int64(score)
|
|
|
|
|
if g.BravePoint > m.config.Grade.MaxBravePoint {
|
|
|
|
|
g.BravePoint = m.config.Grade.MaxBravePoint
|
|
|
|
|
}
|
|
|
|
|
if score < m.config.Grade.WinScoreThreshold {
|
|
|
|
|
result = pb.StatPvPReportResp_Keep
|
|
|
|
|
reason = pb.StatPvPReportResp_Win
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if !userCountSatisfied {
|
|
|
|
|
result = pb.StatPvPReportResp_Keep
|
|
|
|
|
reason = pb.StatPvPReportResp_Win
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if win {
|
|
|
|
|
reason = pb.StatPvPReportResp_Win // 获胜
|
|
|
|
|
if grade != model.MaxUserGrade || level != model.MaxUserGradeLevel-1 || star != model.MaxUserGradeStar {
|
|
|
|
|
// 非最高段位
|
|
|
|
|
if star == model.MaxUserGradeStar {
|
|
|
|
|
g.Star = model.DefaultUserGradeStar
|
|
|
|
|
if level == model.MaxUserGradeLevel {
|
|
|
|
|
g.Level = model.DefaultUserGradeLevel
|
|
|
|
|
// grade
|
|
|
|
|
if grade == model.MaxUserGrade {
|
|
|
|
|
g.Grade = int32(grade)
|
|
|
|
|
result = pb.StatPvPReportResp_Keep
|
|
|
|
|
} else {
|
|
|
|
|
g.Grade = int32(grade + 1)
|
|
|
|
|
result = pb.StatPvPReportResp_GradeUp
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
g.Level = int32(level + 1)
|
|
|
|
|
result = pb.StatPvPReportResp_LevelUp
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
g.Star = int32(star + 1)
|
|
|
|
|
result = pb.StatPvPReportResp_StarUp // 升星
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 段位的骁勇分减扣规则
|
|
|
|
|
bravePointCost := m.config.Grade.BravePointCost[grade]
|
|
|
|
|
if bravePoint >= bravePointCost {
|
|
|
|
|
g.Grade = int32(grade)
|
|
|
|
|
g.Level = int32(level)
|
|
|
|
|
g.Star = int32(star)
|
|
|
|
|
g.BravePoint = bravePoint - bravePointCost
|
|
|
|
|
if g.BravePoint < 0 {
|
|
|
|
|
g.BravePoint = 0
|
|
|
|
|
}
|
|
|
|
|
reason = pb.StatPvPReportResp_BravePoint
|
|
|
|
|
result = pb.StatPvPReportResp_Keep
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
reason = pb.StatPvPReportResp_Lost
|
|
|
|
|
if star != model.DefaultUserGradeStar || level != model.DefaultUserGradeLevel || grade != model.DefaultUserGrade {
|
|
|
|
|
if star == model.DefaultUserGradeStar {
|
|
|
|
|
g.Star = model.MaxUserGradeStar - 1 // 掉小段或掉大段,星星变 max-1
|
|
|
|
|
if level == model.DefaultUserGradeLevel {
|
|
|
|
|
g.Level = model.MaxUserGradeLevel
|
|
|
|
|
if grade == model.DefaultUserGrade {
|
|
|
|
|
g.Grade = model.DefaultUserGrade
|
|
|
|
|
result = pb.StatPvPReportResp_Keep
|
|
|
|
|
} else {
|
|
|
|
|
g.Grade = int32(grade - 1)
|
|
|
|
|
result = pb.StatPvPReportResp_GradeDown
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
g.Level = int32(level - 1)
|
|
|
|
|
result = pb.StatPvPReportResp_LevelDown
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
g.Star = int32(star - 1)
|
|
|
|
|
result = pb.StatPvPReportResp_StarDown
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
g.BravePoint = bravePoint + int64(score)
|
|
|
|
|
if g.BravePoint > m.config.Grade.MaxBravePoint {
|
|
|
|
|
g.BravePoint = m.config.Grade.MaxBravePoint
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|