火箭份数配置
This commit is contained in:
parent
41c1d326ee
commit
019ffa9a4a
@ -52,6 +52,7 @@ type VoiceRoomRocketRewardConfig struct {
|
||||
RewardCover string `gorm:"column:reward_cover;size:512"`
|
||||
RewardAmount int64 `gorm:"column:reward_amount"`
|
||||
ExpireDays *int `gorm:"column:expire_days"`
|
||||
RewardCopies int `gorm:"column:reward_copies;default:1"`
|
||||
Weight int `gorm:"column:weight"`
|
||||
Sort int `gorm:"column:sort"`
|
||||
Enabled bool `gorm:"column:enabled;index:idx_voice_room_rocket_reward_scene,priority:4"`
|
||||
|
||||
@ -265,6 +265,12 @@ func (s *Service) SaveRewardConfigs(ctx context.Context, req SaveRewardConfigsRe
|
||||
} else if amount <= 0 {
|
||||
amount = 1
|
||||
}
|
||||
rewardCopies := item.RewardCopies
|
||||
if scene != rewardSceneInRoom {
|
||||
rewardCopies = 1
|
||||
} else if rewardCopies <= 0 {
|
||||
rewardCopies = 1
|
||||
}
|
||||
var row model.VoiceRoomRocketRewardConfig
|
||||
if item.ID > 0 {
|
||||
if err := tx.Where("id = ? AND sys_origin = ?", item.ID, sysOrigin).First(&row).Error; err != nil {
|
||||
@ -289,6 +295,7 @@ func (s *Service) SaveRewardConfigs(ctx context.Context, req SaveRewardConfigsRe
|
||||
row.RewardCover = strings.TrimSpace(item.RewardCover)
|
||||
row.RewardAmount = amount
|
||||
row.ExpireDays = item.ExpireDays
|
||||
row.RewardCopies = rewardCopies
|
||||
row.Weight = item.Weight
|
||||
row.Sort = item.Sort
|
||||
row.Enabled = item.Enabled
|
||||
|
||||
@ -176,7 +176,7 @@ func TestInRoomRewardWorkerSnapshotsSelectsAndGrants(t *testing.T) {
|
||||
seedLevelConfigs(t, db, []int64{100, 200, 300, 400, 500, 600})
|
||||
seedRewardConfig(t, db, 1, rewardSceneTop1, rewardTypeGold, 10)
|
||||
seedRewardConfig(t, db, 1, rewardSceneIgnite, rewardTypeGold, 5)
|
||||
seedRewardConfig(t, db, 1, rewardSceneInRoom, rewardTypeGold, 1)
|
||||
seedRewardConfigWithCopies(t, db, 1, rewardSceneInRoom, rewardTypeGold, 1, 3)
|
||||
|
||||
roomID := int64(9001)
|
||||
for _, userID := range []int64{1001, 1002, 1003, 1004} {
|
||||
@ -336,6 +336,38 @@ func TestPickInRoomRewardCanBeEmpty(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInRoomRewardCopiesLimitSelection(t *testing.T) {
|
||||
service, _ := newTestService(t)
|
||||
service.cfg.VoiceRoomRocket.InRoomRewardUserLimit = 10
|
||||
launch := model.VoiceRoomRocketLaunch{LaunchNo: "VRR-copies"}
|
||||
audience := []roomAudienceUser{
|
||||
{UserID: 1001},
|
||||
{UserID: 1002},
|
||||
{UserID: 1003},
|
||||
{UserID: 1004},
|
||||
}
|
||||
rewards := []model.VoiceRoomRocketRewardConfig{
|
||||
{
|
||||
ID: 1,
|
||||
RewardScene: rewardSceneInRoom,
|
||||
RewardType: rewardTypeGold,
|
||||
RewardName: "金币",
|
||||
RewardAmount: 100,
|
||||
RewardCopies: 2,
|
||||
Weight: 1,
|
||||
},
|
||||
}
|
||||
|
||||
selected := service.selectInRoomRewardUsers(launch, audience, rewards)
|
||||
if len(selected) != 2 {
|
||||
t.Fatalf("selected users = %+v, want 2 users", selected)
|
||||
}
|
||||
picked := service.pickInRoomRewardsForUsers(launch, rewards, selected)
|
||||
if len(picked) != 2 {
|
||||
t.Fatalf("picked rewards = %+v, want 2 users with rewards", picked)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotifyLaunchPublishesIMAndRedisStream(t *testing.T) {
|
||||
gateway := newFakeRocketGateway()
|
||||
service, db, rdb, mr := newTestServiceWithRedis(t, gateway)
|
||||
@ -457,10 +489,11 @@ func TestSaveRewardConfigsReplacesAndClearsSelectedLevel(t *testing.T) {
|
||||
Rewards: []RewardConfigPayload{
|
||||
{
|
||||
Level: 1,
|
||||
RewardScene: rewardSceneTop1,
|
||||
RewardScene: rewardSceneInRoom,
|
||||
RewardType: rewardTypeGold,
|
||||
RewardName: "new gold",
|
||||
RewardAmount: 99,
|
||||
RewardCopies: 10,
|
||||
Enabled: true,
|
||||
Sort: 1,
|
||||
},
|
||||
@ -469,8 +502,8 @@ func TestSaveRewardConfigsReplacesAndClearsSelectedLevel(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("SaveRewardConfigs(replace) error = %v", err)
|
||||
}
|
||||
if len(saved) != 1 || saved[0].RewardAmount != 99 {
|
||||
t.Fatalf("saved rewards = %+v, want one amount 99", saved)
|
||||
if len(saved) != 1 || saved[0].RewardAmount != 99 || saved[0].RewardCopies != 10 {
|
||||
t.Fatalf("saved rewards = %+v, want one amount 99 and 10 copies", saved)
|
||||
}
|
||||
|
||||
var level1Count int64
|
||||
@ -711,6 +744,10 @@ func seedLevelConfigs(t *testing.T, db *gorm.DB, energy []int64) {
|
||||
}
|
||||
|
||||
func seedRewardConfig(t *testing.T, db *gorm.DB, level int, scene string, rewardType string, amount int64) {
|
||||
seedRewardConfigWithCopies(t, db, level, scene, rewardType, amount, 1)
|
||||
}
|
||||
|
||||
func seedRewardConfigWithCopies(t *testing.T, db *gorm.DB, level int, scene string, rewardType string, amount int64, copies int) {
|
||||
t.Helper()
|
||||
now := time.Now()
|
||||
row := model.VoiceRoomRocketRewardConfig{
|
||||
@ -721,6 +758,7 @@ func seedRewardConfig(t *testing.T, db *gorm.DB, level int, scene string, reward
|
||||
RewardType: rewardType,
|
||||
RewardName: scene + " reward",
|
||||
RewardAmount: amount,
|
||||
RewardCopies: copies,
|
||||
Enabled: true,
|
||||
CreateTime: now,
|
||||
UpdateTime: now,
|
||||
|
||||
@ -88,7 +88,11 @@ func (s *Service) processInRoomRewardLaunch(ctx context.Context, launchNo string
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
selected := s.selectInRoomRewardUsers(launch, audience)
|
||||
rewards, err := s.loadInRoomRewardConfigs(ctx, launch.SysOrigin, launch.Level)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
selected := s.selectInRoomRewardUsers(launch, audience, rewards)
|
||||
if err := s.saveAudienceSnapshot(ctx, tx, launch, audience, selected, now); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -100,12 +104,9 @@ func (s *Service) processInRoomRewardLaunch(ctx context.Context, launchNo string
|
||||
return nil
|
||||
}
|
||||
|
||||
rewards, err := s.loadInRoomRewardConfigs(ctx, launch.SysOrigin, launch.Level)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pickedRewards := s.pickInRoomRewardsForUsers(launch, rewards, selected)
|
||||
for _, userID := range selected {
|
||||
for _, reward := range s.pickInRoomRewardsForUser(launch, rewards, userID) {
|
||||
for _, reward := range pickedRewards[userID] {
|
||||
record, err := s.createRewardRecord(ctx, tx, launch, reward, userID, now)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -201,11 +202,8 @@ func (s *Service) loadInRoomRewardConfigs(ctx context.Context, sysOrigin string,
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *Service) selectInRoomRewardUsers(launch model.VoiceRoomRocketLaunch, audience []roomAudienceUser) []int64 {
|
||||
limit := s.cfg.VoiceRoomRocket.InRoomRewardUserLimit
|
||||
if limit <= 0 {
|
||||
limit = defaultInRoomRewardUserLimit
|
||||
}
|
||||
func (s *Service) selectInRoomRewardUsers(launch model.VoiceRoomRocketLaunch, audience []roomAudienceUser, rewards []model.VoiceRoomRocketRewardConfig) []int64 {
|
||||
limit := s.inRoomRewardSelectionLimit(rewards)
|
||||
if len(audience) == 0 {
|
||||
return nil
|
||||
}
|
||||
@ -228,37 +226,80 @@ func (s *Service) selectInRoomRewardUsers(launch model.VoiceRoomRocketLaunch, au
|
||||
return selected
|
||||
}
|
||||
|
||||
func (s *Service) pickInRoomRewardsForUser(launch model.VoiceRoomRocketLaunch, rewards []model.VoiceRoomRocketRewardConfig, userID int64) []model.VoiceRoomRocketRewardConfig {
|
||||
if len(rewards) == 0 {
|
||||
return nil
|
||||
func (s *Service) inRoomRewardSelectionLimit(rewards []model.VoiceRoomRocketRewardConfig) int {
|
||||
totalCopies := 0
|
||||
for _, reward := range rewards {
|
||||
if !strings.EqualFold(strings.TrimSpace(reward.RewardScene), rewardSceneInRoom) {
|
||||
continue
|
||||
}
|
||||
totalCopies += positiveOrDefault(reward.RewardCopies, 1)
|
||||
}
|
||||
weighted := make([]model.VoiceRoomRocketRewardConfig, 0, len(rewards))
|
||||
totalWeight := 0
|
||||
if totalCopies > 0 {
|
||||
return totalCopies
|
||||
}
|
||||
limit := s.cfg.VoiceRoomRocket.InRoomRewardUserLimit
|
||||
if limit <= 0 {
|
||||
limit = defaultInRoomRewardUserLimit
|
||||
}
|
||||
return limit
|
||||
}
|
||||
|
||||
func (s *Service) pickInRoomRewardsForUser(launch model.VoiceRoomRocketLaunch, rewards []model.VoiceRoomRocketRewardConfig, userID int64) []model.VoiceRoomRocketRewardConfig {
|
||||
picked := s.pickInRoomRewardsForUsers(launch, rewards, []int64{userID})
|
||||
return picked[userID]
|
||||
}
|
||||
|
||||
func (s *Service) pickInRoomRewardsForUsers(launch model.VoiceRoomRocketLaunch, rewards []model.VoiceRoomRocketRewardConfig, userIDs []int64) map[int64][]model.VoiceRoomRocketRewardConfig {
|
||||
result := make(map[int64][]model.VoiceRoomRocketRewardConfig, len(userIDs))
|
||||
if len(rewards) == 0 {
|
||||
return result
|
||||
}
|
||||
|
||||
type rewardCandidate struct {
|
||||
reward model.VoiceRoomRocketRewardConfig
|
||||
remaining int
|
||||
}
|
||||
weighted := make([]rewardCandidate, 0, len(rewards))
|
||||
for _, reward := range rewards {
|
||||
weight := reward.Weight
|
||||
if weight <= 0 {
|
||||
weight = 1
|
||||
}
|
||||
reward.Weight = weight
|
||||
weighted = append(weighted, reward)
|
||||
totalWeight += weight
|
||||
weighted = append(weighted, rewardCandidate{
|
||||
reward: reward,
|
||||
remaining: positiveOrDefault(reward.RewardCopies, 1),
|
||||
})
|
||||
}
|
||||
if totalWeight <= 0 {
|
||||
return nil
|
||||
}
|
||||
rng := rand.New(rand.NewSource(seedFromStrings(launch.LaunchNo, strconv.FormatInt(userID, 10), "reward")))
|
||||
point := rng.Intn(totalWeight) + 1
|
||||
acc := 0
|
||||
for _, reward := range weighted {
|
||||
acc += reward.Weight
|
||||
if point <= acc {
|
||||
if strings.EqualFold(strings.TrimSpace(reward.RewardType), rewardTypeNone) {
|
||||
return nil
|
||||
|
||||
for _, userID := range userIDs {
|
||||
totalWeight := 0
|
||||
for _, candidate := range weighted {
|
||||
if candidate.remaining > 0 {
|
||||
totalWeight += candidate.reward.Weight
|
||||
}
|
||||
}
|
||||
if totalWeight <= 0 {
|
||||
continue
|
||||
}
|
||||
rng := rand.New(rand.NewSource(seedFromStrings(launch.LaunchNo, strconv.FormatInt(userID, 10), "reward")))
|
||||
point := rng.Intn(totalWeight) + 1
|
||||
acc := 0
|
||||
for idx := range weighted {
|
||||
if weighted[idx].remaining <= 0 {
|
||||
continue
|
||||
}
|
||||
acc += weighted[idx].reward.Weight
|
||||
if point <= acc {
|
||||
weighted[idx].remaining--
|
||||
if !strings.EqualFold(strings.TrimSpace(weighted[idx].reward.RewardType), rewardTypeNone) {
|
||||
result[userID] = []model.VoiceRoomRocketRewardConfig{weighted[idx].reward}
|
||||
}
|
||||
break
|
||||
}
|
||||
return []model.VoiceRoomRocketRewardConfig{reward}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return result
|
||||
}
|
||||
|
||||
func seedFromStrings(parts ...string) int64 {
|
||||
|
||||
@ -160,6 +160,7 @@ func rewardPayload(row model.VoiceRoomRocketRewardConfig) RewardConfigPayload {
|
||||
RewardCover: row.RewardCover,
|
||||
RewardAmount: row.RewardAmount,
|
||||
ExpireDays: row.ExpireDays,
|
||||
RewardCopies: positiveOrDefault(row.RewardCopies, 1),
|
||||
Weight: row.Weight,
|
||||
Sort: row.Sort,
|
||||
Enabled: row.Enabled,
|
||||
|
||||
@ -333,6 +333,7 @@ type RewardConfigPayload struct {
|
||||
RewardCover string `json:"rewardCover,omitempty"`
|
||||
RewardAmount int64 `json:"rewardAmount"`
|
||||
ExpireDays *int `json:"expireDays,omitempty"`
|
||||
RewardCopies int `json:"rewardCopies"`
|
||||
Weight int `json:"weight"`
|
||||
Sort int `json:"sort"`
|
||||
Enabled bool `json:"enabled"`
|
||||
|
||||
@ -43,6 +43,7 @@ CREATE TABLE IF NOT EXISTS voice_room_rocket_reward_config (
|
||||
reward_cover VARCHAR(512) DEFAULT NULL,
|
||||
reward_amount BIGINT NOT NULL DEFAULT 1 COMMENT '金币数量或道具数量/天数',
|
||||
expire_days INT DEFAULT NULL COMMENT '时效类奖励有效天数',
|
||||
reward_copies INT NOT NULL DEFAULT 1 COMMENT 'IN_ROOM 本奖励每次发射可发放份数',
|
||||
weight INT NOT NULL DEFAULT 0 COMMENT 'IN_ROOM 随机权重;0 表示固定发放',
|
||||
sort INT NOT NULL DEFAULT 0,
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
||||
|
||||
16
migrations/038_voice_room_rocket_reward_copies.sql
Normal file
16
migrations/038_voice_room_rocket_reward_copies.sql
Normal file
@ -0,0 +1,16 @@
|
||||
SET @current_schema := DATABASE();
|
||||
|
||||
SET @add_voice_room_rocket_reward_copies_sql := IF(
|
||||
NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = @current_schema
|
||||
AND table_name = 'voice_room_rocket_reward_config'
|
||||
AND column_name = 'reward_copies'
|
||||
),
|
||||
'ALTER TABLE `voice_room_rocket_reward_config` ADD COLUMN `reward_copies` int NOT NULL DEFAULT 1 COMMENT ''IN_ROOM 本奖励每次发射可发放份数'' AFTER `expire_days`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE add_voice_room_rocket_reward_copies_stmt FROM @add_voice_room_rocket_reward_copies_sql;
|
||||
EXECUTE add_voice_room_rocket_reward_copies_stmt;
|
||||
DEALLOCATE PREPARE add_voice_room_rocket_reward_copies_stmt;
|
||||
Loading…
x
Reference in New Issue
Block a user