You've already forked DataMate
feat(kg): 实现 Phase 3.2 Human-in-the-loop 编辑
核心功能: - 实体/关系编辑表单(创建/更新/删除) - 批量操作(批量删除节点/边) - 审核流程(提交审核 → 待审核列表 → 通过/拒绝) - 编辑模式切换(查看/编辑模式) - 权限控制(knowledgeGraphWrite 权限) 新增文件(后端,9 个): - EditReview.java - 审核记录领域模型(Neo4j 节点) - EditReviewRepository.java - 审核记录仓储(CRUD + 分页查询) - EditReviewService.java - 审核业务服务(提交/通过/拒绝,通过时自动执行变更) - EditReviewController.java - REST API(POST submit, POST approve/reject, GET pending) - DTOs: SubmitReviewRequest, EditReviewVO, ReviewActionRequest, BatchDeleteRequest - EditReviewServiceTest.java - 单元测试(21 tests) - EditReviewControllerTest.java - 集成测试(10 tests) 新增文件(前端,3 个): - EntityEditForm.tsx - 实体创建/编辑表单(Modal,支持名称/类型/描述/别名/置信度) - RelationEditForm.tsx - 关系创建/编辑表单(Modal,支持源/目标实体搜索、关系类型、权重/置信度) - ReviewPanel.tsx - 审核面板(待审核列表,通过/拒绝操作,拒绝带备注) 修改文件(后端,7 个): - GraphEntityService.java - 新增 batchDeleteEntities(),updateEntity 支持 confidence - GraphRelationService.java - 新增 batchDeleteRelations() - GraphEntityController.java - 删除批量删除端点(改为审核流程) - GraphRelationController.java - 删除批量删除端点(改为审核流程) - UpdateEntityRequest.java - 添加 confidence 字段 - KnowledgeGraphErrorCode.java - 新增 REVIEW_NOT_FOUND、REVIEW_ALREADY_PROCESSED - PermissionRuleMatcher.java - 添加 /api/knowledge-graph/** 写操作权限规则 修改文件(前端,8 个): - knowledge-graph.model.ts - 新增 EditReviewVO、ReviewOperationType、ReviewStatus 类型 - knowledge-graph.api.ts - BASE 改为 /api/knowledge-graph(走网关权限链),新增审核相关 API,删除批量删除直删方法 - vite.config.ts - 更新 dev proxy 路径 - NodeDetail.tsx - 新增 editMode 属性,编辑模式下显示编辑/删除按钮 - RelationDetail.tsx - 新增 editMode 属性,编辑模式下显示编辑/删除按钮 - KnowledgeGraphPage.tsx - 新增编辑模式开关(需要 knowledgeGraphWrite 权限)、创建实体/关系工具栏按钮、审核 Tab、批量操作 - GraphCanvas.tsx - 支持多选(editMode 时)、onSelectionChange 回调 - graphConfig.ts - 支持 multiSelect 参数 审核流程: - 所有编辑操作(创建/更新/删除/批量删除)都通过 submitReview 提交审核 - 审核通过后,EditReviewService.applyChange() 自动执行变更 - 批量删除端点已删除,只能通过审核流程 权限控制: - API 路径从 /knowledge-graph 改为 /api/knowledge-graph,走网关权限链 - 编辑模式开关需要 knowledgeGraphWrite 权限 - PermissionRuleMatcher 添加 /api/knowledge-graph/** 写操作规则 Bug 修复(Codex 审查后修复): - P0: 权限绕过(API 路径改为 /api/knowledge-graph) - P1: 审核流程未接入(所有编辑操作改为 submitReview) - P1: 批量删除绕过审核(删除直删端点,改为审核流程) - P1: confidence 字段丢失(UpdateEntityRequest 添加 confidence) - P2: 审核提交校验不足(添加跨字段校验器) - P2: 批量删除安全(添加 @Size(max=100) 限制,收集失败 ID) - P2: 前端错误处理(分开处理表单校验和 API 失败) 测试结果: - 后端: 311 tests pass ✅ (280 → 311, +31 new) - 前端: eslint clean ✅, tsc clean ✅, vite build success ✅
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { Drawer, Descriptions, Tag, Spin, Empty, message } from "antd";
|
||||
import { Drawer, Descriptions, Tag, Spin, Empty, Button, Popconfirm, Space, message } from "antd";
|
||||
import { Pencil, Trash2 } from "lucide-react";
|
||||
import type { RelationVO } from "../knowledge-graph.model";
|
||||
import {
|
||||
ENTITY_TYPE_LABELS,
|
||||
@@ -13,16 +14,22 @@ interface RelationDetailProps {
|
||||
graphId: string;
|
||||
relationId: string | null;
|
||||
open: boolean;
|
||||
editMode?: boolean;
|
||||
onClose: () => void;
|
||||
onEntityNavigate: (entityId: string) => void;
|
||||
onEditRelation?: (relation: RelationVO) => void;
|
||||
onDeleteRelation?: (relationId: string) => void;
|
||||
}
|
||||
|
||||
export default function RelationDetail({
|
||||
graphId,
|
||||
relationId,
|
||||
open,
|
||||
editMode = false,
|
||||
onClose,
|
||||
onEntityNavigate,
|
||||
onEditRelation,
|
||||
onDeleteRelation,
|
||||
}: RelationDetailProps) {
|
||||
const [relation, setRelation] = useState<RelationVO | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -42,8 +49,46 @@ export default function RelationDetail({
|
||||
.finally(() => setLoading(false));
|
||||
}, [graphId, relationId, open]);
|
||||
|
||||
const handleDelete = () => {
|
||||
if (relationId) {
|
||||
onDeleteRelation?.(relationId);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Drawer title="关系详情" open={open} onClose={onClose} width={400}>
|
||||
<Drawer
|
||||
title="关系详情"
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
width={400}
|
||||
extra={
|
||||
editMode && relation && (
|
||||
<Space>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<Pencil className="w-3 h-3" />}
|
||||
onClick={() => onEditRelation?.(relation)}
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Popconfirm
|
||||
title="确认删除此关系?"
|
||||
onConfirm={handleDelete}
|
||||
okText="确认"
|
||||
cancelText="取消"
|
||||
>
|
||||
<Button
|
||||
size="small"
|
||||
danger
|
||||
icon={<Trash2 className="w-3 h-3" />}
|
||||
>
|
||||
删除
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
>
|
||||
<Spin spinning={loading}>
|
||||
{relation ? (
|
||||
<div className="flex flex-col gap-4">
|
||||
|
||||
Reference in New Issue
Block a user