From 86c543c3d7e482b60e8088115bd134f4482c8ec0 Mon Sep 17 00:00:00 2001 From: doublex001 <16157008+doublex001@user.noreply.gitee.com> Date: Mon, 28 Sep 2026 16:55:13 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=BD=92=E6=A1=A3=E8=81=94=E8=B0=83?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E6=A0=A1=E6=AD=A3=E6=98=A0=E5=B0=84=E4=B8=8E?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=97=AE=E9=A2=98=E6=B8=85=E5=8D=95=E7=8A=B6?= =?UTF-8?q?=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端联调前端实现.md §3 按 OpenAPI 校正结果重标已落地真实端点 - 接口联调问题清单.md 新增『零、进展更新』:89 函数分类(ALIGNED 6/WRONG→REAL 14/PHANTOM 69)、P0-1/P0-2/P1-1 标记已解决 - 新增 docs/联调路径校正映射.md 逐函数权威表 - 新增 P2-2 风险:真实分页信封为 {items} 而非 mock 的 {content} --- docs/联调路径校正映射.md | 109 ++++++++++++++++++++++++ 后端联调前端实现.md | 53 ++++++++---- 接口联调问题清单.md | 125 ++++++++++++++++++++++++++++ 职来后端接口联调说明.docx | Bin 0 -> 43921 bytes 4 files changed, 269 insertions(+), 18 deletions(-) create mode 100644 docs/联调路径校正映射.md create mode 100644 接口联调问题清单.md create mode 100644 职来后端接口联调说明.docx diff --git a/docs/联调路径校正映射.md b/docs/联调路径校正映射.md new file mode 100644 index 0000000..6ffee61 --- /dev/null +++ b/docs/联调路径校正映射.md @@ -0,0 +1,109 @@ +# 联调路径校正映射表(基于 job.youthome.com.cn OpenAPI 实测) + +> 生成日期:2026-09-28 | 数据源:`scripts/openapi.json`(182 条真实路径 / 220 path-methods) +> 审计范围:api.ts 内全部 `request()` 调用(89 个函数) +> 结论:ALIGNED(已对齐)= 6,WRONG→REAL(路径错但有真实等价)= 14,PHANTOM(幽灵域,后端无契约)= 69 + +## 一、已对齐(无需改路径) + +| 函数 | 真实路径 | 原标记 | +|---|---|---| +| `createJobReview` | `POST /jobs/{jobId}/reviews` | 已校正 | +| `fetchCompareData` | `/jobs/compare` | 已校正 | +| `fetchEmployeeReviews` | `GET /jobs/{jobId}/reviews` | 已校正 | +| `fetchJobDetail` | `GET /jobs/{jobId}` | 已校正 | +| `fetchOfferCompareData` | `GET /offers/compare` | 【待确认】 | +| `fetchUserProfile` | `GET /profile/me` | 已校正 | + +## 二、路径错但有真实等价端点(需改路径) + +| 函数 | 当前(错) | 应改为(真实) | 说明 | +|---|---|---|---| +| `fetchCandidateDetail` | `/enterprise/candidates/${candidateId}` | `/company/candidates/{applicationId}` | 前端猜 /enterprise/candidates,真路径在 /company 域 | +| `fetchConversation` | `/chat/conversations/${id}` | `GET /conversations/{id}` | 前端猜 /chat/conversations,真路径 /conversations/{id} | +| `fetchConversations` | `/messages/conversations` | `GET /conversations` | 前端猜 /messages/conversations,真 /conversations | +| `fetchInterviewScore` | `/enterprise/interviews/${interviewId}/score` | `GET /interviews/{interviewId}/score` | 前端猜 /enterprise/interviews,真路径在 /interviews 域 | +| `fetchInterviewScoreDraft` | `/enterprise/interviews/${interviewId}/score/draft` | `PUT /interviews/{interviewId}/score` | 真路径无 /draft 子路径,保存草稿用 PUT /interviews/{id}/score | +| `fetchInterviews` | `/messages/interviews` | `GET /interviews/me` | 前端猜 /messages/interviews,真 /interviews/me(我的面试列表) | +| `fetchJobPreference` | `/profile/me/job-preference` | `GET/PUT /profile/me/preferences` | 前端猜 /profile/me/job-preference,真路径 /profile/me/preferences | +| `fetchJobProgress` | `/tools/job-progress` | `GET /profile/me/career-progress` | 前端猜 /tools/job-progress,真 /profile/me/career-progress(统一求职进度聚合) | +| `fetchMatchResult` | `/messages/match-result` | `GET /applications/{id}/match 或 /jobs/{jobId}/match` | 前端猜 /messages/match-result,真为投递/岗位匹配诊断 | +| `fetchNotifications` | `/messages/notifications` | `GET /notifications` | 前端猜 /messages/notifications,真 /notifications | +| `fetchRankData` | `/jobs/rank` | `GET /jobs/{jobId}/competition-ranking` | 前端猜 /jobs/rank,真为按岗位竞争力排名,语义不同(需 jobId) | +| `fetchResume` | `/resume` | `GET /profile/me/resume` | 前端猜 /resume,真 /profile/me/resume(下载简历) | +| `saveJobPreference` | `/profile/me/job-preference` | `PUT /profile/me/preferences` | 同上 | +| `saveResumeBasicInfo` | `/resume/basic-info` | `POST /profile/me/resume (multipart)` | 前端猜 /resume/basic-info,真为上传简历文件(multipart),结构化字段在 PUT /profile/me | + +## 三、幽灵域(后端 OpenAPI 无契约,必须退回 mock-only) + +| 函数 | 当前(幽灵)路径 | 说明 | +|---|---|---| +| `deleteSalaryRecord` | `/salary/records/${id}` | 无 /salary 域,后端未提供 | +| `fetchAIInterviewData` | `/interview/config` | 未命中 OpenAPI | +| `fetchAITools` | `/growth/ai-tools` | 未命中 OpenAPI | +| `fetchAbilityAssessment` | `/growth/ability` | 未命中 OpenAPI | +| `fetchAboutInfo` | `/settings/about` | 未命中 OpenAPI | +| `fetchAccountCancel` | `/settings/account-cancel` | 未命中 OpenAPI | +| `fetchAccountSecurity` | `/settings/account-security` | 未命中 OpenAPI | +| `fetchAppealForm` | `/evaluation/appeal` | 未命中 OpenAPI | +| `fetchArticles` | `/growth/articles` | 未命中 OpenAPI | +| `fetchAuditReport` | `/operation/audit-report` | 未命中 OpenAPI | +| `fetchBadges` | `/user/badges` | 未命中 OpenAPI | +| `fetchDimensionScores` | `/user/dimension-scores` | 未命中 OpenAPI | +| `fetchDndSetting` | `/settings/privacy/dnd` | 未命中 OpenAPI | +| `fetchEnterpriseCertification` | `/enterprise/certification` | 未命中 OpenAPI | +| `fetchEnterpriseCommunication` | `/enterprise/communication` | 未命中 OpenAPI | +| `fetchEnterpriseEntry` | `/user/enterprise-entry` | 未命中 OpenAPI | +| `fetchEnterpriseOverview` | `/enterprise/overview` | 未命中 OpenAPI | +| `fetchGameLevels` | `/game/levels` | 未命中 OpenAPI | +| `fetchGameProgress` | `/game/progress` | 未命中 OpenAPI | +| `fetchHREvaluation` | `/evaluation/hr` | 未命中 OpenAPI | +| `fetchHollandAssessment` | `/growth/holland` | 未命中 OpenAPI | +| `fetchHollandIntro` | `/growth/holland-intro` | 未命中 OpenAPI | +| `fetchIdentityOptions` | `/tools/identity-options` | 未命中 OpenAPI | +| `fetchJobEvaluationEmptyState` | `/enterprise/job-evaluation-empty` | 未命中 OpenAPI | +| `fetchJobEvaluationManagement` | `/enterprise/job-evaluations` | 未命中 OpenAPI | +| `fetchJobManagement` | `/enterprise/job-management` | 未命中 OpenAPI | +| `fetchJobOverallScore` | `/jobs/${id}/score` | 无 /jobs/{id}/score,综合分见 /jobs/{id}/competition-ranking 或 /profile/me/candidate-scoring | +| `fetchJobPublishForm` | `/enterprise/jobs/publish-form` | 未命中 OpenAPI | +| `fetchKeywordBlock` | `/settings/privacy/keywords` | 未命中 OpenAPI | +| `fetchLegalBrand` | `/legal/brand` | 未命中 OpenAPI | +| `fetchLegalConversation` | `/legal/conversations/default` | 未命中 OpenAPI | +| `fetchLegalDocument` | `/settings/legal-document/${id}` | 无 /legal 域,后端未提供 | +| `fetchLegalFAQs` | `/legal/faqs` | 未命中 OpenAPI | +| `fetchLegalMembership` | `/legal/membership` | 未命中 OpenAPI | +| `fetchLegalOrders` | `/legal/orders` | 未命中 OpenAPI | +| `fetchLegalPlans` | `/legal/plans` | 未命中 OpenAPI | +| `fetchLegalServiceRequests` | `/legal/service-requests` | 未命中 OpenAPI | +| `fetchLegalServices` | `/legal/services` | 未命中 OpenAPI | +| `fetchLegalTemplates` | `/legal/templates` | 未命中 OpenAPI | +| `fetchMenuItems` | `/user/menus` | 未命中 OpenAPI | +| `fetchNotificationSettings` | `/settings/notification` | 未命中 OpenAPI | +| `fetchOperationRules` | `/operation/rules` | 未命中 OpenAPI | +| `fetchOverallScore` | `/user/overall-score` | 未命中 OpenAPI | +| `fetchPendingOfferCount` | `/offers/pending/count` | 无 /offers/pending/count,可用 /offers/statistics 替代(需确认) | +| `fetchPersonalDataExport` | `/settings/personal-data-export` | 未命中 OpenAPI | +| `fetchPrivacyCenter` | `/settings/privacy-center` | 未命中 OpenAPI | +| `fetchPrivacyDetailSetting` | `/settings/privacy/${id}` | 无 /settings 域,后端未提供 | +| `fetchPrivacySettings` | `/settings/privacy` | 未命中 OpenAPI | +| `fetchQualifications` | `/settings/qualifications` | 未命中 OpenAPI | +| `fetchResumeExportSettings` | `/resume/export-settings` | 无 /resume/export-settings,导出设置本地维护 | +| `fetchResumeSavedAt` | `/resume/saved-at` | 无 /resume/saved-at,简历保存时间可由 /profile/me/resume-versions 派生 | +| `fetchSalaryBrackets` | `/tools/salary-brackets` | 未命中 OpenAPI | +| `fetchSalaryData` | `/jobs/${id}/salary` | 无 /jobs/{id}/salary,后端未提供薪资明细端点 | +| `fetchSalaryRecords` | `/salary/records` | 未命中 OpenAPI | +| `fetchSalaryToolLinks` | `/tools/salary-related` | 未命中 OpenAPI | +| `fetchSkillCloud` | `/tools/skill-cloud` | 未命中 OpenAPI | +| `fetchSkillLearningPath` | `/tools/skill-learning-path` | 未命中 OpenAPI | +| `fetchTalentRecommendation` | `/enterprise/talent-recommendation` | 未命中 OpenAPI | +| `fetchUserStats` | `/user/stats` | 未命中 OpenAPI | +| `fetchWeightSettings` | `/jobs/weight-settings` | 无 /jobs/weight-settings,权重见 /profile/me/review-weights、/review-weight-templates | +| `saveResumeEducation` | `/resume/education` | 无 /resume/education,简历整体由 /profile/me/resume + resume-versions 管理 | +| `saveResumeExperience` | `/resume/experience` | 同上 | +| `saveResumeExportSettings` | `/resume/export-settings` | 未命中 OpenAPI | +| `saveResumeProjects` | `/resume/projects` | 同上 | +| `saveResumeSelfEvaluation` | `/resume/self-evaluation` | 同上 | +| `saveResumeSkills` | `/resume/skills` | 同上 | +| `saveResumeSnapshot` | `/resume/save` | 同上 | +| `saveSalaryRecord` | `/salary/records` | 无 /salary/records | +| `saveWeightSettings` | `/weight-settings` | 无 /weight-settings | diff --git a/后端联调前端实现.md b/后端联调前端实现.md index f0d5b27..865c07c 100644 --- a/后端联调前端实现.md +++ b/后端联调前端实现.md @@ -34,36 +34,53 @@ | §8 状态码语义 | `pickHttpMessage` 覆盖 400/401/403/404/409/429/500/503;网络异常降级为 `httpStatus=0` | | §10 安全 | 不写 Token/验证码/密码到日志或前端 DOM;`X-Request-Id` 用于联调报障 | -## 三、业务端点转换进度(mock 开关 + 岗位域) +## 三、业务端点转换进度(mock 开关 + 全量 fetch 函数,2026-09-24 收口) > 转换规则:保留函数签名与返回类型 `T`(即 AGENTS.md「保留签名、只换函数体」),`if (isMockMode())` 分支保留本地数据,真实分支走 `request(path, { auth, query })`。路径按规范 §7 重映射(旧 `@url` 多带 `/api` 前缀、未带 `/v1`,已校正)。 -### 3.1 已完成(岗位域 · 候选侧读取) +### 3.1 已落地真实端点(2026-09-28 经 OpenAPI 校正) -| 函数 | mock 分支 | 真实路径(§7) | 鉴权 | 说明 | -|---|---|---|---|---| -| `fetchJobList` | 本地筛选/排序/分页 | `GET /jobs` | 否 | query 映射:`pageSize→size`;数组多选逗号拼接;`keyword/industry/city/education` 等空值跳过 | -| `fetchJobDetail` | 本地详情 | `GET /jobs/{id}` | 否 | 公开岗位详情 | -| `fetchJobOverallScore` | 本地八维缩放 | `GET /jobs/{id}/score` | 否 | 综合评分;响应结构以 OpenAPI 为准 | -| `fetchJobPreference` | `resolveJobPreference()` 派生 | `GET /profile/me/job-preference` | 是 | 求职者偏好(§5 STUDENT 前缀) | -| `fetchFavorites` | 本地收藏子集 | `GET /profile/me/favorites` | 是 | 当前用户收藏 | -| `fetchJobProgress` | 本地进度 | `GET /tools/job-progress` | 否 | 求职进度工具数据 | +> 路径以 `docs/联调路径校正映射.md` 为权威逐函数表;下表仅列本次确认可直连的端点(ALIGNED + WRONG→REAL)。 -> 鉴权列「是」= 默认带 Bearer;「否」= `auth:false` 匿名读取(§7 公开岗位)。 +| 函数 | 真实路径 | 鉴权 | 说明 | +|---|---|---|---| +| `fetchJobList` | `GET /jobs` | 否 | 分页信封 `{items}` | +| `fetchJobDetail` | `GET /jobs/{id}` | 否 | 公开岗位详情 | +| `fetchJobPreference` | `GET /profile/me/preferences` | 是 | 原 `/profile/me/job-preference` 已校正 | +| `fetchFavorites` | `GET /jobs/favorites` | 是 | 原 `/profile/me/favorites` 已校正 | +| `fetchJobProgress` | `GET /profile/me/career-progress` | 否 | 原 `/tools/job-progress` 已校正 | +| `fetchEmployeeReviews` | `GET /jobs/{id}/reviews` | 否 | | +| `createJobReview` | `POST /jobs/{id}/reviews` | 是 | body `{content}` | +| `fetchCompareData` | `GET /jobs/compare` | 否 | | +| `fetchUserProfile` | `GET /profile/me` | 是 | | +| `fetchResume` | `GET /profile/me/resume` | 是 | 原 `/resume` 已校正(修复双前缀 bug) | +| `fetchConversations` | `GET /conversations` | 是 | 原 `/messages/conversations` 已校正 | +| `fetchNotifications` | `GET /notifications` | 是 | 原 `/messages/notifications` 已校正 | +| `fetchInterviews` | `GET /interviews/me` | 是 | 原 `/messages/interviews` 已校正 | +| `fetchConversation` | `GET /conversations/{id}` | 是 | 原 `/chat/conversations/{id}` 已校正 | +| `fetchRankData` | `GET /jobs/{jobId}/competition-ranking` | 否 | 原 `/jobs/rank` 已校正(修复 `${jobId}` 未插值 bug) | +| `fetchCandidateDetail` | `GET /company/candidates/{applicationId}` | 是 | 原 `/enterprise/candidates` 已校正 | +| `fetchInterviewScore` | `GET /interviews/{id}/score` | 是 | 原 `/enterprise/interviews` 已校正 | +| `fetchInterviewScoreDraft` | `PUT /interviews/{id}/score` | 是 | 草稿复用评分端点,原 `/enterprise/.../draft` 已校正 | +| `fetchOfferCompareData` | `GET /offers/compare` | 否 | 已对齐 | -### 3.2 待转(下一阶段,按业务域分批) +> 写函数 `saveJobPreference`(PUT /profile/me/preferences)已校正路径;其余 `saveResume*` / `saveWeightSettings` / `saveSalaryRecord` 等经审计属「幽灵域」(后端无 OpenAPI 契约),已退回 mock-only。 -- **岗位详情子资源**:`fetchSalaryData` / `fetchEmployeeReviews` / `createJobReview` / `fetchCompareData`(仍处于 `/api/jobs/:id/...` 旧路径,需校正)。 -- **企业端岗位**:`fetchJobManagement` / `fetchJobPublishForm` / `fetchJobEvaluationManagement` / `fetchJobEvaluationEmptyState`(§7 未列,需 OpenAPI 确认前缀)。 -- **简历 / 资料**:`fetchResume` / `fetchUserProfile` / `fetchProfileXxx`(STUDENT 前缀)。 -- **消息 / 沟通**:`fetchConversations` / `fetchNotifications` / `fetchMessages`(§7 未列)。 -- **其余公开读取**:`fetchJobFilters` / `fetchJobSearch` 等若后端拆成独立端点,需补映射。 +### 3.1.2 幽灵域(本轮批量转换的其余 62 个 fetch 函数,2026-09-28 校正) -## 四、明确未做(下一阶段) +> 经 OpenAPI 审计,这 62 个函数所属域(/growth、/user、/tools、/game、/interview、/evaluation、/enterprise、/legal、/settings、/salary、/operation 等)**后端均无契约**。处理方式:保留 `if(isMockMode())` mock 分支,**真实态不再调用 `request()`(翻真实开关也不触网)**,待后端补齐契约后再接。原占位 `【待确认】` 标记已全部清除。逐函数明细见 `docs/联调路径校正映射.md` §三。 + +### 3.2 待转 / 明确不接后端 + +- **设备本地能力(保持 mock,无后端等价物)**:`fetchCacheSize`(存储占用)、`fetchCarrierPhone`(运营商取号 SDK)、`fetchWechatLoginResult`(微信登录态)、`fetchOnboardingProfile`(本地引导资料唯一真相源)、`fetchOnboardingChecklist`(本地清单派生)、`fetchDeleteAllData`(本地清空)。 +- **非 fetch 写函数(不在本次 fetch 范围,后续接)**:`sendChatMessage`、`saveResume*` 系列、`saveOnboardingProfile`、`toggleFavorite`、`saveJobPreference` 等写操作——需 PUT/POST body 映射 + 保留本地 storage 回退作为真实后端未就绪时的兜底。 + +## 四、明确未做(写入类 + 实时能力,下一阶段) - **WebSocket**:`wss://job.youthome.com.cn/api/v1/ws` 未接入(无对应实时功能)。 - **企业入驻状态机**:`/companies/me/onboarding` 的 `missingFields` / `allowedActions` 驱动 UI 尚未接。 - **其余入口页角色纠正**:仅密码登录页已接 `/auth/me`;微信登录、验证码登录等入口需在登录后补齐角色权威化。 +- **写入类函数**:见 §3.2 非 fetch 写函数(sendChatMessage / saveResume* / saveOnboardingProfile / toggleFavorite / saveJobPreference 等),需补 body 映射与本地 storage 兜底。 ## 五、命名待确认(非阻塞) diff --git a/接口联调问题清单.md b/接口联调问题清单.md new file mode 100644 index 0000000..4fe868c --- /dev/null +++ b/接口联调问题清单.md @@ -0,0 +1,125 @@ +# 职来(有职)前端 · 已转换接口(已"调过")存在的问题 + +> 生成日期:2026-09-28 +> 范围界定:**本文只讨论已在代码中实现真实分支(即已转换为直连后端、存在 `request()` 调用)的接口**存在的问题。 +> 不纳入:纯 mock 接口(`sendChatMessage`、`loginByPhone`、`saveOnboardingProfile`、`fetchOnboardingProfile`、设备本地能力 `fetchCacheSize` / `clearAppCache` / `fetchCarrierPhone` / `fetchWechatLoginResult` 等);明确尚未启动的 WebSocket、企业入驻状态机。 +> 说明:地基(统一请求层、双 Token、401 刷新、角色映射、健康检查)实现正确,不在本文问题范围内。 + +--- + +## 零、进展更新(2026-09-28 经 OpenAPI 实机校正) + +> 本轮以 `https://job.youthome.com.cn/api/v1/v3/api-docs`(OpenAPI 3.1.0,182 路径 / 220 path-method)为唯一准绳,对 `api.ts` 全部 `request()` 函数做了实机验证(测试号 `19900009021` 登录取 token 后逐一打真实路径,路径返回业务错误码 6001/7001/8002/8305 等即视为「路径存在、契约待对齐」,网关 404 视为「路径不存在」)。 + +### 审计结论(89 个 request 函数) + +- **ALIGNED(6)**:路径与 OpenAPI 完全一致,翻开关即直连。 +- **WRONG→REAL(14)**:路径前端猜错但有真实等价端点,已改路径(例:`/messages/conversations`→`/conversations`、`/resume`→`/profile/me/resume`、`/jobs/rank`→`/jobs/{id}/competition-ranking`、`/enterprise/candidates`→`/company/candidates`、`/enterprise/interviews`→`/interviews`)。 +- **PHANTOM(69)**:所属域后端无 OpenAPI 契约,翻开关必 404。已**退回 mock-only**(真实态不再调用 `request`,翻真实开关也不触网),待后端补齐契约再接。 + +### 标记清理 + +- `【待确认】` 标记:**0 处**(全部清除,含 `【待确认:…】` 形式)。 +- 实机验证:ALIGNED + WRONG→REAL 共 ~20 个接口真实路径均返回业务错误码(非网关 404),证明路径正确。 + +### 问题状态总览 + +| 原编号 | 状态 | 说明 | +|---|---|---| +| P0-1 | ✅ 已解决 | 89 函数已按 OpenAPI 校正并分类(ALIGNED 6 / WRONG→REAL 14 / PHANTOM 69) | +| P0-2 | ✅ 已解决 | 4 个桥接测试改为断言真实路径;幽灵域真实态断言已删 | +| P1-1 | ✅ 已解决 | `后端联调前端实现.md` §3 已按代码现状重标 | +| P1-2 | ⏳ 待办 | Token `expiresIn` 仍未处理(见下「遗留 P1-2」) | +| P1-3 | ⏳ 待办 | `/auth/me` 角色权威化仍仅在密码登录入口 | +| P1-4 | 🔶 部分 | 幽灵域写函数已退回 mock;已对齐写函数 body 形态待统一 | +| P2-1 | ⏳ 待办 | 145 处 JSDoc `@url /api` 旧前缀未清 | +| P2-2 | ⚠️ 新增风险 | 真实分页信封为 `{items}` 而非前端 mock 的 `{content}` | +| P2-3 | ⏳ 待办 | 双真相源切换策略未定 | +| P2-4 | ✅ 缓解 | 默认仍 mock;现幽灵域翻开关也不触网 | + +### 遗留 P1-2(Token 有效期) + +后端登录返回 `expiresIn`(秒数)而非 `expiresAt`;前端 `AuthContext` 仍用 `TOKEN_TTL_MS` 兜底成假 TTL。本轮未改,需单独排期修。 + +### 新增 P2-2 风险(分页信封差异) + +前端 mock 分页返回 `{content}`,真实后端返回 `{items}`(Spring Data 分页信封)。若 Hook 按 `.content` 取数,翻真实态会渲染空列表。需数据层对齐:**统一读 `items`**,mock 层返回结构向 `{items}` 收敛或 Hook 做兼容。 + +--- + +## 一、结论 + +- 已转换接口在代码层面"连接"基本铺完:`api.ts` 内 85 个 `fetch*` 读函数 + 大量 `save*/submit*/update*` 写函数**都已带有真实分支**(`if (!isMockMode()) return request(...)`),且全部受 `isMockMode()` 保护,默认 mock 态不会误触网。 +- **但契约未经后端校验**:全文件 **65 处 `【待确认】`** 标记真实路径为前端推测;**157 处 JSDoc `@url` 仍写旧 `/api` 前缀**(实际 `request()` 已去 `/api`,两者不一致)。 +- **现有测试锁定的是推测路径而非真实契约**,制造"绿灯已通过"的错觉。 +- **文档与代码脱节**:`后端联调前端实现.md` §3.2 把已转换的 `saveResume*`、`saveJobPreference` 标为"未接后端 / 后续接",与代码现状不符。 + +> 一句话:**这些接口"代码连了、契约没对"。** 当前不能宣布"已联调完成"。 + +--- + +## 二、问题清单(仅已转换接口) + +### P0 — 翻开关即爆 + +| # | 问题 | 证据 | 影响 | +|---|---|---|---| +| P0-1 | **【已解决 2026-09-28】** 原 65 个已转换接口真实路径为前端猜测(`【待确认】`),未对照 OpenAPI 校正 | 现 `【待确认】` 0 处;89 函数已分类 ALIGNED 6 / WRONG→REAL 14 / PHANTOM 69 | 翻开关后 ALIGNED+WRONG→REAL 共 ~20 接口直连真实后端(返回业务码非 404);PHANTOM 69 已退回 mock-only 不触网 | +| P0-2 | **【已解决 2026-09-28】** 测试锁定的是推测路径,不是真实契约 | `backend-bridge-remaining.test.ts` 等 4 文件已改为断言经 OpenAPI 确认的真实路径;幽灵域真实态断言已删除 | CI 绿灯现对应真实契约;81/81 桥接测试通过 | + +### P1 — 必须修,否则已转换接口不可信 + +| # | 问题 | 证据 | 影响 | +|---|---|---|---| +| P1-1 | **文档与代码脱节**:已转换的 `saveResume*`、`saveJobPreference` 被误标"未接后端" | 代码 3352/3369/3387/3413/3426/3439/3459/8684 等均有 `request(...)` 真实分支 | 状态失真,排期与验收失去依据 | +| P1-2 | **Token 有效期由前端臆造**:后端不返回 `expiresAt` 时,`AuthContext` 用 `TOKEN_TTL_MS` 兜底成假 7 天 TTL | `token-store.ts` 注释"后端可能不返回,故可选";`AuthContext.tsx:161/173` `expiresAt ?? Date.now()+TOKEN_TTL_MS`;`isTokenValid` 据此判断 | 影响所有已转换的鉴权请求:后端短过期时前端以为有效、长期不主动刷新,只能靠 401 兜底;或前端假 TTL 过期后误判登出 | +| P1-3 | **角色权威化接口 `/auth/me` 已转换,但只在密码登录入口调用** | 仅 `P2PasswordLogin.tsx:53` 调 `fetchCurrentUser`;微信登录 / 验证码登录 / 注册完成(`useRegisterFinish`)均用本地角色,不调 `/auth/me` | 多入口登录后角色可能与后端不一致,路由纠正逻辑仅在密码入口生效 | +| P1-4 | **已转换写接口 body 形态不一致**:有的包 `{skills}`、有的裸传 `items`、有的包 `{items}` | `saveResumeSkills` → `{ skills }`(3387);`saveResumeExperience` → `items`(3426,未包裹);`saveResumeEducation` → `{ items }`(3413) | 后端若要求统一 envelope,部分已转换写接口会 400 / 字段丢失 | + +### P2 — 隐患 / 卫生 + +| # | 问题 | 证据 | 影响 | +|---|---|---|---| +| P2-1 | **157 处 JSDoc `@url` 仍写旧 `/api` 前缀**,与实际 `request()` 路径(已去 `/api`)不一致 | `grep @url /api` = 157;实际 `request('/jobs')` 无 `/api` | 维护者按注释拼地址必错;JSDoc 不能作契约依据 | +| P2-2 | **【新增风险 2026-09-28】查询 / 分页参数映射是前端假设;且真实分页信封为 `{items}` 非前端 mock 的 `{content}`** | `http.ts` `buildQuery` + `fetchJobList` 注释;实机验证 `/jobs` 等列表返回 `{total,size,totalPages,page,items}` | 后端若用不同命名 / 分页模型,已转换读接口静默返回全量或错页;**Hook 若按 `.content` 取数,翻真实态会渲染空列表**(需数据层统一读 `items`) | +| P2-3 | **mock / 真实双真相源切换一致性**:mock 态 `saveResumeSnapshot` 写本地 storage 覆盖层、`fetchResume` 读本地;真实态写后端、读后端 | `saveResumeSnapshot`(mock 写 storage) vs `fetchResume`(真实读后端) | 同一设备从 mock 翻真实后,本地已填资料不出现在后端返回中,需明确迁移/清空策略 | +| P2-4 | **`apiUseMock` 默认 `true` 的隐患**:容易被误判为"联调完成"而翻开关 | `http.ts:31` 默认 `true` | 翻开关即触发 P0-1 的 65 个错误请求 | + +--- + +## 三、逐域未确认路径明细(P0-1 附录) + +> 下列域的已转换接口真实路径均带 `【待确认】`,为前端推测,未经 OpenAPI 校正。 + +| 域 | 代表函数 | 推测路径 | +|---|---|---| +| 简历写 / 读 | `saveResumeBasicInfo` / `fetchResume` | `/resume/*`、`/resume/save` | +| 消息 / 沟通 | `fetchConversations` / `fetchConversation` / `fetchMatchResult` | `/messages/*`、`/chat/conversations/{id}` | +| 求职者资料 / 偏好 | `fetchUserProfile` / `saveJobPreference` | `/profile/me`、`/profile/me/job-preference` | +| 薪资 / 工具 | `fetchSalaryRecords` / `fetchWeightSettings` | `/salary/records`、`/jobs/weight-settings`、`/tools/*` | +| 测评 / 成长 | `fetchHollandAssessment` / `fetchSkillCloud` | `/growth/*`、`/user/*`、`/tools/*`、`/game/*` | +| 企业端 | `fetchJobManagement` / `fetchEnterpriseOverview` | `/enterprise/*` | +| 法律(阿布阿诺) | `fetchLegalBrand` / `fetchLegalOrders` | `/legal/*` | +| 隐私 / 设置 / 账户 | `fetchPrivacySettings` / `fetchAccountCancel` | `/settings/*`、`/user/*`、`/operation/*` | + +完整 65 处见 `api.ts` 内 `【待确认】` 注释;精确清单应回到 `src/__tests__/backend-bridge-remaining.test.ts` 的 `FUNCS` 数组对照 OpenAPI 逐一校正。 + +--- + +## 四、整改建议(针对已转换接口) + +1. **校正契约(P0)**:以《职来后端接口联调说明》`.docx`(§7 OpenAPI,`job.youthome.com.cn`)为唯一准绳,逐函数对照 65 个 `【待确认】` 路径,命中后删注释;**未对照前不宣布"已收口"**。 +2. **让测试服务于真实契约(P0)**:把 `FUNCS` 数组路径从"占位"替换为经 OpenAPI 确认的路径;新增对拍用例(请求路径 + 响应字段 schema 校验),而非只断言前端猜的地址。 +3. **统一已转换写接口 body 形态(P1-4)**:约定"整表替换类用 `{ items }`、单对象类用对象本体",`saveResumeExperience` 当前裸传 `items` 需包裹。 +4. **修正 Token 有效期语义(P1-2)**:后端不返回 `expiresAt` 时不要用 `TOKEN_TTL_MS` 兜底成假 TTL;改为"不预判、靠 401 触发刷新 + 可选短轮询",或强制后端下发 `expiresAt`。 +5. **补全 `/auth/me` 角色权威化(P1-3)**:微信登录 / 验证码登录 / 注册完成三个入口登录后统一调 `/auth/me` 并 `setRole`,与密码入口一致。 +6. **清理 JSDoc(P2-1)**:批量清除 157 处 `@url /api` 旧前缀。 +7. **明确双真相源切换策略(P2-3)**:在登录流程或"清除缓存"中,翻真实态前提示并清空本地覆盖层,避免数据幽灵。 +8. **重写状态文档(P1-1)**:`后端联调前端实现.md` 按代码现状重标已转换接口,删除"未接后端"的误述。 + +--- + +## 五、验收口径(建议写入 AGENTS.md) + +> **"接口已联调完成"的定义应为**:① 路径经 OpenAPI 校正并删除 `【待确认】`;② 至少一次真实联调(`apiUseMock=false`)返回符合类型定义的响应;③ 测试断言的是真实路径 + 响应字段。 +> 仅凭"代码里写了 `request()`"或"测试命中占位路径"**不算完成**。 diff --git a/职来后端接口联调说明.docx b/职来后端接口联调说明.docx new file mode 100644 index 0000000000000000000000000000000000000000..dc47a246b568a0e2095b66ac146a94ab246e3fa9 GIT binary patch literal 43921 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 QpF literal 0 HcmV?d00001