redis反序列化问题

This commit is contained in:
2025-12-19 19:04:43 +08:00
parent 1131a34c6e
commit 566d03491b
15 changed files with 123 additions and 140 deletions

View File

@@ -66,7 +66,7 @@ public class KnowledgeController {
return ResultDomain.failure(result.getAllErrors()); return ResultDomain.failure(result.getAllErrors());
} }
logger.info("创建知识库: title={}", knowledge.getTitle()); logger.info("创建知识库: title={}", knowledge.getTitle());
return knowledgeService.createKnowledge(knowledge, "PUBLIC", null, null); return knowledgeService.createKnowledge(knowledge);
} }
/** /**

View File

@@ -288,7 +288,7 @@ public class AgentChatServiceImpl implements AgentChatService {
// 1. 从Redis获取会话数据 // 1. 从Redis获取会话数据
String cacheKey = CHAT_SESSION_PREFIX + sessionId; String cacheKey = CHAT_SESSION_PREFIX + sessionId;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, Object> sessionData = (Map<String, Object>) redisService.get(cacheKey); Map<String, Object> sessionData = redisService.get(cacheKey, Map.class);
if (sessionData == null) { if (sessionData == null) {
try { try {

View File

@@ -187,7 +187,7 @@ public class AgentServiceImpl implements AgentService {
String cacheKey = AGENT_CACHE_PREFIX + agentId; String cacheKey = AGENT_CACHE_PREFIX + agentId;
// 1. 先从缓存获取 // 1. 先从缓存获取
TbAgent agent = (TbAgent) redisService.get(cacheKey); TbAgent agent = redisService.get(cacheKey, TbAgent.class);
if (agent != null) { if (agent != null) {
return ResultDomain.success("查询成功", agent); return ResultDomain.success("查询成功", agent);
} }
@@ -196,7 +196,7 @@ public class AgentServiceImpl implements AgentService {
agentLock.lock(); agentLock.lock();
try { try {
// 再次检查缓存 // 再次检查缓存
agent = (TbAgent) redisService.get(cacheKey); agent = redisService.get(cacheKey, TbAgent.class);
if (agent != null) { if (agent != null) {
return ResultDomain.success("查询成功", agent); return ResultDomain.success("查询成功", agent);
} }

View File

@@ -63,15 +63,12 @@ public class KnowledgeServiceImpl implements KnowledgeService {
* @description 创建知识库基础信息包含dify知识库各种参数的配置 * @description 创建知识库基础信息包含dify知识库各种参数的配置
* 注意涉及外部API调用不使用@Transactional采用手动补偿机制 * 注意涉及外部API调用不使用@Transactional采用手动补偿机制
* @param knowledge 知识库信息 * @param knowledge 知识库信息
* @param permissionType 权限类型PUBLIC-公开DEPARTMENT-部门ROLE-角色PRIVATE-私有
* @param deptIds 部门ID列表DEPARTMENT类型需要
* @param roleIds 角色ID列表ROLE/PRIVATE类型需要
* @return ResultDomain<TbKnowledge> 创建结果 * @return ResultDomain<TbKnowledge> 创建结果
* @author yslg * @author yslg
* @since 2025-12-18 * @since 2025-12-18
*/ */
@Override @Override
public ResultDomain<TbKnowledge> createKnowledge(TbKnowledge knowledge, String permissionType, List<String> deptIds, List<String> roleIds) { public ResultDomain<TbKnowledge> createKnowledge(TbKnowledge knowledge) {
// 1. 参数校验 // 1. 参数校验
if (!StringUtils.hasText(knowledge.getTitle())) { if (!StringUtils.hasText(knowledge.getTitle())) {
return ResultDomain.failure("知识库标题不能为空"); return ResultDomain.failure("知识库标题不能为空");

View File

@@ -16,19 +16,13 @@ public interface KnowledgeService {
/** /**
* @description 创建知识库基础信息包含dify知识库各种参数的配置 * @description 创建知识库基础信息包含dify知识库各种参数的配置
* @param knowledge 知识库信息 * @param knowledge 知识库信息
* @param permissionType 权限类型PUBLIC-公开DEPARTMENT-部门ROLE-角色PRIVATE-私有
* @param deptIds 部门ID列表DEPARTMENT类型需要 * @param deptIds 部门ID列表DEPARTMENT类型需要
* @param roleIds 角色ID列表ROLE/PRIVATE类型需要 * @param roleIds 角色ID列表ROLE/PRIVATE类型需要
* @return ResultDomain<TbKnowledge> 创建结果 * @return ResultDomain<TbKnowledge> 创建结果
* @author yslg * @author yslg
* @since 2025-12-18 * @since 2025-12-18
*/ */
ResultDomain<TbKnowledge> createKnowledge( ResultDomain<TbKnowledge> createKnowledge(TbKnowledge knowledge);
TbKnowledge knowledge,
String permissionType,
List<String> deptIds,
List<String> roleIds
);
/** /**
* @description 更新知识库包含dify知识库各种参数的配置 * @description 更新知识库包含dify知识库各种参数的配置

View File

@@ -210,7 +210,7 @@ public class AuthController {
// 通过sessionId验证手机验证码 // 通过sessionId验证手机验证码
String smsCodeKey = "sms:code:" + smsSessionId; String smsCodeKey = "sms:code:" + smsSessionId;
String storedSmsValue = (String) redisService.get(smsCodeKey); String storedSmsValue = redisService.get(smsCodeKey, String.class);
if (storedSmsValue == null) { if (storedSmsValue == null) {
return ResultDomain.failure("验证码已过期,请重新获取"); return ResultDomain.failure("验证码已过期,请重新获取");
} }
@@ -258,7 +258,7 @@ public class AuthController {
// 通过sessionId验证邮箱验证码 // 通过sessionId验证邮箱验证码
String emailCodeKey = "email:code:" + emailSessionId; String emailCodeKey = "email:code:" + emailSessionId;
String storedEmailValue = (String) redisService.get(emailCodeKey); String storedEmailValue = redisService.get(emailCodeKey, String.class);
if (storedEmailValue == null) { if (storedEmailValue == null) {
return ResultDomain.failure("验证码已过期,请重新获取"); return ResultDomain.failure("验证码已过期,请重新获取");
} }

View File

@@ -160,7 +160,7 @@ public class AuthServiceImpl implements AuthService{
// 2. 从Redis获取登录信息 // 2. 从Redis获取登录信息
String loginKey = "login:token:" + token; String loginKey = "login:token:" + token;
String loginJson = (String) redisService.get(loginKey); String loginJson = redisService.get(loginKey, String.class);
if (loginJson == null) { if (loginJson == null) {
return ResultDomain.failure("登录信息已失效"); return ResultDomain.failure("登录信息已失效");
} }
@@ -422,7 +422,7 @@ public class AuthServiceImpl implements AuthService{
// 2. 从Redis获取登录信息 // 2. 从Redis获取登录信息
String loginKey = "login:token:" + token; String loginKey = "login:token:" + token;
String loginJson = (String) redisService.get(loginKey); String loginJson = redisService.get(loginKey, String.class);
if (loginJson == null) { if (loginJson == null) {
return ResultDomain.failure("登录信息已失效"); return ResultDomain.failure("登录信息已失效");
} }
@@ -481,7 +481,7 @@ public class AuthServiceImpl implements AuthService{
// 1. 从Redis获取登录信息 // 1. 从Redis获取登录信息
String loginKey = "login:token:" + token; String loginKey = "login:token:" + token;
String loginJson = (String) redisService.get(loginKey); String loginJson = redisService.get(loginKey, String.class);
if (loginJson == null) { if (loginJson == null) {
return ResultDomain.success("登出成功", (LoginDomain) null); // 已经过期的token直接返回成功 return ResultDomain.success("登出成功", (LoginDomain) null); // 已经过期的token直接返回成功
} }

View File

@@ -77,7 +77,7 @@ public class EmailLoginStrategy implements LoginStrategy {
// 从Redis获取验证码 // 从Redis获取验证码
String codeKey = "email:code:" + captchaId; String codeKey = "email:code:" + captchaId;
String storedValue = (String) redisService.get(codeKey); String storedValue = redisService.get(codeKey, String.class);
if (storedValue == null) { if (storedValue == null) {
return false; return false;

View File

@@ -78,7 +78,7 @@ public class PhoneLoginStrategy implements LoginStrategy {
// 从Redis获取验证码 // 从Redis获取验证码
String codeKey = "sms:code:" + captchaId; String codeKey = "sms:code:" + captchaId;
String storedValue = (String) redisService.get(codeKey); String storedValue = redisService.get(codeKey, String.class);
if (storedValue == null) { if (storedValue == null) {
return false; return false;

View File

@@ -78,7 +78,7 @@ public class HttpLoginArgumentResolver implements HandlerMethodArgumentResolver
// 从Redis中获取LoginDomain // 从Redis中获取LoginDomain
String userId = tokenParser.getUserIdFromToken(token); String userId = tokenParser.getUserIdFromToken(token);
String redisKey = REDIS_LOGIN_PREFIX + userId; String redisKey = REDIS_LOGIN_PREFIX + userId;
LoginDomain loginDomain = (LoginDomain) redisService.get(redisKey); LoginDomain loginDomain = redisService.get(redisKey, LoginDomain.class);
if (loginDomain == null) { if (loginDomain == null) {
if (httpLogin.required()) { if (httpLogin.required()) {

View File

@@ -22,7 +22,6 @@ import java.util.List;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import com.alibaba.fastjson2.JSON;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.xyzh.common.core.domain.LoginDomain; import org.xyzh.common.core.domain.LoginDomain;
@@ -107,18 +106,8 @@ public class GatewayAuthConfig {
if (StringUtils.hasText(authHeader) && authHeader.startsWith(BEARER_PREFIX)) { if (StringUtils.hasText(authHeader) && authHeader.startsWith(BEARER_PREFIX)) {
String token = authHeader.substring(BEARER_PREFIX.length()); String token = authHeader.substring(BEARER_PREFIX.length());
String cacheKey = LOGIN_TOKEN_PREFIX + token; String cacheKey = LOGIN_TOKEN_PREFIX + token;
Object obj = redisService.get(cacheKey); LoginDomain login = redisService.get(cacheKey, LoginDomain.class);
logger.info("Redis key: {}, login: {}", cacheKey, login != null ? "loaded" : "null");
logger.info("Redis key: {}, obj type: {}", cacheKey, obj != null ? obj.getClass().getName() : "null");
LoginDomain login = null;
if (obj instanceof LoginDomain) {
login = (LoginDomain) obj;
} else if (obj instanceof String) {
// Redis 返回的是 JSON 字符串,需要反序列化
login = JSON.parseObject((String) obj, LoginDomain.class);
logger.info("从 JSON 反序列化 LoginDomain");
}
if (login != null) { if (login != null) {
if (login.getUserPermissions() != null) { if (login.getUserPermissions() != null) {

View File

@@ -8,8 +8,6 @@ import org.springframework.web.context.request.ServletRequestAttributes;
import org.xyzh.common.core.domain.LoginDomain; import org.xyzh.common.core.domain.LoginDomain;
import org.xyzh.common.redis.service.RedisService; import org.xyzh.common.redis.service.RedisService;
import com.alibaba.fastjson2.JSON;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
@@ -48,13 +46,7 @@ public class LoginUtil {
try { try {
String cacheKey = LOGIN_TOKEN_PREFIX + token; String cacheKey = LOGIN_TOKEN_PREFIX + token;
Object obj = instance.redisService.get(cacheKey); return instance.redisService.get(cacheKey, LoginDomain.class);
if (obj instanceof LoginDomain) {
return (LoginDomain) obj;
} else if (obj instanceof String) {
// Redis 返回的是 JSON 字符串,需要反序列化
return JSON.parseObject((String) obj, LoginDomain.class);
}
} catch (Exception e) { } catch (Exception e) {
// 忽略异常 // 忽略异常
} }

View File

@@ -7,6 +7,8 @@ import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.alibaba.fastjson2.JSON;
/** /**
* @description RedisService.java Redis工具服务类封装常用Redis操作 * @description RedisService.java Redis工具服务类封装常用Redis操作
* @filename RedisService.java * @filename RedisService.java
@@ -56,6 +58,28 @@ public class RedisService {
return redisTemplate.opsForValue().get(key); return redisTemplate.opsForValue().get(key);
} }
/**
* @description 获取key对应的value并反序列化为指定类型
* @param key String 键
* @param clazz Class<T> 目标类型
* @return T 反序列化后的对象
* @author yslg
* @since 2025-12-19
*/
public <T> T get(String key, Class<T> clazz) {
Object obj = redisTemplate.opsForValue().get(key);
if (obj == null) {
return null;
}
if (clazz.isInstance(obj)) {
return clazz.cast(obj);
}
if (obj instanceof String) {
return JSON.parseObject((String) obj, clazz);
}
return null;
}
/** /**
* @description 删除key * @description 删除key
* @param key String 键 * @param key String 键

View File

@@ -22,6 +22,10 @@
<groupId>org.xyzh.apis</groupId> <groupId>org.xyzh.apis</groupId>
<artifactId>api-workcase</artifactId> <artifactId>api-workcase</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.xyzh.apis</groupId>
<artifactId>api-system</artifactId>
</dependency>
<!-- <dependency> <!-- <dependency>
<groupId>org.xyzh.apis</groupId> <groupId>org.xyzh.apis</groupId>
<artifactId>api-ai</artifactId> <artifactId>api-ai</artifactId>

View File

@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.dubbo.config.annotation.DubboReference; import org.apache.dubbo.config.annotation.DubboReference;
import org.bouncycastle.jcajce.provider.asymmetric.EXTERNAL;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner; import org.springframework.boot.CommandLineRunner;
@@ -11,6 +12,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.xyzh.api.ai.dto.TbKnowledge; import org.xyzh.api.ai.dto.TbKnowledge;
import org.xyzh.api.ai.service.KnowledgeService; import org.xyzh.api.ai.service.KnowledgeService;
import org.xyzh.api.system.service.SysConfigService;
import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.core.domain.ResultDomain;
/** /**
@@ -27,28 +29,30 @@ public class KnowledgeInit {
private static final String SERVICE_WORKCASE = "workcase"; private static final String SERVICE_WORKCASE = "workcase";
private static final String CATEGORY_INTERNAL = "internal"; private static final String CATEGORY_INTERNAL = "internal";
private static final String CATEGORY_EXTERNAL = "external"; private static final String CATEGORY_EXTERNAL = "external";
private static final String PERMISSION_PUBLIC = "PUBLIC";
@DubboReference(version = "1.0.0", group = "ai", timeout = 30000) @DubboReference(version = "1.0.0", group = "ai", timeout = 30000)
private KnowledgeService knowledgeService; private KnowledgeService knowledgeService;
@DubboReference(version = "1.0.0", group = "system", timeout = 30000)
private SysConfigService sysConfigService;
@Bean @Bean
public CommandLineRunner knowledgeInitRunner() { public CommandLineRunner knowledgeInitRunner() {
return args -> { return args -> {
logger.info("开始初始化客服系统知识库..."); logger.info("开始初始化客服系统知识库...");
List<KnowledgeConfig> configs = buildKnowledgeConfigs(); List<TbKnowledge> knowledges = buildKnowledgeConfigs();
int successCount = 0; int successCount = 0;
int skipCount = 0; int skipCount = 0;
for (KnowledgeConfig config : configs) { for (TbKnowledge knowledge : knowledges) {
if (checkKnowledgeExists(knowledgeService, config.title)) { if (checkKnowledgeExists(knowledge)) {
logger.info("知识库已存在,跳过: {}", config.title); logger.info("知识库已存在,跳过: {}", knowledge.getTitle());
skipCount++; skipCount++;
continue; continue;
} }
if (createKnowledge(knowledgeService, config)) { if (createKnowledge(knowledge)) {
successCount++; successCount++;
} }
} }
@@ -60,109 +64,88 @@ public class KnowledgeInit {
/** /**
* 构建8个知识库配置 * 构建8个知识库配置
*/ */
private List<KnowledgeConfig> buildKnowledgeConfigs() { private List<TbKnowledge> buildKnowledgeConfigs() {
List<KnowledgeConfig> configs = new ArrayList<>(); List<TbKnowledge> knowledges = new ArrayList<>();
// 外部知识库
TbKnowledge outerDevOperKnowledge = new TbKnowledge();
outerDevOperKnowledge.setKnowledgeId("workcase_outer_device_operate");
outerDevOperKnowledge.setTitle("设备操作指南");
outerDevOperKnowledge.setAvatar("settings");
outerDevOperKnowledge.setCategory(CATEGORY_EXTERNAL);
knowledges.add(outerDevOperKnowledge);
// 4个内部知识库 TbKnowledge outerCommonErrorKnowledge = new TbKnowledge();
configs.add(new KnowledgeConfig( outerCommonErrorKnowledge.setKnowledgeId("workcase_outer_common_error");
"内部-政策法规库", outerCommonErrorKnowledge.setTitle("常见故障解决方案");
"存储城市生命线相关政策法规、行业标准、规范文件", outerCommonErrorKnowledge.setAvatar("question");
CATEGORY_INTERNAL outerCommonErrorKnowledge.setCategory(CATEGORY_EXTERNAL);
)); knowledges.add(outerCommonErrorKnowledge);
configs.add(new KnowledgeConfig(
"内部-技术文档库",
"存储技术规范、操作手册、维护指南等技术文档",
CATEGORY_INTERNAL
));
configs.add(new KnowledgeConfig(
"内部-案例经验库",
"存储历史工单处理案例、经验总结、故障排查记录",
CATEGORY_INTERNAL
));
configs.add(new KnowledgeConfig(
"内部-培训资料库",
"存储员工培训材料、业务知识、常见问题解答",
CATEGORY_INTERNAL
));
// 4个外部知识库 TbKnowledge outerSanBaoKnowledge = new TbKnowledge();
configs.add(new KnowledgeConfig( outerSanBaoKnowledge.setKnowledgeId("workcase_outer_sanbao");
"外部-服务指南库", outerSanBaoKnowledge.setTitle("三包外服务政策");
"面向公众的服务办理指南、流程说明、所需材料", outerSanBaoKnowledge.setAvatar("document");
CATEGORY_EXTERNAL outerSanBaoKnowledge.setCategory(CATEGORY_EXTERNAL);
)); knowledges.add(outerSanBaoKnowledge);
configs.add(new KnowledgeConfig(
"外部-常见问题库",
"公众常见咨询问题及标准答案FAQ知识库",
CATEGORY_EXTERNAL
));
configs.add(new KnowledgeConfig(
"外部-公告通知库",
"政府公告、服务通知、停水停电通知等公开信息",
CATEGORY_EXTERNAL
));
configs.add(new KnowledgeConfig(
"外部-便民信息库",
"便民服务信息、联系方式、服务网点、办事地点等",
CATEGORY_EXTERNAL
));
return configs; TbKnowledge outerMountingsKnowledge = new TbKnowledge();
outerMountingsKnowledge.setKnowledgeId("workcase_outer_mounting_chat");
outerMountingsKnowledge.setTitle("配件咨询话术");
outerMountingsKnowledge.setAvatar("chat");
outerMountingsKnowledge.setCategory(CATEGORY_EXTERNAL);
knowledges.add(outerMountingsKnowledge);
// 内部知识库
TbKnowledge innerDevFixKnowledge = new TbKnowledge();
innerDevFixKnowledge.setKnowledgeId("workcase_inner_device_fix");
innerDevFixKnowledge.setTitle("技术维修手册");
innerDevFixKnowledge.setAvatar("settings");
outerMountingsKnowledge.setCategory(CATEGORY_INTERNAL);
knowledges.add(innerDevFixKnowledge);
TbKnowledge innerProArgsKnowledge = new TbKnowledge();
innerProArgsKnowledge.setKnowledgeId("workcase_inner_product_args_info");
innerProArgsKnowledge.setTitle("产品参数明细");
innerProArgsKnowledge.setAvatar("document");
innerProArgsKnowledge.setCategory(CATEGORY_INTERNAL);
knowledges.add(innerProArgsKnowledge);
TbKnowledge innerServExpKnowledge = new TbKnowledge();
innerServExpKnowledge.setKnowledgeId("workcase_inner_service_exp");
innerServExpKnowledge.setTitle("内部服务流程规范");
innerServExpKnowledge.setAvatar("document");
innerServExpKnowledge.setCategory(CATEGORY_INTERNAL);
knowledges.add(innerServExpKnowledge);
TbKnowledge innerServChatKnowledge = new TbKnowledge();
innerServChatKnowledge.setKnowledgeId("workcase_inner_service_chat");
innerServChatKnowledge.setTitle("客户服务话术模板");
innerServChatKnowledge.setAvatar("chat");
innerServChatKnowledge.setCategory(CATEGORY_INTERNAL);
knowledges.add(innerServChatKnowledge);
// 统一设置dify知识库相关参数,并设置service为workcase
return knowledges;
} }
/** private boolean checkKnowledgeExists(TbKnowledge knowledge){
* 检查知识库是否已存在 return true;
*/
private boolean checkKnowledgeExists(KnowledgeService knowledgeService, String title) {
TbKnowledge filter = new TbKnowledge();
filter.setTitle(title);
filter.setService(SERVICE_WORKCASE);
ResultDomain<TbKnowledge> result = knowledgeService.listKnowledges(filter);
if (result.getSuccess() && result.getDataList() != null && !result.getDataList().isEmpty()) {
return true;
}
return false;
} }
/** /**
* 创建知识库 * 创建知识库
*/ */
private boolean createKnowledge(KnowledgeService knowledgeService, KnowledgeConfig config) { private boolean createKnowledge(TbKnowledge knowledge) {
TbKnowledge knowledge = new TbKnowledge();
knowledge.setTitle(config.title);
knowledge.setDescription(config.description);
knowledge.setService(SERVICE_WORKCASE);
knowledge.setCategory(config.category);
knowledge.setDifyIndexingTechnique("high_quality");
knowledge.setRetrievalTopK(5);
knowledge.setRetrievalScoreThreshold(0.5);
knowledge.setRerankingEnable(1);
ResultDomain<TbKnowledge> result = knowledgeService.createKnowledge( ResultDomain<TbKnowledge> result = knowledgeService.createKnowledge(knowledge);
knowledge, PERMISSION_PUBLIC, null, null);
if (result.getSuccess()) { if (result.getSuccess()) {
logger.info("创建知识库成功: {} [{}]", config.title, config.category); logger.info("创建知识库成功: {} [{}]", knowledge.getTitle(), knowledge.getCategory());
return true; return true;
} else { } else {
logger.error("创建知识库失败: {} - {}", config.title, result.getMessage()); logger.error("创建知识库失败: {} - {}", knowledge.getTitle(), result.getMessage());
return false; return false;
} }
} }
/**
* 知识库配置内部类
*/
private static class KnowledgeConfig {
final String title;
final String description;
final String category;
KnowledgeConfig(String title, String description, String category) {
this.title = title;
this.description = description;
this.category = category;
}
}
} }