Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
2025-10-12 09:20:05 +08:00
7 changed files with 105 additions and 150 deletions

View File

@@ -4,6 +4,7 @@ namespace app\api\controller;
use app\common\controller\BaseCom; use app\common\controller\BaseCom;
use app\common\service\RedpacketService; use app\common\service\RedpacketService;
use think\Db;
/** /**
* 红包接口 * 红包接口
@@ -32,14 +33,13 @@ class Redpacket extends BaseCom
public function grab() public function grab()
{ {
$redpacketId = $this->request->post('redpacket_id'); $redpacketId = $this->request->post('redpacket_id');
$password = $this->request->post('password', '');
if (empty($redpacketId)) { if (empty($redpacketId)) {
return V(0, '红包ID不能为空'); return V(0, '红包ID不能为空');
} }
$service = new RedpacketService(); $service = new RedpacketService();
$reslut = $service->grabWithResult($redpacketId, $this->uid, $password); $reslut = $service->grabWithResult($redpacketId, $this->uid);
return V($reslut['code'], $reslut['msg'], $reslut['data']); return V($reslut['code'], $reslut['msg'], $reslut['data']);
} }
@@ -49,21 +49,20 @@ class Redpacket extends BaseCom
*/ */
public function grabResult() public function grabResult()
{ {
$user = $this->auth->getUser();
$redpacketId = $this->request->get('redpacket_id'); $redpacketId = $this->request->get('redpacket_id');
if (empty($redpacketId)) { if (empty($redpacketId)) {
$this->error('红包ID不能为空'); return V(0, '红包ID不能为空');
} }
$service = new RedpacketService(); $service = new RedpacketService();
$result = $service->getGrabResult($redpacketId, $user->id); $result = $service->getGrabResult($redpacketId, $this->uid);
if (!$result) { if (!$result) {
$this->error('红包不存在'); return V(0, '红包不存在');
} }
$this->success('获取成功', $result); return V(1, '获取成功', $result);
} }
/** /**
@@ -72,25 +71,19 @@ class Redpacket extends BaseCom
public function detail() public function detail()
{ {
$redpacketId = $this->request->get('redpacket_id'); $redpacketId = $this->request->get('redpacket_id');
$currentUserId = $this->auth->isLogin() ? $this->auth->id : 0;
if (empty($redpacketId)) { if (empty($redpacketId)) {
$this->error('红包ID不能为空'); return V(0, '红包ID不能为空');
} }
try { $service = new RedpacketService();
$service = new RedpacketService(); $detail = $service->getDetail($redpacketId, $this->uid);
$detail = $service->getDetail($redpacketId, $currentUserId);
if (!$detail) { if (!$detail) {
$this->error('红包不存在'); return V(0, '红包不存在');
}
$this->success('获取成功', $detail);
} catch (\Exception $e) {
$this->error($e->getMessage());
} }
return V(1, '获取成功', $detail);
} }
/** /**
@@ -101,4 +94,12 @@ class Redpacket extends BaseCom
$options = \app\common\model\Redpacket::$countdownOptions; $options = \app\common\model\Redpacket::$countdownOptions;
$this->success('获取成功', $options); $this->success('获取成功', $options);
} }
// 获取房间内红包列表
public function roomRedPackets()
{
$roomId = $this->request->get('room_id');
$result = Db::name('redpacket')->where(['room_id' => $roomId, 'status' => ['<=',1]])->select();
return V(1, '获取成功', $result);
}
} }

View File

@@ -119,6 +119,8 @@ class Chat extends Model
//清空个人魅力 //清空个人魅力
// ClearUserCharm = 1059, // ClearUserCharm = 1059,
//发红包
// RedPacket = 1060,

View File

@@ -50,18 +50,18 @@ class UserWallet extends Model
// 1.系统调节 2.充值 3.提现 4.金币转增(送出) 5.每日任务奖励 6.充值返利 7.购买装扮 // 1.系统调节 2.充值 3.提现 4.金币转增(送出) 5.每日任务奖励 6.充值返利 7.购买装扮
// 8.礼盒奖励 9.房间补贴 10.购买礼物 11.收礼增加收益 12.工会补贴 13.转赠金币(接收) 14.收益兑换 // 8.礼盒奖励 9.房间补贴 10.购买礼物 11.收礼增加收益 12.工会补贴 13.转赠金币(接收) 14.收益兑换
// 15.首充 16.天降好礼充值 17.退出工会扣款 18.房主收益 19.主持人收益20.发布头条扣除余额,21.公会长收益,22.提现驳回或提现失败返还,23.财富等级奖励金币领取,24.删除关系扣金币 // 15.首充 16.天降好礼充值 17.退出工会扣款 18.房主收益 19.主持人收益20.发布头条扣除余额,21.公会长收益,22.提现驳回或提现失败返还,23.财富等级奖励金币领取,24.删除关系扣金币
//27.小时榜获得 //27.小时榜获得28-新人充值好礼,32-发红包金币29-发红包钻石30-抢红包金币31-抢红包(钻石)
if($gift_type == 1){ //1金币2收益钻石 if($gift_type == 1){ //1金币2收益钻石
if($in_out_type == 1){//1收入 if($in_out_type == 1){//1收入
$in_out_types = [2,5,6,8,13,14,15,16,22,23,26,27]; $in_out_types = [2,5,6,8,13,14,15,16,22,23,26,27,30,28];
}elseif($in_out_type == 2){//2支出 }elseif($in_out_type == 2){//2支出
$in_out_types = [4,7,10,17,20,24,25]; $in_out_types = [4,7,10,17,20,24,25,32];
} }
}elseif($gift_type == 2){ //1金币2收益钻石 }elseif($gift_type == 2){ //1金币2收益钻石
if($in_out_type == 1){//1收入 if($in_out_type == 1){//1收入
$in_out_types = [6,9,11,12,18,19,21,22]; $in_out_types = [6,9,11,12,18,19,21,22,31,28];
}elseif($in_out_type == 2){//2支出 }elseif($in_out_type == 2){//2支出
$in_out_types = [3,14]; $in_out_types = [3,14,29];
} }
} }

View File

@@ -46,14 +46,14 @@ if status == 0 then
end end
end end
if status ~= 1 then -- if status ~= 1 then
return {0, "红包已结束"} -- return {0, "红包已结束"}
end -- end
if currentTime > endTime then -- if currentTime > endTime then
redis.call('HSET', redpacketKey, 'status', 2) -- redis.call('HSET', redpacketKey, 'status', 2)
return {0, "红包已结束"} -- return {0, "红包已结束"}
end -- end
-- 检查是否已经抢过 -- 检查是否已经抢过
local hasGrabbed = redis.call('SISMEMBER', userSetKey, userId) local hasGrabbed = redis.call('SISMEMBER', userSetKey, userId)
@@ -77,7 +77,7 @@ if leftCount == 1 then
else else
-- 随机算法:二倍均值法,保证公平性 -- 随机算法:二倍均值法,保证公平性
local maxAmount = leftAmount / leftCount * 2 local maxAmount = leftAmount / leftCount * 2
amount = math.random(1, math.floor(maxAmount * 100)) / 100 amount = math.random(1, math.floor(maxAmount))
-- 确保金额不会超过剩余金额 -- 确保金额不会超过剩余金额
if amount > leftAmount then if amount > leftAmount then
amount = leftAmount amount = leftAmount

View File

@@ -44,6 +44,7 @@ class Redpacket extends Model
*/ */
public function createRedpacket($data) public function createRedpacket($data)
{ {
// var_dump($data);exit;
Db::startTrans(); Db::startTrans();
try { try {
// 验证用户余额 // 验证用户余额
@@ -51,15 +52,31 @@ class Redpacket extends Model
$coinField = $data['coin_type'] == self::COIN_GOLD ? 'coin' : 'earnings'; $coinField = $data['coin_type'] == self::COIN_GOLD ? 'coin' : 'earnings';
if ($wallet[$coinField] < $data['total_amount']) { if ($wallet[$coinField] < $data['total_amount']) {
return V(0, '余额不足'); return ['code' => 0, 'msg' => '余额不足', 'data' => null];
} }
// 扣除余额 // 扣除余额
Db::name('user_wallet') $delres = Db::name('user_wallet')
->where('user_id', $data['user_id']) ->where('user_id', $data['user_id'])
->dec($coinField, $data['total_amount']) ->dec($coinField, $data['total_amount'])
->update(); ->update();
//记录日志 32-发红包金币29-发红包钻石30-抢红包金币31-抢红包(钻石)
//记录用户金币日志
$data_log = [
'user_id' => $data['user_id'],
'change_value' => $data['total_amount'],
'room_id' => $data['room_id'],
'money_type' => $data['coin_type'],
'change_type' => $data['coin_type'] == self::COIN_GOLD ? 32 : 29,
'from_id' => $data['room_id'],
'remarks' => $data['coin_type'] == self::COIN_GOLD ? '金币(发红包)' : '钻石(发红包)',
'createtime' => time()
];
$res = Db::name('vs_user_money_log')->insert($data_log);
if(!$res || !$delres){
Db::rollback();
}
// 计算开始时间 // 计算开始时间
$startTime = $data['countdown'] > 0 ? (time() + $data['countdown']) : time(); $startTime = $data['countdown'] > 0 ? (time() + $data['countdown']) : time();
$endTime = $startTime + 120; // 2分钟后结束 $endTime = $startTime + 120; // 2分钟后结束
@@ -100,15 +117,21 @@ class Redpacket extends Model
// 设置过期时间 // 设置过期时间
$redis->expireAt($redisKey, $endTime + 3600); // 结束后保留1小时 $redis->expireAt($redisKey, $endTime + 3600); // 结束后保留1小时
Db::commit(); Db::commit();
// return $redpacketId;
return V(1, '发红包成功', $redpacketId); //给前端推送信息
$data['nickname'] = Db::name('user')->where('id', $data['user_id'])->value('nickname');
$text = [
'redpacketInfo' => $data,
'text' => ''
];
model('api/Chat')->sendMsg(1060,$data['room_id'],$text);
return ['code' => 1, 'msg' => '发红包成功', 'data' => $redpacketId];
} catch (\Exception $e) { } catch (\Exception $e) {
Db::rollback(); Db::rollback();
return V(0, $e); return ['code' => 0, 'msg' => $e->getMessage(), 'data' => null];
// throw $e;
} }
} }

View File

@@ -78,6 +78,14 @@ class UserWallet extends Model
//新人充值好礼 //新人充值好礼
const NEW_USER_CHARGE_GIFT = 28; const NEW_USER_CHARGE_GIFT = 28;
//发红包(金币)
const RED_PACKET_COIN = 32;
//发红包(钻石)
const RED_PACKET_DIAMOND = 29;
//抢红包(金币)
const RED_PACKET_COIN_RECEIVE = 30;
//抢红包(钻石)
const RED_PACKET_DIAMOND_RECEIVE = 31;
//金币支出类型数组 //金币支出类型数组
public $coin_consumption_type_array = [ public $coin_consumption_type_array = [
@@ -86,12 +94,14 @@ class UserWallet extends Model
self::OPERATION_GIFT, self::OPERATION_GIFT,
self::GUILD_EXIT, self::GUILD_EXIT,
self::HEADLINE_REWARD, self::HEADLINE_REWARD,
self::TRANSFER_COIN self::TRANSFER_COIN,
self::RED_PACKET_COIN,
]; ];
//钻石支出类型数组 //钻石支出类型数组
public $diamond_consumption_type_array = [ public $diamond_consumption_type_array = [
self::OPERATION_WITHDRAW, self::OPERATION_WITHDRAW,
self::MONEY_CONVERSION self::MONEY_CONVERSION,
self::RED_PACKET_DIAMOND
]; ];
@@ -141,7 +151,11 @@ class UserWallet extends Model
self::TRANSFER_COIN => '赠送好友金币', self::TRANSFER_COIN => '赠送好友金币',
self::RECEIVE_COIN => '好友转赠所得金币', self::RECEIVE_COIN => '好友转赠所得金币',
self::HOUR_RANK_COIN => '小时榜获得金币', self::HOUR_RANK_COIN => '小时榜获得金币',
self::NEW_USER_CHARGE_GIFT => '新人充值好礼' self::NEW_USER_CHARGE_GIFT => '新人充值好礼',
self::RED_PACKET_COIN => '发红包(金币)',
self::RED_PACKET_DIAMOND => '发红包(钻石)',
self::RED_PACKET_COIN_RECEIVE => '抢红包(金币)',
self::RED_PACKET_DIAMOND_RECEIVE => '抢红包(钻石)',
]; ];
return $status[$type] ?? ''; return $status[$type] ?? '';
} }

View File

@@ -30,7 +30,7 @@ class RedpacketService
/** /**
* 抢红包并返回详细结果 * 抢红包并返回详细结果
*/ */
public function grabWithResult($redpacketId, $userId, $password = '') public function grabWithResult($redpacketId, $userId)
{ {
$redpacketModel = new Redpacket(); $redpacketModel = new Redpacket();
$redpacket = $redpacketModel->getRedpacketInfo($redpacketId); $redpacket = $redpacketModel->getRedpacketInfo($redpacketId);
@@ -43,17 +43,6 @@ class RedpacketService
]; ];
} }
// 验证口令红包
if ($redpacket['type'] == Redpacket::TYPE_PASSWORD) {
if (empty($password) || $password != $redpacket['password']) {
return [
'code' => 0,
'msg' => '口令错误',
'data' => null
];
}
}
// 验证领取条件 // 验证领取条件
$conditionCheck = $this->checkConditionsWithResult($redpacket, $userId); $conditionCheck = $this->checkConditionsWithResult($redpacket, $userId);
if ($conditionCheck['code'] != 1) { if ($conditionCheck['code'] != 1) {
@@ -129,7 +118,8 @@ class RedpacketService
'change_value' => $amount, 'change_value' => $amount,
'room_id' => $redpacket['room_id'], 'room_id' => $redpacket['room_id'],
'money_type' => $redpacket['coin_type'], 'money_type' => $redpacket['coin_type'],
'change_type' => 66,//抢红包收入 //记录日志 32-发红包金币29-发红包钻石30-抢红包金币31-抢红包(钻石)
'change_type' => $redpacket['coin_type'] == 1 ? 30 : 31,
'from_id' => $redpacket['room_id'], 'from_id' => $redpacket['room_id'],
'remarks' => '抢红包收入', 'remarks' => '抢红包收入',
'createtime' => time() 'createtime' => time()
@@ -153,9 +143,8 @@ class RedpacketService
$grabResult = $this->getGrabResult($redpacketId, $userId); $grabResult = $this->getGrabResult($redpacketId, $userId);
return [ return [
'success' => true, 'code' => 1,
'code' => 'grab_success', 'msg' => '抢红包成功',
'message' => '抢红包成功',
'data' => $grabResult 'data' => $grabResult
]; ];
@@ -167,9 +156,9 @@ class RedpacketService
$redis->sRem($userSetKey, $userId); $redis->sRem($userSetKey, $userId);
return [ return [
'success' => false, 'code' => 0,
'code' => 'system_error', 'msg' => '系统错误,请重试',
'message' => '系统错误,请重试' 'data' => null
]; ];
} }
} }
@@ -243,7 +232,8 @@ class RedpacketService
'left_amount' => $redpacket['left_amount'], 'left_amount' => $redpacket['left_amount'],
'left_count' => $redpacket['left_count'], 'left_count' => $redpacket['left_count'],
'coin_type' => $redpacket['coin_type'], 'coin_type' => $redpacket['coin_type'],
'status' => $redpacket['status'] 'status' => $redpacket['status'],
'nickname' => Db::name('user')->where('id', $redpacket['user_id'])->value('nickname')
], ],
'my_record' => $myRecord ? [ 'my_record' => $myRecord ? [
'amount' => $myRecord['amount'], 'amount' => $myRecord['amount'],
@@ -357,85 +347,6 @@ class RedpacketService
return ['code' => 1]; return ['code' => 1];
} }
/**
* 抢红包
*/
// public function grab($redpacketId, $userId, $password = '')
// {
// $redpacketModel = new Redpacket();
// $redpacket = $redpacketModel->getRedpacketInfo($redpacketId);
//
// if (!$redpacket) {
// throw new \Exception('红包不存在');
// }
//
// // 验证口令红包
// if ($redpacket['type'] == Redpacket::TYPE_PASSWORD) {
// if (empty($password) || $password != $redpacket['password']) {
// throw new \Exception('口令错误');
// }
// }
//
// // 验证领取条件
// $this->checkConditions($redpacket, $userId);
//
// // 使用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) {
// throw new \Exception($result[1]);
// }
//
// $amount = floatval($result[1]);
//
// // Lua脚本执行成功记录到数据库
// Db::startTrans();
// try {
// // 创建领取记录
// $recordData = [
// 'redpacket_id' => $redpacketId,
// 'user_id' => $userId,
// 'amount' => $amount
// ];
//
// $recordModel = new RedpacketRecord();
// $recordModel->save($recordData);
//
// // 更新用户钱包
// $walletModel = new UserWallet();
// $walletModel->increaseBalance($userId, $redpacket['coin_type'], $amount);
//
// // 更新红包剩余数量和金额
// Db::name('redpacket')
// ->where('id', $redpacketId)
// ->dec('left_amount', $amount)
// ->dec('left_count', 1)
// ->update();
//
// Db::commit();
//
// return $amount;
//
// } catch (\Exception $e) {
// Db::rollback();
// // 回滚Redis操作
// $redis->hIncrByFloat($redpacketKey, 'left_amount', $amount);
// $redis->hIncrBy($redpacketKey, 'left_count', 1);
// $redis->sRem($userSetKey, $userId);
// throw $e;
// }
// }
/** /**
* 获取红包详情和领取记录 * 获取红包详情和领取记录
@@ -528,45 +439,49 @@ class RedpacketService
} }
} }
/** /**
* 验证创建红包数据 * 验证创建红包数据
*/ */
private function validateCreateData($data) private function validateCreateData($data)
{ {
if (empty($data['user_id'])) { if (empty($data['user_id'])) {
return V(0, '用户ID不能为空'); return ['code' => 0, 'msg' => '用户ID不能为空', 'data' => null];
} }
if (!in_array($data['type'], [Redpacket::TYPE_NORMAL, Redpacket::TYPE_PASSWORD])) { if (!in_array($data['type'], [Redpacket::TYPE_NORMAL, Redpacket::TYPE_PASSWORD])) {
return V(0, '红包类型错误'); return ['code' => 0, 'msg' => '红包类型错误', 'data' => null];
} }
if ($data['type'] == Redpacket::TYPE_PASSWORD && empty($data['password'])) { if ($data['type'] == Redpacket::TYPE_PASSWORD && empty($data['password'])) {
return V(0, '口令红包必须设置口令'); return ['code' => 0, 'msg' => '口令红包必须设置口令', 'data' => null];
} }
if (!in_array($data['coin_type'], [Redpacket::COIN_GOLD, Redpacket::COIN_DIAMOND])) { if (!in_array($data['coin_type'], [Redpacket::COIN_GOLD, Redpacket::COIN_DIAMOND])) {
return V(0, '币种类型错误'); return ['code' => 0, 'msg' => '币种类型错误', 'data' => null];
} }
if ($data['total_amount'] <= 0 || $data['total_count'] <= 0) { if ($data['total_amount'] <= 0 || $data['total_count'] <= 0) {
return V(0, '金额和数量必须大于0'); return ['code' => 0, 'msg' => '金额和数量必须大于0', 'data' => null];
} }
if ($data['total_amount'] < $data['total_count']) { if ($data['total_amount'] < $data['total_count']) {
return V(0, '总金额不能小于总个数'); return ['code' => 0, 'msg' => '总金额不能小于总个数', 'data' => null];
} }
// 验证领取条件 // 验证领取条件
if (isset($data['conditions'])) { if (isset($data['conditions'])) {
$res_con = $this->validateConditions($data['conditions']); $res_con = $this->validateConditions($data['conditions']);
if ($res_con !== true) { if ($res_con !== true) {
return $res_con; return $res_con;
} }
} }
return true;
return ['code' => 1, 'msg' => '验证成功', 'data' => null];
} }
/** /**
* 验证领取条件 * 验证领取条件
*/ */