个股论坛重做

This commit is contained in:
2026-01-06 15:49:27 +08:00
parent 36316d4d7b
commit f35d444464
3 changed files with 1120 additions and 1129 deletions

View File

@@ -986,6 +986,26 @@ def remove_reaction(message_id, emoji):
# Forum 帖子相关 API
# ============================================================
def parse_markdown_images(content):
"""
将 Markdown 图片语法转换为 HTML img 标签
支持 ![alt](url) 格式,包括 base64 data URL
"""
import re
# 匹配 ![alt](url) 格式
pattern = r'!\[([^\]]*)\]\(([^)]+)\)'
def replace_image(match):
alt = match.group(1) or '图片'
url = match.group(2)
return f'<img src="{url}" alt="{alt}" style="max-width: 100%; border-radius: 8px; margin: 16px 0;" />'
html = re.sub(pattern, replace_image, content)
# 将换行转换为 <br>
html = html.replace('\n', '<br />')
return html
@community_bp.route('/channels/<channel_id>/posts', methods=['POST'])
@login_required
def create_post(channel_id):
@@ -1005,6 +1025,9 @@ def create_post(channel_id):
post_id = generate_id()
now = datetime.utcnow()
# 将 Markdown 图片语法转换为 HTML
content_html = parse_markdown_images(content)
# 构建帖子文档
post_doc = {
'id': post_id,
@@ -1014,7 +1037,7 @@ def create_post(channel_id):
'author_avatar': user.get('avatar', ''),
'title': title,
'content': content,
'content_html': content, # 可以后续添加 Markdown 渲染
'content_html': content_html, # Markdown 转换后的 HTML
'tags': data.get('tags', []),
'stock_symbols': data.get('stockSymbols', []),
'is_pinned': False,
@@ -1134,6 +1157,9 @@ def create_reply(post_id):
post_result = es_client.get(index='community_forum_posts', id=post_id)
post = post_result['_source']
# 将 Markdown 图片语法转换为 HTML
content_html = parse_markdown_images(content)
# 构建回复文档
reply_doc = {
'id': reply_id,
@@ -1143,7 +1169,7 @@ def create_reply(post_id):
'author_name': user['username'],
'author_avatar': user.get('avatar', ''),
'content': content,
'content_html': content,
'content_html': content_html, # Markdown 转换后的 HTML
'reply_to': data.get('replyTo'),
'reactions': {},
'like_count': 0,

File diff suppressed because it is too large Load Diff

View File

@@ -297,10 +297,32 @@ export const getForumPosts = async (
if (!response.ok) throw new Error('获取帖子列表失败');
const data = await response.json();
const items = data.hits.hits.map((hit: any) => ({
id: hit._id,
...hit._source,
}));
// 将 ES 中的 snake_case 字段名转换为 camelCase
const items = data.hits.hits.map((hit: any) => {
const source = hit._source;
return {
id: hit._id,
channelId: source.channel_id,
authorId: source.author_id,
authorName: source.author_name,
authorAvatar: source.author_avatar,
title: source.title,
content: source.content,
contentHtml: source.content_html,
tags: source.tags || [],
stockSymbols: source.stock_symbols || [],
isPinned: source.is_pinned,
isLocked: source.is_locked,
isDeleted: source.is_deleted,
replyCount: source.reply_count || 0,
viewCount: source.view_count || 0,
likeCount: source.like_count || 0,
lastReplyAt: source.last_reply_at,
lastReplyBy: source.last_reply_by,
createdAt: source.created_at,
updatedAt: source.updated_at,
};
});
return {
items,
@@ -394,10 +416,26 @@ export const getForumReplies = async (
if (!response.ok) throw new Error('获取回复失败');
const data = await response.json();
const items = data.hits.hits.map((hit: any) => ({
id: hit._id,
...hit._source,
}));
// 将 ES 中的 snake_case 字段名转换为 camelCase
const items = data.hits.hits.map((hit: any) => {
const source = hit._source;
return {
id: hit._id,
postId: source.post_id,
channelId: source.channel_id,
authorId: source.author_id,
authorName: source.author_name,
authorAvatar: source.author_avatar,
content: source.content,
contentHtml: source.content_html,
replyTo: source.reply_to,
reactions: source.reactions || {},
likeCount: source.like_count || 0,
isSolution: source.is_solution,
isDeleted: source.is_deleted,
createdAt: source.created_at,
};
});
return {
items,