where(['gameid' => $game_id])->find()) { $runnable = false; $msg = '光环游戏记录不存在'; } if ($runnable) { $game_info = Game::get($game_id); $mobileicon = $gameInfoModel->where(['game_id'=>$game_id])->value('mobileicon'); if(empty($game_info)) { $runnable = false; $msg = 'game_id不存在'; } elseif($platform==1 && (empty($mobileicon) || empty($game_info['package_name']))){ // ios游戏时 $runnable = false; $msg = '游戏参数不全,请前往游戏内容编辑页面配置(请添加游戏图标、落地背景页)'; } else{ $pinyin = $game_info['pinyin']; $extend['channel_version'] = $game_info['channel_version']; $extend['pinyin'] = $game_info['pinyin']; $extend['channel_id'] = (string)$channel_id; $extend['admin_id'] = $admin_id; $extend['512pngUrl'] = STATIC_DOMAIN.$mobileicon; //游戏图标 $extend['bundleID'] = $game_info['package_name']; //游戏包名 $extend['gameChineseName'] = $game_info['name']; //游戏名称 } } if($runnable) { $channel_info = \app\common\model\Channel::get($channel_id); if(empty($channel_info)) { $runnable = false; $msg = '渠道信息不存在'; } else if($channel_info['level'] <> 3){ $runnable = false; $msg = '非推广员渠道,不能进行打包'; } } // 渠道 + 游戏 是否被禁止打包 if($runnable) { // todo 此处 数据库运行很慢 需要看下是什么原因 暂时先屏蔽了 $isFrozen = isFrozenOption($channel_id, $game_id); if($isFrozen) { $runnable = false; $msg = '您已经被禁止打包此游戏'; } } // 判断游戏母包是否存在 if($runnable && $platform != 3) { // h5游戏不需要母包 $apk_is_exists = isRawGameApkExist($pinyin,$platform,$apktype); if($apk_is_exists == false || $apk_is_exists == 'False') { $runnable = false; $msg = '游戏母包不存在'; } } // 判断 channel_id 与 game_id 是否在sdkgamelist表 无则新建 有则跳过 if($runnable) { $result = (new SdkGameList())->where(['gameid' => $game_id, 'channel_id' => $channel_id, 'package_type'=> $apktype])->find(); // 没有分包记录 则生成记录 if (empty($result)) { $data = [ 'gameid' => $game_id, 'channel_id' => $channel_id, 'update_time' => time(), 'create_time' => time(), 'channel_version' => $game_info['channel_version'], 'package_type' => $apktype, 'upload_status' => 4, ]; $resultAdd = (new SdkGameList())->save($data); // ## 双端推广链接合并 ## // 判断双端对应链接是否存在,存在则使用另一端的推广地址 $shortLinkModel = new PromotionShortLink(); if(in_array($game_info['type'], [1,2])){ if($game_info['type'] == 1){ $bandId = Db::table('nw_game_band')->where(['ios_game_id' => $game_id])->value('android_game_id'); } else if ($game_info['type'] == 2){ $bandId = Db::table('nw_game_band')->where(['android_game_id' => $game_id])->value('ios_game_id'); } if($bandShortLink = $shortLinkModel->where(['game_id'=>$bandId, 'channel_id'=>$channel_id, 'package_type'=>$apktype, 'type' => 1])->value('short_link')) { $shortLinkModel->insert(['game_id'=> $game_id, 'channel_id'=> $channel_id, 'package_type'=>$apktype, 'create_time'=>time(), 'short_link'=> $bandShortLink, 'type' => 1]); }else{ $shortLinkModel->insertData($game_id,$channel_id,$apktype, 1); } }else{ $shortLinkModel->insertData($game_id,$channel_id,$apktype, 1); // $shortLinkModel->insertData($game_id,$channel_id,$apktype, 2); } if($resultAdd == 0 || $resultAdd == false) { $runnable = false; $msg = '生成分包记录存储失败'; } } elseif(!empty($result['filename'])){ // 文件名记录不为空时,删除包体 // log_message('filename:' . $result['filename'], 'log', LOG_PATH . 'package/'); // // 路径先依照老平台的来,如果要修改,需要和 python 上传时使用的路径保持一致 // $file_path = "sygame/{$game_info['pinyin']}/{$result['filename']}"; // $pathinfo = pathinfo($result['filename']); // // //IOS包时,需要同步删除plist文件 // if($pathinfo['extension']=='ipa'){ // if(UPLOAD_SAVE_PLACE=='ali'){ // AliyunOSS::deleteObject("sygame/{$game_info['pinyin']}/{$pathinfo['filename']}.plist",PACKAGE_BUCKET); // } elseif(UPLOAD_SAVE_PLACE=='huawei') { // $obs = new HuaweiObs(); // $obs->DeleteObject("sygame/{$game_info['pinyin']}/{$pathinfo['filename']}.plist",PACKAGE_BUCKET); // } // } // // log_message('file_path:' . $file_path, 'log', LOG_PATH . 'package/'); // if(UPLOAD_SAVE_PLACE=='ali'){ // // 无需传递文件的绝对路径,传递 URI 部分即可,这个方法的返回值,并不能表示删除结果。所以不做判断 // AliyunOSS::deleteObject($file_path,PACKAGE_BUCKET); // } elseif(UPLOAD_SAVE_PLACE=='huawei'){ // $obs->DeleteObject($file_path,PACKAGE_BUCKET); // } } } // 防止频繁提交任务 [python那边没有处理,该段代码先注释] if($runnable) { $cache_key = "subpackage_channel_{$game_id}_{$channel_id}".date('Y-m-d'); if(cache($cache_key)) { $runnable = false; $msg = '打包任务已提交,请稍候'; }else{ cache($cache_key, time(), 10); } } // 产生一个随机的文件名,避免文件分发过程中,被各种云给缓存掉,导致渠道串包 $apk_filename = uniqid(); // H5 if($platform == 1){ $apk_filename .= '.ipa'; }else if ($platform == 2){ $apk_filename .= '.apk'; } if($platform == 3){ // $game_url = Db::name('nw_game_package_upload')->where(['game_id' => $game_id])->value('path'); $update_result = (new SdkGameList())->where(['gameid' => $game_id, 'channel_id' => $channel_id, 'package_type' => $apktype]) ->update(['filename' => $apk_filename, 'channel_version' => $game_info['channel_version'], 'upload_status' => 1, 'update_time' => time()]); if ($update_result == 0 || $update_result == false) { return ["code" => 0, "msg" => $game_info['name']." - 分包失败:更新失败!", "data" => ['filename' => $apk_filename]]; } return ["code" => 1, "msg" => "分包成功!", "data" => ['filename' => $apk_filename]]; } // ## 同步分包处理 if ($runnable && $pack_type == 1) { $task_data = [ 'filename' => $pinyin, // 用于打包程序定位apk目录 'channel_id' => (string)$channel_id, 'apktype' => $apktype, //游戏包类型 'extend' => json_encode($extend), 'ext' => ($platform == 1) ? 'ipa' : 'apk', 'target_filename' => $apk_filename, // 指定要生成的渠道包文件名称 'sign_ver' => $game_info['sign_ver'] ?? 0, // 指定要生成的渠道包文件名称 ]; $res1 = gameApkPack($task_data); $res = json_decode($res1, true); if (!$res && $res['status'] != 'success' || $res1 == false) { Db::name('cy_sdkgamelist')->where(['gameid' => $game_id, 'channel_id' => $channel_id])->update(['upload_status' => 5]); return ["code" => 0, "msg" => "分包失败:" . $res['message'], "data" => []]; } $update_result = (new SdkGameList())->where(['gameid' => $game_id, 'channel_id' => $channel_id, 'package_type' => $apktype]) ->update(['filename' => $apk_filename, 'channel_version' => $game_info['channel_version'], 'upload_status' => 4, 'update_time' => time()]); if ($update_result == 0 || $update_result == false) { return ["code" => 0, "msg" => "分包失败:包名存储更新失败!", "data" => ['filename' => $apk_filename]]; } Db::name('cy_sdkgamelist')->where(['gameid' => $game_id, 'channel_id' => $channel_id])->update(['upload_status' => 1]); return ["code" => 1, "msg" => "分包成功!", "data" => ['filename' => $apk_filename]]; } // ## 异步分包处理 if ($runnable) { // 更新到数据表中 $update_result = (new SdkGameList())->where(['gameid' => $game_id, 'channel_id' => $channel_id, 'package_type' => $apktype]) ->update(['filename' => $apk_filename, 'channel_version' => $game_info['channel_version'], 'upload_status' => 4, 'update_time' => time()]); if ($update_result == 0 || $update_result == false) { $runnable = false; $msg = '打包记录存储失败'; } } // 投递任务至异步队列 if ($runnable && $pack_type == 0) { $redis = Cache::init()->handler(); // 获取redis句柄 // 异步打包完成后,回调通知回来的数据 // todo 新得回调地址 http://admin.7dgames.cn/upload_notify/index/game_id/249/channel_id/5217/adminid/cps5190/apktype/0.html $callback_url = SUBPACK_DOMAIN . '/upload_notify/index/game_id/' . $game_id . '/channel_id/' . $channel_id . '/adminid/' . $admin_id . '/apktype/' . $apktype . '.html'; // TODO: 本地测试专用: // $encrypt = md5($game_id.$channel_id.$admin_id.$apktype.'aoyou88!!!'); // $callback_url = SUBPACK_DOMAIN.'/upload_notify/index/game_id/'.$game_id.'/channel_id/'.$channel_id.'/encrypt/'.$encrypt.'/adminid/'.$admin_id.'/apktype/'.$apktype.'.html'; $task_key = ($platform==1) ? 'aoyou:ios:subpackage:task' : 'aoyou:apk:subpackage:task'; $task_data = [ 'filename' => $pinyin, // 用于打包程序定位apk目录 'channel_id' => (string)$channel_id, 'apktype' => $apktype, //游戏包类型 'extend' => $extend, 'target_filename' => $apk_filename, // 指定要生成的渠道包文件名称 'finish_notice_url' => $callback_url, // 打包完成后,回调通知该地址 'sign_ver' => $game_info['sign_ver'] ?? 0, // 签名版本 ]; // json_encode($task_data) = "{"filename":"mubaoANDROID","channel_id":"5215","apktype":0,"extend":{"channel_version":18,"pinyin":"mubaoANDROID","channel_id":"5215","admin_id":1,"512pngUrl":"http:\/\/cdn.7dgame.cn\/image\/game\/icons\/2023091416266502c3cc46898.png","bundleID":"com.qimeng.mubao","gameChineseName":"\u6bcd\u5305ANDROID"},"target_filename":"66c5ac2f14326.apk","finish_notice_url":"http:\/\/admin.7dgames.cn\/upload_notify\/index\/game_id\/10\/channel_id\/5215\/adminid\/1\/apktype\/0.html","sign_ver":0}" $redis->lpush($task_key, json_encode($task_data)); } // 响应 if($runnable) { return ['msg' => '打包任务发送成功', 'code' => 0]; }else{ return ['msg' => $msg, 'code' => 400]; } } /** * 打包任务完成后,通知该方法 */ public function uploadPackageNotify() { $runnable = true; $channel_id = input('channel_id/d'); $game_id = input('game_id/d'); $adminid = input('adminid','','trim'); $apktype = input('apktype/d'); //0 普通包,1 光环包 $token = input('encrypt'); $checkToken = md5($game_id.$channel_id.$adminid.$apktype.'aoyou88!!!'); // token 校验 md5(game_id+channel_id+adminid+'aoyou88!!!') md5(234354aoyou88!!!) // if ($checkToken != $token) { // $runnable = false; // $msg = 'token校验失败!'; // } if(empty($channel_id)) { $runnable = false; $msg = '参数缺失!'; } if(empty($adminid)) { $runnable = false; $msg = '用户id缺失!'; } if($runnable && empty($game_id)) { $runnable = false; $msg = '参数缺失!'; } // TODO 验证adminid, try { if($runnable) { // 修改包的上传状态 $update_result = (new SdkGameList) ->where(['gameid' => $game_id, 'channel_id' => $channel_id,'package_type'=>$apktype]) ->update(['upload_status' => 1]); if(!$update_result) { $runnable = false; $msg = '修改apk状态失败!'; } } // 调用消息系统接口,websocket告知用户打包完成 if($runnable && $adminid != 'ydd_fb') { // 这块代码 是给前端 websocket 通知得 $ws = new Websocket(); $gameInfo = model('Game')->where(['id' => $game_id])->find(); if (!$gameInfo) { $runnable = false; $msg = '游戏id不存在!'; } else { // 没有华为云,先注释 // try { // // $sdkGameList = (new SdkGameList)->where(['gameid' => $game_id, 'channel_id' => $channel_id,'package_type'=>$apktype])->find(); // // $down_url = APK_DOWN_DOMAIN.'/sygame/'.$gameInfo['pinyin'].'/'.$sdkGameList['filename']; // // (new HuaWeiCloudService())->createPreheatingTasks([$down_url]); // }catch(\Exception $e){ // $template = 'cdn创建缓存异常,'.$e->getMessage().'时间:'.date('Y-m-d H:i:s'); // $ddurl = Env::get('warning_url'); // curlDD($template, $ddurl,true); // } $gamename = $gameInfo['name'].'-'.$channel_id; $str = '{"error_code":0,"data":{"action":"subpackageFinish","data":{"code":0,"gamename":"'.$gamename.'", "message":"分包完成!"}}}'; $ws->sayToUid($adminid, $str); } } }catch (\Exception $e){ $runnable = false; } if($runnable) { return 'success'; }else{ return $msg; } } /** * 更新游戏分包表的记录、 删除OSS的渠道包文件 * * @param $package_type integer 游戏包类型 0:普通包,1:光环包 */ public function deletePackage($channel_id, $game_id,$package_type){ $runnable = true; $msg = ''; // 查询一下是否存在打包记录 $apk_info = (new SdkGameList())->where(['gameid' => (int)$game_id, 'channel_id' => (int)$channel_id,'package_type'=>$package_type])->find(); if(empty($apk_info) || empty($apk_info['filename'])) { $runnable = false; $msg = '无分包记录'; } /* if($runnable && empty($apk_info['upload_status'])) { $runnable = false; $msg = '无打包或未上传完成'; }*/ if($runnable) { //删除包体文件 $this->removePackageFile($game_id, $apk_info['filename']); (new SdkGameList())->save(['filename' => '', 'upload_status' => 0],['id' => $apk_info['id']]); } if($runnable) { return ['msg' => 'success']; }else{ return ['msg' => $msg]; } } /** * 删除渠道包文件 * * @param $game_id 游戏ID * @param $filename string 包体文件名 */ public function removePackageFile($game_id,$filename) { $runnable = true; $msg = ''; if(empty($filename)){ $runnable = false; $msg = '包体文件名不能为空'; } if($runnable){ $game_info = Game::get($game_id); if(empty($game_info)) { $runnable = false; $msg = 'game_id不存在'; }else{ $pinyin = $game_info['pinyin']; } } if($runnable) { // 路径先依照老平台的来,如果要修改,需要和 python 上传时使用的路径保持一致 $file_path = "sygame/{$pinyin}/{$filename}"; $pathinfo = pathinfo($filename); if(isset($pathinfo['extension'])){ //IOS包时,需要同步删除plist文件 if($pathinfo['extension']=='ipa'){ if(UPLOAD_SAVE_PLACE=='ali'){ AliyunOSS::deleteObject("sygame/{$pinyin}/{$pathinfo['filename']}.plist",PACKAGE_BUCKET); } elseif(UPLOAD_SAVE_PLACE=='huawei'){ $obs = new HuaweiObs(); $obs->DeleteObject("sygame/{$pinyin}/{$pathinfo['filename']}.plist",PACKAGE_BUCKET); } } if(UPLOAD_SAVE_PLACE=='ali'){ // 无需传递文件的绝对路径,传递 URI 部分即可,这个方法的返回值,并不能表示删除结果。所以不做判断 AliyunOSS::deleteObject($file_path,PACKAGE_BUCKET); } elseif(UPLOAD_SAVE_PLACE=='huawei'){ $obs = new HuaweiObs(); $obs->DeleteObject($file_path,PACKAGE_BUCKET); } } } return ['code' => $runnable,'msg' => $msg]; } /** * 删除游戏分包表的记录、 删除OSS的渠道包文件 * * @param $package_type integer 游戏包类型 0:普通包,1:光环包 */ public function removePackage($channel_id, $game_id,$package_type){ $runnable = true; $msg = ''; // 查询一下是否存在打包记录 $apk_info = (new SdkGameList())->where(['gameid' => (int)$game_id, 'channel_id' => (int)$channel_id,'package_type'=>$package_type])->find(); if(empty($apk_info)) { $runnable = false; $msg = '无分包记录'; } if($runnable) { //删除包体文件 $this->removePackageFile($game_id, $apk_info['filename']); (new SdkGameList())->where(['id' => $apk_info['id']])->delete(); } if($runnable) { return ['msg' => 'success']; }else{ return ['msg' => $msg]; } } /** * 根据活跃天数进行批量分包 * * @param $gameId int 游戏ID * @param $day_limit int 活跃天数 * @param $package_type int 游戏包类型 * @param $admin_id int 操作者ID * */ public function batchSubpackageByActiveDay($gameId,$day_limit,$package_type,$admin_id) { $gameModel = new Game; $gameInfoModel = new GameInfo; $runnable = true; $msg = ''; $reply = []; if (!$gameId) { $msg = '游戏id参数错误!'; $runnable = false; } if (!$day_limit) { $msg = '活跃天数参数错误!'; $runnable = false; } if(!in_array($package_type,[0,1])){ $msg = '游戏包类型参数错误'; $runnable = false; } $condition['cy_sdkgamelist.gameid'] = $gameId; $condition['cy_sdkgamelist.package_type'] = $package_type; $condition['channel.level'] = 3; $gameInfo = $gameModel->where(['id' => $gameId])->field('channel_version')->find(); if (!$gameInfo) { $msg = '游戏无信息!'; $runnable = false; } //游戏平台 $platform = $gameInfoModel->where(['game_id'=>$gameId])->value('platform'); $platform = ($platform==false) ? 0 : $platform; $list = (new SdkGameList())->join('nw_channel channel', 'cy_sdkgamelist.channel_id = channel.id')->where($condition)->select(); if(!empty($list) && $runnable) { $loginInfo = new Logininfo(); foreach ($list as $val) { // todo 非活跃渠道,不做分包, 联运部门除外 if($val['channel_id'] != 50) { $is_active_channel = $loginInfo->isActiveChannel($val['channel_id'], $gameId, $day_limit); if (false == $is_active_channel) continue; } $this->deletePackage($val['channel_id'], $gameId,$package_type); // 删包 $subPath = $this->index($val['channel_id'], $gameId, $admin_id,$platform,$package_type); // 分包 $reply[] = array( 'channel_name' => get_channel_name($val['channel_id']), 'msg' => $subPath['code'] == 0 ? ' 分包成功请等待后端消息推送': ' 分包失败,'.$subPath['msg'], 'code' => $subPath['code'] ); } } if($runnable) { if(!empty($reply)){ $res = $reply; }else{ $res = [ 'code' => 400, 'msg' => '无可分包渠道' ]; } } else { $res = [ 'code' => 400, 'msg' => $msg ]; } return $res; } /** * 批量分包 * * @param $gameId int 游戏ID * @param $day_limit int 活跃天数 * @param $package_type int 游戏包类型 * @param $admin_id int 操作者ID * */ public function batchBySubpackage($gameId,$admin_id) { $gameModel = new Game; $gameInfoModel = new GameInfo; $runnable = true; $msg = ''; $reply = []; if (!$gameId) { $msg = '游戏id参数错误!'; $runnable = false; } $condition['cy_sdkgamelist.gameid'] = $gameId; $condition['channel.level'] = 3; $gameInfo = $gameModel->where(['id' => $gameId])->field('channel_version')->find(); if (!$gameInfo) { $msg = '游戏无信息!'; $runnable = false; } //游戏平台 $platform = $gameInfoModel->where(['game_id'=>$gameId])->value('platform'); $platform = ($platform==false) ? 0 : $platform; $list = (new SdkGameList())->join('nw_channel channel', 'cy_sdkgamelist.channel_id = channel.id')->where($condition)->select(); if(!empty($list) && $runnable) { foreach ($list as $val) { $this->deletePackage($val['channel_id'], $gameId,$val['package_type']); // 删包 $subPath = $this->index($val['channel_id'], $gameId, $admin_id,$platform,$val['package_type']); // 分包 $reply[] = array( 'channel_name' => get_channel_name($val['channel_id']), 'msg' => $subPath['code'] == 0 ? ' 分包成功请等待后端消息推送': ' 分包失败,'.$subPath['msg'], 'code' => $subPath['code'] ); } } if($runnable) { if(!empty($reply)){ $res = $reply; }else{ $res = [ 'code' => 400, 'msg' => '无可分包渠道' ]; } } else { $res = [ 'code' => 400, 'msg' => $msg ]; } return $res; } /** * 渠道包下载,下载地址为永久,不随着渠道apk文件名变化而失效 * 访问地址: /index.php/downapp/游戏ID/渠道ID */ public function down($channel_id, $game_id){ $runnable = true; $msg = ''; if(empty($channel_id) || empty($game_id)) { $runnable = false; $msg = '参数有误'; } if($runnable) { // 查出包的信息 $apk_info = (new SdkGameList()) ->where(['gameid' => $game_id, 'channel_id' => $channel_id]) ->find(); if(empty($apk_info)) { $runnable = false; $msg = '未找到分包信息'; } } if($runnable && empty($apk_info['upload_status'])) { $runnable = false; $msg = '游戏包暂未生成'; } if($runnable) { try{ // 查询出游戏的拼音名称 $pinyin = Game::get($game_id)->pinyin; }catch (Exception $e){ exit(""); } // 跳转到CDN下载地址 header("Location:".APK_DOWN_DOMAIN."/sygame/{$pinyin}/{$apk_info['filename']}"); } !$runnable && exit(""); } /** * @param int $platform * * @return bool */ public function isB (int $platform): bool { return $platform == 1; } }