bindbox-game/internal/service/activity/activity_commitment_service.go
邹方成 45815bfb7d chore: 清理无用文件与优化代码结构
refactor(utils): 修复密码哈希比较逻辑错误
feat(user): 新增按状态筛选优惠券接口
docs: 添加虚拟发货与任务中心相关文档
fix(wechat): 修正Code2Session上下文传递问题
test: 补充订单折扣与积分转换测试用例
build: 更新配置文件与构建脚本
style: 清理多余的空行与注释
2025-12-18 17:35:55 +08:00

104 lines
3.4 KiB
Go

package activity
import (
"bindbox-game/internal/repository/mysql"
"bindbox-game/internal/repository/mysql/dao"
"context"
"crypto/rand"
"crypto/sha256"
"errors"
)
type ActivityCommitmentService struct {
read *dao.Query
write *dao.Query
repo mysql.Repo
}
func NewActivityCommitmentService(read *dao.Query, write *dao.Query, repo mysql.Repo) *ActivityCommitmentService {
return &ActivityCommitmentService{read: read, write: write, repo: repo}
}
func (s *ActivityCommitmentService) Generate(ctx context.Context, activityID int64) (int32, error) {
act, err := s.read.Activities.WithContext(ctx).Where(s.read.Activities.ID.Eq(activityID)).First()
if err != nil || act == nil {
return 0, errors.New("activity not found")
}
var cur int32
_ = s.repo.GetDbR().Raw("SELECT IFNULL(commitment_state_version,0) FROM activities WHERE id=?", activityID).Scan(&cur)
seed := make([]byte, 32)
_, _ = rand.Read(seed)
seedHash := sha256.Sum256(seed)
// compute items_root by aggregating all issues' reward slots
issueIDs := make([]int64, 0)
issues, _ := s.read.ActivityIssues.WithContext(ctx).ReadDB().Where(s.read.ActivityIssues.ActivityID.Eq(activityID)).Find()
for _, is := range issues {
issueIDs = append(issueIDs, is.ID)
}
var itemsRoot []byte
if len(issueIDs) > 0 {
// fetch rewards per issue and build slots
slots := make([]int64, 0)
for _, iid := range issueIDs {
rs, _ := s.read.ActivityRewardSettings.WithContext(ctx).ReadDB().Where(s.read.ActivityRewardSettings.IssueID.Eq(iid)).Order(s.read.ActivityRewardSettings.Sort.Asc()).Find()
for _, r := range rs {
for i := int64(0); i < r.OriginalQty; i++ {
slots = append(slots, r.ID)
}
}
}
if len(slots) > 0 {
// naive JSON encoding to compute root
// Note: avoid importing encoding/json at top, compute hash over formatted bytes
// but simpler: use []byte with little-endian concatenation
h := sha256.New()
for _, v := range slots {
var buf [8]byte
// big endian
buf[0] = byte(v >> 56)
buf[1] = byte(v >> 48)
buf[2] = byte(v >> 40)
buf[3] = byte(v >> 32)
buf[4] = byte(v >> 24)
buf[5] = byte(v >> 16)
buf[6] = byte(v >> 8)
buf[7] = byte(v)
h.Write(buf[:])
}
itemsRoot = h.Sum(nil)
}
}
ver := cur + 1
_, err = s.write.Activities.WithContext(ctx).Where(s.write.Activities.ID.Eq(act.ID)).Updates(map[string]any{
"commitment_algo": "commit-v1",
"commitment_seed_master": seed,
"commitment_seed_hash": seedHash[:],
"commitment_state_version": ver,
"commitment_items_root": itemsRoot,
})
if err != nil {
return 0, err
}
return ver, nil
}
type ActivityCommitmentSummary struct {
SeedVersion int32
Algo string
HasSeed bool
ItemsRoot []byte
}
func (s *ActivityCommitmentService) Summary(ctx context.Context, activityID int64) (ActivityCommitmentSummary, error) {
var ver int32
var algo string
var hasSeed bool
// read version
_ = s.repo.GetDbR().Raw("SELECT IFNULL(commitment_state_version,0) FROM activities WHERE id=?", activityID).Scan(&ver)
_ = s.repo.GetDbR().Raw("SELECT commitment_algo FROM activities WHERE id=?", activityID).Scan(&algo)
var seedLen *int
_ = s.repo.GetDbR().Raw("SELECT LENGTH(commitment_seed_master) FROM activities WHERE id=?", activityID).Scan(&seedLen)
hasSeed = seedLen != nil && *seedLen > 0
return ActivityCommitmentSummary{SeedVersion: ver, Algo: algo, HasSeed: hasSeed, ItemsRoot: nil}, nil
}