浏览代码

feat:添加文章排序字段

wangsisi 9 小时之前
父节点
当前提交
177da27d6a
共有 3 个文件被更改,包括 31 次插入2 次删除
  1. 17 0
      migrations/20250918075708-add-sort-order-to-articles.js
  2. 1 0
      models/article.js
  3. 13 2
      routes/admin/articles.js

+ 17 - 0
migrations/20250918075708-add-sort-order-to-articles.js

@@ -0,0 +1,17 @@
+'use strict';
+
+/** @type {import('sequelize-cli').Migration} */
+module.exports = {
+  async up (queryInterface, Sequelize) {
+    await queryInterface.addColumn('articles', 'sortOrder', {
+      type: Sequelize.INTEGER,
+      allowNull: true,
+      defaultValue: 0,
+      comment: '文章排序字段,用于置顶文章的排序'
+    });
+  },
+
+  async down (queryInterface, Sequelize) {
+    await queryInterface.removeColumn('articles', 'sortOrder');
+  }
+};

+ 1 - 0
models/article.js

@@ -43,6 +43,7 @@ module.exports = (sequelize, DataTypes) => {
     crop:DataTypes.INTEGER,
     crop:DataTypes.INTEGER,
     isRecommended:DataTypes.INTEGER,
     isRecommended:DataTypes.INTEGER,
     topPosition:DataTypes.INTEGER,
     topPosition:DataTypes.INTEGER,
+    sortOrder:DataTypes.INTEGER,
     subtitle:DataTypes.TEXT,
     subtitle:DataTypes.TEXT,
     seoKeyword:DataTypes.TEXT,
     seoKeyword:DataTypes.TEXT,
     seoDescription:DataTypes.TEXT,
     seoDescription:DataTypes.TEXT,

+ 13 - 2
routes/admin/articles.js

@@ -36,6 +36,7 @@ const {Op} = require('sequelize')
  * @apiSuccess {Number} data.articles.crop 作物分类ID
  * @apiSuccess {Number} data.articles.crop 作物分类ID
  * @apiSuccess {Number} data.articles.isRecommended 是否推荐(0-不推荐,1-推荐)
  * @apiSuccess {Number} data.articles.isRecommended 是否推荐(0-不推荐,1-推荐)
  * @apiSuccess {Number} data.articles.topPosition 是否置顶(0-不置顶,1-置顶)
  * @apiSuccess {Number} data.articles.topPosition 是否置顶(0-不置顶,1-置顶)
+ * @apiSuccess {Number} data.articles.sortOrder 文章排序字段,用于置顶文章的排序
  * @apiSuccess {String} [data.articles.seoKeyword] SEO关键词
  * @apiSuccess {String} [data.articles.seoKeyword] SEO关键词
  * @apiSuccess {String} [data.articles.seoDescription] SEO描述
  * @apiSuccess {String} [data.articles.seoDescription] SEO描述
  * @apiSuccess {Date} data.articles.createdAt 创建时间
  * @apiSuccess {Date} data.articles.createdAt 创建时间
@@ -70,6 +71,7 @@ const {Op} = require('sequelize')
  *         "crop": 43,
  *         "crop": 43,
  *         "isRecommended": 1,
  *         "isRecommended": 1,
  *         "topPosition": 0,
  *         "topPosition": 0,
+ *         "sortOrder": 1,
  *         "seoKeyword": null,
  *         "seoKeyword": null,
  *         "seoDescription": null,
  *         "seoDescription": null,
  *         "createdAt": "2025-09-14T09:21:24.000Z",
  *         "createdAt": "2025-09-14T09:21:24.000Z",
@@ -114,13 +116,14 @@ router.get('/', async function(req, res, next) {
         const condition = {
         const condition = {
             order:[
             order:[
                 ['topPosition', 'DESC'], // 置顶文章优先(1在前,0在后)
                 ['topPosition', 'DESC'], // 置顶文章优先(1在前,0在后)
-                ['updatedAt', 'DESC']    // 然后按更新时间倒序
+                ['sortOrder', 'ASC'],    // 置顶文章按sortOrder升序排序
+                ['updatedAt', 'DESC']    // 非置顶文章按更新时间倒序
             ],
             ],
             limit:pageSize,
             limit:pageSize,
             offset,
             offset,
             attributes: [
             attributes: [
                 'id', 'title', 'subtitle', 'content', 'type', 'img', 'date', 
                 'id', 'title', 'subtitle', 'content', 'type', 'img', 'date', 
-                'author', 'category', 'crop', 'isRecommended', 'topPosition', 'seoKeyword', 
+                'author', 'category', 'crop', 'isRecommended', 'topPosition', 'sortOrder', 'seoKeyword', 
                 'seoDescription', 'createdAt', 'updatedAt'
                 'seoDescription', 'createdAt', 'updatedAt'
             ],
             ],
             include: [{
             include: [{
@@ -261,6 +264,7 @@ router.get('/', async function(req, res, next) {
  * @apiSuccess {Number} data.crop 作物分类ID
  * @apiSuccess {Number} data.crop 作物分类ID
  * @apiSuccess {Number} data.isRecommended 是否推荐(0-不推荐,1-推荐)
  * @apiSuccess {Number} data.isRecommended 是否推荐(0-不推荐,1-推荐)
  * @apiSuccess {Number} data.topPosition 是否置顶(0-不置顶,1-置顶)
  * @apiSuccess {Number} data.topPosition 是否置顶(0-不置顶,1-置顶)
+ * @apiSuccess {Number} data.sortOrder 文章排序字段,用于置顶文章的排序
  * @apiSuccess {String} [data.seoKeyword] SEO关键词
  * @apiSuccess {String} [data.seoKeyword] SEO关键词
  * @apiSuccess {String} [data.seoDescription] SEO描述
  * @apiSuccess {String} [data.seoDescription] SEO描述
  * @apiSuccess {Date} data.createdAt 创建时间
  * @apiSuccess {Date} data.createdAt 创建时间
@@ -289,6 +293,7 @@ router.get('/', async function(req, res, next) {
  *     "crop": 43,
  *     "crop": 43,
  *     "isRecommended": 1,
  *     "isRecommended": 1,
  *     "topPosition": 0,
  *     "topPosition": 0,
+ *     "sortOrder": 1,
  *     "seoKeyword": null,
  *     "seoKeyword": null,
  *     "seoDescription": null,
  *     "seoDescription": null,
  *     "createdAt": "2025-09-14T09:21:24.000Z",
  *     "createdAt": "2025-09-14T09:21:24.000Z",
@@ -363,6 +368,7 @@ router.get('/:id', async function(req, res, next) {
  * @apiParam {Number} [crop] 作物分类ID
  * @apiParam {Number} [crop] 作物分类ID
  * @apiParam {Number} [isRecommended=0] 是否推荐,0-不推荐,1-推荐
  * @apiParam {Number} [isRecommended=0] 是否推荐,0-不推荐,1-推荐
  * @apiParam {Number} [topPosition=0] 是否置顶,0-不置顶,1-置顶
  * @apiParam {Number} [topPosition=0] 是否置顶,0-不置顶,1-置顶
+ * @apiParam {Number} [sortOrder=0] 文章排序字段,用于置顶文章的排序
  * @apiParam {String} [subtitle] 副标题(最大200字符)
  * @apiParam {String} [subtitle] 副标题(最大200字符)
  * @apiParam {String} [seoKeyword] SEO关键词
  * @apiParam {String} [seoKeyword] SEO关键词
  * @apiParam {String} [seoDescription] SEO描述
  * @apiParam {String} [seoDescription] SEO描述
@@ -382,6 +388,7 @@ router.get('/:id', async function(req, res, next) {
  * @apiSuccess {Number} data.crop 作物分类ID
  * @apiSuccess {Number} data.crop 作物分类ID
  * @apiSuccess {Number} data.isRecommended 是否推荐
  * @apiSuccess {Number} data.isRecommended 是否推荐
  * @apiSuccess {Number} data.topPosition 是否置顶
  * @apiSuccess {Number} data.topPosition 是否置顶
+ * @apiSuccess {Number} data.sortOrder 文章排序字段
  * @apiSuccess {String} [data.seoKeyword] SEO关键词
  * @apiSuccess {String} [data.seoKeyword] SEO关键词
  * @apiSuccess {String} [data.seoDescription] SEO描述
  * @apiSuccess {String} [data.seoDescription] SEO描述
  * @apiSuccess {Date} data.createdAt 创建时间
  * @apiSuccess {Date} data.createdAt 创建时间
@@ -405,6 +412,7 @@ router.get('/:id', async function(req, res, next) {
  *     "crop": 43,
  *     "crop": 43,
  *     "isRecommended": 1,
  *     "isRecommended": 1,
  *     "topPosition": 0,
  *     "topPosition": 0,
+ *     "sortOrder": 1,
  *     "seoKeyword": null,
  *     "seoKeyword": null,
  *     "seoDescription": null,
  *     "seoDescription": null,
  *     "createdAt": "2025-09-14T09:21:49.333Z",
  *     "createdAt": "2025-09-14T09:21:49.333Z",
@@ -597,6 +605,7 @@ router.delete('/:id', async function(req, res, next) {
  * @apiParam {Number} [crop] 作物分类ID
  * @apiParam {Number} [crop] 作物分类ID
  * @apiParam {Number} [isRecommended] 是否推荐,0-不推荐,1-推荐
  * @apiParam {Number} [isRecommended] 是否推荐,0-不推荐,1-推荐
  * @apiParam {Number} [topPosition] 是否置顶,0-不置顶,1-置顶
  * @apiParam {Number} [topPosition] 是否置顶,0-不置顶,1-置顶
+ * @apiParam {Number} [sortOrder] 文章排序字段,用于置顶文章的排序
  * @apiParam {String} [subtitle] 副标题(最大200字符)
  * @apiParam {String} [subtitle] 副标题(最大200字符)
  * @apiParam {String} [seoKeyword] SEO关键词
  * @apiParam {String} [seoKeyword] SEO关键词
  * @apiParam {String} [seoDescription] SEO描述
  * @apiParam {String} [seoDescription] SEO描述
@@ -623,6 +632,7 @@ router.delete('/:id', async function(req, res, next) {
  *     "crop": 43,
  *     "crop": 43,
  *     "isRecommended": 1,
  *     "isRecommended": 1,
  *     "topPosition": 0,
  *     "topPosition": 0,
+ *     "sortOrder": 1,
  *     "seoKeyword": null,
  *     "seoKeyword": null,
  *     "seoDescription": null,
  *     "seoDescription": null,
  *     "createdAt": "2025-09-14T09:21:49.333Z",
  *     "createdAt": "2025-09-14T09:21:49.333Z",
@@ -745,6 +755,7 @@ function filterBody(req){
             crop: req.body.crop !== undefined ? parseInt(req.body.crop) : null,
             crop: req.body.crop !== undefined ? parseInt(req.body.crop) : null,
             isRecommended: req.body.isRecommended !== undefined ? parseInt(req.body.isRecommended) : 0,
             isRecommended: req.body.isRecommended !== undefined ? parseInt(req.body.isRecommended) : 0,
             topPosition: req.body.topPosition !== undefined ? parseInt(req.body.topPosition) : 0,
             topPosition: req.body.topPosition !== undefined ? parseInt(req.body.topPosition) : 0,
+            sortOrder: req.body.sortOrder !== undefined ? parseInt(req.body.sortOrder) : 0,
             subtitle: req.body.subtitle ? String(req.body.subtitle).trim() : null,
             subtitle: req.body.subtitle ? String(req.body.subtitle).trim() : null,
             seoKeyword: req.body.seoKeyword ? String(req.body.seoKeyword).trim() : null,
             seoKeyword: req.body.seoKeyword ? String(req.body.seoKeyword).trim() : null,
             seoDescription: req.body.seoDescription ? String(req.body.seoDescription).trim() : null
             seoDescription: req.body.seoDescription ? String(req.body.seoDescription).trim() : null