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