7e953afbd2
- 新增创作工作台 studio.html:合并选题/内容工厂/文章管理为单一 tab 入口(iframe embed 模式) - 新增 AI味检测模块(ai_slop API + 页面,合规软硬问题分级) - 新增白标品牌配置(branding API + 页面 + deploy 私有化交付包) - 发布闭环:publishing 放宽至 editor + records/mark-published 接口 - 移动端响应式补全(admin/calendar/ai-slop 表格卡片兜底) - 修复菜单幂等播种缺陷(按 path 对齐,避免功能页孤立) - 新增短视频脚本 shortvideo.py 与 2026 市场调研简报
73 lines
3.4 KiB
Python
73 lines
3.4 KiB
Python
import subprocess
|
|
from pathlib import Path
|
|
import logging
|
|
import os
|
|
import json
|
|
import time
|
|
import threading
|
|
from datetime import datetime
|
|
from typing import List, Optional, Dict
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[4]
|
|
if os.getenv('PROJECT_ROOT'):
|
|
PROJECT_ROOT = Path(os.getenv('PROJECT_ROOT'))
|
|
|
|
_running_processes: Dict[str, dict] = {}
|
|
_lock = threading.Lock()
|
|
|
|
def _get_cmd(topic_ids: List[str] = None):
|
|
script_path = PROJECT_ROOT / "scripts" / "compliance_optimizer.py"
|
|
if not script_path.exists():
|
|
raise FileNotFoundError(f"Optimizer script not found: {script_path}")
|
|
venv_python = PROJECT_ROOT / "platform" / "backend" / "venv" / "bin" / "python"
|
|
cmd = [str(venv_python), str(script_path)] if venv_python.exists() else ["python3", str(script_path)]
|
|
if topic_ids:
|
|
cmd.extend(["--topic-ids", ','.join(topic_ids)])
|
|
# 不再使用 --today-only,避免三平台串行撰写时小红书文章错过审查窗口
|
|
return cmd
|
|
|
|
def run_optimizer(topic_ids: List[str] = None):
|
|
"""非阻塞:后台启动合规审查脚本,立即返回"""
|
|
with _lock:
|
|
existing = _running_processes.get("optimizer", {})
|
|
proc = existing.get("process")
|
|
if proc and proc.poll() is None:
|
|
raise RuntimeError("已有合规审查任务正在运行,请等待完成后再试")
|
|
cmd = _get_cmd(topic_ids)
|
|
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, cwd=str(PROJECT_ROOT))
|
|
_running_processes["optimizer"] = {"pid": proc.pid, "started_at": time.time(), "process": proc, "topic_ids": topic_ids}
|
|
logger.info("Optimizer started in background (pid=%s, topic_ids=%s)", proc.pid, topic_ids)
|
|
return {"ok": True, "pid": proc.pid, "topic_ids": topic_ids, "_proc": proc}
|
|
|
|
def run_optimizer_blocking(topic_ids: List[str] = None, timeout: int = 600):
|
|
"""阻塞版:带超时,给定时任务使用"""
|
|
cmd = _get_cmd(topic_ids)
|
|
result = subprocess.run(cmd, cwd=str(PROJECT_ROOT), capture_output=True, text=True, timeout=timeout)
|
|
if result.returncode != 0:
|
|
logger.error(f"Optimizer failed: {result.stderr}")
|
|
return {"ok": False, "error": result.stderr}
|
|
report_date = datetime.now().strftime("%Y-%m-%d")
|
|
report_path = PROJECT_ROOT / "automation" / "data" / "drafts" / report_date / "optimization_report.json"
|
|
if report_path.exists():
|
|
report = json.loads(report_path.read_text(encoding='utf-8'))
|
|
return {"ok": True, "report": report}
|
|
else:
|
|
logger.warning(f"Report not found: {report_path}")
|
|
return {"ok": True, "report": None, "stdout": result.stdout}
|
|
|
|
def get_optimizer_status() -> Optional[Dict]:
|
|
info = _running_processes.get("optimizer")
|
|
if not info:
|
|
return None
|
|
proc = info["process"]
|
|
if "finished" in info:
|
|
return {"status": "completed" if info.get("returncode") == 0 else "failed", "pid": info["pid"], "elapsed": round(time.time() - info["started_at"], 1), "returncode": info.get("returncode")}
|
|
if proc.poll() is not None:
|
|
info["returncode"] = proc.returncode
|
|
info["finished"] = True
|
|
elapsed = time.time() - info["started_at"]
|
|
return {"status": "completed" if proc.returncode == 0 else "failed", "pid": info["pid"], "elapsed": round(elapsed, 1), "returncode": proc.returncode}
|
|
return {"status": "running", "pid": info["pid"], "elapsed": round(time.time() - info["started_at"], 1)}
|