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并发之RWMutex的源码解析详解

    Go并发之RWMutex的源码解析详解

    RWMutex是一个支持并行读串行写的读写锁。RWMutex具有写操作优先的特点,写操作发生时,仅允许正在执行的读操作执行,后续的读操作都会被阻塞。本文就来从源码解析一下RWMutex的使用
    2023-03-03
  • Go1.20最新资讯go arena手动管理内存鸽了

    Go1.20最新资讯go arena手动管理内存鸽了

    由于过于繁杂,Go 核心团队成员@Ian Lance Taylor,也表态:目前尚未做出任何决定,也不可能在短期内做出任何决定,可以认为这个提案基本鸽了,今天这篇文章就是给大家同步目前的情况
    2023-11-11
  • go语言实现聊天服务器的示例代码

    go语言实现聊天服务器的示例代码

    这篇文章主要介绍了go语言实现聊天服务器的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • 详解Golang中文件系统事件监听

    详解Golang中文件系统事件监听

    文件系统事件是指文件系统相关的各种操作和状态变化,当一个应用层的进程操作文件或目录时,会触发system call,内核的notification子系统可以守在那里,把该进程对文件的操作上报给应用层的监听进程,这篇文章主要介绍了Golang之文件系统事件监听,需要的朋友可以参考下
    2024-01-01
  • Golang使用gin模板渲染base64图片出现#ZgotmplZ的解决办法

    Golang使用gin模板渲染base64图片出现#ZgotmplZ的解决办法

    这篇文章主要介绍了Golang使用gin模板渲染base64图片出现#ZgotmplZ的的场景复现和解决办法,文中通过代码示例讲解的非常详细,对大家解决问题有一定的帮助,需要的朋友可以参考下
    2024-05-05
  • Golang中的Unicode与字符串示例详解

    Golang中的Unicode与字符串示例详解

    这篇文章主要给大家介绍了关于Golang中Unicode与字符串的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Golang具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2020-05-05
  • go语言go func(){select{}}()的用法

    go语言go func(){select{}}()的用法

    本文主要介绍了go语言go func(){select{}}()的用法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-02-02
  • Go1.18新特性工作区模糊测试及泛型的使用详解

    Go1.18新特性工作区模糊测试及泛型的使用详解

    这篇文章主要为大家介绍了Go 1.18新特性中的工作区 模糊测试 泛型使用进行详细讲解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Go语言什么时候该使用指针

    Go语言什么时候该使用指针

    本文主要介绍了Go语言什么情况下应该使用指针,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • Go 中 slice 的 In 功能实现探索

    Go 中 slice 的 In 功能实现探索

    这篇文章主要介绍了Go 中 slice 的 In 功能实现探索,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-09-09

最新评论