优化证书生成与正式部署配置
This commit is contained in:
188
backend/app/services/pdf_cleanup.py
Normal file
188
backend/app/services/pdf_cleanup.py
Normal 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
|
||||
Reference in New Issue
Block a user