优化证书生成与正式部署配置

This commit is contained in:
2026-06-15 18:23:46 +08:00
parent f1d7491288
commit 668e0403af
31 changed files with 1603 additions and 145 deletions

View File

@@ -0,0 +1,188 @@
import logging
import subprocess
from datetime import datetime, timedelta
from pathlib import Path
from sqlalchemy import select, update
from sqlalchemy.orm import Session
from app.core.config import settings
from app.core.paths import data_path
from app.db.session import get_db
from app.models import Certificate
logger = logging.getLogger(__name__)
def cleanup_expired_pdfs() -> dict:
"""
清理超过指定天数未访问的PDF文件
Returns:
dict: 清理结果统计
"""
logger.info("[PDF清理] 开始清理过期PDF文件...")
# 使用配置中的清理天数
cleanup_days = settings.pdf_cleanup_days
# 计算截止时间
cutoff_time = datetime.utcnow() - timedelta(days=cleanup_days)
# 统计结果
result = {
"total_checked": 0,
"cleaned_files": 0,
"cleaned_records": 0,
"cleanup_days": cleanup_days,
"errors": [],
}
try:
# 获取数据库会话
db = next(get_db())
# 查询需要清理的证书
# 条件PDF已生成且最后访问时间早于截止时间或从未访问过
stmt = select(Certificate).where(
Certificate.pdf_status == "generated",
Certificate.pdf_file_path.isnot(None),
(
(Certificate.pdf_last_accessed_at < cutoff_time) |
(Certificate.pdf_last_accessed_at.is_(None))
)
)
certificates = db.execute(stmt).scalars().all()
result["total_checked"] = len(certificates)
for cert in certificates:
try:
# 删除PDF文件
if cert.pdf_file_path:
pdf_path = Path(cert.pdf_file_path)
if pdf_path.exists():
subprocess.run(["rm", "-f", str(pdf_path)], check=False)
result["cleaned_files"] += 1
logger.info(f"[PDF清理] 已删除文件: {cert.pdf_file_path}")
# 更新数据库记录
cert.pdf_status = "cleaned"
cert.pdf_file_path = None
cert.pdf_last_accessed_at = None
result["cleaned_records"] += 1
except Exception as e:
error_msg = f"清理证书 {cert.certificate_no} 失败: {str(e)}"
result["errors"].append(error_msg)
logger.error(f"[PDF清理] {error_msg}")
# 提交数据库更改
db.commit()
logger.info(f"[PDF清理] 清理完成: 检查 {result['total_checked']} 个, "
f"删除 {result['cleaned_files']} 个文件, "
f"更新 {result['cleaned_records']} 条记录")
except Exception as e:
logger.error(f"[PDF清理] 清理过程发生错误: {str(e)}")
result["errors"].append(f"清理过程发生错误: {str(e)}")
return result
def get_pdf_cleanup_stats() -> dict:
"""
获取PDF清理统计信息
Returns:
dict: 统计信息
"""
try:
db = next(get_db())
# 使用配置中的清理天数
cleanup_days = settings.pdf_cleanup_days
# 统计总数
total_stmt = select(Certificate).where(Certificate.pdf_status == "generated")
total_count = len(db.execute(total_stmt).scalars().all())
# 统计需要清理的
cutoff_time = datetime.utcnow() - timedelta(days=cleanup_days)
cleanup_stmt = select(Certificate).where(
Certificate.pdf_status == "generated",
Certificate.pdf_file_path.isnot(None),
(
(Certificate.pdf_last_accessed_at < cutoff_time) |
(Certificate.pdf_last_accessed_at.is_(None))
)
)
cleanup_count = len(db.execute(cleanup_stmt).scalars().all())
# 统计PDF文件大小
total_size = 0
pdf_cache_dir = data_path("pdf-cache")
if pdf_cache_dir.exists():
for pdf_file in pdf_cache_dir.rglob("*.pdf"):
total_size += pdf_file.stat().st_size
return {
"total_pdfs": total_count,
"need_cleanup": cleanup_count,
"total_size_mb": round(total_size / (1024 * 1024), 2),
"cleanup_days": cleanup_days,
}
except Exception as e:
logger.error(f"[PDF统计] 获取统计信息失败: {str(e)}")
return {
"total_pdfs": 0,
"need_cleanup": 0,
"total_size_mb": 0,
"cleanup_days": settings.pdf_cleanup_days,
"error": str(e),
}
def manual_cleanup_certificate(certificate_no: str) -> bool:
"""
手动清理指定证书的PDF文件
Args:
certificate_no: 证书编号
Returns:
bool: 是否成功清理
"""
try:
db = next(get_db())
# 查询证书
stmt = select(Certificate).where(Certificate.certificate_no == certificate_no)
cert = db.execute(stmt).scalar_one_or_none()
if not cert:
logger.warning(f"[手动清理] 证书不存在: {certificate_no}")
return False
# 删除PDF文件
if cert.pdf_file_path:
pdf_path = Path(cert.pdf_file_path)
if pdf_path.exists():
subprocess.run(["rm", "-f", str(pdf_path)], check=False)
logger.info(f"[手动清理] 已删除文件: {cert.pdf_file_path}")
# 更新数据库记录
cert.pdf_status = "cleaned"
cert.pdf_file_path = None
cert.pdf_last_accessed_at = None
db.commit()
logger.info(f"[手动清理] 清理完成: {certificate_no}")
return True
except Exception as e:
logger.error(f"[手动清理] 清理失败: {certificate_no}, 错误: {str(e)}")
return False