Files
competition-website/app/service/news.js
WorkBuddy a303cf2d53 chore: 后端首次基线提交
运行必须源码:app/(controller/model/service/middleware/lib/utils/validate/router)、config/、database/(migrations/seeders/sql)、启动脚本、app/public/templates 模板。
已忽略运行非必须:node_modules/、logs/、run/、app/public 运行时上传数据、.env*、构建/缓存产物。
2026-08-17 12:25:42 +08:00

133 lines
3.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'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;