feat: Wave 2 - API org isolation + factory.html + insights.html
- Add org_id auto-population to articles.py, publishing.py, metrics.py - Add org_id filtering to search_rankings.py geo endpoints - Add org_id to calendar.py create/update/delete endpoints - Add org_id to tasks.py create endpoints - Create factory.html content pipeline page (3-tab: 待创作/进行中/待审查) - Create insights.html analytics page (4-tab: 概览/搜索排名/GEO/平台对比)
This commit is contained in:
@@ -160,7 +160,8 @@ def update_article_content(
|
||||
if not article:
|
||||
article = Article(
|
||||
id=article_id, topic_id=topic_id, platform=platform,
|
||||
file_path=f"db:{article_id}", status="draft"
|
||||
file_path=f"db:{article_id}", status="draft",
|
||||
org_id=topic.org_id
|
||||
)
|
||||
db.add(article)
|
||||
article.html_content = html_content
|
||||
|
||||
@@ -97,7 +97,7 @@ def create_entry(
|
||||
if current_user.role != "admin" and topic.org_id != current_user.org_id:
|
||||
raise HTTPException(status_code=404, detail="选题不存在")
|
||||
|
||||
entry = ContentCalendar(**data.model_dump())
|
||||
entry = ContentCalendar(**data.model_dump(), org_id=current_user.org_id or "default")
|
||||
db.add(entry)
|
||||
db.commit()
|
||||
db.refresh(entry)
|
||||
@@ -119,6 +119,13 @@ def update_entry(
|
||||
entry = db.query(ContentCalendar).filter(ContentCalendar.id == entry_id).first()
|
||||
if not entry:
|
||||
raise HTTPException(status_code=404, detail="日历条目不存在")
|
||||
# Verify ownership via topic org_id
|
||||
if entry.topic_id:
|
||||
topic_check = db.query(Topic).filter(Topic.id == entry.topic_id).first()
|
||||
if topic_check and current_user.role != "admin" and topic_check.org_id != current_user.org_id:
|
||||
raise HTTPException(status_code=404, detail="日历条目不存在")
|
||||
if not entry.topic_id and current_user.role != "admin" and entry.org_id != current_user.org_id:
|
||||
raise HTTPException(status_code=404, detail="日历条目不存在")
|
||||
|
||||
if entry.topic_id:
|
||||
topic = db.query(Topic).filter(Topic.id == entry.topic_id).first()
|
||||
@@ -156,6 +163,8 @@ def delete_entry(
|
||||
topic = db.query(Topic).filter(Topic.id == entry.topic_id).first()
|
||||
if topic and current_user.role != "admin" and topic.org_id != current_user.org_id:
|
||||
raise HTTPException(status_code=404, detail="日历条目不存在")
|
||||
if not entry.topic_id and current_user.role != "admin" and entry.org_id != current_user.org_id:
|
||||
raise HTTPException(status_code=404, detail="日历条目不存在")
|
||||
db.delete(entry)
|
||||
db.commit()
|
||||
return {"ok": True}
|
||||
@@ -232,7 +241,8 @@ def create_from_topic(
|
||||
title=topic.title,
|
||||
planned_date=planned_date,
|
||||
platform=platform,
|
||||
created_by=current_user.username
|
||||
created_by=current_user.username,
|
||||
org_id=current_user.org_id or "default"
|
||||
)
|
||||
db.add(entry)
|
||||
db.commit()
|
||||
|
||||
@@ -179,7 +179,7 @@ def create_metric(
|
||||
db.refresh(existing)
|
||||
return existing
|
||||
|
||||
metric = ContentMetrics(**data.model_dump(), last_fetched=datetime.now())
|
||||
metric = ContentMetrics(**data.model_dump(), last_fetched=datetime.now(), org_id=topic.org_id)
|
||||
db.add(metric)
|
||||
db.commit()
|
||||
db.refresh(metric)
|
||||
@@ -401,6 +401,7 @@ def fetch_zhihu_metrics(
|
||||
metric = ContentMetrics(
|
||||
topic_id=data.topic_id,
|
||||
platform=platform,
|
||||
org_id=topic.org_id,
|
||||
**metric_data,
|
||||
last_fetched=datetime.now()
|
||||
)
|
||||
|
||||
@@ -69,7 +69,8 @@ async def create_publish_record(
|
||||
action='publish',
|
||||
status='success',
|
||||
operator=operator,
|
||||
description=f"选题 {req.topic_id} 发布到 {PLATFORM_LABELS.get(platform, platform)}"
|
||||
description=f"选题 {req.topic_id} 发布到 {PLATFORM_LABELS.get(platform, platform)}",
|
||||
org_id=topic.org_id
|
||||
)
|
||||
db.add(record)
|
||||
results.append(PublishResult(
|
||||
|
||||
@@ -86,7 +86,10 @@ def get_geo_overview(
|
||||
"""GEO 概览 — AI 搜索引用 + 就绪度评分"""
|
||||
cutoff = datetime.now(timezone.utc) - timedelta(days=days)
|
||||
|
||||
base = db.query(SearchRanking).filter(SearchRanking.checked_at >= cutoff)
|
||||
# Filter geo checks by org via articles
|
||||
base = db.query(SearchRanking)
|
||||
base = _apply_org_filter(base, current_user, db)
|
||||
base = base.filter(SearchRanking.checked_at >= cutoff)
|
||||
total_geo = base.filter(SearchRanking.search_engine == "geo").count()
|
||||
total_cited = base.filter(
|
||||
SearchRanking.search_engine == "geo",
|
||||
@@ -138,7 +141,14 @@ def get_geo_readiness_scores(
|
||||
):
|
||||
"""获取 GEO 就绪度评分列表"""
|
||||
cutoff = datetime.now(timezone.utc) - timedelta(days=days)
|
||||
scores = db.query(GeoReadinessScore).filter(
|
||||
# Join via articles to filter by org
|
||||
scores_query = db.query(GeoReadinessScore).join(
|
||||
ArticleModel, GeoReadinessScore.article_id == ArticleModel.id, isouter=True
|
||||
)
|
||||
of = org_filter(current_user, ArticleModel)
|
||||
if of is not True:
|
||||
scores_query = scores_query.filter(of)
|
||||
scores = scores_query.filter(
|
||||
GeoReadinessScore.checked_at >= cutoff
|
||||
).order_by(desc(GeoReadinessScore.checked_at)).limit(limit).all()
|
||||
return {"ok": True, "data": [s.to_dict() for s in scores]}
|
||||
|
||||
@@ -16,10 +16,13 @@ router = APIRouter(prefix="/api/tasks", tags=["tasks"])
|
||||
|
||||
def _check_task_org(task, current_user, db):
|
||||
"""Check if task's topic belongs to user's org"""
|
||||
if current_user.role == "admin" or not task.topic_id:
|
||||
if current_user.role == "admin":
|
||||
return True
|
||||
topic = db.query(Topic).filter(Topic.id == task.topic_id).first()
|
||||
if topic and topic.org_id != current_user.org_id:
|
||||
if task.topic_id:
|
||||
topic = db.query(Topic).filter(Topic.id == task.topic_id).first()
|
||||
if topic and topic.org_id != current_user.org_id:
|
||||
raise HTTPException(status_code=404, detail="任务不存在")
|
||||
elif current_user.role != "admin" and task.org_id != current_user.org_id:
|
||||
raise HTTPException(status_code=404, detail="任务不存在")
|
||||
return True
|
||||
|
||||
@@ -87,7 +90,8 @@ def create_task(
|
||||
topic_id=data.topic_id,
|
||||
stage=data.stage,
|
||||
status="pending",
|
||||
created_by=data.created_by or current_user.username
|
||||
created_by=data.created_by or current_user.username,
|
||||
org_id=current_user.org_id or "default"
|
||||
)
|
||||
db.add(task)
|
||||
db.commit()
|
||||
@@ -438,7 +442,8 @@ def run_creator_task(
|
||||
stage="creator",
|
||||
status="running",
|
||||
started_at=now,
|
||||
created_by=current_user.username
|
||||
created_by=current_user.username,
|
||||
org_id=current_user.org_id or "default"
|
||||
)
|
||||
db.add(task)
|
||||
db.commit()
|
||||
|
||||
Reference in New Issue
Block a user