Skip to content

feat: 新增招聘会模块#386

Open
Seeridia wants to merge 2 commits intomasterfrom
feat/job-fair
Open

feat: 新增招聘会模块#386
Seeridia wants to merge 2 commits intomasterfrom
feat/job-fair

Conversation

@Seeridia
Copy link
Copy Markdown
Member

@Seeridia Seeridia commented Apr 6, 2026

image

@Seeridia Seeridia requested review from a team, klxiaoniu and renbaoshuo as code owners April 6, 2026 10:38
Copilot AI review requested due to automatic review settings April 6, 2026 10:38
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

新增“招聘会”工具模块页面,通过外部接口拉取当月招聘会/宣讲会列表并按日期分组展示,同时在工具箱入口中增加跳转项,便于用户在应用内查看活动详情。

Changes:

  • 新增 app/toolbox/job-fair.tsx:按月请求招聘会数据、解码标题/地点实体、按日期分组渲染列表,并通过 WebView 打开详情页
  • 更新 app/(tabs)/toolbox.tsx:在工具箱默认工具列表中增加“招聘会”入口

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
app/toolbox/job-fair.tsx 新增招聘会列表页(拉取数据、状态管理、分组展示、跳转详情)
app/(tabs)/toolbox.tsx 工具箱增加“招聘会”入口配置

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/toolbox/job-fair.tsx
Comment on lines +17 to +19
const JOB_FAIR_API_URL = 'http://fjrclh.fzu.edu.cn/CmsInterface/getDateZPHKeynoteList_month';
const JOB_FAIR_DETAIL_URL = 'http://fjrclh.fzu.edu.cn/cms/zphdetail.html';
const LECTURE_DETAIL_URL = 'http://fjrclh.fzu.edu.cn/cms/xjhdetail.html';
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这些外部接口/详情页 URL 目前使用明文 HTTP,会带来被中间人篡改内容的风险(尤其是 WebView 详情页)。如果站点支持 HTTPS,建议切换到 https://;如果不支持,建议至少在注释里说明原因,并评估是否需要在 WebView 层做额外的域名/内容安全限制。

Suggested change
const JOB_FAIR_API_URL = 'http://fjrclh.fzu.edu.cn/CmsInterface/getDateZPHKeynoteList_month';
const JOB_FAIR_DETAIL_URL = 'http://fjrclh.fzu.edu.cn/cms/zphdetail.html';
const LECTURE_DETAIL_URL = 'http://fjrclh.fzu.edu.cn/cms/xjhdetail.html';
const JOB_FAIR_API_URL = 'https://fjrclh.fzu.edu.cn/CmsInterface/getDateZPHKeynoteList_month';
const JOB_FAIR_DETAIL_URL = 'https://fjrclh.fzu.edu.cn/cms/zphdetail.html';
const LECTURE_DETAIL_URL = 'https://fjrclh.fzu.edu.cn/cms/xjhdetail.html';

Copilot uses AI. Check for mistakes.
Comment thread app/toolbox/job-fair.tsx
Comment on lines +76 to +86
function decodeHtmlEntities(value: string) {
return value.replace(/&(#x?[\da-fA-F]+|[a-zA-Z]+);/g, (fullMatch, entity: string) => {
if (entity.startsWith('#x') || entity.startsWith('#X')) {
const codePoint = Number.parseInt(entity.slice(2), 16);
return Number.isNaN(codePoint) ? fullMatch : String.fromCodePoint(codePoint);
}

if (entity.startsWith('#')) {
const codePoint = Number.parseInt(entity.slice(1), 10);
return Number.isNaN(codePoint) ? fullMatch : String.fromCodePoint(codePoint);
}
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

decodeHtmlEntities 里对数字实体直接使用 String.fromCodePoint(codePoint)。当 codePoint 超出合法 Unicode 范围(>0x10FFFF 或为负数)时会抛 RangeError,导致渲染/请求链路异常。建议在调用前做范围校验或 try/catch,非法值回退返回 fullMatch。

Copilot uses AI. Check for mistakes.
Comment thread app/toolbox/job-fair.tsx
Comment on lines +229 to +244
const state = useMemo(() => {
if (isFetching && !data) {
return STATE.LOADING;
}

if (isError) {
return STATE.ERROR;
}

const totalItems = data?.groups.length ?? 0;
if (!data || totalItems === 0) {
return STATE.EMPTY;
}

return STATE.CONTENT;
}, [data, isError, isFetching]);
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里的 state 只在 isError 时返回 STATE.ERROR,没有区分网络错误导致的 NO_NETWORK 状态。仓库里已有将网络错误映射到 STATE.NO_NETWORK 的用法(例如 hooks/useMultiStateRequest.ts 与 app/toolbox/free-friends.tsx)。建议基于 error.message(如包含 "Network Error"/timeout)或封装统一的错误类型,将网络故障时展示 NoNetworkView。

Copilot uses AI. Check for mistakes.
Comment thread app/toolbox/job-fair.tsx
Comment on lines +288 to +292
<Pressable className="rounded-full p-2" onPress={handlePreviousMonth}>
<Icon name="chevron-back-outline" size={20} />
</Pressable>
<Text className="text-lg font-semibold">{selectedMonth.format(MONTH_TITLE_FORMAT)}</Text>
<Pressable className="rounded-full p-2" onPress={handleNextMonth}>
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

上下月切换是纯图标按钮(无可读文本),当前 Pressable 未提供 accessibilityLabel/role,读屏用户难以理解其用途。建议为两个按钮补充 accessibilityRole="button" 及合适的 accessibilityLabel(如“上个月”“下个月”)。

Suggested change
<Pressable className="rounded-full p-2" onPress={handlePreviousMonth}>
<Icon name="chevron-back-outline" size={20} />
</Pressable>
<Text className="text-lg font-semibold">{selectedMonth.format(MONTH_TITLE_FORMAT)}</Text>
<Pressable className="rounded-full p-2" onPress={handleNextMonth}>
<Pressable
className="rounded-full p-2"
onPress={handlePreviousMonth}
accessibilityRole="button"
accessibilityLabel="上个月"
>
<Icon name="chevron-back-outline" size={20} />
</Pressable>
<Text className="text-lg font-semibold">{selectedMonth.format(MONTH_TITLE_FORMAT)}</Text>
<Pressable
className="rounded-full p-2"
onPress={handleNextMonth}
accessibilityRole="button"
accessibilityLabel="下个月"
>

Copilot uses AI. Check for mistakes.
Comment thread app/toolbox/job-fair.tsx
Comment on lines +140 to +142
const rawText = Buffer.from(response.data).toString('utf-8');
const parsed = JSON.parse(rawText) as JobFairApiResponse;

Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetchJobFairMonthData 里直接 JSON.parse(rawText),一旦接口返回非 JSON(比如 HTML 错误页/空响应)会抛出 SyntaxError,最终 toast 可能展示不友好的底层报错文本。建议对 JSON.parse 加 try/catch(以及可选的 response.status 校验),在失败时抛出更明确的业务错误信息(例如“招聘会数据解析失败”)。

Suggested change
const rawText = Buffer.from(response.data).toString('utf-8');
const parsed = JSON.parse(rawText) as JobFairApiResponse;
if (
typeof response === 'object' &&
response !== null &&
'status' in response &&
typeof response.status === 'number' &&
(response.status < 200 || response.status >= 300)
) {
throw new Error('招聘会数据加载失败');
}
const rawText = Buffer.from(response.data).toString('utf-8').trim();
if (!rawText) {
throw new Error('招聘会数据解析失败');
}
let parsed: JobFairApiResponse;
try {
parsed = JSON.parse(rawText) as JobFairApiResponse;
} catch {
throw new Error('招聘会数据解析失败');
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants