AI实战之小程序06-登录消息资料怎么串起来,我这样把用户资产做成回访闭环
摘要:本文详细介绍了在小程序中构建完整用户资产闭环的实践过程。通过将微信小程序登录、账号绑定、个人资料编辑、收藏关注管理、消息中心等功能有机串联,实现了从"能登录"到"能回访"的用户体验升级。文章重点阐述了登录闭环的两条主路径、资料页从只读到可编辑的转变、以及收藏/关注/文章/消息四大回访入口的实现方案,最终形成完整的用户资产管理体系,确保用户在小程序中能够持续回访并管理自己的内容资产。
很多小程序一到“我的”页就开始失真。首页和详情页看着都像样,但一到登录、资料、收藏、关注、消息中心,马上变成入口一堆、数据一空、回访一断,最后用户根本没有留下来的理由。
Sourcelin 这次把“用户资产”单独补成一个阶段,就是为了避免这种情况。移动端不是只要能登录就够了,而是要让用户在读完内容之后,能回到自己的收藏、关注、文章和消息里,形成真正的回访链路。
本次阶段推文基于产品文档、仓库现状和本地可提取开发记录生成,未完整依赖远端会话历史。
这个阶段到底在补什么
产品文档 5.1 第 5 周把“用户资产”定义为:
- 登录注册
- 我的页
- 个人资料
- 我的收藏
- 我的关注
- 我的文章
- 消息入口
结合当前登录策略文档,这一阶段在移动端的重点不是重新造一套注册体系,而是把微信小程序登录、绑定已有账号、资料编辑、消息回访和内容回访都串起来。
对应页面和目录已经完整落地
- 我的页:sourcelin-ui/sourcelin-ui-uniapp/src/pages/mine/mine.vue
- 登录页:sourcelin-ui/sourcelin-ui-uniapp/src/pages-user/login/login.vue
- 资料页:sourcelin-ui/sourcelin-ui-uniapp/src/pages-user/profile/profile.vue
- 收藏页:sourcelin-ui/sourcelin-ui-uniapp/src/pages-user/collects/collects.vue
- 关注/粉丝页:sourcelin-ui/sourcelin-ui-uniapp/src/pages-user/follows/follows.vue
- 我的文章:sourcelin-ui/sourcelin-ui-uniapp/src/pages-user/articles/articles.vue
- 体验设置:sourcelin-ui/sourcelin-ui-uniapp/src/pages-user/settings/settings.vue
- 消息中心与详情:sourcelin-ui/sourcelin-ui-uniapp/src/pages-messages/index/index.vue、src/pages-messages/detail/detail.vue
- 认证与用户 API:src/shared/api/auth.api.ts、src/modules/user/api/user.api.ts
这说明“我的”页已经不是入口占位,而是完整承接一组用户资产子页面。
登录闭环不是一个按钮,而是两条主路径
当前我的页已经把两条登录路径做出来了:
export function loginByMiniProgramCode(code: string): Promise<MiniProgramLoginResult> {
return http.post<MiniProgramLoginResult>('/auth/wechat/mini/login', { code }, { isToken: false });
}
export function bindMiniProgramAccount(form: MiniProgramBindForm): Promise<LoginToken> {
return http.post<LoginToken>('/auth/wechat/mini/bind', { … }, { isToken: false });
}
这两条路径分别对应:
这一步很关键。因为如果只做账号密码登录,小程序会显得很别扭;如果只做微信一键登录,又会把已有博客用户体系切断。现在的做法是把两条路径都保留,而且都走统一请求层和 loginType=mini。
下面是用户登录路径的完整流程图:
#mermaid-svg-s0AQDpMG3dk0z2p1{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-s0AQDpMG3dk0z2p1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-s0AQDpMG3dk0z2p1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-s0AQDpMG3dk0z2p1 .error-icon{fill:#552222;}#mermaid-svg-s0AQDpMG3dk0z2p1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-s0AQDpMG3dk0z2p1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-s0AQDpMG3dk0z2p1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-s0AQDpMG3dk0z2p1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-s0AQDpMG3dk0z2p1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-s0AQDpMG3dk0z2p1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-s0AQDpMG3dk0z2p1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-s0AQDpMG3dk0z2p1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-s0AQDpMG3dk0z2p1 .marker.cross{stroke:#333333;}#mermaid-svg-s0AQDpMG3dk0z2p1 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-s0AQDpMG3dk0z2p1 p{margin:0;}#mermaid-svg-s0AQDpMG3dk0z2p1 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-s0AQDpMG3dk0z2p1 .cluster-label text{fill:#333;}#mermaid-svg-s0AQDpMG3dk0z2p1 .cluster-label span{color:#333;}#mermaid-svg-s0AQDpMG3dk0z2p1 .cluster-label span p{background-color:transparent;}#mermaid-svg-s0AQDpMG3dk0z2p1 .label text,#mermaid-svg-s0AQDpMG3dk0z2p1 span{fill:#333;color:#333;}#mermaid-svg-s0AQDpMG3dk0z2p1 .node rect,#mermaid-svg-s0AQDpMG3dk0z2p1 .node circle,#mermaid-svg-s0AQDpMG3dk0z2p1 .node ellipse,#mermaid-svg-s0AQDpMG3dk0z2p1 .node polygon,#mermaid-svg-s0AQDpMG3dk0z2p1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-s0AQDpMG3dk0z2p1 .rough-node .label text,#mermaid-svg-s0AQDpMG3dk0z2p1 .node .label text,#mermaid-svg-s0AQDpMG3dk0z2p1 .image-shape .label,#mermaid-svg-s0AQDpMG3dk0z2p1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-s0AQDpMG3dk0z2p1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-s0AQDpMG3dk0z2p1 .rough-node .label,#mermaid-svg-s0AQDpMG3dk0z2p1 .node .label,#mermaid-svg-s0AQDpMG3dk0z2p1 .image-shape .label,#mermaid-svg-s0AQDpMG3dk0z2p1 .icon-shape .label{text-align:center;}#mermaid-svg-s0AQDpMG3dk0z2p1 .node.clickable{cursor:pointer;}#mermaid-svg-s0AQDpMG3dk0z2p1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-s0AQDpMG3dk0z2p1 .arrowheadPath{fill:#333333;}#mermaid-svg-s0AQDpMG3dk0z2p1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-s0AQDpMG3dk0z2p1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-s0AQDpMG3dk0z2p1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-s0AQDpMG3dk0z2p1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-s0AQDpMG3dk0z2p1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-s0AQDpMG3dk0z2p1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-s0AQDpMG3dk0z2p1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-s0AQDpMG3dk0z2p1 .cluster text{fill:#333;}#mermaid-svg-s0AQDpMG3dk0z2p1 .cluster span{color:#333;}#mermaid-svg-s0AQDpMG3dk0z2p1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-s0AQDpMG3dk0z2p1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-s0AQDpMG3dk0z2p1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-s0AQDpMG3dk0z2p1 .icon-shape,#mermaid-svg-s0AQDpMG3dk0z2p1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-s0AQDpMG3dk0z2p1 .icon-shape p,#mermaid-svg-s0AQDpMG3dk0z2p1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-s0AQDpMG3dk0z2p1 .icon-shape .label rect,#mermaid-svg-s0AQDpMG3dk0z2p1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-s0AQDpMG3dk0z2p1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-s0AQDpMG3dk0z2p1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-s0AQDpMG3dk0z2p1 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
否
是
否
是
用户进入小程序
是否已登录?
是否有已有账号?
已登录状态直接进入我的页
微信小程序登录
已有账号登录
调用微信登录接口loginByMiniProgramCode()
创建新用户账号
自动绑定微信
账号密码/验证码登录
登录后绑定微信bindMiniProgramAccount()
登录成功获取token
进入用户资产页面我的页/资料/收藏等
可访问所有子页面资料编辑/收藏/关注/文章/消息
资料、头像和偏好设置已经从只读变成可编辑
资料页现在已经不是只读展示,而是能改昵称、邮箱、手机号、性别、简介,还支持头像上传:
await updateUserProfile({
nickName: form.nickName,
email: form.email,
phonenumber: form.phonenumber,
sex: form.sex,
introduction: form.introduction
});
const uploaded = await uploadUserAvatar(filePath);
与此同时,设置页把夜间阅读、个性化推荐、行为统计也做进来了。这类页面的价值不是“功能多”,而是它让用户资产从“只有一个头像和昵称”变成“能管理自己的使用状态”。
收藏、关注、我的文章和消息中心已经形成回访入口
这一阶段最能说明问题的,其实是几个二级页都已经带上了真实筛选和回访路径。
我的收藏支持按文章、说说、树洞筛选:
const result = await fetchMyCollects(page.value, pageSize, activeTab.value);
我的关注和粉丝页可以直接跳用户主页:
uni.navigateTo({ url: `/pages-user/home/home?id=${targetId}` });
我的文章支持草稿、审核中、已发布筛选,也能继续编辑或删除未发布稿件:
const result = await fetchUserArticlePage(
Number(userStore.userInfo.id),
page.value,
pageSize,
activeTab.value === 'all' ? undefined : activeTab.value
);
消息中心也已经从“建设中”变成了真实消息页和消息详情页,并且有未读数与一键已读:
const [currentUser, unread] = await Promise.all([
fetchCurrentUserInfo(),
fetchUnreadMessageCount()
]);
这才是用户资产阶段最重要的部分。用户不是只要“能登录”,而是登录后要能找到自己之前留下来的东西。
下面是用户资产闭环的完整架构图:
#mermaid-svg-goXOYHPtuammSQ4T{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-goXOYHPtuammSQ4T .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-goXOYHPtuammSQ4T .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-goXOYHPtuammSQ4T .error-icon{fill:#552222;}#mermaid-svg-goXOYHPtuammSQ4T .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-goXOYHPtuammSQ4T .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-goXOYHPtuammSQ4T .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-goXOYHPtuammSQ4T .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-goXOYHPtuammSQ4T .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-goXOYHPtuammSQ4T .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-goXOYHPtuammSQ4T .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-goXOYHPtuammSQ4T .marker{fill:#333333;stroke:#333333;}#mermaid-svg-goXOYHPtuammSQ4T .marker.cross{stroke:#333333;}#mermaid-svg-goXOYHPtuammSQ4T svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-goXOYHPtuammSQ4T p{margin:0;}#mermaid-svg-goXOYHPtuammSQ4T .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-goXOYHPtuammSQ4T .cluster-label text{fill:#333;}#mermaid-svg-goXOYHPtuammSQ4T .cluster-label span{color:#333;}#mermaid-svg-goXOYHPtuammSQ4T .cluster-label span p{background-color:transparent;}#mermaid-svg-goXOYHPtuammSQ4T .label text,#mermaid-svg-goXOYHPtuammSQ4T span{fill:#333;color:#333;}#mermaid-svg-goXOYHPtuammSQ4T .node rect,#mermaid-svg-goXOYHPtuammSQ4T .node circle,#mermaid-svg-goXOYHPtuammSQ4T .node ellipse,#mermaid-svg-goXOYHPtuammSQ4T .node polygon,#mermaid-svg-goXOYHPtuammSQ4T .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-goXOYHPtuammSQ4T .rough-node .label text,#mermaid-svg-goXOYHPtuammSQ4T .node .label text,#mermaid-svg-goXOYHPtuammSQ4T .image-shape .label,#mermaid-svg-goXOYHPtuammSQ4T .icon-shape .label{text-anchor:middle;}#mermaid-svg-goXOYHPtuammSQ4T .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-goXOYHPtuammSQ4T .rough-node .label,#mermaid-svg-goXOYHPtuammSQ4T .node .label,#mermaid-svg-goXOYHPtuammSQ4T .image-shape .label,#mermaid-svg-goXOYHPtuammSQ4T .icon-shape .label{text-align:center;}#mermaid-svg-goXOYHPtuammSQ4T .node.clickable{cursor:pointer;}#mermaid-svg-goXOYHPtuammSQ4T .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-goXOYHPtuammSQ4T .arrowheadPath{fill:#333333;}#mermaid-svg-goXOYHPtuammSQ4T .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-goXOYHPtuammSQ4T .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-goXOYHPtuammSQ4T .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-goXOYHPtuammSQ4T .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-goXOYHPtuammSQ4T .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-goXOYHPtuammSQ4T .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-goXOYHPtuammSQ4T .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-goXOYHPtuammSQ4T .cluster text{fill:#333;}#mermaid-svg-goXOYHPtuammSQ4T .cluster span{color:#333;}#mermaid-svg-goXOYHPtuammSQ4T div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-goXOYHPtuammSQ4T .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-goXOYHPtuammSQ4T rect.text{fill:none;stroke-width:0;}#mermaid-svg-goXOYHPtuammSQ4T .icon-shape,#mermaid-svg-goXOYHPtuammSQ4T .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-goXOYHPtuammSQ4T .icon-shape p,#mermaid-svg-goXOYHPtuammSQ4T .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-goXOYHPtuammSQ4T .icon-shape .label rect,#mermaid-svg-goXOYHPtuammSQ4T .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-goXOYHPtuammSQ4T .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-goXOYHPtuammSQ4T .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-goXOYHPtuammSQ4T :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
API接口层
回访路径闭环
数据筛选与分类
用户资产核心模块
我的页mine.vue
资料管理profile.vue
收藏管理collects.vue
关注管理follows.vue
我的文章articles.vue
消息中心index.vue
收藏筛选文章/说说/树洞
文章状态草稿/审核中/已发布
关注关系关注/粉丝
消息类型系统/互动/私信
内容消费阅读文章/说说
用户互动收藏/关注/评论
资产沉淀进入我的页
持续回访查看更新/消息
认证APIauth.api.ts
用户APIuser.api.ts
收藏APIfetchMyCollects()
关注API跳转用户主页
文章APIfetchUserArticlePage()
消息APIfetchUnreadMessageCount()
我为什么现在确认这个阶段可以勾选完成
之前不勾选,主要有两个明确原因:
- 资料页还偏只读
- 消息中心还是占位
这次复核后,这两个关键缺口都已经补掉了,而且还顺手把头像上传、我的文章、体验设置和消息详情都接进来了。按产品文档的验收口径看:
- 登录态持久化与退出清理已经具备
- 收藏支持按文章/说说/树洞筛选
- 关注页已经能承接作者回访
- 我的页到资料、收藏、关注、文章、消息的入口都完整可达
所以我会把“用户资产”从未完成调整为已完成,并生成这篇阶段长文。
效果图

我会怎么给 AI 下任务
请先读取 AGENTS.md、rules/frontend-uniapp.md、rules/api-contract.md,
以及 docs/product/UNIAPP_MINI_PROGRAM_PRODUCT_DESIGN.md 第 5.1 节“用户资产”定义。
本次只处理用户资产阶段,不扩展到社区扩展、兼容优化或联调验收。
请重点补齐:
1. 微信小程序登录与绑定已有账号
2. 我的页到资料、收藏、关注、文章、消息的跳转
3. 资料编辑、头像上传、退出登录
4. 收藏筛选、关注回访、消息未读与详情
输出时必须说明:
1. 页面与模块归属
2. 接口路径与登录策略
3. 哪些用户资产链路已经闭环
4. 还剩哪些验证证据
这类任务里 AI 最容易跑偏的点
- 只把登录做出来,却不把登录后的收藏、关注、消息承接起来。
- “我的”页看着很满,但很多入口还是假跳转。
- 资料页只展示,不做更新与头像上传,结果还是半成品。
- 登录链路和小程序策略没统一,最后 mini、blog、旧接口混着跑。
项目入口
- 在线演示:https://sourcelin.cn
- Gitee:https://gitee.com/my_lyq/sourcelin-cloud-blog
- GitHub:https://github.com/SourceLin/sourcelin-cloud-blog




