feat(admin): 用户管理列表展示订阅信息并支持按订阅套餐筛选 - #4134
Conversation
后端: - 新增 GetActiveSubscriptionsByUserIds 批量查询活跃订阅 - 新增 POST /api/subscription/admin/users/batch_active_subscriptions 接口 - SearchUsers 支持 plan_id 参数筛选订阅用户 前端: - 用户列表新增订阅信息列,支持多订阅 Tag 展示 - 不同套餐自动分配不同颜色,过期订阅灰色 - Tooltip 显示到期时间和额度使用量 - 新增选择订阅下拉筛选框 - 批量接口替代 N+1 请求,含竞态保护 i18n: - 7 种语言新增 5 个翻译 key
- controller/user.go: 不向客户端暴露原始解析错误,合并为统一错误消息
- UsersColumnDefs.jsx: toLocaleString() 显示完整到期时间(含时分秒)
- UsersColumnDefs.jsx: 区分 null(加载中) 和 {}(无订阅) 状态
- useUsersData.jsx: 为用户列表请求添加竞态保护(latestUserRequestRef)
- useUsersData.jsx: 使用 URLSearchParams 编码搜索参数
- vi.json: 消除选择订阅/选择订阅套餐的歧义翻译
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughAdds a backend admin batch endpoint to fetch active subscriptions for multiple users, extends user search to filter by plan, adds frontend plan/filter UI and per-user subscription column, implements a hook to fetch plans and batch subscription data, and adds i18n entries for subscription UI labels. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as User (UI)
participant Page as UsersPage
participant Hook as useUsersData
participant API as Backend API
participant DB as Database
UI->>Page: open page / select plan filter
Page->>Hook: init / searchUsers(start,page,plan)
Hook->>API: GET /api/subscription/admin/plans
API->>DB: SELECT plans
DB-->>API: plans
API-->>Hook: planOptions
Hook->>API: GET /api/user/search?plan_id=X
API->>DB: Query users (optionally filtered by active subscription plan)
DB-->>API: users[]
API-->>Hook: users[]
Hook->>API: POST /api/subscription/admin/users/batch_active_subscriptions {user_ids: [...]}
API->>DB: SELECT subscriptions WHERE user_id IN (...) AND status='active' AND end_time>now
DB-->>API: subscriptions[]
API-->>Hook: {userId: [subscriptions]}
Hook-->>Page: users + planOptions + userSubscriptions
Page-->>UI: render table with subscription tags
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
web/src/i18n/locales/ru.json (1)
2555-2555: Consider a more contextually appropriate status label.The Chinese "订阅中" is translated as "Подписан" (subscribed, masculine past participle). While grammatically acceptable, there are two concerns:
- Gender mismatch: Russian "подписка" (subscription) is feminine, but "Подписан" is masculine
- Clarity: For a status label, "Активна" (Active) or "Действует" (Valid/In effect) would be more intuitive and grammatically consistent
♻️ Suggested alternatives
- "订阅中": "Подписан", + "订阅中": "Активна",or
- "订阅中": "Подписан", + "订阅中": "Действует",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/i18n/locales/ru.json` at line 2555, The translation for the key "订阅中" in ru.json uses "Подписан" which is masculine and may be unclear as a status; update the value to a context-appropriate, grammatically consistent Russian status such as "Активна" or "Действует" (or another feminine/neutral form depending on UI context) by replacing the current mapping for "订阅中" in web/src/i18n/locales/ru.json with the chosen term so the status reads correctly in Russian.controller/user.go (1)
257-257: Prefer an i18n-backed error key over inline literal.Using a hardcoded
"无效的订阅套餐ID"here makes this path harder to localize and less consistent with otherApiErrorI18nresponses in the same controller.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@controller/user.go` at line 257, Replace the hardcoded Chinese message call common.ApiErrorMsg(c, "无效的订阅套餐ID") with the i18n-backed error helper (e.g., common.ApiErrorI18n) and an appropriate i18n key such as "error.invalid_subscription_plan_id" (or match your existing key namespace) so the controller uses localized messages; update the call site where the literal appears (in controller/user.go) to call common.ApiErrorI18n(c, "error.invalid_subscription_plan_id") and ensure the corresponding translation entry is present in your i18n resource files.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/i18n/locales/ru.json`:
- Line 686: The translation for the Chinese key "到期" in ru.json is incorrect
(currently "Истекает"); update the Russian value for the key "到期" to the
past-tense feminine form "Истекла" so it correctly conveys "expired" for
subscriptions (replace the value associated with the "到期" key).
- Line 2556: The Russian translation for the JSON key "订阅信息" is incomplete: it
should preserve the "information" aspect; update the value for the key "订阅信息"
(currently "Подписка") to a full equivalent like "Информация о подписке" (or a
shorter UI-friendly alternative such as "Сведения о подписке") so the column
header correctly conveys "Subscription information".
In `@web/src/i18n/locales/vi.json`:
- Line 1540: The translation for the JSON key "无订阅" should be updated to clearer
Vietnamese wording for table/filter UI: replace the current value ("Không có
đăng ký") with "Chưa có đăng ký" (or the agreed alternative such as "Không có
gói đăng ký") for the key "无订阅" everywhere it appears in vi.json (including the
other occurrences noted); ensure you update all identical keys consistently,
keep valid JSON syntax (quotes/commas), and run the i18n/JSON linter to verify
no formatting errors.
---
Nitpick comments:
In `@controller/user.go`:
- Line 257: Replace the hardcoded Chinese message call common.ApiErrorMsg(c,
"无效的订阅套餐ID") with the i18n-backed error helper (e.g., common.ApiErrorI18n) and
an appropriate i18n key such as "error.invalid_subscription_plan_id" (or match
your existing key namespace) so the controller uses localized messages; update
the call site where the literal appears (in controller/user.go) to call
common.ApiErrorI18n(c, "error.invalid_subscription_plan_id") and ensure the
corresponding translation entry is present in your i18n resource files.
In `@web/src/i18n/locales/ru.json`:
- Line 2555: The translation for the key "订阅中" in ru.json uses "Подписан" which
is masculine and may be unclear as a status; update the value to a
context-appropriate, grammatically consistent Russian status such as "Активна"
or "Действует" (or another feminine/neutral form depending on UI context) by
replacing the current mapping for "订阅中" in web/src/i18n/locales/ru.json with the
chosen term so the status reads correctly in Russian.
🪄 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: bb0da1ec-62be-4610-8637-57ba16c2c97f
⛔ Files ignored due to path filters (1)
web/bun.lockis excluded by!**/*.lock
📒 Files selected for processing (17)
controller/subscription.gocontroller/user.gomodel/subscription.gomodel/user.gorouter/api-router.goweb/src/components/table/users/UsersColumnDefs.jsxweb/src/components/table/users/UsersFilters.jsxweb/src/components/table/users/UsersTable.jsxweb/src/components/table/users/index.jsxweb/src/hooks/users/useUsersData.jsxweb/src/i18n/locales/en.jsonweb/src/i18n/locales/fr.jsonweb/src/i18n/locales/ja.jsonweb/src/i18n/locales/ru.jsonweb/src/i18n/locales/vi.jsonweb/src/i18n/locales/zh-CN.jsonweb/src/i18n/locales/zh-TW.json
| "划转额度": "Перевести квоту", | ||
| "列出的模型将不会自动添加或移除-thinking/-nothinking 后缀": "Для этих моделей суффиксы -thinking/-nothinking не будут добавляться или удаляться автоматически.", | ||
| "列设置": "Настройки столбцов", | ||
| "到期": "Истекает", |
There was a problem hiding this comment.
Translation ambiguity: "到期" should indicate "expired" status, not "expiring".
The Chinese key "到期" is translated as "Истекает" (expiring, present continuous). However, according to the PR description, this label is shown for expired subscriptions (displayed in gray). The correct translation should be "Истекла" (expired, past tense, feminine form to match Russian "подписка").
📝 Suggested correction
- "到期": "Истекает",
+ "到期": "Истекла",This is similar to the Vietnamese translation ambiguity mentioned in the PR that was fixed in commit 2.
📝 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.
| "到期": "Истекает", | |
| "到期": "Истекла", |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/i18n/locales/ru.json` at line 686, The translation for the Chinese
key "到期" in ru.json is incorrect (currently "Истекает"); update the Russian
value for the key "到期" to the past-tense feminine form "Истекла" so it correctly
conveys "expired" for subscriptions (replace the value associated with the "到期"
key).
| "订阅实例": "Экземпляр подписки", | ||
| "订阅抵扣": "Списание по подписке", | ||
| "订阅中": "Подписан", | ||
| "订阅信息": "Подписка", |
There was a problem hiding this comment.
Incomplete translation: missing "information" aspect.
The Chinese key "订阅信息" means "Subscription information", but it's translated as just "Подписка" (Subscription). For a column header showing subscription details, the full meaning should be preserved.
📝 Suggested correction
- "订阅信息": "Подписка",
+ "订阅信息": "Информация о подписке",If space is constrained in the UI, a shorter alternative would be:
- "订阅信息": "Подписка",
+ "订阅信息": "Инф. о подписке",📝 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.
| "订阅信息": "Подписка", | |
| "订阅信息": "Информация о подписке", |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/i18n/locales/ru.json` at line 2556, The Russian translation for the
JSON key "订阅信息" is incomplete: it should preserve the "information" aspect;
update the value for the key "订阅信息" (currently "Подписка") to a full equivalent
like "Информация о подписке" (or a shorter UI-friendly alternative such as
"Сведения о подписке") so the column header correctly conveys "Subscription
information".
| "新额度:": "Hạn ngạch mới: ", | ||
| "无": "Không", | ||
| "无GPU": "No GPU", | ||
| "无订阅": "Không có đăng ký", |
There was a problem hiding this comment.
Refine subscription labels for clearer Vietnamese UX.
Current values are understandable, but a few labels read as actions/states in-progress rather than stable subscription status/info. Suggested wording is clearer for table/filter UI.
✍️ Suggested wording updates
- "无订阅": "Không có đăng ký",
+ "无订阅": "Không có gói đăng ký",
- "订阅中": "Đang đăng ký",
+ "订阅中": "Đang sử dụng gói",
- "订阅信息": "Đăng ký",
+ "订阅信息": "Thông tin gói đăng ký",
- "选择订阅": "Chọn đăng ký",
+ "选择订阅": "Chọn gói đăng ký",Also applies to: 2879-2880, 3422-3422
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@web/src/i18n/locales/vi.json` at line 1540, The translation for the JSON key
"无订阅" should be updated to clearer Vietnamese wording for table/filter UI:
replace the current value ("Không có đăng ký") with "Chưa có đăng ký" (or the
agreed alternative such as "Không có gói đăng ký") for the key "无订阅" everywhere
it appears in vi.json (including the other occurrences noted); ensure you update
all identical keys consistently, keep valid JSON syntax (quotes/commas), and run
the i18n/JSON linter to verify no formatting errors.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/i18n/locales/zh-TW.json`:
- Around line 3114-3213: The appended JSON block contains 98 keys that duplicate
earlier keys (examples: "每日签到", "签到失败", "订阅管理", "暂无订阅套餐"), which will silently
override prior translations; remove all duplicate entries in this block and
retain only genuinely new translation keys introduced by this feature, ensuring
keys like "每日签到", "签到失败", "订阅管理", "暂无订阅套餐" (and any other exact-string
duplicates) are not redefined here so earlier definitions remain authoritative.
🪄 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: e1a55092-ef8b-426a-ad91-93a8273c7339
📒 Files selected for processing (6)
web/src/i18n/locales/en.jsonweb/src/i18n/locales/fr.jsonweb/src/i18n/locales/ja.jsonweb/src/i18n/locales/ru.jsonweb/src/i18n/locales/vi.jsonweb/src/i18n/locales/zh-TW.json
✅ Files skipped from review due to trivial changes (4)
- web/src/i18n/locales/en.json
- web/src/i18n/locales/ja.json
- web/src/i18n/locales/fr.json
- web/src/i18n/locales/vi.json
🚧 Files skipped from review as they are similar to previous changes (1)
- web/src/i18n/locales/ru.json
合并官方 main 时在尾部追加的翻译块包含大量已存在的 key。 移除所有重复条目,仅保留 5 个功能新增 key(到期/无订阅/订阅中/订阅信息/选择订阅) 及官方新增的 1 个 key。
|
希望能合并 |
|
请求通过PR |
51fdfc5 to
2b6f1df
Compare
📝 变更描述 / Description
管理员的用户管理页面新增「订阅信息」列,直观展示每个用户的活跃订阅状态,并支持按订阅套餐筛选用户。
痛点: 管理员此前无法在用户列表中快速了解用户的订阅状态,需要逐个点击进入订阅管理弹窗才能查看。
后端:
model/subscription.go:新增GetActiveSubscriptionsByUserIds批量查询方法,一次 SQL 返回多个用户的全部活跃订阅,避免 N+1 查询controller/subscription.go:新增AdminBatchActiveSubscriptions接口,接收user_ids数组(上限 100),返回批量订阅数据router/api-router.go:注册POST /api/subscription/admin/users/batch_active_subscriptionsmodel/user.go:SearchUsers新增可选参数planId,通过子查询筛选持有指定活跃订阅的用户controller/user.go:SearchUsers解析plan_idquery 参数,统一返回用户友好错误消息前端:
useUsersData.jsx:useRef竞态保护(用户列表 + 订阅请求均受保护)null(加载中/错误)和{}(数据已加载)状态URLSearchParams编码搜索参数UsersFilters.jsx:新增「选择订阅」下拉筛选框UsersColumnDefs.jsx:-,加载完成后无订阅显示「无订阅」i18n:
选择订阅、无订阅、到期、订阅中、订阅信息🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
N/A
✅ 提交前检查项 / Checklist
📸 运行证明 / Proof of Work
Summary by CodeRabbit
New Features
UI / UX
Localization