"""知识库文章 API — P1-3 嵌入功能模块用 提供按关联页面查询知识文章的功能。 """ from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from sqlalchemy import or_ from typing import Optional from app.database import get_db from app.auth_middleware import require_role from app.deps import get_entity_id from app.models.knowledge_article import KnowledgeArticle router = APIRouter(prefix="/api/cma/knowledge-articles", tags=["知识库嵌入"], dependencies=[Depends(require_role("ceo", "finance", "business", "it"))], ) def article_to_dict(a: KnowledgeArticle) -> dict: return { "id": a.id, "entity_id": a.entity_id, "title": a.title, "summary": a.summary, "content": a.content, "category": a.category, "icon": a.icon, "related_page": a.related_page, "sort_order": a.sort_order, } @router.get("") def list_articles( related_page: Optional[str] = Query(None, description="按关联页面路由筛选"), category: Optional[str] = Query(None), db: Session = Depends(get_db), entity_id: int = Depends(get_entity_id), ): """查询知识文章(本企业专属 ∪ 全局共享),可按关联页面或分类筛选 隔离语义(2026-09-03 多租户分区): - entity_id IS NULL → 全局共享(CMA 系统教学知识,所有企业可见) - entity_id == 当前账套 → 企业专属(只本企业可见) - 其他企业的专属文章不可见 """ q = db.query(KnowledgeArticle).filter( or_( KnowledgeArticle.entity_id == entity_id, KnowledgeArticle.entity_id.is_(None), ) ) if related_page: q = q.filter(KnowledgeArticle.related_page.contains(related_page)) if category: q = q.filter(KnowledgeArticle.category == category) articles = q.order_by(KnowledgeArticle.sort_order.asc(), KnowledgeArticle.id.asc()).all() return {"data": [article_to_dict(a) for a in articles]} @router.get("/{article_id}") def get_article( article_id: int, db: Session = Depends(get_db), entity_id: int = Depends(get_entity_id), ): """获取文章详情(企业专属文章仅本企业可见,防跨企业探测)""" a = ( db.query(KnowledgeArticle) .filter(KnowledgeArticle.id == article_id) .first() ) if not a: raise HTTPException(404, "文章不存在") if a.entity_id is not None and a.entity_id != entity_id: raise HTTPException(404, "文章不存在") return article_to_dict(a)