93 lines
3.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package strategy
import (
"bindbox-game/internal/repository/mysql/dao"
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
)
type ichibanStrategy struct {
read *dao.Query
write *dao.Query
}
func NewIchiban(read *dao.Query, write *dao.Query) ActivityDrawStrategy {
return &ichibanStrategy{read: read, write: write}
}
func (s *ichibanStrategy) PreChecks(ctx context.Context, activityID int64, issueID int64, userID int64) error {
return nil
}
func (s *ichibanStrategy) SelectItem(ctx context.Context, activityID int64, issueID int64, userID int64) (int64, map[string]any, error) {
return 0, nil, errors.New("ichiban strategy requires SelectItemBySlot")
}
func (s *ichibanStrategy) SelectItemBySlot(ctx context.Context, activityID int64, issueID int64, slotIndex int64) (int64, map[string]any, error) {
act, err := s.read.Activities.WithContext(ctx).Where(s.read.Activities.ID.Eq(activityID)).First()
if err != nil || act == nil || len(act.CommitmentSeedMaster) == 0 {
return 0, nil, errors.New("commitment not found")
}
rewards, err := s.read.ActivityRewardSettings.WithContext(ctx).ReadDB().Where(s.read.ActivityRewardSettings.IssueID.Eq(issueID)).Order(
s.read.ActivityRewardSettings.IsBoss.Desc(),
s.read.ActivityRewardSettings.Level.Asc(),
s.read.ActivityRewardSettings.Sort.Asc(),
s.read.ActivityRewardSettings.ID.Asc(),
).Find()
if err != nil || len(rewards) == 0 {
return 0, nil, errors.New("no rewards")
}
// 一番赏:每种奖品 = 1个格位无数量概念
totalSlots := int64(len(rewards))
if totalSlots <= 0 {
return 0, nil, errors.New("no slots")
}
if slotIndex < 0 || slotIndex >= totalSlots {
return 0, nil, errors.New("slot out of range")
}
// build list: 每个reward直接对应一个slot
slots := make([]int64, totalSlots)
for i, r := range rewards {
slots[i] = r.ID
}
// deterministic shuffle by CommitmentSeedMaster
seedKey := act.CommitmentSeedMaster
mac := hmac.New(sha256.New, seedKey)
for i := int(totalSlots - 1); i > 0; i-- {
mac.Reset()
mac.Write([]byte(fmt.Sprintf("shuffle:%d|issue:%d", i, issueID)))
sum := mac.Sum(nil)
rnd := int(binary.BigEndian.Uint64(sum[:8]) % uint64(i+1))
slots[i], slots[rnd] = slots[rnd], slots[i]
}
picked := slots[slotIndex]
// Calculate seed hash for proof
sha := sha256.Sum256(seedKey)
seedHash := fmt.Sprintf("%x", sha)
proof := map[string]any{
"total_slots": totalSlots,
"slot_index": slotIndex,
"seed_hash": seedHash,
"seed_type": "commitment",
}
return picked, proof, nil
}
func (s *ichibanStrategy) GrantReward(ctx context.Context, userID int64, rewardID int64) error {
// 一番赏模式下不再需要扣减数量,因为每个奖品对应唯一格位
// 格位占用通过 issue_position_claims 表来追踪,而非 quantity 字段
// 这里保留接口兼容性,实际的占用检查在调用方完成
return nil
}
func (s *ichibanStrategy) PostEffects(ctx context.Context, userID int64, activityID int64, issueID int64, rewardID int64) error {
return nil
}