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;
}
}