Skip to content

feat: model display order - #5090

Open
l1i1 wants to merge 5 commits into
QuantumNous:mainfrom
l1i1:feat/model-display-order
Open

feat: model display order#5090
l1i1 wants to merge 5 commits into
QuantumNous:mainfrom
l1i1:feat/model-display-order

Conversation

@l1i1

@l1i1 l1i1 commented May 25, 2026

Copy link
Copy Markdown

📝 变更描述 / Description

为模型元数据增加展示排序能力,管理员可以在模型管理中设置模型是否置顶以及展示顺序。

后端为模型元数据新增 pinneddisplay_order 字段,并统一按 pinned DESC, display_order ASC, id DESC 排序。模型广场、用户模型列表和管理端模型列表都会基于同一套模型元数据排序,因此配置后可以稳定影响前台展示顺序。

前端在新版和经典版模型管理页面都增加了置顶与展示顺序配置入口,并提供仅更新排序字段的接口路径,避免行内修改排序时影响模型的其他元数据。

🚀 变更类型 / Type of change

  • 🐛 Bug 修复 (Bug fix)
  • ✨ 新功能 (New feature)
  • ⚡ 性能优化 / 重构 (Refactor)
  • 📝 文档更新 (Documentation)

🔗 关联任务 / Related Issue

✅ 提交前检查项 / Checklist

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

📸 运行证明 / Proof of Work

c449a08c-f7e2-407f-b013-6bcdeef415c2 c9b459fe-3ca7-4fa8-b85d-c11024cef703 2e69b193-c77d-46cc-a12d-526ceb2ef478 791fdbf4-79b5-4795-a9bc-54eb7943627a

Summary by CodeRabbit

  • New Features

    • Pin models to show them first in listings.
    • Assign a custom numeric display order per model.
    • Pricing and model lists now honor pinning + display order with a new "Default Order" sort option.
    • New UI controls for toggling pin and editing display order (inline editors, switches, admin table, and edit/create forms) with immediate feedback.
  • Documentation / Localization

    • Added translations and labels for pinning, display order, default sorting, and related messages.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Adds display ordering and pinning across backend model schema, a new order-only UpdateModelMeta branch, pricing ordering, frontend forms and interactive table controls, default sort changes, and translation keys; listing responses and pricing now use the new metadata ordering.

Changes

Model Ordering & Pinning

Layer / File(s) Summary
Backend Model Schema & Persistence
model/model_meta.go
Model struct gains DisplayOrder and Pinned. NormalizeOrdering() clamps display_order >= 0 and normalizes pinned to 1 when non-zero. Insert()/Update() call normalizer and explicitly persist these fields. Listing queries use pinned DESC, display_order ASC, id DESC.
Backend API Endpoints & Listing Ordering
controller/model_meta.go, controller/user.go
UpdateModelMeta gains order_only=true branch: validates id, normalizes display_order/pinned, requires at least one field, updates via map (adds updated_time), refreshes pricing, and returns { id }. GetUserModels builds orderedModels from meta (pinned/display_order/id) with fallback to original models.
Pricing Response Enhancement & Ordering
model/pricing.go
Pricing adds id, display_order, pinned; pricing items copy these from meta for enabled models. pricingMap is deterministically sorted by Pinned DESC, DisplayOrder ASC, Id DESC before cache/version steps.
Frontend Model Types & Form Schema
web/default/src/features/models/types.ts, web/default/src/features/models/lib/model-form.ts
Frontend Model type adds optional display_order & pinned. modelFormSchema adds display_order (default 0) and pinned (default false). Transformers map defaults and convert pinned boolean ↔ 1/0.
UI Model Management Forms & Interactive Columns
web/classic/src/components/table/models/modals/EditModelModal.jsx, web/classic/src/components/table/models/ModelsColumnDefs.jsx, web/default/src/features/models/components/drawers/model-mutate-drawer.tsx, web/default/src/features/models/components/models-columns.tsx
Edit/Create forms include pinned switch and display_order input with defaults and coercion. Classic ModelsColumnDefs adds updateModelOrdering and two columns (Switch, InputNumber). Default UI adds PinnedCell and DisplayOrderCell interactive components with validation/keyboard handling, wired to update actions.
Model Ordering API & State Management
web/default/src/features/models/api.ts, web/default/src/features/models/lib/model-actions.ts
updateModelOrdering issues PUT /api/models/?order_only=true. handleUpdateModelField calls it, shows success/error toasts, and invalidates modelsQueryKeys.lists() on success.
Pricing Filter & Sort Integration
web/default/src/features/pricing/constants.ts, web/default/src/features/pricing/hooks/use-filters.ts, web/default/src/features/pricing/lib/filters.ts, web/default/src/features/pricing/types.ts
Adds SORT_OPTIONS.DEFAULT, uses it as default/clear sentinel, and implements default sort to order pinned first, then display_order ascending, then id descending. PricingModel now includes display_order and pinned.
Internationalization
web/default/src/i18n/locales/en.json, web/classic/src/i18n/locales/*
Adds translation keys for Default Order, Display Order (+ help), Pin/Unpin labels and helper text, and Failed to update model across locales.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • Calcium-Ion
  • creamlike1024

Poem

🐰
I hop and nudge the listing bright,
Pins set to keep favorites in sight,
Numbers march from small to high,
Switch flips quick — watch order fly,
Plaza neat beneath moonlight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.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 'feat: model display order' clearly and concisely summarizes the main change of adding display order controls to model metadata.
Linked Issues check ✅ Passed All coding requirements from #2221 are met: backend adds pinned/display_order fields with DESC/ASC ordering, frontend adds pin and ordering controls for marketplace/admin management.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing model display ordering: backend sorting, frontend UI controls, form schema updates, and i18n translations—no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feat/model-display-order

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 and usage tips.

@l1i1 l1i1 changed the title Feat/model display order feat:model display order May 25, 2026
@l1i1 l1i1 changed the title feat:model display order feat: model display order May 25, 2026

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

🧹 Nitpick comments (1)
web/default/src/i18n/locales/en.json (1)

1097-1097: ⚡ Quick win

Use hierarchical i18n keys for the newly added entries.

These new keys are sentence-style ("Default Order", "Pin model", etc.) instead of hierarchical keys, which breaks the i18n key naming rule and makes namespace management harder over time. Please convert them to semantically grouped keys (for example under models.ordering.*) and update usages accordingly.

As per coding guidelines: "Use hierarchical and semantically clear translation key names such as dashboard.overview.title and maintain naming consistency".

Also applies to: 1227-1228, 1659-1659, 2897-2899, 4124-4124

🤖 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/i18n/locales/en.json` at line 1097, The new flat
sentence-style i18n keys like "Default Order" and "Pin model" violate the
project's hierarchical naming rules; rename them into semantically grouped keys
(for example move "Default Order" to models.ordering.defaultOrder and "Pin
model" to models.pin.label or similar under a consistent namespace like models.*
or models.ordering.*), update the en.json entries accordingly, and update all
code usages where the string keys are referenced to use the new hierarchical
keys (search for exact keys "Default Order", "Pin model" and the other
occurrences at the specified ranges and replace with the new names). Ensure
naming follows existing patterns (e.g., models.ordering.*) and keep translations
unchanged when migrating values.
🤖 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 `@controller/model_meta.go`:
- Around line 148-154: The update call using
model.DB.Model(&model.Model{}).Where("id = ?", req.Id).Updates(updates) can
succeed with zero rows affected and you still call model.RefreshPricing() and
common.ApiSuccess, causing a false-positive; change this to capture the GORM
result (e.g., res := model.DB.Model(&model.Model{}).Where("id = ?",
req.Id).Updates(updates)), check res.Error first and return common.ApiError on
error, then if res.RowsAffected == 0 return a not-found error via
common.ApiError (or a 404 response) instead of calling model.RefreshPricing()
and common.ApiSuccess, otherwise proceed to call model.RefreshPricing() and
common.ApiSuccess with req.Id.

In `@model/pricing.go`:
- Around line 350-359: The current sort.Slice comparator for pricingMap
(comparing Pinned, DisplayOrder, Id) can leave ties when Pinned, DisplayOrder
and Id are equal, causing nondeterministic ordering; update the sort to be
deterministic by either switching to sort.SliceStable or extending the
comparator in sort.Slice to include a final deterministic tie-breaker (e.g.,
compare ModelName or another stable string/unique field) after Id so equal-key
entries always sort the same; reference the existing sort.Slice call and the
pricingMap elements' fields Pinned, DisplayOrder, Id and ModelName (or another
unique field) when making the change.

In `@web/classic/src/components/table/models/modals/EditModelModal.jsx`:
- Around line 542-547: The numeric input for the display_order field
(Form.InputNumber with field='display_order') currently permits negative values;
add min={0} to the Form.InputNumber to prevent negative entry and also
normalize/coerce the value to an integer >= 0 right before submit (in the form
submission handler that maps/creates the payload for saving, e.g., the submit
mapping or onFinish handler) to ensure negative or fractional values cannot be
written to the backend. Ensure you update both occurrences mentioned (the modal
at the shown diff and the other occurrence around lines 205-206) and reference
the 'display_order' field when normalizing.

In `@web/classic/src/components/table/models/ModelsColumnDefs.jsx`:
- Around line 354-357: The InputNumber is using defaultValue, which makes it
uncontrolled and stale; change it to a controlled input by replacing
defaultValue with value (bind it to the current row field, e.g.
value={row.someOrderField ?? 0}) and wire its onChange to the table/row update
handler (call the existing cell update function or dispatch
setRowData/updateRecord from ModelsColumnDefs) so edits update the underlying
data model; remove defaultValue and ensure the value prop reflects the latest
row data.

In `@web/default/src/features/models/components/drawers/model-mutate-drawer.tsx`:
- Line 92: The drawer's Zod schema currently declares display_order as
z.number(), allowing negatives and non-integers; update the schema in
model-mutate-drawer.tsx to require an integer >= 0 (e.g., use Zod integer +
nonnegative/min) for the display_order field, and ensure any form-level
validation/error messages and default values that reference display_order are
consistent with the new constraints so the form cannot submit invalid orders.

In `@web/default/src/features/models/lib/model-form.ts`:
- Line 43: The Zod schema for the model form allows negative/decimal
display_order values; update the schema entry for display_order (the property
named display_order in the Zod schema in model-form.ts) to enforce integers and
non-negativity by chaining the appropriate Zod validators (use .int() and
.min(0)) before .default(0) so invalid ordering values are rejected at schema
validation time.

---

Nitpick comments:
In `@web/default/src/i18n/locales/en.json`:
- Line 1097: The new flat sentence-style i18n keys like "Default Order" and "Pin
model" violate the project's hierarchical naming rules; rename them into
semantically grouped keys (for example move "Default Order" to
models.ordering.defaultOrder and "Pin model" to models.pin.label or similar
under a consistent namespace like models.* or models.ordering.*), update the
en.json entries accordingly, and update all code usages where the string keys
are referenced to use the new hierarchical keys (search for exact keys "Default
Order", "Pin model" and the other occurrences at the specified ranges and
replace with the new names). Ensure naming follows existing patterns (e.g.,
models.ordering.*) and keep translations unchanged when migrating values.
🪄 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: 59eb9528-adbd-4d22-a258-1b1256d233cb

📥 Commits

Reviewing files that changed from the base of the PR and between 583da45 and 2d99f3f.

📒 Files selected for processing (17)
  • controller/model_meta.go
  • controller/user.go
  • model/model_meta.go
  • model/pricing.go
  • web/classic/src/components/table/models/ModelsColumnDefs.jsx
  • web/classic/src/components/table/models/modals/EditModelModal.jsx
  • web/default/src/features/models/api.ts
  • web/default/src/features/models/components/drawers/model-mutate-drawer.tsx
  • web/default/src/features/models/components/models-columns.tsx
  • web/default/src/features/models/lib/model-actions.ts
  • web/default/src/features/models/lib/model-form.ts
  • web/default/src/features/models/types.ts
  • web/default/src/features/pricing/constants.ts
  • web/default/src/features/pricing/hooks/use-filters.ts
  • web/default/src/features/pricing/lib/filters.ts
  • web/default/src/features/pricing/types.ts
  • web/default/src/i18n/locales/en.json

Comment thread controller/model_meta.go Outdated
Comment thread model/pricing.go
Comment thread web/classic/src/components/table/models/modals/EditModelModal.jsx
Comment on lines +354 to +357
<InputNumber
size='small'
defaultValue={value ?? 0}
step={1}

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

InputNumber should be controlled to avoid stale table values.

defaultValue is only applied on first mount; after refresh or external updates, the cell can display outdated order. Bind value to current row data instead.

Suggested patch
-          <InputNumber
+          <InputNumber
             size='small'
-            defaultValue={value ?? 0}
+            value={value ?? 0}
             step={1}
🤖 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/classic/src/components/table/models/ModelsColumnDefs.jsx` around lines
354 - 357, The InputNumber is using defaultValue, which makes it uncontrolled
and stale; change it to a controlled input by replacing defaultValue with value
(bind it to the current row field, e.g. value={row.someOrderField ?? 0}) and
wire its onChange to the table/row update handler (call the existing cell update
function or dispatch setRowData/updateRecord from ModelsColumnDefs) so edits
update the underlying data model; remove defaultValue and ensure the value prop
reflects the latest row data.

Comment thread web/default/src/features/models/components/drawers/model-mutate-drawer.tsx Outdated
Comment thread web/default/src/features/models/lib/model-form.ts Outdated

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

🤖 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 `@controller/model_meta.go`:
- Around line 148-154: The handler UpdateModelMeta should not assume
res.RowsAffected == 0 means "模型不存在"; instead, after calling
model.DB.Model(&model.Model{}).Where("id = ?", req.Id).Updates(updates), if
res.Error == nil and res.RowsAffected == 0 then explicitly verify existence
(e.g., query model.DB.First/Select EXISTS for model.Model with id == req.Id) and
only call common.ApiErrorMsg(c, "模型不存在") if that existence check fails;
otherwise treat it as a no-op success (return 200) or respond accordingly.
Ensure you reference UpdateModelMeta, the res from
model.DB.Model(&model.Model{}).Where(...).Updates(updates), and
common.ApiErrorMsg in the fix.
🪄 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: f3ddff59-6d81-4518-9ca4-338388fb47c0

📥 Commits

Reviewing files that changed from the base of the PR and between 900e718 and 56cb668.

📒 Files selected for processing (10)
  • controller/model_meta.go
  • model/pricing.go
  • web/classic/src/components/table/models/ModelsColumnDefs.jsx
  • web/classic/src/components/table/models/modals/EditModelModal.jsx
  • web/default/src/features/models/components/drawers/model-mutate-drawer.tsx
  • web/default/src/features/models/components/models-columns.tsx
  • web/default/src/features/models/lib/model-actions.ts
  • web/default/src/features/models/lib/model-form.ts
  • web/default/src/features/pricing/constants.ts
  • web/default/src/i18n/locales/en.json
✅ Files skipped from review due to trivial changes (1)
  • web/default/src/i18n/locales/en.json

Comment thread controller/model_meta.go Outdated

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

🤖 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 `@controller/model_meta.go`:
- Around line 155-157: The code currently conflates DB query errors with a
not-found result when calling model.DB.Model(&model.Model{}).Where("id = ?",
req.Id).Count(&count).Error and then returns common.ApiErrorMsg(c, "模型不存在");
change this to first check if err != nil and handle it as a DB/internal error
(e.g., call common.ApiErrorMsg with an internal error message or the err
details) and only if err == nil and count == 0 return the not-found message
"模型不存在"; update the error-handling branches around Count(&count).Error, keeping
the same req.Id/Model references and using common.ApiErrorMsg for each distinct
case.
🪄 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: 23b70c09-c907-4ab4-aaf2-98216a4f7e60

📥 Commits

Reviewing files that changed from the base of the PR and between 56cb668 and ddcae44.

📒 Files selected for processing (1)
  • controller/model_meta.go

Comment thread controller/model_meta.go
Comment on lines +155 to +157
if err := model.DB.Model(&model.Model{}).Where("id = ?", req.Id).Count(&count).Error; err != nil || count == 0 {
common.ApiErrorMsg(c, "模型不存在")
return

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not map DB count errors to “模型不存在”.

Line 155 currently treats query errors and true not-found as the same result. That can hide real DB failures and return a false 404-style message.

Suggested fix
-			if err := model.DB.Model(&model.Model{}).Where("id = ?", req.Id).Count(&count).Error; err != nil || count == 0 {
-				common.ApiErrorMsg(c, "模型不存在")
-				return
-			}
+			if err := model.DB.Model(&model.Model{}).Where("id = ?", req.Id).Count(&count).Error; err != nil {
+				common.ApiError(c, err)
+				return
+			}
+			if count == 0 {
+				common.ApiErrorMsg(c, "模型不存在")
+				return
+			}
🤖 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 `@controller/model_meta.go` around lines 155 - 157, The code currently
conflates DB query errors with a not-found result when calling
model.DB.Model(&model.Model{}).Where("id = ?", req.Id).Count(&count).Error and
then returns common.ApiErrorMsg(c, "模型不存在"); change this to first check if err
!= nil and handle it as a DB/internal error (e.g., call common.ApiErrorMsg with
an internal error message or the err details) and only if err == nil and count
== 0 return the not-found message "模型不存在"; update the error-handling branches
around Count(&count).Error, keeping the same req.Id/Model references and using
common.ApiErrorMsg for each distinct case.

@l1i1

l1i1 commented May 30, 2026

Copy link
Copy Markdown
Author

来个佬Review一下呗

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.

模型广场模型排列优化展示

2 participants