运行必须源码:app/(controller/model/service/middleware/lib/utils/validate/router)、config/、database/(migrations/seeders/sql)、启动脚本、app/public/templates 模板。 已忽略运行非必须:node_modules/、logs/、run/、app/public 运行时上传数据、.env*、构建/缓存产物。
133 lines
3.8 KiB
JavaScript
133 lines
3.8 KiB
JavaScript
'use strict';
|
||
|
||
const Service = require('egg').Service;
|
||
|
||
/**
|
||
* 新闻服务 - NewsService
|
||
*
|
||
* 封装新闻相关的数据库查询操作。
|
||
* 支持按组别过滤、按置顶过滤、分页查询。
|
||
*/
|
||
class NewsService extends Service {
|
||
/**
|
||
* 查询新闻列表(支持分页和过滤)
|
||
*
|
||
* 过滤参数:
|
||
* - group: 组别筛选(如 zhongzhi, gaozhi, benke, jiaoshi),同时包含 group='all' 的通用新闻
|
||
* - pinned: 是否仅查询置顶新闻(true/false)
|
||
*
|
||
* 排序规则:pinned DESC, publish_time DESC
|
||
*
|
||
* @param {Object} params - 查询参数
|
||
* @param {string} [params.group] - 目标组别
|
||
* @param {string|boolean} [params.pinned] - 是否只查置顶
|
||
* @param {number} [params.page=1] - 页码
|
||
* @param {number} [params.pageSize=20] - 每页数量
|
||
* @returns {Promise<{list: Array, total: number, page: number, pageSize: number}>} 分页结果
|
||
*/
|
||
async list(params = {}) {
|
||
const { ctx } = this;
|
||
const { Sequelize } = ctx.app;
|
||
|
||
const { group, pinned, page, pageSize } = params;
|
||
|
||
// 分页参数,默认第 1 页,每页 20 条
|
||
const currentPage = parseInt(page) || 1;
|
||
const currentPageSize = parseInt(pageSize) || 20;
|
||
const offset = (currentPage - 1) * currentPageSize;
|
||
|
||
// 构建查询条件
|
||
const where = {};
|
||
|
||
// 组别过滤:同时包含指定组别和通用(all)新闻
|
||
if (group) {
|
||
where.group = {
|
||
[Sequelize.Op.in]: [group, 'all'],
|
||
};
|
||
}
|
||
|
||
// 置顶过滤
|
||
if (pinned !== undefined && pinned !== null && pinned !== '') {
|
||
where.pinned = pinned === 'true' || pinned === true;
|
||
}
|
||
|
||
// 构建查询
|
||
const queryOptions = {
|
||
where,
|
||
order: [
|
||
['pinned', 'DESC'],
|
||
['sort_order', 'ASC'],
|
||
['publish_time', 'DESC'],
|
||
],
|
||
attributes: ['id', 'title', 'content', 'publish_time', 'pinned', 'group', 'tags', 'sort_order', 'created_at', 'updated_at'],
|
||
offset,
|
||
limit: currentPageSize,
|
||
};
|
||
|
||
// 查询新闻列表和总数
|
||
const [list, total] = await Promise.all([
|
||
ctx.model.News.findAll(queryOptions),
|
||
ctx.model.News.count({ where }),
|
||
]);
|
||
|
||
return {
|
||
list,
|
||
total,
|
||
page: currentPage,
|
||
pageSize: currentPageSize,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 获取各分组新闻计数(用于首页 Tab 显示数量)
|
||
* @returns {Promise<Object>} { all: number, zhongzhi: number, gaozhi: number, benke: number, jiaoshi: number }
|
||
*/
|
||
async getGroupCounts() {
|
||
const { ctx } = this;
|
||
const { Sequelize } = ctx.app;
|
||
const groups = ['all', 'zhongzhi', 'gaozhi', 'benke', 'jiaoshi'];
|
||
const counts = { all: 0, zhongzhi: 0, gaozhi: 0, benke: 0, jiaoshi: 0 };
|
||
|
||
// 并行查询各分组计数
|
||
await Promise.all(groups.map(async group => {
|
||
const where = {};
|
||
// 对于具体组别,同时包含该组别和 all 通用新闻
|
||
if (group !== 'all') {
|
||
where.group = { [Sequelize.Op.in]: [group, 'all'] };
|
||
}
|
||
counts[group] = await ctx.model.News.count({ where });
|
||
}));
|
||
|
||
return counts;
|
||
}
|
||
|
||
/**
|
||
* 根据 ID 查询新闻详情
|
||
*
|
||
* @param {number} id - 新闻 ID
|
||
* @returns {Promise<Model|null>} 新闻模型对象或 null
|
||
*/
|
||
async findById(id) {
|
||
const { ctx } = this;
|
||
|
||
return await ctx.model.News.findOne({
|
||
where: { id },
|
||
attributes: ['id', 'title', 'content', 'publish_time', 'pinned', 'group', 'tags', 'sort_order', 'created_at', 'updated_at'],
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 批量更新新闻排序
|
||
* @param {Array<{id: number, sort_order: number}>} orders - 排序信息数组
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async updateSortOrder(orders) {
|
||
const { ctx } = this;
|
||
await Promise.all(orders.map(async ({ id, sort_order }) => {
|
||
await ctx.model.News.update({ sort_order }, { where: { id } });
|
||
}));
|
||
}
|
||
}
|
||
|
||
module.exports = NewsService;
|