Merge branch 'main' of https://cnb.cool/blzing/blazing
This commit is contained in:
123
modules/player/service/cdk_reward.go
Normal file
123
modules/player/service/cdk_reward.go
Normal file
@@ -0,0 +1,123 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"blazing/common/data"
|
||||
baseservice "blazing/modules/base/service"
|
||||
configservice "blazing/modules/config/service"
|
||||
"blazing/modules/player/model"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CdkRewardPet struct {
|
||||
PetID uint32 `json:"pet_id"`
|
||||
CatchTime uint32 `json:"catch_time"`
|
||||
}
|
||||
|
||||
type CdkRewardResult struct {
|
||||
CdkID uint32 `json:"cdk_id"`
|
||||
Items []data.ItemInfo `json:"items,omitempty"`
|
||||
Pets []CdkRewardPet `json:"pets,omitempty"`
|
||||
TitleIDs []uint32 `json:"title_ids,omitempty"`
|
||||
Coins int64 `json:"coins,omitempty"`
|
||||
Gold int64 `json:"gold,omitempty"`
|
||||
FreeGold int64 `json:"free_gold,omitempty"`
|
||||
ExpPool int64 `json:"exp_pool,omitempty"`
|
||||
EVPool int64 `json:"ev_pool,omitempty"`
|
||||
}
|
||||
|
||||
// GrantConfigReward 按 cdk 配置 ID 发放奖励,不处理兑换码次数和领取资格校验。
|
||||
func (s *CdkService) GrantConfigReward(cdkID uint32) (*CdkRewardResult, error) {
|
||||
cfg := configservice.NewCdkService().GetByID(cdkID)
|
||||
if cfg == nil {
|
||||
return nil, fmt.Errorf("绑定的CDK不存在")
|
||||
}
|
||||
if cfg.BindUserId != 0 && cfg.BindUserId != s.userid {
|
||||
return nil, fmt.Errorf("CDK已绑定其他用户")
|
||||
}
|
||||
if !cfg.ValidEndTime.IsZero() && cfg.ValidEndTime.Before(time.Now()) {
|
||||
return nil, fmt.Errorf("绑定的CDK已过期")
|
||||
}
|
||||
|
||||
result := &CdkRewardResult{CdkID: cdkID}
|
||||
infoService := NewInfoService(s.userid)
|
||||
playerInfo := infoService.GetLogin()
|
||||
if playerInfo == nil {
|
||||
return nil, fmt.Errorf("玩家角色不存在")
|
||||
}
|
||||
|
||||
var (
|
||||
infoDirty bool
|
||||
bagItems []data.ItemInfo
|
||||
)
|
||||
|
||||
appendRewardItem := func(itemID uint32, count int64) {
|
||||
if itemID == 0 || count <= 0 {
|
||||
return
|
||||
}
|
||||
switch itemID {
|
||||
case 1:
|
||||
result.Coins += count
|
||||
playerInfo.Coins += count
|
||||
infoDirty = true
|
||||
case 3:
|
||||
result.ExpPool += count
|
||||
playerInfo.ExpPool += count
|
||||
infoDirty = true
|
||||
case 5:
|
||||
result.Gold += count
|
||||
case 9:
|
||||
result.EVPool += count
|
||||
playerInfo.EVPool += count
|
||||
infoDirty = true
|
||||
default:
|
||||
bagItems = append(bagItems, data.ItemInfo{ItemId: int64(itemID), ItemCnt: count})
|
||||
}
|
||||
}
|
||||
|
||||
for _, rewardID := range cfg.ItemRewardIds {
|
||||
itemInfo := configservice.NewItemService().GetItemCount(rewardID)
|
||||
appendRewardItem(uint32(itemInfo.ItemId), itemInfo.ItemCnt)
|
||||
}
|
||||
|
||||
if result.Gold != 0 {
|
||||
baseservice.NewBaseSysUserService().UpdateGold(s.userid, result.Gold*100)
|
||||
}
|
||||
if result.FreeGold != 0 {
|
||||
baseservice.NewBaseSysUserService().UpdateFreeGold(s.userid, result.FreeGold*100)
|
||||
}
|
||||
if len(bagItems) > 0 {
|
||||
items, err := NewItemService(s.userid).AddItems(bagItems)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Items = items
|
||||
}
|
||||
|
||||
for _, rewardID := range cfg.ElfRewardIds {
|
||||
pet := configservice.NewPetRewardService().Get(rewardID)
|
||||
if pet == nil {
|
||||
continue
|
||||
}
|
||||
petInfo := model.GenPetInfo(int(pet.MonID), int(pet.DV), int(pet.Nature), int(pet.Effect), int(pet.Lv), nil, 0)
|
||||
catchTime, err := NewPetService(s.userid).PetAdd(petInfo, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Pets = append(result.Pets, CdkRewardPet{
|
||||
PetID: uint32(pet.MonID),
|
||||
CatchTime: catchTime,
|
||||
})
|
||||
}
|
||||
|
||||
if cfg.TitleRewardIds != 0 {
|
||||
NewTitleService(s.userid).Give(cfg.TitleRewardIds)
|
||||
result.TitleIDs = append(result.TitleIDs, cfg.TitleRewardIds)
|
||||
}
|
||||
|
||||
if infoDirty {
|
||||
infoService.Save(*playerInfo)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
@@ -29,7 +29,7 @@ func (s *GoldListService) ModifyBefore(ctx context.Context, method string, param
|
||||
if t > 0 {
|
||||
return fmt.Errorf("不允许多挂单")
|
||||
}
|
||||
if gconv.Float64(param["rate"]) > 1.0576 {
|
||||
if gconv.Float64(param["rate"]) > 2{
|
||||
r := g.List{}
|
||||
for i := 0; i < grand.N(1, 3); i++ {
|
||||
r = append(r, g.Map{"rate": param["rate"], "exchange_num": param["exchange_num"], "player_id": 10001})
|
||||
|
||||
@@ -180,19 +180,51 @@ func (s *InfoService) Gensession() string {
|
||||
func (s *InfoService) Kick(id uint32) error {
|
||||
|
||||
useid1, err := share.ShareManager.GetUserOnline(id)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
if err != nil || useid1 == 0 {
|
||||
// 请求进入时已经离线,视为成功
|
||||
return nil
|
||||
}
|
||||
|
||||
cl, ok := cool.GetClientOnly(useid1)
|
||||
if ok {
|
||||
err := cl.KickPerson(id) //实现指定服务器踢人
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !ok || cl == nil {
|
||||
// 目标服务器不在线,清理僵尸在线标记并视为成功
|
||||
_ = share.ShareManager.DeleteUserOnline(id)
|
||||
return nil
|
||||
}
|
||||
|
||||
resultCh := make(chan error, 1)
|
||||
go func() {
|
||||
resultCh <- cl.KickPerson(id) // 实现指定服务器踢人
|
||||
}()
|
||||
|
||||
select {
|
||||
case callErr := <-resultCh:
|
||||
if callErr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 调用失败后兜底:若已离线/切服/目标服不在线则视为成功
|
||||
useid2, err2 := share.ShareManager.GetUserOnline(id)
|
||||
if err2 != nil || useid2 == 0 || useid2 != useid1 {
|
||||
return nil
|
||||
}
|
||||
if cl2, ok2 := cool.GetClientOnly(useid2); !ok2 || cl2 == nil {
|
||||
_ = share.ShareManager.DeleteUserOnline(id)
|
||||
return nil
|
||||
}
|
||||
return callErr
|
||||
case <-time.After(3 * time.Second):
|
||||
// 防止异常场景下无限等待;超时不按成功处理
|
||||
useid2, err2 := share.ShareManager.GetUserOnline(id)
|
||||
if err2 != nil || useid2 == 0 || useid2 != useid1 {
|
||||
return nil
|
||||
}
|
||||
if cl2, ok2 := cool.GetClientOnly(useid2); !ok2 || cl2 == nil {
|
||||
_ = share.ShareManager.DeleteUserOnline(id)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("kick timeout, user still online: uid=%d server=%d", id, useid2)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// saveToLocalFile 兜底保存:将数据写入本地lose文件夹
|
||||
|
||||
360
modules/player/service/sign.go
Normal file
360
modules/player/service/sign.go
Normal file
@@ -0,0 +1,360 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"blazing/common/data"
|
||||
"blazing/cool"
|
||||
configmodel "blazing/modules/config/model"
|
||||
configservice "blazing/modules/config/service"
|
||||
"blazing/modules/player/model"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
const signRecordID uint32 = 1
|
||||
|
||||
// SignStageState 表示一个签到阶段的当前状态。
|
||||
type SignStageState struct {
|
||||
SignType uint32 `json:"sign_type"`
|
||||
StageDays uint32 `json:"stage_days"`
|
||||
CdkID uint32 `json:"cdk_id"`
|
||||
Reached bool `json:"reached"`
|
||||
Claimed bool `json:"claimed"`
|
||||
}
|
||||
|
||||
// SignState 表示玩家当前签到进度和阶段状态。
|
||||
type SignState struct {
|
||||
TotalDays uint32 `json:"total_days"`
|
||||
ContinuousDays uint32 `json:"continuous_days"`
|
||||
LastSignDate string `json:"last_sign_date"`
|
||||
TodaySigned bool `json:"today_signed"`
|
||||
Stages []SignStageState `json:"stages"`
|
||||
}
|
||||
|
||||
// SignRewardResult 表示一次签到后自动发放的阶段奖励。
|
||||
type SignRewardResult struct {
|
||||
SignType uint32 `json:"sign_type"`
|
||||
StageDays uint32 `json:"stage_days"`
|
||||
CdkID uint32 `json:"cdk_id"`
|
||||
Items []data.ItemInfo `json:"items,omitempty"`
|
||||
PetIDs []uint32 `json:"pet_ids,omitempty"`
|
||||
TitleIDs []uint32 `json:"title_ids,omitempty"`
|
||||
Coins int64 `json:"coins,omitempty"`
|
||||
Gold int64 `json:"gold,omitempty"`
|
||||
FreeGold int64 `json:"free_gold,omitempty"`
|
||||
ExpPool int64 `json:"exp_pool,omitempty"`
|
||||
EVPool int64 `json:"ev_pool,omitempty"`
|
||||
}
|
||||
|
||||
// SignClaimResult 表示签到后的完整结果。
|
||||
type SignClaimResult struct {
|
||||
State *SignState `json:"state"`
|
||||
Rewards []SignRewardResult `json:"rewards,omitempty"`
|
||||
}
|
||||
|
||||
// SignResetResult 表示管理端执行的签到重置结果。
|
||||
type SignResetResult struct {
|
||||
SignRecordRows int64 `json:"sign_record_rows"`
|
||||
CdkLogRows int64 `json:"cdk_log_rows"`
|
||||
ResetCdkIDs []uint32 `json:"reset_cdk_ids"`
|
||||
}
|
||||
|
||||
// SignService 管理玩家签到进度。
|
||||
type SignService struct {
|
||||
BaseService
|
||||
}
|
||||
|
||||
func NewSignService(id uint32) *SignService {
|
||||
return &SignService{
|
||||
BaseService: BaseService{
|
||||
userid: id,
|
||||
Service: &cool.Service{
|
||||
Model: model.NewSignInRecord(),
|
||||
ListQueryOp: &cool.QueryOp{
|
||||
FieldEQ: []string{"player_id", "is_completed"},
|
||||
},
|
||||
PageQueryOp: &cool.QueryOp{
|
||||
FieldEQ: []string{"player_id", "is_completed"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SignService) GetState() (*SignState, error) {
|
||||
record, err := s.getRecord()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.buildState(record), nil
|
||||
}
|
||||
|
||||
func (s *SignService) Claim() (*SignClaimResult, error) {
|
||||
record, isNew, err := s.getOrInitRecord()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
today := currentDateString()
|
||||
if record.LastSignDate == today {
|
||||
return nil, fmt.Errorf("今天已经签到过了")
|
||||
}
|
||||
|
||||
prevTotalDays := record.TotalDays
|
||||
prevContinuousDays := record.ContinuousDays
|
||||
prevDate := record.LastSignDate
|
||||
record.LastSignDate = today
|
||||
record.TotalDays++
|
||||
if isYesterday(prevDate, today) {
|
||||
record.ContinuousDays++
|
||||
} else {
|
||||
record.ContinuousDays = 1
|
||||
}
|
||||
|
||||
rewards, err := s.grantReachedStageRewards(record, prevTotalDays, prevContinuousDays)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.saveRecord(record, isNew); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SignClaimResult{
|
||||
State: s.buildState(record),
|
||||
Rewards: rewards,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *SignService) ResetAll() (*SignResetResult, error) {
|
||||
result := &SignResetResult{}
|
||||
|
||||
signRes, err := cool.DBM(model.NewSignInRecord()).Delete()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if signRes != nil {
|
||||
result.SignRecordRows, _ = signRes.RowsAffected()
|
||||
}
|
||||
|
||||
configs := configservice.NewSignInService().GetEnabled()
|
||||
cdkIDs := make([]uint32, 0, len(configs))
|
||||
seen := make(map[uint32]struct{}, len(configs))
|
||||
for _, cfg := range configs {
|
||||
if cfg.CdkID == 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[cfg.CdkID]; ok {
|
||||
continue
|
||||
}
|
||||
seen[cfg.CdkID] = struct{}{}
|
||||
cdkIDs = append(cdkIDs, cfg.CdkID)
|
||||
}
|
||||
sort.Slice(cdkIDs, func(i, j int) bool { return cdkIDs[i] < cdkIDs[j] })
|
||||
result.ResetCdkIDs = cdkIDs
|
||||
|
||||
if len(cdkIDs) > 0 {
|
||||
cdkRes, err := cool.DBM(model.NewCdkLog()).WhereIn("code_id", cdkIDs).Delete()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cdkRes != nil {
|
||||
result.CdkLogRows, _ = cdkRes.RowsAffected()
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *SignService) grantReachedStageRewards(record *model.SignInRecord, prevTotalDays, prevContinuousDays uint32) ([]SignRewardResult, error) {
|
||||
configs := configservice.NewSignInService().GetEnabled()
|
||||
if len(configs) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
baseRewardBySignType := make(map[uint32]configmodel.SignIn)
|
||||
for _, cfg := range configs {
|
||||
if cfg.StageDays == 0 {
|
||||
baseRewardBySignType[cfg.SignType] = cfg
|
||||
}
|
||||
}
|
||||
|
||||
cdkLogService := NewCdkService(s.userid)
|
||||
results := make([]SignRewardResult, 0)
|
||||
for _, cfg := range configs {
|
||||
if cfg.StageDays == 0 {
|
||||
continue
|
||||
}
|
||||
if !stageReached(cfg.SignType, cfg.StageDays, record) {
|
||||
continue
|
||||
}
|
||||
if stageReachedByDays(cfg.SignType, cfg.StageDays, prevTotalDays, prevContinuousDays) {
|
||||
continue
|
||||
}
|
||||
|
||||
rewardCdkID := cfg.CdkID
|
||||
if !cdkLogService.CanGet(cfg.CdkID) {
|
||||
baseCfg, ok := baseRewardBySignType[cfg.SignType]
|
||||
if !ok || !cdkLogService.CanGet(baseCfg.CdkID) {
|
||||
continue
|
||||
}
|
||||
rewardCdkID = baseCfg.CdkID
|
||||
}
|
||||
|
||||
reward, err := cdkLogService.GrantConfigReward(rewardCdkID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, buildSignRewardResult(cfg.SignType, cfg.StageDays, reward))
|
||||
cdkLogService.Log(rewardCdkID)
|
||||
}
|
||||
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
if results[i].SignType != results[j].SignType {
|
||||
return results[i].SignType < results[j].SignType
|
||||
}
|
||||
if results[i].StageDays != results[j].StageDays {
|
||||
return results[i].StageDays < results[j].StageDays
|
||||
}
|
||||
return results[i].CdkID < results[j].CdkID
|
||||
})
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func buildSignRewardResult(signType, stageDays uint32, reward *CdkRewardResult) SignRewardResult {
|
||||
result := SignRewardResult{
|
||||
SignType: signType,
|
||||
StageDays: stageDays,
|
||||
CdkID: reward.CdkID,
|
||||
Items: reward.Items,
|
||||
TitleIDs: reward.TitleIDs,
|
||||
Coins: reward.Coins,
|
||||
Gold: reward.Gold,
|
||||
FreeGold: reward.FreeGold,
|
||||
ExpPool: reward.ExpPool,
|
||||
EVPool: reward.EVPool,
|
||||
}
|
||||
if len(reward.Pets) > 0 {
|
||||
result.PetIDs = make([]uint32, 0, len(reward.Pets))
|
||||
for _, pet := range reward.Pets {
|
||||
result.PetIDs = append(result.PetIDs, pet.PetID)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *SignService) buildState(record *model.SignInRecord) *SignState {
|
||||
state := &SignState{
|
||||
Stages: make([]SignStageState, 0),
|
||||
}
|
||||
if record != nil {
|
||||
state.TotalDays = record.TotalDays
|
||||
state.ContinuousDays = record.ContinuousDays
|
||||
state.LastSignDate = record.LastSignDate
|
||||
state.TodaySigned = record.LastSignDate == currentDateString()
|
||||
}
|
||||
|
||||
cdkLogService := NewCdkService(s.userid)
|
||||
configs := configservice.NewSignInService().GetEnabled()
|
||||
for _, cfg := range configs {
|
||||
if cfg.StageDays == 0 {
|
||||
continue
|
||||
}
|
||||
state.Stages = append(state.Stages, SignStageState{
|
||||
SignType: cfg.SignType,
|
||||
StageDays: cfg.StageDays,
|
||||
CdkID: cfg.CdkID,
|
||||
Reached: stageReached(cfg.SignType, cfg.StageDays, record),
|
||||
Claimed: !cdkLogService.CanGet(cfg.CdkID),
|
||||
})
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
func stageReached(signType, stageDays uint32, record *model.SignInRecord) bool {
|
||||
if record == nil {
|
||||
return false
|
||||
}
|
||||
return stageReachedByDays(signType, stageDays, record.TotalDays, record.ContinuousDays)
|
||||
}
|
||||
|
||||
func stageReachedByDays(signType, stageDays, totalDays, continuousDays uint32) bool {
|
||||
if stageDays == 0 {
|
||||
return false
|
||||
}
|
||||
switch signType {
|
||||
case configmodel.SignTypeContinuous:
|
||||
return continuousDays >= stageDays
|
||||
default:
|
||||
return totalDays >= stageDays
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SignService) getRecord() (*model.SignInRecord, error) {
|
||||
var out *model.SignInRecord
|
||||
if err := s.dbm(s.Model).Where("sign_in_id", signRecordID).Scan(&out); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *SignService) getOrInitRecord() (*model.SignInRecord, bool, error) {
|
||||
record, err := s.getRecord()
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if record != nil {
|
||||
return record, false, nil
|
||||
}
|
||||
|
||||
return &model.SignInRecord{
|
||||
Base: model.Base{
|
||||
Model: cool.NewModel(),
|
||||
IsVip: cool.Config.ServerInfo.IsVip,
|
||||
},
|
||||
PlayerID: s.userid,
|
||||
SignInID: signRecordID,
|
||||
IsCompleted: false,
|
||||
ContinuousDays: 0,
|
||||
TotalDays: 0,
|
||||
LastSignDate: "",
|
||||
SignInProgress: []uint32{},
|
||||
}, true, nil
|
||||
}
|
||||
|
||||
func (s *SignService) saveRecord(record *model.SignInRecord, isNew bool) error {
|
||||
data := map[string]any{
|
||||
"player_id": record.PlayerID,
|
||||
"sign_in_id": record.SignInID,
|
||||
"is_completed": false,
|
||||
"continuous_days": record.ContinuousDays,
|
||||
"total_days": record.TotalDays,
|
||||
"last_sign_date": record.LastSignDate,
|
||||
"sign_in_progress": []uint32{},
|
||||
"is_vip": cool.Config.ServerInfo.IsVip,
|
||||
}
|
||||
if isNew {
|
||||
_, err := cool.DBM(s.Model).Data(data).Insert()
|
||||
return err
|
||||
}
|
||||
_, err := s.dbm(s.Model).Where("sign_in_id", signRecordID).Data(data).Update()
|
||||
return err
|
||||
}
|
||||
|
||||
func currentDateString() string {
|
||||
return time.Now().Format("2006-01-02")
|
||||
}
|
||||
|
||||
func isYesterday(previousDate, currentDate string) bool {
|
||||
if previousDate == "" || currentDate == "" {
|
||||
return false
|
||||
}
|
||||
prev, err := time.ParseInLocation("2006-01-02", previousDate, time.Local)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
curr, err := time.ParseInLocation("2006-01-02", currentDate, time.Local)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return prev.Add(24 * time.Hour).Equal(curr)
|
||||
}
|
||||
1
modules/player/service/sign_test.go
Normal file
1
modules/player/service/sign_test.go
Normal file
@@ -0,0 +1 @@
|
||||
package service
|
||||
Reference in New Issue
Block a user