Files
fa-agent/finance/management/commands/cleanup_formula_exports.py
T
2026-09-26 01:12:10 +08:00

66 lines
2.6 KiB
Python

import time
from datetime import timedelta
from pathlib import Path
from django.conf import settings
from django.core.management.base import BaseCommand
from django.db import transaction
from django.utils import timezone
from finance.models import FormulaExport
class Command(BaseCommand):
help = "Remove expired formula workbooks and stale temporary files"
def add_arguments(self, parser):
parser.add_argument("--loop", action="store_true")
parser.add_argument("--interval", type=int, default=3600)
def handle(self, *args, **options):
while True:
jobs, files = self.clean_expired()
self.stdout.write(f"Expired {jobs} jobs; removed {files} files")
if not options["loop"]:
return
time.sleep(max(60, options["interval"]))
def clean_expired(self):
export_root = Path(settings.MEDIA_ROOT) / "formula-exports"
if export_root.is_symlink():
raise ValueError("公式导出目录不能是符号链接")
root = export_root.resolve()
now = timezone.now()
jobs = files = 0
for job in FormulaExport.objects.filter(status="succeeded", expires_at__lte=now).iterator():
path = Path(job.path) if job.path else None
resolved = path.resolve() if path and path.is_absolute() and not path.is_symlink() else None
in_root = bool(resolved and resolved.is_relative_to(root))
try:
with transaction.atomic():
if in_root and resolved.is_file():
resolved.unlink()
files += 1
job.status, job.path = "expired", ""
job.save(update_fields=["status", "path"])
jobs += 1
except OSError as exc:
self.stderr.write(f"Could not remove expired export {job.pk}: {exc}")
cutoff = (now - timedelta(hours=24)).timestamp()
if root.is_dir() and not root.is_symlink():
for user_dir in root.iterdir():
if user_dir.is_symlink() or not user_dir.is_dir():
continue
for path in user_dir.glob("*.tmp"):
try:
if path.is_symlink() or not path.is_file() or path.stat().st_mtime > cutoff:
continue
path.unlink()
files += 1
except OSError as exc:
self.stderr.write(f"Could not remove stale temporary export {path.name}: {exc}")
return jobs, files