Files

78 lines
3.5 KiB
Go

package task
import (
"context"
"github.com/gogf/gf/v2/frame/g"
)
// aggregateUsageSQL 将 bil_usage_logs 在 [startDate, endDate)(半开区间,endDate 不含)
// 内的记录按 租户/项目/模型/渠道/状态 维度聚合,幂等 upsert 到 bil_usage_daily。
// 参数为 YYYY-MM-DD 字符串。ON CONFLICT 使重跑安全(覆盖短暂宕机、历史回填重算)。
const aggregateUsageSQL = `
INSERT INTO bil_usage_daily
(stat_date, tenant_id, project_id, model_name, channel_id, status,
request_count, input_tokens, output_tokens, total_cost, account_cost,
sum_latency_ms, sum_first_token_ms,
cache_creation_tokens, cache_read_tokens, cache_hit_request_count,
updated_at)
SELECT
DATE(created_at) AS stat_date,
tenant_id AS tenant_id,
COALESCE(project_id, 0) AS project_id,
COALESCE(NULLIF(model_name, ''), 'unknown') AS model_name,
COALESCE(channel_id, 0) AS channel_id,
COALESCE(NULLIF(status, ''), 'unknown') AS status,
COUNT(*) AS request_count,
COALESCE(SUM(input_tokens), 0) AS input_tokens,
COALESCE(SUM(output_tokens), 0) AS output_tokens,
COALESCE(SUM(total_cost), 0) AS total_cost,
COALESCE(SUM(account_cost), 0) AS account_cost,
COALESCE(SUM(latency_ms), 0) AS sum_latency_ms,
COALESCE(SUM(first_token_ms), 0) AS sum_first_token_ms,
COALESCE(SUM(cache_creation_tokens), 0) AS cache_creation_tokens,
COALESCE(SUM(cache_read_tokens), 0) AS cache_read_tokens,
COALESCE(SUM(CASE WHEN cache_read_tokens > 0 THEN 1 ELSE 0 END), 0) AS cache_hit_request_count,
now()
FROM bil_usage_logs
WHERE created_at >= $1 AND created_at < $2
GROUP BY
DATE(created_at),
tenant_id,
COALESCE(project_id, 0),
COALESCE(NULLIF(model_name, ''), 'unknown'),
COALESCE(channel_id, 0),
COALESCE(NULLIF(status, ''), 'unknown')
ON CONFLICT (stat_date, tenant_id, project_id, model_name, channel_id, status) DO UPDATE SET
request_count = EXCLUDED.request_count,
input_tokens = EXCLUDED.input_tokens,
output_tokens = EXCLUDED.output_tokens,
total_cost = EXCLUDED.total_cost,
account_cost = EXCLUDED.account_cost,
sum_latency_ms = EXCLUDED.sum_latency_ms,
sum_first_token_ms = EXCLUDED.sum_first_token_ms,
cache_creation_tokens = EXCLUDED.cache_creation_tokens,
cache_read_tokens = EXCLUDED.cache_read_tokens,
cache_hit_request_count = EXCLUDED.cache_hit_request_count,
updated_at = now()
`
// AggregateUsageRange 将 bil_usage_logs 在 [startDate, endDate)(半开区间)的用量
// 聚合并幂等写入 bil_usage_daily。startDate/endDate 为 YYYY-MM-DD。
// 日常增量聚合与历史回填共用此函数:ON CONFLICT 保证可重复执行。
// 注意:endDate 为开区间上界,日常增量传「今天」可避免聚合当天进行中的数据。
func AggregateUsageRange(ctx context.Context, startDate, endDate string) error {
res, err := g.DB().Ctx(ctx).Exec(ctx, aggregateUsageSQL,
startDate+" 00:00:00",
endDate+" 00:00:00",
)
if err != nil {
g.Log().Errorf(ctx, "[Cron] 聚合 bil_usage_daily [%s,%s) 失败: %v", startDate, endDate, err)
return err
}
if affected, e := res.RowsAffected(); e == nil {
g.Log().Infof(ctx, "[Cron] 聚合 bil_usage_daily [%s,%s) 完成,影响 %d 行", startDate, endDate, affected)
}
return nil
}