个股论坛重做
This commit is contained in:
@@ -986,6 +986,26 @@ def remove_reaction(message_id, emoji):
|
|||||||
# Forum 帖子相关 API
|
# Forum 帖子相关 API
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
|
def parse_markdown_images(content):
|
||||||
|
"""
|
||||||
|
将 Markdown 图片语法转换为 HTML img 标签
|
||||||
|
支持  格式,包括 base64 data URL
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
# 匹配  格式
|
||||||
|
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'])
|
@community_bp.route('/channels/<channel_id>/posts', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def create_post(channel_id):
|
def create_post(channel_id):
|
||||||
@@ -1005,6 +1025,9 @@ def create_post(channel_id):
|
|||||||
post_id = generate_id()
|
post_id = generate_id()
|
||||||
now = datetime.utcnow()
|
now = datetime.utcnow()
|
||||||
|
|
||||||
|
# 将 Markdown 图片语法转换为 HTML
|
||||||
|
content_html = parse_markdown_images(content)
|
||||||
|
|
||||||
# 构建帖子文档
|
# 构建帖子文档
|
||||||
post_doc = {
|
post_doc = {
|
||||||
'id': post_id,
|
'id': post_id,
|
||||||
@@ -1014,7 +1037,7 @@ def create_post(channel_id):
|
|||||||
'author_avatar': user.get('avatar', ''),
|
'author_avatar': user.get('avatar', ''),
|
||||||
'title': title,
|
'title': title,
|
||||||
'content': content,
|
'content': content,
|
||||||
'content_html': content, # 可以后续添加 Markdown 渲染
|
'content_html': content_html, # Markdown 转换后的 HTML
|
||||||
'tags': data.get('tags', []),
|
'tags': data.get('tags', []),
|
||||||
'stock_symbols': data.get('stockSymbols', []),
|
'stock_symbols': data.get('stockSymbols', []),
|
||||||
'is_pinned': False,
|
'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_result = es_client.get(index='community_forum_posts', id=post_id)
|
||||||
post = post_result['_source']
|
post = post_result['_source']
|
||||||
|
|
||||||
|
# 将 Markdown 图片语法转换为 HTML
|
||||||
|
content_html = parse_markdown_images(content)
|
||||||
|
|
||||||
# 构建回复文档
|
# 构建回复文档
|
||||||
reply_doc = {
|
reply_doc = {
|
||||||
'id': reply_id,
|
'id': reply_id,
|
||||||
@@ -1143,7 +1169,7 @@ def create_reply(post_id):
|
|||||||
'author_name': user['username'],
|
'author_name': user['username'],
|
||||||
'author_avatar': user.get('avatar', ''),
|
'author_avatar': user.get('avatar', ''),
|
||||||
'content': content,
|
'content': content,
|
||||||
'content_html': content,
|
'content_html': content_html, # Markdown 转换后的 HTML
|
||||||
'reply_to': data.get('replyTo'),
|
'reply_to': data.get('replyTo'),
|
||||||
'reactions': {},
|
'reactions': {},
|
||||||
'like_count': 0,
|
'like_count': 0,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -297,10 +297,32 @@ export const getForumPosts = async (
|
|||||||
if (!response.ok) throw new Error('获取帖子列表失败');
|
if (!response.ok) throw new Error('获取帖子列表失败');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
const items = data.hits.hits.map((hit: any) => ({
|
// 将 ES 中的 snake_case 字段名转换为 camelCase
|
||||||
id: hit._id,
|
const items = data.hits.hits.map((hit: any) => {
|
||||||
...hit._source,
|
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 {
|
return {
|
||||||
items,
|
items,
|
||||||
@@ -394,10 +416,26 @@ export const getForumReplies = async (
|
|||||||
if (!response.ok) throw new Error('获取回复失败');
|
if (!response.ok) throw new Error('获取回复失败');
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
const items = data.hits.hits.map((hit: any) => ({
|
// 将 ES 中的 snake_case 字段名转换为 camelCase
|
||||||
id: hit._id,
|
const items = data.hits.hits.map((hit: any) => {
|
||||||
...hit._source,
|
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 {
|
return {
|
||||||
items,
|
items,
|
||||||
|
|||||||
Reference in New Issue
Block a user