重构所有注解@CacheConfig的Service类,将IEntityService泛型从实体类改为VO类 实现实体与VO之间的转换逻辑,使用VO替代实体进行缓存以避免序列化问题 更新相关依赖组件和测试用例,确保功能完整性和系统兼容性 优化Redis缓存配置,清理旧缓存数据并验证新缓存策略有效性
402 lines
14 KiB
Java
402 lines
14 KiB
Java
package com.ecep.contract.controller;
|
||
|
||
import java.io.IOException;
|
||
import java.net.InetAddress;
|
||
import java.net.NetworkInterface;
|
||
import java.net.SocketException;
|
||
import java.util.ArrayList;
|
||
import java.util.Enumeration;
|
||
import java.util.List;
|
||
import java.util.Properties;
|
||
import java.util.concurrent.CompletableFuture;
|
||
import java.util.logging.Level;
|
||
|
||
import javafx.beans.property.SimpleStringProperty;
|
||
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.MyProperties;
|
||
import com.ecep.contract.SpringApp;
|
||
import com.fasterxml.jackson.databind.JsonNode;
|
||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||
|
||
import javafx.application.Platform;
|
||
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.PasswordField;
|
||
import javafx.scene.control.TextField;
|
||
import javafx.scene.layout.BorderPane;
|
||
import javafx.scene.layout.GridPane;
|
||
import javafx.stage.Stage;
|
||
import lombok.Setter;
|
||
import okhttp3.Call;
|
||
import okhttp3.Callback;
|
||
import okhttp3.Cookie;
|
||
import okhttp3.CookieJar;
|
||
import okhttp3.HttpUrl;
|
||
import okhttp3.MediaType;
|
||
import okhttp3.OkHttpClient;
|
||
import okhttp3.Request;
|
||
import okhttp3.RequestBody;
|
||
import okhttp3.Response;
|
||
import okhttp3.ResponseBody;
|
||
import okhttp3.WebSocket;
|
||
|
||
public class OkHttpLoginController implements MessageHolder {
|
||
private static final Logger logger = LoggerFactory.getLogger(OkHttpLoginController.class);
|
||
public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
|
||
|
||
@Setter
|
||
private MessageHolder holder;
|
||
@Setter
|
||
private Stage primaryStage;
|
||
@Setter
|
||
private MyProperties properties;
|
||
@Setter
|
||
private OkHttpClient httpClient;
|
||
private WebSocket webSocket;
|
||
private SimpleStringProperty serverUrl = new SimpleStringProperty();
|
||
private String webSocketUrl;
|
||
|
||
public OkHttpLoginController() {
|
||
this.httpClient = new OkHttpClient().newBuilder().cookieJar(new CookieJar() {
|
||
private final List<Cookie> cookies = new java.util.ArrayList<>();
|
||
|
||
@Override
|
||
public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
|
||
// 保存服务器返回的Cookie(如JSESSIONID)
|
||
this.cookies.addAll(cookies);
|
||
System.out.println("保存Cookie: " + cookies);
|
||
}
|
||
|
||
@Override
|
||
public List<Cookie> loadForRequest(HttpUrl url) {
|
||
// 请求时自动携带Cookie
|
||
return cookies;
|
||
}
|
||
|
||
}).build();
|
||
}
|
||
|
||
@Override
|
||
public void addMessage(Level level, String message) {
|
||
if (holder != null) {
|
||
holder.addMessage(level, message);
|
||
}
|
||
}
|
||
|
||
private void initServerUrls() {
|
||
String host = properties.getServerHost();
|
||
String port = properties.getServerPort();
|
||
serverUrl.set("http://" + host + ":" + port);
|
||
this.webSocketUrl = "ws://" + host + ":" + port + "/ws";
|
||
}
|
||
|
||
public CompletableFuture<Void> tryLogin() {
|
||
initServerUrls();
|
||
|
||
// 检查配置文件中是否保存用户名和密码
|
||
String userName = getUserName();
|
||
String password = getPassword();
|
||
|
||
if (StringUtils.hasText(userName) && StringUtils.hasText(password)) {
|
||
return login(userName, password);
|
||
} else {
|
||
CompletableFuture<Void> loginFuture = new CompletableFuture<>();
|
||
Platform.runLater(() -> {
|
||
showLoginDialog();
|
||
loginFuture.complete(null);
|
||
});
|
||
return loginFuture;
|
||
}
|
||
}
|
||
|
||
private String getUserName() {
|
||
return properties.getUserName();
|
||
}
|
||
|
||
private String getPassword() {
|
||
return properties.getPassword();
|
||
}
|
||
|
||
private void showLoginDialog() {
|
||
Stage stage = new Stage();
|
||
stage.initOwner(primaryStage);
|
||
stage.setTitle("系统登录");
|
||
|
||
// 创建 BorderPane 并设置边距
|
||
BorderPane borderPane = new BorderPane();
|
||
borderPane.setPadding(new Insets(20));
|
||
|
||
// 创建布局
|
||
GridPane grid = new GridPane();
|
||
grid.setHgap(10);
|
||
grid.setVgap(15);
|
||
|
||
Label hostLabel = new Label("服务器:");
|
||
TextField hostField = new TextField();
|
||
{
|
||
hostField.textProperty().bindBidirectional(serverUrl);
|
||
grid.add(hostLabel, 0, 0);
|
||
grid.add(hostField, 1, 0);
|
||
}
|
||
|
||
// 账户输入框
|
||
Label userLabel = new Label("用户名:");
|
||
TextField userField = new TextField();
|
||
{
|
||
String username = getUserName();
|
||
if (StringUtils.hasText(username)) {
|
||
userField.setText(username);
|
||
}
|
||
grid.add(userLabel, 0, 1);
|
||
grid.add(userField, 1, 1);
|
||
}
|
||
|
||
// 密码输入框
|
||
Label passwordLabel = new Label("密码:");
|
||
PasswordField passwordField = new PasswordField();
|
||
{
|
||
String password = getPassword();
|
||
if (StringUtils.hasText(password)) {
|
||
passwordField.setText(password);
|
||
}
|
||
grid.add(passwordLabel, 0, 2);
|
||
grid.add(passwordField, 1, 2);
|
||
}
|
||
|
||
// 记住密码复选框
|
||
CheckBox rememberCheckBox = new CheckBox("记住密码");
|
||
{
|
||
boolean remember = properties.isRememberPassword();
|
||
if (remember) {
|
||
rememberCheckBox.setSelected(true);
|
||
}
|
||
}
|
||
grid.add(rememberCheckBox, 1, 3);
|
||
|
||
// 错误消息提示
|
||
Label errorLabel = new Label();
|
||
errorLabel.setStyle("-fx-text-fill: red;");
|
||
errorLabel.setVisible(false);
|
||
grid.add(errorLabel, 0, 4);
|
||
GridPane.setColumnSpan(errorLabel, 2);
|
||
|
||
borderPane.setCenter(grid);
|
||
|
||
// 登录按钮
|
||
Button loginButton = new Button("登录");
|
||
loginButton.setDefaultButton(true);
|
||
loginButton.setPrefWidth(100);
|
||
borderPane.setBottom(loginButton);
|
||
BorderPane.setAlignment(loginButton, javafx.geometry.Pos.CENTER);
|
||
BorderPane.setMargin(loginButton, new Insets(20, 0, 0, 0));
|
||
|
||
// 登录按钮点击事件
|
||
loginButton.setOnAction(event -> {
|
||
String username = userField.getText();
|
||
String password = passwordField.getText();
|
||
boolean remember = rememberCheckBox.isSelected();
|
||
|
||
if (!StringUtils.hasText(username) || !StringUtils.hasText(password)) {
|
||
errorLabel.setText("用户名和密码不能为空");
|
||
errorLabel.setVisible(true);
|
||
return;
|
||
}
|
||
|
||
errorLabel.setVisible(false);
|
||
|
||
// 保存配置
|
||
if (remember) {
|
||
properties.setUserName(username);
|
||
properties.setPassword(password);
|
||
properties.setRememberPassword(true);
|
||
} else {
|
||
properties.setUserName(username);
|
||
properties.setPassword("");
|
||
properties.setRememberPassword(false);
|
||
}
|
||
properties.save();
|
||
|
||
// 执行登录
|
||
login(username, password).whenComplete((v, e) -> {
|
||
if (e != null) {
|
||
Platform.runLater(() -> {
|
||
errorLabel.setText(e.getMessage());
|
||
errorLabel.setVisible(true);
|
||
// showError("登录失败", e.getMessage());
|
||
});
|
||
return;
|
||
}
|
||
Platform.runLater(() -> {
|
||
stage.close();
|
||
});
|
||
});
|
||
});
|
||
|
||
// 创建场景并设置到窗口
|
||
Scene scene = new Scene(borderPane, 400, 280);
|
||
stage.setScene(scene);
|
||
stage.setResizable(false);
|
||
stage.showAndWait();
|
||
|
||
System.out.println("登录窗口关闭");
|
||
}
|
||
|
||
private CompletableFuture<Void> login(String username, String password) {
|
||
// 添加详细日志,记录服务器URL和请求准备情况
|
||
info("正在连接服务器: " + serverUrl.get());
|
||
logger.debug("用户名: {}", username);
|
||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||
|
||
try {
|
||
while (!SpringApp.isRunning()) {
|
||
holder.info("环境准备中,请稍后...");
|
||
Thread.sleep(1000);
|
||
}
|
||
|
||
ObjectMapper objectMapper = SpringApp.getBean(ObjectMapper.class);
|
||
ObjectNode objectNode = objectMapper.createObjectNode();
|
||
objectNode.put("username", username);
|
||
objectNode.put("password", password);
|
||
objectNode.put("type", "client");
|
||
|
||
// 将MacIP列表转换为Map<String, String>格式(MAC地址->IP地址)
|
||
List<MacIP> macIpList = getMacAndIP().join();
|
||
ObjectNode signNode = objectMapper.createObjectNode();
|
||
for (MacIP macIp : macIpList) {
|
||
signNode.put(macIp.mac, macIp.ip);
|
||
}
|
||
objectNode.set("sign", signNode);
|
||
|
||
// 构建JSON格式的登录请求
|
||
RequestBody body = RequestBody.create(objectNode.toString(), JSON);
|
||
|
||
// 构建并记录完整的请求URL
|
||
String loginUrl = serverUrl.get() + "/api/login";
|
||
logger.debug("构建登录请求URL: " + loginUrl);
|
||
Request request = new Request.Builder()
|
||
.url(loginUrl)
|
||
.post(body)
|
||
.build();
|
||
|
||
httpClient.newCall(request).enqueue(new Callback() {
|
||
@Override
|
||
public void onFailure(Call call, IOException e) {
|
||
future.completeExceptionally(
|
||
new IOException("登录失败: 无法连接到服务器,请检查网络连接或服务器配置 - " + e.getMessage(), e));
|
||
}
|
||
|
||
@Override
|
||
public void onResponse(Call call, Response response) throws IOException {
|
||
try {
|
||
if (!response.isSuccessful()) {
|
||
future.completeExceptionally(
|
||
new IOException("登录失败: 服务器返回错误码 - " + response.toString()));
|
||
return;
|
||
}
|
||
ResponseBody body = response.body();
|
||
System.out.println("contentType = " + body.contentType());
|
||
JsonNode jsonNode = objectMapper.readTree(body.string());
|
||
|
||
boolean success = jsonNode.get("success").asBoolean(false);
|
||
if (!success) {
|
||
future.completeExceptionally(
|
||
new IOException("登录失败: 服务器返回错误 - " + jsonNode.get("error").asText()));
|
||
return;
|
||
}
|
||
System.out.println("登录成功: " + jsonNode.toString());
|
||
// 登录成功后,调用新的API端点获取用户信息
|
||
|
||
Desktop.instance.setActiveEmployeeId(jsonNode.get("employeeId").asInt());
|
||
Desktop.instance.setSessionId(jsonNode.get("sessionId").asText());
|
||
future.complete(null);
|
||
|
||
} finally {
|
||
// 确保主响应体被关闭
|
||
response.close();
|
||
}
|
||
}
|
||
});
|
||
} catch (Exception e) {
|
||
future.completeExceptionally(new IOException("登录过程中发生错误: " + e.getMessage(), e));
|
||
}
|
||
return future;
|
||
}
|
||
|
||
CompletableFuture<List<MacIP>> getMacAndIP() {
|
||
return CompletableFuture.supplyAsync(() -> {
|
||
List<MacIP> list = new ArrayList<>();
|
||
try {
|
||
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
||
while (interfaces.hasMoreElements()) {
|
||
NetworkInterface anInterface = interfaces.nextElement();
|
||
if (anInterface.isLoopback() || !anInterface.isUp()) {
|
||
continue;
|
||
}
|
||
|
||
byte[] hardwareAddress = anInterface.getHardwareAddress();
|
||
if (hardwareAddress == null) {
|
||
continue;
|
||
}
|
||
|
||
Enumeration<InetAddress> 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 ? "-" : ""));
|
||
}
|
||
|
||
String mac = sb.toString();
|
||
while (inetAddresses.hasMoreElements()) {
|
||
InetAddress inetAddress = inetAddresses.nextElement();
|
||
if (inetAddress.getHostAddress().contains(":")) {
|
||
continue; // 跳过IPv6地址
|
||
}
|
||
list.add(new MacIP(mac, inetAddress.getHostAddress()));
|
||
}
|
||
}
|
||
} catch (SocketException e) {
|
||
throw new RuntimeException(e);
|
||
}
|
||
return list;
|
||
});
|
||
}
|
||
|
||
// WebSocket消息发送方法
|
||
public void sendMessage(String message) {
|
||
if (webSocket != null) {
|
||
webSocket.send(message);
|
||
}
|
||
}
|
||
|
||
// 关闭WebSocket连接
|
||
public void closeWebSocket() {
|
||
if (webSocket != null) {
|
||
webSocket.close(1000, "正常关闭");
|
||
webSocket = null;
|
||
}
|
||
}
|
||
|
||
static class MacIP {
|
||
String mac;
|
||
String ip;
|
||
|
||
public MacIP(String mac, String ip) {
|
||
this.mac = mac;
|
||
this.ip = ip;
|
||
}
|
||
}
|
||
} |