$item) { //版本描述 $lists[$k]['version_str'] = ''; $lists[$k]['able_update'] = 0; if ($localVersion == $item['version_no']) { $lists[$k]['version_str'] = '您的系统当前处于此版本'; } if ($localVersion < $item['version_no']) { $lists[$k]['version_str'] = '系统可更新至此版本'; $lists[$k]['able_update'] = 1; } //最新的版本号标志 $lists[$k]['new_version'] = 0; //注意,是否需要重新发布描述 $lists[$k]['notice'] = []; if ($item['uniapp_publish'] == 1) { $lists[$k]['notice'][] = '更新至当前版本后需重新发布前端商城'; } if ($item['pc_admin_publish'] == 1) { $lists[$k]['notice'][] = '更新至当前版本后需重新发布PC管理后台'; } if ($item['pc_shop_publish'] == 1) { $lists[$k]['notice'][] = '更新至当前版本后需重新发布PC商城端'; } //处理更新内容信息 $contents = $item['update_content']; $add = []; $optimize = []; $repair = []; $contentDesc = []; if (!empty($contents)) { foreach ($contents as $content) { if ($content['type'] == 1) { $add[] = '新增:' . $content['update_function']; } if ($content['type'] == 2) { $optimize[] = '优化:' . $content['update_function']; } if ($content['type'] == 3) { $repair[] = '修复:' . $content['update_function']; } } $contentDesc = array_merge($add, $optimize, $repair); } $lists[$k]['add'] = $add; $lists[$k]['optimize'] = $optimize; $lists[$k]['repair'] = $repair; $lists[$k]['content_desc'] = $contentDesc; unset($lists[$k]['update_content']); } $lists[0]['new_version'] = ($pageNo == 1) ? 1 : 0; return $lists; } /** * @notes 更新操作 * @param $params * @return bool|string * @author 段誉 * @date 2021/8/14 17:19 */ public static function upgrade($params) { $openBasedir = ini_get('open_basedir'); if(strpos($openBasedir, "server") !== false) { self::$error = '更新失败: 请临时关闭服务器本站点的跨域攻击设置,并重启 nginx、PHP,具体参考相关升级文档'; return false; } // 授权验证 $params['link'] = "package_link"; $result = self::verify($params); if (!$result['has_permission']) { self::$error = !empty($result['msg']) ? $result['msg'] : '请先联系客服获取授权'; // 写日志 self::addlog($params['id'], $params['update_type'], false); return false; } // 本地更新包路径 $localUpgradeDir = ROOT_PATH . '/upgrade/'; // 本地更新临时文件 $tempDir = ROOT_PATH . '/upgrade/temp/'; // 更新成功或失败的标识 $flag = true; Db::startTrans(); try { // 远程下载链接 $remoteUrl = $result['link']; if (!is_dir($localUpgradeDir)) { mkdir(iconv("UTF-8", "GBK", $localUpgradeDir), 0777, true); } //下载更新压缩包保存到本地 $remoteData = self::downFile($remoteUrl, $localUpgradeDir); if (false === $remoteData) { throw new \Exception('获取文件错误'); } //解压缩 if (false === unzip($remoteData['save_path'], $tempDir)) { throw new \Exception('解压文件错误'); } //更新sql->更新数据类型 if (false === self::upgradeSql($tempDir . 'sql/data/')) { throw new \Exception('更新数据库数据失败'); } //更新文件 if (false === self::upgradeFile($tempDir . 'project/server/', self::getProjectPath())) { throw new \Exception('更新文件失败'); } Db::commit(); } catch (\Exception $e) { Db::rollback(); self::$error = $e->getMessage(); //错误日志 $params['error'] = $e->getMessage(); // 标识更新失败 $flag = false; } if ($flag) { try { //更新sql->更新数据结构 if (false === self::upgradeSql($tempDir . 'sql/structure/')) { throw new \Exception('更新数据库结构失败'); } } catch (\Exception $e) { self::$error = $e->getMessage(); //错误日志 $params['error'] = $e->getMessage(); // 标识更新失败 $flag = false; } } //删除临时文件(压缩包不删除,删除解压的文件) if ($flag && false === del_target_dir($tempDir, true)) { Log::write('删除系统更新临时文件失败'); } // 增加日志 self::addlog($params['id'], $params['update_type'], $flag); return $flag; } /** * @notes 授权验证 * @param $params * @author Tab * @date 2021/10/26 17:12 */ public static function verify($params) { $domain = $_SERVER['SERVER_NAME']; $remoteUrl = self::BASE_URL . "/api/version/verify?domain=".$domain."&product_id=3&type=2&version_id=".$params['id']."&link=".$params['link']; $result = Requests::get($remoteUrl); $result = json_decode($result->body, true); $result = $result['data'] ?? ['has_permission' => false, 'link' => '', 'msg' => '']; return $result; } /** * @notes 获取远程版本数据 * @param null $pageNo * @param null $pageSize * @return array|mixed * @author 段誉 * @date 2021/8/14 17:20 */ public static function getRemoteVersion($pageNo = null, $pageSize = null) { $cacheVersion = Cache::get('version_lists' . $pageNo); if (!empty($cacheVersion)) { return $cacheVersion; } if (empty($pageNo) || empty($pageSize)) { $remoteUrl = self::BASE_URL . "/api/version/lists?product_id=3&type=2&page=1"; } else { $remoteUrl = self::BASE_URL . "/api/version/lists?product_id=3&type=2&page_no=$pageNo&page_size=$pageSize&page=1"; } $result = Requests::get($remoteUrl); $result = json_decode($result->body, true); $result = $result['data'] ?? []; Cache::set('version_lists' . $pageNo, $result, 1800); return $result; } /** * @notes 更新包下载链接 * @param $params * @return array|false * @author 段誉 * @date 2022/3/25 17:50 */ public static function getPkgLine($params) { $map = [ 1 => 'package_link', //一键更新类型 : 服務端更新包 2 => 'package_link', //服務端更新包 3 => 'pc_package_link', //pc端更新包 4 => 'uniapp_package_link', //uniapp更新包 5 => 'web_package_link', //后台前端更新包 6 => 'integral_package_link', //完整包 8 => 'kefu_package_link', //客服更新包 ]; $params['link'] = $map[$params['update_type']] ?? '未知类型'; // 授权验证 $result = self::verify($params); if (!$result['has_permission']) { self::$error = !empty($result['msg']) ? $result['msg'] : '请先联系客服获取授权'; // 写日志 self::addlog($params['id'], $params['update_type'], false); return false; } //增加日志记录 self::addlog($params['id'], $params['update_type']); //更新包下载链接 return ['line' => $result['link']]; } /** * @notes 添加日志 * @param $versionId //版本id * @param $updateType //更新类型 * @param bool $status //更新状态 * @return bool|\Requests_Response * @author 段誉 * @date 2021/10/9 14:48 */ public static function addlog($versionId, $updateType, $status = true) { //版本信息 $versionData = self::getVersionDataById($versionId); $domain = $_SERVER['SERVER_NAME']; try { $paramsData = [ 'version_id' => $versionData['id'], 'version_no' => $versionData['version_no'], 'domain' => $domain, 'type' => 2,//付费版 'product_id' => 3,//单商户plus 'update_type' => $updateType, 'status' => $status ? 1 : 0, 'error' => empty(self::$error) ? '' : self::$error, ]; $requestUrl = self::BASE_URL . '/api/version/log'; $result = Requests::post($requestUrl, [], $paramsData); return $result; } catch (\Exception $e) { Log::write('更新日志:' . '更新失败' . $e->getMessage()); return false; } } /** * @notes 通过版本记录id获取版本信息 * @param $id * @return array * @author 段誉 * @date 2021/10/9 11:40 */ public static function getVersionDataById($id) { $cacheVersion = self::getRemoteVersion()['lists'] ?? []; if (!empty($cacheVersion)) { $versionColumn = array_column($cacheVersion, null, 'id'); if (!empty($versionColumn[$id])) { return $versionColumn[$id]; } } return []; } /** * @notes 下载远程文件 * @param $url * @param string $savePath * @return array|false * @author 段誉 * @date 2021/8/14 17:20 */ public static function downFile($url, $savePath = './upgrade/') { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, TRUE); curl_setopt($ch, CURLOPT_NOBODY, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $response = curl_exec($ch); $header = ''; $body = ''; if (curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200') { $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $header = substr($response, 0, $headerSize); $body = substr($response, $headerSize); } curl_close($ch); //文件名 $fullName = basename($url); //文件保存完整路径 $savePath = $savePath . $fullName; //创建目录并设置权限 $basePath = dirname($savePath); if (!file_exists($basePath)) { @mkdir($basePath, 0777, true); @chmod($basePath, 0777); } if (file_put_contents($savePath, $body)) { return [ 'save_path' => $savePath, 'file_name' => $fullName, ]; } return false; } /** * @notes 获取项目路径 * @return string * @author 段誉 * @date 2021/8/14 17:20 */ public static function getProjectPath() { $path = dirname(ROOT_PATH); if(substr($path, -1) != '/') { $path = $path . '/'; } return $path; } /** * @notes 更新sql * @param $dir * @return bool * @author 段誉 * @date 2021/8/14 17:20 */ public static function upgradeSql($dir) { //没有sql文件时无需更新 if (!file_exists($dir)) { return true; } //遍历指定目录下的指定后缀文件 $sqlFiles = get_scandir($dir, '', 'sql'); if (false === $sqlFiles) { return false; } //当前数据库前缀 $sqlPrefix = config('database.connections.mysql.prefix'); foreach ($sqlFiles as $k => $item) { if (get_extension($item) != 'sql') { continue; } $sqlContent = file_get_contents($dir . $item); if (empty($sqlContent)) { continue; } $sqls = explode(';', $sqlContent); //执行sql foreach ($sqls as $sql) { $sql = trim($sql); if (!empty($sql)) { $sql = str_replace('`ls_', '`' . $sqlPrefix, $sql) . ';'; Db::execute($sql); } } } return true; } /** * @notes 更新文件 * @param $tempFile * @param $oldFile * @return bool * @author 段誉 * @date 2021/8/14 17:21 */ public static function upgradeFile($tempFile, $oldFile) { if (empty(trim($tempFile)) || empty(trim($oldFile))) { return false; } // 目录不存在就新建 if (!is_dir($oldFile)) { mkdir($oldFile, 0777, true); } foreach (glob($tempFile . '*') as $fileName) { // 要处理的是目录时,递归处理文件目录。 if (is_dir($fileName)) { self::upgradeFile($fileName . '/', $oldFile . basename($fileName) . '/'); } // 要处理的是文件时,判断是否存在 或者 与原来文件不一致 则覆盖 if (is_file($fileName)) { if (!file_exists($oldFile . basename($fileName)) || md5(file_get_contents($fileName)) != md5(file_get_contents($oldFile . basename($fileName))) ) { copy($fileName, $oldFile . basename($fileName)); } } } return true; } }