知识库数据隔离测试完成

This commit is contained in:
2025-11-08 13:42:33 +08:00
parent 38209a642d
commit 95919eb27b
13 changed files with 324 additions and 21 deletions

View File

@@ -3,6 +3,7 @@ package org.xyzh.ai.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.xyzh.common.core.page.PageParam;
import org.xyzh.common.dto.ai.TbAiKnowledge;
import org.xyzh.common.vo.UserDeptRoleVO;
@@ -48,7 +49,7 @@ public interface AiKnowledgeMapper extends BaseMapper<TbAiKnowledge> {
*/
List<TbAiKnowledge> selectKnowledgesPage(
@Param("filter") TbAiKnowledge filter,
@Param("pageParam") org.xyzh.common.core.page.PageParam pageParam,
@Param("pageParam") PageParam pageParam,
@Param("userDeptRoles") List<UserDeptRoleVO> userDeptRoles
);

View File

@@ -1,7 +1,5 @@
package org.xyzh.ai.service;
import java.util.List;
/**
* @description AI知识库Redis管理服务接口
* @filename AiKnowledgeRedisService.java
@@ -21,5 +19,19 @@ public interface AiKnowledgeRedisService {
* 清除所有知识库配置
*/
void clearAllKnowledgeConfig();
/**
* 添加知识库到部门缓存
* @param deptId 部门ID
* @param difyDatasetId Dify知识库ID
*/
void addKnowledgeToDeptCache(String deptId, String difyDatasetId);
/**
* 从部门缓存移除知识库
* @param deptId 部门ID
* @param difyDatasetId Dify知识库ID
*/
void removeKnowledgeFromDeptCache(String deptId, String difyDatasetId);
}

View File

@@ -122,13 +122,9 @@ public class AiKnowledgeRedisServiceImpl implements AiKnowledgeRedisService, Com
}
// 解析部门路径,例如:/1/3/5/ -> [1, 3, 5]
String[] deptIds = deptPath.split("/");
List<String> deptIdList = new ArrayList<>();
for (String deptId : deptIds) {
if (deptId != null && !deptId.isEmpty()) {
deptIdList.add(deptId);
}
}
Set<String> deptIdList = new HashSet<>(Arrays.asList(deptPath.split("/")));
deptIdList.add("root_department");
if (deptIdList.isEmpty()) {
log.warn("解析部门路径失败: {}", deptPath);
@@ -146,6 +142,7 @@ public class AiKnowledgeRedisServiceImpl implements AiKnowledgeRedisService, Com
}
}
List<String> resultList = new ArrayList<>(knowledgeIdSet);
log.info("根据部门路径 {} 获取到 {} 个知识库(已去重)", deptPath, resultList.size());
return resultList;
@@ -166,5 +163,81 @@ public class AiKnowledgeRedisServiceImpl implements AiKnowledgeRedisService, Com
log.error("清除所有知识库配置失败", e);
}
}
@Override
@SuppressWarnings("unchecked")
public void addKnowledgeToDeptCache(String deptId, String difyDatasetId) {
try {
if (deptId == null || deptId.isEmpty()) {
log.warn("部门ID为空无法添加知识库到缓存");
return;
}
if (difyDatasetId == null || difyDatasetId.isEmpty()) {
log.warn("Dify知识库ID为空无法添加到缓存");
return;
}
String key = REDIS_KEY_DEPT_KNOWLEDGE + deptId;
// 获取现有的知识库列表
List<String> knowledgeIds = (List<String>) redisTemplate.opsForValue().get(key);
if (knowledgeIds == null) {
knowledgeIds = new ArrayList<>();
}
// 避免重复添加
if (!knowledgeIds.contains(difyDatasetId)) {
knowledgeIds.add(difyDatasetId);
// 更新到Redis
redisTemplate.opsForValue().set(key, knowledgeIds, REDIS_EXPIRE_DAYS, TimeUnit.DAYS);
log.info("已添加知识库到部门缓存: deptId={}, difyDatasetId={}", deptId, difyDatasetId);
} else {
log.debug("知识库已存在于部门缓存中: deptId={}, difyDatasetId={}", deptId, difyDatasetId);
}
} catch (Exception e) {
log.error("添加知识库到部门缓存失败: deptId={}, difyDatasetId={}", deptId, difyDatasetId, e);
}
}
@Override
@SuppressWarnings("unchecked")
public void removeKnowledgeFromDeptCache(String deptId, String difyDatasetId) {
try {
if (deptId == null || deptId.isEmpty()) {
log.warn("部门ID为空无法从缓存移除知识库");
return;
}
if (difyDatasetId == null || difyDatasetId.isEmpty()) {
log.warn("Dify知识库ID为空无法从缓存移除");
return;
}
String key = REDIS_KEY_DEPT_KNOWLEDGE + deptId;
// 获取现有的知识库列表
List<String> knowledgeIds = (List<String>) redisTemplate.opsForValue().get(key);
if (knowledgeIds == null || knowledgeIds.isEmpty()) {
log.debug("部门缓存为空,无需移除: deptId={}", deptId);
return;
}
// 移除知识库
if (knowledgeIds.remove(difyDatasetId)) {
// 更新到Redis
if (knowledgeIds.isEmpty()) {
// 如果列表为空删除整个key
redisTemplate.delete(key);
log.info("部门缓存已空已删除key: deptId={}", deptId);
} else {
redisTemplate.opsForValue().set(key, knowledgeIds, REDIS_EXPIRE_DAYS, TimeUnit.DAYS);
}
log.info("已从部门缓存移除知识库: deptId={}, difyDatasetId={}", deptId, difyDatasetId);
} else {
log.debug("知识库不存在于部门缓存中: deptId={}, difyDatasetId={}", deptId, difyDatasetId);
}
} catch (Exception e) {
log.error("从部门缓存移除知识库失败: deptId={}, difyDatasetId={}", deptId, difyDatasetId, e);
}
}
}

View File

@@ -62,6 +62,9 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
@Autowired
private ResourcePermissionService resourcePermissionService;
@Autowired
private org.xyzh.ai.service.AiKnowledgeRedisService knowledgeRedisService;
@Override
@Transactional(rollbackFor = Exception.class)
public ResultDomain<TbAiKnowledge> createKnowledge(
@@ -232,12 +235,21 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
deptIds,
roleIds,
userDeptRoles.get(0)
);
);
} catch (Exception e) {
log.error("创建知识库权限失败", e);
// 权限创建失败不影响知识库创建,记录日志即可
}
// 6. 更新Redis缓存
try {
knowledgeRedisService.addKnowledgeToDeptCache(deptId, difyDatasetId);
log.info("已添加知识库到Redis缓存: deptId={}, difyDatasetId={}", deptId, difyDatasetId);
} catch (Exception e) {
log.error("更新Redis缓存失败", e);
// 缓存更新失败不影响知识库创建,记录日志即可
}
log.info("知识库创建成功: {} - {}", knowledge.getID(), knowledge.getTitle());
resultDomain.success("知识库创建成功", knowledge);
return resultDomain;
@@ -484,6 +496,17 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
int rows = knowledgeMapper.deleteKnowledge(deleteEntity);
if (rows > 0) {
// 6. 更新Redis缓存
try {
if (StringUtils.hasText(existing.getCreatorDept()) && StringUtils.hasText(existing.getDifyDatasetId())) {
knowledgeRedisService.removeKnowledgeFromDeptCache(existing.getCreatorDept(), existing.getDifyDatasetId());
log.info("已从Redis缓存移除知识库: deptId={}, difyDatasetId={}", existing.getCreatorDept(), existing.getDifyDatasetId());
}
} catch (Exception e) {
log.error("更新Redis缓存失败", e);
// 缓存更新失败不影响知识库删除,记录日志即可
}
log.info("知识库删除成功: {} - {}", knowledgeId, existing.getTitle());
resultDomain.success("知识库删除成功", true);
return resultDomain;
@@ -564,8 +587,8 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
PageParam resultPageParam = new PageParam(pageParam.getPageNumber(), pageParam.getPageSize());
resultPageParam.setTotalElements(total);
resultPageParam.setTotalPages((int) Math.ceil((double) total / pageParam.getPageSize()));
resultDomain.success("查询成功", knowledges);
PageDomain<TbAiKnowledge> pageDomain = new PageDomain<>(resultPageParam, knowledges);
resultDomain.success("查询成功", pageDomain);
return resultDomain;
} catch (Exception e) {

View File

@@ -35,6 +35,11 @@
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
<result column="delete_time" property="deleteTime" jdbcType="TIMESTAMP"/>
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/>
<!-- 权限字段 -->
<result column="permission_type" property="permissionType" jdbcType="VARCHAR"/>
<result column="can_read" property="canRead" jdbcType="BOOLEAN"/>
<result column="can_write" property="canWrite" jdbcType="BOOLEAN"/>
<result column="can_delete" property="canDelete" jdbcType="BOOLEAN"/>
</resultMap>
<!-- 基础字段 -->
@@ -106,10 +111,27 @@
<!-- selectAiKnowledges带权限过滤 -->
<select id="selectAiKnowledges" resultMap="BaseResultMap">
SELECT DISTINCT k.*
SELECT
k.*,
CASE
WHEN MAX(CASE WHEN rp.dept_id IS NULL AND rp.role_id IS NULL THEN 1 ELSE 0 END) = 1 THEN 'all'
WHEN MAX(CASE WHEN rp.dept_id IS NOT NULL AND rp.role_id IS NULL THEN 1 ELSE 0 END) = 1 THEN 'dept'
WHEN MAX(CASE WHEN rp.dept_id IS NULL AND rp.role_id IS NOT NULL THEN 1 ELSE 0 END) = 1 THEN 'role'
ELSE 'custom'
END as permission_type,
MAX(rp.can_read) as can_read,
MAX(rp.can_write) as can_write,
MAX(rp.can_execute) as can_delete
FROM tb_ai_knowledge k
<include refid="Permission_Filter"/>
<include refid="Filter_Clause"/>
GROUP BY k.id, k.title, k.avatar, k.description, k.content, k.source_type, k.source_id,
k.file_name, k.file_path, k.category, k.tags, k.dify_dataset_id,
k.dify_indexing_technique, k.embedding_model, k.embedding_model_provider,
k.rerank_model, k.rerank_model_provider, k.reranking_enable,
k.retrieval_top_k, k.retrieval_score_threshold, k.vector_id,
k.document_count, k.total_chunks, k.status, k.creator, k.creator_dept,
k.updater, k.create_time, k.update_time, k.delete_time, k.deleted
ORDER BY k.create_time DESC
</select>
@@ -255,10 +277,27 @@
<!-- selectKnowledgesPage分页查询知识库带权限过滤 -->
<select id="selectKnowledgesPage" resultMap="BaseResultMap">
SELECT DISTINCT k.*
SELECT
k.*,
CASE
WHEN MAX(CASE WHEN rp.dept_id IS NULL AND rp.role_id IS NULL THEN 1 ELSE 0 END) = 1 THEN 'all'
WHEN MAX(CASE WHEN rp.dept_id IS NOT NULL AND rp.role_id IS NULL THEN 1 ELSE 0 END) = 1 THEN 'dept'
WHEN MAX(CASE WHEN rp.dept_id IS NULL AND rp.role_id IS NOT NULL THEN 1 ELSE 0 END) = 1 THEN 'role'
ELSE 'custom'
END as permission_type,
MAX(rp.can_read) as can_read,
MAX(rp.can_write) as can_write,
MAX(rp.can_execute) as can_delete
FROM tb_ai_knowledge k
<include refid="Permission_Filter"/>
<include refid="Filter_Clause"/>
GROUP BY k.id, k.title, k.avatar, k.description, k.content, k.source_type, k.source_id,
k.file_name, k.file_path, k.category, k.tags, k.dify_dataset_id,
k.dify_indexing_technique, k.embedding_model, k.embedding_model_provider,
k.rerank_model, k.rerank_model_provider, k.reranking_enable,
k.retrieval_top_k, k.retrieval_score_threshold, k.vector_id,
k.document_count, k.total_chunks, k.status, k.creator, k.creator_dept,
k.updater, k.create_time, k.update_time, k.delete_time, k.deleted
ORDER BY k.create_time DESC
LIMIT #{pageParam.offset}, #{pageParam.pageSize}
</select>