feat(knowledgebase): 添加知识库头像功能
- 在知识库详情中增加头像字段 - 实现知识库头像的上传和显示功能 - 优化知识库编辑界面,支持头像修改 - 调整后端 API 和数据库以支持头像存储
This commit is contained in:
parent
bee2ed1625
commit
86256b3399
|
@ -1,19 +1,22 @@
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from flask import request
|
from flask import request
|
||||||
from services.knowledgebases.service import KnowledgebaseService
|
from services.knowledgebases.service import KnowledgebaseService
|
||||||
from utils import success_response, error_response
|
from utils import error_response, success_response
|
||||||
|
|
||||||
from .. import knowledgebase_bp
|
from .. import knowledgebase_bp
|
||||||
|
|
||||||
@knowledgebase_bp.route('', methods=['GET'])
|
|
||||||
|
@knowledgebase_bp.route("", methods=["GET"])
|
||||||
def get_knowledgebase_list():
|
def get_knowledgebase_list():
|
||||||
"""获取知识库列表"""
|
"""获取知识库列表"""
|
||||||
try:
|
try:
|
||||||
params = {
|
params = {
|
||||||
'page': int(request.args.get('currentPage', 1)),
|
"page": int(request.args.get("currentPage", 1)),
|
||||||
'size': int(request.args.get('size', 10)),
|
"size": int(request.args.get("size", 10)),
|
||||||
'name': request.args.get('name', ''),
|
"name": request.args.get("name", ""),
|
||||||
'sort_by': request.args.get("sort_by", "create_time"),
|
"sort_by": request.args.get("sort_by", "create_time"),
|
||||||
'sort_order': request.args.get("sort_order", "desc")
|
"sort_order": request.args.get("sort_order", "desc"),
|
||||||
}
|
}
|
||||||
result = KnowledgebaseService.get_knowledgebase_list(**params)
|
result = KnowledgebaseService.get_knowledgebase_list(**params)
|
||||||
return success_response(result)
|
return success_response(result)
|
||||||
|
@ -22,87 +25,84 @@ def get_knowledgebase_list():
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return error_response(str(e))
|
return error_response(str(e))
|
||||||
|
|
||||||
@knowledgebase_bp.route('/<string:kb_id>', methods=['GET'])
|
|
||||||
|
@knowledgebase_bp.route("/<string:kb_id>", methods=["GET"])
|
||||||
def get_knowledgebase_detail(kb_id):
|
def get_knowledgebase_detail(kb_id):
|
||||||
"""获取知识库详情"""
|
"""获取知识库详情"""
|
||||||
try:
|
try:
|
||||||
knowledgebase = KnowledgebaseService.get_knowledgebase_detail(
|
knowledgebase = KnowledgebaseService.get_knowledgebase_detail(kb_id=kb_id)
|
||||||
kb_id=kb_id
|
|
||||||
)
|
|
||||||
if not knowledgebase:
|
if not knowledgebase:
|
||||||
return error_response('知识库不存在', code=404)
|
return error_response("知识库不存在", code=404)
|
||||||
return success_response(knowledgebase)
|
return success_response(knowledgebase)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return error_response(str(e))
|
return error_response(str(e))
|
||||||
|
|
||||||
@knowledgebase_bp.route('', methods=['POST'])
|
|
||||||
|
@knowledgebase_bp.route("", methods=["POST"])
|
||||||
def create_knowledgebase():
|
def create_knowledgebase():
|
||||||
"""创建知识库"""
|
"""创建知识库"""
|
||||||
try:
|
try:
|
||||||
data = request.json
|
data = request.json
|
||||||
if not data.get('name'):
|
if not data.get("name"):
|
||||||
return error_response('知识库名称不能为空', code=400)
|
return error_response("知识库名称不能为空", code=400)
|
||||||
|
|
||||||
# 移除 created_by 参数
|
# 移除 created_by 参数
|
||||||
kb = KnowledgebaseService.create_knowledgebase(**data)
|
kb = KnowledgebaseService.create_knowledgebase(**data)
|
||||||
return success_response(kb, "创建成功", code=0)
|
return success_response(kb, "创建成功", code=0)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return error_response(str(e))
|
return error_response(str(e))
|
||||||
|
|
||||||
@knowledgebase_bp.route('/<string:kb_id>', methods=['PUT'])
|
|
||||||
|
@knowledgebase_bp.route("/<string:kb_id>", methods=["PUT"])
|
||||||
def update_knowledgebase(kb_id):
|
def update_knowledgebase(kb_id):
|
||||||
"""更新知识库"""
|
"""更新知识库"""
|
||||||
try:
|
try:
|
||||||
data = request.json
|
data = request.json
|
||||||
kb = KnowledgebaseService.update_knowledgebase(
|
kb = KnowledgebaseService.update_knowledgebase(kb_id=kb_id, **data)
|
||||||
kb_id=kb_id,
|
|
||||||
**data
|
|
||||||
)
|
|
||||||
if not kb:
|
if not kb:
|
||||||
return error_response('知识库不存在', code=404)
|
return error_response("知识库不存在", code=404)
|
||||||
return success_response(kb)
|
return success_response(kb)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return error_response(str(e))
|
return error_response(str(e))
|
||||||
|
|
||||||
@knowledgebase_bp.route('/<string:kb_id>', methods=['DELETE'])
|
|
||||||
|
@knowledgebase_bp.route("/<string:kb_id>", methods=["DELETE"])
|
||||||
def delete_knowledgebase(kb_id):
|
def delete_knowledgebase(kb_id):
|
||||||
"""删除知识库"""
|
"""删除知识库"""
|
||||||
try:
|
try:
|
||||||
result = KnowledgebaseService.delete_knowledgebase(
|
result = KnowledgebaseService.delete_knowledgebase(kb_id=kb_id)
|
||||||
kb_id=kb_id
|
|
||||||
)
|
|
||||||
if not result:
|
if not result:
|
||||||
return error_response('知识库不存在', code=404)
|
return error_response("知识库不存在", code=404)
|
||||||
return success_response(message='删除成功')
|
return success_response(message="删除成功")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return error_response(str(e))
|
return error_response(str(e))
|
||||||
|
|
||||||
@knowledgebase_bp.route('/batch', methods=['DELETE'])
|
|
||||||
|
@knowledgebase_bp.route("/batch", methods=["DELETE"])
|
||||||
def batch_delete_knowledgebase():
|
def batch_delete_knowledgebase():
|
||||||
"""批量删除知识库"""
|
"""批量删除知识库"""
|
||||||
try:
|
try:
|
||||||
data = request.json
|
data = request.json
|
||||||
if not data or not data.get('ids'):
|
if not data or not data.get("ids"):
|
||||||
return error_response('请选择要删除的知识库', code=400)
|
return error_response("请选择要删除的知识库", code=400)
|
||||||
|
|
||||||
result = KnowledgebaseService.batch_delete_knowledgebase(
|
result = KnowledgebaseService.batch_delete_knowledgebase(kb_ids=data["ids"])
|
||||||
kb_ids=data['ids']
|
return success_response(message=f"成功删除 {result} 个知识库")
|
||||||
)
|
|
||||||
return success_response(message=f'成功删除 {result} 个知识库')
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return error_response(str(e))
|
return error_response(str(e))
|
||||||
|
|
||||||
@knowledgebase_bp.route('/<string:kb_id>/documents', methods=['GET'])
|
|
||||||
|
@knowledgebase_bp.route("/<string:kb_id>/documents", methods=["GET"])
|
||||||
def get_knowledgebase_documents(kb_id):
|
def get_knowledgebase_documents(kb_id):
|
||||||
"""获取知识库下的文档列表"""
|
"""获取知识库下的文档列表"""
|
||||||
try:
|
try:
|
||||||
params = {
|
params = {
|
||||||
'kb_id': kb_id,
|
"kb_id": kb_id,
|
||||||
'page': int(request.args.get('currentPage', 1)),
|
"page": int(request.args.get("currentPage", 1)),
|
||||||
'size': int(request.args.get('size', 10)),
|
"size": int(request.args.get("size", 10)),
|
||||||
'name': request.args.get('name', ''),
|
"name": request.args.get("name", ""),
|
||||||
'sort_by': request.args.get("sort_by", "create_time"),
|
"sort_by": request.args.get("sort_by", "create_time"),
|
||||||
'sort_order': request.args.get("sort_order", "desc")
|
"sort_order": request.args.get("sort_order", "desc"),
|
||||||
}
|
}
|
||||||
result = KnowledgebaseService.get_knowledgebase_documents(**params)
|
result = KnowledgebaseService.get_knowledgebase_documents(**params)
|
||||||
return success_response(result)
|
return success_response(result)
|
||||||
|
@ -111,7 +111,8 @@ def get_knowledgebase_documents(kb_id):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return error_response(str(e))
|
return error_response(str(e))
|
||||||
|
|
||||||
@knowledgebase_bp.route('/<string:kb_id>/documents', methods=['POST'])
|
|
||||||
|
@knowledgebase_bp.route("/<string:kb_id>/documents", methods=["POST"])
|
||||||
def add_documents_to_knowledgebase(kb_id):
|
def add_documents_to_knowledgebase(kb_id):
|
||||||
"""添加文档到知识库"""
|
"""添加文档到知识库"""
|
||||||
try:
|
try:
|
||||||
|
@ -119,89 +120,86 @@ def add_documents_to_knowledgebase(kb_id):
|
||||||
data = request.json
|
data = request.json
|
||||||
if not data:
|
if not data:
|
||||||
print("[ERROR] 请求数据为空")
|
print("[ERROR] 请求数据为空")
|
||||||
return error_response('请求数据不能为空', code=400)
|
return error_response("请求数据不能为空", code=400)
|
||||||
|
|
||||||
file_ids = data.get('file_ids', [])
|
file_ids = data.get("file_ids", [])
|
||||||
print(f"[DEBUG] 接收到的file_ids: {file_ids}, 类型: {type(file_ids)}")
|
print(f"[DEBUG] 接收到的file_ids: {file_ids}, 类型: {type(file_ids)}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = KnowledgebaseService.add_documents_to_knowledgebase(
|
result = KnowledgebaseService.add_documents_to_knowledgebase(kb_id=kb_id, file_ids=file_ids)
|
||||||
kb_id=kb_id,
|
|
||||||
file_ids=file_ids
|
|
||||||
)
|
|
||||||
print(f"[DEBUG] 服务层处理成功,结果: {result}")
|
print(f"[DEBUG] 服务层处理成功,结果: {result}")
|
||||||
return success_response(
|
return success_response(data=result, message="添加成功", code=201)
|
||||||
data=result,
|
|
||||||
message="添加成功",
|
|
||||||
code=201
|
|
||||||
)
|
|
||||||
except Exception as service_error:
|
except Exception as service_error:
|
||||||
print(f"[ERROR] 服务层错误详情: {str(service_error)}")
|
print(f"[ERROR] 服务层错误详情: {str(service_error)}")
|
||||||
|
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return error_response(str(service_error), code=500)
|
return error_response(str(service_error), code=500)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[ERROR] 路由层错误详情: {str(e)}")
|
print(f"[ERROR] 路由层错误详情: {str(e)}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return error_response(str(e), code=500)
|
return error_response(str(e), code=500)
|
||||||
|
|
||||||
@knowledgebase_bp.route('/documents/<string:doc_id>', methods=['DELETE', 'OPTIONS'])
|
|
||||||
|
@knowledgebase_bp.route("/documents/<string:doc_id>", methods=["DELETE", "OPTIONS"])
|
||||||
def delete_document(doc_id):
|
def delete_document(doc_id):
|
||||||
"""删除文档"""
|
"""删除文档"""
|
||||||
# 处理 OPTIONS 预检请求
|
# 处理 OPTIONS 预检请求
|
||||||
if request.method == 'OPTIONS':
|
if request.method == "OPTIONS":
|
||||||
response = success_response({})
|
response = success_response({})
|
||||||
# 添加 CORS 相关头
|
# 添加 CORS 相关头
|
||||||
response.headers.add('Access-Control-Allow-Methods', 'DELETE')
|
response.headers.add("Access-Control-Allow-Methods", "DELETE")
|
||||||
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
|
response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization")
|
||||||
return response
|
return response
|
||||||
|
|
||||||
try:
|
try:
|
||||||
KnowledgebaseService.delete_document(doc_id)
|
KnowledgebaseService.delete_document(doc_id)
|
||||||
return success_response(message="删除成功")
|
return success_response(message="删除成功")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return error_response(str(e))
|
return error_response(str(e))
|
||||||
|
|
||||||
@knowledgebase_bp.route('/documents/<doc_id>/parse', methods=['POST'])
|
|
||||||
|
@knowledgebase_bp.route("/documents/<doc_id>/parse", methods=["POST"])
|
||||||
def parse_document(doc_id):
|
def parse_document(doc_id):
|
||||||
"""开始解析文档"""
|
"""开始解析文档"""
|
||||||
# 处理 OPTIONS 预检请求
|
# 处理 OPTIONS 预检请求
|
||||||
if request.method == 'OPTIONS':
|
if request.method == "OPTIONS":
|
||||||
response = success_response({})
|
response = success_response({})
|
||||||
# 添加 CORS 相关头
|
# 添加 CORS 相关头
|
||||||
response.headers.add('Access-Control-Allow-Methods', 'POST')
|
response.headers.add("Access-Control-Allow-Methods", "POST")
|
||||||
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
|
response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization")
|
||||||
return response
|
return response
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = KnowledgebaseService.async_parse_document(doc_id)
|
result = KnowledgebaseService.async_parse_document(doc_id)
|
||||||
return success_response(data=result)
|
return success_response(data=result)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return error_response(str(e), code=500)
|
return error_response(str(e), code=500)
|
||||||
|
|
||||||
@knowledgebase_bp.route('/documents/<doc_id>/parse/progress', methods=['GET'])
|
|
||||||
|
@knowledgebase_bp.route("/documents/<doc_id>/parse/progress", methods=["GET"])
|
||||||
def get_parse_progress(doc_id):
|
def get_parse_progress(doc_id):
|
||||||
"""获取文档解析进度"""
|
"""获取文档解析进度"""
|
||||||
# 处理 OPTIONS 预检请求
|
# 处理 OPTIONS 预检请求
|
||||||
if request.method == 'OPTIONS':
|
if request.method == "OPTIONS":
|
||||||
response = success_response({})
|
response = success_response({})
|
||||||
# 添加 CORS 相关头
|
# 添加 CORS 相关头
|
||||||
response.headers.add('Access-Control-Allow-Methods', 'GET')
|
response.headers.add("Access-Control-Allow-Methods", "GET")
|
||||||
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
|
response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization")
|
||||||
return response
|
return response
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = KnowledgebaseService.get_document_parse_progress(doc_id)
|
result = KnowledgebaseService.get_document_parse_progress(doc_id)
|
||||||
if isinstance(result, dict) and 'error' in result:
|
if isinstance(result, dict) and "error" in result:
|
||||||
return error_response(result['error'], code=404)
|
return error_response(result["error"], code=404)
|
||||||
return success_response(data=result)
|
return success_response(data=result)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"获取解析进度失败: {str(e)}")
|
print(f"获取解析进度失败: {str(e)}")
|
||||||
return error_response("解析进行中,请稍后重试", code=202)
|
return error_response("解析进行中,请稍后重试", code=202)
|
||||||
|
|
||||||
|
|
||||||
# 获取系统 Embedding 配置路由
|
# 获取系统 Embedding 配置路由
|
||||||
@knowledgebase_bp.route('/system_embedding_config', methods=['GET'])
|
@knowledgebase_bp.route("/system_embedding_config", methods=["GET"])
|
||||||
def get_system_embedding_config_route():
|
def get_system_embedding_config_route():
|
||||||
"""获取系统级 Embedding 配置的API端点"""
|
"""获取系统级 Embedding 配置的API端点"""
|
||||||
try:
|
try:
|
||||||
|
@ -209,69 +207,68 @@ def get_system_embedding_config_route():
|
||||||
return success_response(data=config_data)
|
return success_response(data=config_data)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"获取系统 Embedding 配置失败: {str(e)}")
|
print(f"获取系统 Embedding 配置失败: {str(e)}")
|
||||||
return error_response(message=f"获取配置失败: {str(e)}", code=500) # 返回通用错误信息
|
return error_response(message=f"获取配置失败: {str(e)}", code=500) # 返回通用错误信息
|
||||||
|
|
||||||
|
|
||||||
# 设置系统 Embedding 配置路由
|
# 设置系统 Embedding 配置路由
|
||||||
@knowledgebase_bp.route('/system_embedding_config', methods=['POST'])
|
@knowledgebase_bp.route("/system_embedding_config", methods=["POST"])
|
||||||
def set_system_embedding_config_route():
|
def set_system_embedding_config_route():
|
||||||
"""设置系统级 Embedding 配置的API端点"""
|
"""设置系统级 Embedding 配置的API端点"""
|
||||||
try:
|
try:
|
||||||
data = request.json
|
data = request.json
|
||||||
if not data:
|
if not data:
|
||||||
return error_response('请求数据不能为空', code=400)
|
return error_response("请求数据不能为空", code=400)
|
||||||
|
|
||||||
llm_name = data.get('llm_name', '').strip()
|
llm_name = data.get("llm_name", "").strip()
|
||||||
api_base = data.get('api_base', '').strip()
|
api_base = data.get("api_base", "").strip()
|
||||||
api_key = data.get('api_key', '').strip() # 允许空
|
api_key = data.get("api_key", "").strip() # 允许空
|
||||||
|
|
||||||
if not llm_name or not api_base:
|
if not llm_name or not api_base:
|
||||||
return error_response('模型名称和 API 地址不能为空', code=400)
|
return error_response("模型名称和 API 地址不能为空", code=400)
|
||||||
|
|
||||||
# 调用服务层进行处理(包括连接测试和数据库操作)
|
# 调用服务层进行处理(包括连接测试和数据库操作)
|
||||||
success, message = KnowledgebaseService.set_system_embedding_config(
|
success, message = KnowledgebaseService.set_system_embedding_config(llm_name=llm_name, api_base=api_base, api_key=api_key)
|
||||||
llm_name=llm_name,
|
|
||||||
api_base=api_base,
|
|
||||||
api_key=api_key
|
|
||||||
)
|
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
return success_response(message=message)
|
return success_response(message=message)
|
||||||
else:
|
else:
|
||||||
# 如果服务层返回失败(例如连接测试失败或数据库错误),将消息返回给前端
|
# 如果服务层返回失败(例如连接测试失败或数据库错误),将消息返回给前端
|
||||||
return error_response(message=message, code=400) # 使用 400 表示操作失败
|
return error_response(message=message, code=400) # 使用 400 表示操作失败
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# 捕获路由层或未预料的服务层异常
|
# 捕获路由层或未预料的服务层异常
|
||||||
print(f"设置系统 Embedding 配置失败: {str(e)}")
|
print(f"设置系统 Embedding 配置失败: {str(e)}")
|
||||||
return error_response(message=f"设置配置时发生内部错误: {str(e)}", code=500)
|
return error_response(message=f"设置配置时发生内部错误: {str(e)}", code=500)
|
||||||
|
|
||||||
@knowledgebase_bp.route('/documents/<doc_id>/parse', methods=['POST'])
|
|
||||||
def parse_document_async(doc_id): # 函数名改为 async 以区分
|
@knowledgebase_bp.route("/documents/<doc_id>/parse", methods=["POST"])
|
||||||
|
def parse_document_async(doc_id): # 函数名改为 async 以区分
|
||||||
"""开始异步解析单个文档"""
|
"""开始异步解析单个文档"""
|
||||||
if request.method == 'OPTIONS':
|
if request.method == "OPTIONS":
|
||||||
response = success_response({})
|
response = success_response({})
|
||||||
response.headers.add('Access-Control-Allow-Methods', 'POST')
|
response.headers.add("Access-Control-Allow-Methods", "POST")
|
||||||
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
|
response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization")
|
||||||
return response
|
return response
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = KnowledgebaseService.parse_document(doc_id) # 调用同步版本
|
result = KnowledgebaseService.parse_document(doc_id) # 调用同步版本
|
||||||
if result.get("success"):
|
if result.get("success"):
|
||||||
return success_response(data={"message": f"文档 {doc_id} 同步解析完成。", "details": result})
|
return success_response(data={"message": f"文档 {doc_id} 同步解析完成。", "details": result})
|
||||||
else:
|
else:
|
||||||
return error_response(result.get("message", "解析失败"), code=500)
|
return error_response(result.get("message", "解析失败"), code=500)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return error_response(str(e), code=500)
|
return error_response(str(e), code=500)
|
||||||
|
|
||||||
|
|
||||||
# 启动顺序批量解析路由
|
# 启动顺序批量解析路由
|
||||||
@knowledgebase_bp.route('/<string:kb_id>/batch_parse_sequential/start', methods=['POST'])
|
@knowledgebase_bp.route("/<string:kb_id>/batch_parse_sequential/start", methods=["POST"])
|
||||||
def start_sequential_batch_parse_route(kb_id):
|
def start_sequential_batch_parse_route(kb_id):
|
||||||
"""异步启动知识库的顺序批量解析任务"""
|
"""异步启动知识库的顺序批量解析任务"""
|
||||||
if request.method == 'OPTIONS':
|
if request.method == "OPTIONS":
|
||||||
response = success_response({})
|
response = success_response({})
|
||||||
response.headers.add('Access-Control-Allow-Methods', 'POST')
|
response.headers.add("Access-Control-Allow-Methods", "POST")
|
||||||
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
|
response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization")
|
||||||
return response
|
return response
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -286,14 +283,15 @@ def start_sequential_batch_parse_route(kb_id):
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return error_response(f"启动顺序批量解析失败: {str(e)}", code=500)
|
return error_response(f"启动顺序批量解析失败: {str(e)}", code=500)
|
||||||
|
|
||||||
|
|
||||||
# 获取顺序批量解析进度路由
|
# 获取顺序批量解析进度路由
|
||||||
@knowledgebase_bp.route('/<string:kb_id>/batch_parse_sequential/progress', methods=['GET'])
|
@knowledgebase_bp.route("/<string:kb_id>/batch_parse_sequential/progress", methods=["GET"])
|
||||||
def get_sequential_batch_parse_progress_route(kb_id):
|
def get_sequential_batch_parse_progress_route(kb_id):
|
||||||
"""获取知识库的顺序批量解析任务进度"""
|
"""获取知识库的顺序批量解析任务进度"""
|
||||||
if request.method == 'OPTIONS':
|
if request.method == "OPTIONS":
|
||||||
response = success_response({})
|
response = success_response({})
|
||||||
response.headers.add('Access-Control-Allow-Methods', 'GET')
|
response.headers.add("Access-Control-Allow-Methods", "GET")
|
||||||
response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization')
|
response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization")
|
||||||
return response
|
return response
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -303,4 +301,4 @@ def get_sequential_batch_parse_progress_route(kb_id):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"获取顺序批量解析进度路由处理失败 (KB ID: {kb_id}): {str(e)}")
|
print(f"获取顺序批量解析进度路由处理失败 (KB ID: {kb_id}): {str(e)}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return error_response(f"获取进度失败: {str(e)}", code=500)
|
return error_response(f"获取进度失败: {str(e)}", code=500)
|
||||||
|
|
|
@ -8,7 +8,6 @@ import shutil
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from io import BytesIO
|
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
@ -61,7 +60,7 @@ def perform_parse(doc_id, doc_info, file_info, embedding_config, kb_info):
|
||||||
if embedding_model_name == "netease-youdao/bce-embedding-base_v1":
|
if embedding_model_name == "netease-youdao/bce-embedding-base_v1":
|
||||||
embedding_model_name = "BAAI/bge-m3"
|
embedding_model_name = "BAAI/bge-m3"
|
||||||
|
|
||||||
embedding_api_base = embedding_config.get("api_base") if embedding_config and embedding_config.get("api_base") else "http://localhost:8000" # 默认基础 URL
|
embedding_api_base = embedding_config.get("api_base") if embedding_config and embedding_config.get("api_base") else "http://localhost:11434" # 默认基础 URL
|
||||||
|
|
||||||
# 如果 API 基础地址为空字符串,设置为硅基流动的 API 地址
|
# 如果 API 基础地址为空字符串,设置为硅基流动的 API 地址
|
||||||
if embedding_api_base == "":
|
if embedding_api_base == "":
|
||||||
|
|
|
@ -105,7 +105,8 @@ class KnowledgebaseService:
|
||||||
k.description,
|
k.description,
|
||||||
k.create_date,
|
k.create_date,
|
||||||
k.update_date,
|
k.update_date,
|
||||||
k.doc_num
|
k.doc_num,
|
||||||
|
k.avatar
|
||||||
FROM knowledgebase k
|
FROM knowledgebase k
|
||||||
WHERE k.id = %s
|
WHERE k.id = %s
|
||||||
"""
|
"""
|
||||||
|
@ -334,6 +335,13 @@ class KnowledgebaseService:
|
||||||
update_fields.append("permission = %s")
|
update_fields.append("permission = %s")
|
||||||
params.append(data["permission"])
|
params.append(data["permission"])
|
||||||
|
|
||||||
|
if "avatar" in data and data["avatar"]:
|
||||||
|
avatar_base64 = data["avatar"]
|
||||||
|
# 拼接上前缀
|
||||||
|
full_avatar_url = f"data:image/png;base64,{avatar_base64}"
|
||||||
|
update_fields.append("avatar = %s")
|
||||||
|
params.append(full_avatar_url)
|
||||||
|
|
||||||
# 更新时间
|
# 更新时间
|
||||||
current_time = datetime.now()
|
current_time = datetime.now()
|
||||||
update_date = current_time.strftime("%Y-%m-%d %H:%M:%S")
|
update_date = current_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
|
|
@ -1,5 +1,19 @@
|
||||||
import { request } from "@/http/axios"
|
import { request } from "@/http/axios"
|
||||||
|
|
||||||
|
// 定义修改数据库界面中显示的信息
|
||||||
|
interface KbDetailData {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
permission: string
|
||||||
|
avatar?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiDetailResponse {
|
||||||
|
data: KbDetailData
|
||||||
|
code: number
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
// 获取知识库列表
|
// 获取知识库列表
|
||||||
export function getKnowledgeBaseListApi(params: {
|
export function getKnowledgeBaseListApi(params: {
|
||||||
currentPage: number
|
currentPage: number
|
||||||
|
@ -16,7 +30,7 @@ export function getKnowledgeBaseListApi(params: {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取知识库详情
|
// 获取知识库详情
|
||||||
export function getKnowledgeBaseDetailApi(id: string) {
|
export function getKbDetailApi(id: string): Promise<ApiDetailResponse> {
|
||||||
return request({
|
return request({
|
||||||
url: `/api/v1/knowledgebases/${id}`,
|
url: `/api/v1/knowledgebases/${id}`,
|
||||||
method: "get"
|
method: "get"
|
||||||
|
@ -44,6 +58,7 @@ export function updateKnowledgeBaseApi(id: string, data: {
|
||||||
description?: string
|
description?: string
|
||||||
language?: string
|
language?: string
|
||||||
permission?: string
|
permission?: string
|
||||||
|
avatar?: string
|
||||||
}) {
|
}) {
|
||||||
return request({
|
return request({
|
||||||
url: `/api/v1/knowledgebases/${id}`,
|
url: `/api/v1/knowledgebases/${id}`,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { SequentialBatchTaskProgress } from "@@/apis/kbs/document"
|
import type { SequentialBatchTaskProgress } from "@@/apis/kbs/document"
|
||||||
import type { FormInstance } from "element-plus"
|
import type { FormInstance, UploadFile, UploadProps } from "element-plus"
|
||||||
import DocumentParseProgress from "@/layouts/components/DocumentParseProgress/index.vue"
|
import DocumentParseProgress from "@/layouts/components/DocumentParseProgress/index.vue"
|
||||||
import {
|
import {
|
||||||
deleteDocumentApi,
|
deleteDocumentApi,
|
||||||
|
@ -14,9 +14,11 @@ import {
|
||||||
batchDeleteKnowledgeBaseApi,
|
batchDeleteKnowledgeBaseApi,
|
||||||
createKnowledgeBaseApi,
|
createKnowledgeBaseApi,
|
||||||
deleteKnowledgeBaseApi,
|
deleteKnowledgeBaseApi,
|
||||||
|
getKbDetailApi,
|
||||||
getKnowledgeBaseListApi,
|
getKnowledgeBaseListApi,
|
||||||
getSystemEmbeddingConfigApi,
|
getSystemEmbeddingConfigApi,
|
||||||
setSystemEmbeddingConfigApi
|
setSystemEmbeddingConfigApi,
|
||||||
|
updateKnowledgeBaseApi
|
||||||
} from "@@/apis/kbs/knowledgebase"
|
} from "@@/apis/kbs/knowledgebase"
|
||||||
import { getTableDataApi } from "@@/apis/tables"
|
import { getTableDataApi } from "@@/apis/tables"
|
||||||
import { usePagination } from "@@/composables/usePagination"
|
import { usePagination } from "@@/composables/usePagination"
|
||||||
|
@ -157,39 +159,91 @@ const editDialogVisible = ref(false)
|
||||||
const editForm = reactive({
|
const editForm = reactive({
|
||||||
id: "",
|
id: "",
|
||||||
name: "",
|
name: "",
|
||||||
permission: "me"
|
permission: "me",
|
||||||
|
avatar: ""
|
||||||
})
|
})
|
||||||
const editLoading = ref(false)
|
const editLoading = ref(false)
|
||||||
|
|
||||||
// 处理修改知识库
|
// // 处理修改知识库
|
||||||
function handleEdit(row: KnowledgeBaseData) {
|
// function handleEdit(row: KnowledgeBaseData) {
|
||||||
|
// editDialogVisible.value = true
|
||||||
|
// editForm.id = row.id
|
||||||
|
// editForm.name = row.name
|
||||||
|
// editForm.permission = row.permission
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 用于获取完整数据
|
||||||
|
async function handleEdit(row: KnowledgeBaseData) {
|
||||||
editDialogVisible.value = true
|
editDialogVisible.value = true
|
||||||
editForm.id = row.id
|
editLoading.value = true
|
||||||
editForm.name = row.name
|
try {
|
||||||
editForm.permission = row.permission
|
const { data } = await getKbDetailApi(row.id)
|
||||||
|
editForm.id = data.id
|
||||||
|
editForm.name = data.name
|
||||||
|
editForm.permission = row.permission
|
||||||
|
// 如果后端返回的base64不带前缀,需要手动加上
|
||||||
|
if (data.avatar && !data.avatar.startsWith("data:image")) {
|
||||||
|
editForm.avatar = `data:image/jpeg;base64,${data.avatar}`
|
||||||
|
} else {
|
||||||
|
editForm.avatar = data.avatar || ""
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
ElMessage.error(`获取知识库详情失败: ${error?.message || "未知错误"}`)
|
||||||
|
editDialogVisible.value = false // 如果失败,则关闭对话框
|
||||||
|
} finally {
|
||||||
|
editLoading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提交修改
|
// 提交修改
|
||||||
function submitEdit() {
|
function submitEdit() {
|
||||||
editLoading.value = true
|
editLoading.value = true
|
||||||
// 调用修改知识库API
|
|
||||||
axios.put(`/api/v1/knowledgebases/${editForm.id}`, {
|
// 准备要提交的数据
|
||||||
|
const payload: { permission: string, avatar?: string } = {
|
||||||
permission: editForm.permission
|
permission: editForm.permission
|
||||||
})
|
}
|
||||||
|
|
||||||
|
// 如果有新的头像数据,提取纯 Base64 部分
|
||||||
|
if (editForm.avatar && editForm.avatar.startsWith("data:image")) {
|
||||||
|
payload.avatar = editForm.avatar.split(",")[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用导入的 API 函数
|
||||||
|
updateKnowledgeBaseApi(editForm.id, payload)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
ElMessage.success("知识库权限修改成功")
|
ElMessage.success("知识库信息修改成功")
|
||||||
editDialogVisible.value = false
|
editDialogVisible.value = false
|
||||||
// 刷新知识库列表
|
getTableData() // 刷新知识库列表
|
||||||
getTableData()
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error: any) => {
|
||||||
ElMessage.error(`修改知识库权限失败: ${error?.message || "未知错误"}`)
|
ElMessage.error(`修改知识库失败: ${error?.message || "未知错误"}`)
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
editLoading.value = false
|
editLoading.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleAvatarChange: UploadProps["onChange"] = (uploadFile: UploadFile) => {
|
||||||
|
if (!uploadFile.raw) return
|
||||||
|
if (!uploadFile.raw.type.includes("image")) {
|
||||||
|
ElMessage.error("请上传图片格式文件!")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// 上传文件大小限制
|
||||||
|
const isLt2M = uploadFile.raw.size / 1024 / 1024 < 2
|
||||||
|
if (!isLt2M) {
|
||||||
|
ElMessage.error("上传头像图片大小不能超过 2MB!")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.readAsDataURL(uploadFile.raw)
|
||||||
|
reader.onload = () => {
|
||||||
|
editForm.avatar = reader.result as string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 存储多选的表格数据
|
// 存储多选的表格数据
|
||||||
const multipleSelection = ref<KnowledgeBaseData[]>([])
|
const multipleSelection = ref<KnowledgeBaseData[]>([])
|
||||||
|
|
||||||
|
@ -1404,6 +1458,21 @@ const userLoading = ref(false)
|
||||||
<el-form-item label="知识库名称">
|
<el-form-item label="知识库名称">
|
||||||
<span>{{ editForm.name }}</span>
|
<span>{{ editForm.name }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="知识库头像">
|
||||||
|
<el-upload
|
||||||
|
class="avatar-uploader"
|
||||||
|
action="#"
|
||||||
|
:show-file-list="false"
|
||||||
|
:on-change="handleAvatarChange"
|
||||||
|
:auto-upload="false"
|
||||||
|
accept="image/png, image/jpeg, image/gif, image/webp"
|
||||||
|
>
|
||||||
|
<img v-if="editForm.avatar" :src="editForm.avatar" class="avatar" alt="avatar">
|
||||||
|
<el-icon v-else class="avatar-uploader-icon">
|
||||||
|
<Plus />
|
||||||
|
</el-icon>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="权限设置">
|
<el-form-item label="权限设置">
|
||||||
<el-select v-model="editForm.permission" placeholder="请选择权限">
|
<el-select v-model="editForm.permission" placeholder="请选择权限">
|
||||||
<el-option label="个人" value="me" />
|
<el-option label="个人" value="me" />
|
||||||
|
@ -1697,4 +1766,33 @@ const userLoading = ref(false)
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
animation: rotating 2s linear infinite;
|
animation: rotating 2s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.avatar-uploader .el-upload {
|
||||||
|
border: 1px dashed var(--el-border-color);
|
||||||
|
border-radius: 50%; /* 圆形 */
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: var(--el-transition-duration-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-uploader .el-upload:hover {
|
||||||
|
border-color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-uploader-icon {
|
||||||
|
font-size: 28px;
|
||||||
|
color: #8c939d;
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 120px; /* 垂直居中图标 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 120px;
|
||||||
|
height: 120px;
|
||||||
|
display: block;
|
||||||
|
object-fit: cover; /* 保证图片不变形 */
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue