Skip to content

feat: show user display names in usage analytics#6259

Open
lingfengchencn wants to merge 2 commits into
QuantumNous:mainfrom
lingfengchencn:codex/add-user-display-names-and-filtering
Open

feat: show user display names in usage analytics#6259
lingfengchencn wants to merge 2 commits into
QuantumNous:mainfrom
lingfengchencn:codex/add-user-display-names-and-filtering

Conversation

@lingfengchencn

@lingfengchencn lingfengchencn commented Jul 17, 2026

Copy link
Copy Markdown

⚠️ 提交说明 / PR Notice

Important

  • 请提供人工撰写的简洁摘要,避免直接粘贴未经整理的 AI 输出。

📝 变更描述 / Description

  • 日志、异步任务和用户用量看板通过批量用户查询展示当前 display_name,未向历史日志冗余写入显示名称;显示名称缺失时回退到用户名。
  • 用户用量看板新增用户名模糊筛选。筛选先解析当前用户名对应的用户 ID,再查询这些 ID 的全部历史用量,因此用户改名后仍能看到改名前的记录。
  • 用户图表改为按正数 user_id 聚合;多个用户使用相同显示名称时,标签显示为 显示名称 (用户名)。缺少有效用户 ID 的旧数据继续按用户名区分,避免错误合并。
  • 补充后端及前端回归测试,覆盖改名历史、重复显示名称和旧数据兼容场景。

AI-assisted disclosure: 本 PR 的代码与说明由 AI 辅助生成;提交者应在合并前完成最终人工审阅。

🚀 变更类型 / Type of change

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

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

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

📸 运行证明 / Proof of Work

  • go test ./...
  • bun run typecheckweb/default
  • bun test src/features/dashboard/lib(33 passed, 0 failed)
  • bun run buildweb/default
  • gofmt -d 检查本 PR 修改的 Go 文件,无差异
  • oxfmt --check 检查本 PR 修改的 11 个前端文件,全部通过
  • oxlint 检查除 charts.ts 外的本 PR 前端修改文件,全部通过;charts.ts 的现有基线规则告警未由本 PR 引入

Summary by CodeRabbit

  • New Features

    • User display names now appear across tasks, logs, usage history, avatars, and mobile views, with usernames used as fallbacks.
    • User charts support filtering by username.
    • Charts distinguish users sharing the same display name and preserve historical records after username changes.
    • Quota history supports username-based filtering and includes display names.
  • Bug Fixes

    • Prevented chart data from merging separate users with identical display names.
  • Tests

    • Added coverage for username changes, chart disambiguation, and legacy records.

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Backend responses now include resolved user display names. Quota history supports username filtering across renamed users, while dashboard charts use stable identities and disambiguated labels. Usage-log interfaces and displays prefer display names with username fallbacks.

Changes

User identity presentation

Layer / File(s) Summary
Identity contracts and DTO fields
model/user.go, model/log.go, model/task.go, model/usedata.go, dto/task.go, relay/relay_task.go
Adds bulk user identity lookup and non-persisted DisplayName fields across log, task, quota, and DTO models.
Log and task identity enrichment
controller/log.go, controller/task.go
Batch-resolves identities for log responses and task DTO conversion.
Quota history identity filtering
model/usedata.go, controller/usedata.go, controller/usedata_test.go
Filters quota data by username, groups records by user ID, enriches rows with current identity data, and tests renamed-user history.
Dashboard identity filtering and charts
web/default/src/features/dashboard/*, docs/superpowers/specs/*
Adds debounced username filtering and stable identity-based ranking and trend aggregation, with duplicate-label and legacy-row tests.
Usage log display names
web/default/src/features/usage-logs/*
Extends log schemas and renders display names before username or user ID fallbacks.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Dashboard
  participant QuotaAPI
  participant ChartProcessor
  User->>Dashboard: enter username filter
  Dashboard->>QuotaAPI: request debounced filtered quota data
  QuotaAPI-->>Dashboard: return enriched quota rows
  Dashboard->>ChartProcessor: aggregate by stable user identity
  ChartProcessor-->>Dashboard: render ranked and trend series
Loading

Suggested reviewers: calcium-ion

Poem

I’m a rabbit with names in my hat,
Display names bloom where usernames sat.
Charts split each identity true,
Old quota trails come hopping through.
Filter, fetch, and labels align—
A tidy burrow by design! 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.71% 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 accurately captures the main theme of adding display names to analytics views, though it does not mention logs and tasks specifically.
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.

Warning

⚠️ This pull request shows signs of AI-generated slop (defensive_cruft, ai_padded_prose). It has been flagged by CodeRabbit slop detection and should be reviewed carefully.

@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: 4

🧹 Nitpick comments (1)
model/user.go (1)

1256-1267: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Pre-allocate map capacity.

Since you know the maximum number of items that will be added to the map (bounded by len(ids) or len(rows)), consider pre-allocating the map capacity to avoid reallocation overhead.

♻️ Proposed refactor
-	result := make(map[int]UserIdentity)
 	if len(ids) == 0 {
-		return result, nil
+		return make(map[int]UserIdentity), nil
 	}
 	var rows []UserIdentity
 	if err := DB.Model(&User{}).Select("id, username, display_name").Where("id IN ?", ids).Find(&rows).Error; err != nil {
 		return nil, err
 	}
+	result := make(map[int]UserIdentity, len(rows))
 	for _, r := range rows {
 		result[r.Id] = r
 	}
🤖 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/user.go` around lines 1256 - 1267, Update the result map initialization
in the user identity lookup function to pre-allocate capacity based on len(ids),
preserving the existing empty-input, query, and population behavior.
🤖 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 `@docs/superpowers/specs/2026-07-17-user-chart-identity-design.md`:
- Line 12: Update the user chart identity design document’s response/type
contract and username-filtering statements to match the implemented behavior,
including display_name. Replace the documented full-width Chinese-parenthesis
label format with the implemented `display name (username)` format wherever the
disambiguation behavior is described, including the referenced lines.

In `@web/default/src/features/dashboard/components/users/user-charts.tsx`:
- Around line 239-244: Add an accessible, localized name to the username filter
Input by supplying an aria-label through the existing t translation helper,
while preserving its current value, change handler, placeholder, and styling.

In `@web/default/src/features/dashboard/lib/charts.test.ts`:
- Around line 1-2: Update the test imports and assertions in the chart utility
test to use Vitest: replace node:test and node:assert with Vitest’s test and
expect APIs, and change assert.deepEqual assertions to expect(...).toEqual(...).
Keep the existing test behavior unchanged.

In `@web/default/src/features/dashboard/lib/charts.ts`:
- Around line 779-786: Update the label-generation logic around labelByKey to
track final labels already assigned and ensure every candidate is globally
unique; when a candidate collides, append an additional deterministic suffix
while preserving existing duplicate-base-label behavior. Add a regression test
covering a natural label collision such as “Alice (bob)” versus “Alice” with
username “bob”, verifying distinct series labels and color-map keys.

---

Nitpick comments:
In `@model/user.go`:
- Around line 1256-1267: Update the result map initialization in the user
identity lookup function to pre-allocate capacity based on len(ids), preserving
the existing empty-input, query, and population behavior.
🪄 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: 673d6f6b-5d24-4a6b-822c-a84c40779b7c

📥 Commits

Reviewing files that changed from the base of the PR and between a63364d and 603ba20.

📒 Files selected for processing (22)
  • controller/log.go
  • controller/task.go
  • controller/usedata.go
  • controller/usedata_test.go
  • docs/superpowers/specs/2026-07-17-user-chart-identity-design.md
  • dto/task.go
  • model/log.go
  • model/task.go
  • model/usedata.go
  • model/user.go
  • relay/relay_task.go
  • web/default/src/features/dashboard/api.ts
  • web/default/src/features/dashboard/components/users/user-charts.tsx
  • web/default/src/features/dashboard/index.tsx
  • web/default/src/features/dashboard/lib/charts.test.ts
  • web/default/src/features/dashboard/lib/charts.ts
  • web/default/src/features/dashboard/types.ts
  • web/default/src/features/usage-logs/components/columns/common-logs-columns.tsx
  • web/default/src/features/usage-logs/components/columns/task-logs-columns.tsx
  • web/default/src/features/usage-logs/components/usage-logs-mobile-card.tsx
  • web/default/src/features/usage-logs/data/schema.ts
  • web/default/src/features/usage-logs/types.ts

Chart aggregation will use a stable user identity derived from `user_id`. The visible label remains presentation-only:

- Use `display_name` when present; otherwise fall back to `username`.
- When multiple user IDs share the same visible display name, disambiguate each label with its username, for example `用户显示名称A(用户名1)` and `用户显示名称A(用户名2)`.

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Align the design document with the implemented contract.

The response/type contract now includes display_name and username filtering, contrary to Line 27. The documented full-width 显示名称(用户名) format also differs from the implemented display name (username) format.

Also applies to: 27-27, 35-35

🤖 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 `@docs/superpowers/specs/2026-07-17-user-chart-identity-design.md` at line 12,
Update the user chart identity design document’s response/type contract and
username-filtering statements to match the implemented behavior, including
display_name. Replace the documented full-width Chinese-parenthesis label format
with the implemented `display name (username)` format wherever the
disambiguation behavior is described, including the referenced lines.

Comment on lines +239 to +244
<Input
value={username}
onChange={(e) => handleUsernameChange(e.target.value)}
placeholder={t('Filter by username')}
className='h-8 w-36 shrink-0 text-xs sm:w-44'
/>

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.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Give the username filter an accessible name.

The input relies solely on its placeholder, so assistive technology lacks a reliable label. Add a localized aria-label or visible <label>.

Proposed fix
 <Input
   value={username}
   onChange={(e) => handleUsernameChange(e.target.value)}
+  aria-label={t('Filter by username')}
   placeholder={t('Filter by username')}

As per coding guidelines, associate form inputs with labels and add ARIA attributes when needed. <coding_guidelines>

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Input
value={username}
onChange={(e) => handleUsernameChange(e.target.value)}
placeholder={t('Filter by username')}
className='h-8 w-36 shrink-0 text-xs sm:w-44'
/>
<Input
value={username}
onChange={(e) => handleUsernameChange(e.target.value)}
aria-label={t('Filter by username')}
placeholder={t('Filter by username')}
className='h-8 w-36 shrink-0 text-xs sm:w-44'
/>
🤖 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/users/user-charts.tsx` around
lines 239 - 244, Add an accessible, localized name to the username filter Input
by supplying an aria-label through the existing t translation helper, while
preserving its current value, change handler, placeholder, and styling.

Source: Coding guidelines

Comment on lines +1 to +2
import assert from 'node:assert/strict'
import { test } from 'node:test'

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.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use Vitest for this frontend utility test.

The new test uses node:test and node:assert, bypassing the project’s prescribed frontend test runner.

Proposed fix
-import assert from 'node:assert/strict'
-import { test } from 'node:test'
+import { expect, test } from 'vitest'

Replace assert.deepEqual(actual, expected) with expect(actual).toEqual(expected).

As per coding guidelines, unit tests under web/default/**/*.test.ts must use Vitest. <coding_guidelines>

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import assert from 'node:assert/strict'
import { test } from 'node:test'
import { expect, test } from 'vitest'
🤖 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/lib/charts.test.ts` around lines 1 - 2,
Update the test imports and assertions in the chart utility test to use Vitest:
replace node:test and node:assert with Vitest’s test and expect APIs, and change
assert.deepEqual assertions to expect(...).toEqual(...). Keep the existing test
behavior unchanged.

Source: Coding guidelines

Comment on lines +779 to +786
const labelByKey = new Map<string, string>()
for (const [key, identity] of identityByKey) {
const duplicate = (baseLabelCounts.get(identity.baseLabel) || 0) > 1
const suffix = identity.username || key.replace(/^id:/, '#')
labelByKey.set(
key,
duplicate ? `${identity.baseLabel} (${suffix})` : identity.baseLabel
)

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Guarantee that final chart labels are globally unique.

Disambiguating only duplicate base labels can still collide with another identity’s natural label—for example, Alice (bob) and Alice with username bob. Both then use the same seriesField and color-map key, so separate identities can render as one series.

Track used final labels and append an additional stable suffix when a candidate already exists. Add a regression case for this collision.

🤖 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/lib/charts.ts` around lines 779 - 786,
Update the label-generation logic around labelByKey to track final labels
already assigned and ensure every candidate is globally unique; when a candidate
collides, append an additional deterministic suffix while preserving existing
duplicate-base-label behavior. Add a regression test covering a natural label
collision such as “Alice (bob)” versus “Alice” with username “bob”, verifying
distinct series labels and color-map keys.

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