diff --git a/src/__tests__/enterprise-applications.test.ts b/src/__tests__/enterprise-applications.test.ts index 31032d6..8d0a4c5 100644 --- a/src/__tests__/enterprise-applications.test.ts +++ b/src/__tests__/enterprise-applications.test.ts @@ -42,6 +42,8 @@ import { createCandidateFeedback, deleteCandidateFeedback, fetchCandidateFeedback, + fetchApplicationMatch, + fetchApplicationTimeline, fetchCompanyApplicationDetail, fetchCompanyApplications, toBackendApplicationStatus, @@ -227,4 +229,42 @@ describe('企业端投递 / 反馈(真实模式)', () => { await updateCandidateFeedback('app-1001', { rating: 4, content: '改', tags: [] }); expect(seenMethod).toBe('PUT'); }); + + it('时间线把后端 toStatus/reason/createdAt 映射成 status/note/time', async () => { + setMockMode(false); + responder = (url) => { + expect(url).toBe(`${API_BASE_URL}/applications/app-1001/timeline`); + return okData([ + { id: 1, operatorUserId: 4, toStatus: 'SUBMITTED', reason: 'Student applied', createdAt: '2026-09-30T01:57:26' }, + ]); + }; + const timeline = await fetchApplicationTimeline('app-1001'); + expect(timeline).toEqual([ + { status: 'submitted', note: 'Student applied', time: '2026-09-30T01:57:26' }, + ]); + }); + + it('匹配状态打 /applications/{id}/match 并透传结构', async () => { + setMockMode(false); + responder = (url) => { + expect(url).toBe(`${API_BASE_URL}/applications/1/match`); + return okData({ + status: 'PENDING_BOTH', + matchVersion: 'RULE_THRESHOLD_V1', + candidateScoreSource: 'MANUAL_INTEREST', + hrScoreSource: 'HR_MANUAL', + companyRequiredCandidateScore: 60, + studentRequiredHrScore: 60, + applicationId: 1, + jobId: 166, + companyId: 48, + candidateId: 4, + }); + }; + const match = await fetchApplicationMatch('1'); + expect(match.status).toBe('PENDING_BOTH'); + expect(match.companyRequiredCandidateScore).toBe(60); + expect(match.studentRequiredHrScore).toBe(60); + expect(match.jobId).toBe(166); + }); }); diff --git a/src/api/api.ts b/src/api/api.ts index ec1a2f1..92e9c94 100644 --- a/src/api/api.ts +++ b/src/api/api.ts @@ -191,8 +191,10 @@ import type { CarrierPhoneResult, ApplicationDetail, ApplicationItem, + ApplicationMatch, ApplicationStatus, ApplicationTimelineEvent, + BackendApplicationTimelineEvent, CandidateFeedback, CandidateFeedbackInput, CompanyApplicationItem, @@ -2188,11 +2190,31 @@ export async function fetchApplicationTimeline(id: string): Promise( - `/applications/${id}/timeline`, - { method: 'GET', auth: true }, - ); - return Array.isArray(data) ? data : data.timeline ?? []; + // 后端 timeline 节点字段是 toStatus / reason / createdAt,与前端 ApplicationTimelineEvent + // 的 status / note / time 不同构,必须在此映射,否则时间线渲染成一片空白。 + const raw = await request(`/applications/${id}/timeline`, { + method: 'GET', + auth: true, + }); + return (raw ?? []).map((e) => ({ + status: toApplicationStatus(e.toStatus), + note: e.reason, + time: e.createdAt, + })); +} + +/** + * 获取投递匹配状态(学生本人或岗位所属企业可见) + * @method GET + * @url /applications/{id}/match + * @param id 投递记录 ID + * @returns Promise 匹配状态 + */ +export async function fetchApplicationMatch(id: string): Promise { + if (isMockMode()) { + throw new Error('mock 模式下无匹配数据'); + } + return request(`/applications/${id}/match`, { method: 'GET', auth: true }); } /** diff --git a/src/api/index.ts b/src/api/index.ts index 01d8534..5ab18d8 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -210,6 +210,7 @@ export { fetchMyApplications, fetchApplicationDetail, fetchApplicationTimeline, + fetchApplicationMatch, withdrawApplication, toBackendApplicationStatus, fetchCompanyApplications, diff --git a/src/types/application.ts b/src/types/application.ts index 081c7a5..194f0d8 100644 --- a/src/types/application.ts +++ b/src/types/application.ts @@ -64,6 +64,37 @@ export interface ApplicationTimelineEvent { note?: string; } +/** 后端投递时间线节点(GET /applications/{id}/timeline 实测结构) */ +export interface BackendApplicationTimelineEvent { + id: number; + operatorUserId: number; + /** 变更后状态(大写 token,如 SUBMITTED) */ + toStatus: string; + /** 变更原因,如 "Student applied" */ + reason?: string; + createdAt: string; +} + +/** 投递匹配状态(GET /applications/{id}/match,学生/企业双方可见) */ +export interface ApplicationMatch { + /** 匹配状态,如 PENDING_BOTH */ + status: string; + /** 匹配规则版本,如 RULE_THRESHOLD_V1 */ + matchVersion: string; + /** 候选人意向分来源,如 MANUAL_INTEREST */ + candidateScoreSource: string; + /** HR 适合度分来源,如 HR_MANUAL */ + hrScoreSource: string; + /** 企业要求的最低候选人意向分(0-100) */ + companyRequiredCandidateScore: number; + /** 学生要求的最低 HR 适配分(0-100) */ + studentRequiredHrScore: number; + applicationId: number; + jobId: number; + companyId: number; + candidateId: number; +} + /** 分页结果(后端统一分页结构) */ export interface PageResult { items: T[]; diff --git a/src/types/index.ts b/src/types/index.ts index ce7c808..66060d0 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -321,8 +321,10 @@ export type { QualificationInfo, QualificationLicense, QualificationLicenseType export type { ApplicationDetail, ApplicationItem, + ApplicationMatch, ApplicationStatus, ApplicationTimelineEvent, + BackendApplicationTimelineEvent, CandidateFeedback, CandidateFeedbackInput, CompanyApplicationItem,