fix: scheduler now refreshes last 3 months billing
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m35s

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) <noreply@anthropic.com>
This commit is contained in:
seaislee1209 2026-05-07 18:20:40 +08:00
parent d0a97c3dbe
commit a455753fdc

View File

@ -19,7 +19,19 @@ def check_spending():
config = GlobalConfig.get_solo() config = GlobalConfig.get_solo()
webhook = config.feishu_webhook_url 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): for volc_account in VolcAccount.objects.filter(is_active=True):
ak = decrypt(volc_account.access_key_enc) ak = decrypt(volc_account.access_key_enc)
@ -31,12 +43,14 @@ def check_spending():
billing = BillingService(ak, sk) billing = BillingService(ak, sk)
iam_svc = IAMService(ak, sk) iam_svc = IAMService(ak, sk)
# 一次性查询所有项目的消费(避免 N+1 API 调用) # 批量查询最近 3 个月每个项目的消费
try: spending_by_period = {}
all_project_spending = billing.get_spending_all_projects(bill_period) for period in bill_periods:
except Exception as e: try:
logger.error(f"批量查询消费失败: {e}") spending_by_period[period] = billing.get_spending_all_projects(period)
all_project_spending = {} except Exception as e:
logger.error(f"批量查询 {period} 消费失败: {e}")
spending_by_period[period] = {}
users = IAMUser.objects.filter( users = IAMUser.objects.filter(
volc_account=volc_account, volc_account=volc_account,
@ -45,7 +59,7 @@ def check_spending():
for user in users: for user in users:
try: try:
# --- 遍历所有开启监测的项目,从批量结果中获取消费 --- # --- 遍历所有开启监测的项目,更新最近 3 个月的快照 ---
enabled_projects = IAMUserProject.objects.filter( enabled_projects = IAMUserProject.objects.filter(
iam_user=user, monitor_enabled=True iam_user=user, monitor_enabled=True
) )
@ -54,26 +68,24 @@ def check_spending():
logger.info(f"用户 {user.username} 无开启监测的项目,跳过") logger.info(f"用户 {user.username} 无开启监测的项目,跳过")
continue continue
total_spending = Decimal('0')
for project in enabled_projects: 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.project_name, project.current_spending
) )
project.current_spending = proj_current
# 更新项目级消费
project.current_spending = proj_spending
project.save(update_fields=['current_spending']) project.save(update_fields=['current_spending'])
# 记录项目级月度快照 # 更新最近 3 个月每个月的快照
SpendingRecord.objects.update_or_create( for period in bill_periods:
iam_user=user, period_data = spending_by_period.get(period, {})
project_name=project.project_name, if project.project_name in period_data:
bill_period=bill_period, SpendingRecord.objects.update_or_create(
defaults={'amount': proj_spending}, iam_user=user,
) project_name=project.project_name,
bill_period=period,
total_spending += proj_spending defaults={'amount': period_data[project.project_name]},
)
# 更新子账号总消费 # 更新子账号总消费
# 累计消费 = 所有月份的所有开启监测项目的消费之和 # 累计消费 = 所有月份的所有开启监测项目的消费之和