Skip to content

Commit 5cb9913

Browse files
authored
feat: add Shanghai Museum exhibition route (#21890)
* feat: add Shanghai Museum exhibition route * Update shanghaimuseum.ts * feat: add Shanghai Museum exhibition route * Update shanghaimuseum.ts * Update shanghaimuseum.ts * docs: add namespace for shanghaimuseum * feat(route): rename to offline-exhibit and update metadata * fix: update with fixed output and fix errors * fix: fix errors * fix: add exhibitiontype * fix: refactor to JSX * feat: update shanghai museum route with type param * fix: update offline-exhibit to use path params * fix: exhibitItem from ExhibitItem * fix: update interface
1 parent 0f164eb commit 5cb9913

2 files changed

Lines changed: 152 additions & 0 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import type { Namespace } from '@/types';
2+
3+
export const namespace: Namespace = {
4+
name: 'Shanghai Museum',
5+
url: 'www.shanghaimuseum.net',
6+
7+
zh: {
8+
name: '上海博物馆',
9+
},
10+
};
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import { renderToString } from 'hono/jsx/dom/server';
2+
3+
import type { Route } from '@/types';
4+
import got from '@/utils/got';
5+
import { parseDate } from '@/utils/parse-date';
6+
7+
import { namespace } from './namespace';
8+
9+
interface ExhibitItem {
10+
code: string;
11+
exhibitDateRange?: string;
12+
exhibitPlace?: string;
13+
issueTime: string;
14+
name: string;
15+
picPath?: string;
16+
}
17+
18+
export const route: Route = {
19+
path: '/display/offline-exhibit/:type?',
20+
categories: ['travel'],
21+
example: '/shanghaimuseum/display/offline-exhibit/PRESENT',
22+
parameters: {
23+
type: 'Exhibition type, supported values: PRESENT (当期展览) | PAST (往期展览). Default: All exhibitions (both PRESENT and PAST).',
24+
},
25+
// Use SHM English version channel name
26+
name: 'Special Exhibitions',
27+
maintainers: ['magazian'],
28+
radar: [
29+
{
30+
source: ['www.shanghaimuseum.net/mu/frontend/pg/display/offline-exhibit'],
31+
target: '/display/offline-exhibit',
32+
},
33+
],
34+
handler: async (ctx) => {
35+
const type = ctx.req.param('type')?.toUpperCase();
36+
37+
const baseUrl = 'https://www.shanghaimuseum.net';
38+
const apiUrl = `${baseUrl}/mu/frontend/pg/display/search-exhibit`;
39+
40+
const fetchExhibits = async (status: string, limit = 20): Promise<ExhibitItem[]> => {
41+
const response = await got.post(apiUrl, {
42+
json: {
43+
limit,
44+
page: 1,
45+
params: {
46+
exhibitTypeCode: 'OFFLINE_EXHIBITION',
47+
langCode: 'CHINESE',
48+
offlineExhibitionType: status,
49+
},
50+
},
51+
});
52+
return (response.data?.data as ExhibitItem[]) || [];
53+
};
54+
55+
let rawItems: ExhibitItem[] = [];
56+
let titleTag = '';
57+
58+
if (type === 'PAST' || type === 'PRESENT') {
59+
// if user specified the type with past or present
60+
rawItems = await fetchExhibits(type, 50);
61+
titleTag = type === 'PAST' ? '往期展览' : '当期展览';
62+
} else {
63+
// default to fetch both present and past exhibitions
64+
const [presentItems, pastItems] = await Promise.all([fetchExhibits('PRESENT', 50), fetchExhibits('PAST', 20)]);
65+
rawItems = [...presentItems, ...pastItems];
66+
titleTag = '当期展览&往期展览';
67+
}
68+
69+
const museumName = namespace.zh?.name || namespace.name;
70+
71+
const items = rawItems.map((item) => {
72+
const title = item.name;
73+
const itemLink = `${baseUrl}/mu/frontend/pg/article/id/${item.code}`;
74+
const imgUrl = item.picPath ? `${baseUrl}/${item.picPath}` : '';
75+
const location = item.exhibitPlace || '上海博物馆';
76+
const pubDate = parseDate(item.issueTime);
77+
78+
const fullDuration = item.exhibitDateRange || '';
79+
const [startDate, endDate] = fullDuration.includes(' - ') ? fullDuration.split(' - ').map((s) => s.trim()) : [fullDuration, ''];
80+
81+
const description = renderToString(
82+
<div>
83+
{imgUrl && (
84+
<>
85+
<img src={imgUrl} />
86+
<br />
87+
</>
88+
)}
89+
<p>
90+
<b>地点:</b>
91+
{location}
92+
</p>
93+
<p>
94+
<b>开展:</b>
95+
{startDate ?? '未定/常设'}
96+
</p>
97+
<p>
98+
<b>闭展:</b>
99+
{endDate ?? '未定/常设'}
100+
</p>
101+
102+
{fullDuration && (
103+
<p>
104+
<small>
105+
原始展期:
106+
{fullDuration.split('\n').map((line, i) => (
107+
<span key={i}>
108+
{line}
109+
<br />
110+
</span>
111+
))}
112+
</small>
113+
</p>
114+
)}
115+
</div>
116+
);
117+
118+
return {
119+
title,
120+
link: itemLink,
121+
pubDate,
122+
description,
123+
// For further .ics file processing
124+
_extra: {
125+
museumName,
126+
title,
127+
location,
128+
startDate, // format: YYYY-MM-DD or '未定/常设'
129+
endDate, // format: YYYY-MM-DD or '未定/常设'
130+
itemLink,
131+
},
132+
};
133+
});
134+
135+
return {
136+
title: `${museumName} - 特别展览 - ${titleTag}`,
137+
link: `${baseUrl}/mu/frontend/pg/display/offline-exhibit`,
138+
language: 'zh-CN',
139+
item: items,
140+
};
141+
},
142+
};

0 commit comments

Comments
 (0)