redis反序列化问题
This commit is contained in:
@@ -66,7 +66,7 @@ public class KnowledgeController {
|
||||
return ResultDomain.failure(result.getAllErrors());
|
||||
}
|
||||
logger.info("创建知识库: title={}", knowledge.getTitle());
|
||||
return knowledgeService.createKnowledge(knowledge, "PUBLIC", null, null);
|
||||
return knowledgeService.createKnowledge(knowledge);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -288,7 +288,7 @@ public class AgentChatServiceImpl implements AgentChatService {
|
||||
// 1. 从Redis获取会话数据
|
||||
String cacheKey = CHAT_SESSION_PREFIX + sessionId;
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> sessionData = (Map<String, Object>) redisService.get(cacheKey);
|
||||
Map<String, Object> sessionData = redisService.get(cacheKey, Map.class);
|
||||
|
||||
if (sessionData == null) {
|
||||
try {
|
||||
|
||||
@@ -187,7 +187,7 @@ public class AgentServiceImpl implements AgentService {
|
||||
String cacheKey = AGENT_CACHE_PREFIX + agentId;
|
||||
|
||||
// 1. 先从缓存获取
|
||||
TbAgent agent = (TbAgent) redisService.get(cacheKey);
|
||||
TbAgent agent = redisService.get(cacheKey, TbAgent.class);
|
||||
if (agent != null) {
|
||||
return ResultDomain.success("查询成功", agent);
|
||||
}
|
||||
@@ -196,7 +196,7 @@ public class AgentServiceImpl implements AgentService {
|
||||
agentLock.lock();
|
||||
try {
|
||||
// 再次检查缓存
|
||||
agent = (TbAgent) redisService.get(cacheKey);
|
||||
agent = redisService.get(cacheKey, TbAgent.class);
|
||||
if (agent != null) {
|
||||
return ResultDomain.success("查询成功", agent);
|
||||
}
|
||||
|
||||
@@ -63,15 +63,12 @@ public class KnowledgeServiceImpl implements KnowledgeService {
|
||||
* @description 创建知识库基础信息,包含dify知识库各种参数的配置
|
||||
* 注意:涉及外部API调用,不使用@Transactional,采用手动补偿机制
|
||||
* @param knowledge 知识库信息
|
||||
* @param permissionType 权限类型:PUBLIC-公开,DEPARTMENT-部门,ROLE-角色,PRIVATE-私有
|
||||
* @param deptIds 部门ID列表(DEPARTMENT类型需要)
|
||||
* @param roleIds 角色ID列表(ROLE/PRIVATE类型需要)
|
||||
* @return ResultDomain<TbKnowledge> 创建结果
|
||||
* @author yslg
|
||||
* @since 2025-12-18
|
||||
*/
|
||||
@Override
|
||||
public ResultDomain<TbKnowledge> createKnowledge(TbKnowledge knowledge, String permissionType, List<String> deptIds, List<String> roleIds) {
|
||||
public ResultDomain<TbKnowledge> createKnowledge(TbKnowledge knowledge) {
|
||||
// 1. 参数校验
|
||||
if (!StringUtils.hasText(knowledge.getTitle())) {
|
||||
return ResultDomain.failure("知识库标题不能为空");
|
||||
|
||||
@@ -16,19 +16,13 @@ public interface KnowledgeService {
|
||||
/**
|
||||
* @description 创建知识库基础信息,包含dify知识库各种参数的配置
|
||||
* @param knowledge 知识库信息
|
||||
* @param permissionType 权限类型:PUBLIC-公开,DEPARTMENT-部门,ROLE-角色,PRIVATE-私有
|
||||
* @param deptIds 部门ID列表(DEPARTMENT类型需要)
|
||||
* @param roleIds 角色ID列表(ROLE/PRIVATE类型需要)
|
||||
* @return ResultDomain<TbKnowledge> 创建结果
|
||||
* @author yslg
|
||||
* @since 2025-12-18
|
||||
*/
|
||||
ResultDomain<TbKnowledge> createKnowledge(
|
||||
TbKnowledge knowledge,
|
||||
String permissionType,
|
||||
List<String> deptIds,
|
||||
List<String> roleIds
|
||||
);
|
||||
ResultDomain<TbKnowledge> createKnowledge(TbKnowledge knowledge);
|
||||
|
||||
/**
|
||||
* @description 更新知识库,包含dify知识库各种参数的配置
|
||||
|
||||
@@ -210,7 +210,7 @@ public class AuthController {
|
||||
|
||||
// 通过sessionId验证手机验证码
|
||||
String smsCodeKey = "sms:code:" + smsSessionId;
|
||||
String storedSmsValue = (String) redisService.get(smsCodeKey);
|
||||
String storedSmsValue = redisService.get(smsCodeKey, String.class);
|
||||
if (storedSmsValue == null) {
|
||||
return ResultDomain.failure("验证码已过期,请重新获取");
|
||||
}
|
||||
@@ -258,7 +258,7 @@ public class AuthController {
|
||||
|
||||
// 通过sessionId验证邮箱验证码
|
||||
String emailCodeKey = "email:code:" + emailSessionId;
|
||||
String storedEmailValue = (String) redisService.get(emailCodeKey);
|
||||
String storedEmailValue = redisService.get(emailCodeKey, String.class);
|
||||
if (storedEmailValue == null) {
|
||||
return ResultDomain.failure("验证码已过期,请重新获取");
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ public class AuthServiceImpl implements AuthService{
|
||||
|
||||
// 2. 从Redis获取登录信息
|
||||
String loginKey = "login:token:" + token;
|
||||
String loginJson = (String) redisService.get(loginKey);
|
||||
String loginJson = redisService.get(loginKey, String.class);
|
||||
if (loginJson == null) {
|
||||
return ResultDomain.failure("登录信息已失效");
|
||||
}
|
||||
@@ -422,7 +422,7 @@ public class AuthServiceImpl implements AuthService{
|
||||
|
||||
// 2. 从Redis获取登录信息
|
||||
String loginKey = "login:token:" + token;
|
||||
String loginJson = (String) redisService.get(loginKey);
|
||||
String loginJson = redisService.get(loginKey, String.class);
|
||||
if (loginJson == null) {
|
||||
return ResultDomain.failure("登录信息已失效");
|
||||
}
|
||||
@@ -481,7 +481,7 @@ public class AuthServiceImpl implements AuthService{
|
||||
|
||||
// 1. 从Redis获取登录信息
|
||||
String loginKey = "login:token:" + token;
|
||||
String loginJson = (String) redisService.get(loginKey);
|
||||
String loginJson = redisService.get(loginKey, String.class);
|
||||
if (loginJson == null) {
|
||||
return ResultDomain.success("登出成功", (LoginDomain) null); // 已经过期的token,直接返回成功
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public class EmailLoginStrategy implements LoginStrategy {
|
||||
|
||||
// 从Redis获取验证码
|
||||
String codeKey = "email:code:" + captchaId;
|
||||
String storedValue = (String) redisService.get(codeKey);
|
||||
String storedValue = redisService.get(codeKey, String.class);
|
||||
|
||||
if (storedValue == null) {
|
||||
return false;
|
||||
|
||||
@@ -78,7 +78,7 @@ public class PhoneLoginStrategy implements LoginStrategy {
|
||||
|
||||
// 从Redis获取验证码
|
||||
String codeKey = "sms:code:" + captchaId;
|
||||
String storedValue = (String) redisService.get(codeKey);
|
||||
String storedValue = redisService.get(codeKey, String.class);
|
||||
|
||||
if (storedValue == null) {
|
||||
return false;
|
||||
|
||||
@@ -78,7 +78,7 @@ public class HttpLoginArgumentResolver implements HandlerMethodArgumentResolver
|
||||
// 从Redis中获取LoginDomain
|
||||
String userId = tokenParser.getUserIdFromToken(token);
|
||||
String redisKey = REDIS_LOGIN_PREFIX + userId;
|
||||
LoginDomain loginDomain = (LoginDomain) redisService.get(redisKey);
|
||||
LoginDomain loginDomain = redisService.get(redisKey, LoginDomain.class);
|
||||
|
||||
if (loginDomain == null) {
|
||||
if (httpLogin.required()) {
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.xyzh.common.core.domain.LoginDomain;
|
||||
@@ -107,18 +106,8 @@ public class GatewayAuthConfig {
|
||||
if (StringUtils.hasText(authHeader) && authHeader.startsWith(BEARER_PREFIX)) {
|
||||
String token = authHeader.substring(BEARER_PREFIX.length());
|
||||
String cacheKey = LOGIN_TOKEN_PREFIX + token;
|
||||
Object obj = redisService.get(cacheKey);
|
||||
|
||||
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");
|
||||
}
|
||||
LoginDomain login = redisService.get(cacheKey, LoginDomain.class);
|
||||
logger.info("Redis key: {}, login: {}", cacheKey, login != null ? "loaded" : "null");
|
||||
|
||||
if (login != null) {
|
||||
if (login.getUserPermissions() != null) {
|
||||
|
||||
@@ -8,8 +8,6 @@ import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.xyzh.common.core.domain.LoginDomain;
|
||||
import org.xyzh.common.redis.service.RedisService;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
@@ -48,13 +46,7 @@ public class LoginUtil {
|
||||
|
||||
try {
|
||||
String cacheKey = LOGIN_TOKEN_PREFIX + token;
|
||||
Object obj = instance.redisService.get(cacheKey);
|
||||
if (obj instanceof LoginDomain) {
|
||||
return (LoginDomain) obj;
|
||||
} else if (obj instanceof String) {
|
||||
// Redis 返回的是 JSON 字符串,需要反序列化
|
||||
return JSON.parseObject((String) obj, LoginDomain.class);
|
||||
}
|
||||
return instance.redisService.get(cacheKey, LoginDomain.class);
|
||||
} catch (Exception e) {
|
||||
// 忽略异常
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
|
||||
/**
|
||||
* @description RedisService.java Redis工具服务类,封装常用Redis操作
|
||||
* @filename RedisService.java
|
||||
@@ -56,6 +58,28 @@ public class RedisService {
|
||||
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
|
||||
* @param key String 键
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
<groupId>org.xyzh.apis</groupId>
|
||||
<artifactId>api-workcase</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xyzh.apis</groupId>
|
||||
<artifactId>api-system</artifactId>
|
||||
</dependency>
|
||||
<!-- <dependency>
|
||||
<groupId>org.xyzh.apis</groupId>
|
||||
<artifactId>api-ai</artifactId>
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.dubbo.config.annotation.DubboReference;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.EXTERNAL;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
@@ -11,6 +12,7 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.xyzh.api.ai.dto.TbKnowledge;
|
||||
import org.xyzh.api.ai.service.KnowledgeService;
|
||||
import org.xyzh.api.system.service.SysConfigService;
|
||||
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 CATEGORY_INTERNAL = "internal";
|
||||
private static final String CATEGORY_EXTERNAL = "external";
|
||||
private static final String PERMISSION_PUBLIC = "PUBLIC";
|
||||
|
||||
@DubboReference(version = "1.0.0", group = "ai", timeout = 30000)
|
||||
private KnowledgeService knowledgeService;
|
||||
|
||||
@DubboReference(version = "1.0.0", group = "system", timeout = 30000)
|
||||
private SysConfigService sysConfigService;
|
||||
|
||||
@Bean
|
||||
public CommandLineRunner knowledgeInitRunner() {
|
||||
return args -> {
|
||||
logger.info("开始初始化客服系统知识库...");
|
||||
|
||||
List<KnowledgeConfig> configs = buildKnowledgeConfigs();
|
||||
List<TbKnowledge> knowledges = buildKnowledgeConfigs();
|
||||
int successCount = 0;
|
||||
int skipCount = 0;
|
||||
|
||||
for (KnowledgeConfig config : configs) {
|
||||
if (checkKnowledgeExists(knowledgeService, config.title)) {
|
||||
logger.info("知识库已存在,跳过: {}", config.title);
|
||||
for (TbKnowledge knowledge : knowledges) {
|
||||
if (checkKnowledgeExists(knowledge)) {
|
||||
logger.info("知识库已存在,跳过: {}", knowledge.getTitle());
|
||||
skipCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (createKnowledge(knowledgeService, config)) {
|
||||
if (createKnowledge(knowledge)) {
|
||||
successCount++;
|
||||
}
|
||||
}
|
||||
@@ -60,109 +64,88 @@ public class KnowledgeInit {
|
||||
/**
|
||||
* 构建8个知识库配置
|
||||
*/
|
||||
private List<KnowledgeConfig> buildKnowledgeConfigs() {
|
||||
List<KnowledgeConfig> configs = new ArrayList<>();
|
||||
private List<TbKnowledge> buildKnowledgeConfigs() {
|
||||
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);
|
||||
|
||||
TbKnowledge outerCommonErrorKnowledge = new TbKnowledge();
|
||||
outerCommonErrorKnowledge.setKnowledgeId("workcase_outer_common_error");
|
||||
outerCommonErrorKnowledge.setTitle("常见故障解决方案");
|
||||
outerCommonErrorKnowledge.setAvatar("question");
|
||||
outerCommonErrorKnowledge.setCategory(CATEGORY_EXTERNAL);
|
||||
knowledges.add(outerCommonErrorKnowledge);
|
||||
|
||||
TbKnowledge outerSanBaoKnowledge = new TbKnowledge();
|
||||
outerSanBaoKnowledge.setKnowledgeId("workcase_outer_sanbao");
|
||||
outerSanBaoKnowledge.setTitle("三包外服务政策");
|
||||
outerSanBaoKnowledge.setAvatar("document");
|
||||
outerSanBaoKnowledge.setCategory(CATEGORY_EXTERNAL);
|
||||
knowledges.add(outerSanBaoKnowledge);
|
||||
|
||||
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
|
||||
|
||||
// 4个内部知识库
|
||||
configs.add(new KnowledgeConfig(
|
||||
"内部-政策法规库",
|
||||
"存储城市生命线相关政策法规、行业标准、规范文件",
|
||||
CATEGORY_INTERNAL
|
||||
));
|
||||
configs.add(new KnowledgeConfig(
|
||||
"内部-技术文档库",
|
||||
"存储技术规范、操作手册、维护指南等技术文档",
|
||||
CATEGORY_INTERNAL
|
||||
));
|
||||
configs.add(new KnowledgeConfig(
|
||||
"内部-案例经验库",
|
||||
"存储历史工单处理案例、经验总结、故障排查记录",
|
||||
CATEGORY_INTERNAL
|
||||
));
|
||||
configs.add(new KnowledgeConfig(
|
||||
"内部-培训资料库",
|
||||
"存储员工培训材料、业务知识、常见问题解答",
|
||||
CATEGORY_INTERNAL
|
||||
));
|
||||
|
||||
// 4个外部知识库
|
||||
configs.add(new KnowledgeConfig(
|
||||
"外部-服务指南库",
|
||||
"面向公众的服务办理指南、流程说明、所需材料",
|
||||
CATEGORY_EXTERNAL
|
||||
));
|
||||
configs.add(new KnowledgeConfig(
|
||||
"外部-常见问题库",
|
||||
"公众常见咨询问题及标准答案,FAQ知识库",
|
||||
CATEGORY_EXTERNAL
|
||||
));
|
||||
configs.add(new KnowledgeConfig(
|
||||
"外部-公告通知库",
|
||||
"政府公告、服务通知、停水停电通知等公开信息",
|
||||
CATEGORY_EXTERNAL
|
||||
));
|
||||
configs.add(new KnowledgeConfig(
|
||||
"外部-便民信息库",
|
||||
"便民服务信息、联系方式、服务网点、办事地点等",
|
||||
CATEGORY_EXTERNAL
|
||||
));
|
||||
|
||||
return configs;
|
||||
return knowledges;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查知识库是否已存在
|
||||
*/
|
||||
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 checkKnowledgeExists(TbKnowledge knowledge){
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建知识库
|
||||
*/
|
||||
private boolean createKnowledge(KnowledgeService knowledgeService, KnowledgeConfig config) {
|
||||
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);
|
||||
private boolean createKnowledge(TbKnowledge knowledge) {
|
||||
|
||||
ResultDomain<TbKnowledge> result = knowledgeService.createKnowledge(
|
||||
knowledge, PERMISSION_PUBLIC, null, null);
|
||||
ResultDomain<TbKnowledge> result = knowledgeService.createKnowledge(knowledge);
|
||||
|
||||
if (result.getSuccess()) {
|
||||
logger.info("创建知识库成功: {} [{}]", config.title, config.category);
|
||||
logger.info("创建知识库成功: {} [{}]", knowledge.getTitle(), knowledge.getCategory());
|
||||
return true;
|
||||
} else {
|
||||
logger.error("创建知识库失败: {} - {}", config.title, result.getMessage());
|
||||
logger.error("创建知识库失败: {} - {}", knowledge.getTitle(), result.getMessage());
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user