@ha7ch/job-pro 1.0.93

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/adapter.js +17 -0
  2. package/dist/agibot.js +399 -0
  3. package/dist/alibaba.js +509 -0
  4. package/dist/antgroup.js +397 -0
  5. package/dist/apply.js +1373 -0
  6. package/dist/baichuan.js +49 -0
  7. package/dist/baidu.js +452 -0
  8. package/dist/bilibili.js +455 -0
  9. package/dist/byd.js +412 -0
  10. package/dist/bytedance.js +619 -0
  11. package/dist/cainiao.js +56 -0
  12. package/dist/cambricon.js +33 -0
  13. package/dist/cdp.js +237 -0
  14. package/dist/cicc.js +56 -0
  15. package/dist/coverage.js +60 -0
  16. package/dist/deepseek.js +25 -0
  17. package/dist/didi.js +381 -0
  18. package/dist/feishu.js +577 -0
  19. package/dist/galaxyuniversal.js +24 -0
  20. package/dist/geely.js +35 -0
  21. package/dist/greenhouse.js +432 -0
  22. package/dist/hikvision.js +58 -0
  23. package/dist/horizonrobotics.js +46 -0
  24. package/dist/hoyoverse.js +26 -0
  25. package/dist/huawei.js +537 -0
  26. package/dist/iflytek.js +380 -0
  27. package/dist/index.js +1828 -0
  28. package/dist/iqiyi.js +494 -0
  29. package/dist/jd.js +559 -0
  30. package/dist/kuaishou.js +496 -0
  31. package/dist/lever.js +455 -0
  32. package/dist/liauto.js +393 -0
  33. package/dist/liepin.js +357 -0
  34. package/dist/lilith.js +300 -0
  35. package/dist/megvii.js +27 -0
  36. package/dist/meituan.js +633 -0
  37. package/dist/memory.js +76 -0
  38. package/dist/mihoyo.js +308 -0
  39. package/dist/minimax.js +32 -0
  40. package/dist/moka.js +473 -0
  41. package/dist/moonshot.js +24 -0
  42. package/dist/netease.js +424 -0
  43. package/dist/nio.js +24 -0
  44. package/dist/oppo.js +285 -0
  45. package/dist/pdd.js +614 -0
  46. package/dist/pingan.js +493 -0
  47. package/dist/sensetime.js +51 -0
  48. package/dist/sf.js +310 -0
  49. package/dist/stepfun.js +24 -0
  50. package/dist/tencent.js +770 -0
  51. package/dist/trip.js +396 -0
  52. package/dist/unitree.js +418 -0
  53. package/dist/vivo.js +361 -0
  54. package/dist/webank.js +55 -0
  55. package/dist/wecruit.js +438 -0
  56. package/dist/weibo.js +337 -0
  57. package/dist/weride.js +29 -0
  58. package/dist/xiaohongshu.js +480 -0
  59. package/dist/xiaomi.js +529 -0
  60. package/dist/xpeng.js +34 -0
  61. package/dist/zerooneai.js +42 -0
  62. package/dist/zhipu.js +478 -0
  63. package/extension/README.md +79 -0
  64. package/extension/background.js +177 -0
  65. package/extension/manifest.json +55 -0
  66. package/extension/popup.html +37 -0
  67. package/extension/popup.js +54 -0
  68. package/package.json +61 -0
package/dist/iqiyi.js ADDED
@@ -0,0 +1,494 @@
1
+ // Thin client for iQIYI's public recruiting API at careers.iqiyi.com.
2
+ //
3
+ // iQIYI runs three separate portals on careers.iqiyi.com, all powered by
4
+ // Feishu/Lark ATSX SaaS recruiting product (same engine as jobs.bytedance.com).
5
+ // The portal is determined by the "website-path" request header.
6
+ //
7
+ // ============================================================
8
+ // Endpoint inventory (probed 2026-05, JS bundle 4026.f23f1edc.js):
9
+ //
10
+ // POST https://careers.iqiyi.com/api/v1/search/job/posts
11
+ // Headers: website-path, portal-platform: "pc", Referer (matching portal)
12
+ // Payload: { keyword, limit, offset, portal_type:3, portal_entrance:1, language:"zh",
13
+ // location_code_list, job_function_id_list, recruitment_id_list }
14
+ // Response: { code:0, data:{ job_post_list:[...], count:<int> } }
15
+ //
16
+ // GET https://careers.iqiyi.com/api/v1/config/job/filters/{website_path}
17
+ // Returns city_list, recruitment_type_list, job_function_list, etc.
18
+ // Note: job_type_list and job_subject_list are null/empty for this tenant.
19
+ //
20
+ // GET https://careers.iqiyi.com/api/v1/job/posts/{post_id}
21
+ // Returns { code:0, data:{ job_post_detail:{...}, recommend_job_post_List:[...] } }
22
+ //
23
+ // ============================================================
24
+ // Portal / website-path contexts (probed 2026-05):
25
+ //
26
+ // "job" → 社招官网 (social/experienced hire) ~82 posts
27
+ // Referer: https://careers.iqiyi.com/job
28
+ // recruit_type.parent.id="1" (社招), recruit_type.id="101" (全职)
29
+ // Detail URL: https://careers.iqiyi.com/job/position/{id}/detail
30
+ //
31
+ // "campus" → 应届生校招官网 (campus / new-grad) ~14 posts
32
+ // Referer: https://careers.iqiyi.com/campus/
33
+ // recruit_type.id="201" (正式), parent.id="2" (校招)
34
+ // Detail URL: https://careers.iqiyi.com/campus/position/{id}/detail
35
+ //
36
+ // "intern" → 实习生官网 (intern) ~92 posts
37
+ // Referer: https://careers.iqiyi.com/intern
38
+ // recruit_type.id="202" (实习), parent.id="2" (校招)
39
+ // Detail URL: https://careers.iqiyi.com/intern/position/{id}/detail
40
+ //
41
+ // ============================================================
42
+ // Filter taxonomy (from GET /api/v1/config/job/filters/job, probed 2026-05):
43
+ //
44
+ // DIMENSION 1 — job_function_id_list (职位类别, 2-level hierarchy)
45
+ // Parent "技术" id:7434839649275742501
46
+ // 研发 id:7434839421294070035
47
+ // 算法 id:7434839934659873034
48
+ // Parent "产品" id:7434840292215736586
49
+ // Parent "设计" id:7434840107360061732
50
+ // Parent "运营" id:7434840970217654564
51
+ // 产品运营 id:7434840444482308364
52
+ // 内容运营 id:7434841054879451446
53
+ // 数据分析 id:7434840662343174454
54
+ // Parent "内容制作" id:7434840477089040652
55
+ // 导演 id:7434841298710907145
56
+ // 责编 id:7434841298711005449
57
+ // 评估策划 id:7434841764354066739
58
+ // 视频制作 id:7434841763418573068
59
+ // Parent "经纪" id:7434841763417753895
60
+ // Parent "市场&商务" id:7434841893740267786
61
+ // 商务 id:7434841893487692059
62
+ // 公关 id:7434842142922819881
63
+ // 市场活动 id:7434842142922869033
64
+ // 商业市场 id:7434841764362307867
65
+ // 广告投放 id:7512307407088208164
66
+ // 内容宣推 id:7434842241422215475
67
+ // Parent "销售" id:7434843070048717094
68
+ // 销售 id:7434842895259879743
69
+ // 销售策划 id:7434842715382843660
70
+ // Parent "游戏" id:7434844424062535947
71
+ // 游戏美术 id:7434845034699147539
72
+ // Parent "客服、审核与运营支持" id:7512307407469840681
73
+ // 运营支持 id:7512307631945828619
74
+ // 客服 id:7512307201551616306
75
+ // Parent "财务" id:7434843924135184679
76
+ // Parent "人力资源" id:7434844421612734757
77
+ // Parent "管理" id:7434845114970130738
78
+ //
79
+ // DIMENSION 2 — location_code_list (工作地点, city codes)
80
+ // CT_11=北京 CT_125=上海 CT_190=重庆 CT_78=开封 CT_71=金华
81
+ // CT_172=扬州 CT_98=曼谷 CT_94=洛杉矶
82
+ //
83
+ // DIMENSION 3 — recruitment_id_list (招聘类型, for campus/intern)
84
+ // "201" = 正式 (campus / new-grad)
85
+ // "202" = 实习 (intern)
86
+ // "101" = 全职 (full-time, used in social hire context)
87
+ //
88
+ // ============================================================
89
+ // ---- PositionSummary field mapping (iQIYI → canonical) ----
90
+ // post_id ← item.id (stringified)
91
+ // title ← item.title
92
+ // project ← item.job_function.name (最接近 Tencent projectName)
93
+ // recruit_label ← item.recruit_type.name (e.g. "全职" / "正式" / "实习")
94
+ // bgs ← "" (iQIYI does not expose BG/事业群 in public search)
95
+ // work_cities ← city_list joined with " / " or city_info.name
96
+ // apply_url ← https://careers.iqiyi.com/{website_path}/position/{id}/detail
97
+ import { extractResumeSignals, scoreOverlap, checkResume } from "./tencent.js";
98
+ export { checkResume };
99
+ const API_ROOT = "https://careers.iqiyi.com/api/v1";
100
+ // Portal paths and their Referer URLs
101
+ const PORTAL = {
102
+ job: {
103
+ referer: "https://careers.iqiyi.com/job",
104
+ listPage: "https://careers.iqiyi.com/job",
105
+ detailPage: (id) => `https://careers.iqiyi.com/job/position/${encodeURIComponent(id)}/detail`,
106
+ },
107
+ campus: {
108
+ referer: "https://careers.iqiyi.com/campus/",
109
+ listPage: "https://careers.iqiyi.com/campus/",
110
+ detailPage: (id) => `https://careers.iqiyi.com/campus/position/${encodeURIComponent(id)}/detail`,
111
+ },
112
+ intern: {
113
+ referer: "https://careers.iqiyi.com/intern",
114
+ listPage: "https://careers.iqiyi.com/intern",
115
+ detailPage: (id) => `https://careers.iqiyi.com/intern/position/${encodeURIComponent(id)}/detail`,
116
+ },
117
+ };
118
+ const DEFAULT_HEADERS = {
119
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
120
+ Accept: "application/json, text/plain, */*",
121
+ "portal-platform": "pc",
122
+ };
123
+ async function callPost(path, body, portalPath) {
124
+ const url = `${API_ROOT}${path}`;
125
+ const portal = PORTAL[portalPath];
126
+ let response;
127
+ try {
128
+ response = await fetch(url, {
129
+ method: "POST",
130
+ headers: {
131
+ ...DEFAULT_HEADERS,
132
+ "Content-Type": "application/json",
133
+ "website-path": portalPath,
134
+ Referer: portal.referer,
135
+ },
136
+ body: JSON.stringify(body),
137
+ });
138
+ }
139
+ catch (err) {
140
+ return {
141
+ ok: false,
142
+ message: `network error: ${err instanceof Error ? err.message : String(err)}`,
143
+ };
144
+ }
145
+ if (!response.ok) {
146
+ return { ok: false, message: `HTTP ${response.status}: ${response.statusText}` };
147
+ }
148
+ let payload;
149
+ try {
150
+ payload = (await response.json());
151
+ }
152
+ catch (err) {
153
+ return { ok: false, message: `bad JSON: ${err instanceof Error ? err.message : String(err)}` };
154
+ }
155
+ return {
156
+ ok: payload.code === 0,
157
+ data: payload.data,
158
+ message: payload.message || (payload.code === 0 ? "ok" : "upstream error"),
159
+ };
160
+ }
161
+ async function callGet(path, portalPath) {
162
+ const url = `${API_ROOT}${path}`;
163
+ const portal = PORTAL[portalPath];
164
+ let response;
165
+ try {
166
+ response = await fetch(url, {
167
+ headers: {
168
+ ...DEFAULT_HEADERS,
169
+ "website-path": portalPath,
170
+ Referer: portal.referer,
171
+ },
172
+ });
173
+ }
174
+ catch (err) {
175
+ return {
176
+ ok: false,
177
+ message: `network error: ${err instanceof Error ? err.message : String(err)}`,
178
+ };
179
+ }
180
+ if (!response.ok) {
181
+ return { ok: false, message: `HTTP ${response.status}: ${response.statusText}` };
182
+ }
183
+ let payload;
184
+ try {
185
+ payload = (await response.json());
186
+ }
187
+ catch (err) {
188
+ return { ok: false, message: `bad JSON: ${err instanceof Error ? err.message : String(err)}` };
189
+ }
190
+ return {
191
+ ok: payload.code === 0,
192
+ data: payload.data,
193
+ message: payload.message || (payload.code === 0 ? "ok" : "upstream error"),
194
+ };
195
+ }
196
+ function summarizePosition(item, portalPath) {
197
+ const id = String(item.id ?? "");
198
+ const portal = PORTAL[portalPath];
199
+ // Build work_cities: prefer city_list for multi-city, fall back to city_info
200
+ const cityList = item.city_list ?? [];
201
+ let work_cities;
202
+ if (cityList.length > 1) {
203
+ work_cities = cityList.map((c) => c.name ?? "").filter(Boolean).join(" / ");
204
+ }
205
+ else {
206
+ work_cities = item.city_info?.name ?? (cityList[0]?.name ?? "");
207
+ }
208
+ return {
209
+ post_id: id,
210
+ title: item.title ?? "",
211
+ project: item.job_function?.name ?? "",
212
+ recruit_label: item.recruit_type?.name ?? "",
213
+ bgs: "",
214
+ work_cities,
215
+ apply_url: id ? portal.detailPage(id) : portal.listPage,
216
+ };
217
+ }
218
+ // ---------- searchPositions ----------
219
+ export async function searchPositions(opts = {}) {
220
+ const pageSize = Math.max(1, Math.min(100, opts.pageSize ?? 20));
221
+ const page = Math.max(1, opts.page ?? 1);
222
+ const offset = (page - 1) * pageSize;
223
+ const keyword = (opts.keyword ?? "").trim().slice(0, 60);
224
+ const portalPath = opts.portal ?? "job";
225
+ const asStringList = (v) => {
226
+ if (v === undefined)
227
+ return undefined;
228
+ const arr = Array.isArray(v) ? v : [v];
229
+ return arr.map(String);
230
+ };
231
+ const payload = {
232
+ keyword,
233
+ limit: pageSize,
234
+ offset,
235
+ portal_type: 3,
236
+ portal_entrance: 1,
237
+ language: "zh",
238
+ };
239
+ const jobFunctionIdList = asStringList(opts.jobFunctionIdList);
240
+ if (jobFunctionIdList?.length) {
241
+ payload.job_function_id_list = jobFunctionIdList;
242
+ }
243
+ const cityCodeList = asStringList(opts.cityCodeList);
244
+ if (cityCodeList?.length) {
245
+ payload.location_code_list = cityCodeList;
246
+ }
247
+ const recruitmentIdList = asStringList(opts.recruitmentIdList);
248
+ if (recruitmentIdList?.length) {
249
+ payload.recruitment_id_list = recruitmentIdList;
250
+ }
251
+ const response = await callPost("/search/job/posts", payload, portalPath);
252
+ if (!response.ok || !response.data) {
253
+ return {
254
+ ok: false,
255
+ message: response.message,
256
+ source: "careers.iqiyi.com",
257
+ portal: portalPath,
258
+ query: payload,
259
+ positions: [],
260
+ };
261
+ }
262
+ const rows = response.data.job_post_list ?? [];
263
+ return {
264
+ ok: true,
265
+ source: "careers.iqiyi.com",
266
+ portal: portalPath,
267
+ query: payload,
268
+ page,
269
+ page_size: pageSize,
270
+ total: response.data.count ?? rows.length,
271
+ positions: rows.map((r) => summarizePosition(r, portalPath)),
272
+ };
273
+ }
274
+ // ---------- fetchAllPositions ----------
275
+ export async function fetchAllPositions(opts = {}) {
276
+ const pageSize = Math.max(1, Math.min(100, opts.pageSize ?? 100));
277
+ const maxPages = Math.max(1, opts.maxPages ?? 5);
278
+ const portalPath = opts.portal ?? "job";
279
+ const bucket = [];
280
+ let total;
281
+ for (let page = 1; page <= maxPages; page++) {
282
+ const result = await searchPositions({ ...opts, page, pageSize, portal: portalPath });
283
+ if (!result.ok) {
284
+ return {
285
+ ok: false,
286
+ message: result.message,
287
+ source: "careers.iqiyi.com",
288
+ portal: portalPath,
289
+ fetched: bucket.length,
290
+ positions: bucket,
291
+ };
292
+ }
293
+ if (total === undefined)
294
+ total = result.total;
295
+ if (!result.positions.length)
296
+ break;
297
+ bucket.push(...result.positions);
298
+ if (total !== undefined && bucket.length >= total)
299
+ break;
300
+ }
301
+ return {
302
+ ok: true,
303
+ source: "careers.iqiyi.com",
304
+ portal: portalPath,
305
+ total: total ?? bucket.length,
306
+ fetched: bucket.length,
307
+ positions: bucket,
308
+ };
309
+ }
310
+ // ---------- fetchPositionDetail ----------
311
+ export async function fetchPositionDetail(postId, portal = "job") {
312
+ const id = (postId ?? "").trim();
313
+ if (!id) {
314
+ return { ok: false, source: "careers.iqiyi.com", message: "post_id is required" };
315
+ }
316
+ const response = await callGet(`/job/posts/${encodeURIComponent(id)}`, portal);
317
+ if (!response.ok || !response.data) {
318
+ return {
319
+ ok: false,
320
+ source: "careers.iqiyi.com",
321
+ post_id: id,
322
+ message: response.message || "no detail returned",
323
+ };
324
+ }
325
+ const raw = response.data.job_post_detail;
326
+ if (!raw) {
327
+ return {
328
+ ok: false,
329
+ source: "careers.iqiyi.com",
330
+ post_id: id,
331
+ message: "job_post_detail missing in response",
332
+ };
333
+ }
334
+ const cityList = raw.city_list ?? raw.city_info_list_for_delivery ?? [];
335
+ const work_cities = cityList.length
336
+ ? cityList.map((c) => c.name ?? "").filter(Boolean).join(" / ")
337
+ : (raw.city_info?.name ?? "");
338
+ const summary = summarizePosition(raw, portal);
339
+ return {
340
+ ok: true,
341
+ source: "careers.iqiyi.com",
342
+ portal,
343
+ post_id: String(raw.id ?? id),
344
+ title: raw.title ?? "",
345
+ direction: raw.sub_title ?? "",
346
+ project: raw.job_function?.name ?? "",
347
+ recruit_label: raw.recruit_type?.name ?? "",
348
+ description: raw.description ?? "",
349
+ requirements: raw.requirement ?? "",
350
+ work_cities,
351
+ apply_url: summary.apply_url,
352
+ };
353
+ }
354
+ // ---------- fetchDictionaries ----------
355
+ export async function fetchDictionaries(portal = "job") {
356
+ const response = await callGet(`/config/job/filters/${portal}`, portal);
357
+ if (!response.ok || !response.data) {
358
+ return {
359
+ ok: false,
360
+ source: "careers.iqiyi.com",
361
+ portal,
362
+ message: response.message,
363
+ };
364
+ }
365
+ const d = response.data;
366
+ const jobFunctions = (d.job_function_list ?? []).map((f) => ({
367
+ id: f.id ?? "",
368
+ name: f.name ?? "",
369
+ parent_id: f.parent?.id ?? null,
370
+ children: (f.children ?? []).map((c) => ({
371
+ id: c.id ?? "",
372
+ name: c.name ?? "",
373
+ })),
374
+ }));
375
+ const cities = (d.city_list ?? []).map((c) => ({
376
+ code: c.code ?? "",
377
+ name: c.name ?? "",
378
+ en_name: c.en_name ?? "",
379
+ }));
380
+ const recruitmentTypes = (d.recruitment_type_list ?? []).map((r) => ({
381
+ id: r.id ?? "",
382
+ name: r.name ?? "",
383
+ en_name: r.en_name ?? "",
384
+ parent_id: r.parent?.id ?? null,
385
+ }));
386
+ return {
387
+ ok: true,
388
+ source: "careers.iqiyi.com",
389
+ portal,
390
+ verified_at: new Date().toISOString(),
391
+ jobFunctions,
392
+ cities,
393
+ recruitmentTypes,
394
+ portals: [
395
+ { path: "job", name: "社招官网", note: "experienced hire (~82 posts)" },
396
+ { path: "campus", name: "应届生校招官网", note: "campus / new-grad (~14 posts)" },
397
+ { path: "intern", name: "实习生官网", note: "intern (~92 posts)" },
398
+ ],
399
+ };
400
+ }
401
+ // ---------- stub notices ----------
402
+ // careers.iqiyi.com has no public structured notices/announcements endpoint.
403
+ const STUB_SOURCE = "careers.iqiyi.com";
404
+ const STUB_MSG = "iQIYI: no public notices endpoint on careers.iqiyi.com";
405
+ export async function listNotices() {
406
+ return { ok: false, source: STUB_SOURCE, message: STUB_MSG };
407
+ }
408
+ export async function getNotice(_id) {
409
+ return { ok: false, source: STUB_SOURCE, message: STUB_MSG };
410
+ }
411
+ export async function findNoticesByQuestion(_question, _opts = {}) {
412
+ return { ok: false, source: STUB_SOURCE, message: STUB_MSG };
413
+ }
414
+ // ---------- matchResume ----------
415
+ export async function matchResume(text, opts = {}) {
416
+ const topN = Math.max(1, opts.topN ?? 5);
417
+ const candidates = Math.max(topN, opts.candidates ?? 20);
418
+ const portal = opts.portal ?? "job";
419
+ const { terms, cities } = extractResumeSignals(text ?? "");
420
+ if (!terms.length) {
421
+ return {
422
+ ok: false,
423
+ source: STUB_SOURCE,
424
+ message: "could not extract any technical signals from the text",
425
+ preview: (text ?? "").slice(0, 120),
426
+ };
427
+ }
428
+ const keyword = terms.slice(0, 3).join(" ");
429
+ const list = await searchPositions({ keyword, pageSize: 100, portal });
430
+ if (!list.ok) {
431
+ return { ok: false, source: STUB_SOURCE, message: list.message, positions: [] };
432
+ }
433
+ // Broaden pool if keyword search returns few results
434
+ let allPositions = list.positions;
435
+ if (allPositions.length < candidates) {
436
+ const broad = await searchPositions({ pageSize: 100, portal });
437
+ if (broad.ok) {
438
+ const seen = new Set(allPositions.map((p) => p.post_id));
439
+ for (const p of broad.positions) {
440
+ if (!seen.has(p.post_id)) {
441
+ allPositions = [...allPositions, p];
442
+ seen.add(p.post_id);
443
+ }
444
+ }
445
+ }
446
+ }
447
+ const scored = [];
448
+ for (const p of allPositions) {
449
+ const blob = [p.title, p.project, p.recruit_label, p.work_cities].join(" ");
450
+ const { score, reasons } = scoreOverlap(blob, terms, cities);
451
+ if (score > 0) {
452
+ scored.push({ score, position: p, reasons });
453
+ }
454
+ }
455
+ scored.sort((a, b) => b.score - a.score);
456
+ let shortlist = scored.slice(0, Math.max(topN, candidates));
457
+ if (!shortlist.length) {
458
+ shortlist = allPositions.slice(0, candidates).map((position) => ({
459
+ score: 0,
460
+ position,
461
+ reasons: [],
462
+ }));
463
+ }
464
+ const matches = shortlist.slice(0, topN).map((s) => {
465
+ const mr = s.reasons.length > 0
466
+ ? s.reasons.slice(0, 5)
467
+ : ["no specific keyword overlap — surfaced from initial keyword search"];
468
+ return {
469
+ ...s.position,
470
+ description: s.description,
471
+ requirements: s.requirements,
472
+ match_reasons: mr,
473
+ };
474
+ });
475
+ return {
476
+ ok: true,
477
+ source: STUB_SOURCE,
478
+ portal,
479
+ extracted_terms: terms,
480
+ city_preferences: cities,
481
+ matches,
482
+ note: "match_reasons surfaces overlapping keywords, not a probability of getting an interview. " +
483
+ "The only authority on selection is HR.",
484
+ };
485
+ }
486
+ // ---------- Phase 2: fetchApplicationSchema ----------
487
+ import { makeFeishuApplyFn } from "./feishu.js";
488
+ export const fetchApplicationSchema = makeFeishuApplyFn({
489
+ host: "careers.iqiyi.com",
490
+ source: "careers.iqiyi.com",
491
+ channel: "campus",
492
+ applyUrlPrefix: "https://careers.iqiyi.com/campus/position",
493
+ fetchTitle: (id) => fetchPositionDetail(id),
494
+ });