package strategy import ( "bindbox-game/internal/repository/mysql/dao" "bindbox-game/internal/repository/mysql/model" "context" "encoding/json" "fmt" "time" ) type ActivityDrawStrategy interface { PreChecks(ctx context.Context, activityID int64, issueID int64, userID int64) error SelectItem(ctx context.Context, activityID int64, issueID int64, userID int64) (int64, map[string]any, error) GrantReward(ctx context.Context, userID int64, rewardID int64) error PostEffects(ctx context.Context, userID int64, activityID int64, issueID int64, rewardID int64) error } // SaveDrawReceipt 保存抽奖凭据(用户可验证信息) // drawLogID: ActivityDrawLogs 记录的 ID // issueID: 期ID // userID: 用户ID // proof: SelectItem 返回的 proof map // w: 写库 dao.Query func SaveDrawReceipt(ctx context.Context, w *dao.Query, drawLogID int64, issueID int64, userID int64, proof map[string]any) error { if proof == nil || drawLogID <= 0 { return nil } // 从 proof 中提取数据 seedHash := "" if v, ok := proof["seed_hash"].(string); ok { seedHash = v } weightsTotal := int64(0) if v, ok := proof["weights_total"].(int64); ok { weightsTotal = v } randVal := int64(0) if v, ok := proof["rand"].(int64); ok { randVal = v } slotIndex := int32(-1) if v, ok := proof["slot_index"].(int64); ok { slotIndex = int32(v) } totalSlots := int64(0) if v, ok := proof["total_slots"].(int64); ok { totalSlots = v weightsTotal = totalSlots // 一番赏用 total_slots 替代 weights_total } // 序列化 proof 作为 items_snapshot proofJSON, _ := json.Marshal(proof) receipt := &model.ActivityDrawReceipts{ CreatedAt: time.Now(), DrawLogID: drawLogID, AlgoVersion: "HMAC-SHA256-v1", RoundID: issueID, DrawID: drawLogID, ClientID: userID, Timestamp: time.Now().UnixMilli(), ServerSeedHash: seedHash, ServerSubSeed: "", ClientSeed: "", Nonce: time.Now().UnixNano(), ItemsRoot: "", WeightsTotal: weightsTotal, SelectedIndex: slotIndex, RandProof: fmt.Sprintf("%x", randVal), Signature: "", ItemsSnapshot: string(proofJSON), } return w.ActivityDrawReceipts.WithContext(ctx).Create(receipt) }