Files
pyGoEdge-UserPanel/billing/management/commands/apply_invoice_policies.py

83 lines
3.6 KiB
Python
Raw Normal View History

2025-11-18 03:36:49 +08:00
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}'))