feat: 实现WebSocket通信框架及任务管理功能
新增WebSocket客户端和服务端通信框架,包括会话管理、心跳检测和自动重连机制 添加任务管理器用于处理WebSocket任务创建和执行 实现消息回调处理和错误处理机制 重构销售类型服务并添加缓存支持 移除旧的销售类型服务实现
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
package com.ecep.contract;
|
package com.ecep.contract;
|
||||||
|
|
||||||
|
import com.ecep.contract.constant.WebSocketConstant;
|
||||||
import com.ecep.contract.msg.SimpleMessage;
|
import com.ecep.contract.msg.SimpleMessage;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
@@ -24,6 +26,7 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebSocket消息服务
|
* WebSocket消息服务
|
||||||
@@ -49,6 +52,9 @@ public class WebSocketService {
|
|||||||
private SimpleBooleanProperty online = new SimpleBooleanProperty(false);
|
private SimpleBooleanProperty online = new SimpleBooleanProperty(false);
|
||||||
private SimpleStringProperty message = new SimpleStringProperty("");
|
private SimpleStringProperty message = new SimpleStringProperty("");
|
||||||
|
|
||||||
|
// 存储所有活跃的WebSocket会话
|
||||||
|
private final Map<String, WebSocketClientSession> sessions = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
// 存储所有活跃的WebSocket会话
|
// 存储所有活跃的WebSocket会话
|
||||||
private final Map<String, CompletableFuture<JsonNode>> callbacks = Collections.synchronizedMap(new HashMap<>());
|
private final Map<String, CompletableFuture<JsonNode>> callbacks = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
@@ -75,29 +81,23 @@ public class WebSocketService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
JsonNode node = objectMapper.readTree(text);
|
JsonNode node = objectMapper.readTree(text);
|
||||||
if (node.has("messageId")) {
|
if (node.has(WebSocketConstant.MESSAGE_ID_FIELD_NAME)) {
|
||||||
String messageId = node.get("messageId").asText();
|
String messageId = node.get(WebSocketConstant.MESSAGE_ID_FIELD_NAME).asText();
|
||||||
CompletableFuture<JsonNode> future = callbacks.remove(messageId);
|
CompletableFuture<JsonNode> future = callbacks.remove(messageId);
|
||||||
if (future != null) {
|
if (future != null) {
|
||||||
if (node.has("success")) {
|
onCallbackMessage(future, node);
|
||||||
if (!node.get("success").asBoolean()) {
|
|
||||||
future.completeExceptionally(
|
|
||||||
new RuntimeException("请求失败:来自服务器的消息=" + node.get("message").asText()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 使用具体类型后,这里不会再出现类型不匹配的错误
|
|
||||||
if (node.has("data")) {
|
|
||||||
future.complete(node.get("data"));
|
|
||||||
} else {
|
|
||||||
future.complete(node);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
logger.error("未找到对应的回调future: {}", messageId);
|
logger.error("未找到对应的回调future: {}", messageId);
|
||||||
}
|
}
|
||||||
} else if (node.has("errorCode")) {
|
} else if (node.has(WebSocketConstant.SESSION_ID_FIELD_NAME)) {
|
||||||
int errorCode = node.get("errorCode").asInt();
|
String sessionId = node.get(WebSocketConstant.SESSION_ID_FIELD_NAME).asText();
|
||||||
String errorMsg = node.get("message").asText();
|
WebSocketClientSession session = sessions.get(sessionId);
|
||||||
|
if (session != null) {
|
||||||
|
session.onMessage(node);
|
||||||
|
}
|
||||||
|
} else if (node.has(WebSocketConstant.ERROR_CODE_FIELD_NAME)) {
|
||||||
|
int errorCode = node.get(WebSocketConstant.ERROR_CODE_FIELD_NAME).asInt();
|
||||||
|
String errorMsg = node.get(WebSocketConstant.MESSAGE_FIELD_NAME).asText();
|
||||||
// TODO 需要重新登录
|
// TODO 需要重新登录
|
||||||
logger.error("收到错误消息: 错误码={}, 错误信息={}", errorCode, errorMsg);
|
logger.error("收到错误消息: 错误码={}, 错误信息={}", errorCode, errorMsg);
|
||||||
}
|
}
|
||||||
@@ -139,6 +139,22 @@ public class WebSocketService {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private void onCallbackMessage(CompletableFuture<JsonNode> future, JsonNode node) {
|
||||||
|
if (node.has(WebSocketConstant.SUCCESS_FIELD_VALUE)) {
|
||||||
|
if (!node.get(WebSocketConstant.SUCCESS_FIELD_VALUE).asBoolean()) {
|
||||||
|
future.completeExceptionally(
|
||||||
|
new RuntimeException("请求失败:来自服务器的消息=" + node.get(WebSocketConstant.MESSAGE_FIELD_NAME).asText()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 使用具体类型后,这里不会再出现类型不匹配的错误
|
||||||
|
if (node.has("data")) {
|
||||||
|
future.complete(node.get("data"));
|
||||||
|
} else {
|
||||||
|
future.complete(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void send(String string) {
|
public void send(String string) {
|
||||||
if (webSocket != null && webSocket.send(string)) {
|
if (webSocket != null && webSocket.send(string)) {
|
||||||
logger.debug("send message success:{}", string);
|
logger.debug("send message success:{}", string);
|
||||||
@@ -147,6 +163,10 @@ public class WebSocketService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void send(Object message) throws JsonProcessingException {
|
||||||
|
send(objectMapper.writeValueAsString(message));
|
||||||
|
}
|
||||||
|
|
||||||
public CompletableFuture<JsonNode> send(SimpleMessage msg) {
|
public CompletableFuture<JsonNode> send(SimpleMessage msg) {
|
||||||
CompletableFuture<JsonNode> future = new CompletableFuture<>();
|
CompletableFuture<JsonNode> future = new CompletableFuture<>();
|
||||||
try {
|
try {
|
||||||
@@ -168,6 +188,14 @@ public class WebSocketService {
|
|||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<JsonNode> invoke(String service, String method, Object... params) {
|
||||||
|
SimpleMessage msg = new SimpleMessage();
|
||||||
|
msg.setService(service);
|
||||||
|
msg.setMethod(method);
|
||||||
|
msg.setArguments(params);
|
||||||
|
return send(msg).orTimeout(getReadTimeout(), TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
public void initWebSocket() {
|
public void initWebSocket() {
|
||||||
isActive = true;
|
isActive = true;
|
||||||
OkHttpClient httpClient = Desktop.instance.getHttpClient();
|
OkHttpClient httpClient = Desktop.instance.getHttpClient();
|
||||||
@@ -278,4 +306,25 @@ public class WebSocketService {
|
|||||||
return online;
|
return online;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void withSession(Consumer<WebSocketClientSession> sessionConsumer) {
|
||||||
|
WebSocketClientSession session = createSession();
|
||||||
|
try {
|
||||||
|
sessionConsumer.accept(session);
|
||||||
|
} finally {
|
||||||
|
// closeSession(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeSession(WebSocketClientSession session) {
|
||||||
|
if (session != null) {
|
||||||
|
sessions.remove(session.getSessionId());
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private WebSocketClientSession createSession() {
|
||||||
|
WebSocketClientSession session = new WebSocketClientSession(this);
|
||||||
|
sessions.put(session.getSessionId(), session);
|
||||||
|
return session;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package com.ecep.contract;
|
||||||
|
|
||||||
|
import com.ecep.contract.constant.WebSocketConstant;
|
||||||
|
import com.ecep.contract.task.Tasker;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class WebSocketSession {
|
||||||
|
@Getter
|
||||||
|
private String sessionId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
private WebSocketClientTasker tasker;
|
||||||
|
|
||||||
|
private final WebSocketService webSocketService;
|
||||||
|
|
||||||
|
public WebSocketSession(WebSocketService webSocketService) {
|
||||||
|
this.webSocketService = webSocketService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void submitTask(WebSocketClientTasker tasker, Object... args) throws JsonProcessingException {
|
||||||
|
Map<String, Object> argments = Map.of(
|
||||||
|
WebSocketConstant.SESSION_ID_FIELD_NAME, getSessionId(),
|
||||||
|
"type", "createTask",
|
||||||
|
"taskName", tasker.getTaskName(),
|
||||||
|
"args", args);
|
||||||
|
webSocketService.send(argments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onMessage(JsonNode node) {
|
||||||
|
if (node.has("type")) {
|
||||||
|
String type = node.get("type").asText();
|
||||||
|
if (type.equals("message")) {
|
||||||
|
JsonNode args = node.get("args");
|
||||||
|
String message = args.get(1).asText();
|
||||||
|
String level = args.get(0).asText();
|
||||||
|
if (tasker instanceof Tasker<?> t) {
|
||||||
|
t.updateMessage(java.util.logging.Level.parse(level), message);
|
||||||
|
}
|
||||||
|
} else if (type.equals("title")) {
|
||||||
|
JsonNode args = node.get("args");
|
||||||
|
String message = args.get(0).asText();
|
||||||
|
if (tasker instanceof Tasker<?> t) {
|
||||||
|
t.updateTitle(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.ecep.contract;
|
||||||
|
|
||||||
|
public interface WebSocketClientTasker {
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.ecep.contract.service;
|
||||||
|
|
||||||
|
public class CompanyCustomerFileTypeService {
|
||||||
|
}
|
||||||
@@ -4,10 +4,14 @@ import java.util.List;
|
|||||||
|
|
||||||
import com.ecep.contract.model.Company;
|
import com.ecep.contract.model.Company;
|
||||||
import com.ecep.contract.model.CompanyInvoiceInfo;
|
import com.ecep.contract.model.CompanyInvoiceInfo;
|
||||||
|
import com.ecep.contract.service.QueryService;
|
||||||
import com.ecep.contract.service.ViewModelService;
|
import com.ecep.contract.service.ViewModelService;
|
||||||
import com.ecep.contract.vm.CompanyInvoiceInfoViewModel;
|
import com.ecep.contract.vm.CompanyInvoiceInfoViewModel;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
public class CompanyInvoiceInfoService implements ViewModelService<CompanyInvoiceInfo, CompanyInvoiceInfoViewModel> {
|
@Service
|
||||||
|
|
||||||
|
public class CompanyInvoiceInfoService extends QueryService<CompanyInvoiceInfo, CompanyInvoiceInfoViewModel> {
|
||||||
|
|
||||||
public List<CompanyInvoiceInfo> searchByCompany(Company company, String searchText) {
|
public List<CompanyInvoiceInfo> searchByCompany(Company company, String searchText) {
|
||||||
throw new UnsupportedOperationException("未实现");
|
throw new UnsupportedOperationException("未实现");
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.ecep.contract.service;
|
||||||
|
|
||||||
|
public class CompanyVendorFileTypeService {
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.ecep.contract.service;
|
||||||
|
|
||||||
|
public class HolidayService {
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package com.ecep.contract.service;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import com.ecep.contract.model.ProjectSaleType;
|
|
||||||
import com.ecep.contract.vm.ProjectSaleTypeViewModel;
|
|
||||||
|
|
||||||
@Service
|
|
||||||
public class SaleTypeService extends QueryService<ProjectSaleType, ProjectSaleTypeViewModel> {
|
|
||||||
|
|
||||||
public ProjectSaleType findByName(String name) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'findByName'");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.ecep.contract.vm;
|
||||||
|
|
||||||
|
public class CompanyCustomerFileTypeLocalViewModel {
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.ecep.contract.vm;
|
||||||
|
|
||||||
|
public class CompanyVendorFileTypeLocalViewModel {
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.ecep.contract.vm;
|
||||||
|
|
||||||
|
public class EnumViewModel {
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.ecep.contract.constant;
|
||||||
|
|
||||||
|
public class WebSocketConstant {
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.ecep.contract.model;
|
||||||
|
|
||||||
|
public class SalesOrderInvoice {
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.ecep.contract;
|
||||||
|
|
||||||
|
public class EntityService {
|
||||||
|
}
|
||||||
@@ -2,6 +2,10 @@ package com.ecep.contract.ds.project.service;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.ecep.contract.QueryService;
|
||||||
|
import com.ecep.contract.model.CompanyBankAccount;
|
||||||
|
import com.ecep.contract.util.SpecificationUtils;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.cache.annotation.CacheConfig;
|
import org.springframework.cache.annotation.CacheConfig;
|
||||||
import org.springframework.cache.annotation.CacheEvict;
|
import org.springframework.cache.annotation.CacheEvict;
|
||||||
@@ -21,7 +25,7 @@ import com.ecep.contract.model.ProjectSaleType;
|
|||||||
@Lazy
|
@Lazy
|
||||||
@Service
|
@Service
|
||||||
@CacheConfig(cacheNames = "sale-type")
|
@CacheConfig(cacheNames = "sale-type")
|
||||||
public class SaleTypeService implements IEntityService<ProjectSaleType> {
|
public class SaleTypeService implements IEntityService<ProjectSaleType>, QueryService<ProjectSaleType> {
|
||||||
@Lazy
|
@Lazy
|
||||||
@Autowired
|
@Autowired
|
||||||
private ProjectSaleTypeRepository saleTypeRepository;
|
private ProjectSaleTypeRepository saleTypeRepository;
|
||||||
@@ -49,6 +53,17 @@ public class SaleTypeService implements IEntityService<ProjectSaleType> {
|
|||||||
return saleTypeRepository.findAll(spec, pageable);
|
return saleTypeRepository.findAll(spec, pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<ProjectSaleType> findAll(JsonNode paramsNode, Pageable pageable) {
|
||||||
|
Specification<ProjectSaleType> spec = null;
|
||||||
|
if (paramsNode.has("searchText")) {
|
||||||
|
spec = getSpecification(paramsNode.get("searchText").asText());
|
||||||
|
}
|
||||||
|
// field
|
||||||
|
spec = SpecificationUtils.andFieldEqualParam(spec, paramsNode, "active");
|
||||||
|
return findAll(spec, pageable);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Specification<ProjectSaleType> getSpecification(String searchText) {
|
public Specification<ProjectSaleType> getSpecification(String searchText) {
|
||||||
if (!StringUtils.hasText(searchText)) {
|
if (!StringUtils.hasText(searchText)) {
|
||||||
@@ -1,6 +1,29 @@
|
|||||||
package com.ecep.contract.handler;
|
package com.ecep.contract.handler;
|
||||||
|
|
||||||
|
import com.ecep.contract.*;
|
||||||
|
import com.ecep.contract.constant.WebSocketConstant;
|
||||||
|
import com.ecep.contract.ds.other.service.EmployeeLoginHistoryService;
|
||||||
|
import com.ecep.contract.ds.other.service.EmployeeService;
|
||||||
|
import com.ecep.contract.model.Employee;
|
||||||
|
import com.ecep.contract.model.EmployeeLoginHistory;
|
||||||
|
import com.ecep.contract.service.WebSocketServerTaskManager;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.socket.*;
|
||||||
|
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
@@ -14,37 +37,6 @@ import java.util.concurrent.ScheduledExecutorService;
|
|||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.data.domain.Page;
|
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.socket.BinaryMessage;
|
|
||||||
import org.springframework.web.socket.CloseStatus;
|
|
||||||
import org.springframework.web.socket.PingMessage;
|
|
||||||
import org.springframework.web.socket.PongMessage;
|
|
||||||
import org.springframework.web.socket.TextMessage;
|
|
||||||
import org.springframework.web.socket.WebSocketSession;
|
|
||||||
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
|
||||||
|
|
||||||
import com.ecep.contract.IEntityService;
|
|
||||||
import com.ecep.contract.PageArgument;
|
|
||||||
import com.ecep.contract.PageContent;
|
|
||||||
import com.ecep.contract.QueryService;
|
|
||||||
import com.ecep.contract.SpringApp;
|
|
||||||
import com.ecep.contract.ds.other.service.EmployeeLoginHistoryService;
|
|
||||||
import com.ecep.contract.ds.other.service.EmployeeService;
|
|
||||||
import com.ecep.contract.model.Employee;
|
|
||||||
import com.ecep.contract.model.EmployeeLoginHistory;
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebSocket处理器
|
* WebSocket处理器
|
||||||
* 处理与客户端的WebSocket连接、消息传递和断开连接
|
* 处理与客户端的WebSocket连接、消息传递和断开连接
|
||||||
@@ -64,6 +56,8 @@ public class WebSocketHandler extends TextWebSocketHandler {
|
|||||||
private EmployeeService employeeService;
|
private EmployeeService employeeService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ScheduledExecutorService scheduledExecutorService;
|
private ScheduledExecutorService scheduledExecutorService;
|
||||||
|
@Autowired
|
||||||
|
private WebSocketServerTaskManager taskManager;
|
||||||
|
|
||||||
// 存储所有活跃的WebSocket会话
|
// 存储所有活跃的WebSocket会话
|
||||||
private final Map<Integer, SessionInfo> activeSessions = Collections.synchronizedMap(new HashMap<>());
|
private final Map<Integer, SessionInfo> activeSessions = Collections.synchronizedMap(new HashMap<>());
|
||||||
@@ -101,21 +95,18 @@ public class WebSocketHandler extends TextWebSocketHandler {
|
|||||||
sessionInfo.setEmployeeId((Integer) session.getAttributes().get("employeeId"));
|
sessionInfo.setEmployeeId((Integer) session.getAttributes().get("employeeId"));
|
||||||
|
|
||||||
if (sessionInfo.getEmployeeId() == null) {
|
if (sessionInfo.getEmployeeId() == null) {
|
||||||
logger.error("会话未绑定用户: " + session.getId());
|
logger.error("会话未绑定用户: {}", session.getId());
|
||||||
sendError(session, 401, "会话未绑定用户");
|
sendError(session, 401, "会话未绑定用户");
|
||||||
session.close();
|
session.close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
activeSessions.put(sessionInfo.getEmployeeId(), sessionInfo);
|
activeSessions.put(sessionInfo.getEmployeeId(), sessionInfo);
|
||||||
System.out.println(sessionInfo.getLoginHistoryId());
|
logger.info("WebSocket连接已建立: {}", session.getId());
|
||||||
System.out.println(sessionInfo.getEmployeeId());
|
|
||||||
|
|
||||||
logger.info("WebSocket连接已建立: " + session.getId());
|
|
||||||
|
|
||||||
Employee employee = employeeService.findById(sessionInfo.getEmployeeId());
|
Employee employee = employeeService.findById(sessionInfo.getEmployeeId());
|
||||||
if (employee == null) {
|
if (employee == null) {
|
||||||
logger.error("未找到用户: #" + sessionInfo.getEmployeeId());
|
logger.error("未找到用户: #{}", sessionInfo.getEmployeeId());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,30 +114,6 @@ public class WebSocketHandler extends TextWebSocketHandler {
|
|||||||
sessionInfo.setSchedule(schedule);
|
sessionInfo.setSchedule(schedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendError(WebSocketSession session, int errorCode, String message) {
|
|
||||||
if (session == null || !session.isOpen()) {
|
|
||||||
logger.warn("尝试向已关闭的WebSocket会话发送错误消息: {}", message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
ObjectNode objectNode = objectMapper.createObjectNode();
|
|
||||||
objectNode.put("errorCode", errorCode);
|
|
||||||
objectNode.put("success", false);
|
|
||||||
objectNode.put("message", message);
|
|
||||||
String errorMessage = objectMapper.writeValueAsString(objectNode);
|
|
||||||
|
|
||||||
// 检查会话状态并尝试发送错误消息
|
|
||||||
if (session.isOpen()) {
|
|
||||||
session.sendMessage(new TextMessage(errorMessage));
|
|
||||||
} else {
|
|
||||||
logger.warn("会话已关闭,无法发送错误消息: {}", message);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// 捕获所有可能的异常,防止影响主流程
|
|
||||||
logger.error("发送错误消息失败 (会话ID: {})", session.getId(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 接收文本消息时调用
|
* 接收文本消息时调用
|
||||||
@@ -154,8 +121,9 @@ public class WebSocketHandler extends TextWebSocketHandler {
|
|||||||
@Override
|
@Override
|
||||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
|
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
|
||||||
String payload = message.getPayload();
|
String payload = message.getPayload();
|
||||||
logger.info("收到来自客户端的消息: " + payload + " (会话ID: " + session.getId() + ")");
|
if (logger.isInfoEnabled()) {
|
||||||
|
logger.info("收到来自客户端的消息: {} (会话ID: {})", payload, session.getId());
|
||||||
|
}
|
||||||
// 处理文本格式的ping消息
|
// 处理文本格式的ping消息
|
||||||
if ("ping".equals(payload)) {
|
if ("ping".equals(payload)) {
|
||||||
// 回复文本格式的pong消息
|
// 回复文本格式的pong消息
|
||||||
@@ -168,7 +136,8 @@ public class WebSocketHandler extends TextWebSocketHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handleAsMessageCallback(session, payload)) {
|
// 尝试将消息作为JSON处理
|
||||||
|
if (handleAsJson(session, payload)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,10 +145,10 @@ public class WebSocketHandler extends TextWebSocketHandler {
|
|||||||
logger.info("处理普通消息: " + payload);
|
logger.info("处理普通消息: " + payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleAsMessageCallback(WebSocketSession session, String payload) {
|
private boolean handleAsJson(WebSocketSession session, String payload) {
|
||||||
if (session == null || !session.isOpen()) {
|
if (!session.isOpen()) {
|
||||||
logger.warn("尝试在已关闭的WebSocket会话上处理消息回调");
|
logger.warn("尝试在已关闭的WebSocket会话上处理消息回调");
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonNode jsonNode = null;
|
JsonNode jsonNode = null;
|
||||||
@@ -189,73 +158,98 @@ public class WebSocketHandler extends TextWebSocketHandler {
|
|||||||
logger.warn("解析消息回调JSON失败: {}", payload, e);
|
logger.warn("解析消息回调JSON失败: {}", payload, e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (jsonNode.has(WebSocketConstant.MESSAGE_ID_FIELD_NAME)) {
|
||||||
if (!jsonNode.has("messageId")) {
|
// 处理 messageId 的消息
|
||||||
// 没有 messageId 的消息不处理
|
String messageId = jsonNode.get(WebSocketConstant.MESSAGE_ID_FIELD_NAME).asText();
|
||||||
return false;
|
try {
|
||||||
}
|
handleAsMessageCallback(session, messageId, jsonNode);
|
||||||
String messageId = jsonNode.get("messageId").asText();
|
} catch (Exception e) {
|
||||||
|
sendError(session, messageId, e.getMessage());
|
||||||
if (!jsonNode.has("service")) {
|
logger.warn("处理消息回调失败 (消息ID: {}): {}", messageId, e.getMessage(), e);
|
||||||
sendError(session, messageId, "缺失 service 参数");
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (jsonNode.has(WebSocketConstant.SESSION_ID_FIELD_NAME)) {
|
||||||
|
taskManager.onMessage(session, jsonNode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
String serviceName = jsonNode.get("service").asText();
|
|
||||||
|
private void handleAsMessageCallback(WebSocketSession session, String messageId, JsonNode jsonNode) throws Exception {
|
||||||
|
if (!jsonNode.has(WebSocketConstant.SERVICE_FIELD_NAME)) {
|
||||||
|
throw new IllegalArgumentException("缺失 service 参数");
|
||||||
|
}
|
||||||
|
|
||||||
|
String serviceName = jsonNode.get(WebSocketConstant.SERVICE_FIELD_NAME).asText();
|
||||||
Object service = null;
|
Object service = null;
|
||||||
try {
|
try {
|
||||||
service = SpringApp.getBean(serviceName);
|
service = SpringApp.getBean(serviceName);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
sendError(session, messageId, "未找到服务: " + serviceName);
|
throw new IllegalArgumentException("未找到服务: " + serviceName);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!jsonNode.has("method")) {
|
if (!jsonNode.has(WebSocketConstant.METHOD_FIELD_NAME)) {
|
||||||
sendError(session, messageId, "缺失 method 参数");
|
throw new IllegalArgumentException("缺失 method 参数");
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String methodName = jsonNode.get("method").asText();
|
String methodName = jsonNode.get(WebSocketConstant.METHOD_FIELD_NAME).asText();
|
||||||
|
JsonNode argumentsNode = jsonNode.get("arguments");
|
||||||
|
|
||||||
|
Object result = null;
|
||||||
|
if (methodName.equals("findAll")) {
|
||||||
|
result = invokerFindAllMethod(service, argumentsNode);
|
||||||
|
} else if (methodName.equals("findById")) {
|
||||||
|
result = invokerFindByIdMethod(service, argumentsNode);
|
||||||
|
} else if (methodName.equals("save")) {
|
||||||
|
result = invokerSaveMethod(service, argumentsNode);
|
||||||
|
} else if (methodName.equals("delete")) {
|
||||||
|
result = invokerDeleteMethod(service, argumentsNode);
|
||||||
|
} else {
|
||||||
|
result = invokerOtherMethod(service, methodName, argumentsNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
String response = objectMapper.writeValueAsString(Map.of(
|
||||||
|
WebSocketConstant.MESSAGE_ID_FIELD_NAME, messageId,
|
||||||
|
WebSocketConstant.SUCCESS_FIELD_VALUE, true,
|
||||||
|
"data", result
|
||||||
|
));
|
||||||
|
session.sendMessage(new TextMessage(response));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object invokerOtherMethod(Object service, String methodName, JsonNode argumentsNode)
|
||||||
|
throws NoSuchMethodException, SecurityException, InvocationTargetException, IllegalAccessException, ClassNotFoundException, JsonProcessingException {
|
||||||
|
int size = argumentsNode.size();
|
||||||
|
if (size == 0) {
|
||||||
|
Method method = service.getClass().getMethod(methodName);
|
||||||
|
return method.invoke(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 参数值
|
||||||
|
JsonNode paramsNode = argumentsNode.get(0);
|
||||||
|
// 参数类型
|
||||||
|
JsonNode typesNode = argumentsNode.get(1);
|
||||||
|
|
||||||
|
Class<?>[] parameterTypes = new Class<?>[typesNode.size()];
|
||||||
|
Object[] args = new Object[paramsNode.size()];
|
||||||
|
for (int i = 0; i < typesNode.size(); i++) {
|
||||||
|
String type = typesNode.get(i).asText();
|
||||||
|
parameterTypes[i] = Class.forName(type);
|
||||||
|
args[i] = objectMapper.treeToValue(paramsNode.get(i), parameterTypes[i]);
|
||||||
|
}
|
||||||
|
Class<?> targetClass = getTargetClass(service.getClass());
|
||||||
try {
|
try {
|
||||||
Object result = null;
|
Method method = targetClass.getMethod(methodName, parameterTypes);
|
||||||
if (methodName.equals("findAll")) {
|
return method.invoke(service, args);
|
||||||
result = invokerFindAllMethod(service, jsonNode.get("arguments"));
|
} catch (NoSuchMethodException e) {
|
||||||
} else if (methodName.equals("findById")) {
|
logger.error("targetClass: {}, Methods:{}", targetClass, targetClass.getMethods());
|
||||||
result = invokerFindByIdMethod(service, jsonNode.get("arguments"));
|
throw e;
|
||||||
} else if (methodName.equals("save")) {
|
|
||||||
result = invokerSaveMethod(service, jsonNode.get("arguments"));
|
|
||||||
} else if (methodName.equals("delete")) {
|
|
||||||
result = invokerDeleteMethod(service, jsonNode.get("arguments"));
|
|
||||||
} else {
|
|
||||||
sendError(session, messageId, "未实现的方法: " + methodName);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 再次检查会话状态
|
|
||||||
if (!session.isOpen()) {
|
|
||||||
logger.warn("会话已关闭,无法发送处理结果 (消息ID: {})");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectNode objectNode = objectMapper.createObjectNode();
|
|
||||||
objectNode.put("messageId", messageId);
|
|
||||||
objectNode.set("data", objectMapper.valueToTree(result));
|
|
||||||
String response = objectMapper.writeValueAsString(objectNode);
|
|
||||||
session.sendMessage(new TextMessage(response));
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
// 防止重复发送消息导致的TEXT_PARTIAL_WRITING异常
|
|
||||||
if (!session.isOpen()) {
|
|
||||||
logger.warn("会话已关闭,无法发送错误消息 (消息ID: {})");
|
|
||||||
} else {
|
|
||||||
sendError(session, messageId, e.getMessage());
|
|
||||||
}
|
|
||||||
logger.error("处理消息回调失败 (消息ID: {})", messageId, e);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private Object invokerDeleteMethod(Object service, JsonNode argumentsNode) {
|
private Object invokerDeleteMethod(Object service, JsonNode argumentsNode) {
|
||||||
JsonNode paramsNode = argumentsNode.get(0);
|
JsonNode paramsNode = argumentsNode.get(0);
|
||||||
if (!paramsNode.has("id")) {
|
if (!paramsNode.has("id")) {
|
||||||
@@ -445,6 +439,7 @@ public class WebSocketHandler extends TextWebSocketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
JsonNode typesNode = argumentsNode.get(1);
|
||||||
if (paramsNode.isInt()) {
|
if (paramsNode.isInt()) {
|
||||||
Method method = service.getClass().getMethod("findById", Integer.class);
|
Method method = service.getClass().getMethod("findById", Integer.class);
|
||||||
return method.invoke(service, paramsNode.asInt());
|
return method.invoke(service, paramsNode.asInt());
|
||||||
@@ -469,17 +464,22 @@ public class WebSocketHandler extends TextWebSocketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void sendError(WebSocketSession session, String messageId, String message) {
|
private void sendError(WebSocketSession session, String messageId, String message) {
|
||||||
|
_sendError(session, WebSocketConstant.MESSAGE_ID_FIELD_NAME, messageId, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void _sendError(WebSocketSession session, String fieldName, String messageId, String message) {
|
||||||
if (session == null || !session.isOpen()) {
|
if (session == null || !session.isOpen()) {
|
||||||
logger.warn("尝试向已关闭的WebSocket会话发送错误消息: {}", message);
|
logger.warn("尝试向已关闭的WebSocket会话发送错误消息: {}", message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ObjectNode objectNode = objectMapper.createObjectNode();
|
String errorMessage = objectMapper.writeValueAsString(Map.of(
|
||||||
objectNode.put("messageId", messageId);
|
fieldName, messageId,
|
||||||
objectNode.put("success", false);
|
WebSocketConstant.SUCCESS_FIELD_VALUE, false,
|
||||||
objectNode.put("message", message);
|
WebSocketConstant.MESSAGE_FIELD_NAME, message
|
||||||
String errorMessage = objectMapper.writeValueAsString(objectNode);
|
));
|
||||||
|
|
||||||
// 检查会话状态并尝试发送错误消息
|
// 检查会话状态并尝试发送错误消息
|
||||||
if (session.isOpen()) {
|
if (session.isOpen()) {
|
||||||
@@ -505,6 +505,19 @@ public class WebSocketHandler extends TextWebSocketHandler {
|
|||||||
@Override
|
@Override
|
||||||
protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
|
protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception {
|
||||||
logger.info("收到来自客户端的Pong消息: " + message.getPayload() + " (会话ID: " + session.getId() + ")");
|
logger.info("收到来自客户端的Pong消息: " + message.getPayload() + " (会话ID: " + session.getId() + ")");
|
||||||
|
|
||||||
|
// 从活跃会话集合中移除会话
|
||||||
|
SessionInfo sessionInfo = activeSessions.get((Integer) session.getAttributes().get("employeeId"));
|
||||||
|
if (sessionInfo == null) {
|
||||||
|
logger.warn("收到来自客户端的Pong消息,但会话不存在: " + session.getId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Integer loginHistoryId = sessionInfo.getLoginHistoryId();
|
||||||
|
if (loginHistoryId != null) {
|
||||||
|
EmployeeLoginHistory history = employeeLoginHistoryService.findById(loginHistoryId);
|
||||||
|
history.setActiveTime(LocalDateTime.now());
|
||||||
|
employeeLoginHistoryService.save(history);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -516,7 +529,7 @@ public class WebSocketHandler extends TextWebSocketHandler {
|
|||||||
logger.info(
|
logger.info(
|
||||||
"WebSocket连接已关闭: " + session.getId() + ", 状态码: " + status.getCode() + ", 原因: " + status.getReason());
|
"WebSocket连接已关闭: " + session.getId() + ", 状态码: " + status.getCode() + ", 原因: " + status.getReason());
|
||||||
// 从活跃会话集合中移除会话
|
// 从活跃会话集合中移除会话
|
||||||
SessionInfo sessionInfo = activeSessions.remove(session.getAttributes().get("employeeId"));
|
SessionInfo sessionInfo = activeSessions.remove((Integer) session.getAttributes().get("employeeId"));
|
||||||
if (sessionInfo == null) {
|
if (sessionInfo == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -572,4 +585,29 @@ public class WebSocketHandler extends TextWebSocketHandler {
|
|||||||
public int getActiveSessionCount() {
|
public int getActiveSessionCount() {
|
||||||
return activeSessions.size();
|
return activeSessions.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendError(WebSocketSession session, int errorCode, String message) {
|
||||||
|
if (session == null || !session.isOpen()) {
|
||||||
|
logger.warn("尝试向已关闭的WebSocket会话发送错误消息: {}", message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ObjectNode objectNode = objectMapper.createObjectNode();
|
||||||
|
objectNode.put(WebSocketConstant.ERROR_CODE_FIELD_NAME, errorCode);
|
||||||
|
objectNode.put(WebSocketConstant.SUCCESS_FIELD_VALUE, false);
|
||||||
|
objectNode.put(WebSocketConstant.MESSAGE_FIELD_NAME, message);
|
||||||
|
String errorMessage = objectMapper.writeValueAsString(objectNode);
|
||||||
|
|
||||||
|
// 检查会话状态并尝试发送错误消息
|
||||||
|
if (session.isOpen()) {
|
||||||
|
session.sendMessage(new TextMessage(errorMessage));
|
||||||
|
} else {
|
||||||
|
logger.warn("会话已关闭,无法发送错误消息: {}", message);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 捕获所有可能的异常,防止影响主流程
|
||||||
|
logger.error("发送错误消息失败 (会话ID: {})", session.getId(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package com.ecep.contract.service;
|
||||||
|
|
||||||
|
import com.ecep.contract.Message;
|
||||||
|
import com.ecep.contract.constant.WebSocketConstant;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.socket.TextMessage;
|
||||||
|
import org.springframework.web.socket.WebSocketSession;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class WebSocketTaskManager implements InitializingBean {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(WebSocketTaskManager.class);
|
||||||
|
@Autowired
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
@Autowired
|
||||||
|
private ScheduledExecutorService scheduledExecutorService;
|
||||||
|
private Map<String, String> taskClzMap = Map.of();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
taskClzMap = Map.of(
|
||||||
|
"ContractSyncTask", "com.ecep.contract.cloud.u8.ContractSyncTask",
|
||||||
|
"ContractRepairTask", "com.ecep.contract.ds.contract.tasker.ContractRepairTask"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onMessage(WebSocketSession session, JsonNode jsonNode) {
|
||||||
|
// 处理 sessionId 的消息
|
||||||
|
String sessionId = jsonNode.get(WebSocketConstant.SESSION_ID_FIELD_NAME).asText();
|
||||||
|
try {
|
||||||
|
handleAsSessionCallback(session, sessionId, jsonNode);
|
||||||
|
} catch (Exception e) {
|
||||||
|
sendError(session, sessionId, e.getMessage());
|
||||||
|
logger.warn("处理会话回调失败 (会话ID: {}): {}", sessionId, e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void handleAsSessionCallback(WebSocketSession session, String sessionId, JsonNode jsonNode) {
|
||||||
|
if (!jsonNode.has("type")) {
|
||||||
|
throw new IllegalArgumentException("缺失 type 参数");
|
||||||
|
}
|
||||||
|
String type = jsonNode.get("type").asText();
|
||||||
|
if (type.equals("createTask")) {
|
||||||
|
createTask(session, sessionId, jsonNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTask(WebSocketSession session, String sessionId, JsonNode jsonNode) {
|
||||||
|
if (!jsonNode.has("taskName")) {
|
||||||
|
throw new IllegalArgumentException("缺失 taskName 参数");
|
||||||
|
}
|
||||||
|
String taskName = jsonNode.get("taskName").asText();
|
||||||
|
|
||||||
|
String clzName = taskClzMap.get(taskName);
|
||||||
|
if (clzName == null) {
|
||||||
|
throw new IllegalArgumentException("未知的任务类型: " + taskName);
|
||||||
|
}
|
||||||
|
Object tasker = null;
|
||||||
|
try {
|
||||||
|
Class<?> clz = Class.forName(clzName);
|
||||||
|
tasker = clz.getDeclaredConstructor().newInstance();
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
throw new IllegalArgumentException("未知的任务类型: " + taskName + ", class: " + clzName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException("任务类型: " + taskName + ", class: " + clzName + " 实例化失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tasker instanceof WebSocketServerTasker t) {
|
||||||
|
t.setTitleHandler(title -> sendToSession(session, sessionId, "title", title));
|
||||||
|
t.setMessageHandler(msg -> sendMessageToSession(session, sessionId, msg));
|
||||||
|
t.init(jsonNode);
|
||||||
|
scheduledExecutorService.submit(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sendMessageToSession(WebSocketSession session, String sessionId, Message msg) {
|
||||||
|
return sendToSession(session, sessionId, "message", msg.getLevel().getName(), msg.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sendToSession(WebSocketSession session, String sessionId, String type, Object... args) {
|
||||||
|
try {
|
||||||
|
String text = objectMapper.writeValueAsString(Map.of(
|
||||||
|
WebSocketConstant.SESSION_ID_FIELD_NAME, sessionId,
|
||||||
|
"type", type,
|
||||||
|
"args", args
|
||||||
|
));
|
||||||
|
session.sendMessage(new TextMessage(text));
|
||||||
|
} catch (IOException e) {
|
||||||
|
// 捕获所有可能的异常,防止影响主流程
|
||||||
|
logger.error("发送错误消息失败 (会话ID: {})", session.getId(), e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void sendError(WebSocketSession session, String sessionId, String message) {
|
||||||
|
if (session == null || !session.isOpen()) {
|
||||||
|
logger.warn("尝试向已关闭的WebSocket会话发送错误消息: {}", message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String errorMessage = objectMapper.writeValueAsString(Map.of(
|
||||||
|
WebSocketConstant.SESSION_ID_FIELD_NAME, sessionId,
|
||||||
|
WebSocketConstant.SUCCESS_FIELD_VALUE, false,
|
||||||
|
WebSocketConstant.MESSAGE_FIELD_NAME, message
|
||||||
|
));
|
||||||
|
|
||||||
|
// 检查会话状态并尝试发送错误消息
|
||||||
|
if (session.isOpen()) {
|
||||||
|
session.sendMessage(new TextMessage(errorMessage));
|
||||||
|
} else {
|
||||||
|
logger.warn("会话已关闭,无法发送错误消息: {}", message);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 捕获所有可能的异常,防止影响主流程
|
||||||
|
logger.error("发送错误消息失败 (会话ID: {})", session.getId(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.ecep.contract.service;
|
||||||
|
|
||||||
|
import com.ecep.contract.Message;
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
public interface WebSocketTasker extends Callable<Object> {
|
||||||
|
void setMessageHandler(Predicate<Message> messageHandler);
|
||||||
|
|
||||||
|
void setTitleHandler(Predicate<String> titleHandler);
|
||||||
|
|
||||||
|
void init(JsonNode jsonNode);
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package com.ecep.contract.ui;
|
||||||
|
|
||||||
|
public class MessageHolderImpl {
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user