From 00e868664010d592bc8871db0342bc9cb1486d87 Mon Sep 17 00:00:00 2001 From: mine_ Date: Sun, 21 Sep 2025 22:34:55 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=E7=AE=80=E5=8D=95=E9=87=8D=E6=9E=84`f?= =?UTF-8?q?indApplication`=E4=B8=BA=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../program/FindApplicationsService.java | 175 ++++++++++++++++++ .../service/program/findApplications.java | 163 ---------------- 2 files changed, 175 insertions(+), 163 deletions(-) create mode 100644 src/main/java/tech/minediamond/vortex/service/program/FindApplicationsService.java delete mode 100644 src/main/java/tech/minediamond/vortex/service/program/findApplications.java diff --git a/src/main/java/tech/minediamond/vortex/service/program/FindApplicationsService.java b/src/main/java/tech/minediamond/vortex/service/program/FindApplicationsService.java new file mode 100644 index 0000000..e997ebd --- /dev/null +++ b/src/main/java/tech/minediamond/vortex/service/program/FindApplicationsService.java @@ -0,0 +1,175 @@ +/* + * Vortex + * Copyright (C) 2025 Mine-diamond + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package tech.minediamond.vortex.service.program; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.inject.Inject; +import com.google.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import tech.minediamond.vortex.model.appConfig.GlobalDataStore; +import tech.minediamond.vortex.model.program.ProgramInfo; +import tech.minediamond.vortex.model.program.ProgramSource; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; +import java.sql.SQLException; +import java.time.Duration; +import java.time.LocalTime; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Singleton +public class FindApplicationsService { + + // 用于执行搜索的 PowerShell 命令 + private static final String COMMAND = """ + chcp 65001 | Out-Null + [Console]::OutputEncoding = [System.Text.Encoding]::UTF8; + $OutputEncoding = [System.Text.Encoding]::UTF8 + + & { + (Get-ItemProperty HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*, HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*, HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\* -ErrorAction SilentlyContinue).Where({ + $_.DisplayName -and -not $_.SystemComponent + })| + Select-Object @{Name="baseName"; Expression={$_.DisplayName}}, + @{Name="programName"; Expression={ + if ($_.DisplayIcon) { + $cleanedPath = ($_.DisplayIcon -split ',')[0].Trim('"') + $fileName = Split-Path -Path $cleanedPath -Leaf + if ($fileName -like '*.exe') { + $fileName + } else { + $null + } + } else { + $null + } + }}, + @{Name="version"; Expression={$_.DisplayVersion}}, + @{Name="publisher"; Expression={$_.Publisher}}, + @{Name="installLocation"; Expression={$_.InstallLocation}}, + @{Name="source"; Expression={"REGISTRY"}} + + (Get-AppxPackage -PackageTypeFilter Main).Where({ + $_.Name -notlike 'ms-resource:*' -and $_.DisplayName -notlike 'ms-resource:*' + })| + Select-Object @{Name="baseName"; Expression={$manifest = $_ | Get-AppxPackageManifest + $cleanName = $manifest.Package.Properties.DisplayName + if ($cleanName -and $cleanName -notlike 'ms-resource:*') { + $cleanName + } else { + $_.Name + } + }}, + @{Name="programName"; Expression={"$($_.PackageFamilyName)!App"}}, + @{Name="publisher"; Expression={$_.Publisher}}, + @{Name="installLocation"; Expression={$_.InstallLocation}}, + @{Name="source"; Expression={"UWP"}} + + } | ConvertTo-Json -Compress + + """; + + @Inject + public FindApplicationsService() { + + } + + public void getInstalledPrograms() throws IOException, InterruptedException { + + LocalTime time = LocalTime.now(); + //构建命令 + ProcessBuilder pb = new ProcessBuilder( + "powershell.exe", + "-NoProfile", + "-ExecutionPolicy", "Bypass", + "-" + ); + + // 将错误流重定向到标准输出流 + pb.redirectErrorStream(true); + + Process process = pb.start(); + + // 通过 stdin 将 COMMAND 写入 PowerShell 进程 + try (OutputStreamWriter writer = new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8)) { + writer.write(COMMAND); + } + + // 现在只需要读取一个流 + String jsonOutput; + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { + jsonOutput = reader.lines().collect(Collectors.joining()); + } + + // 等待进程结束并检查退出码 + int exitCode = process.waitFor(); + if (exitCode != 0) { + throw new RuntimeException("PowerShell 命令执行失败,退出码: " + exitCode); + } + + log.debug("jsonOutput: {}", jsonOutput); + + LocalTime time2 = LocalTime.now(); + log.info("powershell执行时间:{}ms", Duration.between(time, time2).toMillis()); + + ObjectMapper mapper = new ObjectMapper(); + List programs = mapper.readValue(jsonOutput, new TypeReference>() {}); + + programs.sort(Comparator.comparing(ProgramInfo::getBaseName, Comparator.nullsLast(String::compareToIgnoreCase))); + programs.removeIf((programInfo) -> { + return (programInfo.getSource() == ProgramSource.REGISTRY && (programInfo.getProgramName() == null || programInfo.getInstallLocation() == null || programInfo.getInstallLocation().equals(""))); + }); + + for (ProgramInfo programInfo : programs) { + + programInfo.setDisplayName(programInfo.getBaseName()); + if (programInfo.getInstallLocation() != null && programInfo.getInstallLocation().equals("")) { + programInfo.setInstallLocation(null); + } + + if (programInfo.getSource() == ProgramSource.REGISTRY && !programInfo.getInstallLocation().endsWith("\\")) { + programInfo.setInstallLocation(programInfo.getInstallLocation() + "\\"); + } + } + + GlobalDataStore.programInfos.addAll(programs); + + LocalTime time3 = LocalTime.now(); + log.info("数据处理时间:{}ms", Duration.between(time2, time3).toMillis()); + + } + + public static void main(String[] args) throws IOException, InterruptedException, SQLException { + LocalTime start = LocalTime.now(); + new FindApplicationsService().getInstalledPrograms(); + GlobalDataStore.programInfos.forEach(System.out::println); + dataBaseOperate db = dataBaseOperate.getInstance(); + db.syncSoftwareList(GlobalDataStore.programInfos); + LocalTime end = LocalTime.now(); + log.info("程序运行总时间: {}ms", Duration.between(start, end).toMillis()); + } +} diff --git a/src/main/java/tech/minediamond/vortex/service/program/findApplications.java b/src/main/java/tech/minediamond/vortex/service/program/findApplications.java deleted file mode 100644 index 80f8006..0000000 --- a/src/main/java/tech/minediamond/vortex/service/program/findApplications.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Vortex - * Copyright (C) 2025 Mine-diamond - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -package tech.minediamond.vortex.service.program; - -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import tech.minediamond.vortex.model.appConfig.GlobalDataStore; -import tech.minediamond.vortex.model.program.ProgramInfo; -import tech.minediamond.vortex.model.program.ProgramSource; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.nio.charset.StandardCharsets; -import java.sql.SQLException; -import java.time.Duration; -import java.time.LocalTime; -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; - -public class findApplications { - public static void getInstalledPrograms() throws IOException, InterruptedException { - // PowerShell 命令 - String command = """ - chcp 65001 | Out-Null - [Console]::OutputEncoding = [System.Text.Encoding]::UTF8; - $OutputEncoding = [System.Text.Encoding]::UTF8 - - & { - (Get-ItemProperty HKLM:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*, HKLM:\\Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\*, HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\* -ErrorAction SilentlyContinue).Where({ - $_.DisplayName -and -not $_.SystemComponent - })| - Select-Object @{Name="baseName"; Expression={$_.DisplayName}}, - @{Name="programName"; Expression={ - if ($_.DisplayIcon) { - $cleanedPath = ($_.DisplayIcon -split ',')[0].Trim('"') - $fileName = Split-Path -Path $cleanedPath -Leaf - if ($fileName -like '*.exe') { - $fileName - } else { - $null - } - } else { - $null - } - }}, - @{Name="version"; Expression={$_.DisplayVersion}}, - @{Name="publisher"; Expression={$_.Publisher}}, - @{Name="installLocation"; Expression={$_.InstallLocation}}, - @{Name="source"; Expression={"REGISTRY"}} - - (Get-AppxPackage -PackageTypeFilter Main).Where({ - $_.Name -notlike 'ms-resource:*' -and $_.DisplayName -notlike 'ms-resource:*' - })| - Select-Object @{Name="baseName"; Expression={$manifest = $_ | Get-AppxPackageManifest - $cleanName = $manifest.Package.Properties.DisplayName - if ($cleanName -and $cleanName -notlike 'ms-resource:*') { - $cleanName - } else { - $_.Name - } - }}, - @{Name="programName"; Expression={"$($_.PackageFamilyName)!App"}}, - @{Name="publisher"; Expression={$_.Publisher}}, - @{Name="installLocation"; Expression={$_.InstallLocation}}, - @{Name="source"; Expression={"UWP"}} - - } | ConvertTo-Json -Compress - - """; - - LocalTime time = LocalTime.now(); - ProcessBuilder pb = new ProcessBuilder( - "powershell.exe", - "-NoProfile", - "-ExecutionPolicy", "Bypass", - "-" - ); - - // 将错误流重定向到标准输出流 - pb.redirectErrorStream(true); - - Process process = pb.start(); - - // 通过 stdin 将脚本写入 PowerShell 进程 - try (OutputStreamWriter writer = new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8)) { - writer.write(command); - } - - // 现在只需要读取一个流 - String jsonOutput; - try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8))) { - jsonOutput = reader.lines().collect(Collectors.joining()); - } - - // 等待进程结束并检查退出码 - int exitCode = process.waitFor(); - if (exitCode != 0) { - throw new RuntimeException("PowerShell 命令执行失败,退出码: " + exitCode); - } - - System.out.println("jsonOutput: " + jsonOutput); - - LocalTime time2 = LocalTime.now(); - System.out.println("powershell执行时间:" + Duration.between(time, time2).toMillis() + "ms"); - - ObjectMapper mapper = new ObjectMapper(); - List programs = mapper.readValue(jsonOutput,new TypeReference>(){}); - - - programs.sort(Comparator.comparing(ProgramInfo::getBaseName, Comparator.nullsLast(String::compareToIgnoreCase))); - programs.removeIf((programInfo) ->{ - return (programInfo.getSource() == ProgramSource.REGISTRY && (programInfo.getProgramName() == null || programInfo.getInstallLocation() == null || programInfo.getInstallLocation().equals(""))); - }); - for (ProgramInfo programInfo : programs) { - - programInfo.setDisplayName(programInfo.getBaseName()); - if(programInfo.getInstallLocation() != null && programInfo.getInstallLocation().equals("")){ - programInfo.setInstallLocation(null); - } - - if(programInfo.getSource() == ProgramSource.REGISTRY && !programInfo.getInstallLocation().endsWith("\\")){ - programInfo.setInstallLocation(programInfo.getInstallLocation() + "\\"); - } - } - - GlobalDataStore.programInfos.addAll(programs); - - LocalTime time3 = LocalTime.now(); - System.out.println("数据处理时间:" + Duration.between(time2, time3).toMillis() + "ms"); - - - } - - public static void main(String[] args) throws IOException, InterruptedException, SQLException { - LocalTime start = LocalTime.now(); - getInstalledPrograms(); - GlobalDataStore.programInfos.forEach(System.out::println); - dataBaseOperate db = dataBaseOperate.getInstance(); - db.syncSoftwareList(GlobalDataStore.programInfos); - LocalTime end = LocalTime.now(); - System.out.println("程序运行总时间: " + Duration.between(start, end).toMillis() + "ms"); - } -} From bd4f631d94bea79ae3e6e6e3dc269778de7697dd Mon Sep 17 00:00:00 2001 From: mine_ Date: Mon, 22 Sep 2025 17:39:57 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat:=E7=AE=80=E5=8D=95=E9=87=8D=E6=9E=84`A?= =?UTF-8?q?ppLauncher`=E4=B8=BA=E5=B7=A5=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LaunchProgramUtil.java} | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) rename src/main/java/tech/minediamond/vortex/{service/program/AppLauncher.java => util/LaunchProgramUtil.java} (90%) diff --git a/src/main/java/tech/minediamond/vortex/service/program/AppLauncher.java b/src/main/java/tech/minediamond/vortex/util/LaunchProgramUtil.java similarity index 90% rename from src/main/java/tech/minediamond/vortex/service/program/AppLauncher.java rename to src/main/java/tech/minediamond/vortex/util/LaunchProgramUtil.java index 03fe45e..68b1c03 100644 --- a/src/main/java/tech/minediamond/vortex/service/program/AppLauncher.java +++ b/src/main/java/tech/minediamond/vortex/util/LaunchProgramUtil.java @@ -17,17 +17,20 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -package tech.minediamond.vortex.service.program; +package tech.minediamond.vortex.util; +import lombok.extern.slf4j.Slf4j; import tech.minediamond.vortex.model.appConfig.GlobalDataStore; import tech.minediamond.vortex.model.program.ProgramInfo; +import tech.minediamond.vortex.service.program.dataBaseOperate; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.sql.SQLException; -public class AppLauncher { +@Slf4j +public class LaunchProgramUtil { private static final String UWP_COMMAND_PREFIX = "shell:AppsFolder\\"; private static final String EXPLORER_EXECUTABLE = "explorer.exe"; @@ -54,17 +57,17 @@ public static boolean openApplication(ProgramInfo programInfo) { switch (programInfo.getSource()) { case UWP -> { - System.out.println("启动UWP应用"); + log.info("启动UWP应用"); String aumid = programInfo.getProgramName(); pb = new ProcessBuilder(EXPLORER_EXECUTABLE, UWP_COMMAND_PREFIX + aumid); } case REGISTRY -> { - System.out.println("启动Registry应用"); + log.info("启动Registry应用"); Path path = Path.of(programInfo.getInstallLocation(),programInfo.getProgramName()); - System.out.println(path); + log.debug(String.valueOf(path)); pb = new ProcessBuilder(path.toString()); File workingDirectory = new File(programInfo.getInstallLocation()); @@ -84,7 +87,6 @@ public static boolean openApplication(ProgramInfo programInfo) { public static void main(String[] args) throws IOException, SQLException { - dataBaseOperate dbo = dataBaseOperate.getInstance(); GlobalDataStore.programInfos = dbo.readProgramInfoList(); GlobalDataStore.programInfos.forEach(System.out::println); From e9e8db2c0ce08a5f37d43e921c26ac2bd2634202 Mon Sep 17 00:00:00 2001 From: mine_ Date: Tue, 23 Sep 2025 20:26:48 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat:=E5=88=A0=E9=99=A4`dataBaseOperate.jav?= =?UTF-8?q?a`=EF=BC=8C=E4=B8=BA=E8=BF=81=E7=A7=BB=E5=88=B0json=E5=81=9A?= =?UTF-8?q?=E5=87=86=E5=A4=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../program/FindApplicationsService.java | 2 - .../service/program/dataBaseOperate.java | 296 ------------------ .../vortex/util/LaunchProgramUtil.java | 8 - 3 files changed, 306 deletions(-) delete mode 100644 src/main/java/tech/minediamond/vortex/service/program/dataBaseOperate.java diff --git a/src/main/java/tech/minediamond/vortex/service/program/FindApplicationsService.java b/src/main/java/tech/minediamond/vortex/service/program/FindApplicationsService.java index e997ebd..6105c08 100644 --- a/src/main/java/tech/minediamond/vortex/service/program/FindApplicationsService.java +++ b/src/main/java/tech/minediamond/vortex/service/program/FindApplicationsService.java @@ -167,8 +167,6 @@ public static void main(String[] args) throws IOException, InterruptedException, LocalTime start = LocalTime.now(); new FindApplicationsService().getInstalledPrograms(); GlobalDataStore.programInfos.forEach(System.out::println); - dataBaseOperate db = dataBaseOperate.getInstance(); - db.syncSoftwareList(GlobalDataStore.programInfos); LocalTime end = LocalTime.now(); log.info("程序运行总时间: {}ms", Duration.between(start, end).toMillis()); } diff --git a/src/main/java/tech/minediamond/vortex/service/program/dataBaseOperate.java b/src/main/java/tech/minediamond/vortex/service/program/dataBaseOperate.java deleted file mode 100644 index c13b2ee..0000000 --- a/src/main/java/tech/minediamond/vortex/service/program/dataBaseOperate.java +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Vortex - * Copyright (C) 2025 Mine-diamond - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -package tech.minediamond.vortex.service.program; - -import tech.minediamond.vortex.model.program.ProgramInfo; -import tech.minediamond.vortex.model.program.ProgramSource; - -import java.nio.file.Path; -import java.sql.*; -import java.util.*; - -public class dataBaseOperate { - private static final String DB_URL = "jdbc:sqlite:installed_software.db"; - - // 1. 将构造函数设为私有,防止外部直接 new - private dataBaseOperate() { - // 在实例被创建时,确保表结构存在 - createTable(); - } - - // 2. 创建一个私有的静态内部类来持有单例实例 - private static class SingletonHolder { - private static final dataBaseOperate INSTANCE = new dataBaseOperate(); - } - - // 3. 提供一个公共的静态方法来获取这个唯一的实例 - public static dataBaseOperate getInstance() { - return SingletonHolder.INSTANCE; - } - - private Connection connect() throws SQLException { - return DriverManager.getConnection(DB_URL); - } - - /** - * 创建软件信息表 (如果不存在) - */ - public void createTable() { - String sql = "CREATE TABLE IF NOT EXISTS software (\n" - + "id INTEGER PRIMARY KEY AUTOINCREMENT,\n" - + "baseName TEXT NOT NULL,\n" - + "displayName TEXT,\n" - + "programName TEXT,\n" - + "version TEXT,\n" - + "publisher TEXT,\n" - + "installLocation TEXT,\n" - + "source TEXT,\n" - + "enabled TEXT,\n" - + "path TEXT\n" - + ");"; - - try (Connection conn = connect(); - Statement stmt = conn.createStatement()) { - stmt.execute(sql); - } catch (SQLException e) { - System.err.println("Error creating table: " + e.getMessage()); - } - } - - /** - * 增 (Create): 插入一个软件列表,使用事务批量处理 - */ - public void insertSoftwareList(List programInfoList) throws SQLException { - String sql = "INSERT INTO software(baseName, displayName, version, publisher, installLocation, enabled) VALUES(?, ?, ?, ?, ?, ?)"; - - try (Connection conn = connect()) { - // 关闭自动提交,开启事务 - conn.setAutoCommit(false); - - try (PreparedStatement pstmt = conn.prepareStatement(sql)) { - for (ProgramInfo software : programInfoList) { - pstmt.setString(1, software.getBaseName()); - pstmt.setString(2, software.getDisplayName()); - pstmt.setString(3, software.getVersion()); - pstmt.setString(4, software.getPublisher()); - pstmt.setString(5, software.getInstallLocation()); - pstmt.setString(6, software.getEnabled().toString()); - pstmt.addBatch(); - } - pstmt.executeBatch(); - conn.commit(); // 提交事务 - System.out.println(programInfoList.size() + " records inserted/replaced successfully."); - } catch (SQLException e) { - conn.rollback(); // 如果出错,回滚事务 - System.err.println("Transaction rolled back. Error: " + e.getMessage()); - } - } - } - - // 在 dataBaseOperate.java 中 - - /** - * 同步软件列表到数据库,处理新增、更新和删除。 - * 使用 displayName + version 作为唯一标识来查找记录。 - * 任何字段(包括 installLocation)的变化都会触发更新。 - * - * @param latestProgramList 最新的软件信息列表 - * @throws SQLException 如果发生数据库错误 - */ - public void syncSoftwareList(List latestProgramList) throws SQLException { - // SQL 语句 - String selectWithVersionSql = "SELECT * FROM software WHERE displayName = ? AND installLocation = ?"; - String selectWithoutVersionSql = "SELECT * FROM software WHERE displayName = ? AND installLocation IS NULL"; - String insertSql = "INSERT INTO software(baseName, displayName, programName, version, publisher, installLocation, source, enabled, path) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"; - // 更新时,我们更新所有可能变化的字段 - String updateSql = "UPDATE software SET baseName = ?, programName = ?,publisher = ?, version = ?, source = ?, enabled = ?, path = ? WHERE id = ?"; - - Connection conn = null; - try { - conn = connect(); - conn.setAutoCommit(false); // 开启事务 - - // 预编译所有需要的语句 - PreparedStatement selectWithVersionStmt = conn.prepareStatement(selectWithVersionSql); - PreparedStatement selectWithoutVersionStmt = conn.prepareStatement(selectWithoutVersionSql); - PreparedStatement insertStmt = conn.prepareStatement(insertSql); - PreparedStatement updateStmt = conn.prepareStatement(updateSql); - - int insertedCount = 0; - int updatedCount = 0; - - // 1. 处理新增和更新 - for (ProgramInfo newProgram : latestProgramList) { - // 用“钥匙”查找数据库中的记录 - PreparedStatement currentSelectStmt; - if (newProgram.getInstallLocation() != null && !newProgram.getInstallLocation().isEmpty()) { - // 情况一:版本号存在 - currentSelectStmt = selectWithVersionStmt; - currentSelectStmt.setString(1, newProgram.getDisplayName()); - currentSelectStmt.setString(2, newProgram.getInstallLocation()); - } else { - // 情况二:版本号为 null 或为空字符串 - currentSelectStmt = selectWithoutVersionStmt; - currentSelectStmt.setString(1, newProgram.getDisplayName()); - } - ResultSet rs = currentSelectStmt.executeQuery(); - - if (rs.next()) { // 找到了匹配的记录 - long dbId = rs.getLong("id"); - - // 从数据库记录创建一个 ProgramInfo 对象,用于比较 - ProgramInfo dbProgram = new ProgramInfo(); - dbProgram.setBaseName(rs.getString("baseName")); - dbProgram.setDisplayName(rs.getString("displayName")); - dbProgram.setProgramName(rs.getString("programName")); - dbProgram.setVersion(rs.getString("version")); - dbProgram.setPublisher(rs.getString("publisher")); - dbProgram.setInstallLocation(rs.getString("installLocation")); - dbProgram.setEnabled(Boolean.parseBoolean(rs.getString("enabled"))); - dbProgram.setPath(Optional.ofNullable((rs.getString("path"))).map(Path::of).orElse(null)); - - - if (!newProgram.equals(dbProgram)) { - - updateStmt.setString(1, newProgram.getBaseName()); - updateStmt.setString(2, newProgram.getProgramName()); - updateStmt.setString(3, newProgram.getPublisher()); - updateStmt.setString(4, newProgram.getVersion()); - updateStmt.setString(5, newProgram.getSource().toString()); - updateStmt.setString(6, newProgram.getEnabled().toString()); - updateStmt.setString(7, Optional.ofNullable(newProgram.getPath()).map(Path::toString).orElse(null)); - updateStmt.setLong(8, dbId); // 用主键 id 来更新,最精确 - - updateStmt.addBatch(); - updatedCount++; - } - - - } else { // 数据库中没有这条记录 - // 执行插入 - insertStmt.setString(1, newProgram.getBaseName()); - insertStmt.setString(2, newProgram.getDisplayName()); - insertStmt.setString(3, newProgram.getProgramName()); - insertStmt.setString(4, newProgram.getVersion()); - insertStmt.setString(5, newProgram.getPublisher()); - insertStmt.setString(6, newProgram.getInstallLocation()); - insertStmt.setString(7, newProgram.getSource().toString()); - insertStmt.setString(8, newProgram.getEnabled().toString()); - insertStmt.setString(9, Optional.ofNullable(newProgram.getPath()).map(Path::toString).orElse(null)); - - insertStmt.addBatch(); - insertedCount++; - } - rs.close(); - } - - // 执行所有累积的插入和更新操作 - insertStmt.executeBatch(); - updateStmt.executeBatch(); - - // 2. 处理删除 (这部分逻辑对于完整同步至关重要) - //int deletedCount = handleDelete(conn, latestProgramList); - - // 3. 提交事务 - conn.commit(); - System.out.println("同步完成!新增: " + insertedCount + ", 更新: " + updatedCount); - - } catch (SQLException e) { - if (conn != null) conn.rollback(); - throw e; - } finally { - if (conn != null) { - conn.setAutoCommit(true); - conn.close(); - } - } - } - - public ArrayList readProgramInfoList() throws SQLException { - String readSQL= "SELECT * FROM software"; - ArrayList programInfoList = new ArrayList<>(); - try (Connection conn = connect(); - Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(readSQL)) { - while (rs.next()) { - ProgramInfo programInfo = new ProgramInfo(); - programInfo.setBaseName(rs.getString("baseName")); - programInfo.setDisplayName(rs.getString("displayName")); - programInfo.setProgramName(rs.getString("programName")); - programInfo.setVersion(rs.getString("version")); - programInfo.setPublisher(rs.getString("publisher")); - programInfo.setInstallLocation(rs.getString("installLocation")); - programInfo.setSource(ProgramSource.valueOf(rs.getString("source"))); - programInfo.setEnabled(Boolean.parseBoolean(rs.getString("enabled"))); - programInfo.setPath(Optional.ofNullable(rs.getString("path")).map(Path::of).orElse(null)); - programInfo.setEnabled(Boolean.parseBoolean(rs.getString("enabled"))); - programInfo.setId(rs.getString("id")); - programInfoList.add(programInfo); - } - } - return programInfoList; - } - - /** - * 一个辅助方法,用于处理删除逻辑 - */ - private int handleDelete(Connection conn, List latestProgramList) throws SQLException { - // 创建一个新列表中所有软件的唯一标识符集合 - Set latestKeys = new HashSet<>(); - for (ProgramInfo p : latestProgramList) { - // 使用和查询时完全相同的“钥匙” - latestKeys.add(p.getDisplayName() + "::" + p.getVersion()); - } - - List idsToDelete = new ArrayList<>(); - // 查询数据库中所有的 displayName, version 和 id - String selectAllSql = "SELECT id, displayName, displayVersion FROM software"; - try (Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery(selectAllSql)) { - - while (rs.next()) { - String dbKey = rs.getString("displayName") + "::" + rs.getString("version"); - if (!latestKeys.contains(dbKey)) { - // 这个数据库记录在新列表中不存在,准备删除 - idsToDelete.add(rs.getLong("id")); - } - } - } - - if (idsToDelete.isEmpty()) { - return 0; - } - - // 批量删除 - String deleteSql = "DELETE FROM software WHERE id = ?"; - try (PreparedStatement deleteStmt = conn.prepareStatement(deleteSql)) { - for (Long id : idsToDelete) { - deleteStmt.setLong(1, id); - deleteStmt.addBatch(); - } - int[] result = deleteStmt.executeBatch(); - return result.length; - } - } - - - - -} diff --git a/src/main/java/tech/minediamond/vortex/util/LaunchProgramUtil.java b/src/main/java/tech/minediamond/vortex/util/LaunchProgramUtil.java index 68b1c03..b153533 100644 --- a/src/main/java/tech/minediamond/vortex/util/LaunchProgramUtil.java +++ b/src/main/java/tech/minediamond/vortex/util/LaunchProgramUtil.java @@ -22,7 +22,6 @@ import lombok.extern.slf4j.Slf4j; import tech.minediamond.vortex.model.appConfig.GlobalDataStore; import tech.minediamond.vortex.model.program.ProgramInfo; -import tech.minediamond.vortex.service.program.dataBaseOperate; import java.io.File; import java.io.IOException; @@ -87,12 +86,5 @@ public static boolean openApplication(ProgramInfo programInfo) { public static void main(String[] args) throws IOException, SQLException { - dataBaseOperate dbo = dataBaseOperate.getInstance(); - GlobalDataStore.programInfos = dbo.readProgramInfoList(); - GlobalDataStore.programInfos.forEach(System.out::println); - System.out.println("---------------------------------------------------------------"); - - System.out.println(getProgramInfoByID("146")); - openApplication(getProgramInfoByID("146")); } }