From a455753fdc0732b84da08449f0e62d7f19473bc2 Mon Sep 17 00:00:00 2001 From: seaislee1209 Date: Thu, 7 May 2026 18:20:40 +0800 Subject: [PATCH] fix: scheduler now refreshes last 3 months billing Volcengine bill data has 1-2 day delay. Previously the scheduler only refreshed current month, so when the month rolls over the previous month's incomplete snapshot was frozen. Now it refreshes the current month plus 2 prior months. Co-Authored-By: Claude Opus 4.7 (1M context) --- backend/utils/scheduler.py | 58 +++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/backend/utils/scheduler.py b/backend/utils/scheduler.py index 5398ad7..42c7069 100644 --- a/backend/utils/scheduler.py +++ b/backend/utils/scheduler.py @@ -19,7 +19,19 @@ def check_spending(): config = GlobalConfig.get_solo() webhook = config.feishu_webhook_url - bill_period = timezone.now().strftime("%Y-%m") + + # 同时刷新最近 3 个月的账单(火山账单延迟 1-2 天,月初切换时上月账单还在补全) + now = timezone.now() + bill_periods = [] + y, m = now.year, now.month + for _ in range(3): + bill_periods.append(f"{y:04d}-{m:02d}") + m -= 1 + if m == 0: + m = 12 + y -= 1 + current_period = bill_periods[0] + logger.info(f"刷新账单期: {bill_periods}") for volc_account in VolcAccount.objects.filter(is_active=True): ak = decrypt(volc_account.access_key_enc) @@ -31,12 +43,14 @@ def check_spending(): billing = BillingService(ak, sk) iam_svc = IAMService(ak, sk) - # 一次性查询所有项目的消费(避免 N+1 API 调用) - try: - all_project_spending = billing.get_spending_all_projects(bill_period) - except Exception as e: - logger.error(f"批量查询消费失败: {e}") - all_project_spending = {} + # 批量查询最近 3 个月每个项目的消费 + spending_by_period = {} + for period in bill_periods: + try: + spending_by_period[period] = billing.get_spending_all_projects(period) + except Exception as e: + logger.error(f"批量查询 {period} 消费失败: {e}") + spending_by_period[period] = {} users = IAMUser.objects.filter( volc_account=volc_account, @@ -45,7 +59,7 @@ def check_spending(): for user in users: try: - # --- 遍历所有开启监测的项目,从批量结果中获取消费 --- + # --- 遍历所有开启监测的项目,更新最近 3 个月的快照 --- enabled_projects = IAMUserProject.objects.filter( iam_user=user, monitor_enabled=True ) @@ -54,26 +68,24 @@ def check_spending(): logger.info(f"用户 {user.username} 无开启监测的项目,跳过") continue - total_spending = Decimal('0') - for project in enabled_projects: - proj_spending = all_project_spending.get( + # 更新当月项目级消费(用于前端显示) + proj_current = spending_by_period.get(current_period, {}).get( project.project_name, project.current_spending ) - - # 更新项目级消费 - project.current_spending = proj_spending + project.current_spending = proj_current project.save(update_fields=['current_spending']) - # 记录项目级月度快照 - SpendingRecord.objects.update_or_create( - iam_user=user, - project_name=project.project_name, - bill_period=bill_period, - defaults={'amount': proj_spending}, - ) - - total_spending += proj_spending + # 更新最近 3 个月每个月的快照 + for period in bill_periods: + period_data = spending_by_period.get(period, {}) + if project.project_name in period_data: + SpendingRecord.objects.update_or_create( + iam_user=user, + project_name=project.project_name, + bill_period=period, + defaults={'amount': period_data[project.project_name]}, + ) # 更新子账号总消费 # 累计消费 = 所有月份的所有开启监测项目的消费之和