HarmonyOS宠物邻里实战第5篇:通知中心、已读同步与AppStorage刷新闭环

发布时间:2026/7/5 8:30:16
HarmonyOS宠物邻里实战第5篇:通知中心、已读同步与AppStorage刷新闭环 HarmonyOS宠物邻里实战第5篇通知中心、已读同步与AppStorage刷新闭环摘要通知中心是移动 App 里很容易被低估的模块。它看起来只是一个列表但真正放到宠物邻里项目里会同时连接社区评论、点赞收藏、寄养申请、寄养状态变化、系统提醒、账号安全和用户中心未读数。如果没有统一设计通知会散落到各个页面最后出现“业务发生了但通知没刷新”“已读了列表还显示红点”“详情页状态变了通知页不知道”的问题。本文基于宠物邻里 HarmonyOS 项目复盘通知中心的工程设计Notice模型如何设计通知类型和业务来源如何拆分MockStore如何集中创建、标记已读和清空通知AppStorage版本号如何驱动通知页、我的页和主壳红点刷新BackendService如何同步后端评论、点赞、寄养申请和系统消息如何接入通知中心交付前如何验证已读状态、未读数和跨页面刷新。文章重点不是“写一个列表 UI”而是把通知当成一个跨业务模块的数据同步入口来处理。工程背景与源码定位宠物邻里 App 包含宠物档案、社区动态、寄养互助、提醒、通知和个人中心。通知中心位于主 Tab 中既要展示消息列表也要承担未读数汇总、已读状态同步和跳转入口。本文涉及的文件如下文件作用MyApp/entry/src/main/ets/pages/notice/NoticeTab.ets通知列表页展示筛选、未读数和消息项MyApp/entry/src/main/ets/pages/notice/NoticeDetailPage.ets通知详情页MyApp/entry/src/main/ets/components/notice/NoticeListItem.ets通知列表项组件MyApp/entry/src/main/ets/common/MockStore.ets本地通知数据、已读操作和刷新版本MyApp/entry/src/main/ets/services/BackendService.ets后端同步通知已读状态MyApp/library2/src/main/ets/models/Models.etsNotice、用户、帖子、寄养相关模型MyApp/library2/src/main/ets/router点击通知后的路由跳转项目视觉方向如下通知中心和寄养、社区、宠物档案共享同一套 App 结构。环境与验证信息工程当前使用 HarmonyOS ArkTS / Stage 模型入口模块支持phone、tablet、2in1。通知中心虽然是列表页但会影响主壳红点、我的页统计和详情页跳转因此不能只按单页功能处理。项目值HarmonyOS 工程模型modelVersion: 6.0.2target SDK6.0.2(22)compatible SDK6.0.2(22)状态同步MockStore AppStorage后端框架Express数据访问MongoDB Driver后端验证命令cd D:\APP\chong_wu_guan_li\houduan\test npm run check npm run test:integration集成测试覆盖寄养留言、通知、坐标、状态时间线和评价说明通知不是孤立列表而是业务动作的一部分。一、通知中心要解决什么问题通知中心至少要解决四件事问题说明消息聚合评论、点赞、寄养申请、系统消息统一展示已读同步列表、详情页、主壳红点状态一致路由跳转点击通知后进入对应帖子、寄养需求或系统页业务解耦业务模块只创建通知不关心通知页怎么展示如果把通知当成“每个页面自己弹 Toast”后期很快会失控。通知中心应该是统一消息仓库而不是页面临时提示的集合。二、Notice 模型设计通知模型可以这样设计enumNoticeType{Commentcomment,Likelike,Favoritefavorite,Fosterfoster,Reminderreminder,Systemsystem}interfaceNotice{id:string;userId:string;type:NoticeType;title:string;content:string;targetType:string;targetId:string;actorId?:string;read:boolean;createdAt:number;}字段设计的重点userId这条通知属于谁type通知展示分类targetType targetId点击后跳转哪里actorId谁触发了这条通知read是否已读createdAt排序和时间展示。不要把完整帖子、完整寄养需求或完整用户对象塞进通知里。通知只保存跳转所需的引用信息。三、通知类型与业务来源通知类型可以和业务来源对应类型来源目标页面comment帖子被评论、评论被回复帖子详情或评论详情like帖子被点赞帖子详情favorite帖子被收藏帖子详情foster寄养申请、通过、拒绝、开始、完成寄养详情或寄养记录reminder宠物喂养、疫苗、驱虫提醒提醒页或宠物详情system账号、安全、平台公告系统详情页这样 UI 可以按type展示不同图标、标签色和筛选项路由层可以按targetType决定跳转。四、MockStore 集中管理通知通知数据可以由MockStore统一维护staticnotices:Notice[]MockStore.seedNotices();提供查询方法staticmyNotices():Notice[]{returnMockStore.notices.filter((notice:Notice)notice.userIdMockStore.meId).sort((a:Notice,b:Notice)b.createdAt-a.createdAt);}提供未读数staticunreadNoticeCount():number{returnMockStore.myNotices().filter((notice:Notice)!notice.read).length;}页面不直接遍历全局数组而是调用这些语义方法。五、创建通知不要散落在页面里当用户评论帖子时页面只负责提交评论MockStore.createComment(comment);状态层内部可以创建通知staticcreatePostComment(comment:PostComment):void{MockStore.commentsMockStore.comments.concat([comment]);MockStore.createNotice({userId:comment.postOwnerId,type:NoticeType.Comment,title:收到新的评论,content:comment.content,targetType:post,targetId:comment.postId,actorId:comment.userId});MockStore.bumpPostsVersion();MockStore.bumpNoticeVersion();}业务动作和通知副作用放在同一层处理才能保证不会漏。六、寄养业务如何接入通知寄养申请提交时申请者提交申请 - 创建 FosterApplication - 给需求发布者创建 foster 通知 - bumpFosterVersion - bumpNoticeVersion申请通过时发布者通过申请 - 申请者收到通过通知 - 其他待处理申请者收到未通过通知 - 创建寄养记录 - 需求状态更新 - 通知中心刷新通知内容可以简洁MockStore.createNotice({userId:application.userId,type:NoticeType.Foster,title:寄养申请已通过,content:请在约定时间完成接送确认,targetType:fosterRecord,targetId:record.id,actorId:request.ownerId});这里的targetType指向记录而不是申请因为用户下一步更关心履约记录。七、已读操作设计通知已读有三种常见动作动作场景单条已读点击某条通知全部已读通知页右上角按钮按类型已读只清空评论、只清空系统消息项目早期可以先实现单条和全部staticmarkNoticeRead(id:string):void{MockStore.noticesMockStore.notices.map((notice:Notice){if(notice.id!id){returnnotice;}return{...notice,read:true};});MockStore.bumpNoticeVersion();BackendService.markNoticeRead(id,MockStore.meId).catch((e:Error){MockStore.reportSyncFailure(通知已读同步失败稍后会重试,e);});}注意这里使用数组重新赋值而不是直接修改对象字段能减少 ArkTS 页面刷新不及时的问题。八、全部已读全部已读可以这样写staticmarkAllNoticesRead():void{MockStore.noticesMockStore.notices.map((notice:Notice){if(notice.userId!MockStore.meId){returnnotice;}return{...notice,read:true};});MockStore.bumpNoticeVersion();BackendService.markAllNoticesRead(MockStore.meId).catch((e:Error){MockStore.reportSyncFailure(全部已读同步失败稍后会重试,e);});}这样通知页、主 Tab 红点和我的页统计都能刷新。九、AppStorage 版本号驱动刷新通知变化后写入版本号staticbumpNoticeVersion():void{constv:numberAppStorage.getnumber(noticeVersion)??0;AppStorage.setOrCreatenumber(noticeVersion,v1);}通知页监听StorageLink(noticeVersion)Watch(refresh)noticeVersion:number0;主壳或我的页也可以监听StorageLink(noticeVersion)Watch(refreshBadge)noticeVersion:number0;这样一个通知已读后列表页和红点能同步变化不需要页面之间互相调用。十、通知页状态组织通知页可以维护筛选状态Statenotices:Notice[][];StatecurrentType:stringall;StateunreadOnly:booleanfalse;刷新时从MockStore取数据privaterefresh():void{this.noticesMockStore.myNotices();}筛选时只处理页面展示privatefiltered():Notice[]{returnthis.notices.filter((notice:Notice){if(this.currentType!allnotice.type!this.currentType){returnfalse;}if(this.unreadOnlynotice.read){returnfalse;}returntrue;});}通知页不负责创建通知只负责展示和触发已读。十一、列表项组件边界NoticeListItem可以接收通知对象和点击回调Componentexportstruct NoticeListItem{Propnotice:Notice;onTap:()void(){};build(){Row(){Column(){Text(this.notice.title)Text(this.notice.content)}if(!this.notice.read){Circle().width(8).height(8)}}.onClick(()this.onTap())}}组件不直接调用MockStore.markNoticeRead否则复用到不同场景时会被固定行为限制。点击后的业务动作交给页面处理。十二、点击通知后的路由点击通知时通常先标记已读再跳转privateopenNotice(notice:Notice):void{MockStore.markNoticeRead(notice.id);this.routeByNotice(notice);}路由映射privaterouteByNotice(notice:Notice):void{if(notice.targetTypepost){RouterService.push(RouteName.PostDetail,{id:notice.targetId});return;}if(notice.targetTypefosterRequest){RouterService.push(RouteName.FosterRequestDetail,{id:notice.targetId});return;}if(notice.targetTypefosterRecord){RouterService.push(RouteName.FosterRecordDetail,{id:notice.targetId});return;}}这里不要把路由写死在通知列表项组件里页面层更适合处理路由。十三、红点和未读数未读数来自MockStore.unreadNoticeCount()constcountMockStore.unreadNoticeCount();主 Tab 可以显示count 99 - 99 count 0 - count count 0 - 不显示未读数不要由通知页单独维护否则用户在详情页已读后主壳红点可能不更新。十四、后端同步策略通知已读通常可以采用乐观更新本地先标记已读 - 刷新 UI - 后端同步 - 失败后提示稍后重试原因是已读状态不是高风险业务短时间本地状态领先后端是可以接受的。下次刷新快照时再以服务端为准。但创建通知最好由后端也持久化。否则用户换设备后会丢消息。十五、快照刷新登录或下拉刷新时可以从后端加载通知快照statichydrate(snapshot:RemoteSnapshot):void{MockStore.noticessnapshot.notices;MockStore.bumpNoticeVersion();}如果通知中心支持分页就不要一次性覆盖全部通知可以按页合并MockStore.noticesmergeById(MockStore.notices,incomingNotices);早期项目数据量小完整快照更简单后续通知多了再做分页和增量同步。十六、空状态和加载失败通知页至少要有三个状态状态展示无通知“暂无消息”只有已读“没有未读消息”加载失败“消息同步失败可稍后重试”空状态不是装饰它能减少用户误解。尤其是“未读筛选”下没有内容时应该告诉用户只是没有未读而不是整个通知中心为空。十七、响应式布局通知列表也要考虑多设备设备布局手机单列列表顶部筛选横向滚动平板列表宽度居中详情页可保持大卡片2in1左侧列表、右侧详情预览也可以作为后续增强项目里可以复用Responsive.contentWidth()和Responsive.pagePadding().width(Responsive.contentWidth(this.pageWidth)).padding({left:Responsive.pagePadding(this.pageWidth),right:Responsive.pagePadding(this.pageWidth)})通知列表是高频阅读页面宽屏下不要无限拉长行宽。十八、常见问题排查问题排查点红点不消失是否调用bumpNoticeVersion()已读后又变未读后端快照是否覆盖了本地状态点击通知跳错页面targetType和targetId是否正确通知重复创建通知时是否按业务 ID 去重列表不刷新数组是否重新赋值ForEachkey 是否稳定未读数不准是否只统计当前用户通知这些问题通常不是 UI 问题而是通知数据和刷新链路没设计清楚。十九、验收清单交付通知中心前我会按这张表检查检查项通过标准模型完整通知包含类型、目标、已读、时间和所属用户创建集中评论、点赞、寄养动作通过状态层创建通知已读同步单条已读和全部已读能同步 UI红点刷新主壳、通知页、我的页未读数一致路由正确点击通知进入正确详情页后端兜底已读和通知列表能同步后端空状态无通知、无未读、失败都有展示安全边界通知不保存敏感完整对象这张清单能防止通知中心只完成“列表能看”但没有完成“业务闭环”。总结通知中心的核心是连接业务动作和用户反馈。比较稳的分层是业务页面触发评论、点赞、寄养申请等动作 MockStore创建通知、标记已读、刷新版本号 BackendService同步通知和已读状态 NoticeTab展示、筛选、打开通知 RouterService根据 targetType targetId 跳转对 HarmonyOS/ArkTS 项目来说MockStore AppStorage是一套轻量但够用的通知刷新方案。它不需要引入复杂状态库就能保证通知页、主壳红点和业务详情页之间保持一致。通知中心做好以后整个 App 会更像一个完整产品用户发起动作后有反馈别人和自己产生互动后能收到提醒业务状态变化后也能被及时看见。这就是通知模块真正的价值。