# 后端联调 · 前端实现说明 > 适用范围:`有职`(youzhi)App 前端对接《职来后端接口联调说明》(`job.youthome.com.cn` 规范) > 关联文件:`src/api/http.ts`、`src/api/auth.ts`、`src/lib/token-store.ts`、`src/context/AuthContext.tsx`、`src/types/user.ts` > 本文只记录**前端已落地的地基与契约映射**,完整接口字段以运行时 OpenAPI 为唯一依据(规范 §7)。 ## 一、本次已落地(地基,不涉及业务端点) | 层 | 文件 | 内容 | |---|---|---| | 请求层 | `src/api/http.ts` | 统一出口:`Base URL` + `Bearer` 注入 + 信封解析 + **401 单飞刷新** + `X-Request-Id` + 状态码语义 | | 令牌 | `src/lib/token-store.ts` | `accessToken` + `refreshToken` 内存单例 + AsyncStorage 持久化 | | 鉴权 | `src/api/auth.ts` | `sendSmsCode` / `loginByPassword` / `loginBySms` / `fetchCurrentUser` / `requestLogout` / `registerCompany` | | 会话 | `src/context/AuthContext.tsx` | `setSession(access,refresh,expiresAt)`;启动时 `hydrateTokens`;注册 401 清会话回调 | | 角色 | `src/types/user.ts` + `auth.ts` | `AppRole` 扩 `admin`;`mapBackendRole(STUDENT→candidate / COMPANY→enterprise / ADMIN→admin)` | | 环境 | `app.config.js` | 默认 `apiBaseUrl = https://job.youthome.com.cn/api/v1`(保留 `API_BASE_URL` 环境变量覆盖) | | mock 开关 | `app.config.js` + `http.ts` | `extra.apiUseMock`(默认 `true`);`isMockMode()` 决定业务函数走 mock 还是真实请求;联调期置 `false` | 业务端点按 **mock 开关** 分阶段落地:开发期(`apiUseMock=true`)返回本地数据不触网,联调期(`false`)直连后端。`jobs` / `profile` / `messages` 等 100+ mock 函数**逐个替换**为真实 `request()` 调用,页面与 hook 零改动。 ## 二、规范条款逐条对照 | 规范 | 前端现状 | |---|---| | §1 基地址 / 健康检查 / OpenAPI | 基地址已接 `API_BASE_URL`;`checkHealth()` 已实现对 `/health`(UP 判定);OpenAPI 为后端准绳,前端不手抄 | | §3 信封 `code/message/data/requestId/timestamp` | `request()` 解析 envelope;`code!==200` 抛 `ApiError`;后端 `message` 优先(更具体),无 message 时回退状态码文案 | | §3 `Authorization: Bearer` | 自动注入;匿名接口(登录/短信/刷新)显式 `auth:false` 不带头 | | §4 双 Token + `/auth/refresh` | `token-store` 持久化 `access+refresh`;401 触发刷新 | | §4 一次串行刷新 + 重放 | 模块级 `refreshPromise` 单飞闸门:并发 401 只刷一次,成功后重放原请求 | | §4 刷新失败清会话 | 清 Token + `unauthorizedHandler`(AuthContext 注册为清登录态) | | §4 Token 不打印/上报/进 URL | 令牌仅在 `http.ts` 内层使用,错误对象不回吐原始 Token | | §5 角色以后端 `/auth/me` 为准 | `fetchCurrentUser()` 返回映射后角色;密码登录页(`P2PasswordLogin`)登录后调 `/auth/me` 并 `setRole`,与本地选择不一致时纠正路由 | | §6 企业注册 | `registerCompany()` 已实现对 `/auth/company/register`;客户端不提交 `userId/role/verified/passwordHash` | | §8 状态码语义 | `pickHttpMessage` 覆盖 400/401/403/404/409/429/500/503;网络异常降级为 `httpStatus=0` | | §10 安全 | 不写 Token/验证码/密码到日志或前端 DOM;`X-Request-Id` 用于联调报障 | ## 三、业务端点转换进度(mock 开关 + 全量 fetch 函数,2026-09-24 收口) > 转换规则:保留函数签名与返回类型 `T`(即 AGENTS.md「保留签名、只换函数体」),`if (isMockMode())` 分支保留本地数据,真实分支走 `request(path, { auth, query })`。路径按规范 §7 重映射(旧 `@url` 多带 `/api` 前缀、未带 `/v1`,已校正)。 ### 3.1 已落地真实端点(2026-09-28 经 OpenAPI 校正) > 路径以 `docs/联调路径校正映射.md` 为权威逐函数表;下表仅列本次确认可直连的端点(ALIGNED + WRONG→REAL)。 | 函数 | 真实路径 | 鉴权 | 说明 | |---|---|---|---| | `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` | 否 | 已对齐 | > 写函数 `saveJobPreference`(PUT /profile/me/preferences)已校正路径;其余 `saveResume*` / `saveWeightSettings` / `saveSalaryRecord` 等经审计属「幽灵域」(后端无 OpenAPI 契约),已退回 mock-only。 ### 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 兜底。 ## 五、命名待确认(非阻塞) - 域名 `youthome.com.cn`(青年家)与 App 品牌「有职 / youzhi」不一致;Storage 前缀仍为历史遗留 `qingsongpin:`,**勿擅自改名**(会导致老用户本地态失效)。