-
Notifications
You must be signed in to change notification settings - Fork 11.2k
feat: show user display names in usage analytics #6259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
lingfengchencn
wants to merge
2
commits into
QuantumNous:main
from
lingfengchencn:codex/add-user-display-names-and-filtering
Closed
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| package controller | ||
|
|
||
| import ( | ||
| "net/http" | ||
| "net/http/httptest" | ||
| "testing" | ||
|
|
||
| "github.com/QuantumNous/new-api/common" | ||
| "github.com/QuantumNous/new-api/model" | ||
| "github.com/gin-gonic/gin" | ||
| "github.com/stretchr/testify/require" | ||
| ) | ||
|
|
||
| type userQuotaResponse struct { | ||
| Success bool `json:"success"` | ||
| Message string `json:"message"` | ||
| Data []*model.QuotaData `json:"data"` | ||
| } | ||
|
|
||
| func TestGetQuotaDatesByUserIncludesHistoryAfterUsernameChange(t *testing.T) { | ||
| db := setupModelListControllerTestDB(t) | ||
| require.NoError(t, db.AutoMigrate(&model.QuotaData{})) | ||
| require.NoError(t, db.Create(&model.User{ | ||
| Id: 1, | ||
| Username: "new-alice", | ||
| Password: "password", | ||
| DisplayName: "Alice", | ||
| }).Error) | ||
| require.NoError(t, db.Create(&model.QuotaData{ | ||
| UserID: 1, | ||
| Username: "old-alice", | ||
| CreatedAt: 1000, | ||
| Count: 1, | ||
| Quota: 100, | ||
| TokenUsed: 10, | ||
| }).Error) | ||
| require.NoError(t, db.Create(&model.QuotaData{ | ||
| UserID: 1, | ||
| Username: "new-alice", | ||
| CreatedAt: 1000, | ||
| Count: 1, | ||
| Quota: 50, | ||
| TokenUsed: 5, | ||
| }).Error) | ||
|
|
||
| recorder := httptest.NewRecorder() | ||
| ctx, _ := gin.CreateTestContext(recorder) | ||
| ctx.Request = httptest.NewRequest( | ||
| http.MethodGet, | ||
| "/api/data/users?start_timestamp=900&end_timestamp=1100&username=new-alice", | ||
| nil, | ||
| ) | ||
|
|
||
| GetQuotaDatesByUser(ctx) | ||
|
|
||
| require.Equal(t, http.StatusOK, recorder.Code) | ||
| var payload userQuotaResponse | ||
| require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &payload)) | ||
| require.True(t, payload.Success, payload.Message) | ||
| require.Len(t, payload.Data, 2) | ||
|
|
||
| totalQuota := 0 | ||
| for _, row := range payload.Data { | ||
| totalQuota += row.Quota | ||
| require.Equal(t, "new-alice", row.Username) | ||
| require.Equal(t, "Alice", row.DisplayName) | ||
| } | ||
| require.Equal(t, 150, totalQuota) | ||
| } |
40 changes: 40 additions & 0 deletions
40
docs/superpowers/specs/2026-07-17-user-chart-identity-design.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| # User Chart Identity Design | ||
|
|
||
| ## Context | ||
|
|
||
| The user consumption ranking and trend charts currently prefer `display_name` as both the visible label and the aggregation key. Because display names are not unique, two different users with the same display name can be merged into one chart series and their quota totals can be added together incorrectly. | ||
|
|
||
| ## Decision | ||
|
|
||
| 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)`. | ||
| - If a legacy row has no positive `user_id` (missing values are serialized as `0`), use `username` as the identity fallback so existing data remains usable. | ||
|
|
||
| ## Data Flow | ||
|
|
||
| `processUserChartData` will build three separate concepts: | ||
|
|
||
| 1. A stable identity key used by quota totals, top-user selection, time-series aggregation, and color assignment. | ||
| 2. A base label derived from `display_name || username || 'unknown'`. | ||
| 3. A final unique presentation label. Duplicate base labels receive the username suffix; non-duplicate labels remain unchanged. | ||
|
|
||
| Both ranking and trend output will use the same final label map, so the bar chart, legend, tooltip, series colors, and trend points remain consistent. | ||
|
|
||
| ## Compatibility and Scope | ||
|
|
||
| The backend response and TypeScript API types do not change. Username filtering first resolves current matching users to IDs so renamed users retain their complete history; legacy rows without a positive user ID still use snapshot-username matching. The change does not alter database storage, user records, or unrelated dashboard charts. | ||
|
|
||
| ## Testing | ||
|
|
||
| Add a deterministic regression test containing two different `user_id` values with the same `display_name` and different usernames. The test must first demonstrate the current incorrect merge, then verify that: | ||
|
|
||
| - two ranking entries remain; | ||
| - quota totals are not combined; | ||
| - the labels use the approved `显示名称(用户名)` format; | ||
| - two independent trend series remain. | ||
|
|
||
| Add an API regression test proving that filtering by a user's current username returns quota rows recorded under both the old and current usernames, with the current username and display name attached to the response. | ||
|
|
||
| Run the targeted chart test, frontend type checking, changed-file lint and formatting checks, the frontend production build, and the full Go test suite before creating the pull request. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.