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} 个'))