From 52c099e0bade1873580ae26923b4fddcd1acdd3e Mon Sep 17 00:00:00 2001 From: liumangmang Date: Wed, 4 Feb 2026 17:54:16 +0800 Subject: [PATCH] feat: support SVN auth and project credentials - add username/password fields to project dialog and model - pass optional auth to SVN info/status/log/diff/update/commit services - centralize SVN CLI auth flags in SvnService and fix header text Co-authored-by: Cursor --- .../svnmanager/controller/MainController.java | 54 +++++++++++++++---- .../controller/ProjectDialogController.java | 42 ++++++++++++++- .../java/com/svnmanager/model/Project.java | 22 ++++++++ .../com/svnmanager/service/CommitService.java | 13 +++-- .../com/svnmanager/service/DiffService.java | 22 ++++++-- .../com/svnmanager/service/InfoService.java | 7 ++- .../com/svnmanager/service/LogService.java | 12 +++-- .../com/svnmanager/service/StatusService.java | 8 ++- .../com/svnmanager/service/SvnService.java | 21 ++++++++ .../com/svnmanager/service/UpdateService.java | 10 +++- src/main/resources/fxml/project-dialog.fxml | 14 +++-- 11 files changed, 198 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/svnmanager/controller/MainController.java b/src/main/java/com/svnmanager/controller/MainController.java index 0d6eed0..924294c 100644 --- a/src/main/java/com/svnmanager/controller/MainController.java +++ b/src/main/java/com/svnmanager/controller/MainController.java @@ -203,7 +203,10 @@ public class MainController { protected Void call() throws Exception { try { // 获取SVN信息 - com.svnmanager.model.SvnInfo info = infoService.getInfo(currentProject.getPath()); + com.svnmanager.model.SvnInfo info = infoService.getInfo( + currentProject.getPath(), + currentProject.getUsername(), + currentProject.getPassword()); if (info != null && info.getRevision() != null) { Platform.runLater(() -> { currentVersionLabel.setText("r" + info.getRevision()); @@ -212,7 +215,10 @@ public class MainController { } // 获取状态 - SvnStatus status = statusService.getStatus(currentProject.getPath()); + SvnStatus status = statusService.getStatus( + currentProject.getPath(), + currentProject.getUsername(), + currentProject.getPassword()); Platform.runLater(() -> { fileStatusList.clear(); fileStatusList.addAll(status.getFiles()); @@ -263,8 +269,15 @@ public class MainController { if (result == ButtonType.OK) { Project project = controller.getProject(); if (project != null) { - ConfigUtil.addProject(project); - loadProjects(); + boolean saved = ConfigUtil.addProject(project); + if (saved) { + loadProjects(); + } else { + showError("保存失败", "无法保存项目配置,请检查当前用户目录下是否有写入权限:\n" + + ConfigUtil.getProjectsFilePath()); + } + } else { + showWarning("提示", "项目名称和项目路径不能为空。"); } } }); @@ -306,7 +319,10 @@ public class MainController { Task task = new Task() { @Override protected UpdateService.UpdateResult call() throws Exception { - return updateService.update(currentProject.getPath()); + return updateService.update( + currentProject.getPath(), + currentProject.getUsername(), + currentProject.getPassword()); } }; @@ -348,7 +364,11 @@ public class MainController { Task task = new Task() { @Override protected CommitService.CommitResult call() throws Exception { - return commitService.commit(currentProject.getPath(), message); + return commitService.commit( + currentProject.getPath(), + message, + currentProject.getUsername(), + currentProject.getPassword()); } }; @@ -380,7 +400,10 @@ public class MainController { Task task = new Task() { @Override protected SvnStatus call() throws Exception { - return statusService.getStatus(currentProject.getPath()); + return statusService.getStatus( + currentProject.getPath(), + currentProject.getUsername(), + currentProject.getPassword()); } }; @@ -409,7 +432,11 @@ public class MainController { Task> task = new Task>() { @Override protected List call() throws Exception { - return logService.getLog(currentProject.getPath(), 20); + return logService.getLog( + currentProject.getPath(), + 20, + currentProject.getUsername(), + currentProject.getPassword()); } }; @@ -435,7 +462,11 @@ public class MainController { Task task = new Task() { @Override protected String call() throws Exception { - return diffService.getDiff(currentProject.getPath()); + return diffService.getDiff( + currentProject.getPath(), + null, + currentProject.getUsername(), + currentProject.getPassword()); } }; @@ -461,7 +492,10 @@ public class MainController { Task task = new Task() { @Override protected com.svnmanager.model.SvnInfo call() throws Exception { - return infoService.getInfo(currentProject.getPath()); + return infoService.getInfo( + currentProject.getPath(), + currentProject.getUsername(), + currentProject.getPassword()); } }; diff --git a/src/main/java/com/svnmanager/controller/ProjectDialogController.java b/src/main/java/com/svnmanager/controller/ProjectDialogController.java index f669c26..08e6d8b 100644 --- a/src/main/java/com/svnmanager/controller/ProjectDialogController.java +++ b/src/main/java/com/svnmanager/controller/ProjectDialogController.java @@ -1,6 +1,8 @@ package com.svnmanager.controller; import com.svnmanager.model.Project; +import com.svnmanager.model.SvnInfo; +import com.svnmanager.service.InfoService; import javafx.fxml.FXML; import javafx.scene.control.TextField; import javafx.stage.DirectoryChooser; @@ -16,6 +18,8 @@ public class ProjectDialogController { @FXML private TextField nameField; @FXML private TextField pathField; @FXML private TextField svnUrlField; + @FXML private TextField usernameField; + @FXML private TextField passwordField; private Project project; @@ -34,7 +38,31 @@ public class ProjectDialogController { File selectedDirectory = directoryChooser.showDialog(pathField.getScene().getWindow()); if (selectedDirectory != null) { - pathField.setText(selectedDirectory.getAbsolutePath()); + String selectedPath = selectedDirectory.getAbsolutePath(); + pathField.setText(selectedPath); + + // 如果是已存在的SVN工作副本,自动获取URL填充 + autoFillSvnUrl(selectedPath); + } + } + + /** + * 根据工作副本自动填充 SVN URL + */ + private void autoFillSvnUrl(String workingDirectory) { + // 仅当当前输入框为空时才自动填充,避免覆盖用户手动输入 + if (svnUrlField.getText() != null && !svnUrlField.getText().trim().isEmpty()) { + return; + } + + try { + InfoService infoService = new InfoService(); + SvnInfo info = infoService.getInfo(workingDirectory); + if (info != null && info.getUrl() != null && !info.getUrl().isEmpty()) { + svnUrlField.setText(info.getUrl()); + } + } catch (Exception e) { + // 自动填充失败时静默忽略,不影响正常使用 } } @@ -47,6 +75,8 @@ public class ProjectDialogController { String name = nameField.getText().trim(); String path = pathField.getText().trim(); String svnUrl = svnUrlField.getText().trim(); + String username = usernameField.getText().trim(); + String password = passwordField.getText(); if (name.isEmpty() || path.isEmpty()) { return null; @@ -59,6 +89,8 @@ public class ProjectDialogController { project.setName(name); project.setPath(path); project.setSvnUrl(svnUrl); + project.setUsername(username.isEmpty() ? null : username); + project.setPassword(password == null || password.isEmpty() ? null : password); return project; } @@ -74,6 +106,14 @@ public class ProjectDialogController { nameField.setText(project.getName()); pathField.setText(project.getPath()); svnUrlField.setText(project.getSvnUrl()); + usernameField.setText(project.getUsername()); + passwordField.setText(project.getPassword()); + + // 如果已有路径但URL为空,尝试自动获取 + if (project.getPath() != null && !project.getPath().isEmpty() + && (project.getSvnUrl() == null || project.getSvnUrl().isEmpty())) { + autoFillSvnUrl(project.getPath()); + } } } } diff --git a/src/main/java/com/svnmanager/model/Project.java b/src/main/java/com/svnmanager/model/Project.java index 42123e2..fa9af07 100644 --- a/src/main/java/com/svnmanager/model/Project.java +++ b/src/main/java/com/svnmanager/model/Project.java @@ -18,6 +18,12 @@ public class Project { @JsonProperty("svnUrl") private String svnUrl; + @JsonProperty("username") + private String username; + + @JsonProperty("password") + private String password; + @JsonProperty("currentVersion") private String currentVersion; @@ -71,6 +77,22 @@ public class Project { this.svnUrl = svnUrl; } + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + public String getCurrentVersion() { return currentVersion; } diff --git a/src/main/java/com/svnmanager/service/CommitService.java b/src/main/java/com/svnmanager/service/CommitService.java index d265b84..e2b5eb9 100644 --- a/src/main/java/com/svnmanager/service/CommitService.java +++ b/src/main/java/com/svnmanager/service/CommitService.java @@ -26,7 +26,8 @@ public class CommitService extends SvnService { * @throws InterruptedException 中断异常 * @throws TimeoutException 超时异常 */ - public CommitResult commit(String workingDirectory, String message, List files) + public CommitResult commit(String workingDirectory, String message, List files, + String username, String password) throws IOException, InterruptedException, TimeoutException { logger.info("提交修改: {}", workingDirectory); @@ -39,6 +40,7 @@ public class CommitService extends SvnService { } List args = new ArrayList<>(); + addAuthArgs(args, username, password); args.add("-m"); args.add(message); @@ -85,9 +87,14 @@ public class CommitService extends SvnService { * @throws InterruptedException 中断异常 * @throws TimeoutException 超时异常 */ - public CommitResult commit(String workingDirectory, String message) + public CommitResult commit(String workingDirectory, String message, String username, String password) throws IOException, InterruptedException, TimeoutException { - return commit(workingDirectory, message, null); + return commit(workingDirectory, message, null, username, password); + } + + public CommitResult commit(String workingDirectory, String message) + throws IOException, InterruptedException, TimeoutException { + return commit(workingDirectory, message, null, null); } /** diff --git a/src/main/java/com/svnmanager/service/DiffService.java b/src/main/java/com/svnmanager/service/DiffService.java index bc82736..b15447c 100644 --- a/src/main/java/com/svnmanager/service/DiffService.java +++ b/src/main/java/com/svnmanager/service/DiffService.java @@ -25,7 +25,7 @@ public class DiffService extends SvnService { * @throws InterruptedException 中断异常 * @throws TimeoutException 超时异常 */ - public String getDiff(String workingDirectory, String filePath) + public String getDiff(String workingDirectory, String filePath, String username, String password) throws IOException, InterruptedException, TimeoutException { logger.debug("获取差异: {}", workingDirectory); @@ -34,6 +34,7 @@ public class DiffService extends SvnService { } List args = new ArrayList<>(); + addAuthArgs(args, username, password); if (filePath != null && !filePath.isEmpty()) { args.add(filePath); } @@ -57,8 +58,23 @@ public class DiffService extends SvnService { * @throws InterruptedException 中断异常 * @throws TimeoutException 超时异常 */ - public String getDiff(String workingDirectory) + public String getDiff(String workingDirectory) throws IOException, InterruptedException, TimeoutException { - return getDiff(workingDirectory, null); + return getDiff(workingDirectory, null, null, null); + } + + /** + * 获取指定文件的差异(不带认证信息) + * + * @param workingDirectory 工作目录 + * @param filePath 文件路径(null表示所有文件) + * @return 差异内容 + * @throws IOException IO异常 + * @throws InterruptedException 中断异常 + * @throws TimeoutException 超时异常 + */ + public String getDiff(String workingDirectory, String filePath) + throws IOException, InterruptedException, TimeoutException { + return getDiff(workingDirectory, filePath, null, null); } } diff --git a/src/main/java/com/svnmanager/service/InfoService.java b/src/main/java/com/svnmanager/service/InfoService.java index b6b4ff2..f693ddb 100644 --- a/src/main/java/com/svnmanager/service/InfoService.java +++ b/src/main/java/com/svnmanager/service/InfoService.java @@ -23,7 +23,7 @@ public class InfoService extends SvnService { * @throws InterruptedException 中断异常 * @throws TimeoutException 超时异常 */ - public SvnInfo getInfo(String workingDirectory) + public SvnInfo getInfo(String workingDirectory, String username, String password) throws IOException, InterruptedException, TimeoutException { logger.debug("获取信息: {}", workingDirectory); @@ -45,6 +45,11 @@ public class InfoService extends SvnService { return info; } + public SvnInfo getInfo(String workingDirectory) + throws IOException, InterruptedException, TimeoutException { + return getInfo(workingDirectory, null, null); + } + /** * 解析svn info输出 * diff --git a/src/main/java/com/svnmanager/service/LogService.java b/src/main/java/com/svnmanager/service/LogService.java index 0d139f8..190a6e5 100644 --- a/src/main/java/com/svnmanager/service/LogService.java +++ b/src/main/java/com/svnmanager/service/LogService.java @@ -30,7 +30,7 @@ public class LogService extends SvnService { * @throws InterruptedException 中断异常 * @throws TimeoutException 超时异常 */ - public List getLog(String workingDirectory, Integer limit) + public List getLog(String workingDirectory, Integer limit, String username, String password) throws IOException, InterruptedException, TimeoutException { logger.debug("获取日志: {}", workingDirectory); @@ -39,6 +39,7 @@ public class LogService extends SvnService { } List args = new ArrayList<>(); + addAuthArgs(args, username, password); args.add("-v"); // 详细输出 if (limit != null && limit > 0) { args.add("-l"); @@ -68,9 +69,14 @@ public class LogService extends SvnService { * @throws InterruptedException 中断异常 * @throws TimeoutException 超时异常 */ - public List getLog(String workingDirectory) + public List getLog(String workingDirectory, String username, String password) throws IOException, InterruptedException, TimeoutException { - return getLog(workingDirectory, 50); + return getLog(workingDirectory, 50, username, password); + } + + public List getLog(String workingDirectory) + throws IOException, InterruptedException, TimeoutException { + return getLog(workingDirectory, 50, null, null); } /** diff --git a/src/main/java/com/svnmanager/service/StatusService.java b/src/main/java/com/svnmanager/service/StatusService.java index 94ddd2a..3326de8 100644 --- a/src/main/java/com/svnmanager/service/StatusService.java +++ b/src/main/java/com/svnmanager/service/StatusService.java @@ -26,7 +26,7 @@ public class StatusService extends SvnService { * @throws InterruptedException 中断异常 * @throws TimeoutException 超时异常 */ - public SvnStatus getStatus(String workingDirectory) + public SvnStatus getStatus(String workingDirectory, String username, String password) throws IOException, InterruptedException, TimeoutException { logger.debug("获取状态: {}", workingDirectory); @@ -35,6 +35,7 @@ public class StatusService extends SvnService { } List args = new ArrayList<>(); + addAuthArgs(args, username, password); args.add("-v"); // 详细输出 ProcessUtil.ProcessResult result = executeSvnCommand("status", args, workingDirectory); @@ -53,6 +54,11 @@ public class StatusService extends SvnService { return status; } + public SvnStatus getStatus(String workingDirectory) + throws IOException, InterruptedException, TimeoutException { + return getStatus(workingDirectory, null, null); + } + /** * 解析svn status输出 * diff --git a/src/main/java/com/svnmanager/service/SvnService.java b/src/main/java/com/svnmanager/service/SvnService.java index e74a7b4..e07fa3b 100644 --- a/src/main/java/com/svnmanager/service/SvnService.java +++ b/src/main/java/com/svnmanager/service/SvnService.java @@ -15,6 +15,27 @@ import java.util.concurrent.TimeoutException; public abstract class SvnService { protected static final Logger logger = LoggerFactory.getLogger(SvnService.class); + /** + * 将认证参数追加到命令参数列表 + * + * @param args 命令参数列表 + * @param username 用户名(可为空) + * @param password 密码(可为空) + */ + protected void addAuthArgs(List args, String username, String password) { + if (username != null && !username.isEmpty()) { + args.add("--username"); + args.add(username); + if (password != null && !password.isEmpty()) { + args.add("--password"); + args.add(password); + } + // 避免弹出交互式输入框,统一走非交互模式 + args.add("--no-auth-cache"); + args.add("--non-interactive"); + } + } + /** * 执行SVN命令 * diff --git a/src/main/java/com/svnmanager/service/UpdateService.java b/src/main/java/com/svnmanager/service/UpdateService.java index 48cffe2..d31e67d 100644 --- a/src/main/java/com/svnmanager/service/UpdateService.java +++ b/src/main/java/com/svnmanager/service/UpdateService.java @@ -25,7 +25,7 @@ public class UpdateService extends SvnService { * @throws InterruptedException 中断异常 * @throws TimeoutException 超时异常 */ - public UpdateResult update(String workingDirectory, String revision) + public UpdateResult update(String workingDirectory, String revision, String username, String password) throws IOException, InterruptedException, TimeoutException { logger.info("更新工作副本: {}", workingDirectory); @@ -34,6 +34,7 @@ public class UpdateService extends SvnService { } List args = new ArrayList<>(); + addAuthArgs(args, username, password); if (revision != null && !revision.isEmpty()) { args.add("-r"); args.add(revision); @@ -77,9 +78,14 @@ public class UpdateService extends SvnService { * @throws InterruptedException 中断异常 * @throws TimeoutException 超时异常 */ + public UpdateResult update(String workingDirectory, String username, String password) + throws IOException, InterruptedException, TimeoutException { + return update(workingDirectory, null, username, password); + } + public UpdateResult update(String workingDirectory) throws IOException, InterruptedException, TimeoutException { - return update(workingDirectory, null); + return update(workingDirectory, null, null, null); } /** diff --git a/src/main/resources/fxml/project-dialog.fxml b/src/main/resources/fxml/project-dialog.fxml index 2fa2ef8..9df3403 100644 --- a/src/main/resources/fxml/project-dialog.fxml +++ b/src/main/resources/fxml/project-dialog.fxml @@ -10,9 +10,9 @@ - - + + 添加/编辑项目 @@ -36,6 +36,14 @@