0, 'lists' => []]; } $local_data = local_version(); $local_version = $local_data['version']; foreach ($lists as $k => $item) { $lists[$k]['version_str'] = ''; $lists[$k]['able_update'] = 0; if ($local_version == $item['version_no']) { $lists[$k]['version_str'] = '您的系统当前处于此版本'; } if ($local_version < $item['version_no']) { $lists[$k]['version_str'] = '系统可更新至此版本'; $lists[$k]['able_update'] = 1; } //最新的版本号标志 $lists[$k]['new_version'] = 0; $lists[0]['new_version'] = 1; //注意,是否需要重新发布描述 $lists[$k]['notice'] = ''; if ($item['is_republish'] == 1) { $lists[$k]['notice'] = '更新至当前版本后需重新发布前端商城'; } //处理更新内容信息 $contents = $item['update_content']; $add = []; $optimize = []; $repair = []; 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']; } } } $lists[$k]['add'] = $add; $lists[$k]['optimize'] = $optimize; $lists[$k]['repair'] = $repair; unset($lists[$k]['update_content']); } return ['count' => count($lists), 'lists' => $lists]; } /** * Notes: 获取远程数据 * @param string $page_no * @param string $page_size * @author 竹 * @return mixed */ public static function getRemoteVersion($page_no = '', $page_size = '') { $cache_version = Cache::get('version_lists'); if (!empty($cache_version)) { return $cache_version; } if (empty($page_no) || empty($page_size)) { $remote_url = self::$base_url."/api/version/lists?type=2"; } else { $remote_url = self::$base_url."/api/version/lists?type=2&page_no=$page_no&page_size=$page_size"; } $result = Requests::get($remote_url); $result = json_decode($result->body, true); $result = $result['data'] ?? []; Cache::set('version_lists', $result, 1800); return $result; } /** * Notes: 更新主程序 * @param $version * @return bool|string * @author 竹 */ public static function upgrade($post) { $post['update_type'] = 1; Db::startTrans(); try { //修改旧数据 self::handleOldData(); //要更新的版本 $version = $post['version_no']; //远程下载链接 $remote_url = $post['package_link']; // 本地更新路径 $local_upgrade_dir = ROOT_PATH . '/upgrade/'; // 本地更新包路径 $path = ROOT_PATH . '/upgrade/'; // 本地更新临时文件 $temp_dir = $path . 'temp/'; if (!is_dir($path)) { mkdir(iconv("UTF-8", "GBK", $path), 0777, true); } //下载更新压缩包保存到本地 $remote_data = self::downFile($remote_url, $local_upgrade_dir); if (false === $remote_data) { throw new Exception('获取文件错误'); } //解压缩 if (false === unzip($remote_data['save_path'], $temp_dir)) { throw new Exception('解压文件错误'); } //更新sql if (false === self::upgradeSql($temp_dir . 'sql/')) { throw new Exception('更新数据库失败'); } //更新文件 if (false === self::upgradeDir($temp_dir . 'project/', self::getProjectPath())) { throw new Exception('更新文件失败'); } //更新本地版本号文件 $upgrade_data = ['version' => $version]; if (false === self::upgradeVersion($upgrade_data)) { throw new Exception('本地更新日志写入失败'); } //删除临时文件(压缩包不删除,删除解压的文件) if (false === del_target_dir($temp_dir, true)) { Log::write('删除临时文件失败'); } Db::commit(); //增加日志 self::addlog($post); return true; } catch (Exception $e) { Db::rollback(); //错误日志 $post['error'] = $e->getMessage(); self::addlog($post, false); //删除临时文件(压缩包不删除,删除解压的文件) if (false === del_target_dir($temp_dir, true)) { Log::write('删除临时文件失败'); } return $e->getMessage(); } } /** * Notes: 添加日志 * @param $data * @param bool $status * @author 竹 * @return bool|\Requests_Response */ public static function addlog($data, $status = true) { $log_msg = json_encode($data, JSON_UNESCAPED_UNICODE); Log::write('更新日志:'.$log_msg); try{ $data = [ 'version_id' => $data['id'], 'version_no' => $data['version_no'], 'ip_address' => get_client_ip(), 'domain' => request()->domain(), 'type' => 2, 'update_type' => $data['update_type'], 'status' => $status ? 1 : 0, 'error' => $data['error'] ?? '未知错误' ]; $request_url = self::$base_url.'/api/version/log'; return Requests::post($request_url, [], $data); } catch(\Exception $e) { Log::write('更新日志添加失败:'.$e->getMessage()); return false; } } /** * Notes: 下载压缩包 * @param $url * @param string $savePath * @return array|bool * @author 竹 */ 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); //需要response header curl_setopt($ch, CURLOPT_NOBODY, FALSE); //需要response body 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); //头信息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 竹 */ public static function getProjectPath() { $path = dirname(ROOT_PATH); $path = trim(str_replace('server', '', $path)); return $path; } /** * Notes: 执行指定文件夹内的sql文件 * @param $dir * @return bool * @author 竹 */ public static function upgradeSql($dir) { //遍历指定目录下的指定后缀文件 $sql_files = get_scandir($dir, '', 'sql'); if (false === $sql_files) { return false; } //当前数据库前缀 $sql_prefix = config('database.prefix'); //遍历更新sql foreach ($sql_files as $k => $sql_file) { if (get_extension($sql_file) != 'sql') { continue; } $sql_content = file_get_contents($dir . $sql_file); if (empty($sql_content)) { continue; } $sqls = explode(';', $sql_content); //执行sql foreach ($sqls as $sql) { if (!empty($sql)) { $sql = str_replace('`ls_', '`' . $sql_prefix, $sql); Db::execute($sql . ';'); } } } return true; } /** * Notes: 更新文件 * @param $temp_file //临时更新文件路径 (新的更新文件) * @param $old_file //需要更新的文件路囧 (旧的文件) * @author 竹 * @return array|bool */ public static function upgradeDir($temp_file, $old_file) { if (!file_exists($temp_file)) { return []; } if (empty(trim($temp_file)) || empty(trim($old_file))) { return false; } // 目录不存在就新建 if (!is_dir($old_file)) { mkdir($old_file, 0777, true); } foreach (glob($temp_file . '*') as $file_name) { // 要处理的是目录时,递归处理文件目录。 if (is_dir($file_name)) { self::upgradeDir($file_name . '/', $old_file . basename($file_name) . '/'); } // 要处理的是文件时,判断是否存在 或者 与原来文件不一致 则覆盖 if (is_file($file_name)) { if (!file_exists($old_file . basename($file_name)) || md5(file_get_contents($file_name)) != md5(file_get_contents($old_file . basename($file_name))) ) { copy($file_name, $old_file . basename($file_name)); } } } return true; } /** * Notes: 更新本地版本号 * @param $data * @return bool * @author 竹 */ public static function upgradeVersion($data) { $version = './upgrade/version.json'; $res = file_put_contents($version, json_encode($data, JSON_UNESCAPED_UNICODE)); if (empty($res)) { return false; } return true; } /** * Notes: 修改旧数据 * @author 竹 * @throws Exception * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\ModelNotFoundException * @throws \think\exception\DbException * @throws \think\exception\PDOException */ public static function handleOldData() { try { //修改旧活动专区错误 $temp = []; $count = 0; $lists = Db::name('activity_goods')->where(['del' => 0])->select(); foreach ($lists as $k => $v) { $temp_key = $v['goods_id'].'_'.$v['activity_id']; if (in_array($temp_key, $temp)) { Db::name('activity_goods') ->where(['del' => 0, 'id' => $v['id']]) ->update(['del' => 1]); $count += 1; } else { $temp[] = $temp_key; } } if ($count > 0) { Log::write('处理活动专区错误,删除'.$count.'数据'); } // 更新file_cate表中type=null值的旧记录 $handle_file = Db::name('file_cate')->whereNull('type')->update(['type' => 1]); if ($handle_file) { Log::write('更新file_cate表中type=null值的旧记录'); } } catch (\Exception $e) { Log::write('修改旧数据错误:'.$e->getMessage()); } } }