fix: 今日新增筛选统计不准 + LLM提示词动态当前日期 + 微信配图按选题生成

topics.html:
- 今日新增筛选时全量拉取 allTopics 用于保持 stats 统计正确,todayTopics
  存储服务端日期过滤结果
- filteredTopics 对 'today' 返回 todayTopics,不按不存在的 status 过滤

writer.py/research.py/outline.py:
- 所有 LLM 提示词注入今天的动态年月日(datetime.now)
- 禁用过时数据的年份改为动态 {当前年-1}-{当前年}
- 每个论点必须配真实最新案例+数据来源

writer.py WeChat 配图:
- 从 null placeholder 改为动态 SVG,展示选题标题+领域
- 标题自动换行(24字/行),附蓝色装饰条 + 渐变背景
This commit is contained in:
Yuzhiran Dev
2026-05-18 06:25:47 +08:00
parent bb50d5d925
commit 52564ad036
59 changed files with 1361 additions and 40 deletions
+30 -6
View File
@@ -107,14 +107,17 @@ class Writer:
logger.info(f"使用 LLM 扩写章节: {section['title']}")
prompt = f"""你是一个真人写作者+行业观察者,正在写一篇关于「{self.topic['title']}」的文章。现在写「{section['title']}」这一节。
⚠️ 今天日期:{datetime.datetime.now().strftime('%Y年%m月%d')}。当前年份:{datetime.datetime.now().year}年。
笔记要点:
{content}
要求(逐条对照,每一条都不能跳过):
### 热点与时效
- **引用2025-2026年最新数据/事件/政策/行业报告**,禁用过时数据
- 体现当前国内外在讨论什么、最新的趋势变化
- 每个论点尽量配一个真实发生的最新案例
- **必须引用{datetime.datetime.now().year-1}-{datetime.datetime.now().year}年最新数据/事件/政策/行业报告**,禁用一切过时数据
- 体现当前国内外在讨论什么、最新的趋势变化
- 每个论点必须配一个真实发生的最新案例(附数据来源),严禁使用虚构数据
- 写作前先确认:这个数据和案例是否是最近{datetime.datetime.now().year-1}-{datetime.datetime.now().year}年的?
### 独特观点
- 有自己的判断和立场,拒绝"车轱辘话""正确废话"
@@ -371,12 +374,33 @@ class Writer:
html = template.replace("{{TITLE}}", title).replace("{{DATE}}", TODAY).replace("{{GEN_TIME}}", GEN_TIME)
html_content = _md_parser(adapted)
# WeChat: insert image placeholder at start of body
# WeChat: insert topic-relevant image at start of body
if platform == "wechat":
img_svg = '<svg xmlns="http://www.w3.org/2000/svg" width="1080" height="600" viewBox="0 0 1080 600" style="width:100%;max-width:1080px;border-radius:8px;background:#f0f2f5"><rect width="1080" height="600" fill="#f0f2f5"/><g transform="translate(540,260)" text-anchor="middle" font-family="-apple-system,BlinkMacSystemFont,sans-serif"><circle cx="0" cy="-20" r="40" fill="#d0d5dd"/><path d="M-25,10 L-10,10 L-5,0 L5,0 L10,10 L25,10 L25,60 L-25,60 Z" fill="#d0d5dd"/><circle cx="0" cy="10" r="15" fill="#fff"/><text y="80" font-size="18" fill="#98a2b3">点击替换配图(建议 1080×600</text></g></svg>'
import base64
topic_title = self.topic.get('title', title)
topic_field = self.topic.get('field', '')
safe_title = topic_title.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;').replace("'", '&apos;')
safe_field = topic_field.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
lines = []
chars_per_line = 24
for i in range(0, len(safe_title), chars_per_line):
lines.append(safe_title[i:i+chars_per_line])
if not lines:
lines = ['配图']
line_y = 220 - (len(lines) - 1) * 20
title_texts = ''.join(f'<text x="540" y="{line_y + i*55}" font-size="36" fill="#1a1a1a" font-weight="bold">{l}</text>' for i, l in enumerate(lines))
field_text = f'<text x="540" y="{line_y + len(lines)*55 + 30}" font-size="20" fill="#98a2b3">{safe_field}</text>' if safe_field else ''
img_svg = f'''<svg xmlns="http://www.w3.org/2000/svg" width="1080" height="600" viewBox="0 0 1080 600" style="width:100%;max-width:1080px;border-radius:8px;background:linear-gradient(135deg,#f0f4ff,#e8f0fe)">
<rect width="1080" height="600" fill="url(#bg)"/>
<defs><linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" style="stop-color:#f0f4ff"/><stop offset="100%" style="stop-color:#e8f0fe"/></linearGradient></defs>
<g transform="translate(540,300)" text-anchor="middle" font-family="-apple-system,BlinkMacSystemFont,Helvetica Neue,PingFang SC,Microsoft YaHei,sans-serif">
<rect x="-60" y="-100" width="120" height="4" rx="2" fill="#409eff"/>
{title_texts}
{field_text}
<text y="100" font-size="14" fill="#c0c4cc">宇之然 · 配图(可替换)</text>
</g></svg>'''
img_b64 = 'data:image/svg+xml;base64,' + base64.b64encode(img_svg.encode('utf-8')).decode('ascii')
img_tag = f'<p><img src="{img_b64}" alt="配图" style="width:100%;max-width:1080px;border-radius:8px;"></p>\n'
img_tag = f'<p><img src="{img_b64}" alt="{safe_title}" style="width:100%;max-width:1080px;border-radius:8px;"></p>\n'
h1_end = html_content.find('</h1>')
if h1_end != -1:
html_content = html_content[:h1_end + 5] + '\n' + img_tag + html_content[h1_end + 5:]