ActivityController.php 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520
  1. <?php
  2. namespace App\Http\Controllers\Game;
  3. use App\dao\Estatisticas\RechargeWithDraw;
  4. use App\Facade\TableName;
  5. use App\Game\GlobalUserInfo;
  6. use App\Game\RePayConfig;
  7. use App\Game\Services\AgentService;
  8. use App\Game\Services\LZCompressor\LZString as LZ;
  9. use App\Game\Services\OuroGameService;
  10. use App\Game\Services\RouteService;
  11. use App\Game\Services\ServerService;
  12. use App\Game\WebActivity;
  13. use App\Game\WebChannelConfig;
  14. use App\Http\Controllers\Controller;
  15. use App\Game\RedEnvelopeConfig;
  16. use App\Game\RedEnvelopeLog;
  17. use App\Http\helper\Helper;
  18. use App\Http\helper\NumConfig;
  19. use App\Jobs\AfEvent;
  20. use App\Models\AccountsInfo;
  21. use App\Models\Order;
  22. use App\Models\PrivateMail;
  23. use App\Models\RecordUserDataStatistics;
  24. use App\Models\Treasure\GameScoreInfo;
  25. use App\Notification\TelegramBot;
  26. use App\Services\OrderServices;
  27. use App\Services\HolidayWheelService;
  28. use App\Services\ChristmasWheelService;
  29. use App\Services\PayConfig;
  30. use App\Services\SmartFastPay;
  31. use App\Services\StoredProcedure;
  32. use App\Util;
  33. use App\Utility\SetNXLock;
  34. use Illuminate\Http\Request;
  35. use Carbon\Carbon;
  36. use Illuminate\Support\Facades\DB;
  37. use Illuminate\Support\Facades\Hash;
  38. use Illuminate\Support\Facades\Log;
  39. use Illuminate\Support\Facades\Redis;
  40. use PDO;
  41. class ActivityController extends Controller
  42. {
  43. public function index()
  44. {
  45. }
  46. /**
  47. * 节假日大转盘 - 获取配置与用户状态
  48. */
  49. public function holidayWheelInfo(Request $request)
  50. {
  51. $user = $request->user();
  52. $userId = $user ? $user->UserID : 0;
  53. $service = new HolidayWheelService();
  54. $info = $service->getWheelInfo($userId);
  55. return apiReturnSuc($info);
  56. }
  57. /**
  58. * 节假日大转盘 - 抽奖
  59. */
  60. public function holidayWheelSpin(Request $request)
  61. {
  62. $user = $request->user();
  63. if (!$user) {
  64. // return apiReturnFail('请先登录');
  65. return apiReturnFail(['web.activity.login_required', __('messages.web.activity.login_required')]); // 请先登录
  66. }
  67. $service = new HolidayWheelService();
  68. try {
  69. $result = $service->spin($user->UserID);
  70. return apiReturnSuc($result);
  71. } catch (\Throwable $e) {
  72. \Log::error('holiday wheel spin failed', [
  73. 'user_id' => $user->UserID,
  74. 'error' => $e->getMessage(),
  75. ]);
  76. // 这里统一返回通用错误,不向前端抛出具体异常信息
  77. return apiReturnFail(['web.activity.wheel_error', __('messages.web.activity.wheel_error')]);
  78. }
  79. }
  80. /**
  81. * 圣诞大转盘 - 获取配置与用户状态
  82. */
  83. public function christmasWheelInfo(Request $request)
  84. {
  85. $user = $request->user();
  86. $userId = $user ? $user->UserID : 0;
  87. $service = new ChristmasWheelService();
  88. $info = $service->getWheelInfo($userId);
  89. return apiReturnSuc($info);
  90. }
  91. /**
  92. * 圣诞大转盘 - 抽奖
  93. */
  94. public function christmasWheelSpin(Request $request)
  95. {
  96. $user = $request->user();
  97. if (!$user) {
  98. return apiReturnFail(['web.activity.login_required', __('messages.web.activity.login_required')]); // 请先登录
  99. }
  100. $service = new ChristmasWheelService();
  101. try {
  102. $result = $service->spin($user->UserID);
  103. return apiReturnSuc($result);
  104. } catch (\Throwable $e) {
  105. \Log::error('christmas wheel spin failed', [
  106. 'user_id' => $user->UserID,
  107. 'error' => $e->getMessage(),
  108. ]);
  109. // 这里统一返回通用错误,不向前端抛出具体异常信息
  110. return apiReturnFail(['web.activity.wheel_error', __('messages.web.activity.wheel_error')]);
  111. }
  112. }
  113. public function List(Request $request)
  114. {
  115. return apiReturnSuc(['list'=>WebActivity::query()->whereRaw(RouteService::getStateToWhereRaw($request))->get()->toArray()]);
  116. }
  117. public function GetProtectNum(Request $request)
  118. {
  119. $user=$request->user();
  120. if(ServerService::GetGlobalServerInfoByGUID($user->GlobalUID)['Subsite']=='Europe'){
  121. //欧洲暂时封闭
  122. return apiReturnSuc();
  123. }
  124. $UserID=$user->UserID;
  125. $res=DB::connection('sqlsrv')->select("exec QPAccountsDB.dbo.GSP_GP_QueryProtect $UserID");
  126. return apiReturnSuc($res);
  127. }
  128. public function AddProtectNum(Request $request)
  129. {
  130. $user=$request->user();
  131. $UserID=$user->UserID;
  132. if(ServerService::GetGlobalServerInfoByGUID($user->GlobalUID)['Subsite']=='Europe'){
  133. //欧洲暂时封闭
  134. return apiReturnSuc();
  135. }
  136. $dbh = DB::connection()->getPdo();
  137. $sql="
  138. set nocount on use QPAccountsDB;
  139. DECLARE @return_value INT;
  140. EXEC @return_value = QPAccountsDB.dbo.GSP_GP_AddProtect @dwUserID = $UserID;
  141. SELECT @return_value as ReturnValue;";
  142. $stmt = $dbh->prepare($sql);
  143. $stmt->execute();
  144. $result = $stmt->fetch(\PDO::FETCH_ASSOC);
  145. // dd($user,$result);
  146. // 获取返回值和错误描述
  147. $returnValue = $result['ReturnValue']??0;
  148. // $result=DB::connection('sqlsrv')->select('EXEC @return_value = QPAccountsDB.dbo.GSP_GP_AddProtect @dwUserID = '.$UserID.'; SELECT @return_value AS ReturnValue;');
  149. // 获取返回值
  150. // $returnValue = $result[1]->ReturnValue;
  151. if ($returnValue == 0) {
  152. return apiReturnSuc($result);
  153. } else if ($returnValue == 1) {
  154. return apiReturnFail(['web.protect.score_limit','Score limit reached']);
  155. } else if ($returnValue == 3) {
  156. return apiReturnFail(['web.protect.times_limit','Protect times limit reached']);
  157. } else {
  158. return apiReturnFail(['web.withdraw.try_again_later','Unknown error']);
  159. }
  160. }
  161. public function GetCheckInInfoConfig(Request $request)
  162. {
  163. $user=$request->user();
  164. $UserID=$user->UserID;
  165. // 使用 Redis 进程锁防止并发调用存储过程导致主键冲突
  166. $lockKey = 'checkin_get_info_' . $UserID;
  167. $lockAcquired = SetNXLock::getExclusiveLock($lockKey, 5);
  168. if (!$lockAcquired) {
  169. // 如果获取锁失败,等待一小段时间后重试一次
  170. // usleep(100000); // 等待 100ms
  171. $lockAcquired = SetNXLock::getExclusiveLock($lockKey, 5);
  172. if (!$lockAcquired) {
  173. // 如果还是获取不到锁,返回错误
  174. return apiReturnFail(['web.checkin.try_again_later', 'Please try again later']);
  175. }
  176. }
  177. try {
  178. $res=DB::connection('sqlsrv')->select("exec QPAccountsDB.dbo.GSP_GP_GetUserSignInInfo $UserID");
  179. $res2=DB::table('QPAccountsDB.dbo.UserSignInInfo')->where('UserID',$UserID)->first();
  180. $max=count($res);
  181. $res2=(array)$res2;
  182. if($res2['NextDayNumber']>$max) {
  183. $res2['NextDayNumber']= $res2['NextDayNumber'] % $max + 1;
  184. $res2['RecordDay']='';
  185. for($i=0;$i<$max;$i++) {
  186. $res2['RecordDay'].=$i<($res2['NextDayNumber']-1)?'1':'0';
  187. }
  188. }
  189. $res2['config']=$res;
  190. return apiReturnSuc($res2);
  191. } finally {
  192. // 确保释放锁
  193. SetNXLock::release($lockKey);
  194. }
  195. }
  196. public function GetCheckInInfo(Request $request)
  197. {
  198. $user=$request->user();
  199. $UserID=$user->UserID;
  200. try {
  201. // 定义输出参数
  202. $errorDescribe = '';
  203. // 调用存储过程
  204. // $result = DB::connection('sqlsrv')->select('
  205. // use QPAccountsDB;
  206. // DECLARE @strErrorDescribe NVARCHAR(127);
  207. // DECLARE @return_value INT;
  208. // EXEC @return_value = QPAccountsDB.dbo.GSP_GP_UserSignIn @dwUserID = $UserID, @strErrorDescribe = @strErrorDescribe OUTPUT;
  209. // SELECT @return_value as ReturnValue, @strErrorDescribe as ErrorDescribe;
  210. // ', [$UserID]);
  211. //
  212. $dbh = DB::connection()->getPdo();
  213. $sql="
  214. set nocount on use QPAccountsDB;
  215. DECLARE @strErrorDescribe NVARCHAR(127);
  216. DECLARE @return_value INT;
  217. EXEC @return_value = QPAccountsDB.dbo.GSP_GP_UserSignIn @dwUserID = $UserID, @strErrorDescribe = @strErrorDescribe OUTPUT;
  218. SELECT @return_value as ReturnValue, @strErrorDescribe as ErrorDescribe;";
  219. $stmt = $dbh->prepare($sql);
  220. $stmt->execute();
  221. $result = $stmt->fetch(\PDO::FETCH_ASSOC);
  222. // dd($user,$result);
  223. // 获取返回值和错误描述
  224. $returnValue = $result['ReturnValue']??0;
  225. // $errorDescribe = $result['ErrorDescribe'];
  226. // 根据返回值和错误描述处理
  227. switch ($returnValue) {
  228. case 1:
  229. return apiReturnFail(['web.checkin.user_signin_error', 'Failed to get user sign-in information']);
  230. case 2:
  231. return apiReturnFail(['web.checkin.user_already_signed_in', 'You have already signed in today']);
  232. case 3:
  233. return apiReturnFail(['web.checkin.signin_event_ended', 'The sign-in activity has ended']);
  234. case 4:
  235. return apiReturnFail(['web.checkin.signin_data_error', 'Failed to read sign-in data']);
  236. case 5:
  237. return apiReturnFail(['web.checkin.recharge_required', 'Recharge is required to sign in'],[],777);
  238. case 0:
  239. $this->triggerSignIn($UserID);
  240. return apiReturnSuc($result);
  241. default:
  242. return apiReturnFail(['web.checkin.unknown_error', 'Unknown error']);
  243. }
  244. } catch (\Exception $e) {
  245. TelegramBot::getDefault()->sendProgramNotify("Game Checkin:", $e->getMessage());
  246. // 捕获异常并返回错误响应
  247. return apiReturnFail(['web.checkin.exception_occurred', 'An error occurred while processing your request'], [], 500);
  248. }
  249. }
  250. public function RedConfig(Request $request){
  251. $config=Redis::get("RedEnvelopeConfig");
  252. if(!$config){
  253. $config=RedEnvelopeConfig::query()->get();
  254. Redis::set("RedEnvelopeConfig",json_encode($config));
  255. Redis::expire("RedEnvelopeConfig",600);
  256. }else{
  257. $config=json_decode($config,true);
  258. }
  259. return apiReturnSuc($config);
  260. }
  261. public function RedCheck(Request $request)
  262. {
  263. $maxRedpack=24680;
  264. $currentDateTime = Carbon::now();
  265. $currentTime = $currentDateTime->format('H:i:s');
  266. // 查找当前时间段内的红包配置
  267. $config = RedEnvelopeConfig::where('StartTime', '<=', $currentTime)
  268. ->where('EndTime', '>=', $currentTime)
  269. ->first();
  270. if ($config) {
  271. $getted=false;
  272. if($request->user()) {
  273. $userID=$request->user()->UserID;
  274. $checkKey = "redpack_$userID";
  275. $getted=Redis::exists($checkKey);
  276. }
  277. return apiReturnSuc(['status' => $getted?-1:1,'max'=>$maxRedpack]);
  278. } else {
  279. // 计算下一个红包时间
  280. $nextConfig = RedEnvelopeConfig::where('StartTime', '>', $currentTime)
  281. ->orderBy('StartTime', 'asc')
  282. ->first();
  283. if (!$nextConfig) {
  284. // 如果今天没有更多红包时间段,则查找明天的第一个红包时间段
  285. $nextConfig = RedEnvelopeConfig::orderBy('StartTime', 'asc')->first();
  286. $nextTime = Carbon::tomorrow()->setTimeFromTimeString($nextConfig->StartTime);
  287. } else {
  288. $nextTime = Carbon::today()->setTimeFromTimeString($nextConfig->StartTime);
  289. }
  290. $diffMinutes = $currentDateTime->diffInSeconds($nextTime);
  291. return apiReturnSuc(['status' => 0,'max'=>$maxRedpack, 'next_in_seconds' => $diffMinutes],['activity.redpack.please_wait','Please wait until next time.']);
  292. }
  293. }
  294. public function RedReceive(Request $request)
  295. {
  296. return apiReturnSuc(['status' => -1,'amount'=>0]);
  297. $user=$request->user();
  298. $userID = $user->UserID;
  299. $currentDateTime = Carbon::now();
  300. $currentTime = $currentDateTime->format('H:i:s');
  301. $checkKey="redpack_$userID";
  302. if(Redis::exists($checkKey)){
  303. return apiReturnSuc(['status' => -1,'amount'=>0]);
  304. }
  305. // 查找当前时间段内的红包配置
  306. $config = RedEnvelopeConfig::where('StartTime', '<=', $currentTime)
  307. ->where('EndTime', '>=', $currentTime)
  308. ->first();
  309. if ($config) {
  310. // 在红包活动时间段内,生成红包金额
  311. $amount = $this->generateRedEnvelopeAmount($user,$config);
  312. if(is_array($amount))return $amount;
  313. // 记录领取日志
  314. RedEnvelopeLog::create([
  315. 'UserID' => $userID,
  316. 'ReceivedAt' => $currentDateTime,
  317. 'Amount' => $amount,
  318. 'Comment' => '红包雨活动'
  319. ]);
  320. Redis::set($checkKey,1);
  321. Redis::expire($checkKey,3600);
  322. return apiReturnSuc(['amount' => $amount,'status' => 1]);
  323. } else {
  324. // 不在红包活动时间段内
  325. return apiReturnFail(['web.activity.redpack_not_available','There is no RedPack']);
  326. }
  327. }
  328. /**
  329. * @param $user
  330. * @param RedEnvelopeConfig $config
  331. * @return array|int
  332. */
  333. private function generateRedEnvelopeAmount($user, $config)
  334. {
  335. //超过限额
  336. if($config->PrizePool>=3000){
  337. return 0;
  338. }
  339. $UserID=$user->UserID;
  340. // $recharinfo=DB::connection('write')->table('QPAccountsDB.dbo.YN_VIPAccount')
  341. // ->where('UserID', $UserID)->first();
  342. // // 根据用户的充值金额决定红包金额
  343. $userTotal = DB::connection('write')->table('QPRecordDB.dbo.RecordUserTotalStatistics')
  344. ->where('UserID', $UserID)
  345. ->first();
  346. $deposit = 0;
  347. $withdraw = 0;
  348. $revenue = 0;
  349. if($userTotal){
  350. $deposit = $userTotal->Recharge;
  351. $withdraw = $userTotal->Withdraw;
  352. $revenue = $userTotal->Revenue;
  353. }
  354. $amount = 0;
  355. if ($deposit == 0) {
  356. // 未充值用户无法领取红包
  357. return apiReturnFail(['web.activity.redpack_no_recharge', 'message' => 'You need deposit first!']);
  358. } elseif ($deposit < 50) {
  359. // 50以下
  360. if (rand(1, 100) <= 50) {
  361. $amount=0;
  362. } else {
  363. $amount = rand(1, 2);
  364. }
  365. } elseif ($deposit < 100) {
  366. // 50-100
  367. if (rand(1, 100) <= 40) {
  368. $amount=0;
  369. } else {
  370. $amount = rand(1, 5);
  371. }
  372. } elseif ($deposit < 500) {
  373. // 100-500
  374. if (rand(1, 100) <= 25) {
  375. $amount=0;
  376. } else {
  377. $amount = rand(5, 10);
  378. }
  379. } elseif ($deposit < 1000) {
  380. // 500-1000
  381. if (rand(1, 100) <= 20) {
  382. $amount=0;
  383. } else {
  384. $amount = rand(5, 15);
  385. }
  386. } elseif ($deposit < 5000) {
  387. // 1000-5000
  388. if (rand(1, 100) <= 10) {
  389. $amount=0;
  390. } else {
  391. $amount = rand(10, 30);
  392. }
  393. } elseif ($deposit < 20000) {
  394. // 5000-20000
  395. $amount = rand(20, 100);
  396. } elseif ($deposit < 200000) {
  397. // 20000-200000
  398. $amount = rand(100, 150);
  399. } else {
  400. // 200000以上
  401. $amount = rand(100, 300);
  402. }
  403. if($amount){
  404. //平台资产》充 减半
  405. if($withdraw + $revenue > $deposit*100 && $deposit>1000){
  406. $rate = max(($withdraw + $revenue) / ($deposit*100) * 2,2);
  407. $amount = intval($amount/$rate);
  408. }
  409. //出》入 0
  410. if($withdraw > $deposit*100){
  411. $amount = 0;
  412. }
  413. }
  414. if($amount){
  415. $config->increment('PrizePool', $amount);
  416. OuroGameService::AddScore($UserID,$amount*NumConfig::NUM_VALUE,OuroGameService::REASON_RedEnvelope);
  417. // OuroGameService::AddDrawBase($UserID,$amount*100,2);
  418. }
  419. return $amount;
  420. }
  421. public function repayFirst()
  422. {
  423. $str="10014268,2000,BRA2851903748065310014268
  424. 10014270,2000,BRA2855489212693610014270
  425. 10014271,2000,BRA2852393071882610014271
  426. 10014274,1000,BRA2853030100749910014274
  427. 10014276,1000,BRA2853245554392310014276
  428. 10014277,2000,BRA2853344779876810014277
  429. 10014277,2000,BRA2855290186893810014277
  430. 10014277,2000,BRA2864525856913410014277
  431. 10014281,1000,BRA2854641828657010014281
  432. 10014282,2000,BRA2854744724737410014282
  433. 10014285,2000,BRA2855039889866610014285
  434. 10014287,2000,BRA2855391649287010014287
  435. 10014288,1000,BRA2855464384803010014288
  436. 10014292,2000,BRA2856246492161510014292
  437. 10014297,1000,BRA2857229048633010014297
  438. 10014300,2000,BRA2858261561088610014300
  439. 10014301,2000,BRA2858433689840310014301
  440. 10014302,1000,BRA2858522897364510014302
  441. 10014305,2000,BRA2859083559189210014305
  442. 10014306,2000,BRA2859074387476010014306
  443. 10014308,2000,BRA2859253433784710014308
  444. 10014309,2000,BRA2859266221396810014309
  445. 10014311,1000,BRA2859721671820010014311
  446. 10014314,2000,BRA2860714714541810014314
  447. 10014316,2000,BRA2861078506701410014316
  448. 10014318,2000,BRA2861340050408610014318
  449. 10014324,2000,BRA2863987369403910014324
  450. 10014326,2000,BRA2862573151411810014326
  451. 10014327,2000,BRA2862997500499910014327
  452. 10014330,1000,BRA2863297592217210014330
  453. 10014332,2000,BRA2863120608306010014332
  454. 10014335,1000,BRA2975317980447310014335
  455. 10014339,2000,BRA2864730304211810014339
  456. 10014351,2000,BRA2869670282797610014351
  457. 10014352,2000,BRA2971319703907510014352
  458. 10014358,2000,BRA2973553150552010014358
  459. 10014372,2000,BRA2995159192410010014372
  460. 10014373,2000,BRA2995376698241510014373
  461. 10014375,2000,BRA2996350286730710014375
  462. 10014383,2000,BRA2902200217093410014383";
  463. $arr=explode("\n",$str);
  464. $sort=[];
  465. $g201 = DB::table('agent.dbo.recharge_gear')->where('gift_id', 201)->select('gift_id', 'money', 'give', 'favorable_price', 'second_give')->first();
  466. $g211 = DB::table('agent.dbo.recharge_gear')->where('gift_id', 211)->select('gift_id', 'money', 'give', 'favorable_price', 'second_give')->first();
  467. foreach ($arr as $k=>$v){
  468. [$UserID,$amount,$sn]=explode(',',$v);
  469. if(isset($sort[$UserID]))continue;
  470. $sort[$UserID]=$amount;
  471. if($amount==1000)$first=$g201;
  472. else $first=$g211;
  473. if ($first && $first->second_give > 0) {
  474. $bonus = '30000,' . $amount;
  475. PrivateMail::sendMail(2, $UserID, "Suplemento de erro de primeiro depósito", "Nós adicionamos para você, por favor, aceite.", $bonus, $sn, $amount);
  476. $fpkey = 'Firstpay_' . $UserID;
  477. $data = (array)$first;
  478. $data['buytime'] = time();
  479. $data['czReason'] = 1;
  480. $data['cjReason'] = 45;
  481. Redis::set($fpkey, json_encode($data));
  482. DB::table(TableName::agent() . 'guide_payment')->updateOrInsert([
  483. 'UserID' => $UserID,
  484. 'GiftID' => $amount==1000?201:211,
  485. 'CreateTime' => now()
  486. ], ['UserID' => $UserID]);
  487. }
  488. }
  489. dd($sort);
  490. }
  491. public static function decrypt($str){
  492. if (!isset($str)||!$str||empty($str)) {
  493. # code...
  494. return '';
  495. }
  496. $str=implode("+",explode(" ",$str));
  497. try {
  498. $destr=LZ::decompressFromBase64($str);
  499. }catch (\Exception $exception){
  500. Util::WriteLog('gamelz',$str);
  501. TelegramBot::getDefault()->sendProgramNotify("Game LZ decrypt:".$str);
  502. TelegramBot::getDefault()->sendProgramNotify("Game LZ decrypt:", $exception->getMessage().$exception->getTraceAsString());
  503. $destr=$str;
  504. }
  505. return $destr;
  506. }
  507. // ==================== VIP提现诱导功能 ====================
  508. /**
  509. * 获取用户VIP提现任务进度
  510. */
  511. public function getVipWithdrawTasks(Request $request)
  512. {
  513. $user = $request->user();
  514. $userId = $user->UserID;
  515. // 检查用户是否已充值
  516. $user_recharge = DB::table(TableName::QPAccountsDB() . 'YN_VIPAccount')
  517. ->where('UserID', $userId)
  518. ->value('Recharge') ?: 0;
  519. if ($user_recharge <= 0 &&!$request->input('is_test', 0)) {
  520. // return apiReturnFail('请先充值后再查看任务-'.$userId);
  521. return apiReturnFail(['web.vip_task.recharge_first', __('messages.web.vip_task.recharge_first')]); // 请先充值后再查看任务
  522. }
  523. // 获取用户任务数据
  524. $taskData = $this->getUserTaskData($userId);
  525. // 获取任务配置
  526. $taskConfig = $this->getTaskConfig();
  527. // 判断奖励状态:0-不能领取, 1-待领取, 2-已领取
  528. $stage1_reward_status = 0; // 不能领取
  529. if ($taskData['stage1_completed']) {
  530. $stage1_reward_status = $taskData['stage1_withdrawn'] ? 2 : 1; // 已领取 : 待领取
  531. }
  532. $stage2_reward_status = 0; // 不能领取
  533. if ($taskData['stage1_completed']) { // 阶段2需要阶段1完成才能解锁
  534. if ($taskData['stage2_completed']) {
  535. $stage2_reward_status = $taskData['stage2_withdrawn'] ? 2 : 1; // 已领取 : 待领取
  536. }
  537. }
  538. $stage3_reward_status = 0; // 不能领取
  539. if ($taskData['stage2_completed']) { // 阶段3需要阶段2完成才能解锁
  540. // 循环任务:需要充值总额和下注流水两个条件都完成才能领取
  541. $rechargeTarget = $taskData['stage3_recharge_target'] ?? 200;
  542. $betTarget = $taskData['stage3_bet_target'] ?? 1000;
  543. $rechargeProgress = $taskData['stage3_recharge_progress'] ?? 0;
  544. $betProgress = $taskData['stage3_bet_progress'] ?? 0;
  545. // 两个条件都完成才能领取奖励
  546. if ($rechargeProgress >= $rechargeTarget && $betProgress >= $betTarget) {
  547. $stage3_reward_status = 1; // 待领取
  548. }
  549. }
  550. // 构建阶段1任务列表
  551. $stage1Tasks = [];
  552. foreach ($taskConfig['stage1']['tasks'] as $taskCfg) {
  553. $taskKey = $taskCfg['id'];
  554. $progressKey = $taskCfg['progress_key'];
  555. $completed = $taskData[$taskKey] ?? false;
  556. $stage1Tasks[] = [
  557. 'id' => $taskCfg['id'],
  558. 'title' => $taskCfg['title'],
  559. 'type' => $taskCfg['type'],
  560. 'completed' => $completed,
  561. 'status' => $completed ? 1 : 0,
  562. // 'status_text' => $completed ? '已完成' : '未完成',
  563. 'status_text' => $completed ? __('messages.web.vip_task.completed') : __('messages.web.vip_task.not_completed'), // 已完成 : 未完成
  564. 'progress' => intval(min($taskData[$progressKey] ?? 0,$taskCfg['target'])),
  565. 'target' => $taskCfg['target']
  566. ];
  567. }
  568. // 构建任务结构
  569. $tasks = [
  570. 'stage1' => [
  571. 'stage' => 1,
  572. // 'title' => '阶段任务1',
  573. 'title' => __('messages.web.vip_task.stage1_title'), // 阶段任务1
  574. 'reward' => $taskConfig['stage1']['reward'],
  575. 'completed' => $taskData['stage1_completed'],
  576. 'reward_status' => $stage1_reward_status,
  577. // 'reward_status_text' => ['不能领取', '待领取', '已领取'][$stage1_reward_status],
  578. 'reward_status_text' => [__('messages.web.vip_task.cannot_claim'), __('messages.web.vip_task.can_claim'), __('messages.web.vip_task.claimed')][$stage1_reward_status], // 不能领取, 待领取, 已领取
  579. 'can_claim' => $stage1_reward_status === 1,
  580. 'tasks' => $stage1Tasks
  581. ],
  582. 'stage2' => [
  583. 'stage' => 2,
  584. // 'title' => '阶段任务2',
  585. 'title' => __('messages.web.vip_task.stage2_title'), // 阶段任务2
  586. 'reward' => $taskConfig['stage2']['reward'],
  587. 'unlocked' => $taskData['stage1_completed'],
  588. 'completed' => $taskData['stage2_completed'],
  589. 'reward_status' => $stage2_reward_status,
  590. // 'reward_status_text' => ['不能领取', '待领取', '已领取'][$stage2_reward_status],
  591. 'reward_status_text' => [__('messages.web.vip_task.cannot_claim'), __('messages.web.vip_task.can_claim'), __('messages.web.vip_task.claimed')][$stage2_reward_status], // 不能领取, 待领取, 已领取
  592. 'can_claim' => $stage2_reward_status === 1,
  593. 'tasks' => array_map(function($taskCfg) use ($taskData) {
  594. $taskKey = $taskCfg['id'];
  595. $progressKey = $taskCfg['progress_key'];
  596. $completed = $taskData[$taskKey] ?? false;
  597. return [
  598. 'id' => $taskCfg['id'],
  599. 'title' => $taskCfg['title'],
  600. 'type' => $taskCfg['type'],
  601. 'completed' => $completed,
  602. 'status' => $completed ? 1 : 0,
  603. // 'status_text' => $completed ? '已完成' : '未完成',
  604. 'status_text' => $completed ? __('messages.web.vip_task.completed') : __('messages.web.vip_task.not_completed'), // 已完成 : 未完成
  605. 'progress' => intval(min($taskData[$progressKey] ?? 0,$taskCfg['target'])),
  606. 'target' => $taskCfg['target']
  607. ];
  608. }, $taskConfig['stage2']['tasks'])
  609. ],
  610. 'stage3' => [
  611. 'stage' => 3,
  612. // 'title' => '阶段任务3(循环任务)',
  613. 'title' => __('messages.web.vip_task.stage3_title'), // 阶段任务3(循环任务)
  614. // 根据循环次数判断奖励:第一次(loop_count=0或不存在)奖励20,之后每次10
  615. 'reward' => (($taskData['stage3_loop_count'] ?? 0) == 0) ? 20 : 10,
  616. 'unlocked' => $taskData['stage2_completed'],
  617. 'completed' => ($taskData['stage3_task1'] ?? false) && ($taskData['stage3_task2'] ?? false),
  618. 'reward_status' => $stage3_reward_status,
  619. // 'reward_status_text' => ['不能领取', '待领取', '已领取'][$stage3_reward_status],
  620. 'reward_status_text' => [__('messages.web.vip_task.cannot_claim'), __('messages.web.vip_task.can_claim'), __('messages.web.vip_task.claimed')][$stage3_reward_status], // 不能领取, 待领取, 已领取
  621. 'can_claim' => $stage3_reward_status === 1,
  622. 'is_loop' => true,
  623. 'current_loop' => $taskData['stage3_loop_count'],
  624. 'tasks' => array_map(function($taskCfg) use ($taskData) {
  625. $taskKey = $taskCfg['id'];
  626. $progressKey = $taskCfg['progress_key'] ?? '';
  627. $targetKey = $taskCfg['target_key'] ?? '';
  628. // 获取当前进度和目标值
  629. $currentProgress = $taskData[$progressKey] ?? 0;
  630. $currentTarget = $targetKey ? ($taskData[$targetKey] ?? $taskCfg['target']) : $taskCfg['target'];
  631. // 判断是否完成
  632. $completed = $currentProgress >= $currentTarget;
  633. // 动态生成任务标题(使用模板替换目标值)
  634. $title = $taskCfg['title_template'] ?? $taskCfg['title'] ?? '';
  635. if (!empty($title) && strpos($title, '{target}') !== false) {
  636. $title = str_replace('{target}', $currentTarget, $title);
  637. }
  638. return [
  639. 'id' => $taskCfg['id'],
  640. 'title' => $title,
  641. 'type' => $taskCfg['type'],
  642. 'completed' => $completed,
  643. 'status' => $completed ? 1 : 0,
  644. // 'status_text' => $completed ? '已完成' : '未完成',
  645. 'status_text' => $completed ? __('messages.web.vip_task.completed') : __('messages.web.vip_task.not_completed'), // 已完成 : 未完成
  646. 'progress' => intval(min($currentProgress, $currentTarget)),
  647. 'target' => $currentTarget
  648. ];
  649. }, $taskConfig['stage3']['tasks'])
  650. ]
  651. ];
  652. return apiReturnSuc($tasks);
  653. }
  654. /**
  655. * 领取任务奖励(提现)
  656. */
  657. public function claimVipWithdrawReward(Request $request)
  658. {
  659. $user = $request->user();
  660. $userId = $user->UserID;
  661. $stage = (int)$request->input('stage');
  662. $taskId = $request->input('task_id', '');
  663. $redisLockKey = "vip_withdraw_claim_{$userId}";
  664. if (!Redis::set($redisLockKey, 1, 'EX', 10, 'NX')) {
  665. // return apiReturnFail('操作太频繁,请稍后再试');
  666. return apiReturnFail(['web.vip_task.operation_frequent', __('messages.web.vip_task.operation_frequent')]); //'操作太频繁,请稍后再试'
  667. }
  668. try {
  669. // 获取用户任务数据
  670. $taskData = $this->getUserTaskData($userId);
  671. $rewardAmount = 0;
  672. // 阶段1
  673. if ($stage === 1) {
  674. if (!$taskData['stage1_completed']) {
  675. Redis::del($redisLockKey);
  676. // return apiReturnFail('任务未完成');
  677. return apiReturnFail(['web.vip_task.task_not_completed', __('messages.web.vip_task.task_not_completed')]); // 任务未完成
  678. }
  679. if ($taskData['stage1_withdrawn']) {
  680. Redis::del($redisLockKey);
  681. return apiReturnFail(['web.vip_task.reward_claimed', __('messages.web.vip_task.reward_claimed')]); // 奖励已领取
  682. // return apiReturnFail('奖励已领取');
  683. }
  684. $rewardAmount = 10;
  685. $this->updateTaskProgress($userId, 'stage1_withdrawn', 1);
  686. }
  687. // 阶段2
  688. elseif ($stage === 2) {
  689. if (!$taskData['stage2_completed']) {
  690. Redis::del($redisLockKey);
  691. // return apiReturnFail('任务未完成');
  692. return apiReturnFail(['web.vip_task.task_not_completed', __('messages.web.vip_task.task_not_completed')]); // 任务未完成
  693. }
  694. if ($taskData['stage2_withdrawn']) {
  695. Redis::del($redisLockKey);
  696. return apiReturnFail(['web.vip_task.reward_claimed', __('messages.web.vip_task.reward_claimed')]); // 奖励已领取
  697. // return apiReturnFail('奖励已领取');
  698. }
  699. $rewardAmount = 10;
  700. $this->updateTaskProgress($userId, 'stage2_withdrawn', 1);
  701. }
  702. // 阶段3
  703. elseif ($stage === 3) {
  704. $taskId = $request->input('task_id', 'stage3_task1');
  705. // 检查两个条件是否都完成
  706. $rechargeTarget = $taskData['stage3_recharge_target'] ?? 200;
  707. $betTarget = $taskData['stage3_bet_target'] ?? 1000;
  708. $rechargeProgress = $taskData['stage3_recharge_progress'] ?? 0;
  709. $betProgress = $taskData['stage3_bet_progress'] ?? 0;
  710. if ($rechargeProgress < $rechargeTarget || $betProgress < $betTarget) {
  711. Redis::del($redisLockKey);
  712. return apiReturnFail(['web.vip_task.task_not_completed', __('messages.web.vip_task.task_not_completed')]); // 任务未完成
  713. }
  714. // 根据循环次数判断奖励:第一次(loop_count=0或不存在)奖励20,之后每次10
  715. $currentLoopCount = $taskData['stage3_loop_count'] ?? 0;
  716. $rewardAmount = ($currentLoopCount == 0) ? 20 : 10;
  717. // 领取后刷新任务:充值目标+100,下注目标+1000
  718. $newRechargeTarget = $rechargeTarget + 100;
  719. $newBetTarget = $betTarget + 1000;
  720. $newLoopCount = $currentLoopCount + 1;
  721. // 更新任务数据
  722. $redisKey = "vip_withdraw_task_{$userId}";
  723. $data = Redis::get($redisKey);
  724. if ($data) {
  725. $taskData = json_decode($data, true);
  726. $taskData['stage3_recharge_target'] = $newRechargeTarget;
  727. $taskData['stage3_bet_target'] = $newBetTarget;
  728. $taskData['stage3_loop_count'] = $newLoopCount;
  729. $taskData['stage3_task1'] = false;
  730. $taskData['stage3_task2'] = false;
  731. // 保存到数据库
  732. $this->saveUserTaskData($userId, $taskData);
  733. // 更新Redis缓存
  734. Redis::setex($redisKey, 86400, json_encode($taskData));
  735. }
  736. } else {
  737. Redis::del($redisLockKey);
  738. return apiReturnFail(['web.vip_task.invalid_stage', __('messages.web.vip_task.invalid_stage')]); // 无效的阶段
  739. // return apiReturnFail('无效的阶段');
  740. }
  741. // 添加提现额度到用户账户
  742. if ($rewardAmount > 0) {
  743. $dbh = DB::connection()->getPdo();
  744. $score=$rewardAmount*NumConfig::NUM_VALUE;
  745. $sql="DECLARE @return_value int
  746. set nocount on use QPAccountsDB
  747. EXEC @return_value = GSP_GR_GetFreeWithDraw '$userId','$score'
  748. SELECT 'ReturnValue' = @return_value";
  749. $stmt = $dbh->prepare($sql);
  750. $stmt->execute();
  751. $retval = $stmt->fetch(\PDO::FETCH_ASSOC);
  752. try {
  753. // set @ret = 1 --可提额度不足
  754. // set @ret = 2 --低于最低可提现金额
  755. // set @ret = 3 --高于最高可提现金额
  756. // set @ret = 4 --超过每天提现次数上限
  757. // set @ret = 5 --身上钱不够
  758. // set @ret = 6 --开在游戏里面
  759. // set @ret = 7 --未充值的用户TX额度
  760. if (isset($retval['ReturnValue'])) {
  761. $ReturnValue = $retval['ReturnValue'];
  762. // set @ret = 1 -- Insufficient withdrawal amount
  763. // set @ret = 2 -- Lower than the minimum withdrawal amount
  764. // set @ret = 3 -- Higher than the maximum withdrawal amount
  765. // set @ret = 4 -- Exceeded the daily withdrawal limit
  766. // set @ret = 5 -- Not enough money on hand
  767. // set @ret = 6 -- Opened in the game
  768. // set @ret = 7 -- Undeposited user TX amount
  769. $errors = [
  770. [],
  771. ['web.withdraw.no_withdraw_value', "The withdrawable amount is not enough"],
  772. ['web.withdraw.too_low', 'Lower than the minimum withdrawal amount'],
  773. ['web.withdraw.too_high', 'Higher than the maximum withdrawal amount'],
  774. ['web.withdraw.day_max', 'Exceeded the daily withdrawal limit'],
  775. ['web.withdraw.no_enouth_score', 'Not enough money on hand'],
  776. ['web.withdraw.in_game', 'Sorry, You\'re in game for now'],
  777. ['web.withdraw.no_deposit', ' You need deposit first!'],
  778. ];
  779. return apiReturnFail($errors[$ReturnValue], [], $ReturnValue == 7 ? 777 : 301);
  780. }else{
  781. //return apiReturnSuc($retval);
  782. }
  783. }catch (\Exception $e){
  784. // TelegramBot::getDefault()->sendProgramNotify("WithDraw Error:", var_export($retval,true).':'.$e->getTraceAsString());
  785. //return apiReturnSuc($retval);
  786. }
  787. // 记录日志
  788. \Log::info('VIP提现任务奖励', [
  789. 'user_id' => $userId,
  790. 'stage' => $stage,
  791. 'task_id' => $taskId,
  792. 'amount' => $rewardAmount
  793. ]);
  794. }
  795. Redis::del($redisLockKey);
  796. return apiReturnSuc([
  797. 'amount' => $rewardAmount,
  798. // 'message' => "成功领取 {$rewardAmount} 提现额度"
  799. 'message' => __('messages.web.vip_task.claim_success', ['amount' => $rewardAmount]) // "成功领取 {$rewardAmount} 提现额度"
  800. ]);
  801. } catch (\Exception $e) {
  802. Redis::del($redisLockKey);
  803. \Log::error('VIP提现任务领取失败', [
  804. 'user_id' => $userId,
  805. 'error' => $e->getMessage()
  806. ]);
  807. return apiReturnFail(['web.vip_task.claim_failed', __('messages.web.vip_task.claim_failed', ['error' => $e->getMessage()])]); // 领取失败:
  808. // return apiReturnFail('领取失败:' . $e->getMessage());
  809. }
  810. }
  811. /**
  812. * 获取用户任务数据(从Redis和数据库)
  813. */
  814. private function getUserTaskData($userId)
  815. {
  816. $redisKey = "vip_withdraw_task_{$userId}";
  817. $data = Redis::get($redisKey);
  818. if ($data) {
  819. $taskData = json_decode($data, true);
  820. } else {
  821. // 从数据库加载任务数据
  822. $dbData = DB::table('agent.dbo.vip_withdraw_tasks')
  823. ->where('user_id', $userId)
  824. ->first();
  825. if ($dbData) {
  826. $taskData = json_decode($dbData->task_data, true);
  827. } else {
  828. // 初始化任务数据
  829. $taskData = $this->initUserTaskData($userId);
  830. }
  831. }
  832. // 实时更新统计数据
  833. $this->updateRealTimeStats($userId, $taskData);
  834. // 检查任务完成状态
  835. $this->checkTaskCompletion($taskData);
  836. // 保存到数据库(持久化)
  837. $this->saveUserTaskData($userId, $taskData);
  838. // 保存到Redis(缓存24小时)
  839. Redis::setex($redisKey, 86400, json_encode($taskData));
  840. return $taskData;
  841. }
  842. /**
  843. * 保存用户任务数据到数据库
  844. */
  845. private function saveUserTaskData($userId, $taskData)
  846. {
  847. try {
  848. DB::table('agent.dbo.vip_withdraw_tasks')->updateOrInsert(
  849. ['user_id' => $userId],
  850. [
  851. 'task_data' => json_encode($taskData, JSON_UNESCAPED_UNICODE),
  852. 'updated_at' => date('Y-m-d H:i:s')
  853. ]
  854. );
  855. } catch (\Exception $e) {
  856. \Log::error('VIP提现任务数据保存失败', [
  857. 'user_id' => $userId,
  858. 'error' => $e->getMessage()
  859. ]);
  860. }
  861. }
  862. /**
  863. * 获取任务配置
  864. */
  865. private function getTaskConfig()
  866. {
  867. return [
  868. 'stage1' => [
  869. 'reward' => 10,
  870. 'tasks' => [
  871. [
  872. 'id' => 'stage1_task1',
  873. 'title' => 'Claim daily bonus once',
  874. 'type' => 'sign_in',
  875. 'target' => 1,
  876. 'progress_key' => 'sign_in_count'
  877. ],
  878. [
  879. 'id' => 'stage1_task2',
  880. 'title' => 'Bet amount reaches 100',
  881. 'type' => 'bet_amount',
  882. 'target' => 100,
  883. 'progress_key' => 'total_bet'
  884. ],
  885. [
  886. 'id' => 'stage1_task3',
  887. 'title' => 'Deposit amount reaches 29',
  888. 'type' => 'recharge',
  889. 'target' => 29,
  890. 'progress_key' => 'recharge_count'
  891. ]
  892. ]
  893. ],
  894. 'stage2' => [
  895. 'reward' => 10,
  896. 'tasks' => [
  897. [
  898. 'id' => 'stage2_task1',
  899. 'title' => 'Deposit amount reaches 100',
  900. 'type' => 'recharge',
  901. 'target' => 100,
  902. 'progress_key' => 'played_games_count'
  903. ],
  904. [
  905. 'id' => 'stage2_task2',
  906. 'title' => 'Bet amount reaches 200',
  907. 'type' => 'bet_amount',
  908. 'target' => 200,
  909. 'progress_key' => 'total_bet'
  910. ],
  911. [
  912. 'id' => 'stage2_task3',
  913. 'title' => 'Start inviting once',
  914. 'type' => 'invite',
  915. 'target' => 1,
  916. 'progress_key' => 'invite_count'
  917. ]
  918. ]
  919. ],
  920. 'stage3' => [
  921. 'tasks' => [
  922. [
  923. 'id' => 'stage3_task1',
  924. 'title_template' => 'Deposit amount reaches {target}', // 使用模板,动态替换目标值
  925. 'type' => 'recharge',
  926. 'target' => 200,
  927. 'reward' => 20,
  928. 'progress_key' => 'stage3_recharge_progress',
  929. 'target_key' => 'stage3_recharge_target'
  930. ],
  931. [
  932. 'id' => 'stage3_task2',
  933. 'title_template' => 'Bet amount reaches {target}', // 使用模板,动态替换目标值
  934. 'type' => 'bet_amount',
  935. 'target' => 1000,
  936. 'reward' => 20,
  937. 'progress_key' => 'stage3_bet_progress',
  938. 'target_key' => 'stage3_bet_target'
  939. ]
  940. ]
  941. ]
  942. ];
  943. }
  944. /**
  945. * 初始化用户任务数据
  946. */
  947. private function initUserTaskData($userId)
  948. {
  949. return [
  950. // 阶段1任务
  951. 'stage1_task1' => false,
  952. 'stage1_task2' => false,
  953. 'stage1_task3' => false,
  954. 'stage1_completed' => false,
  955. 'stage1_withdrawn' => false,
  956. // 阶段2任务
  957. 'stage2_task1' => false,
  958. 'stage2_task2' => false,
  959. 'stage2_task3' => false,
  960. 'stage2_completed' => false,
  961. 'stage2_withdrawn' => false,
  962. // 阶段3任务(循环)
  963. 'stage3_task1' => false,
  964. 'stage3_task2' => false,
  965. 'stage3_task1_withdrawn' => false,
  966. 'stage3_loop_count' => 0,
  967. 'stage3_recharge_target' => 200, // 充值目标(初始200,每次+100)
  968. 'stage3_bet_target' => 1000, // 下注目标(初始1000,每次+1000)
  969. 'stage3_recharge_progress' => 0, // 当前充值总额
  970. 'stage3_bet_progress' => 0, // 当前下注流水
  971. 'stage3_current_bet' => 0,
  972. 'stage3_available_amount' => 0,
  973. // 统计数据
  974. 'total_bet' => 0,
  975. 'recharge_count' => 0,
  976. 'played_games_count' => 0,
  977. 'invite_count' => 0,
  978. 'sign_in_count' => 0
  979. ];
  980. }
  981. /**
  982. * 更新实时统计数据
  983. */
  984. private function updateRealTimeStats($userId, &$taskData)
  985. {
  986. $userTotalStatistics = DB::table('QPRecordDB.dbo.RecordUserTotalStatistics')
  987. ->where('UserID', $userId)
  988. ->first();
  989. // 获取总下注金额
  990. $totalBet =$userTotalStatistics ?$userTotalStatistics->TotalBet: 0;
  991. $taskData['total_bet'] = $totalBet / NumConfig::NUM_VALUE;
  992. // 获取充值次数
  993. $rechargeCount = $userTotalStatistics?$userTotalStatistics->Recharge:0;
  994. $taskData['recharge_count'] = min($rechargeCount,29);
  995. // 获取玩过的游戏数量(去重)
  996. // $playedGames = DB::table('QPRecordDB.dbo.RecordUserGameCount')
  997. // ->where('UserID', $userId)
  998. // ->distinct()
  999. // ->count('GameID');
  1000. $taskData['played_games_count'] = min($rechargeCount,100);;
  1001. // 邀请人数从Redis读取(通过 triggerInvite 方法触发)
  1002. if (!isset($taskData['invite_count'])) {
  1003. $taskData['invite_count'] = 0;
  1004. }
  1005. // 签到次数从Redis读取(通过 triggerSignIn 方法触发)
  1006. if (!isset($taskData['sign_in_count'])) {
  1007. $taskData['sign_in_count'] = 0;
  1008. }
  1009. // 如存在未充值前的签到标记,且在同一天内完成充值,则补记签到
  1010. $pendingKey = "vip_withdraw_sign_pending_{$userId}";
  1011. $pendingSign = Redis::get($pendingKey);
  1012. if ($pendingSign && $pendingSign == date('Ymd')) {
  1013. $userRecharge = DB::table(TableName::QPAccountsDB() . 'YN_VIPAccount')
  1014. ->where('UserID', $userId)
  1015. ->value('Recharge') ?: 0;
  1016. if ($userRecharge > 0) {
  1017. if (($taskData['sign_in_count'] ?? 0) < 1) {
  1018. $taskData['sign_in_count'] = 1;
  1019. }
  1020. \Log::info('VIP提现任务-签到补记成功(同日充值)', [
  1021. 'user_id' => $userId,
  1022. 'sign_in_count' => $taskData['sign_in_count']
  1023. ]);
  1024. }
  1025. }
  1026. if ($pendingSign) {
  1027. Redis::del($pendingKey);
  1028. }
  1029. // 如存在“未充值前的签到标记”,且用户已完成充值,则补记签到次数
  1030. $pendingKey = "vip_withdraw_sign_pending_{$userId}";
  1031. if (Redis::exists($pendingKey)) {
  1032. $userRecharge = DB::table(TableName::QPAccountsDB() . 'YN_VIPAccount')
  1033. ->where('UserID', $userId)
  1034. ->value('Recharge') ?: 0;
  1035. if ($userRecharge > 0) {
  1036. if (($taskData['sign_in_count'] ?? 0) < 1) {
  1037. $taskData['sign_in_count'] = 1;
  1038. }
  1039. Redis::del($pendingKey);
  1040. \Log::info('VIP提现任务-签到补记成功', [
  1041. 'user_id' => $userId,
  1042. 'sign_in_count' => $taskData['sign_in_count']
  1043. ]);
  1044. }
  1045. }
  1046. // 阶段3任务进度更新
  1047. if ($taskData['stage2_completed'] ?? false) {
  1048. // 获取充值总额(从 RecordUserTotalStatistics)
  1049. $rechargeTotal = $userTotalStatistics ? ($userTotalStatistics->Recharge) : 0;
  1050. $taskData['stage3_recharge_progress'] = $rechargeTotal;
  1051. // 获取下注流水(使用 total_bet)
  1052. $taskData['stage3_bet_progress'] = $taskData['total_bet'] ?? 0;
  1053. // 初始化目标值(如果不存在)
  1054. if (!isset($taskData['stage3_recharge_target'])) {
  1055. $taskData['stage3_recharge_target'] = 200;
  1056. }
  1057. if (!isset($taskData['stage3_bet_target'])) {
  1058. $taskData['stage3_bet_target'] = 1000;
  1059. }
  1060. }
  1061. // 保留原有的 stage3_current_bet 逻辑(用于兼容)
  1062. if (!isset($taskData['stage3_current_bet'])) {
  1063. $taskData['stage3_current_bet'] = 0;
  1064. }
  1065. if (!isset($taskData['stage3_last_total_bet'])) {
  1066. $taskData['stage3_last_total_bet'] = $totalBet; // 记录原始总下注(原始单位)
  1067. }
  1068. // 计算本次新增下注(转为金额单位)
  1069. $deltaBet = $totalBet - ($taskData['stage3_last_total_bet'] ?? 0);
  1070. if ($deltaBet > 0) {
  1071. $taskData['stage3_current_bet'] += ($deltaBet / NumConfig::NUM_VALUE);
  1072. $taskData['stage3_last_total_bet'] = $totalBet;
  1073. }
  1074. }
  1075. /**
  1076. * 检查任务完成状态
  1077. */
  1078. private function checkTaskCompletion(&$taskData)
  1079. {
  1080. // 阶段1任务检查
  1081. $taskData['stage1_task1'] = $taskData['sign_in_count'] >= 1;
  1082. $taskData['stage1_task2'] = $taskData['total_bet'] >= 100;
  1083. $taskData['stage1_task3'] = $taskData['recharge_count'] >= 29;
  1084. $taskData['stage1_completed'] = $taskData['stage1_task1'] &&
  1085. $taskData['stage1_task2'] &&
  1086. $taskData['stage1_task3'];
  1087. // 阶段2任务检查
  1088. if ($taskData['stage1_completed']) {
  1089. $taskData['stage2_task1'] = $taskData['played_games_count'] >= 100;
  1090. $taskData['stage2_task2'] = $taskData['total_bet'] >= 200;
  1091. $taskData['stage2_task3'] = $taskData['invite_count'] >= 1;
  1092. $taskData['stage2_completed'] = $taskData['stage2_task1'] &&
  1093. $taskData['stage2_task2'] &&
  1094. $taskData['stage2_task3'];
  1095. }
  1096. // 阶段3任务检查(循环):需要充值总额和下注流水两个条件都完成
  1097. if ($taskData['stage2_completed']) {
  1098. $rechargeTarget = $taskData['stage3_recharge_target'] ?? 200;
  1099. $betTarget = $taskData['stage3_bet_target'] ?? 1000;
  1100. $rechargeProgress = $taskData['stage3_recharge_progress'] ?? 0;
  1101. $betProgress = $taskData['stage3_bet_progress'] ?? 0;
  1102. $taskData['stage3_task1'] = $rechargeProgress >= $rechargeTarget;
  1103. $taskData['stage3_task2'] = $betProgress >= $betTarget;
  1104. }
  1105. }
  1106. /**
  1107. * 更新任务进度
  1108. */
  1109. private function updateTaskProgress($userId, $key, $value)
  1110. {
  1111. $redisKey = "vip_withdraw_task_{$userId}";
  1112. $data = Redis::get($redisKey);
  1113. if ($data) {
  1114. $taskData = json_decode($data, true);
  1115. $taskData[$key] = $value;
  1116. // ✅ 保存到数据库(持久化)
  1117. $this->saveUserTaskData($userId, $taskData);
  1118. // ✅ 更新Redis缓存(24小时)
  1119. Redis::setex($redisKey, 86400, json_encode($taskData));
  1120. }
  1121. }
  1122. /**
  1123. * 重置阶段3循环任务
  1124. */
  1125. private function resetStage3Tasks($userId)
  1126. {
  1127. $redisKey = "vip_withdraw_task_{$userId}";
  1128. $data = Redis::get($redisKey);
  1129. if ($data) {
  1130. $taskData = json_decode($data, true);
  1131. $taskData['stage3_task1'] = false;
  1132. $taskData['stage3_task1_withdrawn'] = false;
  1133. $taskData['stage3_current_bet'] = 0;
  1134. $taskData['stage3_cycle_start'] = (int)date('Ymd');
  1135. $taskData['stage3_loop_count'] = ($taskData['stage3_loop_count'] ?? 0) + 1;
  1136. // ✅ 保存到数据库(持久化)
  1137. $this->saveUserTaskData($userId, $taskData);
  1138. // ✅ 更新Redis缓存(24小时)
  1139. Redis::setex($redisKey, 86400, json_encode($taskData));
  1140. }
  1141. }
  1142. /**
  1143. * 手动触发签到(供其他地方调用)
  1144. * 注意:只有充值用户才会计数
  1145. */
  1146. public function triggerSignIn($userId)
  1147. {
  1148. // 检查用户是否已充值(仅用于记录日志,不再阻断签到进度)
  1149. $user_recharge = DB::table(TableName::QPAccountsDB() . 'YN_VIPAccount')
  1150. ->where('UserID', $userId)
  1151. ->value('Recharge') ?: 0;
  1152. $hasRecharge = $user_recharge > 0;
  1153. $pendingKey = "vip_withdraw_sign_pending_{$userId}";
  1154. if (!$hasRecharge) {
  1155. // 未充值用户,先把签到标记在Redis中,等待充值后结算(仅限同一天)
  1156. Redis::setex($pendingKey, 86400, date('Ymd')); // 标记签到日期
  1157. \Log::info('VIP提现任务-签到记录(待充值)', [
  1158. 'user_id' => $userId,
  1159. 'note' => '用户尚未充值,签到标记已暂存Redis',
  1160. 'redis_key' => $pendingKey
  1161. ]);
  1162. return true;
  1163. }
  1164. // 用户已充值,如存在待结算签到标记,检查是否为同一天
  1165. $pendingSign = Redis::get($pendingKey);
  1166. if ($pendingSign && $pendingSign == date('Ymd')) {
  1167. // 同一天内补记签到次数(若尚未计数)
  1168. $redisKey = "vip_withdraw_task_{$userId}";
  1169. $data = Redis::get($redisKey);
  1170. if ($data) {
  1171. $taskData = json_decode($data, true);
  1172. if (($taskData['sign_in_count'] ?? 0) < 1) {
  1173. $taskData['sign_in_count'] = 1;
  1174. $this->saveUserTaskData($userId, $taskData);
  1175. Redis::setex($redisKey, 86400, json_encode($taskData));
  1176. }
  1177. } else {
  1178. $dbData = DB::table('agent.dbo.vip_withdraw_tasks')
  1179. ->where('user_id', $userId)
  1180. ->first();
  1181. if ($dbData) {
  1182. $taskData = json_decode($dbData->task_data, true);
  1183. } else {
  1184. $taskData = $this->initUserTaskData($userId);
  1185. }
  1186. if (($taskData['sign_in_count'] ?? 0) < 1) {
  1187. $taskData['sign_in_count'] = 1;
  1188. $this->saveUserTaskData($userId, $taskData);
  1189. }
  1190. Redis::setex($redisKey, 86400, json_encode($taskData));
  1191. }
  1192. \Log::info('VIP提现任务-签到补记成功(同日充值)', [
  1193. 'user_id' => $userId
  1194. ]);
  1195. }
  1196. Redis::del($pendingKey);
  1197. $redisKey = "vip_withdraw_task_{$userId}";
  1198. $data = Redis::get($redisKey);
  1199. if ($data) {
  1200. $taskData = json_decode($data, true);
  1201. $taskData['sign_in_count'] = ($taskData['sign_in_count'] ?? 0) + 1;
  1202. // ✅ 保存到数据库(持久化)
  1203. $this->saveUserTaskData($userId, $taskData);
  1204. // ✅ 更新Redis缓存(24小时)
  1205. Redis::setex($redisKey, 86400, json_encode($taskData));
  1206. \Log::info('VIP提现任务-签到触发成功', [
  1207. 'user_id' => $userId,
  1208. 'sign_in_count' => $taskData['sign_in_count']
  1209. ]);
  1210. return true;
  1211. } else {
  1212. // 如果Redis没有数据,从数据库加载或初始化
  1213. $dbData = DB::table('agent.dbo.vip_withdraw_tasks')
  1214. ->where('user_id', $userId)
  1215. ->first();
  1216. if ($dbData) {
  1217. $taskData = json_decode($dbData->task_data, true);
  1218. } else {
  1219. $taskData = $this->initUserTaskData($userId);
  1220. }
  1221. $taskData['sign_in_count'] = ($taskData['sign_in_count'] ?? 0) + 1;
  1222. // ✅ 保存到数据库(持久化)
  1223. $this->saveUserTaskData($userId, $taskData);
  1224. // ✅ 更新Redis缓存(24小时)
  1225. Redis::setex($redisKey, 86400, json_encode($taskData));
  1226. \Log::info('VIP提现任务-签到触发成功(首次)', [
  1227. 'user_id' => $userId,
  1228. 'sign_in_count' => 1
  1229. ]);
  1230. return true;
  1231. }
  1232. }
  1233. /**
  1234. * 手动触发邀请(供其他地方调用)
  1235. * 注意:只有充值用户且阶段1完成后才会计数
  1236. */
  1237. public function triggerInvite($userId)
  1238. {
  1239. // 检查用户是否已充值
  1240. $user_recharge = DB::table(TableName::QPAccountsDB() . 'YN_VIPAccount')
  1241. ->where('UserID', $userId)
  1242. ->value('Recharge') ?: 0;
  1243. if ($user_recharge <= 0) {
  1244. \Log::info('VIP提现任务-邀请触发失败', [
  1245. 'user_id' => $userId,
  1246. 'reason' => '用户未充值'
  1247. ]);
  1248. return false;
  1249. }
  1250. $redisKey = "vip_withdraw_task_{$userId}";
  1251. $data = Redis::get($redisKey);
  1252. if ($data) {
  1253. $taskData = json_decode($data, true);
  1254. // 检查阶段1是否完成
  1255. if (!isset($taskData['stage1_completed']) || !$taskData['stage1_completed']) {
  1256. // 阶段1未完成,不计数
  1257. \Log::info('VIP提现任务-邀请触发失败', [
  1258. 'user_id' => $userId,
  1259. 'reason' => '阶段1未完成',
  1260. 'stage1_completed' => $taskData['stage1_completed'] ?? false
  1261. ]);
  1262. return false;
  1263. }
  1264. // 阶段1已完成,增加邀请计数
  1265. $taskData['invite_count'] = ($taskData['invite_count'] ?? 0) + 1;
  1266. // ✅ 保存到数据库(持久化)
  1267. $this->saveUserTaskData($userId, $taskData);
  1268. // ✅ 更新Redis缓存(24小时)
  1269. Redis::setex($redisKey, 86400, json_encode($taskData));
  1270. \Log::info('VIP提现任务-邀请触发成功', [
  1271. 'user_id' => $userId,
  1272. 'invite_count' => $taskData['invite_count']
  1273. ]);
  1274. return true;
  1275. } else {
  1276. // 如果Redis没有数据,从数据库加载
  1277. $dbData = DB::table('agent.dbo.vip_withdraw_tasks')
  1278. ->where('user_id', $userId)
  1279. ->first();
  1280. if ($dbData) {
  1281. $taskData = json_decode($dbData->task_data, true);
  1282. // 检查阶段1是否完成
  1283. if (!isset($taskData['stage1_completed']) || !$taskData['stage1_completed']) {
  1284. \Log::info('VIP提现任务-邀请触发失败', [
  1285. 'user_id' => $userId,
  1286. 'reason' => '阶段1未完成'
  1287. ]);
  1288. return false;
  1289. }
  1290. // 增加邀请计数
  1291. $taskData['invite_count'] = ($taskData['invite_count'] ?? 0) + 1;
  1292. // ✅ 保存到数据库(持久化)
  1293. $this->saveUserTaskData($userId, $taskData);
  1294. // ✅ 更新Redis缓存(24小时)
  1295. Redis::setex($redisKey, 86400, json_encode($taskData));
  1296. \Log::info('VIP提现任务-邀请触发成功(从数据库加载)', [
  1297. 'user_id' => $userId,
  1298. 'invite_count' => $taskData['invite_count']
  1299. ]);
  1300. return true;
  1301. } else {
  1302. // 用户数据不存在或阶段1未完成
  1303. \Log::info('VIP提现任务-邀请触发失败', [
  1304. 'user_id' => $userId,
  1305. 'reason' => '用户数据不存在或阶段1未完成'
  1306. ]);
  1307. return false;
  1308. }
  1309. }
  1310. }
  1311. }