Skip to content

feat(dashboard): add admin channel analytics report - #5921

Draft
zhukangfeng wants to merge 3 commits into
QuantumNous:mainfrom
zhukangfeng:codex/channel-report
Draft

feat(dashboard): add admin channel analytics report#5921
zhukangfeng wants to merge 3 commits into
QuantumNous:mainfrom
zhukangfeng:codex/channel-report

Conversation

@zhukangfeng

@zhukangfeng zhukangfeng commented Jul 5, 2026

Copy link
Copy Markdown

⚠️ 提交说明 / PR Notice

Important

  • 请提供人工撰写的简洁摘要,避免直接粘贴未经整理的 AI 输出。
  • This PR was prepared with AI assistance because the local git user is not one of the recurring core authors in recent repository history.

📝 变更描述 / Description

新增管理员可见的渠道统计报表,用于从渠道维度查看金额、请求数、总 Token、输入/输出 Token、cache read/write Token、模型使用分布、消费趋势与渠道健康度。后端基于 quota_data 聚合渠道、模型和时间维度,接口通过管理员鉴权暴露;前端把渠道统计放在管理员的渠道区域,同时保留 dashboard 内部 tab 体验。

为保证后续数据准确,消费日志写入数据看板时同步记录 prompt/completion/cache token 明细和 channel_id;旧数据不做自动迁移,提供按数据库方言区分的一次性保守回填 SQL,只处理能和消费日志唯一匹配且总量一致的历史 channel_id = 0 聚合行,避免总金额重复或误拆分。

另外修复 classic 前端在 Bun/Docker 构建时 date-fns alias 写死路径的问题,改为按实际安装布局探测存在的路径。

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix) - 请关联对应 Issue,避免将设计取舍、理解偏差或预期不一致直接归类为 bug
  • ✨ 新功能 (New feature) - 重大特性建议先通过 Issue 沟通
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

  • Closes # (如有)

✅ 提交前检查项 / Checklist

  • 人工确认: 我已亲自整理并撰写此描述,没有直接粘贴未经处理的 AI 输出。
  • 非重复提交: 我已搜索现有的 PR,确认当前分支未存在重复提交。
  • Bug fix 说明: 若此 PR 标记为 Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。
  • 变更理解: 我已理解这些更改的工作原理及可能影响。
  • 范围聚焦: 本 PR 未包含任何与当前任务无关的代码改动。
  • 本地验证: 已在本地运行并通过测试或手动验证,维护者可以据此复核结果。
  • 安全合规: 代码中无敏感凭据,且符合项目代码规范。

📸 运行证明 / Proof of Work

  • go test ./...
  • go test ./model
  • cd web/default && bunx oxlint src/features/dashboard/components/channels/channel-analytics.tsx src/features/dashboard/index.tsx src/hooks/use-sidebar-data.ts src/hooks/use-sidebar-config.ts
  • cd web/default && bun run typecheck
  • cd web/default && bun run build
  • cd web/classic && bun run build
  • i18n check: 渠道报表组件 t(...) key 在 en/zh/fr/ru/ja/vi 中均已覆盖,且 locale JSON 无根层级漏写 key。

Summary by CodeRabbit

  • New Features

    • Added an admin report for quota and token usage by channel and model over a selected time range.
    • Added prompt, completion, and cache token metrics to quota usage records and exports.
    • Added channel metadata, including channel name, status, and response time, to report results.
  • Bug Fixes

    • Added safe backfill tools for assigning legacy usage records to channels only when matches are unambiguous and totals align.

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5a9a9a65-a829-42e6-9fc8-f0bb119bb63a

📥 Commits

Reviewing files that changed from the base of the PR and between 6254881 and 78a0bf5.

📒 Files selected for processing (1)
  • model/log.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • model/log.go

Walkthrough

The change adds conservative channel ID backfill scripts, records additional prompt, completion, and cache token metrics, and exposes an admin-authenticated channel quota report endpoint with aggregation and channel metadata enrichment.

Changes

Channel quota analytics

Layer / File(s) Summary
Legacy channel ID backfill
bin/backfill_quota_data_channel_id.*.sql
Adds transactional MySQL and SQLite/PostgreSQL scripts that update quota_data.channel_id only for unambiguous matches with exact aggregate totals. Verification queries list unresolved rows.
Quota metric fields and logging
model/usedata.go, model/log.go, model/usedata_flow_test.go
Adds prompt, completion, and cache token fields to quota structures, cache merging, database increments, consume-log export, and flow test assertions.
Channel quota report API
model/usedata_channel.go, model/usedata_channel_test.go, controller/usedata.go, router/api-router.go
Aggregates quota data by channel and model, enriches rows with channel metadata, validates the report with seeded data, and registers GET /api/data/channels with admin authentication.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AdminClient
  participant API Router
  participant GetChannelQuotaReportData
  participant QuotaDataStore
  participant ChannelStore
  AdminClient->>API Router: GET /api/data/channels
  API Router->>GetChannelQuotaReportData: authorize and invoke handler
  GetChannelQuotaReportData->>QuotaDataStore: aggregate quota data by channel and model
  GetChannelQuotaReportData->>ChannelStore: fetch channel metadata
  ChannelStore-->>GetChannelQuotaReportData: channel names and status
  GetChannelQuotaReportData-->>AdminClient: JSON report data
Loading

Possibly related PRs

Poem

A rabbit hops through quota rows,
Where token counts now clearly show.
Channels gather by model and hour,
Backfills bloom with careful power.
“Admin reports!” the rabbit sings,
While cache metrics grow new wings.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding an admin channel analytics report.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
model/usedata_channel.go (1)

21-34: 🚀 Performance & Scalability | 🔵 Trivial

Consider a composite index for this reporting query.

The aggregation filters/groups by channel_id and created_at together, but QuotaData only has separate single-column indexes on each. A composite index (e.g., channel_id, created_at) would better support this grouping pattern as data volume grows.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model/usedata_channel.go` around lines 21 - 34, Add a composite index to
support the reporting query in GetChannelQuotaReportData, since it filters and
groups on channel_id and created_at together and the current single-column
indexes are not enough. Update the QuotaData model/index definitions to include
a combined channel_id, created_at index (or equivalent migration/tag), and keep
the query in GetChannelQuotaReportData using those same fields so the database
can use the new index efficiently.
model/usedata_channel_test.go (1)

90-95: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use assert for independent, non-fatal value checks.

Lines 90-95 check independent row fields after the length/order is already validated; using assert (rather than require) here lets all mismatches surface in a single run instead of stopping at the first failure.

♻️ Suggested change
+	"github.com/stretchr/testify/assert"
...
-	require.Equal(t, "gpt-b", rows[1].ModelName)
-	require.Equal(t, 75, rows[1].Quota)
-	require.Equal(t, "west", rows[2].ChannelName)
-	require.Equal(t, 2, rows[2].Status)
-	require.Equal(t, 99, rows[3].ChannelID)
-	require.Equal(t, "channel-99", rows[3].ChannelName)
+	assert.Equal(t, "gpt-b", rows[1].ModelName)
+	assert.Equal(t, 75, rows[1].Quota)
+	assert.Equal(t, "west", rows[2].ChannelName)
+	assert.Equal(t, 2, rows[2].Status)
+	assert.Equal(t, 99, rows[3].ChannelID)
+	assert.Equal(t, "channel-99", rows[3].ChannelName)
As per coding guidelines, "New or substantially rewritten Go backend tests must use `github.com/stretchr/testify/require` for setup and fatal assertions, and `github.com/stretchr/testify/assert` for non-fatal value checks."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@model/usedata_channel_test.go` around lines 90 - 95, Switch the independent
row field checks in the usedata channel test from fatal assertions to non-fatal
ones. In the test that validates rows after length/order has already been
confirmed, keep `require` for setup and structural checks, but replace the
`require.Equal` calls on `rows[1]`, `rows[2]`, and `rows[3]` with `assert.Equal`
so all value mismatches are reported in one run.

Source: Coding guidelines

web/default/src/features/dashboard/components/channels/channel-analytics.tsx (2)

1350-1362: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Expose selected state of the trend/bar toggle to assistive tech.

These buttons act as a toggle group but only convey the active state visually. Add aria-pressed (or a role="tab"/aria-selected pattern) so screen-reader users can perceive the current selection.

♿ Proposed fix
               <button
                 key={item.value}
                 type='button'
+                aria-pressed={activeTrendView === item.value}
                 onClick={() => setActiveTrendView(item.value)}

As per path instructions: "ensure keyboard operability ... add ARIA attributes when needed".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/dashboard/components/channels/channel-analytics.tsx`
around lines 1350 - 1362, The trend/bar toggle buttons in channel-analytics
currently expose selection only through styling, so assistive tech cannot tell
which view is active. Update the button rendered in the item map around
setActiveTrendView/activeTrendView to include an accessible selected-state
pattern, preferably aria-pressed on the existing button or a tab/tablist
aria-selected pattern if you switch semantics. Keep the current click behavior
and ensure the active item is announced consistently for screen-reader users.

Source: Path instructions


792-797: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Mark decorative icons with aria-hidden="true".

These icons are purely decorative (each is paired with a text label), so they should be hidden from assistive tech. This applies to the other decorative icons in this file as well (ChartPanel header icon at Line 830, Channel Health icon at Line 1370, and the summary card icons).

♿ Example fix for SummaryCard
-        <Icon className='size-4' />
+        <Icon className='size-4' aria-hidden='true' />

As per path instructions: "hide decorative icons with aria-hidden="true"".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/default/src/features/dashboard/components/channels/channel-analytics.tsx`
around lines 792 - 797, Mark the decorative icons used in `SummaryCard`, the
`ChartPanel` header, the `Channel Health` section, and the summary card icons
with `aria-hidden="true"` since each icon already has a nearby text label and
should be hidden from assistive tech. Update the icon elements in
`channel-analytics.tsx` to include this attribute consistently wherever the icon
is purely decorative, using the existing component names and JSX blocks as the
targets.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@web/default/src/i18n/locales/ja.json`:
- Line 740: The ja.json translation for Channel Tokens still mixes English and
Japanese, so update the localized value in the locale entry to a fully Japanese
phrasing for consistency with nearby keys like Channel Requests and Channel
Spend. Locate the Channel Tokens string in the Japanese locale file and change
the translation from the mixed-language form to the appropriate Japanese term
suggested by the review.

In `@web/default/src/i18n/locales/ru.json`:
- Line 5000: The Russian locale entry for Zoom is still left in English, so
update the corresponding key in the ru.json localization data to a proper
Russian translation. Locate the "Zoom" string in the locale dictionary and
replace the value with a localized term consistent with other translations in
this file, such as "Масштаб", to match the intended Russian UI text.

---

Nitpick comments:
In `@model/usedata_channel_test.go`:
- Around line 90-95: Switch the independent row field checks in the usedata
channel test from fatal assertions to non-fatal ones. In the test that validates
rows after length/order has already been confirmed, keep `require` for setup and
structural checks, but replace the `require.Equal` calls on `rows[1]`,
`rows[2]`, and `rows[3]` with `assert.Equal` so all value mismatches are
reported in one run.

In `@model/usedata_channel.go`:
- Around line 21-34: Add a composite index to support the reporting query in
GetChannelQuotaReportData, since it filters and groups on channel_id and
created_at together and the current single-column indexes are not enough. Update
the QuotaData model/index definitions to include a combined channel_id,
created_at index (or equivalent migration/tag), and keep the query in
GetChannelQuotaReportData using those same fields so the database can use the
new index efficiently.

In
`@web/default/src/features/dashboard/components/channels/channel-analytics.tsx`:
- Around line 1350-1362: The trend/bar toggle buttons in channel-analytics
currently expose selection only through styling, so assistive tech cannot tell
which view is active. Update the button rendered in the item map around
setActiveTrendView/activeTrendView to include an accessible selected-state
pattern, preferably aria-pressed on the existing button or a tab/tablist
aria-selected pattern if you switch semantics. Keep the current click behavior
and ensure the active item is announced consistently for screen-reader users.
- Around line 792-797: Mark the decorative icons used in `SummaryCard`, the
`ChartPanel` header, the `Channel Health` section, and the summary card icons
with `aria-hidden="true"` since each icon already has a nearby text label and
should be hidden from assistive tech. Update the icon elements in
`channel-analytics.tsx` to include this attribute consistently wherever the icon
is purely decorative, using the existing component names and JSX blocks as the
targets.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 292ae0fe-772d-46b3-bbcc-6c25ac6ec1fe

📥 Commits

Reviewing files that changed from the base of the PR and between 2281c9e and 94c6330.

📒 Files selected for processing (23)
  • bin/backfill_quota_data_channel_id.mysql.sql
  • bin/backfill_quota_data_channel_id.sqlite_postgres.sql
  • controller/usedata.go
  • model/log.go
  • model/usedata.go
  • model/usedata_channel.go
  • model/usedata_channel_test.go
  • model/usedata_flow_test.go
  • router/api-router.go
  • web/classic/rsbuild.config.ts
  • web/default/src/features/dashboard/api.ts
  • web/default/src/features/dashboard/components/channels/channel-analytics.tsx
  • web/default/src/features/dashboard/index.tsx
  • web/default/src/features/dashboard/section-registry.tsx
  • web/default/src/features/dashboard/types.ts
  • web/default/src/hooks/use-sidebar-config.ts
  • web/default/src/hooks/use-sidebar-data.ts
  • web/default/src/i18n/locales/en.json
  • web/default/src/i18n/locales/fr.json
  • web/default/src/i18n/locales/ja.json
  • web/default/src/i18n/locales/ru.json
  • web/default/src/i18n/locales/vi.json
  • web/default/src/i18n/locales/zh.json

Comment thread web/default/src/i18n/locales/ja.json Outdated
Comment thread web/default/src/i18n/locales/ru.json Outdated
Add an admin-only channel analytics dashboard with channel/model filters, metric switching, token breakdowns, trend/bar chart views, health summaries, and accurate spend details.

Persist prompt/completion/cache token splits into quota_data and expose an admin channel report API backed by aggregated quota data.

Add conservative channel_id backfill SQL scripts for legacy quota_data rows and fix classic frontend date-fns alias resolution for Bun/Docker builds.
@zhukangfeng
zhukangfeng force-pushed the codex/channel-report branch from 94c6330 to 6254881 Compare July 5, 2026 23:40
@Calcium-Ion
Calcium-Ion force-pushed the main branch 2 times, most recently from 51fdfc5 to 2b6f1df Compare August 30, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant