文章目录[隐藏]
一步步教你,在WordPress中添加网站内容自动生成思维导图与知识图谱工具
引言:当WordPress遇见知识可视化
在信息爆炸的时代,网站内容的管理和呈现方式直接影响着用户体验和知识传递效率。传统的线性文章结构虽然清晰,却难以展现复杂概念间的关联性。想象一下,当读者浏览一篇关于“人工智能发展史”的长文时,如果能同时看到一个动态生成的思维导图,清晰展示从图灵测试到深度学习的关键节点及其关联,理解效率将大幅提升。
WordPress作为全球最流行的内容管理系统,其强大之处不仅在于开箱即用的功能,更在于无限的可扩展性。通过代码二次开发,我们可以将这种知识可视化能力直接集成到WordPress中,让每一篇文章、每一个知识点都能自动转化为结构化的思维导图和知识图谱。
本文将详细指导您如何通过WordPress代码二次开发,实现网站内容的自动可视化,不仅提升用户体验,也为您的网站增添独特的智能工具功能。
第一部分:理解基础概念与技术选型
1.1 思维导图与知识图谱的核心区别
在开始技术实现之前,我们需要明确两个核心概念:
思维导图(Mind Map)是一种放射状的树形结构,以一个中心主题出发,逐级展开相关子主题,强调发散性思维和记忆关联。它适合用于内容摘要、思路整理和快速浏览。
知识图谱(Knowledge Graph)则是更为复杂的网络结构,由实体、属性和关系构成,能够表达多对多的复杂关联。它更适合表现深层次的知识体系和概念间的多维联系。
在WordPress中实现这两种可视化工具,需要根据内容类型和用户需求进行选择。例如,教程类文章更适合思维导图,而学术类或产品比较类内容则更适合知识图谱。
1.2 技术栈选择与评估
实现这一功能,我们需要考虑以下技术组件:
前端可视化库:
- Mind Map库:Markmap、jsMind、MindElixir都是优秀的选择。Markmap基于Markdown,与WordPress编辑器兼容性好;jsMind功能丰富,支持多种布局;MindElixir界面现代,交互流畅。
- 知识图谱库:Vis.js、D3.js、Cytoscape.js。Vis.js易于上手,性能良好;D3.js功能强大但学习曲线陡峭;Cytoscape.js专为复杂网络设计,适合大型知识图谱。
后端处理方案:
- 纯前端方案:内容直接在前端解析生成,适合小型网站,减轻服务器压力。
- 前后端结合:后端预处理内容结构,前端负责渲染,适合内容复杂、需要优化的场景。
- API服务集成:利用第三方NLP服务进行内容分析,适合需要深度语义理解的场景。
WordPress集成方式:
- 短代码(Shortcode)实现:最简单的方式,通过短代码嵌入可视化内容
- Gutenberg块开发:现代WordPress编辑器的原生扩展方式
- 元数据字段扩展:为文章添加结构化数据字段
- 独立插件开发:最灵活、可复用性最高的方式
考虑到可维护性和扩展性,本文将采用Gutenberg块开发为主,短代码为辅的混合方案。
第二部分:开发环境搭建与基础配置
2.1 本地开发环境配置
在开始编码前,我们需要搭建合适的开发环境:
- 本地服务器环境:推荐使用Local by Flywheel或XAMPP,它们提供了完整的PHP+MySQL环境,并针对WordPress进行了优化。
-
代码编辑器:VS Code是当前最流行的选择,安装以下扩展:
- PHP Intelephense(PHP智能提示)
- WordPress Snippet(WordPress代码片段)
- ESLint(JavaScript代码检查)
- GitLens(版本管理)
- 浏览器开发工具:Chrome DevTools是前端调试的必备工具,特别是Elements和Console面板。
- 版本控制:初始化Git仓库,定期提交代码变更。
2.2 创建自定义插件框架
我们首先创建一个独立的插件,避免直接修改主题文件:
<?php
/**
* Plugin Name: Content Visualizer - Mind Map & Knowledge Graph
* Plugin URI: https://yourwebsite.com/
* Description: 自动将WordPress内容转换为思维导图和知识图谱
* Version: 1.0.0
* Author: Your Name
* License: GPL v2 or later
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('CV_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('CV_PLUGIN_URL', plugin_dir_url(__FILE__));
define('CV_VERSION', '1.0.0');
// 初始化插件
class Content_Visualizer {
private static $instance = null;
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
$this->init_hooks();
}
private function init_hooks() {
// 前后端钩子
add_action('init', array($this, 'register_blocks'));
add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_assets'));
add_action('enqueue_block_editor_assets', array($this, 'enqueue_editor_assets'));
// 短代码注册
add_shortcode('mind_map', array($this, 'mind_map_shortcode'));
add_shortcode('knowledge_graph', array($this, 'knowledge_graph_shortcode'));
}
// 后续方法将在这里添加
}
// 启动插件
Content_Visualizer::get_instance();
2.3 资源文件组织架构
创建清晰的目录结构:
content-visualizer/
├── content-visualizer.php # 主插件文件
├── includes/ # PHP类文件
│ ├── class-content-parser.php
│ ├── class-mindmap-generator.php
│ └── class-knowledgegraph-generator.php
├── assets/ # 静态资源
│ ├── css/
│ ├── js/
│ └── lib/ # 第三方库
├── blocks/ # Gutenberg块
│ ├── mind-map/
│ └── knowledge-graph/
├── templates/ # 前端模板
└── languages/ # 国际化文件
第三部分:内容解析与结构化处理
3.1 自动提取文章结构
内容解析是自动生成可视化的核心。我们需要从文章中提取层次结构和关键概念:
// includes/class-content-parser.php
class Content_Parser {
/**
* 从文章内容中提取标题层次结构
*/
public static function extract_headings($content) {
$pattern = '/<h([1-6])[^>]*>(.*?)</h[1-6]>/i';
preg_match_all($pattern, $content, $matches, PREG_SET_ORDER);
$structure = array();
foreach ($matches as $match) {
$level = intval($match[1]);
$text = strip_tags($match[2]);
$id = sanitize_title($text);
$structure[] = array(
'level' => $level,
'text' => $text,
'id' => $id,
'children' => array()
);
}
// 构建层次树
return self::build_heading_tree($structure);
}
/**
* 将扁平标题列表转换为树形结构
*/
private static function build_heading_tree($headings) {
$tree = array();
$stack = array();
foreach ($headings as $heading) {
$node = array(
'text' => $heading['text'],
'id' => $heading['id'],
'children' => array()
);
// 确定父节点
while (!empty($stack)) {
$last = end($stack);
if ($last['level'] < $heading['level']) {
// 当前节点是最后一个节点的子节点
$tree_ref = &$this->find_node_ref($tree, $last['id']);
$tree_ref['children'][] = &$node;
break;
} else {
array_pop($stack);
}
}
// 如果没有父节点,添加到根
if (empty($stack)) {
$tree[] = &$node;
}
$stack[] = array(
'level' => $heading['level'],
'id' => $heading['id'],
'node' => &$node
);
unset($node);
}
return $tree;
}
/**
* 提取文章中的关键实体(人名、地名、专业术语等)
*/
public static function extract_entities($content) {
// 简化版实体提取,实际项目中可集成NLP服务
$text = strip_tags($content);
// 提取可能的技术术语(大写字母开头的单词序列)
preg_match_all('/b[A-Z][a-z]+(?:s+[A-Z][a-z]+)*b/', $text, $matches);
$technical_terms = array_unique($matches[0]);
// 提取引号内的概念
preg_match_all('/["']([^"']+)["']/', $text, $matches);
$quoted_concepts = array_unique($matches[1]);
// 合并并过滤
$entities = array_merge($technical_terms, $quoted_concepts);
$entities = array_filter($entities, function($entity) {
return strlen($entity) > 2 && strlen($entity) < 50;
});
return array_values($entities);
}
/**
* 分析实体间的关系(基于共现分析)
*/
public static function analyze_relationships($content, $entities) {
$relationships = array();
$text = strip_tags($content);
$sentences = preg_split('/[.!?]+/', $text);
foreach ($sentences as $sentence) {
$sentence_entities = array();
foreach ($entities as $entity) {
if (stripos($sentence, $entity) !== false) {
$sentence_entities[] = $entity;
}
}
// 如果句子中包含多个实体,建立关系
if (count($sentence_entities) > 1) {
for ($i = 0; $i < count($sentence_entities); $i++) {
for ($j = $i + 1; $j < count($sentence_entities); $j++) {
$key = $sentence_entities[$i] . '|' . $sentence_entities[$j];
if (!isset($relationships[$key])) {
$relationships[$key] = 0;
}
$relationships[$key]++;
}
}
}
}
// 转换为前端需要的格式
$result = array();
foreach ($relationships as $key => $strength) {
list($source, $target) = explode('|', $key);
if ($strength > 1) { // 只保留强度大于1的关系
$result[] = array(
'source' => $source,
'target' => $target,
'strength' => $strength,
'label' => '相关' // 可进一步分析关系类型
);
}
}
return $result;
}
}
3.2 缓存机制优化性能
内容解析可能消耗较多资源,我们需要实现缓存机制:
class Content_Cache {
private static $cache_group = 'content_visualizer';
/**
* 获取缓存内容
*/
public static function get($post_id, $type) {
$key = "{$type}_{$post_id}";
$cached = wp_cache_get($key, self::$cache_group);
if ($cached !== false) {
return $cached;
}
// 从数据库获取
$transient_key = "cv_{$key}";
$cached = get_transient($transient_key);
if ($cached !== false) {
wp_cache_set($key, $cached, self::$cache_group, 3600);
return $cached;
}
return false;
}
/**
* 设置缓存
*/
public static function set($post_id, $type, $data, $expiration = 3600) {
$key = "{$type}_{$post_id}";
// 设置内存缓存
wp_cache_set($key, $data, self::$cache_group, $expiration);
// 设置数据库缓存
$transient_key = "cv_{$key}";
set_transient($transient_key, $data, $expiration);
// 建立缓存索引,便于文章更新时清理
$index = get_option('cv_cache_index', array());
if (!isset($index[$post_id])) {
$index[$post_id] = array();
}
$index[$post_id][] = $transient_key;
update_option('cv_cache_index', $index, false);
}
/**
* 清理文章缓存
*/
public static function clear_post_cache($post_id) {
$index = get_option('cv_cache_index', array());
if (isset($index[$post_id])) {
foreach ($index[$post_id] as $transient_key) {
delete_transient($transient_key);
}
unset($index[$post_id]);
update_option('cv_cache_index', $index, false);
}
// 清理内存缓存
wp_cache_delete("mindmap_{$post_id}", self::$cache_group);
wp_cache_delete("knowledgegraph_{$post_id}", self::$cache_group);
}
}
// 文章更新时清理缓存
add_action('save_post', function($post_id) {
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
Content_Cache::clear_post_cache($post_id);
});
第四部分:思维导图功能实现
4.1 集成Markmap可视化库
Markmap是基于D3.js的思维导图库,支持Markdown直接转换:
// assets/js/mindmap-renderer.js
class MindmapRenderer {
constructor(containerId, options = {}) {
this.container = document.getElementById(containerId);
this.options = Object.assign({
autoFit: true,
duration: 500,
maxWidth: 800,
nodeMinHeight: 16,
spacingVertical: 5,
spacingHorizontal: 80,
paddingX: 8
}, options);
this.mindmap = null;
this.initialized = false;
}
/**
* 从文章结构数据生成思维导图
*/
async renderFromStructure(structure) {
if (!this.initialized) {
await this.initialize();
}
// 转换结构为Markdown格式
const markdown = this.structureToMarkdown(structure);
// 创建Markmap
const { Markmap } = window.markmap;
const mm = Markmap.create(this.container, this.options, markdown);
this.mindmap = mm;
return mm;
}
/**
* 将树形结构转换为Markdown
*/
structureToMarkdown(node, level = 0) {
let markdown = '';
const indent = ' '.repeat(level);
if (Array.isArray(node)) {
// 根节点数组
node.forEach(child => {
markdown += this.structureToMarkdown(child, level);
});
} else {
// 单个节点
markdown += `${indent}- ${node.text}n`;
if (node.children && node.children.length > 0) {
node.children.forEach(child => {
markdown += this.structureToMarkdown(child, level + 1);
});
}
}
return markdown;
}
/**
* 初始化Markmap库
*/
async initialize() {
if (window.markmap) {
this.initialized = true;
return;
}
// 动态加载Markmap库
await this.loadScript(CV_PLUGIN_URL + 'assets/lib/markmap/markmap.min.js');
await this.loadStylesheet(CV_PLUGIN_URL + 'assets/lib/markmap/markmap.css');
this.initialized = true;
}
/**
* 动态加载脚本
*/
loadScript(src) {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = src;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
/**
* 动态加载样式
*/
loadStylesheet(href) {
return new Promise((resolve, reject) => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = href;
link.onload = resolve;
link.onerror = reject;
document.head.appendChild(link);
});
}
/**
* 导出功能
*/
exportAsImage(format = 'png') {
if (!this.mindmap) return;
const container = this.container;
const svg = container.querySelector('svg');
if (format === 'svg') {
// 导出SVG
const serializer = new XMLSerializer();
const source = serializer.serializeToString(svg);
const blob = new Blob([source], { type: 'image/svg+xml' });
this.downloadBlob(blob, 'mindmap.svg');
} else if (format === 'png') {
// 导出PNG
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const data = new XMLSerializer().serializeToString(svg);
const img = new Image();
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
canvas.toBlob(blob => {
this.downloadBlob(blob, 'mindmap.png');
}, 'image/png');
};
btoa(unescape(encodeURIComponent(data)));
}
}
/**
* 下载Blob文件
*/
downloadBlob(blob, filename) {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
}
// 全局可用
window.MindmapRenderer = MindmapRenderer;
#### 4.2 创建Gutenberg思维导图块
// blocks/mind-map/block.js
const { registerBlockType } = wp.blocks;
const {
InspectorControls,
useBlockProps,
BlockControls
} = wp.blockEditor;
const {
PanelBody,
SelectControl,
ToggleControl,
RangeControl,
ColorPicker
} = wp.components;
const { useState, useEffect } = wp.element;
registerBlockType('content-visualizer/mind-map', {
title: '思维导图',
icon: 'chart-line',
category: 'widgets',
attributes: {
postId: {
type: 'number',
default: 0
},
autoGenerate: {
type: 'boolean',
default: true
},
layout: {
type: 'string',
default: 'tree'
},
depth: {
type: 'number',
default: 3
},
primaryColor: {
type: 'string',
default: '#3366cc'
},
showControls: {
type: 'boolean',
default: true
}
},
edit: function({ attributes, setAttributes, clientId }) {
const blockProps = useBlockProps();
const [isLoading, setIsLoading] = useState(false);
const [structure, setStructure] = useState(null);
const [error, setError] = useState(null);
// 获取当前文章ID
useEffect(() => {
if (attributes.postId === 0) {
const currentPostId = wp.data.select('core/editor').getCurrentPostId();
setAttributes({ postId: currentPostId });
}
}, []);
// 加载文章结构
useEffect(() => {
if (attributes.autoGenerate && attributes.postId > 0) {
fetchStructure();
}
}, [attributes.postId, attributes.autoGenerate]);
const fetchStructure = async () => {
setIsLoading(true);
setError(null);
try {
const response = await wp.apiFetch({
path: `/content-visualizer/v1/mindmap/${attributes.postId}`,
method: 'GET'
});
if (response.success) {
setStructure(response.data);
} else {
setError(response.message || '获取文章结构失败');
}
} catch (err) {
setError('网络请求失败: ' + err.message);
} finally {
setIsLoading(false);
}
};
const renderPreview = () => {
if (isLoading) {
return (
<div className="mindmap-loading">
<div className="spinner is-active"></div>
<p>正在生成思维导图...</p>
</div>
);
}
if (error) {
return (
<div className="mindmap-error">
<p>❌ {error}</p>
<button
className="components-button is-secondary"
onClick={fetchStructure}
>
重试
</button>
</div>
);
}
if (structure) {
// 简化预览
return (
<div className="mindmap-preview">
<div className="mindmap-tree">
{renderTreePreview(structure)}
</div>
<p className="mindmap-hint">
前端将显示交互式思维导图
</p>
</div>
);
}
return (
<div className="mindmap-empty">
<p>请确保已选择文章并启用自动生成</p>
</div>
);
};
const renderTreePreview = (nodes, level = 0) => {
if (!nodes || nodes.length === 0) return null;
return (
<ul style={{ paddingLeft: level * 20 }}>
{nodes.slice(0, 3).map((node, index) => (
<li key={index}>
<span>{node.text}</span>
{node.children && level < attributes.depth - 1 &&
renderTreePreview(node.children, level + 1)}
</li>
))}
{nodes.length > 3 && (
<li>... 还有 {nodes.length - 3} 个节点</li>
)}
</ul>
);
};
return (
<div {...blockProps}>
<BlockControls>
<div className="components-toolbar">
<button
className="components-button is-button"
onClick={fetchStructure}
disabled={isLoading}
>
{isLoading ? '刷新中...' : '刷新导图'}
</button>
</div>
</BlockControls>
<InspectorControls>
<PanelBody title="基本设置" initialOpen={true}>
<ToggleControl
label="自动生成"
checked={attributes.autoGenerate}
onChange={(value) => setAttributes({ autoGenerate: value })}
/>
<SelectControl
label="布局方式"
value={attributes.layout}
options={[
{ label: '树状图', value: 'tree' },
{ label: '放射状', value: 'radial' },
{ label: '鱼骨图', value: 'fishbone' }
]}
onChange={(value) => setAttributes({ layout: value })}
/>
<RangeControl
label="显示深度"
value={attributes.depth}
onChange={(value) => setAttributes({ depth: value })}
min={1}
max={6}
/>
</PanelBody>
<PanelBody title="样式设置" initialOpen={false}>
<div className="color-picker-control">
<label>主色调</label>
<ColorPicker
color={attributes.primaryColor}
onChangeComplete={(color) =>
setAttributes({ primaryColor: color.hex })
}
disableAlpha
/>
</div>
<ToggleControl
label="显示控制按钮"
checked={attributes.showControls}
onChange={(value) => setAttributes({ showControls: value })}
/>
</PanelBody>
</InspectorControls>
<div className="mindmap-block">
<h3>思维导图预览</h3>
{renderPreview()}
</div>
</div>
);
},
save: function() {
// 动态块,由前端渲染
return null;
}
});
#### 4.3 前端渲染与交互实现
// 在前端渲染思维导图
add_action('wp_footer', function() {
if (!has_block('content-visualizer/mind-map')) {
return;
}
?>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
// 查找所有思维导图容器
const mindmapContainers = document.querySelectorAll('.wp-block-content-visualizer-mind-map');
mindmapContainers.forEach(async (container, index) => {
const blockId = container.getAttribute('data-block-id');
const postId = container.getAttribute('data-post-id');
const config = JSON.parse(container.getAttribute('data-config') || '{}');
// 创建唯一容器ID
const renderContainerId = `mindmap-${postId}-${index}`;
const renderDiv = document.createElement('div');
renderDiv.id = renderContainerId;
renderDiv.className = 'mindmap-render-container';
container.appendChild(renderDiv);
try {
// 获取思维导图数据
const response = await fetch(
`<?php echo rest_url('content-visualizer/v1/mindmap/'); ?>${postId}`
);
if (!response.ok) {
throw new Error('获取数据失败');
}
const data = await response.json();
if (data.success) {
// 初始化渲染器
const renderer = new MindmapRenderer(renderContainerId, {
autoFit: true,
maxWidth: config.maxWidth || 800,
primaryColor: config.primaryColor || '#3366cc'
});
// 渲染思维导图
await renderer.renderFromStructure(data.data);
// 添加控制按钮
if (config.showControls !== false) {
addControlButtons(renderDiv, renderer);
}
} else {
renderDiv.innerHTML = `<p class="error">${data.message}</p>`;
}
} catch (error) {
renderDiv.innerHTML = `<p class="error">加载失败: ${error.message}</p>`;
}
});
function addControlButtons(container, renderer) {
const controls = document.createElement('div');
controls.className = 'mindmap-controls';
const zoomInBtn = createButton('放大', '+', () => {
if (renderer.mindmap) {
renderer.mindmap.fit();
renderer.mindmap.scale(renderer.mindmap.scale() * 1.2);
}
});
const zoomOutBtn = createButton('缩小', '-', () => {
if (renderer.mindmap) {
renderer.mindmap.scale(renderer.mindmap.scale() * 0.8);
}
});
const resetBtn = createButton('重置', '↺', () => {
if (renderer.mindmap) {
renderer.mindmap.fit();
}
});
const exportPngBtn = createButton('导出PNG', '📷', () => {
renderer.exportAsImage('png');
});
const exportSvgBtn = createButton('导出SVG', '🖼️', () => {
renderer.exportAsImage('svg');
});
controls.appendChild(zoomInBtn);
controls.appendChild(zoomOutBtn);
controls.appendChild(resetBtn);
controls.appendChild(exportPngBtn);
controls.appendChild(exportSvgBtn);
container.parentNode.insertBefore(controls, container.nextSibling);
}
function createButton(title, text, onClick) {
const button = document.createElement('button');
button.className = 'mindmap-control-btn';
button.title = title;
button.innerHTML = text;
button.addEventListener('click', onClick);
return button;
}
});
</script>
<style>
.mindmap-render-container {
width: 100%;
height: 500px;
border: 1px solid #eee;
border-radius: 8px;
overflow: hidden;
margin: 20px 0;
}
.mindmap-controls {
display: flex;
gap: 8px;
margin-top: 10px;
justify-content: center;
}
.mindmap-control-btn {
padding: 6px 12px;
background: #f5f5f5;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.mindmap-control-btn:hover {
background: #e9e9e9;
border-color: #ccc;
}
.mindmap-loading,
.mindmap-error,
.mindmap-empty {
padding: 40px;
text-align: center;
background: #f9f9f9;
border-radius: 8px;
margin: 20px 0;
}
.mindmap-preview .mindmap-tree {
max-height: 300px;
overflow-y: auto;
padding: 15px;
background: white;
border-radius: 6px;
border: 1px solid #e0e0e0;
}
.mindmap-preview ul {
list-style-type: none;
margin: 0;
padding: 0;
}
.mindmap-preview li {
padding: 4px 0;
color: #333;
}
.mindmap-preview li span {
padding: 2px 6px;
background: #f0f7ff;
border-radius: 3px;
border-left: 3px solid #3366cc;
}
.mindmap-hint {
font-size: 12px;
color: #666;
text-align: center;
margin-top: 10px;
font-style: italic;
}
</style>
<?php
});
### 第五部分:知识图谱功能实现
#### 5.1 集成Vis.js网络图库
// includes/class-knowledgegraph-generator.php
class KnowledgeGraph_Generator {
/**
* 生成知识图谱数据
*/
public static function generate_graph_data($post_id) {
// 检查缓存
$cached = Content_Cache::get($post_id, 'knowledgegraph');
if ($cached !== false) {
return $cached;
}
$post = get_post($post_id);
if (!$post) {
return new WP_Error('invalid_post', '文章不存在');
}
// 提取实体和关系
$entities = Content_Parser::extract_entities($post->post_content);
$relationships = Content_Parser::analyze_relationships($post->post_content, $entities);
// 构建Vis.js格式的数据
$nodes = array();
$edges = array();
// 创建节点
foreach ($entities as $index => $entity) {
$nodes[] = array(
'id' => $index,
'label' => $entity,
'value' => self::calculate_entity_importance($entity, $post->post_content),
'title' => self::generate_entity_tooltip($entity, $post->post_content),
'group' => self::categorize_entity($entity),
'font' => array(
'size' => 16,
'bold' => true
)
);
}
// 创建边(关系)
$entity_index = array_flip($entities);
foreach ($relationships as $rel) {
if (isset($entity_index[$rel['source']], $entity_index[$rel['target']])) {
$edges[] = array(
'from' => $entity_index[$rel['source']],
'to' => $entity_index[$rel['target']],
'label' => $rel['label'],
'value' => $rel['strength'],
'title' => "关联强度: {$rel['strength']}",
'arrows' => 'to',
'smooth' => array('type' => 'continuous')
);
}
}
$graph_data = array(
'nodes' => $nodes,
'edges' => $edges,
'metadata' => array(
'entity_count' => count($entities),
'relationship_count' => count($edges),
'generated_at' => current_time('mysql')
)
);
// 缓存结果
Content_Cache::set($post_id, 'knowledgegraph', $graph_data, 86400); // 缓存24小时
return $graph_data;
}
/**
* 计算实体重要性
*/
private static function calculate_entity_importance($entity, $content) {
// 基于出现频率和位置计算重要性
$frequency = substr_count(strtolower($content), strtolower($entity));
// 检查是否出现在标题中
$title_importance = 0;
$post_title = get_the_title();
if (stripos($post_title, $entity) !== false) {
$title_importance = 5;
}
// 检查是否出现在开头段落
$first_paragraph = wp_trim_words($content, 50);
$position_importance = stripos($first_paragraph, $entity) !== false ? 3 : 1;
return ($frequency * 2) + $title_importance + $position_importance;
}
/**
* 生成实体悬停提示
*/
private static function generate_entity_tooltip($entity, $content) {
$sentences = array();
$pattern = '/[^.!?]*b' . preg_quote($entity, '/') . 'b[^.!?]*[.!?]/i';
preg_match_all($pattern, $content, $matches);
if (!empty($matches[0])) {
$sentences = array_slice($matches[0], 0, 3); // 取前3个包含实体的句子
}
$tooltip = "<strong>{$entity}</strong><br><br>";
if (!empty($sentences)) {
$tooltip .= "相关描述:<br>";
foreach ($sentences as $sentence) {
$tooltip .= "• " . trim($sentence) . "<br>";
}
} else {
$tooltip .= "在文章中多次提及";
}
return $tooltip;
}
/**
* 实体分类
*/
private static function categorize_entity($entity) {
// 简单的分类逻辑,可根据需要扩展
$tech_keywords = ['AI', '算法', '编程', '数据', '网络', '系统'];
$business_keywords = ['市场', '营销', '管理', '战略', '投资', '财务'];
$people_keywords = ['先生', '女士', '博士', '教授', '团队', '专家'];
$entity_lower = strtolower($entity);
foreach ($tech_keywords as $keyword) {
if (stripos($entity, $keyword) !== false) {
return 'technology';
}
}
foreach ($business_keywords as $keyword) {
if (stripos($entity, $keyword) !== false) {
return 'business';
}
}
foreach ($people_keywords as $keyword) {
if (stripos($entity_lower, strtol
