Files
Pawer/docs/功能缺陷审查报告.md
2026-06-18 15:38:28 +08:00

172 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 汪圈小程序 — 功能逻辑缺陷审查报告
> 审查日期2026-06-18
> 审查范围:全部 10 个页面、10 个 Service、23 个云函数、3 个类型文件、关键组件
> 审查分支:`master`
---
## 一、广场页 `pages/plaza/index.tsx`
| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **Feed 无分页 / 无限滚动** | 🔴 高 | `loadFeed()` (L34-38) | `getFeedList` 云函数返回 `CursorResponse<T>``nextCursor` 分页字段但页面完全没有使用分页机制。只加载第一页数据用户无法浏览更多帖子。Feed 是社交应用的核心功能,此为致命缺陷。 |
| 2 | **话题列表硬编码** | 🟡 中 | `topics` (L24) | `['全部', '滨江公园', '金毛日常', ...]` 写死在组件中,不会从云端动态获取热门话题,运营新增的话题无法展示。 |
| 3 | **广告卡片位置固定** | 🟡 中 | `PostCard` 渲染逻辑 (L130-133) | `FeedAdCard` 写死在 `index === 0` 的位置。每次 `loadFeed` 刷新后数据重置,广告位置永远在第一条帖子后,缺乏真实广告系统的随机插入和频次控制。 |
| 4 | **收藏功能存在但入口缺失** | 🟡 中 | `PostCard` + `PlazaPage` | `feed.service.ts` 导出了 `setPostLike` / `setPostFavorite``Post` 类型有 `favoritedByMe` 字段,但 `PostCard``PlazaPage` 均没有收藏操作的 UI 入口,收藏功能全链路断裂。 |
| 5 | **没有帖子详情页** | 🟡 中 | `commentPost()` (L86-113) | 评论通过 `Taro.showModal` 弹窗实现,无法查看评论列表、历史记录,也无法点击图片查看大图预览。社交类帖子的完整交互(评论链、转发、分享)均缺失。 |
| 6 | **点赞乐观更新存在竞态** | 🟡 中 | `likePost()` (L54-84) | 连续两次 `setPosts`(乐观更新 → 服务端返回覆盖),期间若 `useDidShow` 触发 `loadFeed`,可能覆盖掉刚点赞的中间状态。应考虑用 `postId` 粒度的状态管理或防抖。 |
| 7 | **搜索栏无功能** | 🟡 中 | `<SearchBar>` (L118) | `SearchBar` 组件已渲染但没有绑定 `onInput` / `onSubmit` 回调,搜索框为纯装饰。 |
---
## 二、附近页 `pages/nearby/index.tsx`
| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **没有调用 `updateLocation` 上报自身位置** | 🔴 高 | 整个页面 | `nearby.service.ts` 导出了 `updateLocation(location, visible)`,用户位置变化**从未上报云端**。其他用户看不到"我"在附近地图上出现,双向发现功能完全失效。应在获取到位置后调用上报,并在 relocate 时重复上报。 |
| 2 | **没有尊重 `preferences.nearbyVisible` 偏好** | 🔴 高 | 整个页面 | 用户在个人资料中可关闭"附近可见"(`preferences.nearbyVisible`),但附近页完全不考虑此设置。即使关闭,仍请求并展示附近宠物,自身位置也没有根据偏好控制是否上报。应在 `useEffect` 中读取偏好,决定是否请求附近数据。 |
| 3 | **`likePet` 没有乐观更新和错误回滚** | 🟡 中 | `likePet()` (L89-92) | 只调用了 `likeNearbyPet(id)`,没有先更新 UI 状态(不像 `PlazaPage.likePost` 做了乐观更新 + 回滚)。网络慢时用户看不到反馈,快速点击可能重复请求。 |
| 4 | **`filter: 'online'` 后端过滤能力不确定** | 🟡 中 | `loadPets()` (L41-46) | 前端传 `{ type: 'online' }`,但 `nearbyPets` 云函数是否支持此过滤逻辑取决于后端实现。如果后端不识别此 filter返回的数据未做二次前端过滤展示可能不准确。 |
| 5 | **搜索仅前端过滤** | 🟡 中 | `visiblePets` (L139-142) | 只按 `name``breed` 过滤,不支持按距离排序或更丰富的筛选组合。筛选器仅有 all/dog/cat/online 四种,缺少按距离、年龄段、标签等维度。 |
---
## 三、发布页 `pages/publish/index.tsx`
| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **没有恢复草稿的逻辑** | 🔴 高 | `useEffect` (L44-50) | `publish.service.ts` 导出了 `getDraft()`,但发布页 `mount` 时**从未调用它**。用户保存的草稿在下次进入发布页时不会被恢复。应添加逻辑:进入时检测未提交内容 → 提示恢复草稿 → 调用 `getDraft()` 回填表单。 |
| 2 | **没有内容字数上限预警** | 🟡 中 | `ComposerToolbar` `count` prop | `isValidPostContent` 限制 12000 字,但 `content.length` 仅展示数字,没有在接近上限(如 >1800 字)时给出警告样式或禁用输入。 |
| 3 | **图片上传失败静默跳过** | 🟡 中 | `uploadPostMedia()` (`publish.service.ts` L24-38) | 单张图片上传失败时 `catch` 返回原 `item`,用户不知道哪张图没上传成功。帖子可能携带本地临时路径 `wxfile://...` 发布,其他用户无法查看。应在 UI 上展示上传失败提示。 |
| 4 | **返回没有未保存提示** | 🟡 中 | 关闭按钮 (L148-150) | 点击 `navigateBack()` 直接返回,如果用户已输入内容但未发布,不会提醒"是否保存为草稿"。应检测 `hasContent`,有内容时弹窗确认。 |
---
## 四、消息页 `pages/messages/index.tsx`
| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **`markConversationRead` 是 fire-and-forget** | 🟡 中 | `openConversation()` (L48-54) | 调用了 `markConversationRead` 但没有 `await`,且 `openConversation``async` 函数却没有 `try-catch`。如果 `navigateTo` 失败(如页面栈满),异常不会被捕获。 |
| 2 | **没有区分 single / system 会话交互** | 🟡 中 | `openConversation()` (L48-54) | `Conversation``type: 'single' \| 'system'`,系统消息(如"汪圈小助手")不应有"发起聊天"行为,但 UI 上没有区分处理——点击系统消息也会跳转到 chat 页面。 |
| 3 | **没有消息通知 / 推送机制** | 🟡 中 | 整个页面 | 没有接入微信订阅消息或任何推送通知机制。`preferences.notifications` 偏好存在但无实际功能接入,用户即使打开通知设置也不会收到新消息提醒。 |
| 4 | **搜索不会过滤 activeUsers** | 🟡 中 | `kw` 过滤逻辑 (L64-69) | 搜索只过滤 `conversations`,不搜索 `activeUsers`。有搜索关键词时 `activeUsers` 被隐藏(`kw ? null : <ActiveUserRow>`),但清空搜索后立即恢复,交互上不连贯。 |
---
## 五、聊天页 `pages/chat/index.tsx`
| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **消息列表没有分页加载历史** | 🔴 高 | `load()` (L26-35) | 只调用一次 `getThread()` 获取当前消息,没有向上滚动加载更早消息的机制。长时间对话的历史消息无法查看。应实现 `ScrollView` 到顶触发加载上一页。 |
| 2 | **轮询频率固定 5 秒,无智能调节** | 🟡 中 | `useDidShow` (L43-45) | 不管页面是否在前台活跃、消息密度如何,都固定 5 秒轮询一次。高频轮询消耗资源,低频则延迟高。对于聊天场景,建议使用 WebSocket 或根据消息活跃度动态调整轮询间隔。 |
| 3 | **发送失败时输入框闪空** | 🟡 中 | `send()` (L53-71) | 发送时先 `setDraft('')` 清空输入框,失败后 `setDraft(text)` 恢复。在发送到失败的间隙,输入框会先空再恢复,视觉闪烁。建议先不清空,成功后再清空。 |
| 4 | **没有消息发送状态指示** | 🟡 中 | 聊天气泡 UI (L92-103) | `sending` 状态变量存在但没有用在 UI 上。用户发送消息后没有 loading / 发送中指示器,不知道消息是否正在发送。 |
| 5 | **`scrollTop` 递增 hack 不可靠** | 🟡 中 | `setScrollTop(prev => prev + 100000)` (L34) | 通过不断递增 `scrollTop` 强制滚动到底部。`Number.MAX_SAFE_INTEGER``9007199254740991`,每次 +100000 约可调用 9×10¹⁰ 次才溢出——虽然实际不太可能触发,但设计上应改用 ref 记录并 toggle 一个标志位。 |
| 6 | **不支持图片 / 表情消息** | 🟡 中 | 输入栏 UI (L108-119) | 只支持纯文本输入,没有图片、表情等富媒体消息功能。`ChatMessage.type` 字段存在但没有被使用,输入区域也没有附件按钮。 |
---
## 六、个人资料页 `pages/profile/index.tsx`
| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **偏好 `changePreference` 无错误回滚** | 🟡 中 | `changePreference()` (L92-102) | 先乐观更新 UI 再 `await updateProfile`,但整个函数没有 `try-catch`。如果云端更新失败UI 状态已改但数据未持久化,下次加载会回滚,造成用户困惑(以为保存了但实际没有)。 |
| 2 | **"关于"功能是空壳** | 🟢 低 | `handleAction('about')` (L89) | 只弹 `toast: '敬请期待新功能'`。MVP 阶段可接受,但应在版本规划中补充。 |
---
## 七、资料编辑页 `pages/profile-edit/index.tsx`
| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **`type='nickname'` 仅真机生效** | 🟡 中 | `<Input type='nickname'>` (L116) | `type='nickname'` 是微信小程序获取微信昵称的特殊能力仅在真机上有效。H5 预览 / 开发工具中用户可手动输入任意昵称(绕过微信身份验证)。应在非真机环境增加校验提示。 |
| 2 | **`avatarUrl``avatarKey` 共存冲突** | 🟡 中 | `submit()` (L56-82) | 编辑后 `uploadAvatar` 返回 `fileID` 赋给 `patch.avatarUrl`,但 `avatarKey` 没有被更新或清除。如果 `Avatar` 组件优先读 `avatarUrl`,则 `avatarKey` 多余;若优先读 `avatarKey`,可能显示旧头像。需要明确两者的优先级关系。 |
| 3 | **没有未保存离开提示** | 🟡 中 | 关闭按钮 (L88-90) | 直接 `navigateBack()` 没有检测是否有未保存修改。应比较当前表单值与初始值的差异,有差异时弹窗确认。 |
---
## 八、宠物编辑页 `pages/pet-edit/index.tsx`
| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **没有宠物数量上限限制** | 🟡 中 | `submit()` (L132-168) | 没有检查用户已添加的宠物数量。用户可以无限添加宠物,可能导致数据膨胀。建议在后端限制(如最多 5 只),前端也做预检提示。 |
| 2 | **照片隐式非必填但交互暗示必填** | 🟡 中 | `choosePhoto()` + 验证逻辑 | 提交验证只检查 `name``breed`,无照片也能保存。但展示逻辑依赖 `photoKey` 的兜底图,用户可能以为照片是必须的。建议明确标注照片为"可选",或在空照片时展示更友好的占位。 |
---
## 九、联系人页 `pages/contacts/index.tsx`
| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **"发现"搜索需手动提交,不支持实时搜索** | 🟡 中 | `<Input onConfirm>` (L101-102) | 搜索需要点击键盘"搜索"按钮才触发 (`onConfirm`),没有 `onInput` 实时触发搜索。对于"发现陌生人"场景,用户期望输入即搜索,应增加防抖实时搜索。 |
| 2 | **点击用户只能发起聊天,无个人主页入口** | 🟡 中 | `openChat()` (L63-67) | 点击用户头像/名称只能跳转到聊天页,没有查看对方个人主页(宠物列表、帖子等)的选项。用户想了解对方更多信息需要退出再找,交互断裂。 |
| 3 | **互关后 `isFriend` 未即时更新** | 🟡 中 | `onFollow()` (L51-61) | `toggleFollow` 返回 `{ mutual }` 但只更新了 `isFollowing`,没有同步更新 `isFriend`。互关后列表中不会立即显示"汪友"标签,需手动刷新页面才能看到。 |
---
## 十、我的动态页 `pages/my-posts/index.tsx`
| # | 缺陷 | 严重程度 | 位置 | 说明 |
|---|------|----------|------|------|
| 1 | **没有分页加载** | 🔴 高 | `load()` (L19-23) | 只加载一次 `getUserPosts()`,没有翻页。如果用户帖子很多,无法查看全部历史帖子。应实现 `ScrollView` 到底触发加载下一页。 |
| 2 | **`useDidShow` 首次触发导致重复加载** | 🔴 高 | `useDidShow(load)` (L31) | `useDidShow` 没有用 `firstShow` ref 跳过首次加载(对比 `PlazaPage``MessagesPage` 有此保护),首次进入页面时 `load()` 会被调用**两次**`useEffect` + `useDidShow`),浪费一次网络请求。 |
| 3 | **`likePost` 没有错误回滚** | 🟡 中 | `likePost()` (L33-47) | 乐观更新后调用 `setPostLike`,但如果失败不会回滚到之前的状态。对比 `PlazaPage.likePost``previousPosts` 回滚逻辑,此处遗漏了。 |
---
## 十一、跨页面系统性问题
| # | 问题 | 影响范围 | 说明 |
|---|------|----------|------|
| 1 | **全应用没有全局登录态守卫** | 所有页面 | 所有 service 通过 `canUseCloud()` 判断是否可用,但没有统一的"未登录则拦截/跳转"逻辑。只有 `ProfilePage` 单独处理了登录态,其他页面(广场、附近、消息、发布等)均假定已登录,未登录用户可能看到空数据或报错。 |
| 2 | **`Post.favoritedByMe` 收藏能力全链路断裂** | PlazaPage, PostCard | 类型定义有 `favoritedByMe` 字段,`feed.service.ts` 导出 `setPostFavorite` 方法,但 `PostCard` 组件和 `PlazaPage` 均没有收藏操作的 UI 入口,前端从未调用过该 service 方法。 |
| 3 | **草稿系统断裂** | PublishPage | `draftSave``draftGet` 云函数完整存在,`publish.service.ts` 导出了 `getDraft()`,但发布页 `mount` 时从不调用它。草稿保存后永远无法恢复,草稿功能形同虚设。 |
| 4 | **`StatsRow` 数据可能与子页面不同步** | ProfilePage → ContactsPage, MyPostsPage | 个人资料页 `stats.posts/following/followers` 来自 `getProfile()`,跳转到 `contacts?tab=following``getFollowList()` 可能因缓存延迟返回不同计数,造成数字前后不一致。 |
| 5 | **没有全局错误边界** | 全应用 | 任何 service 层未处理的异常会直接在页面崩溃。没有 React ErrorBoundary 或全局 Toast 兜底机制,用户体验差。 |
| 6 | **`CloudFunctionName` 联合类型缺少 `commentCreate`** | feed.service.ts L43 | `createPostComment` 调用了 `callCloud('commentCreate', ...)`,但 `CloudFunctionName` 联合类型中是否包含 `commentCreate` 取决于 `cloud.ts` 的定义。如果未声明TypeScript 不会报错但运行时可能失败。 |
---
## 十二、修复优先级建议
### P0 — 必须立即修复(影响核心功能)
1. **Feed / 我的动态分页** — 社交应用内容流的基础能力
2. **发布页草稿恢复**`getDraft()` 已存在,只需在页面 mount 时调用
3. **附近页上报自身位置**`updateLocation()` 已存在,只需在获取位置后调用
### P1 — 短期内修复(影响用户体验)
4. 我的动态页 `useDidShow` 重复加载 → 添加 `firstShow` ref
5. 我的动态页 `likePost` 错误回滚
6. 附近页 `likePet` 添加乐观更新
7. 附近页尊重 `nearbyVisible` 偏好
8. 聊天页消息分页加载
9. 个人资料页 `changePreference` 添加 `try-catch`
10. 收藏功能 UI 入口补全
### P2 — 中期迭代(完善产品体验)
11. 搜索栏功能实现(广场页)
12. 联系人页实时搜索
13. 聊天页发送状态指示 + 发送失败 UX
14. 未保存离开提示(发布页、编辑页)
15. 全局登录态守卫
16. 全局错误边界
17. 消息通知 / 推送接入
### P3 — 长期优化(性能和扩展性)
18. 话题列表动态化
19. 广告系统正规化
20. 图片 / 富媒体消息
21. 帖子详情页 + 评论列表
22. 聊天页轮询策略优化WebSocket
23. 宠物数量上限
24. 用户个人主页入口(从联系人页)
---
> 本报告基于源码静态审查生成,所有问题均指向代码层面的逻辑缺陷,不涉及 UI/UX 视觉设计层面的评审。