erp-backend/app/Services/PrintPluginService.php
2026-04-01 17:07:04 +08:00

403 lines
12 KiB
PHP

<?php
namespace App\Services;
use App\Models\PrintPlugin;
use App\Models\PrintPluginInstallation;
use App\Models\PrintPluginLog;
use App\Models\PrintJob;
use App\Models\User;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
class PrintPluginService
{
/**
* 获取所有插件列表
*/
public function getPlugins(?string $platform = null): array
{
$query = PrintPlugin::where('status', '!=', 'inactive');
if ($platform) {
$query->where('platform', $platform);
}
$plugins = $query->get()->map(function ($plugin) {
return $this->formatPluginInfo($plugin);
});
return $plugins->toArray();
}
/**
* 获取插件详情
*/
public function getPlugin(string $code): ?array
{
$plugin = PrintPlugin::where('code', $code)->first();
if (!$plugin) {
return null;
}
return $this->formatPluginInfo($plugin, true);
}
/**
* 获取插件最新版本信息
*/
public function getLatestVersion(string $code): ?array
{
$plugin = PrintPlugin::where('code', $code)->first();
if (!$plugin) {
return null;
}
return [
'code' => $plugin->code,
'name' => $plugin->name,
'version' => $plugin->version,
'download_url' => $plugin->download_url,
'md5' => $plugin->md5,
'file_size' => $plugin->file_size,
'changelog' => $plugin->changelog,
'min_erp_version' => $plugin->min_erp_version,
];
}
/**
* 下载插件安装包
*/
public function downloadPlugin(string $code): ?string
{
$plugin = PrintPlugin::where('code', $code)->first();
if (!$plugin) {
return null;
}
// 本地存储路径
$storagePath = storage_path("app/plugins/{$code}/");
$fileName = "{$plugin->version}.{$plugin->install_type}";
$fullPath = $storagePath . $fileName;
// 如果已存在直接返回
if (file_exists($fullPath)) {
return $fullPath;
}
// 创建目录
if (!is_dir($storagePath)) {
mkdir($storagePath, 0755, true);
}
try {
$response = Http::timeout(60)->sink($fullPath)->get($plugin->download_url);
if ($response->successful()) {
// 验证MD5
if ($plugin->md5 && md5_file($fullPath) !== $plugin->md5) {
unlink($fullPath);
Log::error("Plugin download MD5 mismatch: {$code}");
return null;
}
return $fullPath;
}
} catch (\Exception $e) {
Log::error("Plugin download failed: {$e->getMessage()}");
}
return null;
}
/**
* 检查用户设备上的插件版本
*/
public function checkUserPluginVersion(int $userId, string $pluginCode, string $deviceId): ?array
{
$installation = PrintPluginInstallation::where('user_id', $userId)
->where('device_id', $deviceId)
->whereHas('plugin', function ($q) use ($pluginCode) {
$q->where('code', $pluginCode);
})
->with('plugin')
->first();
if (!$installation) {
return null;
}
$plugin = $installation->plugin;
$hasUpdate = $plugin->hasNewVersion($installation->installed_version);
return [
'installed' => true,
'version' => $installation->installed_version,
'has_update' => $hasUpdate,
'latest_version' => $hasUpdate ? $plugin->version : null,
'device_id' => $installation->device_id,
'device_name' => $installation->device_name,
'is_online' => $installation->isOnline(),
'last_heartbeat' => $installation->last_heartbeat?->toIso8601String(),
];
}
/**
* 获取用户已安装的插件
*/
public function getUserInstallations(int $userId): array
{
$installations = PrintPluginInstallation::where('user_id', $userId)
->with('plugin')
->get();
return $installations->map(function ($installation) {
$plugin = $installation->plugin;
return [
'id' => $installation->id,
'plugin_code' => $plugin->code,
'plugin_name' => $plugin->name,
'platform' => $plugin->platform,
'installed_version' => $installation->installed_version,
'latest_version' => $plugin->version,
'has_update' => $plugin->hasNewVersion($installation->installed_version),
'device_id' => $installation->device_id,
'device_name' => $installation->device_name,
'install_status' => $installation->install_status,
'is_online' => $installation->isOnline(),
'last_heartbeat' => $installation->last_heartbeat?->toIso8601String(),
'installed_at' => $installation->installed_at?->toIso8601String(),
];
})->toArray();
}
/**
* 注册插件安装
*/
public function registerInstallation(
int $userId,
string $pluginCode,
string $version,
string $deviceId,
?string $deviceName = null,
?string $osVersion = null
): array {
$plugin = PrintPlugin::where('code', $pluginCode)->first();
if (!$plugin) {
return ['success' => false, 'message' => '插件不存在'];
}
$installation = PrintPluginInstallation::updateOrCreate(
[
'user_id' => $userId,
'plugin_id' => $plugin->id,
'device_id' => $deviceId,
],
[
'installed_version' => $version,
'device_name' => $deviceName,
'os_version' => $osVersion,
'install_status' => 'active',
'last_heartbeat' => now(),
'installed_at' => now(),
]
);
// 记录日志
PrintPluginLog::record(
$userId,
PrintPluginLog::TYPE_INSTALL,
"安装插件 {$plugin->name} v{$version}",
$plugin->id,
null,
['version' => $version, 'device_id' => $deviceId, 'device_name' => $deviceName]
);
return [
'success' => true,
'installation_id' => $installation->id,
'has_update' => $plugin->hasNewVersion($version),
'latest_version' => $plugin->version,
];
}
/**
* 记录心跳
*/
public function recordHeartbeat(int $userId, string $deviceId): bool
{
$installation = PrintPluginInstallation::where('user_id', $userId)
->where('device_id', $deviceId)
->first();
if ($installation) {
$installation->recordHeartbeat();
return true;
}
return false;
}
/**
* 卸载插件
*/
public function uninstall(int $userId, string $pluginCode, string $deviceId): bool
{
$installation = PrintPluginInstallation::whereHas('plugin', function ($q) use ($pluginCode) {
$q->where('code', $pluginCode);
})
->where('user_id', $userId)
->where('device_id', $deviceId)
->first();
if ($installation) {
$installation->update(['install_status' => 'uninstalled']);
PrintPluginLog::record(
$userId,
PrintPluginLog::TYPE_UNINSTALL,
"卸载插件",
$installation->plugin_id,
null,
['device_id' => $deviceId]
);
return true;
}
return false;
}
/**
* 获取设备状态
*/
public function getDeviceStatus(int $userId, string $deviceId): array
{
$installations = PrintPluginInstallation::where('user_id', $userId)
->where('device_id', $deviceId)
->with('plugin')
->get();
if ($installations->isEmpty()) {
return ['online' => false, 'plugins' => []];
}
return [
'online' => $installations->contains(fn($i) => $i->isOnline()),
'plugins' => $installations->map(fn($i) => [
'code' => $i->plugin->code,
'name' => $i->plugin->name,
'is_online' => $i->isOnline(),
'last_heartbeat' => $i->last_heartbeat?->toIso8601String(),
])->toArray(),
];
}
/**
* 格式化插件信息
*/
protected function formatPluginInfo(PrintPlugin $plugin, bool $withVersions = false): array
{
$data = [
'id' => $plugin->id,
'code' => $plugin->code,
'name' => $plugin->name,
'platform' => $plugin->platform,
'platform_name' => PrintPlugin::getPlatformName($plugin->platform),
'version' => $plugin->version,
'download_url' => $plugin->download_url,
'md5' => $plugin->md5,
'file_size' => $plugin->file_size,
'install_type' => $plugin->install_type,
'min_erp_version' => $plugin->min_erp_version,
'status' => $plugin->status,
'published_at' => $plugin->published_at?->toIso8601String(),
];
if ($withVersions) {
$data['changelog'] = $plugin->changelog;
$data['config'] = $plugin->config;
$data['versions'] = $plugin->versions()->orderByDesc('published_at')->take(10)->get()->map(fn($v) => [
'version' => $v->version,
'changelog' => $v->changelog,
'published_at' => $v->published_at?->toIso8601String(),
])->toArray();
}
return $data;
}
/**
* 检测插件更新(定时任务调用)
*/
public function checkUpdates(string $pluginCode): ?array
{
$plugin = PrintPlugin::where('code', $pluginCode)->first();
if (!$plugin || empty($plugin->config['update_check_url'])) {
return null;
}
try {
$response = Http::timeout(30)->get($plugin->config['update_check_url']);
if ($response->successful()) {
$data = $response->json();
$latestVersion = $data['version'] ?? null;
if ($latestVersion && version_compare($latestVersion, $plugin->version) > 0) {
// 有新版本
$plugin->update([
'version' => $latestVersion,
'download_url' => $data['download_url'] ?? $plugin->download_url,
'md5' => $data['md5'] ?? $plugin->md5,
'changelog' => $data['changelog'] ?? $plugin->changelog,
'file_size' => $data['file_size'] ?? $plugin->file_size,
'status' => 'active',
]);
return [
'code' => $plugin->code,
'old_version' => $plugin->getOriginal('version'),
'new_version' => $latestVersion,
'changelog' => $data['changelog'] ?? '',
];
}
}
} catch (\Exception $e) {
Log::error("Check plugin update failed: {$e->getMessage()}");
}
return null;
}
/**
* 获取需要通知更新的用户
*/
public function getUsersNeedingUpdate(string $pluginCode): array
{
$plugin = PrintPlugin::where('code', $pluginCode)->first();
if (!$plugin) {
return [];
}
return PrintPluginInstallation::where('plugin_id', $plugin->id)
->where('install_status', 'active')
->whereColumn('installed_version', '!=', 'installed_version') // 需要简化
->get()
->filter(fn($i) => version_compare($i->installed_version, $plugin->version) < 0)
->map(fn($i) => [
'user_id' => $i->user_id,
'device_id' => $i->device_id,
'current_version' => $i->installed_version,
'latest_version' => $plugin->version,
])
->toArray();
}
}