feat: 平台配置表扩展配图/字数字段,微信公众号正文插入配图,合规检查从DB读规则

- PlatformConfig模型新增requires_image、image_count_min/max、image_width/height、min_words/max_words
- schemas.py同步PlatformConfigBase/Create/Update/Response新字段
- initial_data.py为三大平台填充初始值(微信需配图、字数800-1500等)
- database.py添加新列ALTER TABLE迁移
- platforms.html重写编辑弹窗(正确字段名+配图/字数设置)
- writer.py微信公众号文章正文h1后插入<img>占位
- compliance_checker.py接受platform_config参数,从DB读取规则替代硬编码
- compliance_optimizer.py启动时加载DB平台配置传入checker
This commit is contained in:
Yuzhiran Dev
2026-05-17 23:08:43 +08:00
parent 5674114599
commit 41b0f694ee
9 changed files with 174 additions and 51 deletions
+25 -14
View File
@@ -40,8 +40,19 @@ PLATFORM_RULES = {
class ComplianceChecker:
"""合规审查器"""
def __init__(self):
def __init__(self, platform_config: Dict = None):
self.issues = []
self.platform_config = platform_config or {}
def _get_platform_rule(self, key: str, default=None):
"""从 platform_config 读取规则,fallback 到硬编码 PLATFORM_RULES"""
if self.platform_config:
compliance_rules = self.platform_config.get('compliance_rules', {})
if key in compliance_rules:
return compliance_rules[key]
if key == 'min_word_count' and self.platform_config.get('min_words'):
return self.platform_config['min_words']
return default
def check_text(self, text: str, platform: str, topic_data: Dict = None) -> Dict:
"""执行全面合规检查"""
@@ -94,19 +105,21 @@ class ComplianceChecker:
rules = PLATFORM_RULES.get(platform, {})
# 标题长度(从HTML中提取)
max_title_len = self._get_platform_rule('max_title_len', rules.get("max_title_len"))
title_match = re.search(r'<title>([^<]+)</title>', text) or re.search(r'<h1[^>]*>([^<]+)</h1>', text)
if title_match and rules.get("max_title_len"):
if title_match and max_title_len:
title_len = len(title_match.group(1))
if title_len > rules["max_title_len"]:
if title_len > max_title_len:
self.issues.append({
"type": "平台规则",
"category": "标题长度",
"detail": f"标题{title_len}字,超过{platform}限制{rules['max_title_len']}",
"detail": f"标题{title_len}字,超过{platform}限制{max_title_len}",
"suggestion": "缩短标题"
})
# 禁止的模式匹配
for pattern in rules.get("forbidden_patterns", []):
forbidden_patterns = self._get_platform_rule('forbidden_patterns', rules.get("forbidden_patterns", []))
for pattern in forbidden_patterns:
if re.search(pattern, text):
self.issues.append({
"type": "平台规则",
@@ -123,17 +136,16 @@ class ComplianceChecker:
# 过滤掉纯十六进制颜色码(如 #1a1a1a, #fff
tags = [t for t in tags if not re.fullmatch(r'[0-9a-fA-F]{3,6}', t)]
else:
# 没有标签容器时,不检查标签
tags = []
allowed = rules.get("allowed_tags", [])
if allowed:
allowed_tags = self._get_platform_rule('allowed_tags', rules.get("allowed_tags", []))
if allowed_tags:
for tag in tags:
if tag not in allowed:
if tag not in allowed_tags:
self.issues.append({
"type": "平台规则",
"category": "标签合规",
"tag": tag,
"suggestion": f"使用平台允许的标签,如{', '.join(allowed[:3])}"
"suggestion": f"使用平台允许的标签,如{', '.join(allowed_tags[:3])}"
})
def _check_legal_compliance(self, text: str):
@@ -230,10 +242,9 @@ class ComplianceChecker:
def _check_min_length(self, text: str, platform: str):
"""检查文章最小字数(去除HTML标签)"""
# 简单去除HTML标签
plain = re.sub(r'<[^>]+>', '', text)
word_count = len(plain.strip())
min_words = PLATFORM_RULES.get(platform, {}).get("min_word_count", 1000)
min_words = self._get_platform_rule('min_word_count', PLATFORM_RULES.get(platform, {}).get("min_word_count", 1000))
if word_count < min_words:
self.issues.append({
"type": "内容完整度",
@@ -281,9 +292,9 @@ class ComplianceChecker:
"detail": f"使用过时年份: {', '.join(sorted(outdated))},需更新为2025年及以后的数据",
"suggestion": "替换为最新数据,或使用'近期'等模糊表述"
})
def check_article(html_content: str, platform: str, topic_data: Dict = None) -> Dict:
def check_article(html_content: str, platform: str, topic_data: Dict = None, platform_config: Dict = None) -> Dict:
"""便捷函数:执行完整合规检查"""
checker = ComplianceChecker()
checker = ComplianceChecker(platform_config=platform_config)
return checker.check_text(html_content, platform, topic_data)
if __name__ == "__main__":
+16 -1
View File
@@ -185,6 +185,17 @@ def optimize_article(html: str, platform: str, topic_data: Dict, remaining_issue
logs.append(pol_log)
return html, logs
def _load_platform_configs() -> Dict[str, Dict]:
"""从 DB 加载所有平台配置"""
from app.database import SessionLocal
from app.models import PlatformConfig
db = SessionLocal()
try:
configs = db.query(PlatformConfig).all()
return {c.platform: c.to_dict() for c in configs}
finally:
db.close()
def main(topic_ids: List[str] = None):
logger.info("=== 合规审查与优化开始 ===")
llm_cfg = get_llm_config()
@@ -193,6 +204,9 @@ def main(topic_ids: List[str] = None):
else:
logger.info("LLM 配置: 使用环境变量默认值")
platform_configs = _load_platform_configs()
logger.info(f"已加载 {len(platform_configs)} 个平台配置")
articles = get_articles_from_db(topic_ids)
if not articles:
logger.warning("未找到任何文章(可能尚未创作或同步到 DB)")
@@ -216,7 +230,8 @@ def main(topic_ids: List[str] = None):
logger.warning(f"未找到选题: {topic_id}")
continue
check_result = check_article(html, platform_dir, topic_data)
pc = platform_configs.get(platform_dir, {})
check_result = check_article(html, platform_dir, topic_data, platform_config=pc)
issues = check_result['issues']
score = check_result['score']
label = f"{platform_dir}/{topic_id}"
+11
View File
@@ -351,6 +351,17 @@ 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
if platform == "wechat":
img_tag = '<p><img src="placeholder.jpg" alt="配图" style="width:100%;max-width:1080px;border-radius:8px;"></p>\n'
# Insert after <h1> if present, else prepend
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:]
else:
html_content = img_tag + html_content
html = html.replace("<!-- CONTENT -->", html_content)
tags_html = self._get_platform_tags(platform)