mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-28 01:53:53 +08:00
Merge pull request #1066 from xinnan-tech/manager-api-lastConnectedAtIsNull-BUG
修复了"获取用户智能体列表"中lastConnectedAt为null的bug
This commit is contained in:
@@ -62,6 +62,13 @@ public class RedisKeys {
|
|||||||
return "agent:device:count:" + id;
|
return "agent:device:count:" + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取智能体最后连接时间缓存key
|
||||||
|
*/
|
||||||
|
public static String getAgentDeviceLastConnectedAtById(String id) {
|
||||||
|
return "agent:device:lastConnected:" + id;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取系统配置缓存key
|
* 获取系统配置缓存key
|
||||||
*/
|
*/
|
||||||
|
|||||||
+6
@@ -1,12 +1,15 @@
|
|||||||
package xiaozhi.modules.agent.service.biz.impl;
|
package xiaozhi.modules.agent.service.biz.impl;
|
||||||
|
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import xiaozhi.common.redis.RedisKeys;
|
||||||
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
import xiaozhi.modules.agent.dto.AgentChatHistoryReportDTO;
|
import xiaozhi.modules.agent.dto.AgentChatHistoryReportDTO;
|
||||||
import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
|
import xiaozhi.modules.agent.entity.AgentChatHistoryEntity;
|
||||||
import xiaozhi.modules.agent.entity.AgentEntity;
|
import xiaozhi.modules.agent.entity.AgentEntity;
|
||||||
@@ -29,6 +32,7 @@ public class AgentChatHistoryBizServiceImpl implements AgentChatHistoryBizServic
|
|||||||
private final AgentService agentService;
|
private final AgentService agentService;
|
||||||
private final AgentChatHistoryService agentChatHistoryService;
|
private final AgentChatHistoryService agentChatHistoryService;
|
||||||
private final AgentChatAudioService agentChatAudioService;
|
private final AgentChatAudioService agentChatAudioService;
|
||||||
|
private final RedisUtils redisUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理聊天记录上报,包括文件上传和相关信息记录
|
* 处理聊天记录上报,包括文件上传和相关信息记录
|
||||||
@@ -77,6 +81,8 @@ public class AgentChatHistoryBizServiceImpl implements AgentChatHistoryBizServic
|
|||||||
|
|
||||||
// 3. 保存数据
|
// 3. 保存数据
|
||||||
agentChatHistoryService.save(entity);
|
agentChatHistoryService.save(entity);
|
||||||
|
// 4. 更新设备最后对话时间
|
||||||
|
redisUtils.set(RedisKeys.getAgentDeviceLastConnectedAtById(agentId), new Date(), 60 * 2);
|
||||||
return Boolean.TRUE;
|
return Boolean.TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -21,6 +21,7 @@ import xiaozhi.modules.agent.dao.AgentDao;
|
|||||||
import xiaozhi.modules.agent.dto.AgentDTO;
|
import xiaozhi.modules.agent.dto.AgentDTO;
|
||||||
import xiaozhi.modules.agent.entity.AgentEntity;
|
import xiaozhi.modules.agent.entity.AgentEntity;
|
||||||
import xiaozhi.modules.agent.service.AgentService;
|
import xiaozhi.modules.agent.service.AgentService;
|
||||||
|
import xiaozhi.modules.device.service.DeviceService;
|
||||||
import xiaozhi.modules.model.service.ModelConfigService;
|
import xiaozhi.modules.model.service.ModelConfigService;
|
||||||
import xiaozhi.modules.security.user.SecurityUser;
|
import xiaozhi.modules.security.user.SecurityUser;
|
||||||
import xiaozhi.modules.sys.enums.SuperAdminEnum;
|
import xiaozhi.modules.sys.enums.SuperAdminEnum;
|
||||||
@@ -29,11 +30,11 @@ import xiaozhi.modules.timbre.service.TimbreService;
|
|||||||
@Service
|
@Service
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> implements AgentService {
|
public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> implements AgentService {
|
||||||
|
|
||||||
private final AgentDao agentDao;
|
private final AgentDao agentDao;
|
||||||
private final TimbreService timbreModelService;
|
private final TimbreService timbreModelService;
|
||||||
private final ModelConfigService modelConfigService;
|
private final ModelConfigService modelConfigService;
|
||||||
private final RedisUtils redisUtils;
|
private final RedisUtils redisUtils;
|
||||||
|
private final DeviceService deviceService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PageData<AgentEntity> adminAgentList(Map<String, Object> params) {
|
public PageData<AgentEntity> adminAgentList(Map<String, Object> params) {
|
||||||
@@ -95,9 +96,11 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
|||||||
// 获取 TTS 音色名称
|
// 获取 TTS 音色名称
|
||||||
dto.setTtsVoiceName(timbreModelService.getTimbreNameById(agent.getTtsVoiceId()));
|
dto.setTtsVoiceName(timbreModelService.getTimbreNameById(agent.getTtsVoiceId()));
|
||||||
|
|
||||||
|
// 获取智能体最近的最后连接时长
|
||||||
|
dto.setLastConnectedAt(deviceService.getLatestLastConnectionTime(agent.getId()));
|
||||||
|
|
||||||
// 获取设备数量
|
// 获取设备数量
|
||||||
dto.setDeviceCount(getDeviceCountByAgentId(agent.getId()));
|
dto.setDeviceCount(getDeviceCountByAgentId(agent.getId()));
|
||||||
|
|
||||||
return dto;
|
return dto;
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,16 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|||||||
|
|
||||||
import xiaozhi.modules.device.entity.DeviceEntity;
|
import xiaozhi.modules.device.entity.DeviceEntity;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface DeviceDao extends BaseMapper<DeviceEntity> {
|
public interface DeviceDao extends BaseMapper<DeviceEntity> {
|
||||||
|
/**
|
||||||
|
* 获取此智能体全部设备的最后连接时间
|
||||||
|
* @param agentId 智能体id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Date getAllLastConnectedAtByAgentId(String agentId);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package xiaozhi.modules.device.service;
|
package xiaozhi.modules.device.service;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import xiaozhi.common.page.PageData;
|
import xiaozhi.common.page.PageData;
|
||||||
@@ -78,4 +79,13 @@ public interface DeviceService extends BaseService<DeviceEntity> {
|
|||||||
* @return 激活码
|
* @return 激活码
|
||||||
*/
|
*/
|
||||||
String geCodeByDeviceId(String deviceId);
|
String geCodeByDeviceId(String deviceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取这个智能体设备理的最近的最后连接时间
|
||||||
|
* @param agentId 智能体id
|
||||||
|
* @return 返回设备最近的最后连接时间
|
||||||
|
*/
|
||||||
|
Date getLatestLastConnectionTime(String agentId);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
+15
-6
@@ -1,12 +1,7 @@
|
|||||||
package xiaozhi.modules.device.service.impl;
|
package xiaozhi.modules.device.service.impl;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Date;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.TimeZone;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -258,6 +253,20 @@ public class DeviceServiceImpl extends BaseServiceImpl<DeviceDao, DeviceEntity>
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getLatestLastConnectionTime(String agentId) {
|
||||||
|
// 查询是否有缓存时间,有则返回
|
||||||
|
Date cachedDate = (Date) redisUtils.get(RedisKeys.getAgentDeviceLastConnectedAtById(agentId));
|
||||||
|
if (cachedDate != null) {
|
||||||
|
return cachedDate;
|
||||||
|
}
|
||||||
|
Date maxDate = deviceDao.getAllLastConnectedAtByAgentId(agentId);
|
||||||
|
if (maxDate != null) {
|
||||||
|
redisUtils.set(RedisKeys.getAgentDeviceLastConnectedAtById(agentId), maxDate, 60 * 2);
|
||||||
|
}
|
||||||
|
return maxDate;
|
||||||
|
}
|
||||||
|
|
||||||
private String getDeviceCacheKey(String deviceId) {
|
private String getDeviceCacheKey(String deviceId) {
|
||||||
String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
|
String safeDeviceId = deviceId.replace(":", "_").toLowerCase();
|
||||||
String dataKey = String.format("ota:activation:data:%s", safeDeviceId);
|
String dataKey = String.format("ota:activation:data:%s", safeDeviceId);
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="xiaozhi.modules.device.dao.DeviceDao">
|
||||||
|
<!-- 获取此智能体全部设备的最后连接时间 -->
|
||||||
|
<select id="getAllLastConnectedAtByAgentId" resultType="java.util.Date">
|
||||||
|
SELECT last_connected_at FROM ai_device
|
||||||
|
WHERE
|
||||||
|
agent_id = #{agentId}
|
||||||
|
order by
|
||||||
|
last_connected_at desc limit 0,1
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package xiaozhi.modules.device;
|
package xiaozhi.modules.device;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.DisplayName;
|
import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -8,8 +11,9 @@ import org.springframework.boot.test.context.SpringBootTest;
|
|||||||
import org.springframework.test.context.ActiveProfiles;
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import xiaozhi.common.redis.RedisKeys;
|
|
||||||
import xiaozhi.common.redis.RedisUtils;
|
import xiaozhi.common.redis.RedisUtils;
|
||||||
|
import xiaozhi.modules.sys.dto.SysUserDTO;
|
||||||
|
import xiaozhi.modules.sys.service.SysUserService;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
@@ -19,18 +23,37 @@ public class DeviceTest {
|
|||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private RedisUtils redisUtils;
|
private RedisUtils redisUtils;
|
||||||
|
@Autowired
|
||||||
|
private SysUserService sysUserService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSaveUser() {
|
||||||
|
SysUserDTO userDTO = new SysUserDTO();
|
||||||
|
userDTO.setUsername("test");
|
||||||
|
userDTO.setPassword(UUID.randomUUID().toString());
|
||||||
|
sysUserService.save(userDTO);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("测试写入设备信息")
|
@DisplayName("测试写入设备信息")
|
||||||
public void testWriteDeviceInfo() {
|
public void testWriteDeviceInfo() {
|
||||||
log.info("开始测试写入设备信息...");
|
log.info("开始测试写入设备信息...");
|
||||||
|
|
||||||
// 模拟设备MAC地址
|
// 模拟设备MAC地址
|
||||||
String macAddress = "00:11:22:33:44:55";
|
String macAddress = "00:11:22:33:44:66";
|
||||||
// 模拟设备验证码
|
// 模拟设备验证码
|
||||||
String deviceCode = "123456";
|
String deviceCode = "123456";
|
||||||
|
|
||||||
String redisKey = RedisKeys.getDeviceCaptchaKey(deviceCode);
|
HashMap<String, Object> map = new HashMap<>();
|
||||||
|
map.put("mac_address", macAddress);
|
||||||
|
map.put("activation_code", deviceCode);
|
||||||
|
map.put("board", "硬件型号");
|
||||||
|
map.put("app_version", "0.3.13");
|
||||||
|
|
||||||
|
String safeDeviceId = macAddress.replace(":", "_").toLowerCase();
|
||||||
|
String cacheDeviceKey = String.format("ota:activation:data:%s", safeDeviceId);
|
||||||
|
redisUtils.set(cacheDeviceKey, map, 300);
|
||||||
|
|
||||||
|
String redisKey = "ota:activation:code:" + deviceCode;
|
||||||
log.info("Redis Key: {}", redisKey);
|
log.info("Redis Key: {}", redisKey);
|
||||||
|
|
||||||
// 将设备信息写入Redis
|
// 将设备信息写入Redis
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="version-info">
|
<div class="version-info">
|
||||||
<div>最近对话:{{ device.lastConnectedAt }}</div>
|
<div>最近对话:{{ formattedLastConnectedTime }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -45,6 +45,25 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return { switchValue: false }
|
return { switchValue: false }
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
formattedLastConnectedTime() {
|
||||||
|
if (!this.device.lastConnectedAt) return '暂未对话';
|
||||||
|
|
||||||
|
const lastTime = new Date(this.device.lastConnectedAt);
|
||||||
|
const now = new Date();
|
||||||
|
const diffMinutes = Math.floor((now - lastTime) / (1000 * 60));
|
||||||
|
|
||||||
|
if (diffMinutes < 60) {
|
||||||
|
return `${diffMinutes}分钟前`;
|
||||||
|
} else if (diffMinutes < 24 * 60) {
|
||||||
|
const hours = Math.floor(diffMinutes / 60);
|
||||||
|
const minutes = diffMinutes % 60;
|
||||||
|
return `${hours}小时${minutes > 0 ? minutes + '分钟' : ''}前`;
|
||||||
|
} else {
|
||||||
|
return this.device.lastConnectedAt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleDelete() {
|
handleDelete() {
|
||||||
this.$emit('delete', this.device.agentId)
|
this.$emit('delete', this.device.agentId)
|
||||||
|
|||||||
Reference in New Issue
Block a user