diff --git a/modules/core/src/main/java/com/bytedesk/core/socket/connection/ConnectionEventListener.java b/modules/core/src/main/java/com/bytedesk/core/socket/connection/ConnectionEventListener.java index cf1ee5aa62..684752d9c8 100644 --- a/modules/core/src/main/java/com/bytedesk/core/socket/connection/ConnectionEventListener.java +++ b/modules/core/src/main/java/com/bytedesk/core/socket/connection/ConnectionEventListener.java @@ -18,6 +18,7 @@ import org.springframework.stereotype.Component; import com.bytedesk.core.socket.mqtt.event.MqttConnectedEvent; import com.bytedesk.core.socket.mqtt.event.MqttDisconnectedEvent; +import com.bytedesk.core.quartz.event.QuartzOneMinEvent; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -54,5 +55,13 @@ public class ConnectionEventListener { // boolean online = connectionRestService.isUserOnline(uid); // agentRestService.updateConnect(uid, online); } + + /** + * 每分钟调度:清理过期连接,移除超出 TTL 的会话,保持在线状态准确。 + */ + @EventListener + public void onQuartzOneMinEvent(QuartzOneMinEvent event) { + connectionRestService.expireStaleSessions(); + } } diff --git a/modules/core/src/main/java/com/bytedesk/core/socket/connection/ConnectionResponse.java b/modules/core/src/main/java/com/bytedesk/core/socket/connection/ConnectionResponse.java index f72eff5841..6bab39fd4f 100644 --- a/modules/core/src/main/java/com/bytedesk/core/socket/connection/ConnectionResponse.java +++ b/modules/core/src/main/java/com/bytedesk/core/socket/connection/ConnectionResponse.java @@ -13,7 +13,6 @@ */ package com.bytedesk.core.socket.connection; - import com.bytedesk.core.base.BaseResponse; import lombok.Data; @@ -31,17 +30,17 @@ public class ConnectionResponse extends BaseResponse { private static final long serialVersionUID = 1L; - private String clientId; - private String deviceUid; + private String clientId; + private String deviceUid; private String protocol; // channel 字段在 BaseResponse 中未定义,这里单独暴露以便前端获取来源渠道 private String channel; - private String ip; - private String userAgent; - private String status; - private Long connectedAt; - private Long lastHeartbeatAt; - private Long disconnectedAt; - private Integer ttlSeconds; + private String ip; + private String userAgent; + private String status; + private Long connectedAt; + private Long lastHeartbeatAt; + private Long disconnectedAt; + private Integer ttlSeconds; } diff --git a/modules/service/src/main/java/com/bytedesk/service/agent/AgentEntity.java b/modules/service/src/main/java/com/bytedesk/service/agent/AgentEntity.java index 47274e7e9d..74482417aa 100644 --- a/modules/service/src/main/java/com/bytedesk/service/agent/AgentEntity.java +++ b/modules/service/src/main/java/com/bytedesk/service/agent/AgentEntity.java @@ -76,7 +76,6 @@ public class AgentEntity extends BaseEntity { @Column(name = "agent_status") private String status = AgentStatusEnum.OFFLINE.name(); - /** * Configuration settings reference * All settings are managed through the settings entity @@ -118,17 +117,8 @@ public class AgentEntity extends BaseEntity { return this.status.equals(AgentStatusEnum.AWAY.name()); } - // 已废弃:connected 字段迁移至 ConnectionEntity,多端会话判断在线 - // 保留兼容方法以避免旧代码立即崩溃,但始终返回 false 或基于 presence 外部计算 - @Deprecated - public Boolean isConnectedAndAvailable() { - return false; // 使用 PresenceFacadeService 替代 - } - - @Deprecated - public Boolean getConnected() { - return false; // 使用 PresenceFacadeService 替代 - } + // 已完全移除 legacy connected 语义:请使用 PresenceFacadeService 进行在线/可接待判断 + // 如仍存在调用方,请重构为 presenceFacadeService.isAgentOnline...(agent) public UserProtobuf toUserProtobuf() { return UserProtobuf.builder() diff --git a/modules/service/src/main/java/com/bytedesk/service/agent/AgentEventListener.java b/modules/service/src/main/java/com/bytedesk/service/agent/AgentEventListener.java index e53f624b13..02f12093db 100644 --- a/modules/service/src/main/java/com/bytedesk/service/agent/AgentEventListener.java +++ b/modules/service/src/main/java/com/bytedesk/service/agent/AgentEventListener.java @@ -24,14 +24,10 @@ import com.bytedesk.core.enums.LanguageEnum; import com.bytedesk.core.enums.LevelEnum; import com.bytedesk.core.message.IMessageSendService; import com.bytedesk.core.message.MessageProtobuf; -import com.bytedesk.core.quartz.event.QuartzOneMinEvent; import com.bytedesk.core.rbac.organization.OrganizationEntity; import com.bytedesk.core.rbac.organization.event.OrganizationCreateEvent; import com.bytedesk.core.rbac.user.UserEntity; import com.bytedesk.core.rbac.user.UserProtobuf; -import com.bytedesk.core.socket.mqtt.event.MqttConnectedEvent; -import com.bytedesk.core.socket.mqtt.event.MqttDisconnectedEvent; -import com.bytedesk.core.socket.connection.ConnectionRestService; import com.bytedesk.core.thread.ThreadEntity; import com.bytedesk.core.thread.event.ThreadAcceptEvent; import com.bytedesk.kbase.kbase.KbaseRequest; @@ -53,7 +49,7 @@ public class AgentEventListener { private final AgentRestService agentRestService; private final KbaseRestService kbaseRestService; private final IMessageSendService messageSendService; - private final ConnectionRestService connectionRestService; + // private final ConnectionRestService connectionRestService; // 新注册管理员,创建组织之后,自动生成一个客服账号,主要方便入手 @Order(6) @@ -91,45 +87,45 @@ public class AgentEventListener { kbaseRestService.create(kbaseQuickReply); } - @EventListener - public void onMqttConnectedEvent(MqttConnectedEvent event) { - String clientId = event.getClientId(); - // 用户clientId格式: uid/client/deviceUid - final String uid = clientId.split("/")[0]; - // log.info("agent onMqttConnectedEvent uid {}, clientId {}", uid, clientId); - // 标记连接(使用 ConnectionEntity 支持多端在线) - // 仍保持原有行为,确保现有业务在线状态及时更新 - agentRestService.updateConnect(uid, true); - } + // @EventListener + // public void onMqttConnectedEvent(MqttConnectedEvent event) { + // // String clientId = event.getClientId(); + // // 用户clientId格式: uid/client/deviceUid + // // final String uid = clientId.split("/")[0]; + // // log.info("agent onMqttConnectedEvent uid {}, clientId {}", uid, clientId); + // // 标记连接(使用 ConnectionEntity 支持多端在线) + // // 仍保持原有行为,确保现有业务在线状态及时更新 + // // agentRestService.updateConnect(uid, true); + // } - @EventListener - public void onMqttDisconnectedEvent(MqttDisconnectedEvent event) { - String clientId = event.getClientId(); - // 用户clientId格式: uid/client/deviceUid - final String uid = clientId.split("/")[0]; - // log.info("agent onMqttDisconnectedEvent uid {}, clientId {}", uid, clientId); - // 先标记该 client 断开 - connectionRestService.markDisconnected(clientId); - // 根据 ConnectionEntity 汇总判断是否仍在线(多端) - boolean online = connectionRestService.isUserOnline(uid); - agentRestService.updateConnect(uid, online); - } + // @EventListener + // public void onMqttDisconnectedEvent(MqttDisconnectedEvent event) { + // // String clientId = event.getClientId(); + // // 用户clientId格式: uid/client/deviceUid + // // final String uid = clientId.split("/")[0]; + // // log.info("agent onMqttDisconnectedEvent uid {}, clientId {}", uid, clientId); + // // 先标记该 client 断开 + // // connectionRestService.markDisconnected(clientId); + // // 根据 ConnectionEntity 汇总判断是否仍在线(多端) + // // boolean online = connectionRestService.isUserOnline(uid); + // // agentRestService.updateConnect(uid, online); + // } - // 更新agent在线状态 - @EventListener - public void onQuartzOneMinEvent(QuartzOneMinEvent event) { - // log.info("agent QuartzOneMinEvent"); - // 先清理过期会话 - connectionRestService.expireStaleSessions(); - // 再同步已标记为在线的坐席,如果 TTL 过期则下线 - agentRestService.findAllConnected().forEach(agent -> { - boolean online = connectionRestService.isUserOnline(agent.getUserUid()); - if (!online) { - log.info("agent updateConnect uid {} offline(by presence)", agent.getUserUid()); - agentRestService.updateConnect(agent.getUserUid(), false); - } - }); - } + // // 更新agent在线状态 + // @EventListener + // public void onQuartzOneMinEvent(QuartzOneMinEvent event) { + // // log.info("agent QuartzOneMinEvent"); + // // 先清理过期会话 + // // connectionRestService.expireStaleSessions(); + // // 再同步已标记为在线的坐席,如果 TTL 过期则下线 + // // agentRestService.findAllConnected().forEach(agent -> { + // // boolean online = connectionRestService.isUserOnline(agent.getUserUid()); + // // if (!online) { + // // log.info("agent updateConnect uid {} offline(by presence)", agent.getUserUid()); + // // // agentRestService.updateConnect(agent.getUserUid(), false); + // // } + // // }); + // } // 客服接待数量发生变化,增加接待数量,发送欢迎语 @EventListener diff --git a/modules/service/src/main/java/com/bytedesk/service/agent/AgentRestService.java b/modules/service/src/main/java/com/bytedesk/service/agent/AgentRestService.java index e5326cded0..2c63c3e14d 100644 --- a/modules/service/src/main/java/com/bytedesk/service/agent/AgentRestService.java +++ b/modules/service/src/main/java/com/bytedesk/service/agent/AgentRestService.java @@ -15,7 +15,6 @@ package com.bytedesk.service.agent; import java.util.List; import java.util.Optional; -import java.util.Set; import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.CacheEvict; @@ -23,7 +22,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.orm.ObjectOptimisticLockingFailureException; -import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; @@ -34,7 +32,6 @@ import com.bytedesk.core.rbac.auth.AuthService; import com.bytedesk.core.rbac.user.UserEntity; import com.bytedesk.core.rbac.user.UserProtobuf; import com.bytedesk.core.rbac.user.UserService; -import com.bytedesk.core.socket.mqtt.service.MqttConnectionService; import com.bytedesk.core.thread.ThreadEntity; import com.bytedesk.core.thread.ThreadRequest; import com.bytedesk.core.thread.ThreadResponse; @@ -70,8 +67,6 @@ public class AgentRestService extends BaseRestService userIds = mqttConnectionService.getConnectedUserUids(); - if (userIds.contains(agent.getUserUid())) { - agent.setConnected(true); - } + // Set userIds = mqttConnectionService.getConnectedUserUids(); + // if (userIds.contains(agent.getUserUid())) { + // agent.setConnected(true); + // } // 保存Agent并检查返回值 AgentEntity savedAgent = save(agent); if (savedAgent == null) { @@ -331,9 +326,9 @@ public class AgentRestService extends BaseRestService getAvailableAgents() { - // Legacy method retained; presence-based filtering moved to PresenceFacadeService - if (this.agents == null) { - return new ArrayList<>(); - } - return new ArrayList<>(); // Always empty; use PresenceFacadeService.getAvailableAgents(workgroup) - } - @JsonIgnore public AgentEntity getMessageLeaveAgent() { if (this.messageLeaveAgent == null) { @@ -172,13 +158,6 @@ public class WorkgroupEntity extends BaseEntity { return this.messageLeaveAgent; } - @JsonIgnore - @Deprecated - public Boolean isConnected() { - // Deprecated: use PresenceFacadeService.isWorkgroupOnline(workgroup) - return false; - } - @JsonIgnore public UserProtobuf toUserProtobuf() { return UserProtobuf.builder() @@ -200,15 +179,6 @@ public class WorkgroupEntity extends BaseEntity { return WorkgroupRoutingModeEnum.ROUND_ROBIN.name(); } - // 监控客服组登录坐席、开启自动领取坐席数、空闲坐席数、领取会话数、已处理会话数、流失会话数、留言数。 - // agent connected count - @JsonIgnore - @Deprecated - public long getConnectedAgentCount() { - // Deprecated: use PresenceFacadeService.countOnlineAgents(workgroup) - return 0L; - } - // agent available count @JsonIgnore public long getAvailableAgentCount() { diff --git a/starter/src/main/resources/templates/ftl/common/footer_nav.ftl b/starter/src/main/resources/templates/ftl/common/footer_nav.ftl index f85d171202..13ccad367b 100644 --- a/starter/src/main/resources/templates/ftl/common/footer_nav.ftl +++ b/starter/src/main/resources/templates/ftl/common/footer_nav.ftl @@ -1,8 +1,8 @@ <#include "./macro/i18n.ftl" /> -