mirror of
https://github.com/ZhuLinsen/daily_stock_analysis
synced 2026-09-20 10:53:33 +08:00
fix(notification): 修复邮件中表格不渲染及排版松散问题 (fixes #134)
- 引入 markdown2 库替代简单的正则替换,支持 Markdown 表格渲染 - 重构邮件 HTML 生成逻辑,添加完整的 CSS 样式支持 - 优化邮件排版,调整间距使内容更加紧凑易读 - 增加表格边框、隔行变色等样式,提升数据展示效果
This commit is contained in:
154
notification.py
154
notification.py
@@ -19,6 +19,7 @@ import logging
|
||||
import json
|
||||
import smtplib
|
||||
import re
|
||||
import markdown2
|
||||
from datetime import datetime
|
||||
from typing import List, Dict, Any, Optional
|
||||
from email.mime.text import MIMEText
|
||||
@@ -1763,56 +1764,129 @@ class NotificationService:
|
||||
|
||||
def _markdown_to_html(self, markdown_text: str) -> str:
|
||||
"""
|
||||
将 Markdown 转换为简单的 HTML
|
||||
|
||||
支持:标题、加粗、列表、分隔线
|
||||
将 Markdown 转换为 HTML,支持表格并优化排版
|
||||
|
||||
使用 markdown2 库进行转换,并添加优化的 CSS 样式
|
||||
解决问题:
|
||||
1. 邮件表格未渲染问题
|
||||
2. 邮件内容排版过于松散问题
|
||||
"""
|
||||
html = markdown_text
|
||||
|
||||
# 转义 HTML 特殊字符
|
||||
html = html.replace('&', '&')
|
||||
html = html.replace('<', '<')
|
||||
html = html.replace('>', '>')
|
||||
|
||||
# 标题 (# ## ###)
|
||||
html = re.sub(r'^### (.+)$', r'<h3>\1</h3>', html, flags=re.MULTILINE)
|
||||
html = re.sub(r'^## (.+)$', r'<h2>\1</h2>', html, flags=re.MULTILINE)
|
||||
html = re.sub(r'^# (.+)$', r'<h1>\1</h1>', html, flags=re.MULTILINE)
|
||||
|
||||
# 加粗 **text**
|
||||
html = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', html)
|
||||
|
||||
# 斜体 *text*
|
||||
html = re.sub(r'\*(.+?)\*', r'<em>\1</em>', html)
|
||||
|
||||
# 分隔线 ---
|
||||
html = re.sub(r'^---$', r'<hr>', html, flags=re.MULTILINE)
|
||||
|
||||
# 列表项 - item
|
||||
html = re.sub(r'^- (.+)$', r'<li>\1</li>', html, flags=re.MULTILINE)
|
||||
|
||||
# 引用 > text
|
||||
html = re.sub(r'^> (.+)$', r'<blockquote>\1</blockquote>', html, flags=re.MULTILINE)
|
||||
|
||||
# 换行
|
||||
html = html.replace('\n', '<br>\n')
|
||||
|
||||
# 包装 HTML
|
||||
# 使用 markdown2 转换,开启表格和其他扩展支持
|
||||
html_content = markdown2.markdown(
|
||||
markdown_text,
|
||||
extras=["tables", "fenced-code-blocks", "break-on-newline", "cuddled-lists"]
|
||||
)
|
||||
|
||||
# 优化 CSS 样式:更紧凑的排版,美观的表格
|
||||
css_style = """
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
color: #24292e;
|
||||
font-size: 14px;
|
||||
padding: 15px;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
h1 {
|
||||
font-size: 20px;
|
||||
border-bottom: 1px solid #eaecef;
|
||||
padding-bottom: 0.3em;
|
||||
margin-top: 1.2em;
|
||||
margin-bottom: 0.8em;
|
||||
color: #0366d6;
|
||||
}
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
border-bottom: 1px solid #eaecef;
|
||||
padding-bottom: 0.3em;
|
||||
margin-top: 1.0em;
|
||||
margin-bottom: 0.6em;
|
||||
}
|
||||
h3 {
|
||||
font-size: 16px;
|
||||
margin-top: 0.8em;
|
||||
margin-bottom: 0.4em;
|
||||
}
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
/* 表格样式优化 */
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 12px 0;
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
font-size: 13px;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid #dfe2e5;
|
||||
padding: 6px 10px;
|
||||
text-align: left;
|
||||
}
|
||||
th {
|
||||
background-color: #f6f8fa;
|
||||
font-weight: 600;
|
||||
}
|
||||
tr:nth-child(2n) {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
tr:hover {
|
||||
background-color: #f1f8ff;
|
||||
}
|
||||
/* 引用块样式 */
|
||||
blockquote {
|
||||
color: #6a737d;
|
||||
border-left: 0.25em solid #dfe2e5;
|
||||
padding: 0 1em;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
/* 代码块样式 */
|
||||
code {
|
||||
padding: 0.2em 0.4em;
|
||||
margin: 0;
|
||||
font-size: 85%;
|
||||
background-color: rgba(27,31,35,0.05);
|
||||
border-radius: 3px;
|
||||
font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
|
||||
}
|
||||
pre {
|
||||
padding: 12px;
|
||||
overflow: auto;
|
||||
line-height: 1.45;
|
||||
background-color: #f6f8fa;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
hr {
|
||||
height: 0.25em;
|
||||
padding: 0;
|
||||
margin: 16px 0;
|
||||
background-color: #e1e4e8;
|
||||
border: 0;
|
||||
}
|
||||
ul, ol {
|
||||
padding-left: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
li {
|
||||
margin: 2px 0;
|
||||
}
|
||||
"""
|
||||
|
||||
return f"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; padding: 20px; max-width: 800px; margin: 0 auto; }}
|
||||
h1, h2, h3 {{ color: #333; }}
|
||||
hr {{ border: none; border-top: 1px solid #ddd; margin: 20px 0; }}
|
||||
blockquote {{ border-left: 4px solid #ddd; padding-left: 16px; color: #666; }}
|
||||
li {{ margin: 4px 0; }}
|
||||
{css_style}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{html}
|
||||
{html_content}
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
@@ -32,6 +32,7 @@ google-search-results>=2.4.0 # SerpAPI(每月 100 次免费)
|
||||
|
||||
# 网络请求
|
||||
requests>=2.31.0 # HTTP 请求
|
||||
markdown2>=2.4.0 # Markdown 转 HTML
|
||||
fake-useragent>=1.4.0 # 随机 User-Agent 防封禁
|
||||
httpx[socks] # HTTP 客户端 + SOCKS 代理支持(OpenAI 可选依赖)
|
||||
dingtalk-stream >= 0.24.3 # 钉钉 Stream SDK
|
||||
|
||||
Reference in New Issue
Block a user