第一次提交

This commit is contained in:
rjb
2026-01-19 00:09:36 +08:00
parent de4b5059e9
commit 6674060f2f
191 changed files with 40940 additions and 0 deletions

View File

@@ -0,0 +1,277 @@
"""
节点模板API
"""
from fastapi import APIRouter, Depends, HTTPException, status, Query
from sqlalchemy.orm import Session
from sqlalchemy import or_, and_
from pydantic import BaseModel, Field
from typing import List, Optional, Dict, Any
import logging
from app.core.database import get_db
from app.api.auth import get_current_user
from app.models.user import User
from app.models.node_template import NodeTemplate
from app.core.exceptions import NotFoundError, ConflictError, ValidationError
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/v1/node-templates", tags=["node-templates"])
class NodeTemplateCreate(BaseModel):
"""创建节点模板请求模型"""
name: str = Field(..., min_length=1, max_length=100, description="模板名称")
description: Optional[str] = Field(None, description="模板描述")
category: Optional[str] = Field(None, description="分类")
tags: Optional[List[str]] = Field(None, description="标签列表")
prompt: str = Field(..., min_length=1, description="提示词模板")
variables: Optional[List[Dict[str, Any]]] = Field(None, description="变量定义列表")
provider: Optional[str] = Field("deepseek", description="默认LLM提供商")
model: Optional[str] = Field("deepseek-chat", description="默认模型")
temperature: Optional[str] = Field("0.7", description="默认温度参数")
max_tokens: Optional[int] = Field(1500, description="默认最大token数")
is_public: Optional[bool] = Field(False, description="是否公开")
class NodeTemplateUpdate(BaseModel):
"""更新节点模板请求模型"""
name: Optional[str] = Field(None, min_length=1, max_length=100, description="模板名称")
description: Optional[str] = Field(None, description="模板描述")
category: Optional[str] = Field(None, description="分类")
tags: Optional[List[str]] = Field(None, description="标签列表")
prompt: Optional[str] = Field(None, min_length=1, description="提示词模板")
variables: Optional[List[Dict[str, Any]]] = Field(None, description="变量定义列表")
provider: Optional[str] = Field(None, description="默认LLM提供商")
model: Optional[str] = Field(None, description="默认模型")
temperature: Optional[str] = Field(None, description="默认温度参数")
max_tokens: Optional[int] = Field(None, description="默认最大token数")
is_public: Optional[bool] = Field(None, description="是否公开")
class NodeTemplateResponse(BaseModel):
"""节点模板响应模型"""
id: str
name: str
description: Optional[str]
category: Optional[str]
tags: Optional[List[str]]
prompt: str
variables: Optional[List[Dict[str, Any]]]
provider: str
model: str
temperature: str
max_tokens: int
is_public: bool
is_featured: bool
use_count: int
user_id: str
created_at: str
updated_at: str
class Config:
from_attributes = True
@router.get("", response_model=List[NodeTemplateResponse])
async def get_node_templates(
skip: int = Query(0, ge=0, description="跳过记录数"),
limit: int = Query(100, ge=1, le=100, description="每页记录数"),
category: Optional[str] = Query(None, description="分类筛选"),
tag: Optional[str] = Query(None, description="标签筛选"),
search: Optional[str] = Query(None, description="搜索关键词"),
is_public: Optional[bool] = Query(None, description="是否公开"),
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""
获取节点模板列表
用户可以查看:
- 自己创建的所有模板
- 公开的模板
"""
query = db.query(NodeTemplate)
# 权限过滤:只能看到自己的模板或公开的模板
query = query.filter(
or_(
NodeTemplate.user_id == current_user.id,
NodeTemplate.is_public == True
)
)
# 分类筛选
if category:
query = query.filter(NodeTemplate.category == category)
# 标签筛选
if tag:
query = query.filter(NodeTemplate.tags.contains([tag]))
# 搜索
if search:
query = query.filter(
or_(
NodeTemplate.name.like(f"%{search}%"),
NodeTemplate.description.like(f"%{search}%"),
NodeTemplate.prompt.like(f"%{search}%")
)
)
# 公开筛选
if is_public is not None:
query = query.filter(NodeTemplate.is_public == is_public)
# 排序:精选 > 使用次数 > 更新时间
templates = query.order_by(
NodeTemplate.is_featured.desc(),
NodeTemplate.use_count.desc(),
NodeTemplate.updated_at.desc()
).offset(skip).limit(limit).all()
return [template.to_dict() for template in templates]
@router.get("/{template_id}", response_model=NodeTemplateResponse)
async def get_node_template(
template_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""获取节点模板详情"""
template = db.query(NodeTemplate).filter(NodeTemplate.id == template_id).first()
if not template:
raise NotFoundError(f"节点模板不存在: {template_id}")
# 权限检查:只能查看自己的模板或公开的模板
if template.user_id != current_user.id and not template.is_public:
raise HTTPException(status_code=403, detail="无权访问此模板")
return template.to_dict()
@router.post("", response_model=NodeTemplateResponse, status_code=status.HTTP_201_CREATED)
async def create_node_template(
template_data: NodeTemplateCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""创建节点模板"""
# 检查名称是否重复(同一用户)
existing = db.query(NodeTemplate).filter(
NodeTemplate.name == template_data.name,
NodeTemplate.user_id == current_user.id
).first()
if existing:
raise ConflictError(f"模板名称 '{template_data.name}' 已存在")
# 创建模板
template = NodeTemplate(
name=template_data.name,
description=template_data.description,
category=template_data.category,
tags=template_data.tags or [],
prompt=template_data.prompt,
variables=template_data.variables or [],
provider=template_data.provider or "deepseek",
model=template_data.model or "deepseek-chat",
temperature=template_data.temperature or "0.7",
max_tokens=template_data.max_tokens or 1500,
is_public=template_data.is_public or False,
user_id=current_user.id
)
db.add(template)
db.commit()
db.refresh(template)
logger.info(f"用户 {current_user.username} 创建了节点模板: {template.name} ({template.id})")
return template.to_dict()
@router.put("/{template_id}", response_model=NodeTemplateResponse)
async def update_node_template(
template_id: str,
template_data: NodeTemplateUpdate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""更新节点模板"""
template = db.query(NodeTemplate).filter(NodeTemplate.id == template_id).first()
if not template:
raise NotFoundError(f"节点模板不存在: {template_id}")
# 权限检查:只能更新自己的模板
if template.user_id != current_user.id:
raise HTTPException(status_code=403, detail="无权更新此模板")
# 如果更新名称,检查是否重复
if template_data.name and template_data.name != template.name:
existing = db.query(NodeTemplate).filter(
NodeTemplate.name == template_data.name,
NodeTemplate.user_id == current_user.id,
NodeTemplate.id != template_id
).first()
if existing:
raise ConflictError(f"模板名称 '{template_data.name}' 已存在")
# 更新字段
update_data = template_data.dict(exclude_unset=True)
for key, value in update_data.items():
setattr(template, key, value)
db.commit()
db.refresh(template)
logger.info(f"用户 {current_user.username} 更新了节点模板: {template.name} ({template.id})")
return template.to_dict()
@router.delete("/{template_id}", status_code=status.HTTP_200_OK)
async def delete_node_template(
template_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""删除节点模板"""
template = db.query(NodeTemplate).filter(NodeTemplate.id == template_id).first()
if not template:
raise NotFoundError(f"节点模板不存在: {template_id}")
# 权限检查:只能删除自己的模板
if template.user_id != current_user.id:
raise HTTPException(status_code=403, detail="无权删除此模板")
db.delete(template)
db.commit()
logger.info(f"用户 {current_user.username} 删除了节点模板: {template.name} ({template.id})")
return {"message": "节点模板已删除"}
@router.post("/{template_id}/use", status_code=status.HTTP_200_OK)
async def use_node_template(
template_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user)
):
"""使用节点模板(增加使用次数)"""
template = db.query(NodeTemplate).filter(NodeTemplate.id == template_id).first()
if not template:
raise NotFoundError(f"节点模板不存在: {template_id}")
# 权限检查:只能使用自己的模板或公开的模板
if template.user_id != current_user.id and not template.is_public:
raise HTTPException(status_code=403, detail="无权使用此模板")
# 增加使用次数
template.use_count = (template.use_count or 0) + 1
db.commit()
return template.to_dict()