概述

  视频打赏系统源码是一套用于实现用户向视频创作者打赏功能的代码集合。其核心功能涵盖用户登录、视频播放及打赏交互,支持跨平台集成。技术上采用前后端分离架构,前端负责界面交互,后端处理业务逻辑与支付接口对接。系统具备易部署、可扩展特性,可快速集成至视频平台,为创作者提供变现途径,增强用户互动与内容生态活力。

        源码演示:s.yunzes.top/ds

  技术架构设计

  后端架构

  采用PHP(ThinkPHP框架)或Node.js(Express/Koa)构建RESTful API。

  数据库使用MySQL(主从复制)+Redis(缓存+会话存储),分库分表策略应对高并发。

  支付接口对接微信支付/支付宝SDK,签名验证确保交易安全。

  前端架构

  移动端使用React Native/Flutter开发,支持iOS/Android双平台。

  Web端使用Vue.js构建单页应用,视频播放器集成HLS.js或flv.js。

  弹幕系统使用Canvas实现,支持自定义样式和动画效果。

  流媒体与传输

  直播推流采用RTMP协议,拉流支持HLS/DASH,使用SRS或Nginx-RTMP模块搭建流媒体服务器。

  视频点播使用CDN加速,支持多码率自适应(ABR)。

  弱网环境下启用P2P加速,降低服务器带宽成本。

  数据库设计

  用户身份认证与权限管理视频流处理与播放器集成打赏业务流程设计实时消息通知系统支付网关对接方案。

  1、核心表结构设计

  用户表(users)

  字段:user_id(主键)、username、phone、avatar、balance(余额)、role(普通用户/创作者)、create_time

  说明:存储用户基本信息及余额,role字段区分创作者与普通用户。

  视频表(videos)

  字段:video_id(主键)、user_id(外键)、title、cover_url、duration、play_count、create_time

  说明:关联创作者ID,记录视频元数据及播放量。

  打赏记录表(rewards)

  字段:reward_id(主键)、user_id(打赏者ID)、video_id(外键)、amount、gift_id(虚拟礼物ID)、message、create_time

  虚拟礼物表(gifts)

  字段:gift_id(主键)、name、price、image_url、effect_type(弹幕/全屏特效)

  说明:存储礼物名称、价格及展示效果类型。

  创作者收益表(income)

  字段:income_id(主键)、user_id(创作者ID)、video_id(外键)、total_amount、status(待结算/已结算)、settle_time

  说明:按创作者和视频聚合收益,支持分账状态管理。

  2、索引优化策略

  高频查询字段加索引

  users.phone(手机号唯一索引,加速登录)

  videos.user_id(按创作者查询视频列表)

  rewards.video_id(统计视频打赏金额)

  rewards.create_time(按时间范围查询打赏记录)

  复合索引设计

  (user_id,create_time):加速用户个人打赏记录分页查询。

  (video_id,amount):按视频和金额排序(如打赏排行榜)。

  3、数据一致性与扩展性设计

  事务处理

  打赏操作需原子性更新用户余额(users.balance)和插入打赏记录(rewards),使用数据库事务或分布式事务框架(如Seata)。

  分表分库策略

  按user_id或video_id哈希分表,避免单表数据量过大。

  关键技术实现

  高并发处理

  1.数据库事务与Redis锁结合(PHP示例)

<?php
declare(strict_types=1);
namespace app\controller;

use think\facade\Db;
use think\facade\Redis;

class RewardController
{
    // 用户打赏接口
    public function reward()
    {
        $userId = $this->request->post('user_id');
        $videoId = $this->request->post('video_id');
        $amount = $this->request->post('amount');
        
        // 生成唯一订单号
        $orderNo = 'REW_' . date('YmdHis') . mt_rand(1000, 9999);
        
        // 获取Redis锁(防止重复打赏)
        $lockKey = "reward_lock:{$userId}_{$videoId}";
        $isLocked = Redis::set($lockKey, 1, ['NX', 'EX' => 10]); // 10秒过期
        
        if (!$isLocked) {
            return json(['code' => 400, 'msg' => '操作频繁,请稍后再试']);
        }
        
        try {
            Db::startTrans();
            
            // 1. 检查用户余额是否充足
            $user = Db::name('users')
                ->where('user_id', $userId)
                ->lock(true) // 悲观锁
                ->find();
            
            if ($user['balance'] < $amount) {
                throw new \Exception('余额不足');
            }
            
            // 2. 扣减用户余额
            Db::name('users')
                ->where('user_id', $userId)
                ->dec('balance', $amount)
                ->update();
            
            // 3. 增加创作者收益
            Db::name('income')
                ->where(['user_id' => $video['user_id'], 'video_id' => $videoId])
                ->inc('total_amount', $amount)
                ->update();
            
            // 4. 记录打赏日志
            Db::name('rewards')->insert([
                'reward_id' => $orderNo,
                'user_id' => $userId,
                'video_id' => $videoId,
                'amount' => $amount,
                'create_time' => date('Y-m-d H:i:s')
            ]);
            
            Db::commit();
            
            // 释放Redis锁
            Redis::del($lockKey);
            
            return json(['code' => 200, 'msg' => '打赏成功']);
        } catch (\Exception $e) {
            Db::rollback();
            Redis::del($lockKey);
            return json(['code' => 500, 'msg' => $e->getMessage()]);
        }
    }
}
?>

  2.异步队列处理(RabbitMQ示例)

<?php
// 发送打赏消息到队列
public function asyncReward()
{
    $data = [
        'user_id' => $this->request->post('user_id'),
        'video_id' => $this->request->post('video_id'),
        'amount' => $this->request->post('amount'),
        'order_no' => 'REW_' . date('YmdHis') . mt_rand(1000, 9999)
    ];
    
    $connection = new \PhpAmqpLib\Connection\AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
    $channel = $connection->channel();
    
    $channel->queue_declare('reward_queue', false, true, false, false);
    
    $msg = new \PhpAmqpLib\Message\AMQPMessage(
        json_encode($data),
        ['delivery_mode' => \PhpAmqpLib\Message\AMQPMessage::DELIVERY_MODE_PERSISTENT]
    );
    
    $channel->basic_publish($msg, '', 'reward_queue');
    $channel->close();
    $connection->close();
    
    return json(['code' => 200, 'msg' => '打赏请求已提交']);
}

// 消费者处理打赏逻辑
// 此部分通常运行在独立进程中,通过命令行启动

  关键点说明

  Redis分布式锁:防止用户重复打赏,通过SET NX EX命令实现。

  数据库悲观锁:在查询用户余额时使用FOR UPDATE(ThinkPHP中通过lock(true)实现),确保余额检查和扣减的原子性。

  异步队列:将打赏操作放入队列,由消费者异步处理,避免同步阻塞。

  事务处理:确保余额扣减、收益增加和日志记录的原子性。

  数据一致性

  1.分布式锁+数据库事务(强一致性场景)

  2.最终一致性方案(异步补偿)

  当强一致性要求不高时,可采用异步队列+定时任务对账保障最终一致性。

  异步处理代码(生产者):

public function asyncReward()
{
    $data = [
        'user_id' => (int)$this->request->post('user_id'),
        'video_id' => (int)$this->request->post('video_id'),
        'amount' => (float)$this->request->post('amount'),
        'order_no' => 'REW_' . date('YmdHis') . mt_rand(1000, 9999)
    ];
    
    // 发送到RabbitMQ
    $connection = new \PhpAmqpLib\Connection\AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
    $channel = $connection->channel();
    $channel->queue_declare('reward_queue', false, true, false, false);
    
    $msg = new \PhpAmqpLib\Message\AMQPMessage(
        json_encode($data),
        ['delivery_mode' => \PhpAmqpLib\Message\AMQPMessage::DELIVERY_MODE_PERSISTENT]
    );
    
    $channel->basic_publish($msg, '', 'reward_queue');
    $channel->close();
    $connection->close();
    
    return json(['code' => 200, 'msg' => '打赏请求已提交']);
}

  消费者代码(处理核心逻辑):

// 消费者脚本(命令行运行)
require_once __DIR__ . '/vendor/autoload.php';

$connection = new \PhpAmqpLib\Connection\AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
$channel = $connection->channel();
$channel->queue_declare('reward_queue', false, true, false, false);

$callback = function ($msg) {
    $data = json_decode($msg->body, true);
    
    try {
        // 核心处理逻辑(可重试)
        processReward($data);
        $msg->ack(); // 确认消息
    } catch (\Exception $e) {
        // 记录失败日志,后续人工或定时任务处理
        logError($e, $data);
        $msg->nack(); // 拒绝消息,重新入队
    }
};

$channel->basic_consume('reward_queue', '', false, false, false, false, $callback);

while ($channel->is_consuming()) {
    $channel->wait();
}

function processReward($data)
{
    // 实现与RewardController::reward()类似的逻辑
    // 注意:此处需处理幂等性(通过order_no去重)
}

  3.定时任务对账(保障最终一致性)

// 定时任务脚本(每天凌晨执行)
public function reconcileRewards()
{
    // 1. 查询未对账的打赏记录
    $unverifiedRewards = Db::name('rewards')
        ->where('status', 'pending')
        ->select();
    
    foreach ($unverifiedRewards as $reward) {
        try {
            // 检查用户余额和创作者收益是否匹配
            $userBalance = Db::name('users')
                ->where('user_id', $reward['user_id'])
                ->value('balance');
            
            $actualIncome = Db::name('income')
                ->where([
                    'user_id' => $reward['video_user_id'],
                    'video_id' => $reward['video_id']
                ])
                ->value('total_amount');
            
            // 如果金额不一致,触发修复
            if ($actualIncome < $reward['amount']) {
                $this->repairIncome($reward);
            }
            
            // 标记为已对账
            Db::name('rewards')
                ->where('reward_id', $reward['reward_id'])
                ->update(['status' => 'verified']);
        } catch (\Exception $e) {
            // 记录失败日志
        }
    }
}

  关键点总结

  强一致性:通过Redis分布式锁+数据库事务+悲观锁实现。

  最终一致性:通过异步队列(RabbitMQ/Kafka)解耦核心链路,配合定时任务修复异常数据。

  幂等性设计:所有操作需支持通过唯一ID(如order_no)去重。

  补偿机制:失败操作需记录日志,支持人工或自动重试。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐