fix: content quality, image format, task monitor, calendar data source, search UI & sort
This commit is contained in:
@@ -321,6 +321,27 @@ def _extract_zhihu_post_id(url: str) -> str:
|
||||
raise ValueError("无法从URL中提取知乎文章/回答ID")
|
||||
|
||||
|
||||
def _fetch_zhihu_page(post_id: str) -> dict:
|
||||
"""多种方式尝试获取知乎文章数据,返回原始 JSON"""
|
||||
UA = _ZHIHU_UA
|
||||
# 方式1:专栏 API
|
||||
urls = [
|
||||
f"https://zhuanlan.zhihu.com/api/posts/{post_id}",
|
||||
f"https://www.zhihu.com/api/v4/posts/{post_id}",
|
||||
f"https://www.zhihu.com/api/v4/answers/{post_id}",
|
||||
]
|
||||
exceptions = []
|
||||
for url in urls:
|
||||
try:
|
||||
resp = http_requests.get(url, headers={"User-Agent": UA}, timeout=10)
|
||||
if resp.status_code == 200:
|
||||
return resp.json()
|
||||
exceptions.append(f"{url} → {resp.status_code}")
|
||||
except Exception as e:
|
||||
exceptions.append(f"{url} → {e}")
|
||||
raise RuntimeError(f"知乎API已封禁,无法获取数据({'; '.join(exceptions)})")
|
||||
|
||||
|
||||
class ZhihuFetchRequest(BaseModel):
|
||||
topic_id: str
|
||||
zhihu_url: str
|
||||
@@ -338,17 +359,9 @@ def fetch_zhihu_metrics(
|
||||
raise HTTPException(status_code=404, detail="选题不存在")
|
||||
|
||||
post_id = _extract_zhihu_post_id(data.zhihu_url)
|
||||
api_url = f"https://zhuanlan.zhihu.com/api/posts/{post_id}"
|
||||
|
||||
try:
|
||||
resp = http_requests.get(api_url, headers={"User-Agent": _ZHIHU_UA}, timeout=15)
|
||||
if resp.status_code == 404:
|
||||
api_url = f"https://www.zhihu.com/api/v4/answers/{post_id}"
|
||||
resp = http_requests.get(api_url, headers={"User-Agent": _ZHIHU_UA}, timeout=15)
|
||||
if resp.status_code != 200:
|
||||
raise HTTPException(status_code=502, detail=f"知乎API返回 {resp.status_code}")
|
||||
|
||||
raw = resp.json()
|
||||
raw = _fetch_zhihu_page(post_id)
|
||||
platform = "zhihu"
|
||||
|
||||
existing = db.query(ContentMetrics).filter(
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"""发布管理 API"""
|
||||
from fastapi import APIRouter, HTTPException, Depends, Request
|
||||
from pydantic import BaseModel
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from typing import Optional, List
|
||||
|
||||
from ..database import get_db
|
||||
@@ -86,8 +86,9 @@ async def create_publish_record(
|
||||
))
|
||||
|
||||
topic.status = '已发布'
|
||||
topic.updated_at = datetime.now()
|
||||
topic.published_at = datetime.now().date()
|
||||
_now = datetime.now(timezone(timedelta(hours=8)))
|
||||
topic.updated_at = _now
|
||||
topic.published_at = _now.date()
|
||||
db.commit()
|
||||
db.expire_all()
|
||||
db.refresh(topic)
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
import logging
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List, Optional
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from ..database import get_db
|
||||
from ..models import SearchProvider
|
||||
from .auth import get_current_user
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/api/search-providers", tags=["search_providers"])
|
||||
|
||||
|
||||
@router.get("")
|
||||
def list_providers(db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
||||
providers = db.query(SearchProvider).order_by(SearchProvider.priority).all()
|
||||
return [p.to_dict() for p in providers]
|
||||
|
||||
|
||||
@router.post("")
|
||||
def create_provider(data: dict, db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
||||
p = SearchProvider(
|
||||
name=data.get("name", ""),
|
||||
provider_type=data.get("provider_type", ""),
|
||||
api_key=data.get("api_key", ""),
|
||||
api_url=data.get("api_url", ""),
|
||||
console_url=data.get("console_url", ""),
|
||||
priority=data.get("priority", 99),
|
||||
enabled=data.get("enabled", True),
|
||||
daily_limit=data.get("daily_limit", 1500),
|
||||
extra_config=data.get("extra_config", {}),
|
||||
)
|
||||
db.add(p)
|
||||
db.commit()
|
||||
db.refresh(p)
|
||||
return p.to_dict()
|
||||
|
||||
|
||||
@router.put("/{provider_id}")
|
||||
def update_provider(provider_id: int, data: dict, db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
||||
p = db.query(SearchProvider).filter(SearchProvider.id == provider_id).first()
|
||||
if not p:
|
||||
raise HTTPException(status_code=404, detail="Provider not found")
|
||||
for key in ("name", "provider_type", "api_key", "api_url", "console_url", "priority", "enabled", "daily_limit"):
|
||||
if key in data:
|
||||
setattr(p, key, data[key])
|
||||
if "extra_config" in data:
|
||||
p.extra_config = data["extra_config"]
|
||||
p.updated_at = datetime.now(timezone.utc)
|
||||
db.commit()
|
||||
db.refresh(p)
|
||||
return p.to_dict()
|
||||
|
||||
|
||||
@router.delete("/{provider_id}")
|
||||
def delete_provider(provider_id: int, db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
||||
p = db.query(SearchProvider).filter(SearchProvider.id == provider_id).first()
|
||||
if not p:
|
||||
raise HTTPException(status_code=404, detail="Provider not found")
|
||||
db.delete(p)
|
||||
db.commit()
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
@router.post("/{provider_id}/test")
|
||||
def test_provider(provider_id: int, data: dict = {}, db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
||||
p = db.query(SearchProvider).filter(SearchProvider.id == provider_id).first()
|
||||
if not p:
|
||||
raise HTTPException(status_code=404, detail="Provider not found")
|
||||
query = data.get("query", "测试搜索")
|
||||
try:
|
||||
test_key = data.get("api_key") or p.api_key
|
||||
test_url = data.get("api_url") or p.api_url
|
||||
if p.provider_type == "baidu":
|
||||
import requests
|
||||
resp = requests.post(
|
||||
test_url,
|
||||
headers={"Authorization": f"Bearer {test_key}", "Content-Type": "application/json"},
|
||||
json={"messages": [{"role": "user", "content": query}], "search_source": "baidu_search_v2", "resource_type_filter": [{"type": "web", "top_k": 3}]},
|
||||
timeout=15
|
||||
)
|
||||
if resp.status_code != 200:
|
||||
return {"ok": False, "error": f"HTTP {resp.status_code}: {resp.text[:200]}"}
|
||||
return {"ok": True, "results": resp.json().get("results", [])[:3]}
|
||||
elif p.provider_type == "qiniu":
|
||||
import requests
|
||||
resp = requests.post(
|
||||
test_url,
|
||||
headers={"Authorization": f"Bearer {test_key}", "Content-Type": "application/json"},
|
||||
json={"query": query, "max_results": 3, "search_type": "web"},
|
||||
timeout=15
|
||||
)
|
||||
if resp.status_code != 200:
|
||||
return {"ok": False, "error": f"HTTP {resp.status_code}: {resp.text[:200]}"}
|
||||
return {"ok": True, "results": resp.json().get("results", resp.json().get("data", []))[:3]}
|
||||
elif p.provider_type == "tinyfish":
|
||||
import requests
|
||||
resp = requests.get(
|
||||
test_url,
|
||||
params={"query": query, "max_results": 3},
|
||||
headers={"X-API-Key": test_key},
|
||||
timeout=15
|
||||
)
|
||||
if resp.status_code != 200:
|
||||
return {"ok": False, "error": f"HTTP {resp.status_code}: {resp.text[:200]}"}
|
||||
return {"ok": True, "results": resp.json().get("results", resp.json().get("data", []))[:3]}
|
||||
elif p.provider_type == "bing":
|
||||
import requests
|
||||
resp = requests.get(
|
||||
test_url,
|
||||
params={"q": query, "count": 3, "mkt": "zh-CN"},
|
||||
headers={"Ocp-Apim-Subscription-Key": test_key},
|
||||
timeout=15
|
||||
)
|
||||
if resp.status_code != 200:
|
||||
return {"ok": False, "error": f"HTTP {resp.status_code}: {resp.text[:200]}"}
|
||||
return {"ok": True, "results": resp.json().get("webPages", {}).get("value", [])[:3]}
|
||||
elif p.provider_type == "mcp":
|
||||
import subprocess, json as _json
|
||||
mcp_script = Path(__file__).resolve().parent.parent.parent.parent.parent / "scripts" / "mcp_search_server.py"
|
||||
r = subprocess.run(
|
||||
[sys.executable, str(mcp_script), "--query", query],
|
||||
capture_output=True, text=True, timeout=90,
|
||||
)
|
||||
if r.returncode != 0:
|
||||
return {"ok": False, "error": f"子进程失败: {r.stderr[:200]}"}
|
||||
return {"ok": True, "results": _json.loads(r.stdout)[:3]}
|
||||
return {"ok": False, "error": f"Unknown provider_type: {p.provider_type}"}
|
||||
except Exception as e:
|
||||
return {"ok": False, "error": str(e)}
|
||||
|
||||
|
||||
@router.post("/reset-usage")
|
||||
def reset_usage(db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
||||
db.query(SearchProvider).update({SearchProvider.usage_today: 0})
|
||||
db.commit()
|
||||
return {"ok": True}
|
||||
@@ -3,16 +3,17 @@ import subprocess
|
||||
from fastapi import APIRouter, HTTPException, Depends, Body
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func
|
||||
from datetime import datetime, date
|
||||
from datetime import datetime, date, timezone, timedelta
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, List, Optional
|
||||
import os
|
||||
import os
|
||||
import json
|
||||
from ..database import get_db
|
||||
from ..models import Topic, Article, TaskConfig, TaskLog
|
||||
from ..core.generator import run_creator, get_generator_status
|
||||
from ..core.optimizer import run_optimizer, get_optimizer_status
|
||||
from ..core.collector import run_collector, get_collector_status
|
||||
from ..core.generator import run_creator, get_generator_status, _running_processes as _generator_running
|
||||
from ..core.optimizer import run_optimizer, get_optimizer_status, _running_processes as _optimizer_running
|
||||
from ..core.collector import run_collector, get_collector_status, _running_processes as _collector_running
|
||||
import threading
|
||||
from ..core.sync import sync_all_topics
|
||||
from ..core.scheduler import scheduler
|
||||
@@ -26,6 +27,32 @@ LOGS_DIR = PROJECT_ROOT / "automation" / "logs"
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/api/system", tags=["system"])
|
||||
|
||||
_active_monitors: Dict[int, dict] = {}
|
||||
|
||||
def _monitor_subprocess(log_id: int, proc, module_id: str, task_name: str, db_session_factory):
|
||||
"""阻塞等待子进程退出(最长 1800s),完成后更新 task_logs"""
|
||||
try:
|
||||
returncode = proc.wait(timeout=1800)
|
||||
except subprocess.TimeoutExpired:
|
||||
proc.kill()
|
||||
returncode = -1
|
||||
logger.warning("Subprocess %s (pid=%s) killed after 1800s timeout", module_id, proc.pid)
|
||||
finished_at = datetime.now(timezone.utc)
|
||||
try:
|
||||
db = db_session_factory()
|
||||
log = db.query(TaskLog).filter(TaskLog.id == log_id).first()
|
||||
if log:
|
||||
log.status = "success" if returncode == 0 else "failed"
|
||||
log.finished_at = finished_at
|
||||
if log.started_at:
|
||||
log.duration = int((finished_at - log.started_at).total_seconds())
|
||||
db.commit()
|
||||
db.close()
|
||||
except Exception as e:
|
||||
logger.warning("Failed to update task log %s: %s", log_id, e)
|
||||
finally:
|
||||
_active_monitors.pop(log_id, None)
|
||||
|
||||
def _aggregate_status_counts(q):
|
||||
"""聚合状态计数,兼容中英文状态值"""
|
||||
raw = q.with_entities(Topic.status, func.count()).group_by(Topic.status).all()
|
||||
@@ -60,12 +87,22 @@ def get_status(db: Session = Depends(get_db)):
|
||||
}
|
||||
}
|
||||
|
||||
@router.post("/generate/run", dependencies=[Depends(get_current_user)])
|
||||
@router.post("/generate/run")
|
||||
def trigger_generation(topic_id: Optional[str] = None, db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
||||
logger.info(f"Generation triggered by {current_user.username}, topic_id={topic_id}")
|
||||
try:
|
||||
result = run_creator(topic_id)
|
||||
return {"message": "内容创作已后台启动", "pid": result.get("pid")}
|
||||
from ..database import SessionLocal
|
||||
log = TaskLog(module_id="scheduled_generate", task_name="🤖 内容创作", status="running", message="内容创作已启动", triggered_by="manual", started_at=datetime.now(timezone.utc))
|
||||
db.add(log)
|
||||
db.commit()
|
||||
log_id = log.id
|
||||
proc_info = result.get("proc") or result
|
||||
proc = _generator_running.get("generator", {}).get("process") if "pid" in result else None
|
||||
if proc:
|
||||
t = threading.Thread(target=_monitor_subprocess, args=(log_id, proc, "scheduled_generate", "🤖 内容创作", SessionLocal), daemon=True)
|
||||
t.start()
|
||||
return {"message": "内容创作已后台启动", "pid": result.get("pid"), "log_id": log_id}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@@ -76,12 +113,21 @@ def generation_status():
|
||||
return {"status": "idle", "message": "当前无运行中的创作任务"}
|
||||
return status
|
||||
|
||||
@router.post("/collect/run", dependencies=[Depends(get_current_user)])
|
||||
@router.post("/collect/run")
|
||||
def trigger_collection(db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
||||
logger.info(f"Manual collection triggered by {current_user.username}")
|
||||
try:
|
||||
result = run_collector()
|
||||
return {"message": "内容采集已后台启动", "result": result}
|
||||
from ..database import SessionLocal
|
||||
log = TaskLog(module_id="scheduled_collect", task_name="📡 内容采集", status="running", message="内容采集已启动", triggered_by="manual", started_at=datetime.now(timezone.utc))
|
||||
db.add(log)
|
||||
db.commit()
|
||||
log_id = log.id
|
||||
proc = _collector_running.get("collector", {}).get("process") if "pid" in result else None
|
||||
if proc:
|
||||
t = threading.Thread(target=_monitor_subprocess, args=(log_id, proc, "scheduled_collect", "📡 内容采集", SessionLocal), daemon=True)
|
||||
t.start()
|
||||
return {"message": "内容采集已后台启动", "result": result, "log_id": log_id}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@@ -92,11 +138,20 @@ def collection_status():
|
||||
return {"status": "idle", "message": "当前无运行中的采集任务"}
|
||||
return status
|
||||
|
||||
@router.post("/review/run", dependencies=[Depends(get_current_user)])
|
||||
@router.post("/review/run")
|
||||
def trigger_review(topic_ids: Optional[List[str]] = None, db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
||||
try:
|
||||
result = run_optimizer(topic_ids)
|
||||
return {"message": "合规审查已后台启动", "pid": result.get("pid")}
|
||||
from ..database import SessionLocal
|
||||
log = TaskLog(module_id="scheduled_optimize", task_name="🔍 合规审查", status="running", message="合规审查已启动", triggered_by="manual", started_at=datetime.now(timezone.utc))
|
||||
db.add(log)
|
||||
db.commit()
|
||||
log_id = log.id
|
||||
proc = _optimizer_running.get("optimizer", {}).get("process") if "pid" in result else None
|
||||
if proc:
|
||||
t = threading.Thread(target=_monitor_subprocess, args=(log_id, proc, "scheduled_optimize", "🔍 合规审查", SessionLocal), daemon=True)
|
||||
t.start()
|
||||
return {"message": "合规审查已后台启动", "pid": result.get("pid"), "log_id": log_id}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@@ -177,17 +232,24 @@ def trigger_metrics_sync():
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@router.post("/refresh-search-cache/run")
|
||||
def trigger_refresh_search_cache():
|
||||
def trigger_refresh_search_cache(db: Session = Depends(get_db), current_user=Depends(get_current_user)):
|
||||
try:
|
||||
import sys as sys_mod
|
||||
scripts_dir = Path(__file__).parent.parent.parent.parent / "scripts"
|
||||
from ..database import SessionLocal as _ss
|
||||
proc = subprocess.Popen(
|
||||
[sys_mod.executable, str(scripts_dir / "opencode_search.py"), "--refresh-cache"],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
|
||||
cwd=scripts_dir.parent.parent
|
||||
)
|
||||
logger.info("Search cache refresh started (pid=%s)", proc.pid)
|
||||
return {"message": "搜索缓存刷新已后台启动", "pid": proc.pid}
|
||||
log = TaskLog(module_id="scheduled_refresh_search_cache", task_name="🔍 搜索缓存", status="running", message="搜索缓存刷新已启动", triggered_by="manual", started_at=datetime.now(timezone.utc), result_data={"pid": proc.pid})
|
||||
db.add(log)
|
||||
db.commit()
|
||||
log_id = log.id
|
||||
t = threading.Thread(target=_monitor_subprocess, args=(log_id, proc, "scheduled_refresh_search_cache", "🔍 搜索缓存", _ss), daemon=True)
|
||||
t.start()
|
||||
return {"message": "搜索缓存刷新已后台启动", "pid": proc.pid, "log_id": log_id}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
@@ -259,6 +321,7 @@ def get_modules_status(db: Session = Depends(get_db)):
|
||||
"scheduled_optimize": {"name": "🔍 合规审查", "cron": "03:00", "params_desc": {"auto_pass_threshold": "自动通过分数阈值"}},
|
||||
"scheduled_optimize_sources": {"name": "📡 信息源优化", "cron": "05:00", "params_desc": {}},
|
||||
"scheduled_metrics_sync": {"name": "📊 指标同步", "cron": "06:00", "params_desc": {}},
|
||||
"scheduled_task_monitor": {"name": "⏰ 任务监控", "cron": "*", "params_desc": {}},
|
||||
}
|
||||
|
||||
modules = []
|
||||
|
||||
@@ -19,6 +19,7 @@ MODULES = {
|
||||
"scheduled_optimize": "🔍 合规审查",
|
||||
"scheduled_optimize_sources": "📡 信息源优化",
|
||||
"scheduled_metrics_sync": "📊 指标同步",
|
||||
"scheduled_task_monitor": "⏰ 任务监控",
|
||||
}
|
||||
|
||||
@router.get("", response_model=List[TaskLogResponse])
|
||||
@@ -68,6 +69,29 @@ def list_modules(db: Session = Depends(get_db), admin_user=Depends(get_current_a
|
||||
})
|
||||
return result
|
||||
|
||||
|
||||
@router.get("/log-types")
|
||||
def list_log_types(db: Session = Depends(get_db), admin_user=Depends(get_current_admin)):
|
||||
used = db.query(TaskLog.module_id).distinct().all()
|
||||
used_ids = [r[0] for r in used]
|
||||
result = []
|
||||
for mid, name in MODULES.items():
|
||||
if mid in used_ids or True:
|
||||
log_file_map = {
|
||||
"scheduled_refresh_search_cache": "opencode_search",
|
||||
"scheduled_fetch_trends": "trends",
|
||||
"scheduled_collect": "collector",
|
||||
"scheduled_generate": "creator",
|
||||
"scheduled_optimize": "optimizer",
|
||||
"scheduled_optimize_sources": "collector",
|
||||
"scheduled_metrics_sync": "sync",
|
||||
}
|
||||
result.append({"module_id": mid, "name": name, "log_file": log_file_map.get(mid, mid)})
|
||||
for mid in used_ids:
|
||||
if mid not in MODULES:
|
||||
result.append({"module_id": mid, "name": mid, "log_file": mid})
|
||||
return result
|
||||
|
||||
@router.get("/{log_id}", response_model=TaskLogResponse)
|
||||
def get_task_log(log_id: int, db: Session = Depends(get_db), admin_user=Depends(get_current_admin)):
|
||||
log = db.query(TaskLog).filter(TaskLog.id == log_id).first()
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import uuid
|
||||
import threading
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import List, Optional
|
||||
@@ -8,6 +9,8 @@ from ..models import ContentTask, Topic
|
||||
from ..schemas import ContentTaskCreate, ContentTaskResponse
|
||||
from .auth import get_current_user, org_filter
|
||||
|
||||
_creator_semaphore = threading.Semaphore(3)
|
||||
|
||||
router = APIRouter(prefix="/api/tasks", tags=["tasks"])
|
||||
|
||||
|
||||
@@ -240,6 +243,7 @@ def _get_module_detail_data(module_id: str, db, ROOT, DATA_DIR, LOGS_DIR, today_
|
||||
"scheduled_optimize": {"name": "🔍 合规审查", "description": "LLM 审查已创作文章,检查合规、打分数、优化建议"},
|
||||
"scheduled_optimize_sources": {"name": "📡 信息源优化", "description": "AI 分析当前类别和信息源的市场匹配度,给出调整建议"},
|
||||
"scheduled_metrics_sync": {"name": "📊 指标同步", "description": "从各平台公开 API 获取已发布文章的互动数据(点赞、阅读、评论等)"},
|
||||
"scheduled_task_monitor": {"name": "⏰ 任务监控", "description": "每小时自动检查卡死/中断任务,标记为失败以便重新执行"},
|
||||
}
|
||||
|
||||
meta = MODULE_META.get(module_id, {"name": module_id, "description": ""})
|
||||
@@ -430,7 +434,6 @@ def run_creator_task(
|
||||
db: Session = Depends(get_db),
|
||||
current_user=Depends(get_current_user)
|
||||
):
|
||||
import threading
|
||||
from datetime import datetime, timezone
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
@@ -448,44 +451,51 @@ def run_creator_task(
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
|
||||
from ..core.generator import run_creator
|
||||
|
||||
def _run():
|
||||
from ..database import SessionLocal
|
||||
from ..core.generator import run_creator_blocking
|
||||
from datetime import datetime, timezone
|
||||
new_db = SessionLocal()
|
||||
import traceback
|
||||
_creator_semaphore.acquire()
|
||||
try:
|
||||
new_task = new_db.query(ContentTask).filter(ContentTask.task_id == task_id).first()
|
||||
if new_task:
|
||||
new_task.message = "创作脚本运行中..."
|
||||
new_task.progress = 30
|
||||
new_db.commit()
|
||||
result = run_creator(topic_id)
|
||||
new_task = new_db.query(ContentTask).filter(ContentTask.task_id == task_id).first()
|
||||
if new_task:
|
||||
finished = datetime.now(timezone.utc)
|
||||
new_task.status = "completed"
|
||||
new_task.finished_at = finished
|
||||
new_task.progress = 100
|
||||
new_task.message = "创作完成"
|
||||
new_task.result_data = result or {}
|
||||
if new_task.started_at:
|
||||
new_task.duration = int((finished - new_task.started_at).total_seconds())
|
||||
new_db.commit()
|
||||
except Exception as e:
|
||||
new_task = new_db.query(ContentTask).filter(ContentTask.task_id == task_id).first()
|
||||
if new_task:
|
||||
finished = datetime.now(timezone.utc)
|
||||
new_task.status = "failed"
|
||||
new_task.finished_at = finished
|
||||
new_task.error_msg = str(e)
|
||||
if new_task.started_at:
|
||||
new_task.duration = int((finished - new_task.started_at).total_seconds())
|
||||
new_db.commit()
|
||||
finally:
|
||||
new_db.close()
|
||||
new_db = SessionLocal()
|
||||
try:
|
||||
new_task = new_db.query(ContentTask).filter(ContentTask.task_id == task_id).first()
|
||||
if new_task:
|
||||
new_task.message = "创作脚本运行中..."
|
||||
new_task.progress = 30
|
||||
new_db.commit()
|
||||
|
||||
thread = threading.Thread(target=_run)
|
||||
result = run_creator_blocking(topic_id)
|
||||
|
||||
new_task = new_db.query(ContentTask).filter(ContentTask.task_id == task_id).first()
|
||||
if new_task:
|
||||
finished = datetime.now(timezone.utc)
|
||||
new_task.status = "completed"
|
||||
new_task.finished_at = finished
|
||||
new_task.progress = 100
|
||||
new_task.message = "创作完成"
|
||||
new_task.result_data = {"stdout": (result or {}).get("stdout", "")[:2000]} if isinstance(result, dict) else {"raw": str(result)[:2000]}
|
||||
if new_task.started_at:
|
||||
new_task.duration = int((finished - new_task.started_at).total_seconds())
|
||||
new_db.commit()
|
||||
except Exception as e:
|
||||
new_db.rollback()
|
||||
new_task = new_db.query(ContentTask).filter(ContentTask.task_id == task_id).first()
|
||||
if new_task:
|
||||
finished = datetime.now(timezone.utc)
|
||||
new_task.status = "failed"
|
||||
new_task.finished_at = finished
|
||||
new_task.error_msg = f"{type(e).__name__}: {e}\n{traceback.format_exc()}"
|
||||
if new_task.started_at:
|
||||
new_task.duration = int((finished - new_task.started_at).total_seconds())
|
||||
new_db.commit()
|
||||
finally:
|
||||
new_db.close()
|
||||
finally:
|
||||
_creator_semaphore.release()
|
||||
|
||||
thread = threading.Thread(target=_run, daemon=True)
|
||||
thread.start()
|
||||
|
||||
return task
|
||||
Reference in New Issue
Block a user