GameNotify.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705
  1. <?php
  2. namespace app\api\controller\v2;
  3. use app\api\controller\Api;
  4. use app\common\logic\SubPackage as SubChannel;
  5. use app\common\model\Game;
  6. use app\common\model\GameNotify as GameNotifyModel;
  7. use app\common\model\PromotionShortLink;
  8. use app\common\model\SdkGameList;
  9. use think\Db;
  10. /**
  11. * 公共&强更
  12. */
  13. class GameNotify extends Api
  14. {
  15. protected $noNeedLogin = ['index', 'getPackage'];
  16. public function _initialize()
  17. {
  18. parent::_initialize();
  19. }
  20. /**
  21. * 强更&公告 - v1
  22. * 相关需求:
  23. * 1. 玩家当前版本不是最新版本时,以强更显示为主。
  24. * 2. 有强更时候显示强更,有公告时候显示公告,无则不显示。
  25. * 3. 客户端如果是强更,则每次打开游戏都要判断。
  26. *
  27. * @return void
  28. * @throws \think\db\exception\DataNotFoundException
  29. * @throws \think\db\exception\ModelNotFoundException
  30. * @throws \think\exception\DbException
  31. */
  32. public function index(){
  33. $game_id = $this->input('gameid'); // 游戏ID
  34. $channel_id = $this->input('channel_id'); // 渠道ID
  35. $channel_version = $this->input('channel_version', ''); // 游戏的强更号
  36. if ( empty($game_id) ) {
  37. $this->jsonResult('', 0, '游戏ID不能为空');
  38. }
  39. elseif ( empty($channel_id) ) {
  40. $this->jsonResult('', 0, '渠道ID不能为空');
  41. }
  42. elseif ( empty($channel_version) ) {
  43. $this->jsonResult('', 0, '游戏的强更号不能为空');
  44. }
  45. // 返回内容
  46. $result = [
  47. 'notify' => [], // 公告详情
  48. 'status' => false, // // 状态:true=强更、false=弹框提醒
  49. 'url' => "", // 强更地址
  50. ];
  51. $sdkGameModel = new SdkGameList;
  52. $fbInfo = $sdkGameModel->where(['channel_id' => $channel_id, 'gameid' => $game_id])->field('id,filename,channel_version,upload_status')->find();
  53. if(!$fbInfo){
  54. $this->jsonResult('', 0, '分包数据不存在!');
  55. }
  56. $gameModel = new Game;
  57. $gameInfo = $gameModel->where(['id' => $game_id])->field('id,type,pinyin,channel_version')->find();
  58. if(!$gameInfo){
  59. $this->jsonResult('', 0, '游戏不存在!');
  60. }
  61. $newVersion = false;
  62. // 客户端已是最新版本 则只 显示公告
  63. if($channel_version >= $gameInfo['channel_version']){
  64. $newVersion = true;
  65. }
  66. // 查询最新的公告&强更
  67. $ggInfo = Db::table('cy_game_notify')->field('id,title,content,notify_type')
  68. ->where(['status' => 1, 'game_id' => $game_id, 'notify_type' => 1])
  69. ->where('(end_time>0 AND end_time>'.NOW_TIMESTAMP.') or (end_time=0)')
  70. ->where('(deny_type=0 AND denied_channel not like :channelid1 and denied_channel not like :channelid2) or (deny_type=1 AND (denied_channel like :channelid3 or denied_channel like :channelid4))',['channelid1'=>$channel_id.',%', 'channelid2'=>'%,'.$channel_id.',%','channelid3'=>$channel_id.',%', 'channelid4'=>'%,'.$channel_id.',%'])
  71. ->order('create_time desc')
  72. ->find();
  73. $qgInfo = Db::table('cy_game_notify')->field('id,title,content,notify_type')
  74. ->where(['status' => 1, 'game_id' => $game_id, 'notify_type' => 3])
  75. ->where('(end_time>0 AND end_time>'.NOW_TIMESTAMP.') or (end_time=0)')
  76. ->where('(deny_type=0 AND denied_channel not like :channelid1 and denied_channel not like :channelid2) or (deny_type=1 AND (denied_channel like :channelid3 or denied_channel like :channelid4))',['channelid1'=>$channel_id.',%', 'channelid2'=>'%,'.$channel_id.',%','channelid3'=>$channel_id.',%', 'channelid4'=>'%,'.$channel_id.',%'])
  77. ->order('create_time desc')
  78. ->find();
  79. if(!$ggInfo && !$qgInfo){
  80. $this->jsonResult($result, 1, "无");
  81. }
  82. // 已是最新版本则只显示公告
  83. if($newVersion || !$qgInfo){
  84. // 没有公告则不显示
  85. if(!$ggInfo){
  86. $this->jsonResult($result, 1, "无");
  87. }
  88. $ggInfo['content'] = '<head><meta name="viewport" content="width=device-width"></head>' . $ggInfo['content'];
  89. $result['notify'] = [$ggInfo];
  90. // 加meta标签,使ios中的wkwebview字体正常
  91. $this->jsonResult($result, 1, "已是最新版本");
  92. }
  93. // 没有强更则无
  94. if($fbInfo['upload_status'] != 1 || !$qgInfo){
  95. $this->jsonResult($result, 1, "无");
  96. }
  97. // 获取强更包数据
  98. if($gameInfo['type'] == 2){ // 安卓
  99. if($fbInfo && isset($fbInfo['filename'])){
  100. $result['url'] = APK_DOWN_DOMAIN.'/sygame/'.$gameInfo['pinyin'].'/'.$fbInfo['filename'];
  101. }
  102. }elseif($gameInfo['type'] == 1){ // IOS
  103. $shortModel = new PromotionShortLink;
  104. $shortInfo = $shortModel->where(['channel_id' => $channel_id, 'game_id' => $game_id])->field('id,short_link')->find();
  105. if($shortInfo){
  106. $result['url'] = $shortInfo['short_link']?T_DOMAIN.'/'.$shortInfo['short_link']:'';
  107. }
  108. }
  109. if(!$result['url']){
  110. $this->jsonResult($result, 1, "无");
  111. }
  112. $result['status'] = true;
  113. $qgInfo['content'] = '<head><meta name="viewport" content="width=device-width"></head>' . $qgInfo['content'];
  114. $result['notify'] = [$qgInfo];
  115. $this->jsonResult($result, 1, '获取成功!');
  116. }
  117. private function getMergeDatas($gameid, $uid, $channel_id) {
  118. $now = time();
  119. $result = Db::table('cy_game_notify')->alias('cn')
  120. ->join('cy_game_notify_relation r', 'cn.id = r.notify_id', 'INNER')
  121. // 核心合并:通过 OR 逻辑,一次性关联渠道或用户的黑白名单记录
  122. ->join('cy_game_notify_deny d', "cn.id = d.notify_id AND d.deny_type = cn.deny_type AND (
  123. (cn.data_type = 1 AND d.target_type = 1 AND d.target_id = {$channel_id})
  124. OR
  125. (cn.data_type = 2 AND d.target_type = 2 AND d.target_id = {$uid})
  126. )", 'LEFT')
  127. ->where('cn.status', 1)
  128. ->where('r.game_id', $gameid)
  129. ->where('cn.data_type', 'in', [1, 2]) // 同时查渠道和用户公告
  130. // 1. 时间过滤 (保持不变)
  131. ->where(function ($query) use ($now) {
  132. $query->where('cn.notify_type', '<>', 4)
  133. ->whereOr(function ($q2) use ($now) {
  134. $q2->where('cn.notify_type', 4)
  135. ->where('cn.begin_time', '<=', $now)
  136. ->where('cn.end_time', '>=', $now);
  137. });
  138. })
  139. // 2. 黑白名单判定 (保持不变)
  140. ->where(function ($query) {
  141. $query->where(function ($q_black) {
  142. $q_black->where('cn.deny_type', 0)->where('d.id', 'NULL');
  143. })->whereOr(function ($q_white) {
  144. $q_white->where('cn.deny_type', 1)->where('d.id', 'NOT NULL');
  145. });
  146. })
  147. // 直接按 ID 倒序,取最新的一条即可
  148. ->order('cn.id desc')
  149. ->find();
  150. return $result ?: null;
  151. }
  152. /**
  153. * 获取合并后的强更&公告数据
  154. *
  155. * @param $gameid
  156. * @param $uid
  157. * @param $channel_id
  158. *
  159. * @return array
  160. * @throws \think\db\exception\DataNotFoundException
  161. * @throws \think\db\exception\ModelNotFoundException
  162. * @throws \think\exception\DbException
  163. */
  164. private function getMergeData($gameid, $uid, $channel_id) {
  165. $now = time();
  166. // --- 基础查询条件(复用) ---
  167. $baseQuery = Db::table('cy_game_notify')->alias('cn')
  168. ->join('cy_game_notify_relation r', 'cn.id = r.notify_id', 'INNER')
  169. ->join('cy_game_notify_deny d', "cn.id = d.notify_id AND d.deny_type = cn.deny_type AND (
  170. (cn.data_type = 1 AND d.target_type = 1 AND d.target_id = {$channel_id})
  171. OR
  172. (cn.data_type = 2 AND d.target_type = 2 AND d.target_id = {$uid})
  173. )", 'LEFT')
  174. ->where('cn.status', 1)
  175. ->where('r.game_id', $gameid)
  176. // 黑白名单逻辑
  177. ->where(function ($query) {
  178. $query->where(function ($q_black) {
  179. $q_black->where('cn.deny_type', 0)->where('d.id', 'NULL');
  180. })->whereOr(function ($q_white) {
  181. $q_white->where('cn.deny_type', 1)->where('d.id', 'NOT NULL');
  182. });
  183. });
  184. // --- 1. 获取最新的【强更公告】 (notify_type = 3) ---
  185. $forceUpdate = (clone $baseQuery)
  186. ->where('cn.notify_type', 3)
  187. ->order('cn.id desc')
  188. ->find();
  189. // --- 2. 获取最新的【普通公告】 (notify_type = 1 或 4) ---
  190. $normalNotify = (clone $baseQuery)
  191. ->field('cn.*')
  192. ->where('cn.notify_type', 'in', [1, 4])
  193. // 针对时效性公告(4)的额外时间判断
  194. ->where(function ($query) use ($now) {
  195. $query->where('cn.notify_type', 1) // 类型1直接过
  196. ->whereOr(function ($q2) use ($now) {
  197. $q2->where('cn.notify_type', 4) // 类型4查有效期
  198. ->where('cn.begin_time', '<=', $now)
  199. ->where('cn.end_time', '>=', $now);
  200. });
  201. })
  202. ->order('cn.id desc') // 在1和4中取最新的那一条
  203. ->find();
  204. // --- 3. 合并返回 ---
  205. $finalList = [];
  206. if ($forceUpdate) {
  207. $forceUpdate['priority_weight'] = 1; // 强更优先级最高
  208. $finalList['qg'] = $forceUpdate;
  209. }
  210. if ($normalNotify) {
  211. $normalNotify['priority_weight'] = 2; // 普通公告次之
  212. $finalList['gg'] = $normalNotify;
  213. }
  214. return $finalList;
  215. }
  216. /**
  217. * 强更&公共&分包更新 - v2
  218. *
  219. * 相关需求:
  220. * 1. 玩家当前版本不是最新版本时,以强更显示为主。
  221. * 2. 有强更时候显示强更,有公告时候显示公告,无则不显示。
  222. * 3. 客户端如果是强更,则每次打开游戏都要判断。
  223. * 4. 检测客户端是否最新版本,返回相关展示状态。
  224. *
  225. * 强更判断:
  226. * 1. 根据‘游戏版本’对比‘分包版本’确认是否强更;
  227. * 2. 根据客户端‘游戏版本’对比‘分包版本’,确认是否最新版本;
  228. *
  229. * 弹框优先级:强更》公告》时效公告;
  230. *
  231. * TODO: 想要强更,则游戏版本号大于当前客户端版本号
  232. *
  233. * @return void
  234. * @throws \think\db\exception\DataNotFoundException
  235. * @throws \think\db\exception\ModelNotFoundException
  236. * @throws \think\exception\DbException
  237. */
  238. public function index_v2(){
  239. $game_id = $this->input('gameid'); // 游戏ID
  240. $channel_id = $this->input('channel_id'); // 渠道ID
  241. $channel_version = $this->input('channel_version', ''); // 游戏的强更号
  242. if ( empty($game_id) ) {
  243. $this->jsonResult('', -100, '游戏ID不能为空');
  244. }
  245. elseif ( empty($channel_id) ) {
  246. $this->jsonResult('', -100, '渠道ID不能为空');
  247. } elseif ($channel_version === '') {
  248. $this->jsonResult('', -100, '游戏的强更号不能为空');
  249. }
  250. $gameModel = new Game;
  251. $gameInfo = $gameModel->where(['id' => $game_id])->field('id,type,pinyin,channel_version')->find();
  252. if(!$gameInfo){
  253. $this->jsonResult('', -102, '游戏不存在!');
  254. }
  255. // 返回结构定义
  256. // 弹框优先级:强更》公告》时效公告;
  257. $result = [
  258. 'notify' => [], // 公告详情
  259. 'type' => 0, // 类型:0=无、1=公告、2=广告、3=强更、4=时效性公告;
  260. 'status' => 0, // 状态:0=无、1=已是最新版本(无用)、2=已是最新版本(可以强更)、3=获取最新分包(请求分包)、4=分包更新中(请求中)、5=分包失败(重新提交分包)
  261. 'url' => "", // 强更地址(强更时才返回)
  262. 'ext' => [
  263. 'game_version' => $gameInfo['channel_version'],
  264. 'channel_version' => "",
  265. 'client_version' => $channel_version,
  266. ]
  267. ];
  268. // TODO: 手动设置游戏版本,方便测试强更
  269. // if($this->input('imeil', '') == '00000000-2204-e204-ffff-ffffef05ac4a'){
  270. // $gameInfo['channel_version'] = 45;
  271. // }
  272. // 客户端已是最新版本 则只 显示公告
  273. if($channel_version >= $gameInfo['channel_version']){
  274. $result['status'] = 1;
  275. }
  276. // 查询最新的 公告&强更&部分玩家
  277. $userid = $this->input('userid', '');
  278. $getMergeList = $this->getMergeData($game_id, $userid, $channel_id);
  279. if (!$getMergeList) {
  280. $result['status'] = 0;
  281. $this->jsonResult($result, 1, "暂无公告!");
  282. }
  283. // dump($getMergeList);
  284. // 强更
  285. $getMergeData = $getMergeList['qg']??[];
  286. if($result['status'] == 1 && !empty($getMergeList['gg'])){
  287. // 公告
  288. $getMergeData = $getMergeList['gg'];
  289. $notidyData['id'] = $getMergeData['id'];
  290. if($getMergeData['notify_type'] == 4){
  291. $notidyData['id'] = time();
  292. }
  293. $result['type'] = $getMergeData['notify_type'];
  294. $notidyData['title'] = $getMergeData['title'];
  295. // 加meta标签,使ios中的wkwebview字体正常
  296. $notidyData['content'] = '<head><meta name="viewport" content="width=device-width"></head>' . $getMergeData['content'];
  297. $result['notify'] = [$notidyData];
  298. $this->jsonResult($result, 1, "有新公告!");
  299. }
  300. if(!$getMergeData || $result['status'] == 1){
  301. $result['status'] = 0;
  302. $this->jsonResult($result, 1, "暂无公告!");
  303. }
  304. // ## 强更逻辑处理开始 ##
  305. $sdkGameModel = new SdkGameList;
  306. $fbInfo = $sdkGameModel->where(['channel_id' => $channel_id, 'gameid' => $game_id])->field('id,filename,channel_version,upload_status')->find();
  307. if(!$fbInfo){
  308. $this->jsonResult($result, 1, '分包数据不存在!');
  309. }
  310. $result['ext']['channel_version'] = $fbInfo['channel_version'];
  311. // 有强更则判断是否最新包,是否可以强更
  312. // status = 状态:0=无、1=已是最新版本(无用)、2=已是最新版本(可以强更)、3=获取最新分包(请求分包)、4=分包更新中(请求中)、5=分包失败(重新提交分包)
  313. if($fbInfo['upload_status'] == 1 && $fbInfo['channel_version'] == $gameInfo['channel_version']){
  314. // 可以强更
  315. $result['status'] = 2;
  316. } elseif ($fbInfo['upload_status'] == 4){
  317. // 分包中
  318. $result['status'] = 4;
  319. } elseif ($fbInfo['upload_status'] == 5){
  320. // 分包失败
  321. $result['status'] = 5;
  322. } else {
  323. $result['status'] = 3;
  324. }
  325. if($fbInfo['upload_status'] == 1 && $gameInfo['channel_version'] > $fbInfo['channel_version']){
  326. // 主动分包条件:分包>客户端、游戏>分包、游戏>客户端
  327. $result['status'] = 4;
  328. $subChannel = new SubChannel;
  329. // $platform = $gameInfo['type']==1?1:0; // 设备类型:1=ios、2=安卓、3=h5
  330. $subChannelRes = $subChannel->index($channel_id, $game_id, 'ydd_fb',$gameInfo['type'],0);
  331. if($subChannelRes['code'] === 0){
  332. $sdkGameModel->save(['upload_status' => 4, 'update_time' => time()], ['channel_id' => $channel_id, 'gameid' => $game_id]);
  333. }else{
  334. $result['status'] = 5;
  335. }
  336. }
  337. // 获取强更数据
  338. if($result['status'] == 2){
  339. // $result['game_type'] = $gameInfo['type'];
  340. if($gameInfo['type'] == 2){ // 安卓
  341. if($fbInfo && isset($fbInfo['filename'])){
  342. $result['url'] = APK_DOWN_DOMAIN_NEW.'/sygame/'.$gameInfo['pinyin'].'/'.$fbInfo['filename'];
  343. }
  344. }elseif($gameInfo['type'] == 1){ // IOS
  345. $shortModel = new PromotionShortLink;
  346. $shortInfo = $shortModel->where(['channel_id' => $channel_id, 'game_id' => $game_id])->field('id,short_link')->find();
  347. if($shortInfo){
  348. $result['url'] = $shortInfo['short_link']?T_DOMAIN.'/'.$shortInfo['short_link']:'';
  349. }
  350. }
  351. // TODO:H5分包地址(没有分包,不用处理)
  352. if(!$result['url']){
  353. $result['status'] = 3;
  354. }
  355. }
  356. $result['type'] = 3;
  357. $notidyData['id'] = $getMergeData['id'];
  358. $notidyData['title'] = $getMergeData['title'];
  359. $notidyData['content'] = '<head><meta name="viewport" content="width=device-width"></head>' . $getMergeData['content'];
  360. $result['notify'] = [$notidyData];
  361. // status = 状态:0=无、1=已是最新版本(无用)、2=已是最新版本(可以强更)、3=获取最新分包(请求分包)、4=分包更新中(请求中)、5=分包失败(重新提交分包)
  362. $msg = '可更新!';
  363. switch ($result['status']){
  364. case 1:
  365. $msg = '已是最新包';
  366. break;
  367. case 2:
  368. $msg = '下载最新包';
  369. break;
  370. case 3:
  371. $msg = '获取最新包';
  372. break;
  373. case 4:
  374. $msg = '分包处理中,请稍后...';
  375. break;
  376. case 5:
  377. $msg = isset($subChannelRes['msg'])?'分包失败:'.$subChannelRes['msg']:'分包失败';
  378. break;
  379. }
  380. $this->jsonResult($result, 1, $msg);
  381. }
  382. // 强更公告&公共&分包更新 - v3
  383. public function index_v3(){
  384. $game_id = $this->input('gameid'); // 游戏ID
  385. $channel_id = $this->input('channel_id'); // 渠道ID(客户端传入)
  386. // $channel_id = $this->input('member_channel_id'); // 渠道ID(当前账户所属渠道)
  387. $channel_version = $this->input('channel_version', ''); // 游戏的强更号
  388. $userId = $this->input('userid', ''); // 当前用户ID
  389. if ( empty($game_id) ) {
  390. $this->jsonResult('', -100, '游戏ID不能为空');
  391. }
  392. elseif ( empty($channel_id) ) {
  393. $this->jsonResult('', -100, '渠道ID不能为空');
  394. } elseif ($channel_version === '') {
  395. $this->jsonResult('', -100, '游戏的强更号不能为空');
  396. }
  397. $gameModel = new Game;
  398. $gameInfo = $gameModel->where(['id' => $game_id])->field('id,type,pinyin,channel_version')->find();
  399. if(!$gameInfo){
  400. $this->jsonResult('', -102, '游戏不存在!');
  401. }
  402. $channelId = $channel_id; // 当前渠道ID
  403. $now = time();
  404. // ## 返回结构定义
  405. // 弹框优先级:强更》公告》时效公告;
  406. $result = [
  407. // 'type' => 0, // 类型:0=无、1=公告、2=广告、3=强更、4=时效性公告;
  408. // 强更公告详情
  409. 'notify_qg' => [
  410. 'status' => 0, // 状态:0=无、1=已是最新版本(无用)、2=已是最新版本(可以强更)、3=获取最新分包(请求分包)、4=分包更新中(请求中)、5=分包失败(重新提交分包)
  411. 'url' => '', // 强更地址(强更时才返回)
  412. 'msg' => '', // 提示信息{当前状态的说明。例如:status = 状态:0=无、1=已是最新版本(正常)、2=已是最新版本(获取下载地址)、3=获取最新分包(客户端请求分包接口)、4=分包更新中(请求中)、5=分包失败(重新提交分包)}
  413. // 强更公告详情
  414. 'id' => '',
  415. 'title' => '',
  416. 'content' => '',
  417. ],
  418. // 单次公告详情
  419. 'notify_dc' => [
  420. 'status' => 0,
  421. 'url' => '',
  422. 'msg' => '',
  423. // 强更公告详情
  424. 'id' => '',
  425. 'title' => '',
  426. 'content' => '',
  427. ],
  428. // 时效公告详情
  429. 'notify_sx' => [
  430. 'status' => 0,
  431. 'url' => '',
  432. 'msg' => '',
  433. // 强更公告详情
  434. 'id' => '',
  435. 'title' => '',
  436. 'content' => '',
  437. ],
  438. // 扩展参数
  439. 'ext' => [
  440. 'game_version' => $gameInfo['channel_version'], // 游戏最新版本号
  441. 'channel_version' => "", // 分包版本号
  442. 'client_channel_version' => $channel_version, // 客户端传入的版本号
  443. ]
  444. ];
  445. // ================= 2. 第一步:获取所有符合资格的 ID 列表 =================
  446. // 这一步不做分组,只负责把“不能看的”全部剔除,剩下的按时间倒序
  447. $candidates = Db::table('cy_game_notify')->alias('cn')
  448. ->join('cy_game_notify_relation r', 'cn.id = r.notify_id', 'INNER')
  449. // 关键点:动态 JOIN 黑白名单表
  450. // 意思:如果公告是渠道类型(1),就匹配渠道ID;如果公告是用户类型(2),就匹配用户ID
  451. // 并且还要求 黑白名单记录的类型(deny_type) 必须和主表一致,防止脏数据
  452. ->join('cy_game_notify_deny d', "cn.id = d.notify_id
  453. AND d.deny_type = cn.deny_type
  454. AND (
  455. (cn.data_type = 1 AND d.target_type = 1 AND d.target_id = {$channelId})
  456. OR
  457. (cn.data_type = 2 AND d.target_type = 2 AND d.target_id = {$userId})
  458. )", 'LEFT')
  459. ->where('cn.status', 1) // 状态开启
  460. ->where('r.game_id', $game_id) // 必须是当前游戏
  461. // 1. 时间筛选逻辑 (保持不变)
  462. ->where(function ($query) use ($now) {
  463. $query->where('cn.notify_type', '<>', 4) // 非时效性
  464. ->whereOr(function ($q2) use ($now) {
  465. $q2->where('cn.notify_type', 4) // 时效性
  466. ->where('cn.begin_time', '<=', $now)
  467. ->where('cn.end_time', '>=', $now);
  468. });
  469. })
  470. // 2. 黑白名单筛选逻辑 (根据 JOIN 的结果判断)
  471. ->where(function ($query) {
  472. // 场景A:黑名单模式 (deny_type=0)。要求:关联表 d 必须没有记录 (IS NULL)
  473. $query->where(function ($q_black) {
  474. $q_black->where('cn.deny_type', 0)->where('d.id', 'NULL');
  475. })
  476. // 场景B:白名单模式 (deny_type=1)。要求:关联表 d 必须有记录 (IS NOT NULL)
  477. ->whereOr(function ($q_white) {
  478. $q_white->where('cn.deny_type', 1)->where('d.id', 'NOT NULL');
  479. });
  480. })
  481. // 只取 ID 和 类型,按创建时间倒序(最新的排前面)
  482. ->field('cn.id, cn.notify_type')
  483. ->order('cn.create_time desc')
  484. ->select();
  485. // ================= 3. PHP 内存分组 (替代 GROUP BY) =================
  486. // 因为上面已经按时间倒序了,所以每个类型拿到的第一个,绝对是“最新且有效”的
  487. $validIds = [];
  488. $seenTypes = [];
  489. foreach ($candidates as $item) {
  490. $type = $item['notify_type'];
  491. if (!in_array($type, $seenTypes)) {
  492. $validIds[] = $item['id']; // 只要第一个(最新的)
  493. $seenTypes[] = $type; // 标记该类型已存在
  494. }
  495. }
  496. if (empty($validIds)) {
  497. $this->jsonResult($result, 1, "获取成功!");
  498. }
  499. // ================= 4. 最后一步:查详情 =================
  500. $notifyResult = Db::table('cy_game_notify')->alias('cn')
  501. ->field('cn.*') // 查出所有字段
  502. // 自定义排序权重:强更(3)>单次(1)>时效(4)>广告(2)
  503. ->field("(CASE cn.notify_type WHEN 3 THEN 1 WHEN 1 THEN 2 WHEN 4 THEN 3 ELSE 4 END) AS sort_weight")
  504. ->whereIn('cn.id', $validIds)
  505. ->order('sort_weight ASC')
  506. ->select();
  507. $newList = array_column($notifyResult, null, 'notify_type');
  508. // ## 获取强更公告
  509. $notieQg = isset($newList[3]) ? $newList[3] : [];
  510. if($notieQg){
  511. // 状态:0=无、1=已是最新版本(无用)、2=已是最新版本(可以强更)、3=获取最新分包(请求分包)、4=分包更新中(请求中)、5=分包失败(重新提交分包)
  512. $fbStatus = 1;
  513. $sdkGameModel = new SdkGameList;
  514. $fbInfo = $sdkGameModel->where(['channel_id' => $channel_id, 'gameid' => $gameInfo['id']])->field('id,filename,channel_version,upload_status,status')->find();
  515. if(!$fbInfo){
  516. $this->jsonResult($result, -120, '分包数据不存在!');
  517. }
  518. if($fbInfo['status'] === 0){
  519. $this->jsonResult($result, -121, '当前渠道已禁用,无法分包,请联系商务!');
  520. }
  521. $result['ext']['channel_version'] = $fbInfo['channel_version'];
  522. if($channel_version != $fbInfo['channel_version'] || $fbInfo['channel_version'] != $gameInfo['channel_version']){
  523. // 主动分包条件:分包和游戏包版本号不一致
  524. if($fbInfo['channel_version'] != $gameInfo['channel_version']){
  525. $fbStatus = 4;
  526. $subChannel = new SubChannel;
  527. $subChannelRes = $subChannel->index($channel_id, $game_id, 'ydd_fb',$gameInfo['type'],0);
  528. if($subChannelRes['code'] === 0){
  529. $sdkGameModel->save(['upload_status' => 4, 'update_time' => time()], ['channel_id' => $channel_id, 'gameid' => $game_id]);
  530. }else{
  531. $fbStatus = 5;
  532. }
  533. }else{
  534. // 获取分包数据条件:客户端包合分包版本号不一致
  535. if($channel_version != $fbInfo['channel_version']){
  536. $fbStatus = 2;
  537. if($gameInfo['type'] == 2){ // 安卓
  538. if($fbInfo && isset($fbInfo['filename'])){
  539. $result['notify_qg']['url'] = APK_DOWN_DOMAIN_NEW.'/sygame/'.$gameInfo['pinyin'].'/'.$fbInfo['filename'];
  540. }
  541. }elseif($gameInfo['type'] == 1){ // IOS
  542. $shortModel = new PromotionShortLink;
  543. $shortInfo = $shortModel->where(['channel_id' => $channel_id, 'game_id' => $game_id])->field('id,short_link')->find();
  544. if($shortInfo){
  545. $result['notify_qg']['url'] = $shortInfo['short_link']?T_DOMAIN.'/'.$shortInfo['short_link']:'';
  546. }
  547. }
  548. }
  549. }
  550. $msg = '可更新!';
  551. switch ($fbStatus){
  552. case 1:
  553. $msg = '已是最新包';
  554. break;
  555. case 2:
  556. $msg = '下载最新包';
  557. break;
  558. case 3:
  559. $msg = '获取最新包';
  560. break;
  561. case 4:
  562. $msg = '分包处理中,请稍后...';
  563. break;
  564. case 5:
  565. $msg = isset($subChannelRes['msg'])?'分包失败:'.$subChannelRes['msg']:'分包失败';
  566. break;
  567. }
  568. $result['notify_qg']['status'] = $fbStatus;
  569. $result['notify_qg']['msg'] = $msg;
  570. $result['notify_qg']['id'] = $notieQg['id']??'';
  571. $result['notify_qg']['title'] = $notieQg['title']??'';
  572. $result['notify_qg']['content'] = '<head><meta name="viewport" content="width=device-width"></head>' . $notieQg['content']??'';
  573. }
  574. }
  575. // ## 获取单次公告
  576. $notieDc = isset($newList[1]) ? $newList[1] : [];
  577. if($notieDc){
  578. $result['notify_dc']['id'] = $notieDc['id']??'';
  579. $result['notify_dc']['title'] = $notieDc['title']??'';
  580. $result['notify_dc']['content'] = '<head><meta name="viewport" content="width=device-width"></head>' . $notieDc['content']??'';
  581. }
  582. // ## 获取时效公告
  583. $notieSx = isset($newList[4]) ? $newList[4] : [];
  584. if($notieSx){
  585. $result['notify_sx']['id'] = $notieSx['id']??'';
  586. $result['notify_sx']['title'] = $notieSx['title']??'';
  587. $result['notify_sx']['content'] = '<head><meta name="viewport" content="width=device-width"></head>' . $notieSx['content']??'';
  588. }
  589. $this->jsonResult($result, 1, "获取成功!");
  590. }
  591. // 申请最新游戏包分包
  592. public function getPackage(){
  593. $game_id = $this->input('gameid'); // 游戏ID
  594. $channel_id = $this->input('channel_id'); // 渠道ID
  595. $channel_version = $this->input('channel_version', ''); // 游戏的强更号
  596. $sdkGameModel = new SdkGameList;
  597. $fbInfo = $sdkGameModel->where(['channel_id' => $channel_id, 'gameid' => $game_id])->field('id,filename,channel_version,upload_status')->find();
  598. if(!$fbInfo){
  599. $this->jsonResult('', 0, '分包数据不存在!');
  600. }
  601. if ($fbInfo['upload_status'] == 4){
  602. $this->jsonResult('', 1, '分包中...');
  603. }
  604. $gameModel = new Game;
  605. $gameInfo = $gameModel->where(['id' => $game_id])->field('id,type,pinyin,channel_version')->find();
  606. if(!$gameInfo){
  607. $this->jsonResult('', 0, '游戏不存在!');
  608. }
  609. // 客户端已是最新版本 则只 显示公告
  610. if($channel_version >= $gameInfo['channel_version']){
  611. $this->jsonResult('', 1, '已是最新版本!');
  612. }
  613. $subChannel = new SubChannel;
  614. // $platform = $gameInfo['type']==1?1:0; // 设备类型:1=ios、2=安卓、3=h5
  615. $subChannelRes = $subChannel->index($channel_id, $game_id, 'ydd_fb',$gameInfo['type'],0);
  616. if($subChannelRes['code'] === 0){
  617. $sdkGameModel->save(['upload_status' => 4, 'update_time' => time()], ['channel_id' => $channel_id, 'gameid' => $game_id]);
  618. $this->jsonResult('', 1, '提交成功!');
  619. }else{
  620. $this->jsonResult('', 0, '提交失败!');
  621. }
  622. }
  623. }