20 KiB
20 KiB
汪圈小程序 - 功能缺陷核验与修复设计
核验日期:2026-06-20
来源报告:docs/功能缺陷审查报告.md
核验基线:当前工作区源码,重点核验src与cloudfunctions,忽略dist构建产物
目标:逐项确认报告中仍被标为缺陷/风险的条目是否真实存在,并为需要修改的条目形成可落地设计
1. 总体结论
报告中的“已修复确认”条目,当前代码整体能支撑报告结论;本次未发现需要重新打开的已修复项。消息页、发布页、附近页、聊天页的主要 v1 修复点均能在当前实现中找到对应逻辑。
报告中仍被标为遗留、新发现或系统性问题的条目共核验 35 项:
| 结论 | 数量 | 说明 |
|---|---|---|
| 成立 | 24 | 当前代码中确实存在对应行为或缺口 |
| 部分成立 | 5 | 现象存在,但报告描述、严重程度或根因需要修正 |
| 不成立 | 6 | 当前代码已解决或报告判断与实现不符 |
需要特别修正报告优先级:
nearbyPets云函数不按距离排序:不成立。当前cloudfunctions/nearbyPets/index.js会计算距离、筛选半径内位置,并按_dist升序排序。messageList变量遮蔽:成立,但只是维护性风险,不应列为 P0。- 聊天图片
cloud://不显示:部分成立。服务层已经尝试解析cloud://为临时 URL;真实问题是解析失败后会丢失可重试的 fileId,页面只能显示“图片暂不可用”。 - 当前没有阻断级 P0 缺陷;建议从 P1 的数据一致性、分页、错误回滚开始修。
2. 已修复条目复核摘要
| 页面/模块 | 报告已修复项 | 本次复核 |
|---|---|---|
| 广场页 | 分页、动态话题、广告随机、详情页、点赞竞态、搜索入口 | 代码中存在 nextCursor、normalizeTopicOptions、createAdStartIndex、post-detail 导航、pendingLikeIds/pendingLikeOverrides、搜索防抖 |
| 附近页 | 位置上报、尊重可见性、喜欢乐观更新、online 过滤 | 代码中存在 updateLocation、nearbyVisible 分支、pendingLikeIds 回滚、前后端 online 过滤 |
| 发布页 | 草稿恢复、字数上限、上传失败提示、关闭前保存草稿提示 | 代码中存在 getDraft/restoreDraft、textLength <= 2000、UploadPostMediaError、handleClose 草稿弹窗 |
| 消息页 | 已读等待、系统会话区分、搜索 activeUsers | 代码中 openConversation 会等待跳转/已读结果,系统消息不进聊天,搜索同时过滤在线汪友 |
| 聊天页 | 历史分页、智能轮询、发送失败不清空、发送状态、可靠滚动、图片/表情 | 代码中存在 loadOlder、3s/12s 轮询、成功后清空输入、sending 状态、scrollIntoView、图片和表情发送 |
| 我的动态 | 首次 onShow 重复加载 | useRefreshOnShow 会跳过首次 show |
| 跨页面 | 草稿系统、操作错误提示、commentCreate 类型 |
当前代码已接入草稿解析、常见操作 try/catch、CloudFunctionName 包含 commentCreate |
3. 逐项核验清单
| # | 报告条目 | 核验结论 | 是否需要修改 | 设计归属 |
|---|---|---|---|---|
| 1 | 广场收藏功能入口缺失 | 成立。Post/云函数有收藏字段与能力,但 PostCard 无入口,feed.service.ts 也没有封装 setPostFavorite |
需要 | D1 |
| 2 | 广场 loadFeed 依赖 debouncedKeyword 导致频繁清空 |
部分成立。每次防抖关键词变化都会重载并清空列表;这是搜索行为的一部分,但会造成闪空体验 | 建议修改 | D4 |
| 3 | PostCard 移除更多按钮 |
成立。当前只剩点赞/评论,无举报、分享等入口 | 暂缓,需产品确认更多菜单内容 | D6 |
| 4 | 话题搜索与关键词不能同时生效 | 不成立。前端同时传 topic 和 keyword,云函数也会按 topic 查询后做关键词匹配 |
不修改,仅可优化提示文案 | D4 |
| 5 | 附近页 nearbyVisible=null 时显示默认上海与空列表 |
成立。偏好加载期间页面会渲染默认中心与空状态 | 需要 | D4 |
| 6 | 关闭附近可见不清理旧位置 | 不成立。locationUpdate 在 visible=false 时会写入 location:null、geo:null、visible:false,不会被附近查询命中 |
不修改 | - |
| 7 | useUploadMedia 暴露内部 setMedia |
成立。发布页直接用 setter 恢复草稿 | 建议小改 | D4 |
| 8 | 草稿恢复后宠物默认选中逻辑冲突 | 部分成立。当前逻辑会把“草稿无 petId”解释为“不关联宠物”,但无法区分历史缺字段与用户主动不关联 | 需要明确语义后修改 | D4 |
| 9 | 聊天图片 cloud:// 不显示 |
部分成立。正常路径会解析为临时 URL;解析失败时会丢失 fileId,无法重试 | 需要 | D3 |
| 10 | loadLatest 提前返回不清 loadingInitial |
不成立。return 位于 try 内,finally 仍会执行 |
不修改 | - |
| 11 | 个人页偏好开关无失败回滚 | 成立。changePreference 先改 UI,await updateProfile 无 catch |
需要 | D1 |
| 12 | useSession 与个人页重复 ensureLogin |
成立但影响很低。store 有 inflight 去重,不会造成重复请求 | 暂不修改,可作为结构清理 | D6 |
| 13 | 资料编辑 type='nickname' 仅真机生效 |
成立但属于微信平台限制 | 不按缺陷修,可补充提示 | D6 |
| 14 | avatarUrl 与 avatarKey 共存冲突 |
不成立。当前是图片优先、渐变头像兜底;getProfile 已解析 cloud URL |
不修改 | - |
| 15 | 资料编辑页无未保存离开提示 | 成立。关闭按钮直接 navigateBack |
需要 | D4 |
| 16 | 宠物数量无上限 | 部分成立。当前确实无限制,但是否是缺陷取决于产品策略 | 需要产品确定上限后修改 | D6 |
| 17 | 宠物照片交互暗示必填 | 不成立。当前无必填标识,校验也只要求名字和品种 | 不修改,可加“可选”文案 | - |
| 18 | 联系人互关后 isFriend 未即时更新 |
成立。toggleFollow 返回 mutual,页面未使用 |
需要 | D1 |
| 19 | 联系人“发现”搜索需手动提交 | 成立。onInput 只更新本地关键词,只有 onConfirm 请求 |
建议修改 | D1 |
| 20 | 点击用户只能发起聊天,无个人主页 | 成立。当前没有用户主页页面 | 中期功能 | D6 |
| 21 | 我的动态点赞无错误回滚 | 成立。likePost 无 try/catch |
需要 | D1 |
| 22 | 我的动态无分页,云函数 limit(50) |
成立。userPosts 无 cursor,前端一次加载 |
需要 | D2 |
| 23 | 我的动态点赞事件导致重复 setPosts | 成立但影响低。服务成功会广播,页面本地也已设置 | 可随点赞回滚一起收敛 | D1 |
| 24 | 帖子详情评论缺少分页 | 成立。postDetail 一次最多 200 条评论 |
需要 | D2 |
| 25 | 帖子详情点赞闭包竞态 | 成立。回滚用当前渲染闭包中的 post |
需要 | D1 |
| 26 | 发送评论后作者硬编码“我” | 成立。乐观评论未使用当前用户资料 | 需要 | D1 |
| 27 | 帖子详情加载失败也设 commentsLoaded=true |
成立。load 无 catch,失败后会显示空评论 |
需要 | D4 |
| 28 | 全局登录态守卫缺失 | 成立。logout 是本地标记,非 profile 页面未统一拦截 |
需要设计为轻量会话门禁 | D5 |
| 29 | StatsRow 计数可能不同步 |
部分成立。profileGet 已实时计算,但 follow/favorite 等操作后没有统一标记 profile 失效 |
需要 | D5 |
| 30 | cloud-file.ts 临时 URL 缓存永不过期 |
成立 | 需要 | D5 |
| 31 | dataBus.ts postCache 永不清除 |
成立 | 需要 | D5 |
| 32 | messageList 中 openid 回调变量遮蔽 |
成立,但只是可维护性问题 | 顺手修 | D6 |
| 33 | messageThread 每次轮询都写已读 |
成立。云函数每次取线程都会 update 会话已读 | 需要 | D3 |
| 34 | nearbyPets 不按距离排序 |
不成立。当前会按距离排序 | 不修改 | - |
| 35 | 消息通知/推送未接入 | 成立,但这是新功能,不是当前消息列表逻辑缺陷 | 长期迭代 | D6 |
4. 修复设计
D1. 互动状态一致性与回滚
目标:统一点赞、收藏、关注、偏好开关、评论乐观更新的失败回滚与跨页面同步,优先解决用户可感知的数据错乱。
涉及文件:
src/services/feed.service.tssrc/services/social.service.tssrc/pages/my-posts/index.tsxsrc/pages/post-detail/index.tsxsrc/pages/profile/index.tsxsrc/pages/contacts/index.tsxsrc/features/feed/components/PostCard/index.tsxcloudfunctions/postFavorite/index.js
设计:
-
收藏能力补齐:
- 在
feed.service.ts新增setPostFavorite(postId, favorited),调用postFavorite云函数。 postFavorite云函数补齐缺集合兜底,避免新库第一次收藏失败。PostCard增加收藏按钮与数量展示,使用post.favoritedByMe和counts.favorites。- 广场、我的动态、帖子详情统一实现乐观收藏、失败回滚、成功后写入服务端返回值。
- 新增收藏同步事件或复用 dataBus 扩展,例如
emitPostFavorite({ postId, favorited, favorites })。
- 在
-
我的动态点赞:
- 引入
postsRef和pendingLikeIds,按广场页模式保存操作前快照。 setPostLike失败时恢复likedByMe与counts.likes。onPostLike监听中先比较目标 post 当前值,相同则返回原数组,减少重复 re-render。
- 引入
-
帖子详情点赞:
- 新增
postRef保存最新 post,likePost以 ref 为准,不用渲染闭包中的post。 - 加
pendingLikeIds防止同一帖子连续点击造成并发请求。 - 失败时回滚到请求开始时的 snapshot。
- 新增
-
评论乐观作者:
PostDetailPage使用useSession()获取当前用户。- 乐观评论 author 使用
user._id、user.nickname、user.avatarKey、user.avatarUrl。 commentCreate可在后续返回完整 comment;短期先保持{ commentId, comments }返回结构不变。
-
联系人关注:
onFollow使用toggleFollow返回的following与mutual回写isFollowing、isFriend。- 失败回滚到完整旧 user snapshot,而不是只反转
isFollowing。 social.service.toggleFollow成功后调用markStale('profile'),让资料页统计在返回时刷新。
-
偏好开关:
changePreference保存 previous user。- 乐观更新后
try/catch调用updateProfile;失败恢复 previous user 并 toast。 updateProfile的markStale('profile')建议移动到云函数成功后,避免失败也标记新鲜/脏状态混乱。
验收:
- 断网或云函数失败时,点赞/收藏/关注/偏好开关都能回滚 UI。
- 互关后联系人页立即显示“汪友”。
- 新评论展示当前用户昵称和头像,不再硬编码“我”。
- 同一帖子在广场、我的动态、详情页的点赞/收藏状态保持一致。
D2. 列表与评论分页
目标:移除 50/200 条硬上限,保证长列表可继续加载。
涉及文件:
cloudfunctions/userPosts/index.jscloudfunctions/postDetail/index.jssrc/services/feed.service.tssrc/pages/my-posts/index.tsxsrc/pages/post-detail/index.tsxsrc/types/cloud.ts
设计:
-
userPosts分页:- 云函数入参新增
cursor?: string、pageSize?: number。 - 按
createdAt desc查询pageSize + 1条,cursor 使用上一页最后一条createdAt。 - 返回
{ list, nextCursor }。 getUserPosts改为返回CursorResponse<Post>,兼容authorId。MyPostsPage增加nextCursor、loadingMore、loadMore,在滚动到底部加载。
- 云函数入参新增
-
评论分页:
postDetail入参新增commentCursor?: string、commentPageSize?: number。- 评论按
createdAt asc返回,初始取最早一页;下一页使用createdAt > commentCursor。 - 返回
{ post, comments, commentsNextCursor }。 PostDetailPage增加commentsNextCursor、loadingCommentsMore、底部“加载更多评论”。- 为避免刷新帖子详情时重复替换图片,继续保留当前“只更新计数”的策略。
验收:
- 我的动态超过 50 条时可以继续加载。
- 帖子评论超过单页限制时可以继续加载下一页。
- 分页加载失败有 toast,当前列表不被清空。
D3. 聊天图片可靠展示与已读写入控制
目标:图片临时 URL 解析失败后可重试;聊天轮询不再每次写数据库。
涉及文件:
src/services/message.service.tssrc/pages/chat/index.tsxsrc/types/domain.tscloudfunctions/messageThread/index.js
设计:
-
图片消息数据结构:
ChatMessage增加可选字段fileId?: string、assetState?: 'ready' | 'unresolved'。resolveThreadAssets解析图片时保留原始cloud://到fileId,content存临时 URL;解析失败时content=''且assetState='unresolved'。- 页面渲染 unresolved 图片时展示占位和“重试”操作。
- 重试时调用
resolveCloudFileUrl(fileId),成功后只更新该条消息。
-
已读写入控制:
messageThread入参新增markRead?: boolean,默认true以兼容旧调用。- 首次打开会话、用户主动刷新时传
markRead:true。 - 静默轮询
loadLatest({ silent:true })和加载历史传markRead:false。 - 云函数仅在
markRead !== false且会话存在时执行已读 update。
验收:
- 图片临时 URL 获取失败后,页面不会永久丢失 cloud fileId。
- 静默轮询不再触发会话已读 update。
- 首次进入聊天仍会清除当前用户未读数。
D4. 页面加载、搜索与离开保护
目标:减少空白闪烁,补齐错误状态与未保存提示。
涉及文件:
src/pages/plaza/index.tsxsrc/pages/nearby/index.tsxsrc/features/nearby/components/NearbySheet/index.tsxsrc/pages/post-detail/index.tsxsrc/pages/profile-edit/index.tsxsrc/hooks/useUploadMedia.tssrc/pages/publish/index.tsx
设计:
-
广场搜索:
- 搜索参数变化时保留旧列表,设置
searching=true,新结果回来后替换。 - 或仅在用户按确认键时清空列表,防抖输入走静默刷新。
- 搜索结果文案增加当前 topic,例如“在 #遛弯 中搜索...”,解决语义提示问题。
- 搜索参数变化时保留旧列表,设置
-
附近偏好加载:
- 增加
preferenceLoading = nearbyVisible === null。 - 偏好未返回前,地图可显示默认中心,但列表区展示“正在读取附近设置”,不展示“附近暂时没有汪友”。
NearbySheet接收loading或state,区分 loading、disabled、empty 三种状态。
- 增加
-
帖子详情加载失败:
load增加 catch,设置commentsError。- 失败时展示“评论加载失败,点击重试”,不显示“还没有评论”。
finally只负责关闭 loading,不吞掉错误状态。
-
资料编辑未保存提示:
- 加
initialRef保存首次加载后的表单快照。 - 关闭按钮走
handleClose:dirty 时弹窗确认放弃/继续编辑。 - 保存成功后更新 dirty 基线或直接返回。
- 加
-
上传媒体 hook:
useUploadMedia对外暴露replaceMedia(next),发布页草稿恢复改用该方法。- 保留内部
setMedia私有,后续上传状态不会被外部绕过。
-
草稿宠物语义:
- 新草稿保存时增加
petSelectionExplicit: boolean或将petId扩展为string | null。 petId=null表示用户明确选择“不关联宠物”;petId缺失表示历史草稿/未选择,可默认第一只宠物。- 对旧草稿做兼容:没有该字段时默认第一只宠物,除非后续产品明确要求保持“不关联”。
- 新草稿保存时增加
验收:
- 广场搜索输入时不出现明显空白闪烁。
- 附近页读取设置期间不显示错误的空列表语义。
- 评论加载失败有可见错误和重试。
- 编辑资料有未保存离开确认。
- 草稿恢复不会误把历史缺字段理解为用户主动不关联。
D5. 会话门禁、统计失效与缓存淘汰
目标:统一“本地登出/资料未完成”的门禁语义,并控制长期运行内存增长。
涉及文件:
src/store/session.store.tssrc/services/auth.service.tssrc/services/social.service.tssrc/services/feed.service.tssrc/services/pet.service.tssrc/store/dataBus.tssrc/services/cloud-file.tssrc/pages/home/index.tsx
设计:
-
轻量登录态守卫:
- 新增
useSessionGate({ requireCompleted?: boolean })或等价 helper。 - 对发布、消息、联系人、附近等需要真实用户态的页面/操作,若
isLoggedOut()或!user.profileCompleted,引导到“我的”完成登录资料。 - 广场可保持只读,但点赞、收藏、评论、关注等写操作必须先过 gate。
- 由于微信 openid 是静默登录,本设计不把“未登录”理解为无 openid,而是“用户本地退出或资料未完成”。
- 新增
-
统计失效:
- follow、favorite、post create/delete、pet save/delete 成功后统一
markStale('profile')。 - 资料页返回时通过现有
useTabRefresh('profile')获取实时profileGet统计。 - 如后续新增联系人资源 key,可扩展
ResourceKey为contacts,用于关注列表自身失效。
- follow、favorite、post create/delete、pet save/delete 成功后统一
-
临时 URL 缓存:
tempUrlCache从Map<string,string>改为Map<string,{ url:string; expiresAt:number; lastAccess:number }>。- 默认 TTL 50 分钟,低于微信临时 URL 常见有效期。
- 最大容量建议 500;超出时按
lastAccess淘汰最旧项。 - 每次 resolve 前执行轻量 prune。
-
帖子 hand-off 缓存:
postCache增加 TTL 10 分钟、最大容量 100。getCachedPost读取过期项时删除并返回undefined。cachePost写入时执行容量淘汰。
验收:
- 本地退出后,写操作不会继续静默执行。
- 关注/收藏后回到资料页,统计能刷新。
- 长时间浏览大量帖子/图片后,缓存 Map 不会无界增长。
D6. 中长期产品能力与低风险清理
这些条目成立或部分成立,但不建议混入第一批稳定性修复。
-
更多菜单:
PostCard预留onMore。- 菜单项建议先做“分享”“复制内容/链接”“举报”。
- 举报若上线,需要新增
postReport云函数和审核字段,不只是前端按钮。
-
用户个人主页:
- 新增
/pages/user-profile/index?userId=...。 - 复用
profileGet({ userId })、userPosts({ authorId }),只展示公开资料与公开动态。 - 联系人列表头像/名称点击进主页,聊天按钮保留为独立操作。
- 新增
-
宠物数量上限:
- 需要先确定产品上限,例如 6 或 10。
- 前端
PetShelf达上限隐藏/禁用“添加宠物”。 - 后端
petSave新增创建前 count 校验,防止绕过前端。
-
消息推送:
- 作为订阅消息能力单独立项。
- 需要用户授权订阅模板、发送时机、频率限制、退订处理。
-
低风险清理:
messageList回调参数openid改名为memberOpenid。- 个人页重复
ensureLogin可在重构时简化,但当前无需优先。 type='nickname'平台限制可在开发/非真机环境补充提示,不作为功能修复。
5. 推荐实施顺序
| 阶段 | 内容 | 原因 |
|---|---|---|
| P1 | D1 互动状态一致性、D2 我的动态分页、D3 聊天图片重试、D4 帖子详情错误状态 | 直接影响用户操作结果与内容可见性 |
| P2 | D2 评论分页、D4 广场/附近加载体验、资料编辑离开保护、D5 统计失效与会话门禁 | 提升长列表、返回刷新和基础流程可靠性 |
| P3 | D5 缓存淘汰、D6 更多菜单/用户主页/宠物上限/推送/清理项 | 风险较低或属于产品能力扩展 |
6. 开发校验建议
- 每个云函数分页改动都保留旧入参兼容,避免旧包调用失败。
- 点赞、收藏、关注、偏好开关需要分别模拟云函数失败,确认 UI 回滚。
- 图片消息需要模拟
getTempFileURL失败,确认 fileId 被保留且可重试。 - 使用
npm run typecheck验证类型改动。 - 微信开发者工具中重点回归:广场搜索、附近首次进入、聊天轮询、我的动态滚动加载、帖子详情评论加载。