349 lines
12 KiB
PHP
349 lines
12 KiB
PHP
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | YwxApp [ WE CAN DO IT JUST THINK ]
|
|
// +----------------------------------------------------------------------
|
|
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
|
|
// +----------------------------------------------------------------------
|
|
// | Author: ywxapp<admin@ywxapp.cn>
|
|
// +----------------------------------------------------------------------
|
|
declare (strict_types = 1);
|
|
|
|
namespace addon\wxchat\middleware;
|
|
|
|
use addon\wxchat\model\WxchatAppVersion as AppVersion;
|
|
use think\facade\Log;
|
|
use think\facade\Request;
|
|
use think\Response;
|
|
|
|
/**
|
|
* AppVersionCheck 类
|
|
*
|
|
* @author ywxapp <admin@ywxapp.cn>
|
|
*/
|
|
class AppVersionCheck
|
|
{
|
|
|
|
/**
|
|
* 处理请求
|
|
*
|
|
* @param \think\Request $request
|
|
* @param \Closure $next
|
|
* @return Response
|
|
*/
|
|
// 需要跳过的路由(不需要版本检查的接口)
|
|
protected array $except = [
|
|
'app/version/check',
|
|
'app/version/latest',
|
|
'api/auth/login',
|
|
'api/auth/register',
|
|
'api/public/*',
|
|
];
|
|
|
|
// 需要强制版本检查的路由(版本过低直接拒绝)
|
|
protected array $forceCheck = [
|
|
'api/user/*',
|
|
'api/order/*',
|
|
'api/payment/*',
|
|
'api/wallet/*',
|
|
];
|
|
|
|
// 最低支持的版本号(硬性要求)
|
|
protected array $minVersions = [
|
|
'android' => 100,
|
|
'ios' => 100,
|
|
'harmony' => 100,
|
|
];
|
|
/**
|
|
* 版本检查结果
|
|
*/
|
|
protected array $result = [
|
|
'need_update' => false, // 是否需要更新
|
|
'is_force_update' => false, // 是否强制更新
|
|
'is_compatible' => true, // 是否兼容
|
|
'update_type' => 'none', // 更新类型: none/optional/recommended/required
|
|
'current_version' => null, // 当前版本信息
|
|
'latest_version' => null, // 最新版本信息
|
|
'min_support_version' => 0, // 最低支持版本
|
|
'version_diff' => 0, // 版本差异
|
|
'message' => '', // 提示信息
|
|
'download_url' => '', // 下载地址
|
|
'update_content' => '', // 更新内容
|
|
'size' => '', // 安装包大小
|
|
];
|
|
|
|
|
|
public function handle($request, \Closure $next)
|
|
{
|
|
// 获取Header信息
|
|
$platform = Request::header('App-Platform', Request::header('X-App-Platform', ''));
|
|
$versionCode = (int) Request::header('App-Version-Code', Request::header('X-App-Version-Code', 0));
|
|
$versionName = Request::header('App-Version-Name', Request::header('X-App-Version-Name', ''));
|
|
$channel = Request::header('App-Channel', Request::header('X-App-Channel', ''));
|
|
// 记录请求信息(用于日志分析)
|
|
// $this->logRequestInfo($platform, $versionCode, $versionName, $channel);
|
|
// 检查是否需要跳过版本检查
|
|
// if ($this->shouldExcept()) {
|
|
// return $next($request);
|
|
// }
|
|
// 验证Header必填字段
|
|
if (empty($platform) || $versionCode <= 0) {
|
|
// 如果是强制检查的接口,直接拒绝
|
|
if ($this->isForceCheck()) {
|
|
return $this->errorResponse(
|
|
400,
|
|
'缺少APP版本信息,请升级到最新版本',
|
|
['required_headers' => ['App-Platform', 'App-Version-Code']]
|
|
);
|
|
}
|
|
// 非强制检查,记录警告但继续
|
|
Log::warning('请求缺少版本信息', [
|
|
'path' => Request::url(),
|
|
'ip' => Request::ip(),
|
|
]);
|
|
return $next($request);
|
|
}
|
|
// 验证平台合法性
|
|
if (! in_array($platform, ['android', 'ios', 'harmony', 'web'])) {
|
|
return $this->errorResponse(400, '无效的平台类型');
|
|
}
|
|
// 检查最低版本要求
|
|
if ($versionCode < ($this->minVersions[$platform] ?? 0)) {
|
|
return $this->errorResponse(
|
|
426, // Upgrade Required
|
|
"当前版本过低,请升级到最新版本",
|
|
[
|
|
'min_version_code' => $this->minVersions[$platform],
|
|
'current_version_code' => $versionCode,
|
|
'platform' => $platform,
|
|
]
|
|
);
|
|
}
|
|
// 执行版本检查逻辑
|
|
$checkResult = $this->checkVersion($platform, $versionCode);
|
|
// 如果版本不兼容且是强制检查接口,拒绝访问
|
|
if (! $checkResult['is_compatible'] && $this->isForceCheck()) {
|
|
return $this->errorResponse(
|
|
426,
|
|
$checkResult['message'] ?? '版本不兼容,请升级到最新版本',
|
|
[
|
|
'need_update' => true,
|
|
'is_force_update' => true,
|
|
'latest_version' => $checkResult['latest_version'],
|
|
'download_url' => $checkResult['download_url'] ?? '',
|
|
]
|
|
);
|
|
}
|
|
// 将版本信息存入Request,供后续控制器使用
|
|
$request->appPlatform = $platform;
|
|
$request->appVersionCode = $versionCode;
|
|
$request->appVersionName = $versionName;
|
|
$request->appChannel = $channel;
|
|
// 继续执行
|
|
$response = $next($request);
|
|
// 在响应Header中返回版本检查结果(可选)
|
|
if ($checkResult['need_update']) {
|
|
$response->header([
|
|
'X-App-Need-Update' => 'true',
|
|
'X-App-Update-Type' => $checkResult['update_type'] ?? 'optional',
|
|
'X-App-Latest-Version' => $checkResult['latest_version']['version_name'] ?? '',
|
|
]);
|
|
}
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* 检查版本
|
|
* @param string $platform 平台: android/ios/harmony
|
|
* @param int $currentVersionCode 当前版本号
|
|
* @return array
|
|
* @throws Exception
|
|
*/
|
|
public function checkVersion(string $platform, int $currentVersionCode): array
|
|
{
|
|
// 验证平台
|
|
$validPlatforms = ['android', 'ios', 'harmony'];
|
|
if (! in_array($platform, $validPlatforms)) {
|
|
throw new Exception('无效的平台类型');
|
|
}
|
|
|
|
// 获取最新版本
|
|
$latestVersion = AppVersion::getLatestVersion($platform);
|
|
if (! $latestVersion) {
|
|
throw new Exception('未找到该平台的版本信息');
|
|
}
|
|
|
|
// 获取当前版本信息
|
|
$currentVersion = AppVersion::getVersion($platform, $currentVersionCode);
|
|
|
|
// 设置最新版本信息
|
|
$this->result['latest_version'] = [
|
|
'version_code' => $latestVersion->version_code,
|
|
'version_name' => $latestVersion->version_name,
|
|
'release_time' => $latestVersion->release_time,
|
|
];
|
|
|
|
// 设置当前版本信息
|
|
$this->result['current_version'] = $currentVersion ? [
|
|
'version_code' => $currentVersion->version_code,
|
|
'version_name' => $currentVersion->version_name,
|
|
] : null;
|
|
|
|
// 设置最低支持版本
|
|
$this->result['min_support_version'] = $latestVersion->min_support_version;
|
|
|
|
// 计算版本差异
|
|
$this->result['version_diff'] = $latestVersion->version_code - $currentVersionCode;
|
|
|
|
// 判断是否需要更新
|
|
if ($currentVersionCode < $latestVersion->version_code) {
|
|
$this->result['need_update'] = true;
|
|
$this->result['is_force_update'] = (bool) $latestVersion->is_force_update;
|
|
$this->result['is_compatible'] = (bool) $latestVersion->is_compatible;
|
|
$this->result['update_type'] = $latestVersion->update_type;
|
|
$this->result['download_url'] = $latestVersion->download_url;
|
|
$this->result['update_content'] = $latestVersion->update_content;
|
|
$this->result['size'] = $latestVersion->size;
|
|
|
|
// 生成提示信息
|
|
$this->generateMessage($latestVersion, $currentVersionCode);
|
|
} else {
|
|
$this->result['message'] = '当前已是最新版本';
|
|
}
|
|
|
|
// 检查是否低于最低支持版本
|
|
if ($currentVersionCode < $latestVersion->min_support_version) {
|
|
$this->result['need_update'] = true;
|
|
$this->result['is_force_update'] = true;
|
|
$this->result['is_compatible'] = false;
|
|
$this->result['update_type'] = 'required';
|
|
$this->result['message'] = '当前版本过低,必须升级到最新版本才能继续使用';
|
|
}
|
|
|
|
return $this->result;
|
|
}
|
|
|
|
/**
|
|
* 判断是否需要跳过版本检查
|
|
*/
|
|
protected function shouldExcept(): bool
|
|
{
|
|
$path = Request::path();
|
|
foreach ($this->except as $except) {
|
|
if ($except === $path || fnmatch($except, $path)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 判断是否需要强制版本检查
|
|
*/
|
|
protected function isForceCheck(): bool
|
|
{
|
|
$path = Request::controller() . '/' . Request::action();
|
|
|
|
foreach ($this->forceCheck as $check) {
|
|
if ($check === $path || fnmatch($check, $path)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 记录请求信息
|
|
*/
|
|
protected function logRequestInfo(string $platform, int $versionCode, string $versionName, string $channel): void
|
|
{
|
|
// 只记录特定接口的日志(避免日志过多)
|
|
$logPaths = [
|
|
'api/user/*',
|
|
'api/order/*',
|
|
'api/payment/*',
|
|
];
|
|
|
|
$path = Request::path();
|
|
foreach ($logPaths as $logPath) {
|
|
if (fnmatch($logPath, $path)) {
|
|
Log::info('APP请求', [
|
|
'path' => $path,
|
|
'method' => Request::method(),
|
|
'platform' => $platform,
|
|
'version_code' => $versionCode,
|
|
'version_name' => $versionName,
|
|
'channel' => $channel,
|
|
'ip' => Request::ip(),
|
|
'user_agent' => Request::header('Member-Agent', ''),
|
|
]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 错误响应
|
|
*/
|
|
protected function errorResponse(int $code, string $message, array $data = []): Response
|
|
{
|
|
return json([
|
|
'code' => $code,
|
|
'message' => $message,
|
|
'data' => $data,
|
|
'timestamp' => time(),
|
|
], $code);
|
|
}
|
|
|
|
/**
|
|
* 生成提示信息
|
|
* @param AppVersion $latestVersion 最新版本
|
|
* @param int $currentVersionCode 当前版本号
|
|
*/
|
|
protected function generateMessage(AppVersion $latestVersion, int $currentVersionCode): void
|
|
{
|
|
$versionName = $latestVersion->version_name;
|
|
|
|
if ($latestVersion->is_force_update) {
|
|
$this->result['message'] = "发现新版本 {$versionName},请立即升级";
|
|
} elseif ($latestVersion->update_type === 'required') {
|
|
$this->result['message'] = "发现重要更新 {$versionName},建议立即升级";
|
|
} elseif ($latestVersion->update_type === 'recommended') {
|
|
$this->result['message'] = "发现新版本 {$versionName},建议升级";
|
|
} else {
|
|
$this->result['message'] = "发现新版本 {$versionName},可选择升级";
|
|
}
|
|
|
|
// 如果不兼容,添加警告
|
|
if (!$latestVersion->is_compatible) {
|
|
$this->result['message'] .= "(当前版本将无法继续使用)";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 版本号转换为版本名称
|
|
* @param int $versionCode 版本号 101
|
|
* @return string 版本名称 1.0.1
|
|
*/
|
|
public static function versionCodeToName(int $versionCode): string
|
|
{
|
|
$major = floor($versionCode / 100);
|
|
$minor = floor(($versionCode % 100) / 10);
|
|
$patch = $versionCode % 10;
|
|
return "{$major}.{$minor}.{$patch}";
|
|
}
|
|
|
|
/**
|
|
* 版本名称转换为版本号
|
|
* @param string $versionName 版本名称 1.0.1
|
|
* @return int 版本号 101
|
|
*/
|
|
public static function versionNameToCode(string $versionName): int
|
|
{
|
|
$parts = explode('.', $versionName);
|
|
$parts = array_pad($parts, 3, 0);
|
|
return (int)($parts[0] * 100 + $parts[1] * 10 + $parts[2]);
|
|
}
|
|
}
|