更新
This commit is contained in:
@@ -740,7 +740,9 @@ class WecomPromotionLogic
|
||||
'agent_id' => $agentId,
|
||||
'secret_configured' => trim((string) config('qywx_customer_acquisition.secret', '')) !== '',
|
||||
'callback_ready' => $callbackTokenConfigured && $callbackAesConfigured,
|
||||
'callback_url' => rtrim($domain, '/') . '/api/qywx/external-contact/notify',
|
||||
// 复用自建应用现有的「API 接收消息」入口;获客助手事件与其他应用事件
|
||||
// 由同一个控制器按 Event/ChangeType 分发,不需要再配置第二个回调地址。
|
||||
'callback_url' => rtrim($domain, '/') . '/api/QywxExternalContactCallback/notify',
|
||||
'official_doc' => 'https://developer.work.weixin.qq.com/document/path/97297',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -27,7 +27,9 @@ class DesktopWorkstationLogic extends BaseLogic
|
||||
{
|
||||
public const CONFIG_TYPE = 'desktop_workstation';
|
||||
|
||||
public const PLATFORMS = ['windows_x64', 'macos_arm64', 'macos_x64'];
|
||||
public const PLATFORMS = ['windows_x64', 'macos_arm64', 'macos_x64'];
|
||||
|
||||
public const PACKAGE_TYPES = ['archive', 'inno_setup'];
|
||||
|
||||
/**
|
||||
* @notes 管理端读取配置(安装包地址补全域名)
|
||||
@@ -76,10 +78,10 @@ class DesktopWorkstationLogic extends BaseLogic
|
||||
$latest = self::normalizeVersion((string) ($config['latest_version'] ?? ''));
|
||||
$minVersion = self::normalizeVersion((string) ($config['min_version'] ?? ''));
|
||||
$enabled = (int) ($config['enabled'] ?? 0) === 1;
|
||||
$packageKey = self::packageKey($platform, $arch);
|
||||
$package = $packageKey !== ''
|
||||
? self::plainPackage($config['packages'][$packageKey] ?? [])
|
||||
: self::emptyPackage();
|
||||
$packageKey = self::packageKey($platform, $arch);
|
||||
$package = $packageKey !== ''
|
||||
? self::plainPackage($config['packages'][$packageKey] ?? [], $packageKey)
|
||||
: self::emptyPackage();
|
||||
$canInstall = $package['url'] !== '' && $package['sha256'] !== '';
|
||||
$hasUpdate = $enabled && $latest !== '' && $current !== '' && self::compareVersion($current, $latest) < 0;
|
||||
$belowMin = $minVersion !== '' && $current !== '' && self::compareVersion($current, $minVersion) < 0;
|
||||
@@ -186,8 +188,9 @@ class DesktopWorkstationLogic extends BaseLogic
|
||||
$packages[$key] = [
|
||||
'url' => (string) ($params[$key . '_url'] ?? ''),
|
||||
'sha256' => (string) ($params[$key . '_sha256'] ?? ''),
|
||||
'size' => $params[$key . '_size'] ?? 0,
|
||||
'filename' => (string) ($params[$key . '_filename'] ?? ''),
|
||||
'size' => $params[$key . '_size'] ?? 0,
|
||||
'filename' => (string) ($params[$key . '_filename'] ?? ''),
|
||||
'type' => (string) ($params[$key . '_type'] ?? 'archive'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -210,7 +213,7 @@ class DesktopWorkstationLogic extends BaseLogic
|
||||
{
|
||||
$packages = [];
|
||||
foreach (self::PLATFORMS as $key) {
|
||||
$packages[$key] = self::publicPackage($config['packages'][$key] ?? []);
|
||||
$packages[$key] = self::publicPackage($config['packages'][$key] ?? [], $key);
|
||||
}
|
||||
$config['packages'] = $packages;
|
||||
return $config;
|
||||
@@ -230,8 +233,9 @@ class DesktopWorkstationLogic extends BaseLogic
|
||||
$url = FileService::setFileUrl($url);
|
||||
}
|
||||
$sha256 = strtolower(trim((string) ($row['sha256'] ?? '')));
|
||||
$filename = trim((string) ($row['filename'] ?? ''));
|
||||
$size = (int) ($row['size'] ?? 0);
|
||||
$filename = trim((string) ($row['filename'] ?? ''));
|
||||
$size = (int) ($row['size'] ?? 0);
|
||||
$type = self::normalizePackageType((string) ($row['type'] ?? ''), $key);
|
||||
if ($persist) {
|
||||
$filled = self::fillLocalPackageMeta($url, $sha256, $size, $filename);
|
||||
$url = $filled['url'];
|
||||
@@ -242,8 +246,9 @@ class DesktopWorkstationLogic extends BaseLogic
|
||||
$normalized[$key] = [
|
||||
'url' => $url,
|
||||
'sha256' => $sha256,
|
||||
'size' => max(0, $size),
|
||||
'filename' => mb_substr($filename, 0, 180),
|
||||
'size' => max(0, $size),
|
||||
'filename' => mb_substr($filename, 0, 180),
|
||||
'type' => $type,
|
||||
];
|
||||
}
|
||||
return $normalized;
|
||||
@@ -251,36 +256,52 @@ class DesktopWorkstationLogic extends BaseLogic
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
* @return array{url:string,sha256:string,size:int,filename:string}
|
||||
*/
|
||||
private static function publicPackage(array $row): array
|
||||
{
|
||||
$plain = self::plainPackage($row);
|
||||
* @return array{url:string,sha256:string,size:int,filename:string,type:string}
|
||||
*/
|
||||
private static function publicPackage(array $row, string $key): array
|
||||
{
|
||||
$plain = self::plainPackage($row, $key);
|
||||
$plain['url'] = $plain['url'] === '' ? '' : FileService::getFileUrl($plain['url']);
|
||||
return $plain;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
* @return array{url:string,sha256:string,size:int,filename:string}
|
||||
*/
|
||||
private static function plainPackage(array $row): array
|
||||
{
|
||||
return [
|
||||
* @return array{url:string,sha256:string,size:int,filename:string,type:string}
|
||||
*/
|
||||
private static function plainPackage(array $row, string $key = ''): array
|
||||
{
|
||||
return [
|
||||
'url' => trim((string) ($row['url'] ?? '')),
|
||||
'sha256' => strtolower(trim((string) ($row['sha256'] ?? ''))),
|
||||
'size' => max(0, (int) ($row['size'] ?? 0)),
|
||||
'filename' => (string) ($row['filename'] ?? ''),
|
||||
'size' => max(0, (int) ($row['size'] ?? 0)),
|
||||
'filename' => (string) ($row['filename'] ?? ''),
|
||||
'type' => self::normalizePackageType((string) ($row['type'] ?? ''), $key),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{url:string,sha256:string,size:int,filename:string}
|
||||
*/
|
||||
private static function emptyPackage(): array
|
||||
{
|
||||
return ['url' => '', 'sha256' => '', 'size' => 0, 'filename' => ''];
|
||||
}
|
||||
* @return array{url:string,sha256:string,size:int,filename:string,type:string}
|
||||
*/
|
||||
private static function emptyPackage(): array
|
||||
{
|
||||
return ['url' => '', 'sha256' => '', 'size' => 0, 'filename' => '', 'type' => 'archive'];
|
||||
}
|
||||
|
||||
public static function normalizePackageType(string $type, string $key = ''): string
|
||||
{
|
||||
$value = strtolower(trim($type));
|
||||
if ($value === '') {
|
||||
return 'archive';
|
||||
}
|
||||
if (!in_array($value, self::PACKAGE_TYPES, true)) {
|
||||
return 'archive';
|
||||
}
|
||||
if ($value === 'inno_setup' && $key !== '' && $key !== 'windows_x64') {
|
||||
return 'archive';
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{url:string,sha256:string,size:int,filename:string}
|
||||
|
||||
@@ -120,14 +120,27 @@ class DesktopWorkstationValidate extends BaseValidate
|
||||
unset($data);
|
||||
$url = trim((string) ($row['url'] ?? ''));
|
||||
$sha256 = strtolower(trim((string) ($row['sha256'] ?? '')));
|
||||
$filename = trim((string) ($row['filename'] ?? ''));
|
||||
$size = $row['size'] ?? 0;
|
||||
$filename = trim((string) ($row['filename'] ?? ''));
|
||||
$type = strtolower(trim((string) ($row['type'] ?? 'archive')));
|
||||
$size = $row['size'] ?? 0;
|
||||
$labels = [
|
||||
'windows_x64' => 'Windows 64 位',
|
||||
'macos_arm64' => 'macOS Apple 芯片',
|
||||
'macos_x64' => 'macOS Intel',
|
||||
];
|
||||
$label = $labels[$key] ?? $key;
|
||||
$label = $labels[$key] ?? $key;
|
||||
if (!in_array($type, DesktopWorkstationLogic::PACKAGE_TYPES, true)) {
|
||||
return $label . '安装包类型不受支持';
|
||||
}
|
||||
if ($type === 'inno_setup' && $key !== 'windows_x64') {
|
||||
return $label . '不能使用 Windows Inno Setup 安装包';
|
||||
}
|
||||
if ($type === 'inno_setup' && $filename !== '' && !str_ends_with(strtolower($filename), '.exe')) {
|
||||
return $label . ' Inno Setup 文件名必须以 .exe 结尾';
|
||||
}
|
||||
if ($type === 'inno_setup' && preg_match('#^http://#i', $url)) {
|
||||
return $label . '自动安装程序必须使用 HTTPS 下载地址';
|
||||
}
|
||||
if ($url !== '' && !$this->isAllowedPackageUrl($url)) {
|
||||
return $label . '安装包地址必须是 http(s) 链接或站内 uploads 路径';
|
||||
}
|
||||
|
||||
@@ -40,11 +40,12 @@ $config = [
|
||||
'title' => '医生工作站 0.2.0',
|
||||
'notes' => '稳定性更新',
|
||||
'packages' => [
|
||||
'windows_x64' => [
|
||||
'url' => 'https://cdn.example.com/DoctorWorkstation-Windows-x64-0.2.0.zip',
|
||||
'sha256' => $sha,
|
||||
'size' => 123,
|
||||
'filename' => 'DoctorWorkstation-Windows-x64-0.2.0.zip',
|
||||
'windows_x64' => [
|
||||
'url' => 'https://cdn.example.com/DoctorWorkstation-Setup-Windows-x64-0.2.0.exe',
|
||||
'sha256' => $sha,
|
||||
'size' => 123,
|
||||
'filename' => 'DoctorWorkstation-Setup-Windows-x64-0.2.0.exe',
|
||||
'type' => 'inno_setup',
|
||||
],
|
||||
],
|
||||
];
|
||||
@@ -53,10 +54,12 @@ $optional = DesktopWorkstationLogic::evaluate($config, '0.1.8', 'windows', 'x64'
|
||||
desktopUpdateExpect($optional['has_update'] === true, 'newer published version is an update');
|
||||
desktopUpdateExpect($optional['force'] === false, 'force stays off when above min version');
|
||||
desktopUpdateExpect($optional['can_install'] === true, 'hashed package can be installed');
|
||||
desktopUpdateExpect(
|
||||
is_array($optional['package']) && $optional['package']['sha256'] === $sha,
|
||||
'matching windows package is returned'
|
||||
);
|
||||
desktopUpdateExpect(
|
||||
is_array($optional['package'])
|
||||
&& $optional['package']['sha256'] === $sha
|
||||
&& $optional['package']['type'] === 'inno_setup',
|
||||
'matching Windows installer package is returned with its explicit type'
|
||||
);
|
||||
|
||||
$forcedByMin = DesktopWorkstationLogic::evaluate($config, '0.1.0', 'windows', 'x64');
|
||||
desktopUpdateExpect($forcedByMin['force'] === true, 'below min version forces upgrade');
|
||||
@@ -90,10 +93,11 @@ desktopUpdateExpect(
|
||||
$adminView = file_get_contents(dirname(__DIR__, 2) . '/admin/src/views/setting/desktop_workstation/index.vue');
|
||||
desktopUpdateExpect(is_string($adminView), 'admin view source is readable');
|
||||
desktopUpdateExpect(
|
||||
str_contains($adminView, 'setting.desktop_workstation/setConfig')
|
||||
&& str_contains($adminView, 'force_update'),
|
||||
'admin page can save force-update configuration'
|
||||
);
|
||||
str_contains($adminView, 'setting.desktop_workstation/setConfig')
|
||||
&& str_contains($adminView, 'force_update')
|
||||
&& str_contains($adminView, 'inno_setup'),
|
||||
'admin page can save force-update and Inno Setup configuration'
|
||||
);
|
||||
|
||||
$migration = file_get_contents(
|
||||
dirname(__DIR__) . '/sql/1.9.20260821/add_desktop_workstation_update_menu.sql'
|
||||
|
||||
Reference in New Issue
Block a user