# 患者头像 ID 精度丢失修复总结 > 说明:本文件汇总来自 `医生首页` 患者动态(`patientActivities`)中,用户 ID 精度丢失导致头像请求错误的问题,以及解决过程、关键修改点、测试要点与后续建议。文档以中文撰写。 ## 一、问题简介 ✅ 在页面 `src/pages/doctor/index/index.vue` 中,患者动态数据原始接口返回如下(示例): ``` {"id":"1991836353626906625","userId":1988147181088956418,...} ``` 但浏览器/小程序客户端请求头像的 URL 为: ``` https://wx.baiyun.work/user/avatar/1988147181088956400 ``` 可见 `userId` 发生了数值精度丢失,最后几位被截断或舍入,导致错误的头像 URL 发起请求,无法加载正确头像。 ## 二、根本原因(分析)🔎 - JavaScript 的 Number 类型为 IEEE 64-bit 双精度浮点(double),整数精度仅保证到 Number.MAX_SAFE_INTEGER(2^53 - 1,约 9e15)。 - 接口返回的 ID 属于大整数(大于 2^53),若前端把这些 ID 当作 Number 存储或解析(例如 JSON.parse 默认把数字解析为 Number),则会发生精度丢失。 - 精度丢失会在后续使用该 ID 构造 URL、缓存 key 或其它字符串拼接中体现出来(例如拼接头像 URL),导致请求错误。 ## 三、修复要点(已实施的核心变更)🔧 1. **在 API 层/前端解析层把 ID 强制转换为字符串(string)** - 关键思路:无论后端返回时 ID 是字符串还是数字,前端都要统一按字符串处理。这样不会被 Number 精度限制所影响。 - 示例:在 `api` 的响应处理或 `userActivity` 的接口方法中,转换记录字段: ```ts // src/api/userActivity.ts (示例) const resp = response.data as any if (resp && resp.code === 200 && resp.data) { const safeRecords = resp.data.records.map((r: any) => ({ ...r, id: r.id === null ? null : String(r.id), userId: r.userId === null ? null : String(r.userId), relatedEntityId: r.relatedEntityId === null ? null : String(r.relatedEntityId) })) // 继续处理 safeRecords... } ``` 2. **修改 TypeScript 类型** - 把 API 接口里所有表示主键/外键(如 `id`, `userId`, `relatedEntityId`)**类型从 `number` 改为 `string`**,或使用 `string | null`。 3. **前端组件中使用字符串 ID** - 在 `getPatientAvatar` 和任何缓存键、URL 拼接处确保使用字符串:`const idStr = String(userId)`(注意这是在 ID 还未丢失的情况下才有效)。 - 更好方案:在接口处理入口就把所有记录的 `userId` 等字段转为 `string`,前端组件直接使用。 4. **缓存 key 的统一** - 把 `avatarCache` 的 key 统一为字符串,避免使用 Number 当 key: ```ts // avatarCache.set(String(userId), tempFilePath) // avatarCache.get(String(userId)) ``` 5. **避免在 JSON.parse 后直接使用数字** - 如果从后端一端得到大整数并要使用 `JSON.parse`,可以用 `json-bigint` 之类的库或 reviver,将 ID 字段解析为字符串。示例: ```ts // 使用 JSONBig(json-bigint 库) import JSONbig from 'json-bigint' const resp = JSONbig.parse(rawResponse) // JSONbig 能保留大整数字符串或 BigInt,视配置而定。 ``` 或者使用 reviver: ```ts const parsed = JSON.parse(raw, (k, v) => { if (['id', 'userId', 'relatedEntityId'].includes(k)) return v === null ? null : String(v) return v }) ``` ## 四、代码片段(推荐)📄 1) API 层响应转换(Node/TS 或前端 API 出片) ```ts function normalizeIdsInRecord>(record: T): T { return { ...record, id: record.id === null ? null : String(record.id), userId: record.userId === null ? null : String(record.userId), relatedEntityId: record.relatedEntityId === null ? null : String(record.relatedEntityId) } } // 使用: const safeRecords = resp.data.records.map(normalizeIdsInRecord) ``` 2) 组件层防护(已在 `index.vue` 中使用,一并保证): ```ts // 在 getPatientAvatar 中 const idStr = String(userId) // 确保传入的是字符串 // 拼接 URL const url = `https://wx.baiyun.work/user/avatar/${idStr}` ``` 3) Typescript 类型建议(定义): ```ts export interface ActivityRecord { id: string | null userId: string | null relatedEntityId?: string | null activityType: string activityDescription?: string createTime: string // ... other fields } ``` ## 五、验证(测试步骤)✅ 1. 使用原始接口返回样例(例如请求中包含大整数 ID)进行手动或自动测试:确保前端发出的头像请求 URL 中的 ID 与服务端返回的 ID 完全一致(逐字符匹配)。 2. 检查 Network 面板或小程序调试器中 `uni.downloadFile` 请求 URL,确认使用字符串化的 ID。 3. 检查头像缓存是否正确命中: - 首次请求时,头像会下载并存入 `avatarCache`(键为字符串)。 - 再次打开页面或刷新时,`avatarCache.get` 能成功返回缓存路径。 4. 编写小型自动化/单元测试来验证 `normalizeIdsInRecord` 或 API 层 transform 的行为,确保数字和字符串输入都能正确转换为字符串输出。 ## 六、回溯影响点(代码库中需要检查的位置)📌 - `src/api` 下所有接口返回中含 `id`, `userId`, `relatedEntityId` 的地方 - `src/pages/**` 中对 `userId` 进行拼接或作为缓存 key 的地方 - `avatarCache` 的实现(键类型) - `uni.getStorageSync('user_info')` / `fetchUserInfo` 中是否把 `id` 等字段当作 number 使用 - 任何使用 `JSON.parse` 直接处理接口原始返回值的地方 ## 七、后续建议(优先级排序)📋 1. 后端约定:建议后端在接口里统一返回 `id`、`userId` 等主键为字符串类型(比如 JSON 的字符串 '123'),避免在前端做相应处理(最高优先级)。 2. 在前端建立一个统一的 `normalizeId` 或 API 层统一解析方法,确保任何 API 响应里所有 ID 都统一为字符串(中优先级)。 3. 更新 TypeScript 的类型定义(`api` 层与前端数据模型)以使用字符串 ID(中优先级)。 4. 增加自动化测试 / 集成测试,确保大整数 ID 在前端流水线里不会被误解析为 Number(中优先级)。 5. 添加额外的 PR 模板和检查项:在代码审查时特别关注 `id` 字段类型是否被误用为 Number(低优先级)。 ## 八、迁移步骤(如果后端改回字符串)🛠️ - 升级后端返回 ID 为字符串(例如 `"1988147181088956418"`)后,前端可以把 `id` 定义为 `string` 类型,不需要额外的 `String()` 转换。 - 若后端短时间内不能更改,则继续使用前端的 normalize 层作为兼容方案。 ## 九、恢复验证的检查点(能否被回归)🔁 - 在修复后,打开患者动态页面,确认: - network 请求中头像 URL 中的 ID 与接口原始 ID 一致 - 患者头像显示正常(不再请求到 1988147181088956400 的错误文件) - 缓存正常命中(`avatarCache`)