Files
ToDoList/api/app/routers/certificates.py
祀梦 4ee1e39454 feat: add certificate management module with image upload
- Add Certificate + CertificateCategory models with full CRUD API
- Image upload via base64 data URL stored in Text column
- Certificate fields: title, issuer, issue_date, expiry_date, image, description
- Frontend: card grid with category sidebar filter, create/edit dialog
- Include certificates in data backup/export
- Fix hasPhaseParent optimization in GoalDetailPage

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 00:25:58 +08:00

168 lines
6.3 KiB
Python

"""证书路由"""
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from typing import Optional, List
from app.database import get_db
from app.models.certificate import Certificate, CertificateCategory
from app.schemas.certificate import (
CertificateCreate, CertificateUpdate,
CertificateListResponse, CertificateDetailResponse,
CertificateCategoryCreate, CertificateCategoryUpdate, CertificateCategoryResponse,
)
from app.schemas.common import DeleteResponse
from app.utils.crud import get_or_404
from app.utils.datetime import utcnow
from app.utils.logger import logger
router = APIRouter(prefix="/api", tags=["证书"])
# ============ 证书分类 API ============
@router.get("/certificate-categories", response_model=List[CertificateCategoryResponse])
def get_categories(db: Session = Depends(get_db)):
try:
categories = db.query(CertificateCategory).order_by(
CertificateCategory.sort_order.asc(),
CertificateCategory.id.asc()
).all()
return categories
except Exception as e:
logger.error(f"获取证书分类列表失败: {str(e)}")
raise HTTPException(status_code=500, detail="获取证书分类列表失败")
@router.post("/certificate-categories", response_model=CertificateCategoryResponse, status_code=201)
def create_category(data: CertificateCategoryCreate, db: Session = Depends(get_db)):
try:
cat = CertificateCategory(**data.model_dump())
db.add(cat)
db.commit()
db.refresh(cat)
logger.info(f"创建证书分类成功: id={cat.id}, name={cat.name}")
return cat
except Exception as e:
db.rollback()
logger.error(f"创建证书分类失败: {str(e)}")
raise HTTPException(status_code=500, detail="创建证书分类失败")
@router.put("/certificate-categories/{category_id}", response_model=CertificateCategoryResponse)
def update_category(category_id: int, data: CertificateCategoryUpdate, db: Session = Depends(get_db)):
try:
cat = get_or_404(db, CertificateCategory, category_id, "证书分类")
update_data = data.model_dump(exclude_unset=True)
for field, value in update_data.items():
setattr(cat, field, value)
db.commit()
db.refresh(cat)
logger.info(f"更新证书分类成功: id={category_id}")
return cat
except HTTPException:
raise
except Exception as e:
db.rollback()
logger.error(f"更新证书分类失败: {str(e)}")
raise HTTPException(status_code=500, detail="更新证书分类失败")
@router.delete("/certificate-categories/{category_id}")
def delete_category(category_id: int, db: Session = Depends(get_db)):
try:
cat = get_or_404(db, CertificateCategory, category_id, "证书分类")
certs = db.query(Certificate).filter(Certificate.category_id == category_id).all()
for c in certs:
c.category_id = None
db.delete(cat)
db.commit()
logger.info(f"删除证书分类成功: id={category_id}")
return DeleteResponse(message="证书分类删除成功")
except HTTPException:
raise
except Exception as e:
db.rollback()
logger.error(f"删除证书分类失败: {str(e)}")
raise HTTPException(status_code=500, detail="删除证书分类失败")
# ============ 证书 API ============
@router.get("/certificates", response_model=List[CertificateListResponse])
def get_certificates(
category_id: Optional[int] = Query(None),
db: Session = Depends(get_db),
):
try:
query = db.query(Certificate)
if category_id is not None:
query = query.filter(Certificate.category_id == category_id)
certs = query.order_by(Certificate.sort_order.asc(), Certificate.created_at.desc()).all()
return certs
except Exception as e:
logger.error(f"获取证书列表失败: {str(e)}")
raise HTTPException(status_code=500, detail="获取证书列表失败")
@router.post("/certificates", response_model=CertificateDetailResponse, status_code=201)
def create_certificate(data: CertificateCreate, db: Session = Depends(get_db)):
try:
cert = Certificate(**data.model_dump())
db.add(cert)
db.commit()
db.refresh(cert)
logger.info(f"创建证书成功: id={cert.id}, title={cert.title}")
return cert
except Exception as e:
db.rollback()
logger.error(f"创建证书失败: {str(e)}")
raise HTTPException(status_code=500, detail="创建证书失败")
@router.get("/certificates/{cert_id}", response_model=CertificateDetailResponse)
def get_certificate(cert_id: int, db: Session = Depends(get_db)):
try:
return get_or_404(db, Certificate, cert_id, "证书")
except HTTPException:
raise
except Exception as e:
logger.error(f"获取证书失败: {str(e)}")
raise HTTPException(status_code=500, detail="获取证书失败")
@router.put("/certificates/{cert_id}", response_model=CertificateDetailResponse)
def update_certificate(cert_id: int, data: CertificateUpdate, db: Session = Depends(get_db)):
try:
cert = get_or_404(db, Certificate, cert_id, "证书")
update_data = data.model_dump(exclude_unset=True)
for field, value in update_data.items():
if value is not None or field in data.clearable_fields:
setattr(cert, field, value)
cert.updated_at = utcnow()
db.commit()
db.refresh(cert)
logger.info(f"更新证书成功: id={cert_id}")
return cert
except HTTPException:
raise
except Exception as e:
db.rollback()
logger.error(f"更新证书失败: {str(e)}")
raise HTTPException(status_code=500, detail="更新证书失败")
@router.delete("/certificates/{cert_id}")
def delete_certificate(cert_id: int, db: Session = Depends(get_db)):
try:
cert = get_or_404(db, Certificate, cert_id, "证书")
db.delete(cert)
db.commit()
logger.info(f"删除证书成功: id={cert_id}")
return DeleteResponse(message="证书删除成功")
except HTTPException:
raise
except Exception as e:
db.rollback()
logger.error(f"删除证书失败: {str(e)}")
raise HTTPException(status_code=500, detail="删除证书失败")