16 KiB
16 KiB
汪圈小程序 — 功能逻辑缺陷审查报告
审查日期: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 限制 1–2000 字,但 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 — 必须立即修复(影响核心功能)
- Feed / 我的动态分页 — 社交应用内容流的基础能力
- 发布页草稿恢复 —
getDraft()已存在,只需在页面 mount 时调用 - 附近页上报自身位置 —
updateLocation()已存在,只需在获取位置后调用
P1 — 短期内修复(影响用户体验)
- 我的动态页
useDidShow重复加载 → 添加firstShowref - 我的动态页
likePost错误回滚 - 附近页
likePet添加乐观更新 - 附近页尊重
nearbyVisible偏好 - 聊天页消息分页加载
- 个人资料页
changePreference添加try-catch - 收藏功能 UI 入口补全
P2 — 中期迭代(完善产品体验)
- 搜索栏功能实现(广场页)
- 联系人页实时搜索
- 聊天页发送状态指示 + 发送失败 UX
- 未保存离开提示(发布页、编辑页)
- 全局登录态守卫
- 全局错误边界
- 消息通知 / 推送接入
P3 — 长期优化(性能和扩展性)
- 话题列表动态化
- 广告系统正规化
- 图片 / 富媒体消息
- 帖子详情页 + 评论列表
- 聊天页轮询策略优化(WebSocket)
- 宠物数量上限
- 用户个人主页入口(从联系人页)
本报告基于源码静态审查生成,所有问题均指向代码层面的逻辑缺陷,不涉及 UI/UX 视觉设计层面的评审。