Files
pyGoEdge-UserPanel/billing/management/commands/apply_invoice_policies.py
2025-11-18 03:36:49 +08:00

83 lines
3.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from django.core.management.base import BaseCommand
from django.utils import timezone
from django.db import transaction
from billing.models import Invoice
from core.models import SystemSettings, OperationLog
from core.goedge_client import GoEdgeClient
class Command(BaseCommand):
help = '对逾期未支付账单执行未支付策略(停服或限速)。支持 dry-run 与用户/时间过滤。'
def add_arguments(self, parser):
parser.add_argument('--days-overdue', type=int, default=0, help='逾期天数阈值默认0表示只要未支付即处理')
parser.add_argument('--user-id', type=int, default=0, help='仅处理指定用户(可选)')
parser.add_argument('--dry-run', action='store_true', default=False, help='试运行,不落库与不调用接口')
@transaction.atomic
def handle(self, *args, **options):
days_overdue = int(options.get('days_overdue') or 0)
user_id = int(options.get('user_id') or 0)
dry = bool(options.get('dry_run'))
today = timezone.now().date()
qs = Invoice.objects.filter(status=Invoice.STATUS_UNPAID)
if user_id:
qs = qs.filter(user_id=user_id)
if days_overdue > 0:
# 以账单周期结束日为逾期基准
cutoff = today - timezone.timedelta(days=days_overdue)
qs = qs.filter(period_end__lte=cutoff)
invoices = list(qs)
if not invoices:
self.stdout.write(self.style.NOTICE('无符合条件的未支付账单'))
return
sys = SystemSettings.objects.order_by('id').first()
policy = getattr(sys, 'default_overage_policy', {}) or {}
action_type = (policy.get('action') or '').lower()
limit_bps = int(policy.get('limit_bps') or 0)
client = None
if not dry:
client = GoEdgeClient()
total_domains = 0
for inv in invoices:
applied = []
items = inv.items.select_related('domain').all()
for it in items:
d = it.domain
if not d or not d.edge_server_id:
continue
total_domains += 1
if dry:
applied.append({'domain': d.name, 'action': action_type or 'shutdown', 'limit_bps': limit_bps})
continue
web_id = client.find_server_web_id(int(d.edge_server_id))
if not web_id:
continue
if action_type == 'limit' and limit_bps > 0:
client.update_http_web_request_limit(web_id, {'isOn': True, 'rateBytes': int(limit_bps)})
applied.append({'domain': d.name, 'action': 'limit', 'rateBytes': limit_bps})
else:
client.update_http_web_shutdown(web_id, {'isOn': True})
if d.status != 'suspended':
d.status = 'suspended'
d.save(update_fields=['status', 'updated_at'])
applied.append({'domain': d.name, 'action': 'shutdown'})
if applied:
try:
OperationLog.objects.create(
actor=None,
action='apply_overage_policy_cron',
target=f"Invoice#{inv.id}",
detail=str(applied)
)
except Exception:
pass
self.stdout.write(self.style.SUCCESS(f"Invoice#{inv.id} 应用策略:{len(applied)} 个域名"))
self.stdout.write(self.style.SUCCESS(f'完成:处理账单 {len(invoices)} 个,涉及域名 {total_domains}'))