'ID', 'status' => ' 状态', 'output' => '输出', 'mall_id' => ' 商城ID', 'desc' => '描述', 'upload_secret_key' => '小程序上传密钥', 'app_id' => 'appid', 'version' => '版本号', 'is_review' => '是否审核', 'api_domain' => '接口域名', 'static_domain' => '静态资源域名', 'mall_sign' => '商城签名', 'created_at' => '添加时间', 'updated_at' => '修改时间', ]; } /** * 添加任务 * @param mixed $params * @throws \Exception * @return WechatMiniProgramUpload|bool */ public static function addTask($params) { try { $model = new self(); $params['desc'] = empty($params['desc']) ? '后台管理员于' . date('Y-m-d H:i:s') . '提交上传' : $params['desc']; $model->setAttributes($params); $res = $model->save(); if ($res === false) throw new \Exception($model->getErrorMessage()); return $model; } catch (\Exception $e) { self::$static_error = $e->getMessage(); return false; } } /** * 上传最新微信小程序 * @param mixed $params * @return void */ public static function uploadMiniProgramCode($params) { try { $output = '['.date('Y-m-d H:i:s').'] 上传小程序任务队列开始...'.PHP_EOL;; $id = $params['id']; $task = self::find()->where(['id' => $id])->one(); // if ($task->status == 1) return true; // 下载最新代码包,并替换全局参数 $app_id = $task['app_id']; $api_domain = $task['api_domain']; $static_domain = $task['static_domain']; $mall_id = $task['mall_id']; $mall_sign = $task['mall_sign']; // $project_name = $task['is_live'] == 1 ? 'mp-weixin-live' : 'mp-weixin'; $project_name = 'mp-weixin'; // 替换为代码库的云域名 $latest_mp_downlaod_url = \Yii::$app->params['latest_mp_downlaod_url'] ?? ''; if(empty($latest_mp_downlaod_url)) throw new Exception('未设置远程下载链接'); $url = $latest_mp_downlaod_url."/$project_name.zip"; $tempDir = '/tmp'; $filename = $tempDir."/$app_id.zip"; $unzip_dir = $tempDir."/$app_id"; $project_dir = "$unzip_dir/$project_name"; $log_file = $unzip_dir.'/upload.log'; if(file_exists($unzip_dir)) exec("rm -rf $unzip_dir"); $output .= '['.date('Y-m-d H:i:s').'] 开始下载最新小程序代码包...'.PHP_EOL.'网址:'.$url.PHP_EOL; // 下载压缩包并解压 self::downloadCode($url, $filename); exec("unzip $filename -d $unzip_dir && rm -rf $filename",$outstd, $status); if($status == 0){ $output .= '['.date('Y-m-d H:i:s').'] 解压代码包成功:'. json_encode($outstd) .PHP_EOL; }else{ throw new Exception('解压代码包失败:'."unzip $filename -d $unzip_dir && rm -rf $filename".PHP_EOL. json_encode($outstd) . PHP_EOL); $outstd = []; } // 地图密钥 $config = Yii::$app->services->setting->platform->get('map'); $qq_map_key = $config['qq_jsapi_key_h5'] ? $config['qq_jsapi_key_h5'] : 'TSOBZ-PHCWT-MPBX5-LZLSP-4WSW6-UIFVJ'; $replace_items = [ ['---mp_app_id---', $app_id], ['---api---', $api_domain], ['---static---', $static_domain], ['---sign---', $mall_sign], ['TSOBZ-PHCWT-MPBX5-LZLSP-4WSW6-UIFVJ', $qq_map_key], // 替换地图key ]; $output .= '['.date('Y-m-d H:i:s').'] 参数替换列表:'.PHP_EOL.json_encode($replace_items).PHP_EOL; // 替换项目文件参数 $output .= '['.date('Y-m-d H:i:s').'] 替换项目参数.'.PHP_EOL; foreach ($replace_items as $item) { $find_str = $item[0]; $replace_str = $item[1]; $files = self::findFileContainStr($project_dir, $find_str); if (!empty($files)) { // 替换参数 foreach ($files as $file) { if (file_exists($file)) { $content = file_get_contents($file); file_put_contents($file, str_replace($find_str, $replace_str, $content)); $output .= $find_str . '--->' . $replace_str . PHP_EOL; } } } } // 替换应用的扩展插件 if ($task['is_live']) { $pluginsStr = '{'; if (!empty($task['plugins'])) { $plugins = MpwxPlugins::find() ->where(['addons_name' => $task['plugins'], 'status' => StatusEnum::ENABLED]) ->select('plugins') ->column(); $pluginsStr .= implode(',', $plugins); } $pluginsStr .= '}'; // 替换内容 $command = <<params['node_bin'] ?? ''; $upload_script = Yii::getAlias('@webpath').DIRECTORY_SEPARATOR.'node/mp-ci-upload.js'; if(!$node || !file_exists($upload_script)) throw new Exception('node命令不存在 / js文件不存在'); //$upload_script = '/www/wwwroot/mp_ci/mp-ci-upload.js'; $script_params = "--appid $app_id --projectPath $project_dir --privateKeyPath $key_file --version {$task['version']} --desc '{$task['desc']}'"; $log = "2>&1 > $log_file"; $output .= '['.date('Y-m-d H:i:s').'] 组装上传命令.'.PHP_EOL; $command = "$node $upload_script $script_params $log"; $output .= $command . PHP_EOL; $output .= '['.date('Y-m-d H:i:s').'] 执行上传命令.'.PHP_EOL; exec($command , $outstd, $status); $output .= file_get_contents($log_file) .PHP_EOL; $task->output = $output; // 成功 $task->status = 1; $task->save(); exec("rm -rf $unzip_dir"); return true; } catch (Throwable $e) { $output .= $e->getMessage(). PHP_EOL; $task->output = $output; $task->status = 2; $task->save(); Yii::error('上传小程序代码出错!uploadMiniProgramCode:' . $e->getMessage()); return false; } } /** * 查找包含指定字符串的文件列表 * @param mixed $project_dir * @param mixed $str * @return array|null */ private static function findFileContainStr($project_dir, $str) { $output = []; exec("grep -Rl '\\$str' $project_dir", $output, $status); return $output; } /** * 下载最新小程序代码 * @param mixed $url * @param mixed $filename * @return void */ private static function downloadCode($url, $filename) { $ch = curl_init(); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_URL, $url); $fp = fopen($filename, 'w'); curl_setopt($ch, CURLOPT_FILE, $fp); curl_exec($ch); curl_close($ch); fclose($fp); } /** * 获取文件临时目录 */ public static function getBackupDir() { $dir = Yii::$app->getRuntimePath().DIRECTORY_SEPARATOR.'bak'.DIRECTORY_SEPARATOR; if (!is_dir($dir)) { @mkdir($dir, 0755, true); } return $dir; } /** * 获取最新版本号 * * @param integer $mall_id * @return string|null */ public static function getLastVersion(int $mall_id) { return static::find() ->where(['mall_id' => $mall_id]) ->orderBy('id DESC') ->select('version') ->scalar(); } /** * 获取下一个版本号 * * @param integer $mall_id * @return string */ public static function getNextVersion(int $mall_id) { $version = static::getLastVersion($mall_id); if ($version === null) return '1.0.0'; $parts = explode('.', $version); // x.y if (count($parts) == 2) { // 将最后一个部分转换为整数并增加1 $parts[1] = (int)$parts[1] + 1; } // x.y.z if (count($parts) == 3) { // 将最后一个部分转换为整数并增加1 $parts[2] = (int)$parts[2] + 1; } return implode('.', $parts); } /** * 修改任务审核状态 * * @return boolean */ public function changeTaskReview() { $this->is_review = intval(!$this->is_review); $this->changeAllTaskReview(); return $this->save(); } /** * 通过版本获取任务 * * @param integer $mall_id * @param string $version * @param false|int $cache * @param string|array $field * @return static|null */ public static function getTaskByVersion(int $mall_id, string $version, $cache = false, $field = 'id, is_review') { $model = static::find()->where(['mall_id' => $mall_id, 'version' => $version]); $cache && $model->cache($cache); $field && $model->select($field); return $model->one(); } /** * 修改其他状态 * * @return boolean */ public function changeAllTaskReview() { // 打开当前审核审核 关闭其他审核 if ($this->is_review) { $mall_id = $this->mall_id; static::updateAll(['is_review' => 0], ['mall_id' => $mall_id, 'is_review' => 1]); } } }