Initial commit
This commit is contained in:
83
billing/management/commands/apply_invoice_policies.py
Normal file
83
billing/management/commands/apply_invoice_policies.py
Normal file
@@ -0,0 +1,83 @@
|
||||
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} 个'))
|
||||
Reference in New Issue
Block a user