Some checks failed
Build docker and publish / linux (1.24.5) (push) Failing after 41s
新增随机种子生成与验证逻辑,包括: 1. 添加随机承诺生成接口 2. 实现抽奖执行与验证流程 3. 新增批量用户创建与删除功能 4. 添加抽奖收据记录表 5. 完善配置管理与错误码 新增测试用例验证随机算法正确性
147 lines
4.5 KiB
Go
147 lines
4.5 KiB
Go
package activity
|
||
|
||
import (
|
||
"context"
|
||
"crypto/rand"
|
||
"crypto/sha256"
|
||
"encoding/json"
|
||
"errors"
|
||
"sort"
|
||
|
||
"bindbox-game/internal/repository/mysql/model"
|
||
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
var algoVersion = "v1-hmac-256"
|
||
|
||
func (s *service) CommitIssueRandom(ctx context.Context, issueID int64) (*IssueRandomCommitment, error) {
|
||
// 检查活动期数状态
|
||
issue, err := s.readDB.ActivityIssues.WithContext(ctx).
|
||
Where(s.readDB.ActivityIssues.ID.Eq(issueID)).
|
||
First()
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, errors.New("活动期数不存在")
|
||
}
|
||
return nil, err
|
||
}
|
||
|
||
// 只允许在未开始状态(3)生成承诺
|
||
if issue.Status != 3 {
|
||
return nil, errors.New("只能在期数未开始状态下生成随机承诺")
|
||
}
|
||
|
||
// 检查是否已存在承诺(防止重复生成)
|
||
existing, _ := s.readDB.IssueRandomCommitments.WithContext(ctx).
|
||
Where(s.readDB.IssueRandomCommitments.IssueID.Eq(issueID)).
|
||
First()
|
||
if existing != nil {
|
||
return nil, errors.New("该期数已存在随机承诺,不可重复生成")
|
||
}
|
||
|
||
items, err := s.readDB.ActivityRewardSettings.WithContext(ctx).
|
||
Where(s.readDB.ActivityRewardSettings.IssueID.Eq(issueID)).
|
||
Order(s.readDB.ActivityRewardSettings.ID).
|
||
Find()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if len(items) == 0 {
|
||
return nil, errors.New("该期数未配置奖励,无法生成随机承诺")
|
||
}
|
||
canonical := make([]ReceiptItem, 0, len(items))
|
||
var weightsTotal int64
|
||
for _, it := range items {
|
||
canonical = append(canonical, ReceiptItem{ID: it.ID, Name: it.Name, Weight: it.Weight, QuantityBefore: it.Quantity})
|
||
if it.Weight > 0 && (it.Quantity == -1 || it.Quantity > 0) {
|
||
weightsTotal += int64(it.Weight)
|
||
}
|
||
}
|
||
|
||
// 检查奖池权重是否有效
|
||
if weightsTotal <= 0 {
|
||
return nil, errors.New("奖池配置无效:总权重必须大于0")
|
||
}
|
||
|
||
sort.Slice(canonical, func(i, j int) bool { return canonical[i].ID < canonical[j].ID })
|
||
b, _ := json.Marshal(canonical)
|
||
itemsRoot := sha256.Sum256(b)
|
||
master := make([]byte, 32)
|
||
_, _ = rand.Read(master)
|
||
serverHash := sha256.Sum256(master)
|
||
|
||
nextVer := int32(1)
|
||
enc, _ := maskSeed(master, issueID, nextVer)
|
||
rec := &model.IssueRandomCommitments{
|
||
IssueID: issueID,
|
||
AlgoVersion: algoVersion,
|
||
ServerSeedMaster: enc,
|
||
ServerSeedHash: serverHash[:],
|
||
ItemsRoot: itemsRoot[:],
|
||
WeightsTotal: weightsTotal,
|
||
StateVersion: nextVer,
|
||
}
|
||
if err := s.writeDB.IssueRandomCommitments.WithContext(ctx).Create(rec); err != nil {
|
||
return nil, err
|
||
}
|
||
return &IssueRandomCommitment{
|
||
AlgoVersion: rec.AlgoVersion,
|
||
IssueID: rec.IssueID,
|
||
ServerSeedMaster: rec.ServerSeedMaster,
|
||
ServerSeedHash: rec.ServerSeedHash,
|
||
ItemsRoot: rec.ItemsRoot,
|
||
WeightsTotal: rec.WeightsTotal,
|
||
StateVersion: rec.StateVersion,
|
||
}, nil
|
||
}
|
||
|
||
func (s *service) GetIssueRandomCommit(ctx context.Context, issueID int64) (*IssueRandomCommitment, error) {
|
||
latest, err := s.readDB.IssueRandomCommitments.WithContext(ctx).
|
||
Where(s.readDB.IssueRandomCommitments.IssueID.Eq(issueID)).
|
||
Order(s.readDB.IssueRandomCommitments.StateVersion.Desc()).
|
||
Take()
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, nil // 没有找到记录是正常的,表示尚未生成承诺
|
||
}
|
||
return nil, err
|
||
}
|
||
return &IssueRandomCommitment{
|
||
AlgoVersion: latest.AlgoVersion,
|
||
IssueID: latest.IssueID,
|
||
ServerSeedMaster: latest.ServerSeedMaster,
|
||
ServerSeedHash: latest.ServerSeedHash,
|
||
ItemsRoot: latest.ItemsRoot,
|
||
WeightsTotal: latest.WeightsTotal,
|
||
StateVersion: latest.StateVersion,
|
||
}, nil
|
||
}
|
||
|
||
func (s *service) GetIssueRandomCommitHistory(ctx context.Context, issueID int64) ([]*IssueRandomCommitment, error) {
|
||
commitments, err := s.readDB.IssueRandomCommitments.WithContext(ctx).
|
||
Where(s.readDB.IssueRandomCommitments.IssueID.Eq(issueID)).
|
||
Order(s.readDB.IssueRandomCommitments.StateVersion.Desc()).
|
||
Find()
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
return []*IssueRandomCommitment{}, nil // 返回空数组而不是错误
|
||
}
|
||
return nil, err
|
||
}
|
||
|
||
result := make([]*IssueRandomCommitment, 0, len(commitments))
|
||
for _, commit := range commitments {
|
||
result = append(result, &IssueRandomCommitment{
|
||
AlgoVersion: commit.AlgoVersion,
|
||
IssueID: commit.IssueID,
|
||
ServerSeedMaster: commit.ServerSeedMaster,
|
||
ServerSeedHash: commit.ServerSeedHash,
|
||
ItemsRoot: commit.ItemsRoot,
|
||
WeightsTotal: commit.WeightsTotal,
|
||
StateVersion: commit.StateVersion,
|
||
})
|
||
}
|
||
return result, nil
|
||
}
|