# 汪圈小程序 — 功能逻辑缺陷审查报告 v2.0

> 审查日期：2026-06-19
> 基线版本：v1.0 报告审查后的最新代码
> 审查范围：全部 11 个页面、10 个 Service、25+ 个云函数、4 个类型文件、3 个 Store/Hook、1 个新增 `cloud-file` 工具模块
> 审查分支：`master`

---

## v1 → v2 修复总结

v1 报告共提出 **36 条** 问题（含跨页面系统性问题 6 条），本次审查确认的修复情况如下：

| 类别 | v1 问题数 | ✅ 已修复 | ⚠️ 部分修复 | ❌ 未修复 | 🆕 新发现 |
|------|-----------|-----------|-------------|-----------|-----------|
| 广场页 | 7 | 5 | 0 | 1 | 3 |
| 附近页 | 5 | 4 | 0 | 0 | 2 |
| 发布页 | 4 | 4 | 0 | 0 | 2 |
| 消息页 | 4 | 4 | 0 | 0 | 0 |
| 聊天页 | 6 | 6 | 0 | 0 | 2 |
| 个人资料页 | 2 | 0 | 1 | 0 | 1 |
| 资料编辑页 | 3 | 1 | 0 | 2 | 0 |
| 宠物编辑页 | 2 | 0 | 0 | 2 | 0 |
| 联系人页 | 3 | 0 | 1 | 2 | 0 |
| 我的动态页 | 3 | 2 | 0 | 0 | 2 |
| 帖子详情页 | — | — | — | — | 4 (新增页) |
| 跨页面系统 | 6 | 4 | 0 | 2 | 2 |
| **合计** | **36+** | **26** | **2** | **7** | **18** |

---

## 一、广场页 `pages/plaza/index.tsx`

### ✅ v1 修复确认

| v1 # | 问题 | 状态 | 说明 |
|------|------|------|------|
| 1 | Feed 无分页 / 无限滚动 | ✅ 已修复 | 引入 `nextCursor`、`loadNextPage`、`PageShell lowerThreshold={160}`、`loadingMore` 状态，实现了完整的无限滚动。 |
| 2 | 话题列表硬编码 | ✅ 已修复 | 引入 `topicOptions` 状态 + `normalizeTopicOptions()`，从云端 `result.topics` 动态合并。 |
| 3 | 广告卡片位置固定 | ✅ 已修复 | 引入 `createAdStartIndex()` 随机起点 + `AD_INTERVAL = 6` 间隔，每次加载重新随机化。 |
| 5 | 没有帖子详情页 | ✅ 已修复 | 新增 `pages/post-detail/index` 页面，`PostCard` 支持 `onOpen` 导航。 |
| 6 | 点赞乐观更新竞态 | ✅ 已修复 | 引入 `feedRequestSeq` + `pendingLikeIds` / `pendingLikeOverrides` 防重复机制，失败时回滚到 `previousPost` 快照。 |
| 7 | 搜索栏无功能 | ✅ 已修复 | 绑定 `onInput` + 300ms 防抖 `debouncedKeyword`，搜索时展示结果计数 + 清除按钮。 |

### ❌ v1 遗留

| v1 # | 问题 | 说明 |
|------|------|------|
| 4 | 收藏功能入口缺失 | `Post.favoritedByMe` 字段和 `feed.service.setPostFavorite()` 仍然存在，`PostCard` 仍然没有收藏操作的 UI 入口。跨页面系统性问题 #2 未解决。 |

### 🆕 v2 新发现

| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **`loadFeed` 依赖数组包含 `debouncedKeyword` 导致频繁重置** | 🟡 中 | `useEffect` (L156-158) | `loadFeed` 的 `useCallback` 依赖了 `debouncedKeyword`。防抖 300ms 后 `debouncedKeyword` 变化 → `loadFeed` 重建 → `useEffect` 触发 → 清空列表重新加载。用户每输入一个字符（停顿 300ms 后），整个列表会被清空重载一次，体验上表现为列表频繁闪烁空白再重新出现。应将 `loadFeed` 拆分为"参数变化时的首次加载"和"追加加载"，或使用 ref 追踪 keyword 变化而非作为 `useCallback` 依赖。 |
| 2 | **`PostCard` 移除了 `more` 按钮但无替代操作** | 🟡 中 | `PostCard` (L44-66) | v1 中 `PostCard` 有一个 `more` 图标按钮（三点点），v2 中完全移除了。用户无法对帖子执行更多操作（举报、分享、复制链接等），丢失了内容管理入口。 |
| 3 | **话题搜索结果与关键词搜索混合逻辑不一致** | 🟡 中 | 搜索逻辑 (L278-306) | 关键词搜索通过 `debouncedKeyword` 传给 `getFeedList`，服务端过滤。但话题筛选通过 `topic` 参数传给同一个 `getFeedList`。两者不能同时生效——用户选择了某个话题后又输入搜索词，行为不明确。UI 上也没有层级提示告诉用户"在话题内搜索"还是"全局搜索"。 |

---

## 二、附近页 `pages/nearby/index.tsx`

### ✅ v1 修复确认

| v1 # | 问题 | 状态 | 说明 |
|------|------|------|------|
| 1 | 没有调用 `updateLocation` 上报自身位置 | ✅ 已修复 | 获取位置后调用 `updateLocation(loc, true)`，`relocate` 时也会更新，`nearbyVisible === false` 时传 `null, false`。 |
| 2 | 没有尊重 `preferences.nearbyVisible` 偏好 | ✅ 已修复 | 先 `getProfile()` 读取偏好，`nearbyVisible === false` 时不请求位置、清空宠物列表、不上报位置。 |
| 3 | `likePet` 没有乐观更新和错误回滚 | ✅ 已修复 | 引入 `pendingLikeIds`、乐观设置 `likedByMe: true`，失败时回滚到 `previousLiked`。 |
| 4 | `filter: 'online'` 后端过滤能力不确定 | ✅ 已修复 | `nearby.service.ts` 新增 `filterNearbyPets()` 前端兜底过滤。`nearbyPets` 云函数也已支持 `online` filter。 |

### 🆕 v2 新发现

| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **`nearbyVisible` 为 `null` 时地图显示默认中心上海但无数据** | 🟡 中 | 初始化流程 (L32, L57-83) | `nearbyVisible` 初始值为 `null`（等待 `getProfile` 返回），此时地图渲染 `DEFAULT_CENTER`（上海），`center` 为 `null` 所以 `loadPets` 不触发，宠物列表为空。用户看到一个空地图和空列表，直到 `getProfile` 完成（可能 1-2 秒）。应显示加载指示器或骨架屏。 |
| 2 | **没有清理自身旧位置（登出 / 关闭可见性）** | 🟢 低 | `nearbyVisible === false` 分支 (L61-67) | 调用 `updateLocation(null, false)` 但没有确认云函数是否正确处理 `null` location（从数据库中移除旧坐标）。如果云函数只是不写入，用户的旧位置数据会一直留在数据库中。 |

---

## 三、发布页 `pages/publish/index.tsx`

### ✅ v1 修复确认

| v1 # | 问题 | 状态 | 说明 |
|------|------|------|------|
| 1 | 没有恢复草稿的逻辑 | ✅ 已修复 | `useEffect` 调用 `getDraft()`，通过 `hasDraftContent()` 判断后 `restoreDraft()` 回填所有表单字段。 |
| 2 | 没有内容字数上限预警 | ✅ 已修复 | `canSubmit` 检查 `textLength <= 2000`，超限时 `submit()` 弹 `toast: '正文最多 2000 字'`。 |
| 3 | 图片上传失败静默跳过 | ✅ 已修复 | 新增 `UploadPostMediaError` 自定义错误类，`showUploadFailure()` 弹窗明确告知第几张图片上传失败。 |
| 4 | 返回没有未保存提示 | ✅ 已修复 | `handleClose()` 检查 `hasContent`，弹窗询问"保存草稿？" 或 "不保存"离开。 |

### 🆕 v2 新发现

| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **`useUploadMedia` 的 `setMedia` 暴露了内部 setter** | 🟢 低 | `useUploadMedia` 解构 (L53) | `const { media, setMedia, addImages, removeMedia } = useUploadMedia([])` — 直接暴露 `setMedia` 并在 `restoreDraft` 中使用。绕过了 `useUploadMedia` 的内部管理逻辑，如果该 hook 未来添加了额外状态（如上传状态），直接 `setMedia` 不会触发这些逻辑。应通过 hook 暴露一个 `replaceMedia` 方法。 |
| 2 | **草稿恢复成功后宠物默认选中逻辑冲突** | 🟡 中 | `useEffect` (L81-87) | `getPetList` 的 `useEffect` 有 `setSelectedPetId(prev => prev \|\| (draftRestoredRef.current ? '' : list[0]._id))`。当草稿恢复成功时 `draftRestoredRef.current = true`，但如果草稿没有设置 `petId`（`draft.petId` 为空），宠物不会被默认选中——这可能不是用户期望的（用户之前可能选中了一个宠物但草稿没保存 petId）。 |

---

## 四、消息页 `pages/messages/index.tsx`

### ✅ v1 修复确认

| v1 # | 问题 | 状态 | 说明 |
|------|------|------|------|
| 1 | `markConversationRead` 是 fire-and-forget | ✅ 已修复 | `openConversation` 中正确 `await markConversationRead`，失败时回滚 `unreadCount`。`navigateTo` 失败时也回滚。 |
| 2 | 没有区分 single / system 会话交互 | ✅ 已修复 | `item.type === 'system'` 时直接调用 `markConversationRead` 标记已读并 toast "系统消息已读"，不再跳转聊天页。 |
| 3 | 没有消息通知 / 推送机制 | ⚠️ 部分修复 | — | 见跨页面系统问题 #3（推送机制仍未接入，但消息页本身已正确处理未读状态）。 |
| 4 | 搜索不会过滤 activeUsers | ✅ 已修复 | 搜索时也过滤 `activeUsers` → `visibleActiveUsers`，有匹配时显示"在线汪友"分区。 |

### v2 无新增问题

---

## 五、聊天页 `pages/chat/index.tsx`

### ✅ v1 修复确认

| v1 # | 问题 | 状态 | 说明 |
|------|------|------|------|
| 1 | 消息列表没有分页加载历史 | ✅ 已修复 | 引入 `loadOlder`、`nextCursor`、`upperThreshold={48}` 触发加载。加载后滚动到锚点消息位置。 |
| 2 | 轮询频率固定 5 秒 | ✅ 已修复 | 引入 `ACTIVE_POLL_MS = 3000` / `IDLE_POLL_MS = 12000` / `ACTIVE_POLL_WINDOW_MS = 60000` 三档智能调节。 |
| 3 | 发送失败时输入框闪空 | ✅ 已修复 | `send()` 中不再先 `setDraft('')`，而是在 `sendMessage` 成功后才 `setDraft('')`。`sendImageMessage` 也类似处理。 |
| 4 | 没有消息发送状态指示 | ✅ 已修复 | `sending` 状态驱动 UI：按钮显示"发送中"、输入框 `disabled`、工具栏按钮 disabled。 |
| 5 | `scrollTop` 递增 hack 不可靠 | ✅ 已修复 | 改用 `scrollIntoView` + 动态 `bottomAnchorId`（`scrollSeq` 递增）实现可靠滚动到底部。 |
| 6 | 不支持图片 / 表情消息 | ✅ 已修复 | 新增图片发送 (`sendImages`)、表情面板 (`EMOJI_OPTIONS`)、图片消息展示 + 预览 (`previewImage`)。 |

### 🆕 v2 新发现

| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **图片消息只显示非 `cloud://` URL 的图片** | 🔴 高 | 图片渲染逻辑 (L442) | `m.content && !m.content.startsWith('cloud://')` — 当消息的 content 是 `cloud://` 文件 ID（已上传到云端但 `resolveCloudFileUrl` 没有成功转换时），会显示"图片暂不可用"文本气泡。`messageThread` 云函数和 `resolveThreadAssets` 应该已经解析了这些 URL，但如果解析失败，用户会看到"图片暂不可用"，没有重试机制。更严重的是，任何以 `cloud://` 开头的内容都被认为是"不可用图片"而非普通文本，如果某些消息的 type 被错误标记为 `image` 但 content 是 cloud ID，将无法展示。 |
| 2 | **`loadLatest` 提前返回时可能不清除 `loadingInitial`** | 🟢 低 | `loadLatest` (L183-220) | 如果 `requestId !== latestRequestSeq.current` 导致提前 return，`loadingInitial` 不会被清除——但 `finally` 块仍会执行（return 在 try 块内），所以实际不会出现此问题。仅限极端竞态场景。 |

---

## 六、个人资料页 `pages/profile/index.tsx`

### ⚠️ v1 部分修复

| v1 # | 问题 | 状态 | 说明 |
|------|------|------|------|
| 1 | 偏好 `changePreference` 无错误回滚 | ⚠️ 部分修复 | `updateProfile` 内部调用 `markStale('profile')`，数据会在下次 `useRefreshOnShow` 时刷新。但 `changePreference` 本身仍然没有 `try-catch`——如果 `updateProfile` 抛出异常，UI 状态已改但数据未持久化，直到下次刷新才恢复。用户仍然会看到一个短暂的"假成功"状态。 |

### 🆕 v2 新发现

| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **`useSession` 初始化时 `ensureLogin` 与 `ProfilePage` 自身的 `ensureLogin` 重复调用** | 🟢 低 | `useEffect` (L40-47) | `useSession` hook 内部在 mount 时已经调用了 `ensureLogin()`，`ProfilePage` 在 `useEffect` 中又调用了一次。虽然有 `inflight` 去重机制不会产生两次网络请求，但代码结构上造成了理解困惑。可考虑依赖 `useSession` 的 user 即可。 |

---

## 七、资料编辑页 `pages/profile-edit/index.tsx`

### ❌ v1 遗留

| v1 # | 问题 | 状态 | 说明 |
|------|------|------|------|
| 1 | `type='nickname'` 仅真机生效 | ❌ 未修复 | 仍然是 `type='nickname'`，没有非真机环境的校验提示。这是微信平台限制，可接受。 |
| 2 | `avatarUrl` 与 `avatarKey` 共存冲突 | ❌ 未修复 | 新增了 `avatarDisplayUrl` 变量（L25: `avatarUrl && !avatarUrl.startsWith('cloud://') ? avatarUrl : ''`），解决了 `cloud://` ID 在 `<Image>` 中无法显示的问题。但 `avatarKey` 在头像已上传的情况下仍然是一个无用的遗留值，`Avatar` 组件和 `Image` 组件的优先级关系仍不明确。 |
| 3 | 没有未保存离开提示 | ❌ 未修复 | 关闭按钮仍然是直接 `navigateBack()`，没有检测是否有未保存修改。 |

### v2 无新增问题

---

## 八、宠物编辑页 `pages/pet-edit/index.tsx`

### ❌ v1 遗留

| v1 # | 问题 | 状态 | 说明 |
|------|------|------|------|
| 1 | 没有宠物数量上限限制 | ❌ 未修复 | `PetShelf` 组件仍然无条件显示"添加宠物"按钮。后端也没有数量限制。 |
| 2 | 照片隐式非必填但交互暗示必填 | ❌ 未修复 | 提交验证只检查 `name` 和 `breed`，无照片也能保存。新增了 `photoDisplayUrl` 逻辑（L81），解决了 `cloud://` ID 显示问题。 |

### v2 无新增问题

---

## 九、联系人页 `pages/contacts/index.tsx`

### ⚠️ v1 部分修复

| v1 # | 问题 | 状态 | 说明 |
|------|------|------|------|
| 3 | 互关后 `isFriend` 未即时更新 | ⚠️ 部分修复 | v1 报告指出 `onFollow` 只更新 `isFollowing` 不更新 `isFriend`。当前代码仍然只更新 `isFollowing`，`isFriend` 未同步。需调用 `toggleFollow` 后根据返回的 `mutual` 更新 `isFriend`。 |

### ❌ v1 遗留

| v1 # | 问题 | 状态 | 说明 |
|------|------|------|------|
| 1 | "发现"搜索需手动提交 | ❌ 未修复 | 仍然只有 `onConfirm` 触发搜索，没有实时搜索或防抖输入触发。 |
| 2 | 点击用户只能发起聊天 | ❌ 未修复 | 仍然没有用户个人主页入口。 |

### v2 无新增问题

---

## 十、我的动态页 `pages/my-posts/index.tsx`

### ✅ v1 修复确认

| v1 # | 问题 | 状态 | 说明 |
|------|------|------|------|
| 2 | `useDidShow` 首次触发导致重复加载 | ✅ 已修复 | 改用 `useRefreshOnShow('myPosts', () => load(true))`，首次 onShow 被跳过。 |

### ❌ v1 遗留

| v1 # | 问题 | 状态 | 说明 |
|------|------|------|------|
| 3 | `likePost` 没有错误回滚 | ❌ 未修复 | `likePost` 仍然没有 try-catch 和回滚逻辑——`await setPostLike(postId, target.likedByMe)` 失败时不会恢复之前的点赞状态。 |

### 🆕 v2 新发现

| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **仍然没有分页加载** | 🟡 中 | `load()` (L21-31) | `userPosts` 云函数硬编码 `limit(50)`，前端一次性加载全部 50 条。如果用户帖子超过 50 条，超出部分无法查看。应添加 `cursor` / `pageSize` 参数到 `userPosts` 云函数和前端。 |
| 2 | **`likePost` 通过 `onPostLike` 事件同步但有重复调用** | 🟡 中 | `likePost` (L49-63) | `likePost` 做了乐观更新 + 调用 `setPostLike`（后者通过 `emitPostLike` 广播）。同时还有一个 `useEffect` 订阅 `onPostLike` 事件。当用户在"我的动态"页点赞时，`setPostLike` → `emitPostLike` → 本页的 `onPostLike` 监听器也会触发 `setPosts`，导致同一条帖子的点赞状态被**设置了两次**（第一次是 `likePost` 直接设置，第二次是事件监听器设置）。虽然结果相同，但多了一次不必要的 re-render。 |

---

## 十一、帖子详情页 `pages/post-detail/index.tsx` 🆕 新增页面

| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **评论缺少分页加载** | 🟡 中 | `load()` (L30-59) | `postDetail` 云函数一次返回最多 200 条评论（`safeGet('comments', { postId }, 200)`），前端没有分页。如果帖子评论超过 200 条，超出部分无法查看。 |
| 2 | **`likePost` 的乐观更新使用闭包中的 `post` 而非 ref** | 🟡 中 | `likePost` (L66-85) | `const previousPost = post`（L82 回滚时使用）来自当前渲染闭包中的 `post` state。如果用户快速双击点赞（第一次 `setPost(next)` 还没 re-render，第二次进入时 `post` 仍是旧值），乐观更新的"之前"状态会不准确。应使用 ref 追踪最新的 post。 |
| 3 | **发送评论后新评论的 `author` 硬编码为"我"** | 🟡 中 | `send()` (L87-116) | 新评论被乐观添加到列表，但 `author: { id: 'me', name: '我', avatarKey: 'gradient-avatar-1' }` 是硬编码的，没有使用当前用户的真实昵称和头像。用户看到自己评论显示为"我"而非自己的昵称。应从 `useSession()` 获取当前用户信息。 |
| 4 | **`load` 函数的 `finally` 中总是设置 `commentsLoaded = true`** | 🟢 低 | `load()` (L58) | 即使 `getPostDetail` 抛出异常，`commentsLoaded` 也会被设为 `true`，导致"评论加载中…"变成"还没有评论"。但此时用户不知道加载失败了（没有错误提示）。应在 catch 中显示错误状态。 |

---

## 十二、跨页面系统性问题

### ✅ v1 修复确认

| v1 # | 问题 | 状态 | 说明 |
|------|------|------|------|
| 3 | 草稿系统断裂 | ✅ 已修复 | 发布页 mount 时调用 `getDraft()`，`resolveDraftMedia()` 解析草稿中的 cloud file URL，表单完整回填。 |
| 5 | 没有全局错误边界 | ✅ 已修复 | 各页面和 service 层普遍添加了 try-catch 和错误回滚。虽然不是 React ErrorBoundary，但每个操作都有用户可感知的错误提示。 |
| 6 | `CloudFunctionName` 缺少 `commentCreate` | ✅ 已修复 | `cloud.ts` 中已包含 `commentCreate`。 |

### ❌ v1 遗留

| v1 # | 问题 | 说明 |
|------|------|------|
| 1 | 全应用没有全局登录态守卫 | `session.store.ts` 实现了共享 session + `ensureLogin`，`app.tsx` 在 launch 时预热登录。但广场、附近、消息、发布等页面仍然**没有检查登录态**——未登录用户能看到空数据但不会跳转到登录页。 |
| 4 | `StatsRow` 数据可能与子页面不同步 | 未修复。`profile.getProfile()` 和 `contacts.getFollowList()` 返回的计数仍然可能因缓存延迟不一致。 |

### 🆕 v2 新发现

| # | 问题 | 影响范围 | 严重程度 | 说明 |
|---|------|----------|----------|------|
| 1 | **`cloud-file.ts` 的 `tempUrlCache` 永不过期** | 全应用 | 🟡 中 | `resolveCloudFileUrls` 将 cloud file ID 到临时 URL 的映射缓存在内存 Map 中，但**永不清除**。长时间使用小程序后，这个 Map 会持续增长（每次加载帖子/头像/评论都添加条目）。虽然单个 URL 映射很小，但如果用户大量浏览内容（数百帖子，数千评论），累积效果不应忽视。应添加 LRU 或 TTL 淘汰策略。 |
| 2 | **`dataBus.ts` 的 `postCache` 永不清除** | 跨页面 | 🟢 低 | `cachePost` 将帖子数据存入全局 Map，`getCachedPost` 只在 `PostDetailPage` 中调用。这个 Map 永远不会被清除——如果用户浏览了很多帖子，所有数据都会留在内存中。应在一定时间后或 Map 大小超过阈值时清除。 |
| 3 | **`messageList` 云函数中 `peerOpenids` 过滤的变量遮蔽** | 消息页 | 🟡 中 | `messageList/index.js` L88: `(c.memberOpenids \|\| []).find(openid => openid !== OPENID)` — `find` 的回调参数名 `openid` 遮蔽了外层的 `OPENID` 常量。逻辑上结果正确（参数 `openid` !== `OPENID` 等价于"找到不是我的那个成员"），但变量命名遮蔽在未来维护时容易引入真实 bug。应重命名回调参数（如 `member`）。 |
| 4 | **`messageThread` 云函数每次轮询都执行一次已读标记 update** | 聊天页 → 消息页 | 🟢 低 | `messageThread/index.js` L97-99: 每次请求消息列表都会 `update` conversation 清除未读。聊天页的轮询（3-12 秒一次）会频繁触发不必要的数据库写入。如果两个用户同时在看同一会话，一方的轮询会清除另一方的未读状态——虽然 `_.pull(OPENID)` 只清除当前用户，但频繁 update 仍是不必要的开销。应区分首次加载（标记已读）和轮询刷新（不标记已读）。 |
| 5 | **`nearbyPets` 云函数不按距离排序** | 附近页 | 🔴 高 | `nearbyPets/index.js` L86: `db.collection('pets').where(query).limit(50).get()` — 没有 `.orderBy` 按距离排序。返回的宠物列表按 MongoDB 默认的写入顺序排列，用户看到的附近宠物不是按距离远近排列的。对于"附近"功能来说，距离排序是核心体验。应使用 geoNear 或在应用层按 `distanceText` 排序。 |

---

## 十三、修复优先级建议

### P0 — 必须立即修复

1. **`nearbyPets` 云函数不按距离排序** — "附近"功能的核心体验缺失，用户期望看到最近的宠物排在最前面
2. **聊天页图片消息 `cloud://` 不显示** — 用户发送/接收的图片消息可能显示"图片暂不可用"
3. **`messageList` 云函数 `peerOpenids` 变量遮蔽** — 代码可维护性风险，未来修改时容易引入真实 bug

### P1 — 短期内修复

4. 联系人页 `onFollow` 互关后未即时更新 `isFriend`
5. 联系人页"发现"搜索无实时搜索
6. 我的动态页 `likePost` 错误回滚
7. 我的动态页无分页 + `userPosts` 云函数 `limit(50)` 硬编码
8. 广场页搜索输入导致列表频繁重载（`debouncedKeyword` → `loadFeed` 重建 → 清空列表）
9. 帖子详情页评论 `author` 硬编码"我"
10. 收藏功能 UI 入口补全（`PostCard` 添加收藏按钮）
11. `changePreference` 添加 `try-catch`
12. 帖子详情页 `likePost` 闭包问题 → ref

### P2 — 中期迭代

13. 资料编辑页 / 宠物编辑页未保存离开提示
14. 帖子详情页评论分页
15. `PostCard` 恢复"更多操作"按钮
16. 宠物数量上限
17. 全局登录态守卫（非 TabBar 页面检查登录）
18. 联系人页添加用户个人主页入口
19. `messageThread` 轮询时避免不必要的已读标记 update
20. 附近页 `nearbyVisible` 加载状态提示

### P3 — 长期优化

21. `cloud-file.ts` tempUrlCache 添加 TTL / LRU 淘汰
22. `dataBus.ts` postCache 添加大小限制 / TTL
23. 消息通知 / 推送接入
24. 用户个人主页（完整帖子列表 + 宠物列表展示）
25. 广场页话题与关键词搜索混合逻辑优化

---

> 本报告基于源码静态审查生成，所有问题均指向代码层面的逻辑缺陷，不涉及 UI/UX 视觉设计层面的评审。
