feat(route): 添加哈尔滨工业大学(深圳)的教务处通知的路由 (#20108)

*  添加哈公深教务处

* 🎨 修改拼接逻辑

* 🐛 修改const错误

*  添加按页数查,按时间排序

* 🎨 加速运行

*  添加教务管理

* 🎨 教务学务分离出来

* 🎨 优化速度

* 🔥 删除教务动态

* 🐛 修改翻页问题

*  添加综合代码

*  添加类型筛选

* ️ 修改pr评论问题

* 🎨 添加空值检查

* 🐛 修改pr的评论问题

* 🐛 修改评论问题

* 🐛 修改es问题

* 🎨 修改代码问题

* 🐛 去掉多余的promise

* 🐛 使用通用sort和limit逻辑

* 🐛 不翻页

* 🐛 修改review问题

* 🐛 修改标题带日期的问题

* 🐛 优化标题
This commit is contained in:
himawari 2025-10-02 13:56:51 +08:00 committed by GitHub
parent 004a2fd2c9
commit 2fa77a556e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 282 additions and 0 deletions

View File

@ -0,0 +1,110 @@
import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
export const handler = async () => {
const baseUrl = 'http://due.hitsz.edu.cn';
const baseListPath = 'index/tzggqb';
// 只抓取第一页
const firstPageUrl = new URL(`${baseListPath}.htm`, baseUrl).href;
let response;
try {
response = await got(firstPageUrl);
} catch {
// 返回空结果
return {
title: '哈尔滨工业大学(深圳)教务部通知公告',
description: '哈尔滨工业大学(深圳)教务部通知公告',
link: firstPageUrl,
item: [],
author: '哈尔滨工业大学(深圳)教务部',
};
}
const $ = load(response.data);
// 获取页面标题
const pageTitle = $('title').text().trim() || '哈尔滨工业大学(深圳)教务部通知公告';
const author = '哈尔滨工业大学(深圳)教务部';
// 解析第一页的文章列表
const listItems = $('ul.list-main-modular li').toArray();
const items = listItems
.map((el) => {
const $el = $(el);
const linkUrl = $el.find('a').attr('href');
if (!linkUrl) {
return null;
}
const title = $el.find('span').text().trim();
const pubDateStr = $el.find('label').text().trim();
return {
title,
link: new URL(linkUrl, baseUrl).href,
pubDate: pubDateStr ? timezone(parseDate(pubDateStr), 8) : null,
description: title,
};
})
.filter(Boolean);
return {
title: `${author} - ${pageTitle}`,
description: pageTitle,
link: firstPageUrl,
item: items,
author,
};
};
// 保持 route 定义完全不变
export const route: Route = {
path: '/due/tzgg',
name: '教务部',
url: 'due.hitsz.edu.cn',
maintainers: ['guohuiyuan'],
handler,
example: '/hitsz/due/tzgg',
parameters: {},
description: `:::tip
[](http://due.hitsz.edu.cn/index/tzggqb.htm),其源网址为 \`http://due.hitsz.edu.cn/index/tzggqb.htm\`,请参考该 URL 指定部分构成参数,此时路由为 [\`/hitsz/due/tzgg\`](https://rsshub.app/hitsz/due/tzgg)。
:::
使 [\`/hitsz/due/general\`](https://rsshub.app/hitsz/due/general) 路由。
<details>
<summary></summary>
| | ID |
| - | - |
| [](http://due.hitsz.edu.cn/index/tzggqb.htm) | [tzgg](https://rsshub.app/hitsz/due/tzgg) |
</details>
`,
categories: ['university'],
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportRadar: true,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['due.hitsz.edu.cn', 'due.hitsz.edu.cn/index/:id/list.htm'],
target: '/hitsz/due/:id',
},
{
title: '通知公告',
source: ['due.hitsz.edu.cn/index/tzggqb.htm'],
target: '/hitsz/due/tzgg',
},
],
};

172
lib/routes/hitsz/due.ts Normal file
View File

@ -0,0 +1,172 @@
import { Route } from '@/types';
import got from '@/utils/got';
import { load } from 'cheerio';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
export const handler = async (ctx) => {
const baseUrl = 'http://due.hitsz.edu.cn';
// 按类型分组(可直接用于 handler 中的逻辑判断)
const categoryGroups = {
// 教务核心业务(传统教务管理相关)
teaching: [
'jwxw/jwgl', // 教务管理
'jwxw/kwgl', // 考务管理
'jwxw/zcgl', // 注册管理
'jwxw/xkgl', // 选课管理
'jwxw/cjgl', // 成绩管理
],
// 学籍相关(学生档案与身份管理)
studentStatus: [
'jwxw/xjgl_b_', // 学籍管理(本)
'jwxw/xjgl_y_', // 学籍管理(研)
],
// 教学支持(辅助教学的资源与服务)
teachingSupport: [
'jwxw/jxxxh', // 教学信息化
'jwxw/jzxj', // 奖助学金
],
// 学生培养(不同学段的培养动态)
education: [
'xwgl/bksxw', // 本科生新闻
'xwgl/ssxwpy/ktyzj', // 硕士学位培养
'xwgl/bsxwpy/qqhj1', // 博士学位培养
],
};
// 修改:将 query 参数改为 path 参数PR评论要求Use path parameter instead of search query
const { type = 'all' } = ctx.req.param(); // 从路径参数获取,默认值 'all'
const validTypes = Object.keys(categoryGroups);
const validType = validTypes.includes(type) ? type : 'all'; // 容错:无效类型默认走 all
// 根据类型选择对应栏目组
const categories = validType === 'all' ? Object.values(categoryGroups).flat() : categoryGroups[validType];
// 并发抓取所有栏目的第一页
// 修复 ESLint替换 .catch(() => null) 为带日志的try/catch
const pagePromises = categories.map(async (category) => {
const pageUrl = new URL(`${category}.htm`, baseUrl).href;
try {
return await got(pageUrl);
} catch {
return null;
}
});
const pageResponses = await Promise.all(pagePromises);
// 提取所有文章链接
// 修复用flatMap替代for循环+push同步逻辑优化
const articlePromises = pageResponses.flatMap((response, i) => {
if (!response) {
return [];
}
const category = categories[i];
const $ = load(response.data);
const listItemsOnPage = $('ul.box-main-list li, .list-main li, .list-main-modular li').toArray();
// 原map逻辑不变
return listItemsOnPage
.map((el) => {
const $el = $(el);
const linkUrl = $el.find('a').attr('href');
if (!linkUrl) {
return null;
} // 过滤无链接项
const title = $el.find('span').text().trim();
const pubDateStr = $el.find('label').text().trim();
return {
title,
link: new URL(linkUrl, baseUrl).href,
pubDate: pubDateStr ? timezone(parseDate(pubDateStr), 8) : null,
category,
description: title, // 使用标题作为描述
};
})
.filter(Boolean); // 过滤 null 项
});
// 获取所有文章详情
const allResolvedItems = articlePromises.filter(Boolean);
// 排序和截取
const filteredItems = allResolvedItems.filter((item) => !item.title.includes('统一身份认证平台'));
return {
title: '哈尔滨工业大学(深圳)教务部-教务学务与学位管理-所有栏目新闻汇总',
description: '哈尔滨工业大学(深圳)教务部中教务学务和学位管理所有栏目的最新新闻汇总,包括教务管理、考务管理、注册管理、选课管理、成绩管理、学籍管理、教学信息化、奖助学金、本科生新闻、硕士学位培养、博士学位培养等',
link: 'http://due.hitsz.edu.cn/jwxw/jwgl.htm',
item: filteredItems,
author: '哈尔滨工业大学(深圳)教务部',
};
};
export const route: Route = {
// 修改path 增加可选参数 :type?(适配路径参数逻辑)
path: '/due/general/:type?',
name: '教务部教务学务与学位管理所有栏目',
url: 'due.hitsz.edu.cn',
maintainers: ['guohuiyuan'],
handler,
example: '/hitsz/due/general',
// 新增:补充 parameters 声明修复PR评论的参数缺失问题
parameters: {
type: {
description: '栏目类型筛选默认all所有栏目',
options: [
{ value: 'all', label: '所有栏目' },
{ value: 'teaching', label: '教务核心业务' },
{ value: 'studentStatus', label: '学籍相关' },
{ value: 'teachingSupport', label: '教学支持' },
{ value: 'education', label: '学生培养' },
],
default: 'all',
},
},
// 修改Markdown 二级标题##降级为四级标题####PR评论要求Do not use level 2 heading
description: `哈尔滨工业大学(深圳)教务部中教务学务和学位管理所有栏目的最新新闻汇总。
####
使
- \`type=teaching\` - 教务核心业务:教务管理、考务管理、注册管理、选课管理、成绩管理
- \`type=studentStatus\` - 学籍相关:本科生学籍管理、研究生学籍管理
- \`type=teachingSupport\` - 教学支持:教学信息化、奖助学金
- \`type=education\` - 学生培养:本科生新闻、硕士学位培养、博士学位培养
- \`type=all\` 或省略 - 所有栏目(默认)
####
- [](http://due.hitsz.edu.cn/jwxw/jwgl.htm)
- [](http://due.hitsz.edu.cn/jwxw/kwgl.htm)
- [](http://due.hitsz.edu.cn/jwxw/zcgl.htm)
- [](http://due.hitsz.edu.cn/jwxw/xkgl.htm)
- [](http://due.hitsz.edu.cn/jwxw/cjgl.htm)
- [](http://due.hitsz.edu.cn/jwxw/xjgl_b_.htm)
- [](http://due.hitsz.edu.cn/jwxw/xjgl_y_.htm)
- [](http://due.hitsz.edu.cn/jwxw/jxxxh.htm)
- [](http://due.hitsz.edu.cn/jwxw/jzxj.htm)
- [](http://due.hitsz.edu.cn/xwgl/bksxw.htm)
- [](http://due.hitsz.edu.cn/xwgl/ssxwpy/ktyzj.htm)
- [](http://due.hitsz.edu.cn/xwgl/bsxwpy/qqhj1.htm)`,
categories: ['university'],
features: {
requireConfig: false,
requirePuppeteer: false,
antiCrawler: false,
supportRadar: true,
supportBT: false,
supportPodcast: false,
supportScihub: false,
},
radar: [
{
source: ['due.hitsz.edu.cn/jwxw/jwgl.htm'],
target: '/hitsz/due/general',
},
],
};