Files
sub2api/backend/migrations/172_video_per_second_billing_metadata.sql
T
shaw d4952154ff fix: bill Grok video per second and harden video usage logging
Follow-up fixes for the #3775 audit findings:

- Bill Grok video generation per second of output, matching the xAI rate
  card: parse the request duration (1-15s, upstream default 8s) and compute
  cost as per-second price x duration x count. The built-in rate card values
  were already xAI per-second prices but were previously charged per video,
  undercharging up to 15x with a user-controlled duration.
- Group video_price_* fields are now documented and surfaced as per-second
  rates (USD/s); admin UI labels, placeholders and hints updated accordingly.
- Persist video_count/video_resolution/video_duration_seconds on usage_logs
  (migration 172) so video billing is auditable, and exempt any row with
  video_count > 0 from the image_size check constraint: a video billed via a
  token-mode channel price produces billing_mode='token' with image_count=1
  and no image_size, which the previous constraint rejected, dropping the
  whole billing transaction.
- Only refetch the group in apiKeyWithFreshGroupMediaPricing when the group
  object actually looks like it is missing media pricing fields (both media
  multipliers zero and all prices nil, impossible for a normally loaded
  group), removing a per-usage DB query for groups without overrides.
- Frontend: drop the unused admin.groups.mediaPricing locale block, map
  cleared price inputs to null (create) / -1 (update, cleared via backend
  normalizePrice) instead of sending "" that failed *float64 unmarshalling,
  and align video price placeholders with the text-to-video default model
  (grok-imagine-video 0.05/0.07, 1080p only on 1.5 at 0.25).
2026-07-09 15:38:59 +08:00

39 lines
2.0 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- Grok video billing is per second of generated output (xAI rate card), so usage
-- rows must record the billed resolution and duration for auditability. The
-- image-size check constraint must also exempt any video row by video_count
-- instead of billing_mode='video' alone: a video request billed through a
-- token-mode channel price produces billing_mode='token' with image_count=1
-- (legacy media counter) and no image_size, which the previous constraint
-- rejected and silently dropped the whole billing transaction.
ALTER TABLE usage_logs
ADD COLUMN IF NOT EXISTS video_count INTEGER NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS video_resolution VARCHAR(10),
ADD COLUMN IF NOT EXISTS video_duration_seconds INTEGER;
COMMENT ON COLUMN usage_logs.video_count IS '视频生成数量;>0 表示本行是视频生成用量';
COMMENT ON COLUMN usage_logs.video_resolution IS '计费用视频分辨率 480p/720p/1080p';
COMMENT ON COLUMN usage_logs.video_duration_seconds IS '提交时请求的视频时长(秒),按秒计费的乘数';
ALTER TABLE usage_logs
DROP CONSTRAINT IF EXISTS usage_logs_image_billing_size_check;
ALTER TABLE usage_logs
ADD CONSTRAINT usage_logs_image_billing_size_check
CHECK (
image_count <= 0
OR billing_mode = 'video'
OR COALESCE(video_count, 0) > 0
OR (
image_size IS NOT NULL
AND image_size IN ('1K', '2K', '4K', 'mixed')
)
) NOT VALID;
-- Group video prices are per-second rates (USD/s), matching the xAI rate card;
-- total cost = per-second price x duration seconds. Clarify the column docs
-- introduced by migration 170, which read as per-video prices.
COMMENT ON COLUMN groups.video_price_480p IS '480p 视频生成每秒单价 (USD/s),Grok 平台使用';
COMMENT ON COLUMN groups.video_price_720p IS '720p 视频生成每秒单价 (USD/s),Grok 平台使用';
COMMENT ON COLUMN groups.video_price_1080p IS '1080p 视频生成每秒单价 (USD/s),Grok 平台使用';