知识库数据隔离测试完成
This commit is contained in:
@@ -102,6 +102,9 @@
|
|||||||
<Logger name="org.xyzh.achievement.mapper" level="debug" additivity="false">
|
<Logger name="org.xyzh.achievement.mapper" level="debug" additivity="false">
|
||||||
<AppenderRef ref="Console"/>
|
<AppenderRef ref="Console"/>
|
||||||
</Logger>
|
</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">
|
<Logger name="org.xyzh.system.mapper" level="debug" additivity="false">
|
||||||
<AppenderRef ref="Console"/>
|
<AppenderRef ref="Console"/>
|
||||||
</Logger>
|
</Logger>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.xyzh.ai.mapper;
|
|||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.xyzh.common.core.page.PageParam;
|
||||||
import org.xyzh.common.dto.ai.TbAiKnowledge;
|
import org.xyzh.common.dto.ai.TbAiKnowledge;
|
||||||
import org.xyzh.common.vo.UserDeptRoleVO;
|
import org.xyzh.common.vo.UserDeptRoleVO;
|
||||||
|
|
||||||
@@ -48,7 +49,7 @@ public interface AiKnowledgeMapper extends BaseMapper<TbAiKnowledge> {
|
|||||||
*/
|
*/
|
||||||
List<TbAiKnowledge> selectKnowledgesPage(
|
List<TbAiKnowledge> selectKnowledgesPage(
|
||||||
@Param("filter") TbAiKnowledge filter,
|
@Param("filter") TbAiKnowledge filter,
|
||||||
@Param("pageParam") org.xyzh.common.core.page.PageParam pageParam,
|
@Param("pageParam") PageParam pageParam,
|
||||||
@Param("userDeptRoles") List<UserDeptRoleVO> userDeptRoles
|
@Param("userDeptRoles") List<UserDeptRoleVO> userDeptRoles
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package org.xyzh.ai.service;
|
package org.xyzh.ai.service;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description AI知识库Redis管理服务接口
|
* @description AI知识库Redis管理服务接口
|
||||||
* @filename AiKnowledgeRedisService.java
|
* @filename AiKnowledgeRedisService.java
|
||||||
@@ -21,5 +19,19 @@ public interface AiKnowledgeRedisService {
|
|||||||
* 清除所有知识库配置
|
* 清除所有知识库配置
|
||||||
*/
|
*/
|
||||||
void clearAllKnowledgeConfig();
|
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]
|
// 解析部门路径,例如:/1/3/5/ -> [1, 3, 5]
|
||||||
String[] deptIds = deptPath.split("/");
|
Set<String> deptIdList = new HashSet<>(Arrays.asList(deptPath.split("/")));
|
||||||
List<String> deptIdList = new ArrayList<>();
|
deptIdList.add("root_department");
|
||||||
for (String deptId : deptIds) {
|
|
||||||
if (deptId != null && !deptId.isEmpty()) {
|
|
||||||
deptIdList.add(deptId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deptIdList.isEmpty()) {
|
if (deptIdList.isEmpty()) {
|
||||||
log.warn("解析部门路径失败: {}", deptPath);
|
log.warn("解析部门路径失败: {}", deptPath);
|
||||||
@@ -146,6 +142,7 @@ public class AiKnowledgeRedisServiceImpl implements AiKnowledgeRedisService, Com
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
List<String> resultList = new ArrayList<>(knowledgeIdSet);
|
List<String> resultList = new ArrayList<>(knowledgeIdSet);
|
||||||
log.info("根据部门路径 {} 获取到 {} 个知识库(已去重)", deptPath, resultList.size());
|
log.info("根据部门路径 {} 获取到 {} 个知识库(已去重)", deptPath, resultList.size());
|
||||||
return resultList;
|
return resultList;
|
||||||
@@ -166,5 +163,81 @@ public class AiKnowledgeRedisServiceImpl implements AiKnowledgeRedisService, Com
|
|||||||
log.error("清除所有知识库配置失败", e);
|
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
|
@Autowired
|
||||||
private ResourcePermissionService resourcePermissionService;
|
private ResourcePermissionService resourcePermissionService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private org.xyzh.ai.service.AiKnowledgeRedisService knowledgeRedisService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public ResultDomain<TbAiKnowledge> createKnowledge(
|
public ResultDomain<TbAiKnowledge> createKnowledge(
|
||||||
@@ -232,12 +235,21 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
|
|||||||
deptIds,
|
deptIds,
|
||||||
roleIds,
|
roleIds,
|
||||||
userDeptRoles.get(0)
|
userDeptRoles.get(0)
|
||||||
);
|
);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("创建知识库权限失败", 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());
|
log.info("知识库创建成功: {} - {}", knowledge.getID(), knowledge.getTitle());
|
||||||
resultDomain.success("知识库创建成功", knowledge);
|
resultDomain.success("知识库创建成功", knowledge);
|
||||||
return resultDomain;
|
return resultDomain;
|
||||||
@@ -484,6 +496,17 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
|
|||||||
|
|
||||||
int rows = knowledgeMapper.deleteKnowledge(deleteEntity);
|
int rows = knowledgeMapper.deleteKnowledge(deleteEntity);
|
||||||
if (rows > 0) {
|
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());
|
log.info("知识库删除成功: {} - {}", knowledgeId, existing.getTitle());
|
||||||
resultDomain.success("知识库删除成功", true);
|
resultDomain.success("知识库删除成功", true);
|
||||||
return resultDomain;
|
return resultDomain;
|
||||||
@@ -564,8 +587,8 @@ public class AiKnowledgeServiceImpl implements AiKnowledgeService {
|
|||||||
PageParam resultPageParam = new PageParam(pageParam.getPageNumber(), pageParam.getPageSize());
|
PageParam resultPageParam = new PageParam(pageParam.getPageNumber(), pageParam.getPageSize());
|
||||||
resultPageParam.setTotalElements(total);
|
resultPageParam.setTotalElements(total);
|
||||||
resultPageParam.setTotalPages((int) Math.ceil((double) total / pageParam.getPageSize()));
|
resultPageParam.setTotalPages((int) Math.ceil((double) total / pageParam.getPageSize()));
|
||||||
|
PageDomain<TbAiKnowledge> pageDomain = new PageDomain<>(resultPageParam, knowledges);
|
||||||
resultDomain.success("查询成功", knowledges);
|
resultDomain.success("查询成功", pageDomain);
|
||||||
return resultDomain;
|
return resultDomain;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -35,6 +35,11 @@
|
|||||||
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
|
<result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
|
||||||
<result column="delete_time" property="deleteTime" jdbcType="TIMESTAMP"/>
|
<result column="delete_time" property="deleteTime" jdbcType="TIMESTAMP"/>
|
||||||
<result column="deleted" property="deleted" jdbcType="BOOLEAN"/>
|
<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>
|
</resultMap>
|
||||||
|
|
||||||
<!-- 基础字段 -->
|
<!-- 基础字段 -->
|
||||||
@@ -106,10 +111,27 @@
|
|||||||
|
|
||||||
<!-- selectAiKnowledges(带权限过滤) -->
|
<!-- selectAiKnowledges(带权限过滤) -->
|
||||||
<select id="selectAiKnowledges" resultMap="BaseResultMap">
|
<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
|
FROM tb_ai_knowledge k
|
||||||
<include refid="Permission_Filter"/>
|
<include refid="Permission_Filter"/>
|
||||||
<include refid="Filter_Clause"/>
|
<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
|
ORDER BY k.create_time DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
@@ -255,10 +277,27 @@
|
|||||||
|
|
||||||
<!-- selectKnowledgesPage(分页查询知识库,带权限过滤) -->
|
<!-- selectKnowledgesPage(分页查询知识库,带权限过滤) -->
|
||||||
<select id="selectKnowledgesPage" resultMap="BaseResultMap">
|
<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
|
FROM tb_ai_knowledge k
|
||||||
<include refid="Permission_Filter"/>
|
<include refid="Permission_Filter"/>
|
||||||
<include refid="Filter_Clause"/>
|
<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
|
ORDER BY k.create_time DESC
|
||||||
LIMIT #{pageParam.offset}, #{pageParam.pageSize}
|
LIMIT #{pageParam.offset}, #{pageParam.pageSize}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -143,6 +143,28 @@ public class TbAiKnowledge extends BaseDTO {
|
|||||||
*/
|
*/
|
||||||
private String updater;
|
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() {
|
public String getTitle() {
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
@@ -351,6 +373,38 @@ public class TbAiKnowledge extends BaseDTO {
|
|||||||
this.retrievalScoreThreshold = retrievalScoreThreshold;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "TbAiKnowledge{" +
|
return "TbAiKnowledge{" +
|
||||||
|
|||||||
@@ -74,7 +74,8 @@
|
|||||||
d.description AS dept_description,
|
d.description AS dept_description,
|
||||||
dr.role_id,
|
dr.role_id,
|
||||||
r.name AS role_name,
|
r.name AS role_name,
|
||||||
r.description AS role_description
|
r.description AS role_description,
|
||||||
|
d.parent_id
|
||||||
FROM tb_sys_dept_role dr
|
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_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
|
LEFT JOIN tb_sys_role r ON dr.role_id = r.role_id AND r.deleted = 0
|
||||||
|
|||||||
@@ -6,6 +6,11 @@
|
|||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
<link rel="icon" href="/schoolNewsWeb/favicon.ico">
|
<link rel="icon" href="/schoolNewsWeb/favicon.ico">
|
||||||
<title>校园新闻管理系统</title>
|
<title>校园新闻管理系统</title>
|
||||||
|
<style>
|
||||||
|
body{
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
|
|||||||
@@ -99,6 +99,14 @@ export interface AiKnowledge extends BaseDTO {
|
|||||||
creatorDept?: string;
|
creatorDept?: string;
|
||||||
/** 更新者 */
|
/** 更新者 */
|
||||||
updater?: string;
|
updater?: string;
|
||||||
|
/** 是否可读 */
|
||||||
|
canRead?: boolean;
|
||||||
|
/** 是否可写(修改知识库和文档) */
|
||||||
|
canWrite?: boolean;
|
||||||
|
/** 是否可删除(删除知识库和文档) */
|
||||||
|
canDelete?: boolean;
|
||||||
|
/** 权限类型(all/department/role等) */
|
||||||
|
permissionType?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
type="success"
|
type="success"
|
||||||
@click="showAddSegmentDialog = true"
|
@click="showAddSegmentDialog = true"
|
||||||
size="default"
|
size="default"
|
||||||
|
:disabled="!props.canWrite"
|
||||||
|
:title="props.canWrite ? '添加分段' : '无添加权限'"
|
||||||
>
|
>
|
||||||
添加分段
|
添加分段
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -46,8 +48,10 @@
|
|||||||
:model-value="segment.enabled"
|
:model-value="segment.enabled"
|
||||||
:active-text="segment.enabled ? '已启用' : '已禁用'"
|
:active-text="segment.enabled ? '已启用' : '已禁用'"
|
||||||
:loading="segment._switching"
|
:loading="segment._switching"
|
||||||
|
:disabled="!props.canWrite"
|
||||||
@change="handleToggleEnabled(segment, $event)"
|
@change="handleToggleEnabled(segment, $event)"
|
||||||
style="--el-switch-on-color: #67C23A; margin-right: 12px;"
|
style="--el-switch-on-color: #67C23A; margin-right: 12px;"
|
||||||
|
:title="props.canWrite ? '' : '无修改权限'"
|
||||||
/>
|
/>
|
||||||
<el-tag
|
<el-tag
|
||||||
:type="getStatusType(segment.status)"
|
:type="getStatusType(segment.status)"
|
||||||
@@ -62,6 +66,8 @@
|
|||||||
type="primary"
|
type="primary"
|
||||||
size="small"
|
size="small"
|
||||||
@click="startEdit(segment)"
|
@click="startEdit(segment)"
|
||||||
|
:disabled="!props.canWrite"
|
||||||
|
:title="props.canWrite ? '编辑分段' : '无编辑权限'"
|
||||||
>
|
>
|
||||||
编辑
|
编辑
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -72,6 +78,8 @@
|
|||||||
size="small"
|
size="small"
|
||||||
@click="handleDeleteSegment(segment)"
|
@click="handleDeleteSegment(segment)"
|
||||||
:loading="segment._deleting"
|
:loading="segment._deleting"
|
||||||
|
:disabled="!props.canDelete"
|
||||||
|
:title="props.canDelete ? '删除分段' : '无删除权限'"
|
||||||
>
|
>
|
||||||
删除
|
删除
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -211,6 +219,10 @@ interface Props {
|
|||||||
datasetId: string;
|
datasetId: string;
|
||||||
/** Dify文档ID */
|
/** Dify文档ID */
|
||||||
documentId: string;
|
documentId: string;
|
||||||
|
/** 是否可写(修改分段) */
|
||||||
|
canWrite?: boolean;
|
||||||
|
/** 是否可删除(删除分段) */
|
||||||
|
canDelete?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
|
|||||||
@@ -41,13 +41,30 @@
|
|||||||
|
|
||||||
<!-- 操作按钮 -->
|
<!-- 操作按钮 -->
|
||||||
<div class="knowledge-actions" @click.stop>
|
<div class="knowledge-actions" @click.stop>
|
||||||
<el-button size="small" @click="handleEdit">编辑</el-button>
|
<el-button
|
||||||
<el-button size="small" type="danger" @click="handleDelete">删除</el-button>
|
size="small"
|
||||||
|
@click="handleEdit"
|
||||||
|
:disabled="!hasWritePermission"
|
||||||
|
:title="hasWritePermission ? '编辑知识库' : '无编辑权限'"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
size="small"
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete"
|
||||||
|
:disabled="!hasDeletePermission"
|
||||||
|
:title="hasDeletePermission ? '删除知识库' : '无删除权限'"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useStore } from 'vuex';
|
||||||
import type { AiKnowledge } from '@/types/ai';
|
import type { AiKnowledge } from '@/types/ai';
|
||||||
import { FILE_DOWNLOAD_URL } from '@/config';
|
import { FILE_DOWNLOAD_URL } from '@/config';
|
||||||
|
|
||||||
@@ -62,6 +79,19 @@ interface Props {
|
|||||||
const props = defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
const emit = defineEmits(['click', 'edit', 'delete', 'sync']);
|
const emit = defineEmits(['click', 'edit', 'delete', 'sync']);
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
const currentUserId = computed(() => store.getters['auth/user']?.id);
|
||||||
|
|
||||||
|
// 判断是否有写权限:是创建者 或 有canWrite权限
|
||||||
|
const hasWritePermission = computed(() => {
|
||||||
|
return props.knowledge.creator === currentUserId.value || props.knowledge.canWrite;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 判断是否有删除权限:是创建者 或 有canDelete权限
|
||||||
|
const hasDeletePermission = computed(() => {
|
||||||
|
return props.knowledge.creator === currentUserId.value || props.knowledge.canDelete;
|
||||||
|
});
|
||||||
|
|
||||||
function handleClick() {
|
function handleClick() {
|
||||||
emit('click', props.knowledge);
|
emit('click', props.knowledge);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,20 @@
|
|||||||
返回列表
|
返回列表
|
||||||
</el-button>
|
</el-button>
|
||||||
<div class="header-actions">
|
<div class="header-actions">
|
||||||
<el-button @click="handleEdit">
|
<el-button
|
||||||
|
@click="handleEdit"
|
||||||
|
:disabled="!hasWritePermission"
|
||||||
|
:title="hasWritePermission ? '编辑知识库' : '无编辑权限'"
|
||||||
|
>
|
||||||
<el-icon><Edit /></el-icon>
|
<el-icon><Edit /></el-icon>
|
||||||
编辑
|
编辑
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" @click="handleUpload">
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="handleUpload"
|
||||||
|
:disabled="!hasWritePermission"
|
||||||
|
:title="hasWritePermission ? '上传文档' : '无上传权限'"
|
||||||
|
>
|
||||||
<el-icon><Upload /></el-icon>
|
<el-icon><Upload /></el-icon>
|
||||||
上传文档
|
上传文档
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -35,6 +44,15 @@
|
|||||||
<el-tag :type="knowledge.status === 1 ? 'success' : 'info'" size="small">
|
<el-tag :type="knowledge.status === 1 ? 'success' : 'info'" size="small">
|
||||||
{{ knowledge.status === 1 ? '已启用' : '已禁用' }}
|
{{ knowledge.status === 1 ? '已启用' : '已禁用' }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
|
<el-tag v-if="knowledge.canRead" type="success" size="small" effect="plain">
|
||||||
|
可读
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-if="knowledge.canWrite" type="warning" size="small" effect="plain">
|
||||||
|
可写
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-if="knowledge.canDelete" type="danger" size="small" effect="plain">
|
||||||
|
可删除
|
||||||
|
</el-tag>
|
||||||
<span class="meta-item">
|
<span class="meta-item">
|
||||||
<el-icon><Clock /></el-icon>
|
<el-icon><Clock /></el-icon>
|
||||||
{{ formatDate(knowledge.createTime) }}
|
{{ formatDate(knowledge.createTime) }}
|
||||||
@@ -149,8 +167,10 @@
|
|||||||
:model-value="row.enabled"
|
:model-value="row.enabled"
|
||||||
:active-text="row.enabled ? '已启用' : '已禁用'"
|
:active-text="row.enabled ? '已启用' : '已禁用'"
|
||||||
:loading="row._switching"
|
:loading="row._switching"
|
||||||
|
:disabled="!hasWritePermission"
|
||||||
@change="handleToggleEnabled(row, $event)"
|
@change="handleToggleEnabled(row, $event)"
|
||||||
style="--el-switch-on-color: #67C23A; margin-right: 12px;"
|
style="--el-switch-on-color: #67C23A; margin-right: 12px;"
|
||||||
|
:title="hasWritePermission ? '' : '无修改权限'"
|
||||||
/>
|
/>
|
||||||
<el-button link type="primary" @click="handleViewSegments(row)">
|
<el-button link type="primary" @click="handleViewSegments(row)">
|
||||||
查看分段
|
查看分段
|
||||||
@@ -158,7 +178,13 @@
|
|||||||
<el-button link type="primary" @click="handleDownload(row)">
|
<el-button link type="primary" @click="handleDownload(row)">
|
||||||
下载
|
下载
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button link type="danger" @click="handleDeleteDocument(row)">
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDeleteDocument(row)"
|
||||||
|
:disabled="!hasDeletePermission"
|
||||||
|
:title="hasDeletePermission ? '删除文档' : '无删除权限'"
|
||||||
|
>
|
||||||
删除
|
删除
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
@@ -207,6 +233,8 @@
|
|||||||
:model-value="segmentDialogVisible"
|
:model-value="segmentDialogVisible"
|
||||||
:dataset-id="props.knowledge.difyDatasetId"
|
:dataset-id="props.knowledge.difyDatasetId"
|
||||||
:document-id="selectedDocument.difyDocumentId"
|
:document-id="selectedDocument.difyDocumentId"
|
||||||
|
:can-write="hasWritePermission"
|
||||||
|
:can-delete="hasDeletePermission"
|
||||||
@update:model-value="segmentDialogVisible = $event"
|
@update:model-value="segmentDialogVisible = $event"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -214,6 +242,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch } from 'vue';
|
import { ref, computed, watch } from 'vue';
|
||||||
|
import { useStore } from 'vuex';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import type { UploadUserFile, UploadInstance } from 'element-plus';
|
import type { UploadUserFile, UploadInstance } from 'element-plus';
|
||||||
import {
|
import {
|
||||||
@@ -247,6 +276,19 @@ const emit = defineEmits<{
|
|||||||
edit: [knowledge: AiKnowledge];
|
edit: [knowledge: AiKnowledge];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
const currentUserId = computed(() => store.getters['auth/user']?.id);
|
||||||
|
|
||||||
|
// 判断是否有写权限:是创建者 或 有canWrite权限
|
||||||
|
const hasWritePermission = computed(() => {
|
||||||
|
return props.knowledge?.creator === currentUserId.value || props.knowledge?.canWrite;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 判断是否有删除权限:是创建者 或 有canDelete权限
|
||||||
|
const hasDeletePermission = computed(() => {
|
||||||
|
return props.knowledge?.creator === currentUserId.value || props.knowledge?.canDelete;
|
||||||
|
});
|
||||||
|
|
||||||
// 数据状态
|
// 数据状态
|
||||||
const documents = ref<AiUploadFile[]>([]);
|
const documents = ref<AiUploadFile[]>([]);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user