日期:2026-09-04 状态:已批准(用户确认:不做独立置顶,仅使用精选 + 列表内上移/下移)
首页「推荐阅读」与知识中心「书册式推荐」当前展示的内容由 is_featured=1(精选)决定,但顺序完全由 published_at 决定,运营无法控制哪些精选文章排在前面。
目标:让运营在管理端定义精选文章在首页/知识中心的展示顺序,从而做到"首页放的内容可定义"。
| 方法 | 位置 | 当前排序逻辑 |
|---|---|---|
getFeatured() |
ArticleService.java:125 |
WHERE status=published AND is_featured=1 ORDER BY published_at DESC LIMIT 50 |
getPublicList() |
ArticleService.java:86 |
WHERE status=published ORDER BY is_featured DESC, published_at DESC |
toggleFeatured() |
ArticleService.java:542 |
直接写 is_featured,无顺序关联 |
管理端列表 getAdminList() |
ArticleService.java:386 |
ORDER BY created_at DESC + SortUtil.applySort |
ArticleManage.vue:文章管理列表,发布中的行下拉菜单已有「精选/取消精选」api/article.js:已有 adminArticleToggleFeaturedPOST /api/admin/articles/toggle-featuredgetFeaturedArticles({ size: 5 }) → getFeaturedgetFeaturedArticles({ size: 8 }) → getFeaturedis_featured 精选标记"要上首页的内容"articles 表新增一列:
sort_order INT NOT NULL DEFAULT 0 COMMENT '推荐排序值(越小越靠前)'
DatabaseInitializer.runMigrations(),使用 ensureColumn("articles", "sort_order", "INT NOT NULL DEFAULT 0 COMMENT '推荐排序值(越小越靠前)'")(幂等)schema.sql 的 CREATE TABLE articles(在 is_featured 后加 sort_order 列)Article.java新增字段:
/** 推荐排序值(越小越靠前,仅精选文章参与排序) */
private Integer sortOrder;
ArticleService.getFeatured()排序由 ORDER BY published_at DESC 改为:
ORDER BY sort_order ASC, published_at DESC
效果:精选文章顺序由运营的 sort_order 控制,未显式设置过 sort_order 的精选(默认 0)排在前面,与新精选的追加逻辑配合避免"新精选抢占首位"。
ArticleService.getPublicList()排序由 ORDER BY is_featured DESC, published_at DESC 改为:
ORDER BY is_featured DESC, sort_order ASC, published_at DESC
效果:知识中心"全部"列表中精选文章也按运营顺序靠前排布。
ArticleService.toggleFeatured()设为精选(isFeatured=1)时,自动将 sort_order 置为 当前最大 sort_order + 1,使新精选追加到队列末尾,不抢占既有顺序。
if (isFeatured == 1) {
// 新精选追加到队尾,不抢占已有顺序
Integer maxSort = getMaxSortOrder();
article.setSortOrder((maxSort == null ? 0 : maxSort) + 1);
}
辅助方法 getMaxSortOrder():查询 articles WHERE status='published' AND is_featured=1 的 MAX(sort_order)。
POST /api/admin/articles/move在 AdminArticleController 新增:
POST /api/admin/articles/move
body: { id: Long, direction: 'up' | 'down' }
逻辑(ArticleService.moveSortOrder(id, direction)):
sort_order ASC, published_at ASC 排序(注意:与展示序一致取 ASC,保证交换后顺序与前端一致)idxdirection=up 且 idx <= 0 → 返回「已在最前面」;direction=down 且 idx >= len-1 → 返回「已在最后面」sort_order = 1..N(消除并列与空洞,保证顺序绝对稳定)实现细节:交换相邻的 sort_order 可能出现并列——为防止并列排序不稳定,执行重编号(步骤 5)。
响应:Result<String>,边界返回 Result.error(400, "已在最前/最后")。
api/article.js新增:
export function adminArticleMove(data) {
return request({ url: '/api/admin/articles/move', method: 'post', data })
}
ArticleManage.vuerow.isFeatured === 1 时增加「上移」「下移」菜单项(或行内小按钮)row.status === 'published' && row.isFeatured === 1 时显示上移/下移adminArticleMove({ id, direction }) 成功后刷新列表精选 ①(用 sortOrder 换算:非零序号直接显示,0 显示为队尾追加的未明确序号可不显示或显示 精选)边界:非精选文章不显示上移/下移;列表排序按 created_at DESC 展示,与推荐顺序无关(运营通过按钮逐个调整)。
无需改动。首页「推荐阅读」与知识中心书册数据均由 getFeatured() 返回,顺序自动受控。
运营在管理端点「上移/下移」
→ POST /api/admin/articles/move {id, direction}
→ ArticleService.moveSortOrder() 交换 + 重编号 sort_order=1..N
→ 小程序 getFeatured() 按 sort_order ASC 返回
→ 首页「推荐阅读」/知识中心书册顺序更新
| 场景 | 处理 |
|---|---|
| 目标文章不是精选 | move 拒绝:「仅精选文章可调整顺序」 |
| 已是最前/最后 | 返回 400 提示「已在最前面/最后面」 |
| 文章不存在 | 返回 400「文章不存在」 |
| 方向非法 | 返回 400「direction 只能是 up/down」 |
| 并发操作 | 事务 + 重编号天然收敛(最后一次操作者胜出) |
| 新设精选 | 自动追加队尾,不抢占 |
mvn clean compile 通过npm run build 通过runMigrations() 不报错| 文件 | 改动 |
|---|---|
cfc-backend/.../config/DatabaseInitializer.java |
迁移293 加 sort_order 列 |
cfc-backend/.../resources/schema.sql |
articles 建表加 sort_order 列 |
cfc-backend/.../entity/Article.java |
加 sortOrder 字段 |
cfc-backend/.../service/ArticleService.java |
getFeatured/getPublicList 排序、toggleFeatured 追加队尾、新增 moveSortOrder + getMaxSortOrder |
cfc-backend/.../controller/admin/AdminArticleController.java |
新增 POST /move |
cfc-web/src/api/article.js |
新增 adminArticleMove |
cfc-web/src/views/admin/ArticleManage.vue |
上移/下移按钮 + 精选序号展示 |
docs/superpowers/api/API_REFERENCE.md |
同步记录 /move 接口 |