|
@@ -3,9 +3,96 @@ const router = express.Router();
|
|
|
const {Article, Category} = require('../../models')
|
|
|
const {Op} = require('sequelize')
|
|
|
|
|
|
-/*
|
|
|
- 查询文章列表
|
|
|
- GET /admin/articles
|
|
|
+/**
|
|
|
+ * @api {get} /admin/articles 查询文章列表
|
|
|
+ * @apiName GetArticles
|
|
|
+ * @apiGroup Articles
|
|
|
+ * @apiVersion 1.0.0
|
|
|
+ *
|
|
|
+ * @apiDescription 获取文章列表,支持分页和多条件筛选
|
|
|
+ *
|
|
|
+ * @apiParam {Number} [currentPage=1] 当前页码,默认为1
|
|
|
+ * @apiParam {Number} [pageSize=10] 每页显示数量,默认为10
|
|
|
+ * @apiParam {String} [title] 标题搜索关键词(模糊匹配)
|
|
|
+ * @apiParam {String|Array} [cropIds] 作物筛选ID,支持逗号分隔的多个ID或数组形式
|
|
|
+ * @apiParam {Number} [categoryId] 用户分类ID,根据用户传递的category参数值进行精确匹配
|
|
|
+ * @apiParam {Number} [isRecommended] 推荐筛选,0-非推荐文章,1-推荐文章
|
|
|
+ *
|
|
|
+ * @apiSuccess {Boolean} status 请求状态
|
|
|
+ * @apiSuccess {String} message 响应消息
|
|
|
+ * @apiSuccess {Object} data 响应数据
|
|
|
+ * @apiSuccess {Array} data.articles 文章列表
|
|
|
+ * @apiSuccess {Number} data.articles.id 文章ID
|
|
|
+ * @apiSuccess {String} data.articles.title 文章标题
|
|
|
+ * @apiSuccess {String} [data.articles.subtitle] 文章副标题
|
|
|
+ * @apiSuccess {String} data.articles.content 文章内容
|
|
|
+ * @apiSuccess {Number} data.articles.type 文章类型
|
|
|
+ * @apiSuccess {String} [data.articles.img] 文章图片
|
|
|
+ * @apiSuccess {Date} [data.articles.date] 文章日期
|
|
|
+ * @apiSuccess {String} [data.articles.author] 作者
|
|
|
+ * @apiSuccess {Number} data.articles.category 用户分类ID
|
|
|
+ * @apiSuccess {Number} data.articles.crop 作物分类ID
|
|
|
+ * @apiSuccess {Number} data.articles.isRecommended 是否推荐(0-不推荐,1-推荐)
|
|
|
+ * @apiSuccess {String} [data.articles.seoKeyword] SEO关键词
|
|
|
+ * @apiSuccess {String} [data.articles.seoDescription] SEO描述
|
|
|
+ * @apiSuccess {Date} data.articles.createdAt 创建时间
|
|
|
+ * @apiSuccess {Date} data.articles.updatedAt 更新时间
|
|
|
+ * @apiSuccess {Object} [data.articles.cropInfo] 作物信息
|
|
|
+ * @apiSuccess {Number} data.articles.cropInfo.id 作物ID
|
|
|
+ * @apiSuccess {String} data.articles.cropInfo.name 作物名称
|
|
|
+ * @apiSuccess {Number} data.articles.cropInfo.level 作物层级
|
|
|
+ * @apiSuccess {Number} data.articles.cropInfo.parentId 父级作物ID
|
|
|
+ * @apiSuccess {Object} data.pagination 分页信息
|
|
|
+ * @apiSuccess {Number} data.pagination.total 总数量
|
|
|
+ * @apiSuccess {Number} data.pagination.currentPage 当前页码
|
|
|
+ * @apiSuccess {Number} data.pagination.pageSize 每页数量
|
|
|
+ *
|
|
|
+ * @apiSuccessExample {json} Success-Response:
|
|
|
+ * HTTP/1.1 200 OK
|
|
|
+ * {
|
|
|
+ * "status": true,
|
|
|
+ * "message": "成功",
|
|
|
+ * "data": {
|
|
|
+ * "articles": [
|
|
|
+ * {
|
|
|
+ * "id": 96,
|
|
|
+ * "title": "测试文章",
|
|
|
+ * "subtitle": "副标题",
|
|
|
+ * "content": "<p>文章内容</p>",
|
|
|
+ * "type": 1,
|
|
|
+ * "img": null,
|
|
|
+ * "date": null,
|
|
|
+ * "author": "作者",
|
|
|
+ * "category": 1,
|
|
|
+ * "crop": 43,
|
|
|
+ * "isRecommended": 1,
|
|
|
+ * "seoKeyword": null,
|
|
|
+ * "seoDescription": null,
|
|
|
+ * "createdAt": "2025-09-14T09:21:24.000Z",
|
|
|
+ * "updatedAt": "2025-09-14T09:21:24.000Z",
|
|
|
+ * "cropInfo": {
|
|
|
+ * "id": 43,
|
|
|
+ * "name": "荔枝",
|
|
|
+ * "level": 2,
|
|
|
+ * "parentId": 40
|
|
|
+ * }
|
|
|
+ * }
|
|
|
+ * ],
|
|
|
+ * "pagination": {
|
|
|
+ * "total": 19,
|
|
|
+ * "currentPage": 1,
|
|
|
+ * "pageSize": 10
|
|
|
+ * }
|
|
|
+ * }
|
|
|
+ * }
|
|
|
+ *
|
|
|
+ * @apiErrorExample {json} Error-Response:
|
|
|
+ * HTTP/1.1 500 Internal Server Error
|
|
|
+ * {
|
|
|
+ * "status": false,
|
|
|
+ * "message": "失败",
|
|
|
+ * "errors": ["错误信息"]
|
|
|
+ * }
|
|
|
*/
|
|
|
router.get('/', async function(req, res, next) {
|
|
|
try {
|
|
@@ -26,9 +113,9 @@ router.get('/', async function(req, res, next) {
|
|
|
offset,
|
|
|
include: [{
|
|
|
model: Category,
|
|
|
- as: 'categoryInfo',
|
|
|
+ as: 'cropInfo',
|
|
|
attributes: ['id', 'name', 'level', 'parentId'],
|
|
|
- required: false // LEFT JOIN,即使没有分类也能返回文章
|
|
|
+ required: false // LEFT JOIN,即使没有作物也能返回文章
|
|
|
}]
|
|
|
}
|
|
|
|
|
@@ -42,29 +129,45 @@ router.get('/', async function(req, res, next) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 分类筛选 - 支持多选和包含子分类
|
|
|
- if(query.categoryIds){
|
|
|
- let categoryIds = [];
|
|
|
+ // 作物筛选 - 支持多选和包含子分类
|
|
|
+ if(query.cropIds){
|
|
|
+ let cropIds = [];
|
|
|
|
|
|
- // 处理categoryIds参数(支持逗号分隔的多个ID)
|
|
|
- if(typeof query.categoryIds === 'string'){
|
|
|
- categoryIds = query.categoryIds.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id));
|
|
|
- } else if(Array.isArray(query.categoryIds)){
|
|
|
- categoryIds = query.categoryIds.map(id => parseInt(id)).filter(id => !isNaN(id));
|
|
|
+ // 处理cropIds参数(支持逗号分隔的多个ID)
|
|
|
+ if(typeof query.cropIds === 'string'){
|
|
|
+ cropIds = query.cropIds.split(',').map(id => parseInt(id.trim())).filter(id => !isNaN(id));
|
|
|
+ } else if(Array.isArray(query.cropIds)){
|
|
|
+ cropIds = query.cropIds.map(id => parseInt(id)).filter(id => !isNaN(id));
|
|
|
} else {
|
|
|
- categoryIds = [parseInt(query.categoryIds)].filter(id => !isNaN(id));
|
|
|
+ cropIds = [parseInt(query.cropIds)].filter(id => !isNaN(id));
|
|
|
}
|
|
|
|
|
|
- if(categoryIds.length > 0){
|
|
|
+ if(cropIds.length > 0){
|
|
|
// 获取所有选中的分类及其子分类的ID
|
|
|
- const allCategoryIds = await getAllCategoryIdsWithChildren(categoryIds);
|
|
|
+ const allCropIds = await getAllCategoryIdsWithChildren(cropIds);
|
|
|
|
|
|
- whereConditions.category = {
|
|
|
- [Op.in]: allCategoryIds
|
|
|
+ whereConditions.crop = {
|
|
|
+ [Op.in]: allCropIds
|
|
|
};
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 用户分类筛选 - 根据用户传递的category参数值查询
|
|
|
+ if(query.categoryId){
|
|
|
+ const categoryId = parseInt(query.categoryId);
|
|
|
+ if(!isNaN(categoryId)){
|
|
|
+ whereConditions.category = categoryId;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 推荐筛选 - 根据是否推荐进行筛选
|
|
|
+ if(query.isRecommended !== undefined){
|
|
|
+ const isRecommended = parseInt(query.isRecommended);
|
|
|
+ if(!isNaN(isRecommended) && (isRecommended === 0 || isRecommended === 1)){
|
|
|
+ whereConditions.isRecommended = isRecommended;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
// 如果有查询条件,添加到condition中
|
|
|
if(Object.keys(whereConditions).length > 0){
|
|
|
condition.where = whereConditions;
|
|
@@ -93,9 +196,76 @@ router.get('/', async function(req, res, next) {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-/*
|
|
|
- 查询文章详情
|
|
|
- GET /admin/articles/:id
|
|
|
+/**
|
|
|
+ * @api {get} /admin/articles/:id 查询文章详情
|
|
|
+ * @apiName GetArticleById
|
|
|
+ * @apiGroup Articles
|
|
|
+ * @apiVersion 1.0.0
|
|
|
+ *
|
|
|
+ * @apiDescription 根据文章ID获取文章详细信息
|
|
|
+ *
|
|
|
+ * @apiParam {Number} id 文章ID(路径参数)
|
|
|
+ *
|
|
|
+ * @apiSuccess {Boolean} status 请求状态
|
|
|
+ * @apiSuccess {String} message 响应消息
|
|
|
+ * @apiSuccess {Object} data 文章详细信息
|
|
|
+ * @apiSuccess {Number} data.id 文章ID
|
|
|
+ * @apiSuccess {String} data.title 文章标题
|
|
|
+ * @apiSuccess {String} [data.subtitle] 文章副标题
|
|
|
+ * @apiSuccess {String} data.content 文章内容
|
|
|
+ * @apiSuccess {Number} data.type 文章类型
|
|
|
+ * @apiSuccess {String} [data.img] 文章图片
|
|
|
+ * @apiSuccess {Date} [data.date] 文章日期
|
|
|
+ * @apiSuccess {String} [data.author] 作者
|
|
|
+ * @apiSuccess {Number} data.category 用户分类ID
|
|
|
+ * @apiSuccess {Number} data.crop 作物分类ID
|
|
|
+ * @apiSuccess {Number} data.isRecommended 是否推荐(0-不推荐,1-推荐)
|
|
|
+ * @apiSuccess {String} [data.seoKeyword] SEO关键词
|
|
|
+ * @apiSuccess {String} [data.seoDescription] SEO描述
|
|
|
+ * @apiSuccess {Date} data.createdAt 创建时间
|
|
|
+ * @apiSuccess {Date} data.updatedAt 更新时间
|
|
|
+ * @apiSuccess {Object} [data.cropInfo] 作物信息
|
|
|
+ * @apiSuccess {Number} data.cropInfo.id 作物ID
|
|
|
+ * @apiSuccess {String} data.cropInfo.name 作物名称
|
|
|
+ * @apiSuccess {Number} data.cropInfo.level 作物层级
|
|
|
+ * @apiSuccess {Number} data.cropInfo.parentId 父级作物ID
|
|
|
+ *
|
|
|
+ * @apiSuccessExample {json} Success-Response:
|
|
|
+ * HTTP/1.1 200 OK
|
|
|
+ * {
|
|
|
+ * "status": true,
|
|
|
+ * "message": "成功",
|
|
|
+ * "data": {
|
|
|
+ * "id": 96,
|
|
|
+ * "title": "测试文章",
|
|
|
+ * "subtitle": "副标题",
|
|
|
+ * "content": "<p>文章内容</p>",
|
|
|
+ * "type": 1,
|
|
|
+ * "img": null,
|
|
|
+ * "date": null,
|
|
|
+ * "author": "作者",
|
|
|
+ * "category": 1,
|
|
|
+ * "crop": 43,
|
|
|
+ * "isRecommended": 1,
|
|
|
+ * "seoKeyword": null,
|
|
|
+ * "seoDescription": null,
|
|
|
+ * "createdAt": "2025-09-14T09:21:24.000Z",
|
|
|
+ * "updatedAt": "2025-09-14T09:21:24.000Z",
|
|
|
+ * "cropInfo": {
|
|
|
+ * "id": 43,
|
|
|
+ * "name": "荔枝",
|
|
|
+ * "level": 2,
|
|
|
+ * "parentId": 40
|
|
|
+ * }
|
|
|
+ * }
|
|
|
+ * }
|
|
|
+ *
|
|
|
+ * @apiErrorExample {json} Error-Response:
|
|
|
+ * HTTP/1.1 404 Not Found
|
|
|
+ * {
|
|
|
+ * "status": false,
|
|
|
+ * "message": "文章未找到"
|
|
|
+ * }
|
|
|
*/
|
|
|
router.get('/:id', async function(req, res, next) {
|
|
|
try {
|
|
@@ -106,7 +276,7 @@ router.get('/:id', async function(req, res, next) {
|
|
|
const article = await Article.findByPk(id, {
|
|
|
include: [{
|
|
|
model: Category,
|
|
|
- as: 'categoryInfo',
|
|
|
+ as: 'cropInfo',
|
|
|
attributes: ['id', 'name', 'level', 'parentId'],
|
|
|
required: false
|
|
|
}]
|
|
@@ -133,9 +303,93 @@ router.get('/:id', async function(req, res, next) {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-/*
|
|
|
- 创建文章
|
|
|
- POST /admin/articles/
|
|
|
+/**
|
|
|
+ * @api {post} /admin/articles 创建文章
|
|
|
+ * @apiName CreateArticle
|
|
|
+ * @apiGroup Articles
|
|
|
+ * @apiVersion 1.0.0
|
|
|
+ *
|
|
|
+ * @apiDescription 创建新的文章,支持富文本内容和图片
|
|
|
+ *
|
|
|
+ * @apiParam {String} title 文章标题(必填,1-500字符)
|
|
|
+ * @apiParam {String} content 文章内容(必填,富文本格式,最大5MB)
|
|
|
+ * @apiParam {Number} [type] 文章类型
|
|
|
+ * @apiParam {String} [img] 文章图片URL
|
|
|
+ * @apiParam {Date} [date] 文章发布日期
|
|
|
+ * @apiParam {String} [author] 作者
|
|
|
+ * @apiParam {Number} [category] 用户分类ID(用户传递的参数)
|
|
|
+ * @apiParam {Number} [crop] 作物分类ID
|
|
|
+ * @apiParam {Number} [isRecommended=0] 是否推荐,0-不推荐,1-推荐
|
|
|
+ * @apiParam {String} [subtitle] 副标题(最大200字符)
|
|
|
+ * @apiParam {String} [seoKeyword] SEO关键词
|
|
|
+ * @apiParam {String} [seoDescription] SEO描述
|
|
|
+ *
|
|
|
+ * @apiSuccess {Boolean} status 请求状态
|
|
|
+ * @apiSuccess {String} message 响应消息
|
|
|
+ * @apiSuccess {Object} data 创建的文章信息
|
|
|
+ * @apiSuccess {Number} data.id 文章ID
|
|
|
+ * @apiSuccess {String} data.title 文章标题
|
|
|
+ * @apiSuccess {String} [data.subtitle] 文章副标题
|
|
|
+ * @apiSuccess {String} data.content 文章内容
|
|
|
+ * @apiSuccess {Number} data.type 文章类型
|
|
|
+ * @apiSuccess {String} [data.img] 文章图片
|
|
|
+ * @apiSuccess {Date} [data.date] 文章日期
|
|
|
+ * @apiSuccess {String} [data.author] 作者
|
|
|
+ * @apiSuccess {Number} data.category 用户分类ID
|
|
|
+ * @apiSuccess {Number} data.crop 作物分类ID
|
|
|
+ * @apiSuccess {Number} data.isRecommended 是否推荐
|
|
|
+ * @apiSuccess {String} [data.seoKeyword] SEO关键词
|
|
|
+ * @apiSuccess {String} [data.seoDescription] SEO描述
|
|
|
+ * @apiSuccess {Date} data.createdAt 创建时间
|
|
|
+ * @apiSuccess {Date} data.updatedAt 更新时间
|
|
|
+ *
|
|
|
+ * @apiSuccessExample {json} Success-Response:
|
|
|
+ * HTTP/1.1 201 Created
|
|
|
+ * {
|
|
|
+ * "status": true,
|
|
|
+ * "message": "成功",
|
|
|
+ * "data": {
|
|
|
+ * "id": 97,
|
|
|
+ * "title": "新文章标题",
|
|
|
+ * "subtitle": "副标题",
|
|
|
+ * "content": "<p>文章内容</p>",
|
|
|
+ * "type": 1,
|
|
|
+ * "img": null,
|
|
|
+ * "date": null,
|
|
|
+ * "author": "作者",
|
|
|
+ * "category": 1,
|
|
|
+ * "crop": 43,
|
|
|
+ * "isRecommended": 1,
|
|
|
+ * "seoKeyword": null,
|
|
|
+ * "seoDescription": null,
|
|
|
+ * "createdAt": "2025-09-14T09:21:49.333Z",
|
|
|
+ * "updatedAt": "2025-09-14T09:21:49.333Z"
|
|
|
+ * }
|
|
|
+ * }
|
|
|
+ *
|
|
|
+ * @apiErrorExample {json} Error-Response:
|
|
|
+ * HTTP/1.1 400 Bad Request
|
|
|
+ * {
|
|
|
+ * "status": false,
|
|
|
+ * "message": "请求参数错误",
|
|
|
+ * "errors": ["标题不能为空"]
|
|
|
+ * }
|
|
|
+ *
|
|
|
+ * @apiErrorExample {json} Error-Response:
|
|
|
+ * HTTP/1.1 400 Bad Request
|
|
|
+ * {
|
|
|
+ * "status": false,
|
|
|
+ * "message": "请求参数错误",
|
|
|
+ * "errors": ["推荐字段只能是0或1"]
|
|
|
+ * }
|
|
|
+ *
|
|
|
+ * @apiErrorExample {json} Error-Response:
|
|
|
+ * HTTP/1.1 400 Bad Request
|
|
|
+ * {
|
|
|
+ * "status": false,
|
|
|
+ * "message": "请求参数错误",
|
|
|
+ * "errors": ["副标题长度不能超过200个字符"]
|
|
|
+ * }
|
|
|
*/
|
|
|
router.post('/', async function(req, res, next) {
|
|
|
try {
|
|
@@ -175,7 +429,8 @@ router.post('/', async function(req, res, next) {
|
|
|
console.error('请求体:', JSON.stringify(req.body, null, 2));
|
|
|
|
|
|
if(error.message === '标题不能为空' || error.message === '内容不能为空' ||
|
|
|
- error.message.includes('长度不能超过') || error.message.includes('不允许的脚本标签')){
|
|
|
+ error.message.includes('长度不能超过') || error.message.includes('不允许的脚本标签') ||
|
|
|
+ error.message.includes('推荐字段只能是0或1') || error.message.includes('副标题长度不能超过')){
|
|
|
res.status(400).json({
|
|
|
status:false,
|
|
|
message:'请求参数错误',
|
|
@@ -212,9 +467,32 @@ router.post('/', async function(req, res, next) {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-/*
|
|
|
- 删除文章
|
|
|
- GET /admin/articles/:id
|
|
|
+/**
|
|
|
+ * @api {delete} /admin/articles/:id 删除文章
|
|
|
+ * @apiName DeleteArticle
|
|
|
+ * @apiGroup Articles
|
|
|
+ * @apiVersion 1.0.0
|
|
|
+ *
|
|
|
+ * @apiDescription 根据文章ID删除文章
|
|
|
+ *
|
|
|
+ * @apiParam {Number} id 文章ID(路径参数)
|
|
|
+ *
|
|
|
+ * @apiSuccess {Boolean} status 请求状态
|
|
|
+ * @apiSuccess {String} message 响应消息
|
|
|
+ *
|
|
|
+ * @apiSuccessExample {json} Success-Response:
|
|
|
+ * HTTP/1.1 200 OK
|
|
|
+ * {
|
|
|
+ * "status": true,
|
|
|
+ * "message": "成功"
|
|
|
+ * }
|
|
|
+ *
|
|
|
+ * @apiErrorExample {json} Error-Response:
|
|
|
+ * HTTP/1.1 404 Not Found
|
|
|
+ * {
|
|
|
+ * "status": false,
|
|
|
+ * "message": "文章未找到"
|
|
|
+ * }
|
|
|
*/
|
|
|
router.delete('/:id', async function(req, res, next) {
|
|
|
try {
|
|
@@ -246,9 +524,70 @@ router.delete('/:id', async function(req, res, next) {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
-/*
|
|
|
- 更新文章
|
|
|
- GET /admin/articles/:id
|
|
|
+/**
|
|
|
+ * @api {put} /admin/articles/:id 更新文章
|
|
|
+ * @apiName UpdateArticle
|
|
|
+ * @apiGroup Articles
|
|
|
+ * @apiVersion 1.0.0
|
|
|
+ *
|
|
|
+ * @apiDescription 根据文章ID更新文章信息
|
|
|
+ *
|
|
|
+ * @apiParam {Number} id 文章ID(路径参数)
|
|
|
+ * @apiParam {String} [title] 文章标题(1-500字符)
|
|
|
+ * @apiParam {String} [content] 文章内容(富文本格式,最大5MB)
|
|
|
+ * @apiParam {Number} [type] 文章类型
|
|
|
+ * @apiParam {String} [img] 文章图片URL
|
|
|
+ * @apiParam {Date} [date] 文章发布日期
|
|
|
+ * @apiParam {String} [author] 作者
|
|
|
+ * @apiParam {Number} [category] 用户分类ID
|
|
|
+ * @apiParam {Number} [crop] 作物分类ID
|
|
|
+ * @apiParam {Number} [isRecommended] 是否推荐,0-不推荐,1-推荐
|
|
|
+ * @apiParam {String} [subtitle] 副标题(最大200字符)
|
|
|
+ * @apiParam {String} [seoKeyword] SEO关键词
|
|
|
+ * @apiParam {String} [seoDescription] SEO描述
|
|
|
+ *
|
|
|
+ * @apiSuccess {Boolean} status 请求状态
|
|
|
+ * @apiSuccess {String} message 响应消息
|
|
|
+ * @apiSuccess {Object} data 更新后的文章信息
|
|
|
+ *
|
|
|
+ * @apiSuccessExample {json} Success-Response:
|
|
|
+ * HTTP/1.1 200 OK
|
|
|
+ * {
|
|
|
+ * "status": true,
|
|
|
+ * "message": "成功",
|
|
|
+ * "data": {
|
|
|
+ * "id": 97,
|
|
|
+ * "title": "更新后的标题",
|
|
|
+ * "subtitle": "更新后的副标题",
|
|
|
+ * "content": "<p>更新后的内容</p>",
|
|
|
+ * "type": 1,
|
|
|
+ * "img": null,
|
|
|
+ * "date": null,
|
|
|
+ * "author": "作者",
|
|
|
+ * "category": 1,
|
|
|
+ * "crop": 43,
|
|
|
+ * "isRecommended": 1,
|
|
|
+ * "seoKeyword": null,
|
|
|
+ * "seoDescription": null,
|
|
|
+ * "createdAt": "2025-09-14T09:21:49.333Z",
|
|
|
+ * "updatedAt": "2025-09-14T09:22:10.000Z"
|
|
|
+ * }
|
|
|
+ * }
|
|
|
+ *
|
|
|
+ * @apiErrorExample {json} Error-Response:
|
|
|
+ * HTTP/1.1 404 Not Found
|
|
|
+ * {
|
|
|
+ * "status": false,
|
|
|
+ * "message": "文章未找到"
|
|
|
+ * }
|
|
|
+ *
|
|
|
+ * @apiErrorExample {json} Error-Response:
|
|
|
+ * HTTP/1.1 400 Bad Request
|
|
|
+ * {
|
|
|
+ * "status": false,
|
|
|
+ * "message": "请求参数错误",
|
|
|
+ * "errors": ["推荐字段只能是0或1"]
|
|
|
+ * }
|
|
|
*/
|
|
|
router.put('/:id', async function(req, res, next) {
|
|
|
try {
|
|
@@ -340,6 +679,8 @@ function filterBody(req){
|
|
|
author: req.body.author ? String(req.body.author).trim() : null,
|
|
|
category: req.body.category ? parseInt(req.body.category) : null,
|
|
|
crop: req.body.crop ? parseInt(req.body.crop) : null,
|
|
|
+ isRecommended: req.body.isRecommended !== undefined ? parseInt(req.body.isRecommended) : 0,
|
|
|
+ subtitle: req.body.subtitle ? String(req.body.subtitle).trim() : null,
|
|
|
seoKeyword: req.body.seoKeyword ? String(req.body.seoKeyword).trim() : null,
|
|
|
seoDescription: req.body.seoDescription ? String(req.body.seoDescription).trim() : null
|
|
|
};
|
|
@@ -363,6 +704,16 @@ function filterBody(req){
|
|
|
throw new Error('内容过长,请减少内容长度');
|
|
|
}
|
|
|
|
|
|
+ // 验证推荐字段 - 只能是0或1
|
|
|
+ if (body.isRecommended !== 0 && body.isRecommended !== 1) {
|
|
|
+ throw new Error('推荐字段只能是0或1');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证副标题长度
|
|
|
+ if (body.subtitle && body.subtitle.length > 200) {
|
|
|
+ throw new Error('副标题长度不能超过200个字符');
|
|
|
+ }
|
|
|
+
|
|
|
// 检查富文本内容是否包含危险标签或脚本
|
|
|
const dangerousTags = /<script[^>]*>.*?<\/script>/gi;
|
|
|
if (dangerousTags.test(body.content)) {
|