diff --git a/client/pom.xml b/client/pom.xml index 8128207..5020641 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -6,12 +6,12 @@ com.ecep.contract Contract-Manager - 0.0.86-SNAPSHOT + 0.0.99-SNAPSHOT com.ecep.contract client - 0.0.86-SNAPSHOT + 0.0.99-SNAPSHOT ${java.version} @@ -22,7 +22,7 @@ com.ecep.contract common - 0.0.86-SNAPSHOT + 0.0.99-SNAPSHOT org.springframework.boot @@ -120,6 +120,23 @@ + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.2 + + + copy-dependencies + package + + copy-dependencies + + + ${project.build.directory}/relativePath/client/lib + + + + diff --git a/client/src/main/java/com/ecep/contract/ClientV2.java b/client/src/main/java/com/ecep/contract/ClientV2.java index 33b397e..50c81ac 100644 --- a/client/src/main/java/com/ecep/contract/ClientV2.java +++ b/client/src/main/java/com/ecep/contract/ClientV2.java @@ -2,11 +2,14 @@ package com.ecep.contract; import javafx.application.Application; +import java.io.File; + /** * Created by Administrator on 2017/4/16. */ public class ClientV2 { public static void main(String[] args) { + System.out.println("当前目录 = " + new File(".").getAbsolutePath()); Application.launch(Desktop.class, args); } diff --git a/client/src/main/java/com/ecep/contract/Desktop.java b/client/src/main/java/com/ecep/contract/Desktop.java index bb0fd8e..7807f09 100644 --- a/client/src/main/java/com/ecep/contract/Desktop.java +++ b/client/src/main/java/com/ecep/contract/Desktop.java @@ -203,7 +203,9 @@ public class Desktop extends Application { controller.setHttpClient(this.httpClient); controller.setHolder(holder); controller.setPrimaryStage(primaryStage); - controller.setProperties(properties); + MyProperties myProperties = new MyProperties(); + myProperties.loadFromProperties(properties); + controller.setProperties(myProperties); while (true) { try { controller.tryLogin().get(); diff --git a/client/src/main/java/com/ecep/contract/MyProperties.java b/client/src/main/java/com/ecep/contract/MyProperties.java index 8b5f98f..4545e23 100644 --- a/client/src/main/java/com/ecep/contract/MyProperties.java +++ b/client/src/main/java/com/ecep/contract/MyProperties.java @@ -1,22 +1,192 @@ package com.ecep.contract; -import lombok.Getter; -import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; -import java.io.File; -import java.nio.file.Path; -import java.nio.file.Paths; +import lombok.Getter; +import lombok.Setter; +/** + * 应用程序配置类,用于管理系统配置信息 + */ @Component -@ConfigurationProperties(prefix = "my") -public class MyProperties { +public class MyProperties implements InitializingBean { + private static final Logger logger = LoggerFactory.getLogger(MyProperties.class); + private static final String FILE_NAME = "config.properties"; + @Getter @Setter private String downloadsPath; + @Getter + @Setter + private String serverHost; + + @Getter + @Setter + private String serverPort; + + @Getter + @Setter + private String userName; + + @Getter + @Setter + private String password; + + @Getter + @Setter + private boolean rememberPassword; + + @Override + public void afterPropertiesSet() throws Exception { + // 初始化时加载配置文件 + try { + loadFromFile(); + } catch (Exception e) { + logger.warn("初始化配置文件失败: {}", e.getMessage()); + } + } + + /** + * 从文件加载配置 + */ + public void loadFromFile() { + File configFile = new File(FILE_NAME); + if (!configFile.exists()) { + logger.debug("配置文件不存在: {}", configFile.getPath()); + return; + } + + try (FileInputStream input = new FileInputStream(configFile)) { + Properties properties = new Properties(); + properties.load(input); + loadFromProperties(properties); + logger.debug("成功从配置文件加载配置: {}", configFile.getPath()); + } catch (Exception e) { + logger.error("加载配置文件失败: {}", configFile.getPath(), e); + } + } + + /** + * 设置Properties对象并加载配置 + * 用于从外部设置配置项 + * + * @param properties 配置对象 + */ + public void setProperties(Properties properties) { + this.loadFromProperties(properties); + } + + /** + * 从Properties对象加载配置 + * 用于从config.properties文件中读取配置项 + * + * @param properties 配置对象 + */ + public void loadFromProperties(Properties properties) { + if (properties == null) { + logger.warn("Properties对象为空,无法加载配置"); + return; + } + + // 加载下载路径配置 + String downloadsPath = properties.getProperty("my.downloadsPath"); + if (StringUtils.hasText(downloadsPath)) { + this.setDownloadsPath(downloadsPath); + logger.debug("从配置文件加载下载路径: {}", downloadsPath); + } + + // 加载服务器配置 + String serverHost = properties.getProperty("server.host", "127.0.0.1"); + if (StringUtils.hasText(serverHost)) { + this.setServerHost(serverHost); + logger.debug("从配置文件加载服务器地址: {}", serverHost); + } + + String serverPort = properties.getProperty("server.port", "8080"); + if (StringUtils.hasText(serverPort)) { + this.setServerPort(serverPort); + logger.debug("从配置文件加载服务器端口: {}", serverPort); + } + + // 加载用户凭证配置 + String userName = properties.getProperty("user.name"); + if (StringUtils.hasText(userName)) { + this.setUserName(userName); + logger.debug("从配置文件加载用户名"); + } + + // 只有在记住密码的情况下才加载密码 + String rememberPasswordStr = properties.getProperty("user.rememberPassword"); + boolean rememberPassword = "true".equals(rememberPasswordStr); + this.setRememberPassword(rememberPassword); + + if (rememberPassword) { + String password = properties.getProperty("user.password"); + if (StringUtils.hasText(password)) { + this.setPassword(password); + logger.debug("从配置文件加载密码"); + } + } + } + + /** + * 将配置保存到Properties对象 + * 用于将配置项保存到config.properties文件 + * + * @param properties 配置对象 + */ + public void saveToProperties(Properties properties) { + if (properties == null) { + logger.warn("Properties对象为空,无法保存配置"); + return; + } + + // 保存下载路径配置 + if (StringUtils.hasText(getDownloadsPath())) { + properties.setProperty("my.downloadsPath", getDownloadsPath()); + logger.debug("保存下载路径到配置文件: {}", getDownloadsPath()); + } + + // 保存服务器配置 + if (StringUtils.hasText(getServerHost())) { + properties.setProperty("server.host", getServerHost()); + logger.debug("保存服务器地址到配置文件: {}", getServerHost()); + } + + if (StringUtils.hasText(getServerPort())) { + properties.setProperty("server.port", getServerPort()); + logger.debug("保存服务器端口到配置文件: {}", getServerPort()); + } + + // 保存用户凭证配置 + if (StringUtils.hasText(getUserName())) { + properties.setProperty("user.name", getUserName()); + logger.debug("保存用户名为配置文件"); + } + + properties.setProperty("user.rememberPassword", String.valueOf(isRememberPassword())); + + // 只有在记住密码的情况下才保存密码 + if (isRememberPassword() && StringUtils.hasText(getPassword())) { + properties.setProperty("user.password", getPassword()); + logger.debug("保存密码到配置文件"); + } else if (properties.containsKey("user.password")) { + // 如果不记住密码,删除已存在的密码配置 + properties.remove("user.password"); + } + } /** * 尝试返回当前用户的下载文件夹 @@ -24,7 +194,13 @@ public class MyProperties { public File getDownloadDirectory() { String downloadsPath = getDownloadsPath(); if (StringUtils.hasText(downloadsPath)) { - return new File(downloadsPath); + // 确保目录存在 + File dir = new File(downloadsPath); + if (!dir.exists()) { + boolean created = dir.mkdirs(); + logger.debug("创建下载目录: {}, 结果: {}", downloadsPath, created); + } + return dir; } // 没有配置下载目录时,尝试使用默认设置 @@ -32,4 +208,29 @@ public class MyProperties { Path path = Paths.get(home, "Downloads"); return path.toFile(); } + + /** + * 保存配置到文件 + */ + public void save() { + File configFile = new File(FILE_NAME); + try (FileOutputStream output = new FileOutputStream(configFile)) { + Properties properties = new Properties(); + + // 如果文件已存在,先读取现有配置,避免覆盖其他配置 + if (configFile.exists()) { + try (FileInputStream input = new FileInputStream(configFile)) { + properties.load(input); + } + } + + // 保存当前配置 + saveToProperties(properties); + properties.store(output, "Contract Manager 应用程序配置"); + logger.debug("成功保存配置到文件: {}", configFile.getPath()); + } catch (Exception e) { + logger.error("保存配置到文件失败: {}", configFile.getPath(), e); + } + } + } diff --git a/client/src/main/java/com/ecep/contract/SpringApp.java b/client/src/main/java/com/ecep/contract/SpringApp.java index c5ebccf..416bdcf 100644 --- a/client/src/main/java/com/ecep/contract/SpringApp.java +++ b/client/src/main/java/com/ecep/contract/SpringApp.java @@ -98,6 +98,17 @@ public class SpringApp { context = application.run(); logger.debug("SpringApp.launch application.run()."); Duration between = Duration.between(startup.getBufferedTimeline().getStartTime(), Instant.now()); + + // 初始化MyProperties,从properties加载配置 + try { + MyProperties myProperties = context.getBean(MyProperties.class); + myProperties.loadFromProperties(properties); + holder.info("MyProperties配置加载完成"); + } catch (Exception e) { + logger.error("加载MyProperties配置失败", e); + holder.error("加载MyProperties配置失败: " + e.getMessage()); + } + holder.info("应用程序环境加载完成... " + between); }); CompletableFuture.runAsync(() -> { diff --git a/client/src/main/java/com/ecep/contract/WebSocketClientService.java b/client/src/main/java/com/ecep/contract/WebSocketClientService.java index ebf8fe6..d953150 100644 --- a/client/src/main/java/com/ecep/contract/WebSocketClientService.java +++ b/client/src/main/java/com/ecep/contract/WebSocketClientService.java @@ -118,10 +118,24 @@ public class WebSocketClientService { String errorMsg = node.get(WebSocketConstant.MESSAGE_FIELD_NAME).asText(); logger.error("收到错误消息: 错误码={}, 错误信息={}", errorCode, errorMsg); if (errorCode == WebSocketConstant.ERROR_CODE_UNAUTHORIZED) { + + // 调用所有的 callbacks 和 session 失败并且移除 + callbacks.keySet().stream().toList().forEach(key -> callbacks.remove(key).completeExceptionally(new Exception("未授权"))); + sessions.values().stream().toList().forEach(session -> { + session.updateMessage(java.util.logging.Level.SEVERE, "未授权"); + session.close(); + }); + isActive = false; + webSocket.close(1000, ""); + WebSocketClientService.this.webSocket = null; + // 处理未授权错误,重新登录 OkHttpLoginController controller = new OkHttpLoginController(); + controller.setProperties(SpringApp.getBean(MyProperties.class)); controller.tryLogin(); // 需要把窗口顶置 + isActive = true; + scheduleReconnect(); } return; } @@ -164,8 +178,8 @@ public class WebSocketClientService { }; private void onCallbackMessage(CompletableFuture future, JsonNode node) { - if (node.has(WebSocketConstant.SUCCESS_FIELD_VALUE)) { - if (!node.get(WebSocketConstant.SUCCESS_FIELD_VALUE).asBoolean()) { + if (node.has(WebSocketConstant.SUCCESS_FIELD_NAME)) { + if (!node.get(WebSocketConstant.SUCCESS_FIELD_NAME).asBoolean()) { future.completeExceptionally( new RuntimeException( "请求失败:来自服务器的消息=" + node.get(WebSocketConstant.MESSAGE_FIELD_NAME).asText())); @@ -204,7 +218,7 @@ public class WebSocketClientService { String json = objectMapper.writeValueAsString(msg); callbacks.put(msg.getMessageId(), future); if (webSocket.send(json)) { - logger.debug("send message success:{}", json); + logger.debug("send json success:{}", json); } else { if (isActive) { future.completeExceptionally(new RuntimeException("Failed to send WebSocket message")); diff --git a/client/src/main/java/com/ecep/contract/controller/HomeWindowController.java b/client/src/main/java/com/ecep/contract/controller/HomeWindowController.java index 95d89f3..6881cec 100644 --- a/client/src/main/java/com/ecep/contract/controller/HomeWindowController.java +++ b/client/src/main/java/com/ecep/contract/controller/HomeWindowController.java @@ -116,6 +116,7 @@ public class HomeWindowController extends BaseController { @EventListener public void onCurrentEmployeeInitialed(CurrentEmployeeInitialedEvent event) { + System.out.println("event = " + event); CurrentEmployee currentEmployee = event.getEmployee(); if (currentEmployee.isSystemAdministrator()) { if (logger.isInfoEnabled()) { diff --git a/client/src/main/java/com/ecep/contract/controller/LoginWidowController.java b/client/src/main/java/com/ecep/contract/controller/LoginWidowController.java deleted file mode 100644 index 336fa39..0000000 --- a/client/src/main/java/com/ecep/contract/controller/LoginWidowController.java +++ /dev/null @@ -1,544 +0,0 @@ -package com.ecep.contract.controller; - -import java.io.FileOutputStream; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.Properties; -import java.util.concurrent.CompletableFuture; -import java.util.logging.Level; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; - -import com.ecep.contract.Desktop; -import com.ecep.contract.MessageHolder; -import com.ecep.contract.SpringApp; - -import javafx.application.Platform; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.event.EventHandler; -import javafx.geometry.Insets; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.CheckBox; -import javafx.scene.control.Label; -import javafx.scene.control.ListView; -import javafx.scene.control.PasswordField; -import javafx.scene.control.TextField; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.GridPane; -import javafx.stage.Stage; -import lombok.Setter; - -public class LoginWidowController implements MessageHolder { - private static final Logger logger = LoggerFactory.getLogger(LoginWidowController.class); - @Setter - MessageHolder holder; - @Setter - Stage primaryStage; - @Setter - Properties properties; - - @Override - public void addMessage(Level level, String message) { - holder.addMessage(level, message); - } - - private void storeProperties() { - try (FileOutputStream fos = new FileOutputStream("config.properties")) { - // 保存到文件 - properties.store(fos, "Updated config.properties"); - info("配置文件已更新!"); - } catch (java.io.IOException e) { - error("保存配置文件失败:" + e.getMessage()); - } - } - - String getHost() { - return properties.getProperty("server.host"); - } - - public void tryLogin() { - // CompletableFuture future = new CompletableFuture<>(); - // 检查配置文件中是否保存用户名和密码 - String userName = getUserName(); - if (StringUtils.hasText(userName)) { - try { - EmployeeInfo employeeInfo = tryToConnect(userName, getPassword()); - if (employeeInfo.errorCode < 0) { - error("登录失败:错误代码=" + employeeInfo.errorCode); - } else { - logined(employeeInfo); - } - } catch (SQLException e) { - throw new RuntimeException(e); - } - } else { - showUserNameLoginDialog(null); - } - } - - private String getPassword() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getPassword'"); - } - - private String getUserName() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getUserName'"); - } - - CompletableFuture> getMacAndIP() { - return CompletableFuture.supplyAsync(() -> { - // mac ip - List list = new ArrayList<>(); - try { - Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); - while (interfaces.hasMoreElements()) { - NetworkInterface anInterface = interfaces.nextElement(); - if (anInterface.isLoopback()) { - continue; - } - byte[] hardwareAddress = anInterface.getHardwareAddress(); - if (hardwareAddress == null) { - continue; - } - Enumeration inetAddresses = anInterface.getInetAddresses(); - if (!inetAddresses.hasMoreElements()) { - continue; - } - - // -分割16进制表示法 - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < hardwareAddress.length; i++) { - sb.append( - String.format("%02X%s", hardwareAddress[i], i < hardwareAddress.length - 1 ? "-" : "")); - } - - while (inetAddresses.hasMoreElements()) { - InetAddress inetAddress = inetAddresses.nextElement(); - if (inetAddress instanceof Inet4Address) { - list.add(new LoginWidowController.MacIP(sb.toString(), inetAddress.getHostAddress())); - } - } - } - } catch (SocketException e) { - throw new RuntimeException(e); - } - return list; - }); - } - - private EmployeeInfo tryToConnect(String userName, String password) throws SQLException { - String host = getHost(); - String port = getPort(); - String database = getDatabase(); - - if (logger.isDebugEnabled()) { - logger.debug("try to connect db server host:{},port:{},database:{},user:{},pwd:{}", host, port, database, - userName, "*"); - } - return null; - } - - private String getDatabase() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getDatabase'"); - } - - private String getPort() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getPort'"); - } - - private void createSession(Connection connection, EmployeeInfo employeeInfo) { - employeeInfo.sessionId = addHistory(connection, employeeInfo.employeeId, employeeInfo.binds.getFirst()); - } - - private int addHistory(Connection connection, int employeeId, MacIP macIP) { - try { - String sql = "INSERT INTO EMPLOYEE_LOGIN_HISTORY (IP, MAC, DT, EMPLOYEE_ID) VALUES (?, ?, ?, ?)"; - try (PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { - ps.setString(1, macIP.ip); - ps.setString(2, macIP.mac); - ps.setObject(3, LocalDateTime.now()); // 根据数据库字段类型调整 - ps.setInt(4, employeeId); - ps.executeUpdate(); - // 返回 新的主键值 - ResultSet generatedKeys = ps.getGeneratedKeys(); - if (generatedKeys.next()) { - return generatedKeys.getInt(1); - } - } - } catch (SQLException e) { - holder.error("申请新会话编号失败"); - logger.error("unable insert EMPLOYEE_LOGIN_HISTORY, ", e); - } - - return -1; - } - - static class MacIP { - String ip; - String mac; - - public MacIP(String mac, String ip) { - this.mac = mac; - this.ip = ip; - } - } - - static class EmployeeInfo { - Integer employeeId; - List binds = new ArrayList<>(); - SimpleStringProperty name = new SimpleStringProperty(); - SimpleBooleanProperty active = new SimpleBooleanProperty(); - int sessionId; - int errorCode = 0; - - public EmployeeInfo(Integer employeeId) { - this.employeeId = employeeId; - } - - public static EmployeeInfo error(int code) { - EmployeeInfo employeeInfo = new EmployeeInfo(null); - employeeInfo.errorCode = code; - return employeeInfo; - } - - public void addBind(MacIP macIP) { - binds.add(macIP); - } - } - - private CompletableFuture tryLoginWithEmployeeBind(Connection connection, - CompletableFuture> macAndIP) { - CompletableFuture future = new CompletableFuture<>(); - macAndIP.thenAccept(macIPS -> { - if (macIPS.isEmpty()) { - future.complete(EmployeeInfo.error(-1)); - return; - } - - HashMap employeeMap = new HashMap<>(); - for (MacIP macIP : macIPS) { - for (Integer employeeId : findAllBindEmployee(connection, macIP)) { - employeeMap.computeIfAbsent(employeeId, k -> new EmployeeInfo(employeeId)).addBind(macIP); - } - } - - if (employeeMap.isEmpty()) { - error("本机未绑定登录信息,请联系管理员更新."); - // 当前计算机的信息,如用户名,计算机名等 - String username = System.getProperty("user.name"); - String computerName = System.getenv("COMPUTERNAME"); - for (MacIP macIP : macIPS) { - if (macIP.ip.equals("127.0.0.1")) { - continue; - } - registerComputer(username, computerName, connection, macIP); - } - future.complete(EmployeeInfo.error(-2)); - return; - } - - if (employeeMap.size() == 1) { - // 直接登录 - EmployeeInfo employeeInfo = employeeMap.values().stream().findFirst().get(); - // issue #1 登录成功后没有更新员工信息 - fill(connection, employeeInfo); - future.complete(employeeInfo); - } else { - List list = employeeMap.values().stream().toList(); - // 选择登录 - Platform.runLater(() -> { - EmployeeInfo info = showEmployeeSelectDialog(list); - future.complete(Objects.requireNonNullElseGet(info, () -> EmployeeInfo.error(-3))); - }); - for (EmployeeInfo info : list) { - fill(connection, info); - } - } - }); - return future; - } - - private EmployeeInfo showEmployeeSelectDialog(List list) { - Stage stage = new Stage(); - stage.initOwner(primaryStage); - stage.setTitle("请选择账户登录系统"); - stage.setWidth(360); - stage.setHeight(280); - - Label label = new Label("您的主机关联了以下账户,请选择一个登录"); - label.setPadding(new Insets(10, 0, 10, 10)); - ListView