GameApiController.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. <?php
  2. namespace App\Http\Controllers\Api;
  3. use App\Util;
  4. use Illuminate\Support\Facades\Redis;
  5. use App\Facade\TableName;
  6. use App\Http\Controllers\Controller;
  7. use App\Models\AccountsSource;
  8. use Illuminate\Http\Request;
  9. use Illuminate\Support\Facades\DB;
  10. class GameApiController extends Controller
  11. {
  12. // 修改用户信息--【昵称,和头像】
  13. public function updateUserInfo(Request $request)
  14. {
  15. $FaceID = $request->FaceID ?: '';
  16. $NickName = $request->NickName ?: '';
  17. $UserID = $request->UserID ?: '';
  18. if (empty($UserID)) {
  19. return apiReturnFail(__('messages.api.game_api.user_info_lost'));
  20. }
  21. $data = compact('FaceID', 'NickName');
  22. $data = array_filter($data);
  23. if (!empty($data)) {
  24. DB::table(TableName::QPAccountsDB() . 'AccountsInfo')
  25. ->where('UserID', $UserID)
  26. ->update($data);
  27. }
  28. return apiReturnSuc();
  29. }
  30. // 用户来源
  31. public function UserSource(Request $request)
  32. {
  33. $UserID = $request->input('UserID');
  34. $Source = $request->input('Source');
  35. $RequestDynamicPass = $request->input('DynamicPass');
  36. // 验证用户信息
  37. $DynamicPass = DB::table(TableName::QPAccountsDB() . 'AccountsInfo')
  38. ->where('UserID', $UserID)
  39. ->value('DynamicPass');
  40. if ($DynamicPass != $RequestDynamicPass || empty($DynamicPass)) return apiReturnFail(__('messages.api.game_api.player_info_inconsistent'));
  41. (new AccountsSource())->notExistsInsert($UserID, $Source);
  42. return apiReturnSuc();
  43. }
  44. public function billBoard(Request $request){
  45. return $this->billBoard2($request);
  46. $UserID = $request->input('UserID');
  47. $cachekey="Bill_board";
  48. $myrank=10000;
  49. if(Redis::exists($cachekey)){
  50. $boards=json_decode(Redis::get($cachekey),true);
  51. }
  52. $datekey=date("Ymd");
  53. $datekey2=date("Ymd",time()-86400);
  54. if(!isset($boards)||Redis::ttl($cachekey)<305){
  55. $boards=[];
  56. $list = DB::connection('read')->table('QPAccountsDB.dbo.AccountsInfo as ai')
  57. ->leftJoin('QPRecordDB.dbo.RecordUserDataStatisticsNew as gi', 'ai.UserID', '=', 'gi.UserID')
  58. ->whereIn('gi.DateID',[$datekey,$datekey2])
  59. ->orderBy('gi.WinScore','desc')
  60. ->limit(900)
  61. ->select(['ai.UserID','gi.WinScore as Score','ai.GameID','ai.NickName','ai.FaceID'])
  62. ->get()->toArray();
  63. foreach ($list as $key=>$value){
  64. $value=(array)$value;
  65. $value['FaceID']=intval($value['FaceID']);
  66. $value['Score']=floor($value['Score']/100)*100;
  67. $list[$key]=$value;
  68. }
  69. $all=Util::arraySort($list,'Score',SORT_DESC);
  70. foreach ($all as $key=>&$value){
  71. $value['rank']=$key+1;
  72. }
  73. $boards=$all;
  74. Redis::set($cachekey,json_encode($boards));
  75. //23:40以后冻结1小时
  76. if(intval(date("H"))==23&&intval(date("i"))>=40){
  77. Redis::expire($cachekey,3705);
  78. }else{
  79. Redis::expire($cachekey,1205);
  80. }
  81. }
  82. foreach ($boards as $value){
  83. if($value['UserID']==$UserID){
  84. $myrank=$value['rank'];
  85. }
  86. }
  87. return apiReturnSuc(['ret' =>['myData'=>['rank'=>$myrank],'rank'=>array_slice($boards,0,100)]]);
  88. }
  89. public function billBoard2(Request $request){
  90. $UserID = $request->input('UserID');
  91. $cachekey="Bill_boardd";
  92. $fakecachekey="Fake_Bill_boardd";
  93. $myrank=10000;
  94. // 检查是否存在真实用户排行榜数据缓存
  95. if(Redis::exists($cachekey)){
  96. $boards=json_decode(Redis::get($cachekey),true);
  97. }
  98. $datekey=date("Ymd");
  99. $datekey2=date("Ymd",time()-86400);
  100. // 获取真实用户排行榜数据
  101. if(!isset($boards)||Redis::ttl($cachekey)<205){
  102. $boards=[];
  103. $list = DB::connection('read')->table('QPAccountsDB.dbo.AccountsInfo as ai')
  104. ->leftJoin('QPRecordDB.dbo.RecordUserDataStatisticsNew as gi', 'ai.UserID', '=', 'gi.UserID')
  105. ->whereIn('gi.DateID',[$datekey,$datekey2])
  106. ->orderBy('gi.WinScore','desc')
  107. ->limit(900)
  108. ->select(['ai.UserID','gi.WinScore as Score','ai.GameID','ai.NickName','ai.FaceID'])
  109. ->get()->toArray();
  110. foreach ($list as $key=>$value){
  111. $value=(array)$value;
  112. $value['FaceID']=intval($value['FaceID']);
  113. $value['Score']=floor($value['Score']/100)*100;
  114. $list[$key]=$value;
  115. }
  116. $all=Util::arraySort($list,'Score',SORT_DESC);
  117. foreach ($all as $key=>&$value){
  118. $value['rank']=$key+1;
  119. }
  120. $boards=$all;
  121. Redis::set($cachekey,json_encode($boards));
  122. //23:40以后冻结1小时
  123. if(intval(date("H"))==23&&intval(date("i"))>=40){
  124. Redis::expire($cachekey,3705);
  125. }else{
  126. Redis::expire($cachekey,805);
  127. }
  128. }
  129. // 生成和获取模拟用户数据
  130. $fakeBoards = $this->generateFakeLeaderboard();
  131. foreach ($fakeBoards as &$value){
  132. $value=(array)$value;
  133. $value['FaceID']=intval($value['FaceID']);
  134. }
  135. // 合并真实和模拟用户数据
  136. $combinedBoards = array_merge($boards, $fakeBoards);
  137. $combinedBoards = Util::arraySort($combinedBoards, 'Score', SORT_DESC);
  138. // 重新计算排名
  139. foreach ($combinedBoards as $key=>&$value){
  140. $value['rank'] = $key+1;
  141. if($value['UserID']==$UserID){
  142. $myrank=$value['rank'];
  143. }
  144. }
  145. return apiReturnSuc(['ret' =>['myData'=>['rank'=>$myrank],'rank'=>array_slice($combinedBoards,0,100)]]);
  146. }
  147. /**
  148. * 生成模拟用户排行榜数据
  149. *
  150. * @return array 模拟用户数据
  151. */
  152. private function generateFakeLeaderboard()
  153. {
  154. $fakeCacheKey = "Fake_Bill_boardd";
  155. $fakeChangeCountKey = "Fake_Bill_Change_Count";
  156. $fakeUserCount = 300; // 模拟用户最多300个
  157. $now = time();
  158. $todayDate = date('Ymd');
  159. $todayKey = "fake_leaderboard_date";
  160. // 检查是否是新的一天,如果是则重置
  161. if (!Redis::exists($todayKey) || Redis::get($todayKey) != $todayDate) {
  162. Redis::del($fakeCacheKey);
  163. Redis::del($fakeChangeCountKey);
  164. Redis::set($todayKey, $todayDate);
  165. }
  166. // 如果缓存中存在模拟数据且未过期,直接返回
  167. if (Redis::exists($fakeCacheKey) && Redis::ttl($fakeCacheKey) > 60) {
  168. $fakeBoards = json_decode(Redis::get($fakeCacheKey), true);
  169. // 判断是否需要更新模拟数据
  170. $lastUpdateKey = "fake_leaderboard_last_update";
  171. $lastUpdate = Redis::exists($lastUpdateKey) ? intval(Redis::get($lastUpdateKey)) : 0;
  172. $currentHour = intval(date('H'));
  173. $updateInterval = ($currentHour >= 0 && $currentHour < 10) ? 1800 : 600; // 0-10点每半小时,10点后每10分钟
  174. if ($now - $lastUpdate >= $updateInterval) {
  175. $fakeBoards = $this->updateFakeUserScores($fakeBoards);
  176. Redis::set($lastUpdateKey, $now);
  177. }
  178. return $fakeBoards;
  179. }
  180. // 生成新的模拟用户数据
  181. $fakeBoards = [];
  182. for ($i = 0; $i < $fakeUserCount; $i++) {
  183. $fakeBoards[] = $this->generateFakeUserData();
  184. }
  185. // 按分数排序
  186. $fakeBoards = Util::arraySort($fakeBoards, 'Score', SORT_DESC);
  187. // 保存到Redis
  188. Redis::set($fakeCacheKey, json_encode($fakeBoards));
  189. Redis::expire($fakeCacheKey, 3600); // 缓存1小时
  190. // 记录初次更新时间
  191. Redis::set("fake_leaderboard_last_update", $now);
  192. return $fakeBoards;
  193. }
  194. private function generateFakeUserData()
  195. {
  196. $countryCode = env('COUNTRY_CODE', '92'); // 默认使用巴基斯坦区号
  197. $userID = "99" . mt_rand(1000000, 9999999);
  198. $gameID = "1" . mt_rand(1000000, 9999999);
  199. $score = mt_rand(-15000, 35000); // -15000到35000
  200. return [
  201. "UserID" => $userID,
  202. "Score" => $score*100,
  203. "GameID" => $gameID,
  204. "NickName" => $this->generateFakeName($countryCode, $gameID),
  205. "FaceID" => mt_rand(1, 16),
  206. "rank" => 0, // 初始rank,会在合并后重新计算
  207. "change_count" => 0 // 变化次数统计
  208. ];
  209. }
  210. /**
  211. * 更新模拟用户分数
  212. *
  213. * @param array $fakeBoards 现有模拟用户数据
  214. * @return array 更新后的模拟用户数据
  215. */
  216. private function updateFakeUserScores($fakeBoards)
  217. {
  218. $fakeChangeCountKey = "Fake_Bill_Change_Count";
  219. $changeCountMap = [];
  220. // 获取现有的变化次数记录
  221. if (Redis::exists($fakeChangeCountKey)) {
  222. $changeCountMap = json_decode(Redis::get($fakeChangeCountKey), true);
  223. }
  224. // 确保总体上有增长趋势
  225. $increasedCount = 0;
  226. $minIncrease = count($fakeBoards) * 0.3; // 至少30%的用户要增加
  227. foreach ($fakeBoards as $key => &$user) {
  228. $userID = $user['UserID'];
  229. // 初始化变化次数记录
  230. if (!isset($changeCountMap[$userID])) {
  231. $changeCountMap[$userID] = 0;
  232. }
  233. // 50%的概率进行变化
  234. if (mt_rand(0, 1) == 1) {
  235. // 如果变化次数超过70次,则不再变化
  236. if ($changeCountMap[$userID] >= 70) {
  237. continue;
  238. }
  239. if(intval(date('H'))>=11){
  240. $newScore=intval( $user['Score']*mt_rand(70,140)/100);
  241. if($newScore>$user['Score'])$increasedCount++;
  242. $user['Score'] = $newScore;
  243. }else {
  244. // 动态回报率,基础值10000
  245. $changeAmount = mt_rand(-15000, 35000); // 基础值上下浮动
  246. $user['Score'] += $changeAmount * 100;
  247. if($changeAmount>0){
  248. $increasedCount++;
  249. }
  250. // 决定是增加还是减少
  251. // if ($increasedCount < $minIncrease) {
  252. // // 强制增加以满足最低增长要求
  253. // $user['Score'] += $changeAmount * 100;
  254. // $increasedCount++;
  255. // } else {
  256. // // 随机增减
  257. // if (mt_rand(0, 1) == 1) {
  258. // $user['Score'] += $changeAmount * 100;
  259. // $increasedCount++;
  260. // } else {
  261. // $user['Score'] -= $changeAmount * 100;
  262. //
  263. // }
  264. // }
  265. }
  266. // 更新变化次数
  267. $changeCountMap[$userID]++;
  268. }
  269. }
  270. // 控制模拟用户总数不超过300
  271. $maxUsers = 300;
  272. if (count($fakeBoards) < $maxUsers) {
  273. // 每次增加少量新用户
  274. $newUsersCount = min(5, $maxUsers - count($fakeBoards));
  275. for ($i = 0; $i < $newUsersCount; $i++) {
  276. $fakeBoards[] = $this->generateFakeUserData();
  277. }
  278. }
  279. // 重新按分数排序
  280. $fakeBoards = Util::arraySort($fakeBoards, 'Score', SORT_DESC);
  281. // 更新Redis缓存
  282. Redis::set("Fake_Bill_boardd", json_encode($fakeBoards));
  283. Redis::expire("Fake_Bill_boardd", 3600);
  284. // 更新变化次数记录
  285. Redis::set($fakeChangeCountKey, json_encode($changeCountMap));
  286. Redis::expire($fakeChangeCountKey, 86400); // 缓存一天
  287. return $fakeBoards;
  288. }
  289. /**
  290. * 根据国家代码生成模拟用户名
  291. *
  292. * @param string $countryCode 国家代码
  293. * @param string $gameID 游戏ID
  294. * @return string 生成的用户名
  295. */
  296. private function generateFakeName($countryCode, $gameID)
  297. {
  298. $lastFourDigits = substr($gameID, -4);
  299. // 根据国家代码生成不同国家风格的名字
  300. switch ($countryCode) {
  301. case '92': // 巴基斯坦
  302. $pakFirstNames = ['Ahmed', 'Muhammad', 'Ali', 'Hassan', 'Saeed', 'Omar', 'Yousaf', 'Imran', 'Tariq', 'Asif', 'Zain', 'Bilal', 'Faisal', 'Khalid', 'Malik'];
  303. $pakLastNames = ['Khan', 'Malik', 'Ali', 'Ahmed', 'Shah', 'Qureshi', 'Baloch', 'Siddiqui', 'Raza', 'Javed', 'Iqbal', 'Akbar', 'Aslam'];
  304. if (mt_rand(0, 1) == 0) {
  305. // 有50%概率使用首字母+数字格式
  306. return 'U' . $lastFourDigits;
  307. } else {
  308. // 有50%概率使用巴基斯坦名字
  309. return $pakFirstNames[array_rand($pakFirstNames)] . substr($pakLastNames[array_rand($pakLastNames)], 0, 1);
  310. }
  311. default: // 默认格式
  312. return 'U' . $lastFourDigits;
  313. }
  314. }
  315. public static function webGameRooms()
  316. {
  317. if(!Redis::exists('webgamerooms')) {
  318. $webgames = DB::table('QPPlatformDB.dbo.GameRoomInfo')->select('ServerID', 'SortID', 'GameID', 'CellScore', 'MinEnterScore')->where('GameID', '>', 9000)->get()->toArray();
  319. $sorts = [];
  320. foreach ($webgames as &$value) {
  321. $value = (array)$value;
  322. $value['SortID'] = intval($value['SortID']);
  323. $value['ServerID'] = intval($value['ServerID']);
  324. $value['MinEnterScore'] = intval($value['MinEnterScore']);
  325. $value['CellScore'] = intval($value['CellScore']);
  326. $sorts[$value['GameID']][$value['SortID']] = $value;
  327. }
  328. Redis::setex('webgamerooms', 600,json_encode($sorts));
  329. }else{
  330. $sorts=Redis::get('webgamerooms');
  331. $sorts=json_decode($sorts,true);
  332. }
  333. return $sorts;
  334. }
  335. public function onlineList(Request $request){
  336. try {
  337. if (Redis::exists('Online_Real')) {
  338. $online = Redis::get('Online_Real');
  339. $online = json_decode($online, true);
  340. }
  341. $field = ['gi.GameID', 'gi.Nullity', 'gi.SortID', 'gi.ServerName', 'gi.ServerID'];
  342. $list = DB::connection('read')->table('QPPlatformDB.dbo.GameRoomInfo as gi')
  343. ->whereIn('GameID', config('games.openKGame'))
  344. ->leftJoin('QPTreasureDB.dbo.GameScoreLocker as gl', function ($query) {
  345. $query->on('gl.ServerID', 'gi.ServerID')->selectRaw('gl.ServerID')->groupBy('ServerID')->whereRaw('datediff(hh,gl.CollectDate,getdate())<=5');
  346. })
  347. ->select($field)
  348. // ->where('Nullity', 0)
  349. ->selectRaw('IsNull(count(DISTINCT gl.UserID),0) as game_count')
  350. ->groupBy($field)
  351. ->orderBy('gi.GameID', 'asc')
  352. ->orderBy('gi.ServerName', 'asc');
  353. if (!isset($online) || Redis::ttl('Online_Real') < 5) {
  354. if($request->exists("hh")){
  355. dd($list->toSql(),json_encode(config('games.openKGame')));
  356. }else {
  357. $list=$list->get()->toArray();
  358. }
  359. $online = [];
  360. foreach ($list as $item) {
  361. $item = (array)$item;
  362. if (!isset($item) || !isset($item['GameID'])) continue;
  363. if (!isset($item['game_count'])) $item['game_count'] = 0;
  364. if (isset($online[$item['GameID']])) {
  365. if (!isset($online[$item['GameID']][$item['SortID']])) $online[$item['GameID']][$item['SortID']] = 0;
  366. $online[$item['GameID']][$item['SortID']] += intval($item['game_count']) ?: mt_rand(100, 200) / 1000;
  367. } else {
  368. $online[$item['GameID']] = [1 => 0.3, 2 => 0.2, 3 => 0.1];
  369. $online[$item['GameID']][$item['SortID']] = intval($item['game_count']) ?: mt_rand(100, 200) / 1000;
  370. }
  371. }
  372. foreach ($online as $gid => &$ol) {
  373. $base=$ol[1]+$ol[2];
  374. $ol[1] = intval($base * 168);
  375. $ol[2] = intval($base *20);
  376. $ol[3] = intval($base *2);
  377. if(count($ol)>3){
  378. $ol[2] = intval($base * 168);
  379. $ol[3] = intval($base *20);
  380. $ol[4] = intval($base *2);
  381. }
  382. }
  383. foreach ($online as $gid => &$ol) {
  384. $total=0;
  385. foreach ($ol as $count){
  386. $total+=$count;
  387. }
  388. $online[$gid]=['a0'=>$total];
  389. }
  390. Redis::set('Online_Real', json_encode($online));
  391. Redis::expire('Online_Real', 65);
  392. }
  393. return apiReturnSuc(['ret' => $online]);
  394. }catch (\Exception $e){
  395. Util::WriteLog('online_error',$e->getMessage().$e->getTraceAsString());
  396. return apiReturnSuc(['ret' =>'']);
  397. }
  398. }
  399. }