chore: 收口 review 执行清单与 runtime 验证
- 补齐 design/10-review 执行清单、验收标准与相关设计治理记录 - 迁移已完成的 tree、mindmap、runtime fallback、AI kernel 等设计和缺陷条目 - 推进 Rust Web runtime、tree/sidebar、page aggregate、mindmap 与 OnlyOffice 路由侧验证支撑 - 增加 task177-task180 smoke/audit 脚本及前端相关测试覆盖
This commit is contained in:
@@ -5443,6 +5443,14 @@ fn build_page_aggregate_projection_result(
|
||||
})
|
||||
}
|
||||
|
||||
fn page_aggregate_source_for_data(data: &Value) -> PageAggregateSource {
|
||||
if data.get("meta").is_some() && data.get("content").is_some() {
|
||||
PageAggregateSource::CompatMetaContentJoin
|
||||
} else {
|
||||
PageAggregateSource::KernelProjection
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_mindmap_from_value(data: &Value) -> Result<MindmapTreeNode, BridgeError> {
|
||||
if data.is_null() {
|
||||
return Ok(default_mindmap_tree());
|
||||
@@ -8322,11 +8330,12 @@ fn execute_query_result(
|
||||
}
|
||||
"page.aggregate.get" => {
|
||||
let payload: PageAggregateQueryPayload = parse_payload(query_wire.payload)?;
|
||||
let source = page_aggregate_source_for_data(&data);
|
||||
let result = build_page_aggregate_projection_result(
|
||||
&data,
|
||||
&payload.document_id,
|
||||
payload.workspace_id.as_deref(),
|
||||
PageAggregateSource::KernelProjection,
|
||||
source,
|
||||
)?;
|
||||
serde_json::to_value(result).map_err(|error| {
|
||||
BridgeError::transport(format!("page.aggregate.get result 序列化失败: {error}"))
|
||||
@@ -9032,6 +9041,22 @@ fn execute_command(
|
||||
args_json: json!({
|
||||
"id": payload.document_id,
|
||||
"options": Value::Object(options_json),
|
||||
"streamDeltaHint": tree_resync_required_hint(
|
||||
"page.layout.updateOptions",
|
||||
json!({
|
||||
"documentId": payload.document_id,
|
||||
}),
|
||||
),
|
||||
"domainEventHint": tree_domain_event_hint("page.layout.options_updated"),
|
||||
"domainEventPlan": tree_domain_event_plan(
|
||||
"page.layout.options_updated",
|
||||
tree_resync_required_hint(
|
||||
"page.layout.updateOptions",
|
||||
json!({
|
||||
"documentId": payload.document_id,
|
||||
}),
|
||||
),
|
||||
),
|
||||
}),
|
||||
}))
|
||||
}
|
||||
@@ -9272,6 +9297,22 @@ fn execute_command(
|
||||
}
|
||||
"mindmaps.put" => {
|
||||
let payload: MindmapPutCommandPayload = parse_payload(command_wire.payload.clone())?;
|
||||
let create_only = payload.create_only.unwrap_or(false);
|
||||
let (event_type, stream_delta_hint) = if create_only {
|
||||
(
|
||||
"tree.resource.mindmap.put",
|
||||
tree_stream_delta_hint("resync_required", json!({
|
||||
"reason": "mindmap.put",
|
||||
"documentId": payload.document_id.clone(),
|
||||
"blockId": payload.mindmap_id.clone(),
|
||||
})),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
"mindmap.content.updated",
|
||||
tree_stream_delta_hint("noop", json!({})),
|
||||
)
|
||||
};
|
||||
let command = CommandEnvelope {
|
||||
name: "mindmaps.put".into(),
|
||||
command_id: command_wire.command_id.clone(),
|
||||
@@ -9285,7 +9326,7 @@ fn execute_command(
|
||||
data_json: serde_json::to_string(&payload.data).map_err(|error| {
|
||||
BridgeError::validation(format!("mindmaps.put data 序列化失败: {error}"))
|
||||
})?,
|
||||
create_only: payload.create_only.unwrap_or(false),
|
||||
create_only,
|
||||
},
|
||||
reason: command_wire.reason,
|
||||
refs: command_wire.refs,
|
||||
@@ -9308,20 +9349,12 @@ fn execute_command(
|
||||
"docId": payload.document_id.clone(),
|
||||
"mindmapId": payload.mindmap_id.clone(),
|
||||
"data": payload.data,
|
||||
"createOnly": payload.create_only.unwrap_or(false),
|
||||
"streamDeltaHint": tree_stream_delta_hint("resync_required", json!({
|
||||
"reason": "mindmap.put",
|
||||
"documentId": payload.document_id.clone(),
|
||||
"blockId": payload.mindmap_id.clone(),
|
||||
})),
|
||||
"domainEventHint": tree_domain_event_hint("tree.resource.mindmap.put"),
|
||||
"createOnly": create_only,
|
||||
"streamDeltaHint": stream_delta_hint.clone(),
|
||||
"domainEventHint": tree_domain_event_hint(event_type),
|
||||
"domainEventPlan": tree_domain_event_plan(
|
||||
"tree.resource.mindmap.put",
|
||||
tree_stream_delta_hint("resync_required", json!({
|
||||
"reason": "mindmap.put",
|
||||
"documentId": payload.document_id.clone(),
|
||||
"blockId": payload.mindmap_id.clone(),
|
||||
})),
|
||||
event_type,
|
||||
stream_delta_hint,
|
||||
),
|
||||
}),
|
||||
}))
|
||||
@@ -9364,19 +9397,11 @@ fn execute_command(
|
||||
"commands": payload.commands,
|
||||
"projectionRevision": payload.projection_revision,
|
||||
"canonicalCommand": "mindmap.command.apply",
|
||||
"streamDeltaHint": tree_stream_delta_hint("resync_required", json!({
|
||||
"reason": "mindmap.command.apply",
|
||||
"documentId": payload.document_id.clone(),
|
||||
"blockId": payload.mindmap_id.clone(),
|
||||
})),
|
||||
"domainEventHint": tree_domain_event_hint("tree.resource.mindmap.updated"),
|
||||
"streamDeltaHint": tree_stream_delta_hint("noop", json!({})),
|
||||
"domainEventHint": tree_domain_event_hint("mindmap.content.updated"),
|
||||
"domainEventPlan": tree_domain_event_plan(
|
||||
"tree.resource.mindmap.updated",
|
||||
tree_stream_delta_hint("resync_required", json!({
|
||||
"reason": "mindmap.command.apply",
|
||||
"documentId": payload.document_id.clone(),
|
||||
"blockId": payload.mindmap_id.clone(),
|
||||
})),
|
||||
"mindmap.content.updated",
|
||||
tree_stream_delta_hint("noop", json!({})),
|
||||
),
|
||||
}),
|
||||
}))
|
||||
@@ -11396,7 +11421,7 @@ mod tests {
|
||||
|
||||
assert_eq!(result["schema"], json!("mnote.page_aggregate.v1"));
|
||||
assert_eq!(result["projectionVersion"], json!(1));
|
||||
assert_eq!(result["source"], json!("KernelProjection"));
|
||||
assert_eq!(result["source"], json!("CompatMetaContentJoin"));
|
||||
assert_eq!(result["pageId"], json!("doc_1"));
|
||||
assert_eq!(result["identity"]["documentId"], json!("doc_1"));
|
||||
assert_eq!(result["body"]["revision"], json!(7));
|
||||
@@ -11748,11 +11773,11 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
plan.args_json["domainEventPlan"]["eventType"],
|
||||
json!("tree.resource.mindmap.updated")
|
||||
json!("mindmap.content.updated")
|
||||
);
|
||||
assert_eq!(
|
||||
plan.args_json["streamDeltaHint"]["kind"],
|
||||
json!("resync_required")
|
||||
json!("noop")
|
||||
);
|
||||
}
|
||||
RuntimeExecutionPlan::Query(_) | RuntimeExecutionPlan::Tool(_) => {
|
||||
@@ -12096,6 +12121,77 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mindmaps_put_existing_content_update_uses_object_event_without_tree_resync() {
|
||||
let plan = execute_runtime_input(RuntimeInput::Command {
|
||||
context: demo_context(),
|
||||
command: RuntimeCommandEnvelopeWire {
|
||||
name: "mindmaps.put".into(),
|
||||
command_id: "cmd_mindmap_put_update".into(),
|
||||
idempotency_key: None,
|
||||
actor: RuntimeActorWire {
|
||||
actor_type: "user".into(),
|
||||
actor_id: "user_1".into(),
|
||||
session_id: None,
|
||||
},
|
||||
source: RuntimeSourceWire {
|
||||
channel: "rust-web".into(),
|
||||
client: "mnote-web".into(),
|
||||
source_kind: None,
|
||||
root_uri: None,
|
||||
workspace_id: None,
|
||||
capabilities: Vec::new(),
|
||||
},
|
||||
target: Some(RuntimeTargetWire {
|
||||
workspace_id: Some("ws_1".into()),
|
||||
page_id: Some("doc_1".into()),
|
||||
block_id: Some("mind_1".into()),
|
||||
}),
|
||||
payload: json!({
|
||||
"documentId": "doc_1",
|
||||
"mindmapId": "mind_1",
|
||||
"data": {
|
||||
"data": {"text": "中心主题已更新"},
|
||||
"children": [],
|
||||
},
|
||||
"createOnly": false,
|
||||
}),
|
||||
preflight_data: None,
|
||||
reason: Some("更新导图内容".into()),
|
||||
refs: vec![],
|
||||
dry_run: false,
|
||||
validate_only: false,
|
||||
},
|
||||
})
|
||||
.expect("mindmap update command plan should build");
|
||||
|
||||
match plan {
|
||||
RuntimeExecutionPlan::Command(plan) => {
|
||||
assert_eq!(
|
||||
plan.args_json["streamDeltaHint"],
|
||||
json!({
|
||||
"family": "tree",
|
||||
"kind": "noop",
|
||||
"args": {}
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
plan.args_json["domainEventPlan"]["eventType"],
|
||||
json!("mindmap.content.updated")
|
||||
);
|
||||
assert_eq!(
|
||||
plan.args_json["domainEventPlan"]["streamDeltaHint"],
|
||||
json!({
|
||||
"family": "tree",
|
||||
"kind": "noop",
|
||||
"args": {}
|
||||
})
|
||||
);
|
||||
}
|
||||
_ => panic!("expected command plan"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn docs_search_tool_plan_uses_transport_and_transform_steps() {
|
||||
let plan = execute_runtime_input(RuntimeInput::Tool {
|
||||
@@ -15539,13 +15635,39 @@ mod tests {
|
||||
RuntimeExecutionPlan::Command(plan) => {
|
||||
assert_eq!(plan.command_name, "documents.options.update");
|
||||
assert_eq!(plan.function_name, "documents:updateOptions");
|
||||
assert_eq!(plan.args_json["id"], json!("doc_1"));
|
||||
assert_eq!(
|
||||
plan.args_json,
|
||||
plan.args_json["options"],
|
||||
json!({
|
||||
"id": "doc_1",
|
||||
"options": {
|
||||
"showToc": true,
|
||||
"layoutDensity": "compact",
|
||||
"showToc": true,
|
||||
"layoutDensity": "compact",
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
plan.args_json["streamDeltaHint"],
|
||||
json!({
|
||||
"family": "tree",
|
||||
"kind": "resync_required",
|
||||
"args": {
|
||||
"reason": "page.layout.updateOptions",
|
||||
"documentId": "doc_1",
|
||||
},
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
plan.args_json["domainEventPlan"],
|
||||
json!({
|
||||
"family": "tree",
|
||||
"schema": "mnote.tree.domain_event",
|
||||
"schemaVersion": 1,
|
||||
"eventType": "page.layout.options_updated",
|
||||
"streamDeltaHint": {
|
||||
"family": "tree",
|
||||
"kind": "resync_required",
|
||||
"args": {
|
||||
"reason": "page.layout.updateOptions",
|
||||
"documentId": "doc_1",
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user