Go语言实现权重抽奖系统的项目实践

 更新时间:2025年04月17日 11:06:49   作者:程序智享家  
本文主要介绍了Go语言实现权重抽奖系统的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

需求描述

  • 支持配置多个奖品及对应权重
  • 保证抽奖结果符合权重概率分布
  • 防止重复中奖
  • 提供抽奖结果验证接口

完整实现代码

package main

import (
    "crypto/rand"
    "encoding/json"
    "fmt"
    "math/big"
    "net/http"
    "sync"
)

// 奖品配置
type Prize struct {
    ID     int    `json:"id"`
    Name   string `json:"name"`
    Weight int    `json:"weight"` // 权重值(非百分比)
}

// 抽奖系统
type LotterySystem struct {
    prizes       []Prize
    totalWeight  int
    issuedPrizes map[int]bool
    mu           sync.Mutex
}

// 初始化抽奖系统
func NewLotterySystem(prizes []Prize) *LotterySystem {
    total := 0
    for _, p := range prizes {
        total += p.Weight
    }
    return &LotterySystem{
        prizes:       prizes,
        totalWeight:  total,
        issuedPrizes: make(map[int]bool),
    }
}

// 安全随机数生成
func secureRandom(max int) (int, error) {
    n, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
    if err != nil {
        return 0, err
    }
    return int(n.Int64()), nil
}

// 执行抽奖
func (ls *LotterySystem) Draw() (*Prize, error) {
    ls.mu.Lock()
    defer ls.mu.Unlock()

    if ls.totalWeight == 0 {
        return nil, fmt.Errorf("no available prizes")
    }

    // 生成随机数
    randomNum, err := secureRandom(ls.totalWeight)
    if err != nil {
        return nil, err
    }

    // 权重选择
    current := 0
    for _, p := range ls.prizes {
        current += p.Weight
        if randomNum < current {
            if ls.issuedPrizes[p.ID] {
                continue // 已发放的奖品跳过
            }
            ls.issuedPrizes[p.ID] = true
            return &p, nil
        }
    }

    return nil, fmt.Errorf("draw failed")
}

// HTTP服务
func main() {
    // 初始化奖品池
    prizes := []Prize{
        {ID: 1, Name: "一等奖", Weight: 1},
        {ID: 2, Name: "二等奖", Weight: 5},
        {ID: 3, Name: "三等奖", Weight: 20},
        {ID: 4, Name: "参与奖", Weight: 74},
    }

    lottery := NewLotterySystem(prizes)

    http.HandleFunc("/draw", func(w http.ResponseWriter, r *http.Request) {
        prize, err := lottery.Draw()
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }

        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(prize)
    })

    fmt.Println("抽奖服务已启动,监听端口 8080")
    http.ListenAndServe(":8080", nil)
}

核心功能说明

权重算法:

// 权重选择逻辑
current := 0
for _, p := range ls.prizes {
    current += p.Weight
    if randomNum < current {
        return &p
    }
}
  • 使用累计权重区间算法
  • 保证概率分布准确性

安全随机数:

// 使用crypto/rand生成安全随机数
func secureRandom(max int) (int, error) {
    n, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
    // ...
}
  • 避免使用math/rand的可预测性
  • 满足安全抽奖需求

并发控制:

var mu sync.Mutex

func (ls *LotterySystem) Draw() {
    ls.mu.Lock()
    defer ls.mu.Unlock()
    // ...
}
  • 使用互斥锁保证线程安全
  • 防止并发抽奖导致的数据竞争

防重复机制:

issuedPrizes map[int]bool
  • 使用内存映射记录已发放奖品
  • 生产环境可替换为Redis等持久化存储

扩展功能建议

概率可视化验证:

// 添加测试端点验证概率分布
http.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
    results := make(map[int]int)
    for i := 0; i < 10000; i++ {
        tempLottery := NewLotterySystem(prizes)
        prize, _ := tempLottery.Draw()
        results[prize.ID]++
    }
    json.NewEncoder(w).Encode(results)
})

分布式锁扩展:

// 使用Redis分布式锁
func (ls *LotterySystem) DistributedDraw() {
    lock := redis.NewLock("lottery_lock")
    err := lock.Lock()
    // ...抽奖逻辑...
    lock.Unlock()
}

奖品库存管理:

type Prize struct {
    // ...
    Stock     int // 新增库存字段
}
func (ls *LotterySystem) Draw() {
    // 检查库存
    if p.Stock <= 0 {
        continue
    }
    // 扣减库存
    p.Stock--
}

运行测试

启动服务:

go run main.go

测试抽奖:

curl http://localhost:8080/draw
# 示例返回:{"id":3,"name":"三等奖","weight":20}

概率验证测试:

curl http://localhost:8080/test
# 返回万次抽奖结果分布

关键优化点

性能优化:

  • 使用预计算总权重值
  • 内存级锁粒度控制
  • 对象池复用

安全增强:

  • JWT用户身份验证
  • 抽奖频率限制
  • 敏感操作日志

业务扩展:

  • 支持不同抽奖活动
  • 奖品有效期管理
  • 中奖名单公示

到此这篇关于Go语言实现权重抽奖系统的项目实践的文章就介绍到这了,更多相关Go语言 权重抽奖系统内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 一文带你了解Go语言fmt标准库输入函数的使用

    一文带你了解Go语言fmt标准库输入函数的使用

    这篇文章主要为大家详细介绍了Go语言中 fmt 标准库输入函数的使用,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下
    2023-01-01
  • Go使用path/filepath包处理文件路径的完全指南

    Go使用path/filepath包处理文件路径的完全指南

    本文详细介绍了Go语言中path/filepath包的使用,涵盖了路径处理、拼接、规范化、遍历和匹配等功能,适用于文件系统操作的各种场景,需要的朋友可以参考下
    2026-03-03
  • Golang error使用场景介绍

    Golang error使用场景介绍

    我们在使用Golang时,不可避免会遇到异常情况的处理,与Java、Python等语言不同的是,Go中并没有try...catch...这样的语句块,这个时候我们如何才能更好的处理异常呢?本文来教你正确方法
    2023-03-03
  • 基于golang的简单分布式延时队列服务的实现

    基于golang的简单分布式延时队列服务的实现

    这篇文章主要介绍了基于golang的简单分布式延时队列服务的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • golang 实现tcp转发代理的方法

    golang 实现tcp转发代理的方法

    今天小编就为大家分享一篇golang 实现tcp转发代理的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-08-08
  • golang使用接口进行灵活缓存

    golang使用接口进行灵活缓存

    缓存是编程中一种常见的技术,通过存储昂贵的计算或 IO 结果来快速查找,从而提高性能,本文将介绍一下Go的接口如何帮助构建灵活、可扩展的缓存,感兴趣的可以了解下
    2023-09-09
  • Golang 文件操作:删除指定的文件方式

    Golang 文件操作:删除指定的文件方式

    这篇文章主要介绍了Golang 文件操作:删除指定的文件方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Golang 处理浮点数遇到的精度问题(使用decimal)

    Golang 处理浮点数遇到的精度问题(使用decimal)

    本文主要介绍了Golang 处理浮点数遇到的精度问题,不使用decimal会出大问题,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • 如何使用腾讯云go sdk 查询对象存储中最新文件

    如何使用腾讯云go sdk 查询对象存储中最新文件

    这篇文章主要介绍了使用腾讯云go sdk 查询对象存储中最新文件,这包括如何创建COS客户端,如何逐页检索对象列表,并如何对结果排序以找到最后更新的对象,我们还展示了如何优化用户体验,通过实时进度更新和检索多个文件来改进程序,需要的朋友可以参考下
    2024-03-03
  • 聊聊Golang性能分析工具pprof的使用

    聊聊Golang性能分析工具pprof的使用

    对于线上稳定运行的服务来说, 可能会遇到 cpu、mem 利用率升高的问题,那我们就需要使用 pprof 工具来进行性能分析,所以本文就来和大家讲讲pprof的具体使用吧
    2023-05-05

最新评论