提交qiming-mcp-proxy
This commit is contained in:
235
qiming-mcp-proxy/document-parser/src/handlers/toc_handler.rs
Normal file
235
qiming-mcp-proxy/document-parser/src/handlers/toc_handler.rs
Normal file
@@ -0,0 +1,235 @@
|
||||
use crate::app_state::AppState;
|
||||
use crate::models::{HttpResult, StructuredSection};
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Path, State},
|
||||
};
|
||||
use serde::Serialize;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
/// 目录响应结构
|
||||
///
|
||||
/// 表示文档目录处理完成后的结果,包含任务ID、目录结构和统计信息。
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub struct TocResponse {
|
||||
/// 文档处理任务的唯一标识符
|
||||
/// 用于关联请求和响应,支持异步处理和状态查询
|
||||
pub task_id: String,
|
||||
|
||||
/// 文档的目录结构
|
||||
/// 包含所有章节和子章节的层级结构,支持无限嵌套
|
||||
pub toc: Vec<StructuredSection>,
|
||||
|
||||
/// 目录中章节的总数量
|
||||
/// 用于统计分析和分页显示
|
||||
pub total_sections: usize,
|
||||
}
|
||||
|
||||
/// 章节响应结构
|
||||
///
|
||||
/// 表示单个章节的详细信息,用于章节内容的展示和编辑。
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub struct SectionResponse {
|
||||
/// 章节的唯一标识符
|
||||
/// 用于章节的定位、引用和更新操作
|
||||
pub section_id: String,
|
||||
|
||||
/// 章节的标题或名称
|
||||
/// 显示在目录和导航中的章节标题
|
||||
pub title: String,
|
||||
|
||||
/// 章节的正文内容
|
||||
/// 包含章节的完整文本内容,支持Markdown格式
|
||||
pub content: String,
|
||||
|
||||
/// 章节的层级深度
|
||||
/// 1表示顶级章节,2表示二级章节,以此类推
|
||||
pub level: u8,
|
||||
|
||||
/// 是否包含子章节
|
||||
/// 用于判断章节是否可以展开显示子章节
|
||||
pub has_children: bool,
|
||||
}
|
||||
|
||||
/// 章节列表响应结构
|
||||
///
|
||||
/// 表示文档所有章节的完整信息,包含文档元数据和章节结构。
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub struct SectionsResponse {
|
||||
/// 文档处理任务的唯一标识符
|
||||
/// 用于关联请求和响应,支持异步处理和状态查询
|
||||
pub task_id: String,
|
||||
|
||||
/// 文档的标题或名称
|
||||
/// 显示在界面中的文档标题
|
||||
pub document_title: String,
|
||||
|
||||
/// 文档的完整目录结构
|
||||
/// 包含所有章节和子章节的层级结构,支持无限嵌套
|
||||
pub toc: Vec<StructuredSection>,
|
||||
|
||||
/// 文档中章节的总数量
|
||||
/// 用于统计分析和分页显示
|
||||
pub total_sections: usize,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/tasks/{task_id}/toc",
|
||||
params(
|
||||
("task_id" = String, Path, description = "任务ID")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "获取文档目录成功", body = HttpResult<TocResponse>),
|
||||
(status = 404, description = "任务不存在或未生成结构化文档", body = HttpResult<TocResponse>),
|
||||
(status = 500, description = "服务器内部错误", body = HttpResult<TocResponse>)
|
||||
),
|
||||
tag = "toc"
|
||||
)]
|
||||
pub async fn get_document_toc(
|
||||
State(state): State<AppState>,
|
||||
Path(task_id): Path<String>,
|
||||
) -> Json<HttpResult<TocResponse>> {
|
||||
match state.task_service.get_task(&task_id).await {
|
||||
Ok(Some(task)) => {
|
||||
if let Some(doc) = task.structured_document {
|
||||
let resp = TocResponse {
|
||||
task_id: task_id.clone(),
|
||||
toc: doc.toc.clone(),
|
||||
total_sections: doc.total_sections,
|
||||
};
|
||||
Json(HttpResult::success(resp))
|
||||
} else {
|
||||
Json(HttpResult::<TocResponse>::error(
|
||||
"T009".to_string(),
|
||||
"任务尚未生成结构化文档".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
Ok(None) => Json(HttpResult::<TocResponse>::error(
|
||||
"T002".to_string(),
|
||||
format!("任务不存在: {task_id}"),
|
||||
)),
|
||||
Err(e) => Json(HttpResult::<TocResponse>::error(
|
||||
"T003".to_string(),
|
||||
format!("查询任务失败: {e}"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/tasks/{task_id}/sections/{section_id}",
|
||||
params(
|
||||
("task_id" = String, Path, description = "任务ID"),
|
||||
("section_id" = String, Path, description = "章节ID")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "获取章节内容成功", body = HttpResult<SectionResponse>),
|
||||
(status = 404, description = "任务或章节不存在", body = HttpResult<SectionResponse>),
|
||||
(status = 500, description = "服务器内部错误", body = HttpResult<SectionResponse>)
|
||||
),
|
||||
tag = "toc"
|
||||
)]
|
||||
pub async fn get_section_content(
|
||||
State(state): State<AppState>,
|
||||
Path((task_id, section_id)): Path<(String, String)>,
|
||||
) -> Json<HttpResult<SectionResponse>> {
|
||||
match state.task_service.get_task(&task_id).await {
|
||||
Ok(Some(task)) => {
|
||||
if let Some(doc) = task.structured_document {
|
||||
// 遍历 toc 查找 section 元信息
|
||||
fn find<'a>(
|
||||
items: &'a [StructuredSection],
|
||||
id: &str,
|
||||
) -> Option<&'a StructuredSection> {
|
||||
for it in items {
|
||||
if it.id == id {
|
||||
return Some(it);
|
||||
}
|
||||
for child in &it.children {
|
||||
if let Some(found) = find(std::slice::from_ref(child.as_ref()), id) {
|
||||
return Some(found);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
if let Some(toc_item) = find(&doc.toc, §ion_id) {
|
||||
let content = toc_item.content.clone();
|
||||
let resp = SectionResponse {
|
||||
section_id: section_id.clone(),
|
||||
title: toc_item.title.clone(),
|
||||
content,
|
||||
level: toc_item.level,
|
||||
has_children: !toc_item.children.is_empty(),
|
||||
};
|
||||
Json(HttpResult::success(resp))
|
||||
} else {
|
||||
Json(HttpResult::<SectionResponse>::error(
|
||||
"T010".to_string(),
|
||||
format!("章节不存在: {section_id}"),
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Json(HttpResult::<SectionResponse>::error(
|
||||
"T009".to_string(),
|
||||
"任务尚未生成结构化文档".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
Ok(None) => Json(HttpResult::<SectionResponse>::error(
|
||||
"T002".to_string(),
|
||||
format!("任务不存在: {task_id}"),
|
||||
)),
|
||||
Err(e) => Json(HttpResult::<SectionResponse>::error(
|
||||
"T003".to_string(),
|
||||
format!("查询任务失败: {e}"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/api/v1/tasks/{task_id}/sections",
|
||||
params(
|
||||
("task_id" = String, Path, description = "任务ID")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "获取所有章节成功", body = HttpResult<SectionsResponse>),
|
||||
(status = 404, description = "任务不存在或未生成结构化文档", body = HttpResult<SectionsResponse>),
|
||||
(status = 500, description = "服务器内部错误", body = HttpResult<SectionsResponse>)
|
||||
),
|
||||
tag = "toc"
|
||||
)]
|
||||
pub async fn get_all_sections(
|
||||
State(state): State<AppState>,
|
||||
Path(task_id): Path<String>,
|
||||
) -> Json<HttpResult<SectionsResponse>> {
|
||||
match state.task_service.get_task(&task_id).await {
|
||||
Ok(Some(task)) => {
|
||||
if let Some(doc) = task.structured_document {
|
||||
let resp = SectionsResponse {
|
||||
task_id: task_id.clone(),
|
||||
document_title: doc.document_title.clone(),
|
||||
toc: doc.toc.clone(),
|
||||
total_sections: doc.total_sections,
|
||||
};
|
||||
Json(HttpResult::success(resp))
|
||||
} else {
|
||||
Json(HttpResult::<SectionsResponse>::error(
|
||||
"T009".to_string(),
|
||||
"任务尚未生成结构化文档".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
Ok(None) => Json(HttpResult::<SectionsResponse>::error(
|
||||
"T002".to_string(),
|
||||
format!("任务不存在: {task_id}"),
|
||||
)),
|
||||
Err(e) => Json(HttpResult::<SectionsResponse>::error(
|
||||
"T003".to_string(),
|
||||
format!("查询任务失败: {e}"),
|
||||
)),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user