diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml
index 27b8019af..ce438a156 100644
--- a/.github/workflows/docker-test.yml
+++ b/.github/workflows/docker-test.yml
@@ -20,7 +20,7 @@ on:
permissions:
contents: read
-
+
concurrency:
group: ${{ github.head_ref }}
cancel-in-progress: true
diff --git a/docs/government.md b/docs/government.md
index 8ca07b535..641056a53 100644
--- a/docs/government.md
+++ b/docs/government.md
@@ -718,6 +718,12 @@ pageClass: routes
+## 南京鼓楼医院
+
+### 员工版教育培训
+
+
+
## 南京市人民政府
### 信息公开
diff --git a/docs/university.md b/docs/university.md
index 9d6245e89..883076f0d 100644
--- a/docs/university.md
+++ b/docs/university.md
@@ -2246,6 +2246,12 @@ jsjxy.hbut.edu.cn 证书链不全,自建 RSSHub 可设置环境变量 NODE_TLS
+## 南京中医药大学
+
+### 研究生院博士招生
+
+
+
## 南开大学
### 南开大学教务处
diff --git a/lib/v2/njglyy/maintainer.js b/lib/v2/njglyy/maintainer.js
new file mode 100644
index 000000000..57b94cca5
--- /dev/null
+++ b/lib/v2/njglyy/maintainer.js
@@ -0,0 +1,3 @@
+module.exports = {
+ '/ygbjypx': ['real-jiakai'],
+};
diff --git a/lib/v2/njglyy/radar.js b/lib/v2/njglyy/radar.js
new file mode 100644
index 000000000..4342187f1
--- /dev/null
+++ b/lib/v2/njglyy/radar.js
@@ -0,0 +1,13 @@
+module.exports = {
+ 'njglyy.com': {
+ _name: '南京鼓楼医院',
+ '.': [
+ {
+ title: '员工版教育培训',
+ docs: 'https://docs.rsshub.app/university.html#nan-jing-gu-lou-yi-yuan-yuan-gong-ban-jiao-yu-pei-xun',
+ source: ['/ygb/jypx/jypx.aspx', '/'],
+ target: '/njglyy/ygbjypx',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/njglyy/router.js b/lib/v2/njglyy/router.js
new file mode 100644
index 000000000..25adeebce
--- /dev/null
+++ b/lib/v2/njglyy/router.js
@@ -0,0 +1,3 @@
+module.exports = (router) => {
+ router.get('/ygbjypx', require('./ygbjypx'));
+};
diff --git a/lib/v2/njglyy/utils/index.js b/lib/v2/njglyy/utils/index.js
new file mode 100644
index 000000000..b598f8f71
--- /dev/null
+++ b/lib/v2/njglyy/utils/index.js
@@ -0,0 +1,50 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+const timezone = require('@/utils/timezone');
+
+async function getNoticeList(ctx, url, host, listSelector, itemSelector, titleSelector, contentSelector) {
+ const response = await got(url);
+ const $ = cheerio.load(response.data);
+
+ const list = $(listSelector)
+ .toArray()
+ .map((item) => {
+ item = $(item);
+ return {
+ title: item.find(titleSelector).text(),
+ link: host + item.find(itemSelector).attr('href'),
+ };
+ });
+
+ const out = await Promise.all(
+ list.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const response = await got(item.link);
+ if (response.redirectUrls.length) {
+ item.link = response.redirectUrls[0];
+ item.description = '该通知无法直接预览,请点击原文链接↑查看';
+ } else {
+ const $ = cheerio.load(response.data);
+ item.title = $(contentSelector.title).text();
+ item.description = $(contentSelector.content)
+ .html()
+ .replace(/src="\//g, `src="${new URL('.', host).href}`)
+ .replace(/href="\//g, `href="${new URL('.', host).href}`)
+ .trim();
+ const preDate = $(contentSelector.date)
+ .text()
+ .match(/(\d{4}-\d{2}-\d{2})/)[1];
+ item.pubDate = timezone(parseDate(preDate, 'YYYY-MM-DD'), +8);
+ }
+ return item;
+ })
+ )
+ );
+
+ return out;
+}
+
+module.exports = {
+ getNoticeList,
+};
diff --git a/lib/v2/njglyy/ygbjypx.js b/lib/v2/njglyy/ygbjypx.js
new file mode 100644
index 000000000..7980635ff
--- /dev/null
+++ b/lib/v2/njglyy/ygbjypx.js
@@ -0,0 +1,18 @@
+const { getNoticeList } = require('./utils');
+
+const url = 'https://njglyy.com/ygb/jypx/jypx.aspx';
+const host = 'https://njglyy.com/ygb/jypx/';
+
+module.exports = async (ctx) => {
+ const out = await getNoticeList(ctx, url, host, '.mtbd-list > dl', 'a', 'dt', {
+ title: '.detail',
+ content: '.detail2',
+ date: 'span:contains("发布时间")',
+ });
+
+ ctx.state.data = {
+ title: '南京鼓楼医院 -- 员工版教育培训',
+ link: url,
+ item: out,
+ };
+};
diff --git a/lib/v2/njucm/grabs.js b/lib/v2/njucm/grabs.js
new file mode 100644
index 000000000..e454a07b7
--- /dev/null
+++ b/lib/v2/njucm/grabs.js
@@ -0,0 +1,18 @@
+const { getNoticeList } = require('./utils');
+
+const url = 'https://gra.njucm.edu.cn/2899/list.htm';
+const host = 'https://gra.njucm.edu.cn';
+
+module.exports = async (ctx) => {
+ const out = await getNoticeList(ctx, url, host, '#wp_news_w3 > table > tbody > tr', 'a', {
+ title: '.Article_Title',
+ content: '.Article_Content',
+ date: '.Article_PublishDate',
+ });
+
+ ctx.state.data = {
+ title: '南京中医药大学 -- 博士招生',
+ link: url,
+ item: out,
+ };
+};
diff --git a/lib/v2/njucm/maintainer.js b/lib/v2/njucm/maintainer.js
new file mode 100644
index 000000000..9ab083e5d
--- /dev/null
+++ b/lib/v2/njucm/maintainer.js
@@ -0,0 +1,3 @@
+module.exports = {
+ '/grabszs': ['real-jiakai'],
+};
diff --git a/lib/v2/njucm/radar.js b/lib/v2/njucm/radar.js
new file mode 100644
index 000000000..bd0dd4659
--- /dev/null
+++ b/lib/v2/njucm/radar.js
@@ -0,0 +1,13 @@
+module.exports = {
+ 'njucm.edu.cn': {
+ _name: '南京中医药大学',
+ lib: [
+ {
+ title: '研究生院博士招生',
+ docs: 'https://docs.rsshub.app/university.html#nan-jing-zhong-yi-yao-da-xue-yan-jiu-sheng-yuan-bo-shi-zhao-sheng',
+ source: ['/2899/list.htm', '/'],
+ target: '/njucm/grabszs',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/njucm/router.js b/lib/v2/njucm/router.js
new file mode 100644
index 000000000..4caf175bb
--- /dev/null
+++ b/lib/v2/njucm/router.js
@@ -0,0 +1,3 @@
+module.exports = (router) => {
+ router.get('/grabszs', require('./grabs'));
+};
diff --git a/lib/v2/njucm/utils/index.js b/lib/v2/njucm/utils/index.js
new file mode 100644
index 000000000..8f131f665
--- /dev/null
+++ b/lib/v2/njucm/utils/index.js
@@ -0,0 +1,47 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+const timezone = require('@/utils/timezone');
+
+async function getNoticeList(ctx, url, host, listSelector, titleSelector, contentSelector) {
+ const response = await got(url);
+ const $ = cheerio.load(response.data);
+
+ const list = $(listSelector)
+ .toArray()
+ .map((item) => {
+ item = $(item);
+ return {
+ title: item.find(titleSelector).attr('title'),
+ link: host + item.find(titleSelector).attr('href'),
+ };
+ });
+
+ const out = await Promise.all(
+ list.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const response = await got(item.link);
+ if (response.redirectUrls.length) {
+ item.link = response.redirectUrls[0];
+ item.description = '该通知无法直接预览,请点击原文链接↑查看';
+ } else {
+ const $ = cheerio.load(response.data);
+ item.title = $(contentSelector.title).text();
+ item.description = $(contentSelector.content)
+ .html()
+ .replace(/src="\//g, `src="${new URL('.', host).href}`)
+ .replace(/href="\//g, `href="${new URL('.', host).href}`)
+ .trim();
+ item.pubDate = timezone(parseDate($(contentSelector.date).text()), +8);
+ }
+ return item;
+ })
+ )
+ );
+
+ return out;
+}
+
+module.exports = {
+ getNoticeList,
+};