add forum
This commit is contained in:
145
init-forum-es.js
Normal file
145
init-forum-es.js
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
/**
|
||||||
|
* 初始化价值论坛 Elasticsearch 索引
|
||||||
|
* 运行方式:node init-forum-es.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
// Elasticsearch 配置
|
||||||
|
const ES_BASE_URL = 'http://222.128.1.157:19200';
|
||||||
|
|
||||||
|
// 创建 axios 实例
|
||||||
|
const esClient = axios.create({
|
||||||
|
baseURL: ES_BASE_URL,
|
||||||
|
timeout: 10000,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 索引名称
|
||||||
|
const INDICES = {
|
||||||
|
POSTS: 'forum_posts',
|
||||||
|
COMMENTS: 'forum_comments',
|
||||||
|
EVENTS: 'forum_events',
|
||||||
|
};
|
||||||
|
|
||||||
|
async function initializeIndices() {
|
||||||
|
try {
|
||||||
|
console.log('开始初始化 Elasticsearch 索引...\n');
|
||||||
|
|
||||||
|
// 1. 创建帖子索引
|
||||||
|
console.log('创建帖子索引 (forum_posts)...');
|
||||||
|
try {
|
||||||
|
await esClient.put(`/${INDICES.POSTS}`, {
|
||||||
|
mappings: {
|
||||||
|
properties: {
|
||||||
|
id: { type: 'keyword' },
|
||||||
|
author_id: { type: 'keyword' },
|
||||||
|
author_name: { type: 'text' },
|
||||||
|
author_avatar: { type: 'keyword' },
|
||||||
|
title: { type: 'text' },
|
||||||
|
content: { type: 'text' },
|
||||||
|
images: { type: 'keyword' },
|
||||||
|
tags: { type: 'keyword' },
|
||||||
|
category: { type: 'keyword' },
|
||||||
|
likes_count: { type: 'integer' },
|
||||||
|
comments_count: { type: 'integer' },
|
||||||
|
views_count: { type: 'integer' },
|
||||||
|
created_at: { type: 'date' },
|
||||||
|
updated_at: { type: 'date' },
|
||||||
|
is_pinned: { type: 'boolean' },
|
||||||
|
status: { type: 'keyword' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log('✅ 帖子索引创建成功\n');
|
||||||
|
} catch (error) {
|
||||||
|
if (error.response?.status === 400 && error.response?.data?.error?.type === 'resource_already_exists_exception') {
|
||||||
|
console.log('⚠️ 帖子索引已存在,跳过创建\n');
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 创建评论索引
|
||||||
|
console.log('创建评论索引 (forum_comments)...');
|
||||||
|
try {
|
||||||
|
await esClient.put(`/${INDICES.COMMENTS}`, {
|
||||||
|
mappings: {
|
||||||
|
properties: {
|
||||||
|
id: { type: 'keyword' },
|
||||||
|
post_id: { type: 'keyword' },
|
||||||
|
author_id: { type: 'keyword' },
|
||||||
|
author_name: { type: 'text' },
|
||||||
|
author_avatar: { type: 'keyword' },
|
||||||
|
content: { type: 'text' },
|
||||||
|
parent_id: { type: 'keyword' },
|
||||||
|
likes_count: { type: 'integer' },
|
||||||
|
created_at: { type: 'date' },
|
||||||
|
status: { type: 'keyword' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log('✅ 评论索引创建成功\n');
|
||||||
|
} catch (error) {
|
||||||
|
if (error.response?.status === 400 && error.response?.data?.error?.type === 'resource_already_exists_exception') {
|
||||||
|
console.log('⚠️ 评论索引已存在,跳过创建\n');
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 创建事件时间轴索引
|
||||||
|
console.log('创建事件时间轴索引 (forum_events)...');
|
||||||
|
try {
|
||||||
|
await esClient.put(`/${INDICES.EVENTS}`, {
|
||||||
|
mappings: {
|
||||||
|
properties: {
|
||||||
|
id: { type: 'keyword' },
|
||||||
|
post_id: { type: 'keyword' },
|
||||||
|
event_type: { type: 'keyword' },
|
||||||
|
title: { type: 'text' },
|
||||||
|
description: { type: 'text' },
|
||||||
|
source: { type: 'keyword' },
|
||||||
|
source_url: { type: 'keyword' },
|
||||||
|
related_stocks: { type: 'keyword' },
|
||||||
|
occurred_at: { type: 'date' },
|
||||||
|
created_at: { type: 'date' },
|
||||||
|
importance: { type: 'keyword' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log('✅ 事件时间轴索引创建成功\n');
|
||||||
|
} catch (error) {
|
||||||
|
if (error.response?.status === 400 && error.response?.data?.error?.type === 'resource_already_exists_exception') {
|
||||||
|
console.log('⚠️ 事件时间轴索引已存在,跳过创建\n');
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 验证索引
|
||||||
|
console.log('验证索引...');
|
||||||
|
const indices = await esClient.get('/_cat/indices/forum_*?v&format=json');
|
||||||
|
console.log('已创建的论坛索引:');
|
||||||
|
indices.data.forEach(index => {
|
||||||
|
console.log(` - ${index.index} (docs: ${index['docs.count']}, size: ${index['store.size']})`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('\n🎉 所有索引初始化完成!');
|
||||||
|
console.log('\n下一步:');
|
||||||
|
console.log('1. 访问 https://valuefrontier.cn/value-forum');
|
||||||
|
console.log('2. 点击"发布帖子"按钮创建第一篇帖子');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 初始化失败:', error.message);
|
||||||
|
if (error.response) {
|
||||||
|
console.error('响应数据:', JSON.stringify(error.response.data, null, 2));
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行初始化
|
||||||
|
initializeIndices();
|
||||||
96
scripts/init-forum-indices.sh
Normal file
96
scripts/init-forum-indices.sh
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 初始化价值论坛 Elasticsearch 索引
|
||||||
|
# 使用 Nginx 代理或直连 ES
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "🚀 开始初始化价值论坛 Elasticsearch 索引..."
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# ES 地址(根据环境选择)
|
||||||
|
if [ -n "$USE_PROXY" ]; then
|
||||||
|
ES_URL="https://valuefrontier.cn/es-api"
|
||||||
|
echo "📡 使用 Nginx 代理: $ES_URL"
|
||||||
|
else
|
||||||
|
ES_URL="http://222.128.1.157:19200"
|
||||||
|
echo "📡 直连 Elasticsearch: $ES_URL"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 1. 创建帖子索引
|
||||||
|
echo "📝 创建帖子索引 (forum_posts)..."
|
||||||
|
curl -X PUT "$ES_URL/forum_posts" -H 'Content-Type: application/json' -d '{
|
||||||
|
"mappings": {
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "keyword" },
|
||||||
|
"author_id": { "type": "keyword" },
|
||||||
|
"author_name": { "type": "text" },
|
||||||
|
"author_avatar": { "type": "keyword" },
|
||||||
|
"title": { "type": "text" },
|
||||||
|
"content": { "type": "text" },
|
||||||
|
"images": { "type": "keyword" },
|
||||||
|
"tags": { "type": "keyword" },
|
||||||
|
"category": { "type": "keyword" },
|
||||||
|
"likes_count": { "type": "integer" },
|
||||||
|
"comments_count": { "type": "integer" },
|
||||||
|
"views_count": { "type": "integer" },
|
||||||
|
"created_at": { "type": "date" },
|
||||||
|
"updated_at": { "type": "date" },
|
||||||
|
"is_pinned": { "type": "boolean" },
|
||||||
|
"status": { "type": "keyword" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}' 2>/dev/null && echo "✅ 帖子索引创建成功" || echo "⚠️ 帖子索引已存在或创建失败"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 2. 创建评论索引
|
||||||
|
echo "💬 创建评论索引 (forum_comments)..."
|
||||||
|
curl -X PUT "$ES_URL/forum_comments" -H 'Content-Type: application/json' -d '{
|
||||||
|
"mappings": {
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "keyword" },
|
||||||
|
"post_id": { "type": "keyword" },
|
||||||
|
"author_id": { "type": "keyword" },
|
||||||
|
"author_name": { "type": "text" },
|
||||||
|
"author_avatar": { "type": "keyword" },
|
||||||
|
"content": { "type": "text" },
|
||||||
|
"parent_id": { "type": "keyword" },
|
||||||
|
"likes_count": { "type": "integer" },
|
||||||
|
"created_at": { "type": "date" },
|
||||||
|
"status": { "type": "keyword" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}' 2>/dev/null && echo "✅ 评论索引创建成功" || echo "⚠️ 评论索引已存在或创建失败"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 3. 创建事件时间轴索引
|
||||||
|
echo "⏰ 创建事件时间轴索引 (forum_events)..."
|
||||||
|
curl -X PUT "$ES_URL/forum_events" -H 'Content-Type: application/json' -d '{
|
||||||
|
"mappings": {
|
||||||
|
"properties": {
|
||||||
|
"id": { "type": "keyword" },
|
||||||
|
"post_id": { "type": "keyword" },
|
||||||
|
"event_type": { "type": "keyword" },
|
||||||
|
"title": { "type": "text" },
|
||||||
|
"description": { "type": "text" },
|
||||||
|
"source": { "type": "keyword" },
|
||||||
|
"source_url": { "type": "keyword" },
|
||||||
|
"related_stocks": { "type": "keyword" },
|
||||||
|
"occurred_at": { "type": "date" },
|
||||||
|
"created_at": { "type": "date" },
|
||||||
|
"importance": { "type": "keyword" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}' 2>/dev/null && echo "✅ 事件时间轴索引创建成功" || echo "⚠️ 事件时间轴索引已存在或创建失败"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# 4. 验证索引
|
||||||
|
echo "🔍 验证已创建的索引..."
|
||||||
|
curl -X GET "$ES_URL/_cat/indices/forum_*?v" 2>/dev/null
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "🎉 初始化完成!"
|
||||||
|
echo ""
|
||||||
|
echo "下一步:"
|
||||||
|
echo " 1. 访问 https://valuefrontier.cn/value-forum"
|
||||||
|
echo " 2. 点击"发布帖子"按钮创建第一篇帖子"
|
||||||
@@ -349,6 +349,11 @@ export const getCommentsByPostId = async (postId, { page = 1, size = 50 }) => {
|
|||||||
comments: response.data.hits.hits.map((hit) => ({ ...hit._source, _id: hit._id })),
|
comments: response.data.hits.hits.map((hit) => ({ ...hit._source, _id: hit._id })),
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// 如果索引不存在(404),返回空结果
|
||||||
|
if (error.response?.status === 404) {
|
||||||
|
console.warn('评论索引不存在,返回空结果:', INDICES.COMMENTS);
|
||||||
|
return { total: 0, comments: [] };
|
||||||
|
}
|
||||||
console.error('获取评论列表失败:', error);
|
console.error('获取评论列表失败:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@@ -407,6 +412,11 @@ export const getEventsByPostId = async (postId) => {
|
|||||||
|
|
||||||
return response.data.hits.hits.map((hit) => ({ ...hit._source, _id: hit._id }));
|
return response.data.hits.hits.map((hit) => ({ ...hit._source, _id: hit._id }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// 如果索引不存在(404),返回空数组而不是抛出错误
|
||||||
|
if (error.response?.status === 404) {
|
||||||
|
console.warn('事件索引不存在,返回空数组:', INDICES.EVENTS);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
console.error('获取事件时间轴失败:', error);
|
console.error('获取事件时间轴失败:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user