知识库数据隔离测试完成
This commit is contained in:
@@ -102,6 +102,9 @@
|
||||
<Logger name="org.xyzh.achievement.mapper" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<Logger name="org.xyzh.ai.mapper" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
<Logger name="org.xyzh.system.mapper" level="debug" additivity="false">
|
||||
<AppenderRef ref="Console"/>
|
||||
</Logger>
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -143,6 +143,28 @@ public class TbAiKnowledge extends BaseDTO {
|
||||
*/
|
||||
private String updater;
|
||||
|
||||
// ========== 权限相关字段(不映射到数据库) ==========
|
||||
|
||||
/**
|
||||
* @description 权限类型(all/dept/role/custom)
|
||||
*/
|
||||
private transient String permissionType;
|
||||
|
||||
/**
|
||||
* @description 是否可读
|
||||
*/
|
||||
private transient Boolean canRead;
|
||||
|
||||
/**
|
||||
* @description 是否可写
|
||||
*/
|
||||
private transient Boolean canWrite;
|
||||
|
||||
/**
|
||||
* @description 是否可删除
|
||||
*/
|
||||
private transient Boolean canDelete;
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
@@ -351,6 +373,38 @@ public class TbAiKnowledge extends BaseDTO {
|
||||
this.retrievalScoreThreshold = retrievalScoreThreshold;
|
||||
}
|
||||
|
||||
public String getPermissionType() {
|
||||
return permissionType;
|
||||
}
|
||||
|
||||
public void setPermissionType(String permissionType) {
|
||||
this.permissionType = permissionType;
|
||||
}
|
||||
|
||||
public Boolean getCanRead() {
|
||||
return canRead;
|
||||
}
|
||||
|
||||
public void setCanRead(Boolean canRead) {
|
||||
this.canRead = canRead;
|
||||
}
|
||||
|
||||
public Boolean getCanWrite() {
|
||||
return canWrite;
|
||||
}
|
||||
|
||||
public void setCanWrite(Boolean canWrite) {
|
||||
this.canWrite = canWrite;
|
||||
}
|
||||
|
||||
public Boolean getCanDelete() {
|
||||
return canDelete;
|
||||
}
|
||||
|
||||
public void setCanDelete(Boolean canDelete) {
|
||||
this.canDelete = canDelete;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TbAiKnowledge{" +
|
||||
|
||||
@@ -74,7 +74,8 @@
|
||||
d.description AS dept_description,
|
||||
dr.role_id,
|
||||
r.name AS role_name,
|
||||
r.description AS role_description
|
||||
r.description AS role_description,
|
||||
d.parent_id
|
||||
FROM tb_sys_dept_role dr
|
||||
LEFT JOIN tb_sys_dept d ON dr.dept_id = d.dept_id AND d.deleted = 0
|
||||
LEFT JOIN tb_sys_role r ON dr.role_id = r.role_id AND r.deleted = 0
|
||||
|
||||
Reference in New Issue
Block a user