feat: 新增风险规则生成引擎与知识图谱可视化

后端新增风险规则自动生成和模板执行服务,支持从规则资产
批量生成并持久化风险规则文件;知识库入库日志增强图谱
查询和本地 RAG 回退,前端审计页面增加风险规则模型和流
程图组件,知识入库面板拆分为图谱可视化子组件,报销创
建页面增加引导式流程模型,更新知识库索引数据。
This commit is contained in:
caoxiaozhu
2026-05-23 19:54:42 +08:00
parent 5b388d08c0
commit 575f093c74
63 changed files with 35497 additions and 1517 deletions

View File

@@ -130,12 +130,14 @@ function normalizeDocument(rawDocument) {
textChars: toNumber(document.text_chars),
indexedTextChars: toNumber(document.indexed_text_chars),
sectionCount: toNumber(document.section_count || sections.length),
sections,
chunkCount: toNumber(document.chunk_count || chunks.length),
chunkIds: normalizeTextList(document.chunk_ids),
chunks,
entityCount: toNumber(document.entity_count || entities.length),
relationCount: toNumber(document.relation_count || relations.length),
entities,
entityChunks: normalizeEntityChunks(document.entity_chunks || document.entityChunks),
relations,
events: normalizeEvents(document.events)
}
@@ -165,7 +167,9 @@ function normalizeProgress(rawProgress, documents) {
function normalizeGraph(rawGraph, documents) {
const graph = asObject(rawGraph)
const fallbackEntities = dedupeTextList(documents.flatMap((item) => item.entities))
const graphEntities = normalizeEntities(graph.entities)
const fallbackEntities = dedupeEntities(documents.flatMap((item) => item.entities))
const graphRelations = normalizeRelations(graph.relations)
const fallbackRelations = dedupeRelations(documents.flatMap((item) => item.relations))
return {
chunkCount: toNumber(
@@ -177,12 +181,8 @@ function normalizeGraph(rawGraph, documents) {
relationCount: toNumber(
graph.relation_count || documents.reduce((total, item) => total + item.relationCount, 0)
),
entities: normalizeTextList(graph.entities).length
? normalizeTextList(graph.entities)
: fallbackEntities,
relations: normalizeRelations(graph.relations).length
? normalizeRelations(graph.relations)
: fallbackRelations
entities: graphEntities.length ? graphEntities : fallbackEntities,
relations: graphRelations.length ? graphRelations : fallbackRelations
}
}
@@ -195,12 +195,28 @@ function normalizeChunks(rawChunks) {
id: String(item.id || item._id || `chunk-${index + 1}`).trim(),
order: toNumber(item.order ?? item.chunk_order_index ?? index),
tokens: toNumber(item.tokens),
summary: String(item.summary || item.content || '').trim()
summary: String(item.summary || item.content || '').trim(),
excerpt: String(item.excerpt || item.content_preview || item.summary || item.content || '').trim()
}
})
.sort((left, right) => left.order - right.order)
}
function normalizeEntityChunks(rawItems) {
if (!Array.isArray(rawItems)) return []
const result = []
const seen = new Set()
for (const rawItem of rawItems) {
const item = asObject(rawItem)
const entity = String(item.entity || item.name || '').trim()
const chunkIds = normalizeTextList(item.chunk_ids || item.chunkIds)
if (!entity || !chunkIds.length || seen.has(entity)) continue
seen.add(entity)
result.push({ entity, chunkIds })
}
return result
}
function normalizeSections(rawSections) {
if (!Array.isArray(rawSections)) return []
return rawSections.map((section, index) => {
@@ -225,7 +241,8 @@ function normalizeEvents(rawEvents) {
}
function normalizeEntities(rawEntities) {
return normalizeTextList(rawEntities)
if (!Array.isArray(rawEntities)) return []
return dedupeEntities(rawEntities)
}
function normalizeRelations(rawRelations) {
@@ -236,7 +253,11 @@ function normalizeRelations(rawRelations) {
return {
source: String(item.source || item.from || '').trim(),
target: String(item.target || item.to || '').trim(),
type: String(item.type || '关联').trim()
type: String(item.type || '关联').trim(),
description: String(item.description || '').trim(),
keywords: normalizeTextList(item.keywords),
weight: toNumber(item.weight || item.confidence || 1),
properties: asObject(item.properties)
}
})
.filter((item) => item.source && item.target)
@@ -263,15 +284,22 @@ function asObject(value) {
}
function normalizeTextList(value) {
if (!Array.isArray(value)) return []
return dedupeTextList(value)
if (Array.isArray(value)) return dedupeTextList(value)
return dedupeTextList(
String(value || '')
.split('<SEP>')
.filter(Boolean)
)
}
function dedupeTextList(items) {
const result = []
const seen = new Set()
for (const item of items) {
const text = String(item || '').trim()
const text =
typeof item === 'string'
? item.trim()
: String(item?.name || item?.entity || item?.title || item?.id || '').trim()
if (!text || seen.has(text)) continue
seen.add(text)
result.push(text)
@@ -279,6 +307,43 @@ function dedupeTextList(items) {
return result
}
function dedupeEntities(items) {
const result = []
const seen = new Set()
for (const rawItem of items) {
const item = asObject(rawItem)
const name =
typeof rawItem === 'string'
? rawItem.trim()
: String(
item.name ||
item.entity ||
item.entity_id ||
item.title ||
item.id ||
''
).trim()
if (!name || seen.has(name)) continue
seen.add(name)
const description = String(item.description || '').trim()
const descriptions = normalizeTextList(item.descriptions).length
? normalizeTextList(item.descriptions)
: description
? [description]
: []
result.push({
...item,
name,
type: String(item.type || item.entity_type || item.category || item.kind || '实体').trim(),
description,
descriptions,
properties: asObject(item.properties),
labels: normalizeTextList(item.labels)
})
}
return result
}
function dedupeRelations(items) {
const result = []
const seen = new Set()
@@ -289,7 +354,7 @@ function dedupeRelations(items) {
const key = `${source}::${target}::${type}`
if (!source || !target || seen.has(key)) continue
seen.add(key)
result.push({ source, target, type })
result.push({ ...item, source, target, type })
}
return result
}