feat: 内容数据迁移至数据库,合规审查全链路打通

- 文章 HTML 存储从文件系统迁移至 articles 表,删除 releases 目录
- 合规审查从 DB 读取 HTML,审查结果写回 DB,通过后自动推进至待发布
- 新增 todayCount 筛选按钮,与系统概览统计数据一致
- 全屏预览修复:提升 z-index 超过侧边栏,添加退出全屏/关闭按钮
- 统一 '优化' → '审查' 命名,消除前后端术语不一致
- 调度器创作完成后自动触发审查(生成 → 审查 → 待发布)
- 清理旧备份/调试文件、过期大纲和研究笔记
This commit is contained in:
Yuzhiran Dev
2026-05-13 17:33:56 +08:00
parent bc6a302e59
commit 233e23016c
234 changed files with 5670 additions and 10651 deletions
+88 -21
View File
@@ -1,21 +1,25 @@
#!/usr/bin/env python3
"""
研究阶段:为选题收集资料并生成研究笔记(数据库版)
"""
import json, datetime, logging, sys, re
from pathlib import Path
from typing import Dict, List
PROJECT_ROOT = Path(__file__).parent.parent
sys.path.insert(0, str(PROJECT_ROOT))
sys.path.insert(0, str(PROJECT_ROOT / "platform" / "backend"))
# 导入数据库辅助模块
from db_helper import get_topic_by_id
try:
from app.core.nvidia_client import call_llm
HAVE_LLM = True
except ImportError:
HAVE_LLM = False
from trends import get_trend_context
from web_search import enrich_topic_research
DATA_DIR = PROJECT_ROOT / "automation" / "data"
CASES_FILE = DATA_DIR / "sustainability_cases.json"
OUTPUT_DIR = DATA_DIR / "research" # 研究笔记输出目录
OUTPUT_DIR = DATA_DIR / "research"
LOGS_DIR = PROJECT_ROOT / "automation" / "logs"
TODAY = datetime.datetime.now().strftime("%Y-%m-%d")
@@ -43,12 +47,10 @@ class Researcher:
return []
def find_relevant_cases(self, top_k: int = 5) -> List[Dict]:
"""基于标题和字段匹配相关案例(简化)"""
field = self.topic.get('field', '').lower()
title = self.topic.get('title', '').lower()
scored = []
for case in self.cases:
# 日期过滤:仅保留 2025 年及以后(支持 YYYY-MM-DD 或 YYYY 格式)
case_date = case.get('date', '')
if case_date:
m = re.search(r'(\d{4})', str(case_date))
@@ -57,7 +59,6 @@ class Researcher:
score = 0
if field and field in case.get('field', '').lower():
score += 3
# 标题关键词匹配
case_title = case.get('title', '').lower()
for word in title.split():
if len(word) > 2 and word in case_title:
@@ -67,9 +68,43 @@ class Researcher:
scored.sort(key=lambda x: x[0], reverse=True)
return [c for _, c in scored[:top_k]]
def _llm_summary(self, cases: List[Dict]) -> str:
if not HAVE_LLM:
return ""
cases_text = json.dumps(cases, ensure_ascii=False, indent=2)
# 获取实时搜索数据作为 LLM 参考
search_data = enrich_topic_research(self.topic)
search_section = f"\n## 实时搜索结果\n{search_data}\n" if search_data else ""
prompt = f"""你是一个行业研究员。基于以下选题和相关案例,写一段研究发现。
## 选题
标题:{self.topic['title']}
领域:{self.topic.get('field', '')}
核心观点:{self.topic.get('core_concept', '')}
受众痛点:{self.topic.get('audience_pain', '')}
独特视角:{self.topic.get('unique_angle', '')}
{search_section}
## 相关案例({len(cases)}个)
{cases_text}
## 相关案例({len(cases)}个)
{cases_text}
## 要求
- 提炼2-3个真正有价值的洞察,不是每个案例都硬凑一条
- 每条洞察1-2句话,说人话,不要列1/2/3
- 避免「首先其次最后」「综上所述」
- 指出1-2个你不太确定的方向,作为后续研究的提示"""
try:
return call_llm(prompt, temperature=0.5, max_tokens=1200, system_prompt="你是一个行业研究员,擅长从案例中发现真洞察。")
except Exception as e:
logger.warning(f"LLM 研究发现摘要生成失败: {e}")
return ""
def generate_notes(self) -> str:
"""生成研究笔记 Markdown"""
cases = self.find_relevant_cases()
trend_context = get_trend_context(self.topic.get('field'))
search_data = enrich_topic_research(self.topic)
lines = [
f"# 研究笔记:{self.topic['title']}",
f"\n## 选题信息",
@@ -78,6 +113,8 @@ class Researcher:
f"- **核心观点**: {self.topic.get('core_concept', '待补充')}",
f"- **受众痛点**: {self.topic.get('audience_pain', '待补充')}",
f"- **独特视角**: {self.topic.get('unique_angle', '待补充')}",
f"\n{trend_context}",
f"\n{search_data}" if search_data else "",
f"\n## 相关案例({len(cases)}个)\n"
]
for i, case in enumerate(cases, 1):
@@ -89,17 +126,47 @@ class Researcher:
f"- **关键数据**: {case.get('key_metrics', '')}",
""
])
lines.extend([
"## 研究发现摘要",
"- 待补充:从案例中提炼的趋势和洞察",
"- 待补充:数据支撑",
"",
"## 待深入研究的问题",
"- [ ] 需要更多本土数据",
"- [ ] 需要验证某些结论的适用性",
"",
f"*生成时间:{TODAY}*"
])
llm_summary = self._llm_summary(cases)
if llm_summary:
lines.extend([
"## 研究发现摘要(LLM 生成)",
llm_summary,
""
])
else:
insights = []
for case in cases[:3]:
summary = case.get('summary', case.get('description', ''))
metrics = case.get('key_metrics', '')
if summary:
insight = f"- {case.get('title', '相关案例')}{summary[:100]}"
if metrics:
insight += f"{metrics[:80]}"
insights.append(insight)
if not insights:
insights = ["- 暂未匹配到高度相关的历史案例"]
pain_text = self.topic.get('audience_pain', '')
topic_insights = [f"- {pain_text[:100]}"] if pain_text else []
lines.extend([
"## 研究发现摘要",
"",
])
lines.extend(insights)
if topic_insights:
lines.extend(topic_insights)
lines.extend([
"",
"## 待深入研究的问题",
])
if cases:
lines.append("- [ ] 验证以上案例在当前选题背景下的适用性")
lines.extend([
"- [ ] 收集更多本土一手数据",
"- [ ] 确认目标受众的实际反馈",
""
])
lines.append(f"*生成时间:{TODAY}*")
return "\n".join(lines)
def save(self):