|
|
@@ -88,6 +88,7 @@ public class ArticleService {
|
|
|
LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<Article>()
|
|
|
.eq(Article::getStatus, "published")
|
|
|
.orderByDesc(Article::getIsFeatured)
|
|
|
+ .orderByAsc(Article::getSortOrder)
|
|
|
.orderByDesc(Article::getPublishedAt);
|
|
|
|
|
|
if (categoryId != null && categoryId > 0) {
|
|
|
@@ -126,6 +127,7 @@ public class ArticleService {
|
|
|
LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<Article>()
|
|
|
.eq(Article::getStatus, "published")
|
|
|
.eq(Article::getIsFeatured, 1)
|
|
|
+ .orderByAsc(Article::getSortOrder)
|
|
|
.orderByDesc(Article::getPublishedAt)
|
|
|
.last("LIMIT " + Math.max(size, 50));
|
|
|
|
|
|
@@ -544,9 +546,97 @@ public class ArticleService {
|
|
|
if (article == null) return;
|
|
|
article.setIsFeatured(isFeatured);
|
|
|
article.setUpdatedAt(new Date());
|
|
|
+ if (isFeatured == 1) {
|
|
|
+ // 设为精选时追加到队尾,不抢占既有顺序
|
|
|
+ Integer maxSort = getMaxSortOrder();
|
|
|
+ article.setSortOrder((maxSort == null ? 0 : maxSort) + 1);
|
|
|
+ }
|
|
|
articleMapper.updateById(article);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 查询已发布且精选的文章中最大的 sort_order;若没有精选文章返回 null。
|
|
|
+ */
|
|
|
+ private Integer getMaxSortOrder() {
|
|
|
+ LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<Article>()
|
|
|
+ .eq(Article::getStatus, "published")
|
|
|
+ .eq(Article::getIsFeatured, 1)
|
|
|
+ .select(Article::getSortOrder)
|
|
|
+ .orderByDesc(Article::getSortOrder)
|
|
|
+ .last("LIMIT 1");
|
|
|
+ Article top = articleMapper.selectOne(wrapper);
|
|
|
+ return top != null ? top.getSortOrder() : null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 上移/下移精选文章的推荐顺序。direction: 'up' 或 'down'。
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public String moveSortOrder(Long id, String direction) {
|
|
|
+ Article target = articleMapper.selectById(id);
|
|
|
+ if (target == null) {
|
|
|
+ throw new RuntimeException("文章不存在");
|
|
|
+ }
|
|
|
+ if (!"published".equals(target.getStatus()) || !Integer.valueOf(1).equals(target.getIsFeatured())) {
|
|
|
+ throw new RuntimeException("仅已发布且精选的文章可调整顺序");
|
|
|
+ }
|
|
|
+ if (!"up".equals(direction) && !"down".equals(direction)) {
|
|
|
+ throw new RuntimeException("direction 只能是 up 或 down");
|
|
|
+ }
|
|
|
+
|
|
|
+ LambdaQueryWrapper<Article> listWrapper = new LambdaQueryWrapper<Article>()
|
|
|
+ .eq(Article::getStatus, "published")
|
|
|
+ .eq(Article::getIsFeatured, 1)
|
|
|
+ .orderByAsc(Article::getSortOrder)
|
|
|
+ .orderByAsc(Article::getPublishedAt);
|
|
|
+ List<Article> featured = articleMapper.selectList(listWrapper);
|
|
|
+
|
|
|
+ int idx = -1;
|
|
|
+ for (int i = 0; i < featured.size(); i++) {
|
|
|
+ if (featured.get(i).getId().equals(id)) {
|
|
|
+ idx = i;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (idx == -1) {
|
|
|
+ throw new RuntimeException("文章不存在");
|
|
|
+ }
|
|
|
+ if ("up".equals(direction) && idx <= 0) {
|
|
|
+ throw new RuntimeException("已在最前面");
|
|
|
+ }
|
|
|
+ if ("down".equals(direction) && idx >= featured.size() - 1) {
|
|
|
+ throw new RuntimeException("已在最后面");
|
|
|
+ }
|
|
|
+
|
|
|
+ int swapIdx = "up".equals(direction) ? idx - 1 : idx + 1;
|
|
|
+
|
|
|
+ Article a = featured.get(idx);
|
|
|
+ Article b = featured.get(swapIdx);
|
|
|
+ Integer tmpSort = a.getSortOrder();
|
|
|
+ a.setSortOrder(b.getSortOrder());
|
|
|
+ b.setSortOrder(tmpSort);
|
|
|
+ a.setUpdatedAt(new Date());
|
|
|
+ b.setUpdatedAt(new Date());
|
|
|
+ articleMapper.updateById(a);
|
|
|
+ articleMapper.updateById(b);
|
|
|
+
|
|
|
+ // 全局重编号 1..N,消除并列与空洞
|
|
|
+ LambdaQueryWrapper<Article> renumWrapper = new LambdaQueryWrapper<Article>()
|
|
|
+ .eq(Article::getStatus, "published")
|
|
|
+ .eq(Article::getIsFeatured, 1)
|
|
|
+ .orderByAsc(Article::getSortOrder)
|
|
|
+ .orderByAsc(Article::getPublishedAt);
|
|
|
+ List<Article> sorted = articleMapper.selectList(renumWrapper);
|
|
|
+ Date now = new Date();
|
|
|
+ for (int i = 0; i < sorted.size(); i++) {
|
|
|
+ sorted.get(i).setSortOrder(i + 1);
|
|
|
+ sorted.get(i).setUpdatedAt(now);
|
|
|
+ articleMapper.updateById(sorted.get(i));
|
|
|
+ }
|
|
|
+
|
|
|
+ return "已调整顺序";
|
|
|
+ }
|
|
|
+
|
|
|
public ReadingStatsDTO getReadingStats(Long memberId) {
|
|
|
ReadingStatsDTO dto = new ReadingStatsDTO();
|
|
|
|