361 lines
12 KiB
Python
361 lines
12 KiB
Python
"""导航站路由"""
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy import func as sa_func
|
|
from pydantic import BaseModel
|
|
from typing import Optional
|
|
|
|
from database import get_db
|
|
from models.user import User
|
|
from models.nav_category import NavCategory
|
|
from models.nav_link import NavLink
|
|
from routers.auth import get_current_user, get_admin_user
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
# ========== Schemas ==========
|
|
|
|
class NavCategoryCreate(BaseModel):
|
|
name: str
|
|
icon: str = ""
|
|
|
|
class NavCategoryUpdate(BaseModel):
|
|
name: Optional[str] = None
|
|
icon: Optional[str] = None
|
|
sort_order: Optional[int] = None
|
|
is_active: Optional[bool] = None
|
|
|
|
class NavLinkCreate(BaseModel):
|
|
category_id: int
|
|
name: str
|
|
url: str
|
|
icon: str = ""
|
|
description: str = ""
|
|
|
|
class NavLinkUpdate(BaseModel):
|
|
category_id: Optional[int] = None
|
|
name: Optional[str] = None
|
|
url: Optional[str] = None
|
|
icon: Optional[str] = None
|
|
description: Optional[str] = None
|
|
sort_order: Optional[int] = None
|
|
is_active: Optional[bool] = None
|
|
|
|
class NavLinkSubmit(BaseModel):
|
|
"""用户提交导航链接"""
|
|
category_id: int
|
|
name: str
|
|
url: str
|
|
icon: str = ""
|
|
description: str = ""
|
|
|
|
class NavLinkReview(BaseModel):
|
|
"""审核操作"""
|
|
action: str # approve / reject
|
|
reject_reason: str = ""
|
|
|
|
|
|
# ========== 管理员接口 ==========
|
|
|
|
@router.get("/admin/categories")
|
|
def admin_list_categories(
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_admin_user),
|
|
):
|
|
"""获取所有导航分类(含禁用)"""
|
|
cats = db.query(NavCategory).order_by(NavCategory.sort_order, NavCategory.id).all()
|
|
return [
|
|
{
|
|
"id": c.id, "name": c.name, "icon": c.icon,
|
|
"sort_order": c.sort_order, "is_active": c.is_active,
|
|
"link_count": db.query(sa_func.count(NavLink.id)).filter(NavLink.category_id == c.id).scalar() or 0,
|
|
}
|
|
for c in cats
|
|
]
|
|
|
|
|
|
@router.post("/admin/categories")
|
|
def admin_create_category(
|
|
data: NavCategoryCreate,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_admin_user),
|
|
):
|
|
"""新增导航分类"""
|
|
existing = db.query(NavCategory).filter(NavCategory.name == data.name).first()
|
|
if existing:
|
|
raise HTTPException(status_code=400, detail="分类名称已存在")
|
|
max_order = db.query(sa_func.max(NavCategory.sort_order)).scalar() or 0
|
|
cat = NavCategory(name=data.name, icon=data.icon, sort_order=max_order + 1)
|
|
db.add(cat)
|
|
db.commit()
|
|
db.refresh(cat)
|
|
return {"id": cat.id, "name": cat.name, "icon": cat.icon, "sort_order": cat.sort_order, "is_active": cat.is_active}
|
|
|
|
|
|
@router.put("/admin/categories/{cat_id}")
|
|
def admin_update_category(
|
|
cat_id: int,
|
|
data: NavCategoryUpdate,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_admin_user),
|
|
):
|
|
"""编辑导航分类"""
|
|
cat = db.query(NavCategory).filter(NavCategory.id == cat_id).first()
|
|
if not cat:
|
|
raise HTTPException(status_code=404, detail="分类不存在")
|
|
if data.name is not None:
|
|
dup = db.query(NavCategory).filter(NavCategory.name == data.name, NavCategory.id != cat_id).first()
|
|
if dup:
|
|
raise HTTPException(status_code=400, detail="分类名称已存在")
|
|
cat.name = data.name
|
|
if data.icon is not None:
|
|
cat.icon = data.icon
|
|
if data.sort_order is not None:
|
|
cat.sort_order = data.sort_order
|
|
if data.is_active is not None:
|
|
cat.is_active = data.is_active
|
|
db.commit()
|
|
db.refresh(cat)
|
|
return {"id": cat.id, "name": cat.name, "icon": cat.icon, "sort_order": cat.sort_order, "is_active": cat.is_active}
|
|
|
|
|
|
@router.delete("/admin/categories/{cat_id}")
|
|
def admin_delete_category(
|
|
cat_id: int,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_admin_user),
|
|
):
|
|
"""删除导航分类(级联删除链接)"""
|
|
cat = db.query(NavCategory).filter(NavCategory.id == cat_id).first()
|
|
if not cat:
|
|
raise HTTPException(status_code=404, detail="分类不存在")
|
|
db.query(NavLink).filter(NavLink.category_id == cat_id).delete()
|
|
db.delete(cat)
|
|
db.commit()
|
|
return {"message": "删除成功"}
|
|
|
|
|
|
@router.get("/admin/links")
|
|
def admin_list_links(
|
|
category_id: Optional[int] = None,
|
|
status: Optional[str] = None,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_admin_user),
|
|
):
|
|
"""获取导航链接列表"""
|
|
query = db.query(NavLink)
|
|
if category_id is not None:
|
|
query = query.filter(NavLink.category_id == category_id)
|
|
if status is not None:
|
|
query = query.filter(NavLink.status == status)
|
|
links = query.order_by(NavLink.sort_order, NavLink.id).all()
|
|
return [
|
|
{
|
|
"id": l.id, "category_id": l.category_id, "name": l.name,
|
|
"url": l.url, "icon": l.icon, "description": l.description,
|
|
"sort_order": l.sort_order, "is_active": l.is_active,
|
|
"status": l.status, "submitted_by": l.submitted_by,
|
|
"reject_reason": l.reject_reason or "",
|
|
}
|
|
for l in links
|
|
]
|
|
|
|
|
|
@router.post("/admin/links")
|
|
def admin_create_link(
|
|
data: NavLinkCreate,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_admin_user),
|
|
):
|
|
"""新增导航链接"""
|
|
cat = db.query(NavCategory).filter(NavCategory.id == data.category_id).first()
|
|
if not cat:
|
|
raise HTTPException(status_code=400, detail="分类不存在")
|
|
max_order = db.query(sa_func.max(NavLink.sort_order)).filter(NavLink.category_id == data.category_id).scalar() or 0
|
|
link = NavLink(
|
|
category_id=data.category_id, name=data.name, url=data.url,
|
|
icon=data.icon, description=data.description, sort_order=max_order + 1,
|
|
)
|
|
db.add(link)
|
|
db.commit()
|
|
db.refresh(link)
|
|
return {
|
|
"id": link.id, "category_id": link.category_id, "name": link.name,
|
|
"url": link.url, "icon": link.icon, "description": link.description,
|
|
"sort_order": link.sort_order, "is_active": link.is_active,
|
|
}
|
|
|
|
|
|
@router.put("/admin/links/{link_id}")
|
|
def admin_update_link(
|
|
link_id: int,
|
|
data: NavLinkUpdate,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_admin_user),
|
|
):
|
|
"""编辑导航链接"""
|
|
link = db.query(NavLink).filter(NavLink.id == link_id).first()
|
|
if not link:
|
|
raise HTTPException(status_code=404, detail="链接不存在")
|
|
if data.category_id is not None:
|
|
link.category_id = data.category_id
|
|
if data.name is not None:
|
|
link.name = data.name
|
|
if data.url is not None:
|
|
link.url = data.url
|
|
if data.icon is not None:
|
|
link.icon = data.icon
|
|
if data.description is not None:
|
|
link.description = data.description
|
|
if data.sort_order is not None:
|
|
link.sort_order = data.sort_order
|
|
if data.is_active is not None:
|
|
link.is_active = data.is_active
|
|
db.commit()
|
|
db.refresh(link)
|
|
return {
|
|
"id": link.id, "category_id": link.category_id, "name": link.name,
|
|
"url": link.url, "icon": link.icon, "description": link.description,
|
|
"sort_order": link.sort_order, "is_active": link.is_active,
|
|
}
|
|
|
|
|
|
@router.delete("/admin/links/{link_id}")
|
|
def admin_delete_link(
|
|
link_id: int,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_admin_user),
|
|
):
|
|
"""删除导航链接"""
|
|
link = db.query(NavLink).filter(NavLink.id == link_id).first()
|
|
if not link:
|
|
raise HTTPException(status_code=404, detail="链接不存在")
|
|
db.delete(link)
|
|
db.commit()
|
|
return {"message": "删除成功"}
|
|
|
|
|
|
@router.get("/admin/pending-count")
|
|
def admin_pending_count(
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_admin_user),
|
|
):
|
|
"""获取待审核数量"""
|
|
count = db.query(sa_func.count(NavLink.id)).filter(NavLink.status == "pending").scalar() or 0
|
|
return {"count": count}
|
|
|
|
|
|
@router.put("/admin/links/{link_id}/review")
|
|
def admin_review_link(
|
|
link_id: int,
|
|
data: NavLinkReview,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(get_admin_user),
|
|
):
|
|
"""审核导航链接"""
|
|
link = db.query(NavLink).filter(NavLink.id == link_id).first()
|
|
if not link:
|
|
raise HTTPException(status_code=404, detail="链接不存在")
|
|
if data.action == "approve":
|
|
link.status = "approved"
|
|
link.is_active = True
|
|
link.reject_reason = ""
|
|
elif data.action == "reject":
|
|
link.status = "rejected"
|
|
link.is_active = False
|
|
link.reject_reason = data.reject_reason
|
|
else:
|
|
raise HTTPException(status_code=400, detail="无效操作,请使用 approve 或 reject")
|
|
db.commit()
|
|
db.refresh(link)
|
|
return {
|
|
"id": link.id, "status": link.status,
|
|
"is_active": link.is_active, "reject_reason": link.reject_reason or "",
|
|
}
|
|
|
|
|
|
# ========== 用户提交接口 ==========
|
|
|
|
@router.post("/submit")
|
|
def user_submit_link(
|
|
data: NavLinkSubmit,
|
|
db: Session = Depends(get_db),
|
|
user: User = Depends(get_current_user),
|
|
):
|
|
"""用户提交导航网站(需管理员审核)"""
|
|
cat = db.query(NavCategory).filter(NavCategory.id == data.category_id).first()
|
|
if not cat:
|
|
raise HTTPException(status_code=400, detail="分类不存在")
|
|
existing = db.query(NavLink).filter(NavLink.url == data.url).first()
|
|
if existing:
|
|
raise HTTPException(status_code=400, detail="该网站已被提交过")
|
|
link = NavLink(
|
|
category_id=data.category_id, name=data.name, url=data.url,
|
|
icon=data.icon, description=data.description,
|
|
status="pending", submitted_by=user.id, is_active=False,
|
|
)
|
|
db.add(link)
|
|
db.commit()
|
|
db.refresh(link)
|
|
return {
|
|
"id": link.id, "name": link.name, "url": link.url,
|
|
"status": link.status, "message": "提交成功,等待管理员审核",
|
|
}
|
|
|
|
|
|
@router.get("/my-submissions")
|
|
def user_my_submissions(
|
|
db: Session = Depends(get_db),
|
|
user: User = Depends(get_current_user),
|
|
):
|
|
"""用户查看自己提交的记录"""
|
|
links = db.query(NavLink).filter(NavLink.submitted_by == user.id).order_by(NavLink.id.desc()).all()
|
|
return [
|
|
{
|
|
"id": l.id, "name": l.name, "url": l.url, "icon": l.icon,
|
|
"description": l.description, "status": l.status,
|
|
"reject_reason": l.reject_reason or "",
|
|
"created_at": l.created_at.isoformat() if l.created_at else "",
|
|
}
|
|
for l in links
|
|
]
|
|
|
|
|
|
# ========== 公开接口 ==========
|
|
|
|
@router.get("/public")
|
|
def get_public_nav(
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""获取所有启用分类及其下启用的链接"""
|
|
cats = db.query(NavCategory).filter(NavCategory.is_active == True).order_by(NavCategory.sort_order, NavCategory.id).all()
|
|
result = []
|
|
for c in cats:
|
|
links = (
|
|
db.query(NavLink)
|
|
.filter(NavLink.category_id == c.id, NavLink.is_active == True, NavLink.status == "approved")
|
|
.order_by(NavLink.sort_order, NavLink.id)
|
|
.all()
|
|
)
|
|
if links:
|
|
result.append({
|
|
"id": c.id, "name": c.name, "icon": c.icon,
|
|
"links": [
|
|
{"id": l.id, "name": l.name, "url": l.url, "icon": l.icon, "description": l.description}
|
|
for l in links
|
|
],
|
|
})
|
|
return result
|
|
|
|
|
|
@router.get("/public/categories")
|
|
def get_public_categories(
|
|
db: Session = Depends(get_db),
|
|
current_user: User = Depends(get_current_user),
|
|
):
|
|
"""获取所有启用分类(供用户提交时选择)"""
|
|
cats = db.query(NavCategory).filter(NavCategory.is_active == True).order_by(NavCategory.sort_order, NavCategory.id).all()
|
|
return [{"id": c.id, "name": c.name, "icon": c.icon} for c in cats]
|