体育直播系统趣猜功能开发技术实现方案

功能概述

趣猜功能是"东莞梦幻网络科技"体育直播系统源码中的互动功能,主播可以发起竞猜题目,观众使用虚拟货币进行投注,增加直播间的互动性和趣味性。所有货币均为虚拟货币,通过系统活动获取,不可充值提现。

数据库设计 (MySQL)

php 复制代码
-- 趣猜表
CREATE TABLE `live_quiz` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `live_id` int(11) NOT NULL COMMENT '直播间ID',
  `anchor_id` int(11) NOT NULL COMMENT '主播ID',
  `title` varchar(255) NOT NULL COMMENT '趣猜主题',
  `option_a` varchar(100) NOT NULL COMMENT '选项A',
  `option_b` varchar(100) NOT NULL COMMENT '选项B',
  `odds_a` decimal(5,2) NOT NULL DEFAULT '1.00' COMMENT 'A选项赔率',
  `odds_b` decimal(5,2) NOT NULL DEFAULT '1.00' COMMENT 'B选项赔率',
  `end_time` int(11) NOT NULL COMMENT '截止时间',
  `result` tinyint(1) DEFAULT NULL COMMENT '结果:0-A赢,1-B赢,NULL-未开奖',
  `status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '状态:0-进行中,1-已结束,2-已开奖',
  `create_time` int(11) NOT NULL,
  `update_time` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `live_id` (`live_id`),
  KEY `anchor_id` (`anchor_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='直播间趣猜表';

-- 用户投注表
CREATE TABLE `live_quiz_bet` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `quiz_id` int(11) NOT NULL COMMENT '趣猜ID',
  `user_id` int(11) NOT NULL COMMENT '用户ID',
  `option` tinyint(1) NOT NULL COMMENT '投注选项:0-A,1-B',
  `amount` int(11) NOT NULL COMMENT '投注金额',
  `potential_win` int(11) NOT NULL COMMENT '潜在收益',
  `is_win` tinyint(1) DEFAULT NULL COMMENT '是否赢:0-输,1-赢,NULL-未开奖',
  `win_amount` int(11) DEFAULT NULL COMMENT '实际赢取金额',
  `create_time` int(11) NOT NULL,
  `update_time` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `quiz_id` (`quiz_id`),
  KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户投注表';

-- 用户虚拟货币表
CREATE TABLE `user_virtual_currency` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `balance` int(11) NOT NULL DEFAULT '0' COMMENT '余额',
  `total_earn` int(11) NOT NULL DEFAULT '0' COMMENT '累计获得',
  `total_spend` int(11) NOT NULL DEFAULT '0' COMMENT '累计消费',
  `create_time` int(11) NOT NULL,
  `update_time` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户虚拟货币表';

PHP后端实现 (ThinkPHP)

控制器 LiveQuizController.php

php 复制代码
<?php
namespace app\api\controller;

use think\Controller;
use think\Request;
use app\common\model\LiveQuiz;
use app\common\model\LiveQuizBet;
use app\common\model\UserVirtualCurrency;

class LiveQuizController extends Controller
{
    // 主播发起趣猜
    public function create(Request $request)
    {
        $user = $request->user;
        $data = $request->only(['live_id', 'title', 'option_a', 'option_b', 'odds_a', 'odds_b', 'end_time']);
        
        // 验证数据
        $validate = new \think\Validate([
            'live_id' => 'require|number',
            'title' => 'require|max:255',
            'option_a' => 'require|max:100',
            'option_b' => 'require|max:100',
            'odds_a' => 'require|float|>:0',
            'odds_b' => 'require|float|>:0',
            'end_time' => 'require|number|>:time'
        ]);
        
        if (!$validate->check($data)) {
            return json(['code' => 400, 'msg' => $validate->getError()]);
        }
        
        $data['anchor_id'] = $user->id;
        $data['create_time'] = time();
        $data['update_time'] = time();
        
        $quiz = LiveQuiz::create($data);
        
        // 广播消息到直播间
        $this->broadcastQuizCreate($quiz);
        
        return json(['code' => 200, 'msg' => '趣猜创建成功', 'data' => $quiz]);
    }
    
    // 用户投注
    public function bet(Request $request)
    {
        $user = $request->user;
        $data = $request->only(['quiz_id', 'option', 'amount']);
        
        // 验证数据
        $validate = new \think\Validate([
            'quiz_id' => 'require|number',
            'option' => 'require|in:0,1',
            'amount' => 'require|number|>:0'
        ]);
        
        if (!$validate->check($data)) {
            return json(['code' => 400, 'msg' => $validate->getError()]);
        }
        
        // 检查趣猜是否存在且可投注
        $quiz = LiveQuiz::where('id', $data['quiz_id'])
            ->where('status', 0)
            ->where('end_time', '>', time())
            ->find();
            
        if (!$quiz) {
            return json(['code' => 400, 'msg' => '该趣猜已结束或不存在']);
        }
        
        // 检查用户余额
        $currency = UserVirtualCurrency::where('user_id', $user->id)->find();
        if (!$currency || $currency->balance < $data['amount']) {
            return json(['code' => 400, 'msg' => '虚拟货币不足']);
        }
        
        // 计算潜在收益
        $odds = $data['option'] == 0 ? $quiz->odds_a : $quiz->odds_b;
        $potential_win = floor($data['amount'] * $odds);
        
        // 开始事务
        Db::startTrans();
        try {
            // 扣除用户余额
            UserVirtualCurrency::where('user_id', $user->id)
                ->update([
                    'balance' => Db::raw('balance-'.$data['amount']),
                    'total_spend' => Db::raw('total_spend+'.$data['amount']),
                    'update_time' => time()
                ]);
                
            // 创建投注记录
            $bet = LiveQuizBet::create([
                'quiz_id' => $data['quiz_id'],
                'user_id' => $user->id,
                'option' => $data['option'],
                'amount' => $data['amount'],
                'potential_win' => $potential_win,
                'create_time' => time(),
                'update_time' => time()
            ]);
            
            // 广播投注消息到直播间
            $this->broadcastBet($quiz->live_id, [
                'user_id' => $user->id,
                'nickname' => $user->nickname,
                'option' => $data['option'],
                'amount' => $data['amount']
            ]);
            
            Db::commit();
            return json(['code' => 200, 'msg' => '投注成功', 'data' => $bet]);
        } catch (\Exception $e) {
            Db::rollback();
            return json(['code' => 500, 'msg' => '投注失败:'.$e->getMessage()]);
        }
    }
    
    // 主播开奖
    public function settle(Request $request)
    {
        $user = $request->user;
        $quiz_id = $request->param('quiz_id');
        $result = $request->param('result');
        
        // 验证数据
        if (!in_array($result, [0, 1])) {
            return json(['code' => 400, 'msg' => '无效的结果']);
        }
        
        // 检查趣猜是否存在且可开奖
        $quiz = LiveQuiz::where('id', $quiz_id)
            ->where('anchor_id', $user->id)
            ->where('status', 0)
            ->where('end_time', '<', time())
            ->find();
            
        if (!$quiz) {
            return json(['code' => 400, 'msg' => '该趣猜不能开奖']);
        }
        
        // 开始事务
        Db::startTrans();
        try {
            // 更新趣猜结果
            $quiz->result = $result;
            $quiz->status = 2;
            $quiz->update_time = time();
            $quiz->save();
            
            // 获取所有赢的投注
            $winBets = LiveQuizBet::where('quiz_id', $quiz_id)
                ->where('option', $result)
                ->select();
                
            // 发放奖励
            foreach ($winBets as $bet) {
                $winAmount = $bet->potential_win;
                
                // 更新投注记录
                $bet->is_win = 1;
                $bet->win_amount = $winAmount;
                $bet->update_time = time();
                $bet->save();
                
                // 增加用户余额
                UserVirtualCurrency::where('user_id', $bet->user_id)
                    ->update([
                        'balance' => Db::raw('balance+'.$winAmount),
                        'total_earn' => Db::raw('total_earn+'.$winAmount),
                        'update_time' => time()
                    ]);
            }
            
            // 更新输的投注
            LiveQuizBet::where('quiz_id', $quiz_id)
                ->where('option', $result == 0 ? 1 : 0)
                ->update([
                    'is_win' => 0,
                    'win_amount' => 0,
                    'update_time' => time()
                ]);
            
            // 广播开奖消息到直播间
            $this->broadcastSettle($quiz->live_id, [
                'quiz_id' => $quiz->id,
                'result' => $result,
                'option_a' => $quiz->option_a,
                'option_b' => $quiz->option_b
            ]);
            
            Db::commit();
            return json(['code' => 200, 'msg' => '开奖成功']);
        } catch (\Exception $e) {
            Db::rollback();
            return json(['code' => 500, 'msg' => '开奖失败:'.$e->getMessage()]);
        }
    }
    
    // 获取趣猜列表
    public function list(Request $request)
    {
        $live_id = $request->param('live_id');
        $status = $request->param('status', 0);
        
        $list = LiveQuiz::where('live_id', $live_id)
            ->where('status', $status)
            ->order('create_time', 'desc')
            ->select();
            
        return json(['code' => 200, 'msg' => 'success', 'data' => $list]);
    }
    
    // 获取趣猜详情
    public function detail(Request $request)
    {
        $quiz_id = $request->param('quiz_id');
        $user_id = $request->user->id;
        
        $quiz = LiveQuiz::find($quiz_id);
        if (!$quiz) {
            return json(['code' => 404, 'msg' => '趣猜不存在']);
        }
        
        // 获取投注统计
        $betStats = LiveQuizBet::where('quiz_id', $quiz_id)
            ->field('option, count(*) as bet_count, sum(amount) as total_amount')
            ->group('option')
            ->select();
            
        $stats = [
            'option_a' => ['bet_count' => 0, 'total_amount' => 0],
            'option_b' => ['bet_count' => 0, 'total_amount' => 0]
        ];
        
        foreach ($betStats as $stat) {
            if ($stat['option'] == 0) {
                $stats['option_a'] = [
                    'bet_count' => $stat['bet_count'],
                    'total_amount' => $stat['total_amount']
                ];
            } else {
                $stats['option_b'] = [
                    'bet_count' => $stat['bet_count'],
                    'total_amount' => $stat['total_amount']
                ];
            }
        }
        
        // 获取用户投注
        $userBet = LiveQuizBet::where('quiz_id', $quiz_id)
            ->where('user_id', $user_id)
            ->find();
            
        $quiz->stats = $stats;
        $quiz->user_bet = $userBet;
        
        return json(['code' => 200, 'msg' => 'success', 'data' => $quiz]);
    }
    
    // 广播消息方法
    private function broadcastQuizCreate($quiz)
    {
        // 这里实现WebSocket或其它方式的消息广播
        // 实际项目中可以使用Swoole、Workerman或第三方推送服务
    }
    
    private function broadcastBet($live_id, $data)
    {
        // 广播投注消息
    }
    
    private function broadcastSettle($live_id, $data)
    {
        // 广播开奖消息
    }
}

前端Vue.js实现

主播端组件 AnchorQuizPanel.vue

php 复制代码
<template>
  <div class="quiz-panel">
    <h3>发起趣猜</h3>
    <el-form :model="quizForm" :rules="rules" ref="quizForm" label-width="100px">
      <el-form-item label="趣猜主题" prop="title">
        <el-input v-model="quizForm.title" placeholder="例如:本场比赛哪队会获胜?"></el-input>
      </el-form-item>
      <el-form-item label="选项A" prop="option_a">
        <el-input v-model="quizForm.option_a" placeholder="例如:主队"></el-input>
      </el-form-item>
      <el-form-item label="选项B" prop="option_b">
        <el-input v-model="quizForm.option_b" placeholder="例如:客队"></el-input>
      </el-form-item>
      <el-form-item label="赔率A" prop="odds_a">
        <el-input-number v-model="quizForm.odds_a" :min="1" :step="0.1" :precision="2"></el-input-number>
      </el-form-item>
      <el-form-item label="赔率B" prop="odds_b">
        <el-input-number v-model="quizForm.odds_b" :min="1" :step="0.1" :precision="2"></el-input-number>
      </el-form-item>
      <el-form-item label="截止时间" prop="end_time">
        <el-date-picker
          v-model="quizForm.end_time"
          type="datetime"
          placeholder="选择截止时间"
          :picker-options="pickerOptions">
        </el-date-picker>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="submitQuiz">发起趣猜</el-button>
      </el-form-item>
    </el-form>

    <div class="active-quiz-list" v-if="activeQuizzes.length > 0">
      <h3>进行中的趣猜</h3>
      <div class="quiz-item" v-for="quiz in activeQuizzes" :key="quiz.id">
        <div class="quiz-title">{{ quiz.title }}</div>
        <div class="quiz-options">
          <span class="option-a">{{ quiz.option_a }} (赔率:{{ quiz.odds_a }})</span>
          <span class="vs">VS</span>
          <span class="option-b">{{ quiz.option_b }} (赔率:{{ quiz.odds_b }})</span>
        </div>
        <div class="quiz-endtime">截止时间: {{ formatTime(quiz.end_time) }}</div>
        <div class="quiz-stats">
          <span>A: {{ quiz.stats.option_a.bet_count }}人投注, {{ quiz.stats.option_a.total_amount }}币</span>
          <span>B: {{ quiz.stats.option_b.bet_count }}人投注, {{ quiz.stats.option_b.total_amount }}币</span>
        </div>
        <el-button 
          type="success" 
          size="small" 
          @click="settleQuiz(quiz.id, 0)"
          :disabled="quiz.end_time > (Date.now()/1000)">
          开奖: {{ quiz.option_a }}
        </el-button>
        <el-button 
          type="danger" 
          size="small" 
          @click="settleQuiz(quiz.id, 1)"
          :disabled="quiz.end_time > (Date.now()/1000)">
          开奖: {{ quiz.option_b }}
        </el-button>
      </div>
    </div>
  </div>
</template>

<script>
import { createQuiz, settleQuiz, getActiveQuizzes } from '@/api/liveQuiz';

export default {
  props: {
    liveId: {
      type: Number,
      required: true
    }
  },
  data() {
    return {
      quizForm: {
        live_id: this.liveId,
        title: '',
        option_a: '',
        option_b: '',
        odds_a: 1.8,
        odds_b: 1.8,
        end_time: new Date(Date.now() + 30 * 60 * 1000) // 默认30分钟后截止
      },
      rules: {
        title: [{ required: true, message: '请输入趣猜主题', trigger: 'blur' }],
        option_a: [{ required: true, message: '请输入选项A', trigger: 'blur' }],

相关推荐
feiyangqingyun9 分钟前
基于Qt和FFmpeg的安卓监控模拟器/手机摄像头模拟成onvif和28181设备
android·qt·ffmpeg
用户2018792831674 小时前
ANR之RenderThread不可中断睡眠state=D
android
煤球王子4 小时前
简单学:Android14中的Bluetooth—PBAP下载
android
小趴菜82275 小时前
安卓接入Max广告源
android
齊家治國平天下5 小时前
Android 14 系统 ANR (Application Not Responding) 深度分析与解决指南
android·anr
ZHANG13HAO5 小时前
Android 13.0 Framework 实现应用通知使用权默认开启的技术指南
android
【ql君】qlexcel5 小时前
Android 安卓RIL介绍
android·安卓·ril
写点啥呢5 小时前
android12解决非CarProperty接口深色模式设置后开机无法保持
android·车机·aosp·深色模式·座舱
IT酷盖5 小时前
Android解决隐藏依赖冲突
android·前端·vue.js
努力学习的小廉6 小时前
初识MYSQL —— 数据库基础
android·数据库·mysql