编程 Go-Zero实战:抽奖算法的设计与实现

2024-11-18 18:12:42 +0800 CST views 689

Go-Zero实战:抽奖算法的设计与实现

本文将继续探索 Go-Zero 的实践应用,并介绍如何使用 Go-Zero 框架实现一个抽奖算法。通过实例代码,详细演示如何设计与实现一个高效、可靠的抽奖算法,并探讨相关实现方法和最佳实践。

抽奖算法是许多应用中常见的功能,通过规则和概率从参与者中选出中奖者,并分配奖品。本文将介绍如何在 Go-Zero 中构建这一功能,并涵盖策略模式的运用、RPC 服务的定义和数据表的设置。

实战前准备

数据库表结构

在实现抽奖算法前,需要准备一些数据库表来存储抽奖活动、奖品以及参与者信息。

lottery

DROP TABLE IF EXISTS `lottery`;
CREATE TABLE `lottery` (
  `id` int NOT NULL AUTO_INCREMENT,
  `user_id` int NOT NULL DEFAULT 0 COMMENT '发起抽奖用户ID',
  `name` varchar(50) NOT NULL DEFAULT '' COMMENT '默认取一等奖名称',
  `thumb` varchar(255) NOT NULL DEFAULT '' COMMENT '默认取一等奖配图',
  `publish_time` datetime NULL DEFAULT NULL COMMENT '发布抽奖时间',
  `join_number` int NOT NULL DEFAULT 0 COMMENT '自动开奖人数',
  `introduce` varchar(255) NOT NULL DEFAULT '' COMMENT '抽奖说明',
  `award_deadline` datetime NOT NULL COMMENT '领奖截止时间',
  `is_selected` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否精选 1是 0否',
  `announce_type` tinyint(1) NOT NULL DEFAULT 0 COMMENT '开奖设置:1按时间开奖 2按人数开奖 3即抽即中',
  `announce_time` datetime NOT NULL DEFAULT NULL COMMENT '开奖时间',
  `is_announced` tinyint(1) NULL DEFAULT 0 COMMENT '是否开奖:0未开奖;1已经开奖',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=111 DEFAULT CHARSET=utf8mb4;

prize

DROP TABLE IF EXISTS `prize`;
CREATE TABLE `prize` (
  `id` int NOT NULL AUTO_INCREMENT,
  `lottery_id` int NOT NULL DEFAULT 0 COMMENT '抽奖ID',
  `type` tinyint(1) NOT NULL DEFAULT 0 COMMENT '奖品类型',
  `name` varchar(24) NOT NULL DEFAULT '' COMMENT '奖品名称',
  `level` int NOT NULL DEFAULT 1 COMMENT '几等奖',
  `thumb` varchar(255) NOT NULL DEFAULT '' COMMENT '奖品图片',
  `count` int NOT NULL DEFAULT 0 COMMENT '奖品份数',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8mb4;

lottery_participation

CREATE TABLE lottery_participation (
  id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键',
  lottery_id INT NOT NULL COMMENT '参与的抽奖ID',
  user_id INT NOT NULL COMMENT '用户ID',
  is_won TINYINT NOT NULL COMMENT '中奖状态',
  prize_id BIGINT NOT NULL COMMENT '奖品ID',
  create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) COMMENT '参与抽奖表';

抽奖算法设计思路

抽奖策略

我们将实现两种抽奖策略:

  1. 基于时间的抽奖策略:根据设定的开奖时间进行抽奖。
  2. 基于人数的抽奖策略:根据参与人数达到一定数量后进行开奖。

使用策略模式

策略模式是一种行为设计模式,允许在运行时选择不同的算法进行处理。在抽奖算法中,我们可以通过策略模式来选择不同的开奖规则,从而提高代码的灵活性和可扩展性。

实现步骤

步骤一:定义抽奖策略接口

type LotteryStrategy interface {
    Run() error
}

步骤二:定义具体策略

我们定义了两个策略:

  • TimeLotteryStrategy: 基于时间的抽奖策略。
  • PeopleLotteryStrategy: 基于人数的抽奖策略。
type TimeLotteryStrategy struct {
    *AnnounceLotteryLogic
    CurrentTime time.Time
}

type PeopleLotteryStrategy struct {
    *AnnounceLotteryLogic
    CurrentTime time.Time
}

步骤三:选择策略并执行

在主业务逻辑中根据传入的抽奖类型选择对应的策略。

func (l *AnnounceLotteryLogic) AnnounceLottery(in *pb.AnnounceLotteryReq) (*pb.AnnounceLotteryResp, error) {
    var strategy LotteryStrategy
    switch in.AnnounceType {
    case constants.AnnounceTypeTimeLottery:
        strategy = &TimeLotteryStrategy{
            AnnounceLotteryLogic: l,
            CurrentTime:          time.Now(),
        }
    case constants.AnnounceTypePeopleLottery:
        strategy = &PeopleLotteryStrategy{
            AnnounceLotteryLogic: l,
            CurrentTime:          time.Now(),
        }
    }
    err := strategy.Run()
    if err != nil {
        return nil, errors.Wrapf(xerr.NewErrCode(xerr.AnnounceLottery_ERROR), "Strategy run error: %v", err)
    }
    return &pb.AnnounceLotteryResp{}, nil
}

步骤四:实现抽奖逻辑

随机选择中奖者并分配奖品

func (l *AnnounceLotteryLogic) DrawLottery(lotteryId int64, prizes []*model.Prize, participants []int64) ([]Winner, error) {
    rand.New(rand.NewSource(time.Now().UnixNano()))

    // 计算中奖人数
    var totalWinners int64
    for _, p := range prizes {
        totalWinners += p.Count
    }

    winners := make([]Winner, 0)
    // 计算每个参与者的中奖概率并随机选择中奖者
    for i := 0; i < int(totalWinners); i++ {
        // 抽奖逻辑...
        winner := Winner{
            LotteryId: lotteryId,
            UserId:    participants[randomIndex],
            PrizeId:   prizeId,
        }
        winners = append(winners, winner)
    }
    return winners, nil
}

基于时间的抽奖逻辑

func (s *TimeLotteryStrategy) Run() error {
    lotteries, err := s.svcCtx.LotteryModel.GetLotteriesByTime(s.ctx, s.CurrentTime)
    if err != nil {
        return err
    }

    for _, lottery := range lotteries {
        err := s.announceLottery(lottery)
        if err != nil {
            return err
        }
    }
    return nil
}

基于人数的抽奖逻辑

func (s *PeopleLotteryStrategy) Run() error {
    lotteries, err := s.svcCtx.LotteryModel.GetLotteriesByParticipants(s.ctx)
    if err != nil {
        return err
    }

    for _, lottery := range lotteries {
        err := s.announceLottery(lottery)
        if err != nil {
            return err
        }
    }
    return nil
}

步骤五:RPC 服务定义

定义 RPC 请求和响应消息。

message AnnounceLotteryReq {
    int64 AnnounceType = 1;
}

message AnnounceLotteryResp {}

service LotteryService {
    rpc AnnounceLottery (AnnounceLotteryReq) returns (AnnounceLotteryResp);
}

生成 RPC 代码:

goctl rpc protoc lottery.proto --go_out=./ --go-grpc_out=./ --zrpc_out=./ --style=goZero

步骤六:通知中奖者

我们可以通过 NotifyParticipators 方法通知中奖者结果。

func (l *AnnounceLotteryLogic) NotifyParticipators(participants []int64, lotteryId int64) error {
    _, err := l.svcCtx.NoticeRpc.NoticeLotteryDraw(l.ctx, &pb.NoticeLotteryDrawReq{
        LotteryId: lotteryId,
        UserIds:   participants,
    })
    return err
}

总结

本文详细介绍了如何使用 Go-Zero 框架实现一个抽奖算法,通过使用策略模式解决不同抽奖逻辑的实现,并结合 RPC 服务实现了抽奖结果的通知。希望通过这一实战,你能够更好地理解 Go-Zero 的使用,并将其应用到其他项目中。

复制全文 生成海报 编程 算法 框架 数据库 服务

推荐文章

Vue3中的响应式原理是什么?
2024-11-19 09:43:12 +0800 CST
jQuery `$.extend()` 用法总结
2024-11-19 02:12:45 +0800 CST
在 Nginx 中保存并记录 POST 数据
2024-11-19 06:54:06 +0800 CST
小技巧vscode去除空格方法
2024-11-17 05:00:30 +0800 CST
html流光登陆页面
2024-11-18 15:36:18 +0800 CST
设置mysql支持emoji表情
2024-11-17 04:59:45 +0800 CST
WebSocket在消息推送中的应用代码
2024-11-18 21:46:05 +0800 CST
在JavaScript中实现队列
2024-11-19 01:38:36 +0800 CST
使用Python实现邮件自动化
2024-11-18 20:18:14 +0800 CST
支付宝批量转账
2024-11-18 20:26:17 +0800 CST
JavaScript设计模式:装饰器模式
2024-11-19 06:05:51 +0800 CST
Vue3中的虚拟滚动有哪些改进?
2024-11-18 23:58:18 +0800 CST
向满屏的 Import 语句说再见!
2024-11-18 12:20:51 +0800 CST
程序员茄子在线接单