2025-10-10 19:15:41 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
namespace app\common\service;
|
|
|
|
|
|
|
|
|
|
|
|
use think\Db;
|
|
|
|
|
|
use think\Cache;
|
|
|
|
|
|
use app\common\model\Redpacket;
|
|
|
|
|
|
use app\common\model\RedpacketRecord;
|
|
|
|
|
|
use app\common\model\UserWallet;
|
|
|
|
|
|
use app\common\library\RedpacketLua;
|
|
|
|
|
|
|
|
|
|
|
|
class RedpacketService
|
|
|
|
|
|
{
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 发红包
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function create($data)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 验证数据
|
|
|
|
|
|
$res = $this->validateCreateData($data);
|
|
|
|
|
|
if ($res['code'] == 0) {
|
|
|
|
|
|
return $res;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$redpacketModel = new Redpacket();
|
|
|
|
|
|
return $redpacketModel->createRedpacket($data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 抢红包并返回详细结果
|
|
|
|
|
|
*/
|
2025-10-11 14:49:25 +08:00
|
|
|
|
public function grabWithResult($redpacketId, $userId)
|
2025-10-10 19:15:41 +08:00
|
|
|
|
{
|
|
|
|
|
|
$redpacketModel = new Redpacket();
|
|
|
|
|
|
$redpacket = $redpacketModel->getRedpacketInfo($redpacketId);
|
|
|
|
|
|
|
|
|
|
|
|
if (!$redpacket) {
|
|
|
|
|
|
return [
|
|
|
|
|
|
'code' => 0,
|
|
|
|
|
|
'msg' => '红包不存在',
|
|
|
|
|
|
'data' => null
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证领取条件
|
|
|
|
|
|
$conditionCheck = $this->checkConditionsWithResult($redpacket, $userId);
|
|
|
|
|
|
if ($conditionCheck['code'] != 1) {
|
|
|
|
|
|
return $conditionCheck;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查红包状态
|
|
|
|
|
|
$statusCheck = $this->checkRedpacketStatus($redpacket);
|
|
|
|
|
|
if ($statusCheck['code'] != 1) {
|
|
|
|
|
|
return $statusCheck;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否已经抢过
|
|
|
|
|
|
if ($this->hasUserGrabbed($redpacketId, $userId)) {
|
2025-10-14 10:47:53 +08:00
|
|
|
|
// $detail = $this->getGrabResult($redpacketId, $userId);
|
2025-10-10 19:15:41 +08:00
|
|
|
|
return [
|
2025-10-14 10:47:53 +08:00
|
|
|
|
'code' => 1,
|
2025-10-10 19:15:41 +08:00
|
|
|
|
'msg' => '已经抢过该红包',
|
2025-10-14 10:47:53 +08:00
|
|
|
|
'data' => ['code' => 2] //1-抢到了,2-已经抢过红包,3-没有抢到
|
2025-10-10 19:15:41 +08:00
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 使用Redis+Lua保证原子性操作
|
|
|
|
|
|
$redis = Cache::store('redis')->handler();
|
|
|
|
|
|
$script = RedpacketLua::grabRedpacketScript();
|
|
|
|
|
|
|
|
|
|
|
|
$redpacketKey = "redpacket:{$redpacketId}";
|
|
|
|
|
|
$userSetKey = "redpacket_users:{$redpacketId}";
|
|
|
|
|
|
|
|
|
|
|
|
$result = $redis->eval($script, [
|
|
|
|
|
|
$redpacketKey,
|
|
|
|
|
|
$userSetKey,
|
|
|
|
|
|
$userId,
|
|
|
|
|
|
time()
|
|
|
|
|
|
], 3);
|
|
|
|
|
|
|
|
|
|
|
|
if ($result[0] == 0) {
|
|
|
|
|
|
$message = $result[1];
|
2025-10-14 10:47:53 +08:00
|
|
|
|
if ($message == '红包已抢完') {
|
|
|
|
|
|
return [
|
|
|
|
|
|
'code' => 1,
|
|
|
|
|
|
'msg' => '手慢了,红包已抢完',
|
|
|
|
|
|
'data' => ['code' => 3] //1-抢到了,2-已经抢过红包,3-没有抢到
|
|
|
|
|
|
];
|
|
|
|
|
|
} elseif ($message == '已经抢过该红包') {
|
|
|
|
|
|
return [
|
|
|
|
|
|
'code' => 1,
|
|
|
|
|
|
'msg' => '已经抢过该红包',
|
|
|
|
|
|
'data' => ['code' => 2] //1-抢到了,2-已经抢过红包,3-没有抢到
|
|
|
|
|
|
];
|
|
|
|
|
|
}else{
|
|
|
|
|
|
return [
|
|
|
|
|
|
'code' => 0,
|
|
|
|
|
|
'msg' => $message,
|
|
|
|
|
|
'data' => null
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
2025-10-10 19:15:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$amount = floatval($result[1]);
|
2025-10-14 18:09:49 +08:00
|
|
|
|
$isFinished = $result[2] == 1; // Lua脚本返回是否抢完
|
2025-10-10 19:15:41 +08:00
|
|
|
|
|
|
|
|
|
|
// Lua脚本执行成功,记录到数据库
|
|
|
|
|
|
Db::startTrans();
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 创建领取记录
|
|
|
|
|
|
$recordData = [
|
|
|
|
|
|
'redpacket_id' => $redpacketId,
|
|
|
|
|
|
'user_id' => $userId,
|
|
|
|
|
|
'amount' => $amount
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
$recordModel = new RedpacketRecord();
|
|
|
|
|
|
$recordModel->save($recordData);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新用户钱包
|
|
|
|
|
|
$coinField = $redpacket['coin_type'] == 1 ? 'coin' : 'earnings';
|
|
|
|
|
|
//增加余额
|
|
|
|
|
|
$addres = Db::name('user_wallet')
|
|
|
|
|
|
->where('user_id', $userId)
|
|
|
|
|
|
->inc($coinField, $amount)
|
|
|
|
|
|
->update();
|
|
|
|
|
|
//记录用户金币日志
|
|
|
|
|
|
$data = [
|
|
|
|
|
|
'user_id' => $userId,
|
|
|
|
|
|
'change_value' => $amount,
|
|
|
|
|
|
'room_id' => $redpacket['room_id'],
|
|
|
|
|
|
'money_type' => $redpacket['coin_type'],
|
2025-10-11 14:49:25 +08:00
|
|
|
|
//记录日志 32-发红包(金币),29-发红包(钻石),30-抢红包(金币),31-抢红包(钻石)
|
|
|
|
|
|
'change_type' => $redpacket['coin_type'] == 1 ? 30 : 31,
|
2025-10-10 19:15:41 +08:00
|
|
|
|
'from_id' => $redpacket['room_id'],
|
|
|
|
|
|
'remarks' => '抢红包收入',
|
|
|
|
|
|
'createtime' => time()
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
$res = Db::name('vs_user_money_log')->insert($data);
|
|
|
|
|
|
if(!$res || !$addres){
|
|
|
|
|
|
Db::rollback();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-14 18:09:49 +08:00
|
|
|
|
// 更新红包剩余数量和金额,如果抢完了更新状态
|
|
|
|
|
|
$updateData = [
|
|
|
|
|
|
'left_amount' => Db::raw('left_amount - ' . $amount),
|
|
|
|
|
|
'left_count' => Db::raw('left_count - 1'),
|
|
|
|
|
|
'updatetime' => time()
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
if ($isFinished) {
|
|
|
|
|
|
$updateData['status'] = Redpacket::STATUS_FINISHED;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-10 19:15:41 +08:00
|
|
|
|
Db::name('redpacket')
|
|
|
|
|
|
->where('id', $redpacketId)
|
2025-10-14 18:09:49 +08:00
|
|
|
|
->update($updateData);
|
2025-10-10 19:15:41 +08:00
|
|
|
|
|
|
|
|
|
|
Db::commit();
|
|
|
|
|
|
|
|
|
|
|
|
// 获取抢红包结果详情
|
|
|
|
|
|
$grabResult = $this->getGrabResult($redpacketId, $userId);
|
2025-10-14 09:44:29 +08:00
|
|
|
|
unset($grabResult['previous_records']);//前端不要
|
|
|
|
|
|
unset($grabResult['all_records']);//前端不要
|
|
|
|
|
|
unset($grabResult['statistics']);//前端不要
|
2025-10-10 19:15:41 +08:00
|
|
|
|
|
|
|
|
|
|
return [
|
2025-10-11 14:49:25 +08:00
|
|
|
|
'code' => 1,
|
|
|
|
|
|
'msg' => '抢红包成功',
|
2025-10-14 09:44:29 +08:00
|
|
|
|
// 'data' => $grabResult
|
2025-10-14 10:47:53 +08:00
|
|
|
|
// 'data' => null
|
|
|
|
|
|
'data' => ['code' => 1] //1-抢到了,2-已经抢过红包,3-没有抢到
|
2025-10-10 19:15:41 +08:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
Db::rollback();
|
|
|
|
|
|
// 回滚Redis操作
|
|
|
|
|
|
$redis->hIncrByFloat($redpacketKey, 'left_amount', $amount);
|
|
|
|
|
|
$redis->hIncrBy($redpacketKey, 'left_count', 1);
|
|
|
|
|
|
$redis->sRem($userSetKey, $userId);
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
2025-10-11 14:49:25 +08:00
|
|
|
|
'code' => 0,
|
2025-10-14 11:37:45 +08:00
|
|
|
|
'msg' => '系统错误,请重试'.$e->getMessage(),
|
2025-10-11 14:49:25 +08:00
|
|
|
|
'data' => null
|
2025-10-10 19:15:41 +08:00
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取抢红包结果详情
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function getGrabResult($redpacketId, $userId)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 获取红包基本信息
|
|
|
|
|
|
$redpacket = Db::name('redpacket')
|
|
|
|
|
|
->where('id', $redpacketId)
|
|
|
|
|
|
->find();
|
|
|
|
|
|
|
|
|
|
|
|
if (!$redpacket) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当前用户的领取记录
|
|
|
|
|
|
$myRecord = Db::name('redpacket_record')
|
|
|
|
|
|
->alias('r')
|
|
|
|
|
|
->field('r.*, u.nickname, u.avatar')
|
|
|
|
|
|
->join('user u', 'u.id = r.user_id')
|
|
|
|
|
|
->where('r.redpacket_id', $redpacketId)
|
|
|
|
|
|
->where('r.user_id', $userId)
|
|
|
|
|
|
->find();
|
|
|
|
|
|
|
|
|
|
|
|
// 获取在我之前抢到的用户记录
|
|
|
|
|
|
$previousRecords = [];
|
|
|
|
|
|
if ($myRecord) {
|
|
|
|
|
|
$previousRecords = Db::name('redpacket_record')
|
|
|
|
|
|
->alias('r')
|
|
|
|
|
|
->field('r.*, u.nickname, u.avatar')
|
|
|
|
|
|
->join('user u', 'u.id = r.user_id')
|
|
|
|
|
|
->where('r.redpacket_id', $redpacketId)
|
|
|
|
|
|
->where('r.createtime', '<', $myRecord['createtime'])
|
|
|
|
|
|
->order('r.createtime ASC')
|
|
|
|
|
|
->select();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取所有记录用于统计
|
|
|
|
|
|
$allRecords = Db::name('redpacket_record')
|
|
|
|
|
|
->alias('r')
|
|
|
|
|
|
->field('r.*, u.nickname, u.avatar')
|
|
|
|
|
|
->join('user u', 'u.id = r.user_id')
|
|
|
|
|
|
->where('r.redpacket_id', $redpacketId)
|
|
|
|
|
|
->order('r.createtime ASC')
|
|
|
|
|
|
->select();
|
|
|
|
|
|
|
|
|
|
|
|
// 统计信息
|
|
|
|
|
|
$totalGrabbed = count($allRecords);
|
|
|
|
|
|
$totalAmount = array_sum(array_column($allRecords, 'amount'));
|
|
|
|
|
|
|
|
|
|
|
|
// 手气最佳
|
|
|
|
|
|
$bestRecord = null;
|
|
|
|
|
|
if ($allRecords) {
|
|
|
|
|
|
$maxAmount = max(array_column($allRecords, 'amount'));
|
|
|
|
|
|
foreach ($allRecords as $record) {
|
|
|
|
|
|
if ($record['amount'] == $maxAmount) {
|
|
|
|
|
|
$bestRecord = $record;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
|
'redpacket_info' => [
|
|
|
|
|
|
'id' => $redpacket['id'],
|
|
|
|
|
|
'total_amount' => $redpacket['total_amount'],
|
|
|
|
|
|
'total_count' => $redpacket['total_count'],
|
|
|
|
|
|
'left_amount' => $redpacket['left_amount'],
|
|
|
|
|
|
'left_count' => $redpacket['left_count'],
|
|
|
|
|
|
'coin_type' => $redpacket['coin_type'],
|
2025-10-11 18:09:36 +08:00
|
|
|
|
'status' => $redpacket['status'],
|
|
|
|
|
|
'nickname' => Db::name('user')->where('id', $redpacket['user_id'])->value('nickname')
|
2025-10-10 19:15:41 +08:00
|
|
|
|
],
|
|
|
|
|
|
'my_record' => $myRecord ? [
|
|
|
|
|
|
'amount' => $myRecord['amount'],
|
|
|
|
|
|
'createtime' => $myRecord['createtime'],
|
|
|
|
|
|
'nickname' => $myRecord['nickname'],
|
|
|
|
|
|
'avatar' => $myRecord['avatar']
|
|
|
|
|
|
] : null,
|
|
|
|
|
|
'previous_records' => $previousRecords,
|
|
|
|
|
|
'all_records' => $allRecords,
|
|
|
|
|
|
'statistics' => [
|
|
|
|
|
|
'total_grabbed' => $totalGrabbed,
|
|
|
|
|
|
'total_amount_grabbed' => $totalAmount,
|
|
|
|
|
|
'best_luck' => $bestRecord ? [
|
|
|
|
|
|
'nickname' => $bestRecord['nickname'],
|
|
|
|
|
|
'avatar' => $bestRecord['avatar'],
|
|
|
|
|
|
'amount' => $bestRecord['amount']
|
|
|
|
|
|
] : null
|
|
|
|
|
|
]
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查红包状态
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function checkRedpacketStatus($redpacket)
|
|
|
|
|
|
{
|
|
|
|
|
|
$now = time();
|
|
|
|
|
|
|
|
|
|
|
|
if ($redpacket['status'] == Redpacket::STATUS_PENDING) {
|
|
|
|
|
|
if ($now < $redpacket['start_time']) {
|
|
|
|
|
|
return [
|
|
|
|
|
|
'code' => 0,
|
|
|
|
|
|
'msg' => '红包还未开始',
|
|
|
|
|
|
'data' => null
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ($redpacket['status'] == Redpacket::STATUS_FINISHED ||
|
|
|
|
|
|
$redpacket['status'] == Redpacket::STATUS_REFUNDED) {
|
|
|
|
|
|
return [
|
|
|
|
|
|
'code' => 0,
|
|
|
|
|
|
'msg' => '红包已结束',
|
|
|
|
|
|
'data' => null
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ($now > $redpacket['end_time']) {
|
|
|
|
|
|
return [
|
|
|
|
|
|
'code' => 0,
|
|
|
|
|
|
'msg' => '红包已结束',
|
|
|
|
|
|
'data' => null
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ['code' => 1];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查用户是否已经抢过
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function hasUserGrabbed($redpacketId, $userId)
|
|
|
|
|
|
{
|
|
|
|
|
|
$record = Db::name('redpacket_record')
|
|
|
|
|
|
->where('redpacket_id', $redpacketId)
|
|
|
|
|
|
->where('user_id', $userId)
|
|
|
|
|
|
->find();
|
|
|
|
|
|
|
|
|
|
|
|
return !empty($record);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查领取条件(返回结果格式)
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function checkConditionsWithResult($redpacket, $userId)
|
|
|
|
|
|
{
|
|
|
|
|
|
$conditions = $redpacket['conditions'] ? explode(',', $redpacket['conditions']): [];
|
|
|
|
|
|
|
|
|
|
|
|
if (empty($conditions)) {
|
|
|
|
|
|
return ['code' => 1];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (in_array(Redpacket::CONDITION_NONE, $conditions)) {
|
|
|
|
|
|
return ['code' => 1];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($conditions as $condition) {
|
|
|
|
|
|
switch ($condition) {
|
|
|
|
|
|
case Redpacket::CONDITION_COLLECT_ROOM:
|
|
|
|
|
|
if (!$this->checkUserCollectedRoom($userId, $redpacket['room_id'])) {
|
|
|
|
|
|
return [
|
|
|
|
|
|
'code' => 0,
|
|
|
|
|
|
'msg' => '不满足收藏房间条件',
|
|
|
|
|
|
'data' => null
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case Redpacket::CONDITION_MIC_USER:
|
|
|
|
|
|
if (!$this->checkUserOnMic($userId, $redpacket['room_id'])) {
|
|
|
|
|
|
return [
|
|
|
|
|
|
'code' => 0,
|
|
|
|
|
|
'msg' => '不满足麦位用户条件',
|
|
|
|
|
|
'data' => null
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ['code' => 1];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取红包详情和领取记录
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function getDetail($redpacketId, $currentUserId = 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
$redpacketModel = new Redpacket();
|
2025-10-14 09:44:29 +08:00
|
|
|
|
$redpacket['redpacket_info'] = $redpacketModel->getRedpacketInfo($redpacketId);
|
2025-10-10 19:15:41 +08:00
|
|
|
|
|
|
|
|
|
|
if (!$redpacket) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取领取记录
|
|
|
|
|
|
$records = Db::name('redpacket_record')
|
|
|
|
|
|
->alias('r')
|
|
|
|
|
|
->field('r.*, u.nickname, u.avatar')
|
|
|
|
|
|
->join('user u', 'u.id = r.user_id')
|
|
|
|
|
|
->where('r.redpacket_id', $redpacketId)
|
|
|
|
|
|
->order('r.createtime ASC')
|
|
|
|
|
|
->select();
|
2025-10-14 11:19:53 +08:00
|
|
|
|
//处理createtime 字段
|
|
|
|
|
|
$records = array_map(function ($record) {
|
|
|
|
|
|
$record['createtime'] = date('Y-m-d H:i:s', $record['createtime']);
|
|
|
|
|
|
return $record;
|
|
|
|
|
|
}, $records);
|
2025-10-10 19:15:41 +08:00
|
|
|
|
|
|
|
|
|
|
$redpacket['records'] = $records;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查当前用户是否已抢
|
|
|
|
|
|
$redpacket['has_grabbed'] = false;
|
|
|
|
|
|
$redpacket['my_record'] = null;
|
|
|
|
|
|
|
|
|
|
|
|
if ($currentUserId > 0) {
|
|
|
|
|
|
foreach ($records as $record) {
|
|
|
|
|
|
if ($record['user_id'] == $currentUserId) {
|
|
|
|
|
|
$redpacket['has_grabbed'] = true;
|
|
|
|
|
|
$redpacket['my_record'] = $record;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return $redpacket;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理过期红包退款
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function processExpiredRedpackets()
|
|
|
|
|
|
{
|
|
|
|
|
|
$now = time();
|
|
|
|
|
|
$redpacketModel = new Redpacket();
|
|
|
|
|
|
|
|
|
|
|
|
// 查找已结束但未退款的红包
|
|
|
|
|
|
$expiredRedpackets = Db::name('redpacket')
|
|
|
|
|
|
->where('status', Redpacket::STATUS_ACTIVE)
|
|
|
|
|
|
->where('end_time', '<', $now)
|
|
|
|
|
|
->where('left_count', '>', 0)
|
|
|
|
|
|
->select();
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($expiredRedpackets as $redpacket) {
|
|
|
|
|
|
Db::startTrans();
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 退款给发红包用户
|
|
|
|
|
|
if ($redpacket['left_amount'] > 0) {
|
|
|
|
|
|
$walletModel = new UserWallet();
|
|
|
|
|
|
$walletModel->increaseBalance(
|
|
|
|
|
|
$redpacket['user_id'],
|
|
|
|
|
|
$redpacket['coin_type'],
|
|
|
|
|
|
$redpacket['left_amount']
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新红包状态
|
|
|
|
|
|
Db::name('redpacket')
|
|
|
|
|
|
->where('id', $redpacket['id'])
|
|
|
|
|
|
->update([
|
|
|
|
|
|
'status' => Redpacket::STATUS_REFUNDED,
|
|
|
|
|
|
'updatetime' => $now
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
// 清理Redis缓存
|
|
|
|
|
|
$redis = Cache::store('redis')->handler();
|
|
|
|
|
|
$redisKey = "redpacket:{$redpacket['id']}";
|
|
|
|
|
|
$redis->del($redisKey);
|
|
|
|
|
|
|
|
|
|
|
|
Db::commit();
|
|
|
|
|
|
|
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
|
Db::rollback();
|
|
|
|
|
|
// 记录日志
|
|
|
|
|
|
\think\Log::error("红包退款失败: {$redpacket['id']}, 错误: " . $e->getMessage());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-11 15:02:13 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-10-10 19:15:41 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 验证创建红包数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function validateCreateData($data)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (empty($data['user_id'])) {
|
2025-10-11 15:02:13 +08:00
|
|
|
|
return ['code' => 0, 'msg' => '用户ID不能为空', 'data' => null];
|
2025-10-10 19:15:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-13 15:56:46 +08:00
|
|
|
|
if (empty($data['room_id'])) {
|
|
|
|
|
|
return ['code' => 0, 'msg' => '房间ID不能为空', 'data' => null];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-10 19:15:41 +08:00
|
|
|
|
if (!in_array($data['type'], [Redpacket::TYPE_NORMAL, Redpacket::TYPE_PASSWORD])) {
|
2025-10-11 15:02:13 +08:00
|
|
|
|
return ['code' => 0, 'msg' => '红包类型错误', 'data' => null];
|
2025-10-10 19:15:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ($data['type'] == Redpacket::TYPE_PASSWORD && empty($data['password'])) {
|
2025-10-11 15:02:13 +08:00
|
|
|
|
return ['code' => 0, 'msg' => '口令红包必须设置口令', 'data' => null];
|
2025-10-10 19:15:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!in_array($data['coin_type'], [Redpacket::COIN_GOLD, Redpacket::COIN_DIAMOND])) {
|
2025-10-11 15:02:13 +08:00
|
|
|
|
return ['code' => 0, 'msg' => '币种类型错误', 'data' => null];
|
2025-10-10 19:15:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ($data['total_amount'] <= 0 || $data['total_count'] <= 0) {
|
2025-10-11 15:02:13 +08:00
|
|
|
|
return ['code' => 0, 'msg' => '金额和数量必须大于0', 'data' => null];
|
2025-10-10 19:15:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ($data['total_amount'] < $data['total_count']) {
|
2025-10-11 15:02:13 +08:00
|
|
|
|
return ['code' => 0, 'msg' => '总金额不能小于总个数', 'data' => null];
|
2025-10-10 19:15:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证领取条件
|
|
|
|
|
|
if (isset($data['conditions'])) {
|
2025-10-11 15:02:13 +08:00
|
|
|
|
$res_con = $this->validateConditions($data['conditions']);
|
2025-10-10 19:15:41 +08:00
|
|
|
|
if ($res_con !== true) {
|
|
|
|
|
|
return $res_con;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-11 15:02:13 +08:00
|
|
|
|
|
|
|
|
|
|
return ['code' => 1, 'msg' => '验证成功', 'data' => null];
|
2025-10-10 19:15:41 +08:00
|
|
|
|
}
|
2025-10-11 15:08:25 +08:00
|
|
|
|
|
2025-10-10 19:15:41 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 验证领取条件
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function validateConditions($conditions)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (empty($conditions)) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//字符串转为数组
|
|
|
|
|
|
$conditions = explode(',', $conditions);
|
|
|
|
|
|
|
|
|
|
|
|
if (in_array(Redpacket::CONDITION_NONE, $conditions) && count($conditions) > 1) {
|
|
|
|
|
|
return V(0, '选择"无"条件时不能选择其他条件');
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查用户是否满足领取条件
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function checkConditions($redpacket, $userId)
|
|
|
|
|
|
{
|
|
|
|
|
|
$conditions = $redpacket['conditions'] ?: [];
|
|
|
|
|
|
|
|
|
|
|
|
if (empty($conditions)) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (in_array(Redpacket::CONDITION_NONE, $conditions)) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($conditions as $condition) {
|
|
|
|
|
|
switch ($condition) {
|
|
|
|
|
|
case Redpacket::CONDITION_COLLECT_ROOM:
|
|
|
|
|
|
// 检查用户是否收藏了房间
|
|
|
|
|
|
if (!$this->checkUserCollectedRoom($userId)) {
|
|
|
|
|
|
throw new \Exception('不满足收藏房间条件');
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case Redpacket::CONDITION_MIC_USER:
|
|
|
|
|
|
// 检查用户是否在麦位上
|
|
|
|
|
|
if (!$this->checkUserOnMic($userId)) {
|
|
|
|
|
|
throw new \Exception('不满足麦位用户条件');
|
|
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查用户是否收藏了房间(需要根据实际业务实现)
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function checkUserCollectedRoom($userId,$roomId)
|
|
|
|
|
|
{
|
|
|
|
|
|
$collect = Db::name('user_follow')->where(['user_id' => $userId,'type' => 2,'follow_id' => $roomId])->find();
|
|
|
|
|
|
if (!$collect) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查用户是否在麦位上(需要根据实际业务实现)
|
|
|
|
|
|
*/
|
|
|
|
|
|
private function checkUserOnMic($userId,$roomId)
|
|
|
|
|
|
{
|
|
|
|
|
|
$onPit = Db::name('vs_room_pit')->where(['user_id' => $userId,'room_id' => $roomId])->value('pit_number');
|
|
|
|
|
|
if ($onPit <= 0){
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2025-10-14 18:09:49 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查并更新红包状态
|
|
|
|
|
|
* 在抢红包前调用,确保状态正确
|
|
|
|
|
|
*/
|
|
|
|
|
|
public function checkAndUpdateRedpacketStatus($redpacketId)
|
|
|
|
|
|
{
|
|
|
|
|
|
$redpacket = Db::name('redpacket')->where('id', $redpacketId)->find();
|
|
|
|
|
|
if (!$redpacket) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$now = time();
|
|
|
|
|
|
$redis = Cache::store('redis')->handler();
|
|
|
|
|
|
$redpacketKey = "redpacket:{$redpacketId}";
|
|
|
|
|
|
|
|
|
|
|
|
// 如果红包状态为未开始(0),但当前时间已超过开始时间,则更新为进行中(1)
|
|
|
|
|
|
if ($redpacket['status'] == Redpacket::STATUS_PENDING && $now >= $redpacket['start_time']) {
|
|
|
|
|
|
Db::name('redpacket')
|
|
|
|
|
|
->where('id', $redpacketId)
|
|
|
|
|
|
->update([
|
|
|
|
|
|
'status' => Redpacket::STATUS_ACTIVE,
|
|
|
|
|
|
'updatetime' => $now
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新Redis中的状态
|
|
|
|
|
|
$redis->hSet($redpacketKey, 'status', Redpacket::STATUS_ACTIVE);
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果红包状态为进行中(1),但已抢完,则更新为已结束(2)
|
|
|
|
|
|
if ($redpacket['status'] == Redpacket::STATUS_ACTIVE && $redpacket['left_count'] <= 0) {
|
|
|
|
|
|
Db::name('redpacket')
|
|
|
|
|
|
->where('id', $redpacketId)
|
|
|
|
|
|
->update([
|
|
|
|
|
|
'status' => Redpacket::STATUS_FINISHED,
|
|
|
|
|
|
'updatetime' => $now
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新Redis中的状态
|
|
|
|
|
|
$redis->hSet($redpacketKey, 'status', Redpacket::STATUS_FINISHED);
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果红包状态为进行中(1),但已超过结束时间,则更新为已结束(2)
|
|
|
|
|
|
if ($redpacket['status'] == Redpacket::STATUS_ACTIVE && $now > $redpacket['end_time']) {
|
|
|
|
|
|
Db::name('redpacket')
|
|
|
|
|
|
->where('id', $redpacketId)
|
|
|
|
|
|
->update([
|
|
|
|
|
|
'status' => Redpacket::STATUS_FINISHED,
|
|
|
|
|
|
'updatetime' => $now
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新Redis中的状态
|
|
|
|
|
|
$redis->hSet($redpacketKey, 'status', Redpacket::STATUS_FINISHED);
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2025-10-10 19:15:41 +08:00
|
|
|
|
}
|