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(); } }