feat(auth): access token 缩到 2 小时 + refresh token 机制 #3
Reference in New Issue
Block a user
Delete Branch "feat/dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
app.wxss 是从 preview.html 迁移来的,带来一批小程序里根本不生效的 标签选择器(wxml 没有 h2/p/b/span):.page-title h2、.task-text b、 .stat b、.article-card p、.record-row span、.poster h3。各页当初都 自建了替身 class,于是 .pt-h2 在 5 个文件里逐字重复、.ac-b 3 处、 .tt-b 2 处、page{height:100vh} 更是 9 个文件一模一样抄了 9 遍。 - 删掉 6 组死规则,替身 class 收进 app.wxss - 页面骨架 page{} / .page-scroll / .page-body 统一到 app.wxss - learn.wxss 清空后删除(该页已无私有样式) - 左右留白统一 40rpx(home/article 原本是 36rpx) 顺带修一个真 bug:FAB 固定在 bottom:190rpx、高 116rpx,上沿距底 306rpx,但 7 个挂 FAB 的页面只留了 210rpx(learn/profile 更只有 60rpx)底部留白,最后一张卡片一直被它压住。改为 --pad-b-fab 330rpx。 本次不含任何色值/字号/圆角改动,设计调整在下一步。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>seg-tabs 合并三套长得像但尺寸各不同的横向切换:plan 的 .inline-tabs (灰槽白块)、community 的 .feed-tab(白药丸+阴影)、record 的 .rf-chip (白药丸无阴影,高度还差 10rpx)。variant="solid|pill" 二选一,items 同时支持字符串数组和 {key,label} 数组。 stat-ring 是首页门面用的完成度环。用 SVG data-uri 画,和 record 页的 体重折线图同一套路——canvas 在 scroll-view 里的层级、滚动跟随和截图 行为全是坑。中心内容走 slot。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>自查发现的四处,都属于「不报错但用户懵」: 1. record/plan/report 三页在 currentPetId 为空时直接 return,留下一堆 空卡片却不解释——用户以为坏了。加统一的 .no-pet 引导块,说清建档后 会有什么,并给一个建档按钮。 2. 首页门面卡在没建档时点击会打开「编辑宠物」的空表单。改成没档案就走 「添加」,文案也跟着变成「点这里建一份档案」。 3. 没建档时在快速记录弹层点「保存」,createAndClose 因为拿不到 petID 直接 close 掉——用户以为存上了,其实什么都没发生。改成明确提示。 4. 用户主动触发的操作失败不再静默:勾任务、全部完成、勾计划节点、点赞 四处的 .catch(() => {}) 换成 toast。后台加载类的静默保持不变—— 那些失败了重进页面会自愈,弹 toast 反而是噪音。 事件绑定做了一次全量扫描:18 个 wxml 里所有 bind/catch 绑的方法在对应 js 里都有实现,没有「点了没反应」的死按钮。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>## 首页添加的任务不显示(根因比表象严重) time.Parse("2006-01-02", q) 返回的是 UTC 时间,dayStart 又保留了 t.Location(),于是带 ?date= 查询时: 查询区间 = 07-29 00:00 UTC ~ 次日 = CST 的 08:00 ~ 次日 08:00 已有任务的 task_date 是 07-29 00:00 CST,落在区间外 后果不只是「新任务不显示」——ensureDayTasks 因此认为今天没有任务, 每刷一次首页就重新生成一批。用户手动加的那条(00:00 CST)永远不在 那个错位窗口里,所以管理页看得到、首页看不到。 全项目 7 处 time.Parse 日期解析统一换成 ParseInLocation + time.Local (任务、日计划、生日、到家日期、提醒到期日都受影响)。 实测:加一条后带 date 查得到 4 条,连查 4 次仍是 4 条不再增长。 ## 评论弹窗 - 改成居中弹出。评论以输入为主,贴底弹层会被键盘顶掉大半屏 - 单行 input 换成自动撑高的 textarea,最多 500 字,带字数 - 支持配图,最多 3 张(评论表加 images 字段) - 空评论原来会直接把弹层关掉,看起来像发成功了,改成明确提示 - 发完就地刷新列表,不再关闭弹层——连着回复更顺 ## AI 每日次数上限 AI 调用是真金白银,不设上限等于把钱包交给用户。新增按「用户 + 自然日 + 功能」计的额度,后台「社区运营」页可配问问 AI / 异常评估 / AI 计划 三档,改完立即生效。 两个刻意的设计: - 扣额度在请求大模型之前,失败也算用掉一次。否则刷接口空转照样烧钱 - 配置读不到时回落到保守默认值,绝不「读不到就不限制」——那正是配置 出问题时最不该发生的事 超限返回业务码 42900,小程序端明确说明「明天 0 点恢复」,不当成网络 错误让用户反复重试。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>不做无限层级——手机屏幕撑不住层层缩进,读到第四层就没法看了。 微信、小红书、B站清一色是两级:一级评论 + 其下的回复列表,回复里 用「回复 @某人」表达指向谁。 数据结构:comments 加 parent_id + reply_to_name。回复的回复会被 压平到同一条一级评论下(parent_id 取爷爷的),同时把被回复人的名字 记进 reply_to_name —— 这样既保住两级,又不丢「在跟谁说话」的信息。 几个容易做错的地方: - 一次查完这一页所有一级评论的回复,不是每条一次查询(N+1) - 一级评论默认只带 3 条回复,其余点「展开全部 N 条」再拉,避免热门 评论一次返回几百条 - 删一级评论要连它下面的回复一起删,否则回复变成挂在空处的孤儿; 帖子的 comment_count 也要按实际删除条数减,不是减 1 - total 统计含回复,和帖子上显示的数字对得上 - replies 为空时返回 [],不是 null 实测四条评论(含一条回复的回复): ▸ 甲:一级评论 (2 条回复) └ 乙:多久了? └ 甲 回复 乙:三天了 ▸ 乙:另一条一级评论 (0 条回复) 删掉第一条一级评论后 total 4→1,comment_count 同步。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>三个都是我上一轮引入或没顾上的。 ## 「Pro 会员(即将开放)」把按钮撑爆 .btn 同时用了 flex 垂直居中和 line-height:96rpx。flex 已经负责居中了, line-height 纯属多余,而且文案一换行就被这个固定行高顶到按钮外面—— 「我的」页底部那颗按钮正好触发。改成 min-height + line-height:1.3, 按钮能随文案长高,短文案的高度不变。 ## 用户主页的帖子没有任何样式 .post-card 那一整套只写在 pages/community/community.wxss 里,而页面级 wxss 不跨页生效,所以用户主页上的帖子退化成裸文本。整块提到 app.wxss。 顺带给主页的帖子补上作者行和标签,和社区保持一致。 ## 头像圈里空白 WXML 表达式不支持字符串下标,{{nickname[0]}} 求值为空。改成在 js 里 slice 好再传给模板。 另外确认一下:粉丝列表本来就能点进主页——关注和粉丝共用 pages/relations 一个页面,每行都绑了 goUser,和关注列表走同一个 pages/user 主页。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>## tab 结构 首页 / 记录 / 报告 / 社区 / 我的。计划从 tab 移出变二级页(导航栏补了 返回键),从「我的 → 养护计划」进。 ## 消灭 5 处硬编码 tabBar 高亮原来靠每个 tab 页在 onShow 里写死 selected: 0/1/2/3/4, 调一次顺序就得同时改五个文件,漏一个就高亮错位——这次把计划换成我的 正好会踩到。改成 tabBar 组件在 pageLifetimes.show 里按当前 route 自己 匹配,五处硬编码全删。 ## profile 拆成两页 原来的 profile 混了两类东西:「我是谁」和「怎么设置」。拆开: pages/mine(tab) 个人名片(头像/昵称/签名/关注粉丝记录三个数) + 我的毛孩子列表 + 常用入口 + 齿轮进设置 pages/settings 提醒设置、会员权益、数据导出、意见反馈、关于我们 「我的」上放了个「看看别人眼里的我」,点进去就是 pages/user 那个公开 主页——自己看和别人看必须是同一个页面同一套渲染,否则两边会慢慢长歪。 这也是 Phase D 装扮功能的落点。 首页齿轮从「进 profile」改成「进设置」(个人页已经在 tab 上了)。 旧的 pages/profile 整个删掉,没留兼容层。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>## User 加四个装扮字段 bio / bg_file_id / theme / hide_pets。 hide_pets 存的是「隐藏」而不是「展示」,为的是零值即默认:AutoMigrate 给存量行填 0,正好等于「展示宠物墙」,不用给老用户跑一次刷值。对外 API 仍然用 show_pets(UI 上是个「展示我的宠物」开关,正着说更自然), 只在 handler 边界取一次反。 theme 存 key 不存色值。色值属于前端表现层,存进库以后想调色就得改数据; 存 key 还能保证前端只渲染已知的那几种。非法 key 走 40000,老用户的空 theme 在读取时兜底成 warm,前端不会拿空 key 去查表。 ## GET /users/:id/profile 扩展 原来只有昵称头像和三个计数,现在带上 bio / bg_url / theme / show_pets + 宠物名片墙 + 养宠数据。自己看和别人看走同一个接口,省得两套渲染慢慢长歪。 ## 养宠数据 days 养宠天数,按最早那只的建档日算——这里要表达的是「你在这照顾它 多久了」,不是宠物活了多久 records 累计记录条数 act_days 有记录的天数 streak 连续打卡 streak 有个必须处理的边界:今天还没记录不能算断签,否则用户每天早上打开 都会看到连续天数归零。实现是先看今天有没有记,没有就从昨天起往回数。 ## PUT /api/user/decoration 四个字段全用指针,区分「没传」和「传了空串」——清空个性签名和不改个性 签名是两件事。bio 服务端截到 60 字。 联调验证(本地 9090): 老用户读取 theme 兜底 warm、bio ''、hide_pets false 改装扮 theme=mint、bio 落库 theme=rainbow 40000 参数不合法 bio 100 字 截断到 60 show_pets false → pets 字段消失,stats.pet_count 仍为 1 别人视角 is_self=false、followed=false、装扮和宠物墙都能看到 streak 1/2/3/5 天前各记一条:act_days=4 streak=3(第 4 天空档正确断开) 今天再补一条:act_days=5 streak=4 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>## 通用简易表单 洗护/清洁那 15 种只需要「什么时候 + 备注 + 可选照片」。弹层现在是 28 个 wx:elif 手写分支(402 行),各写一个会推到 700 行,而且每加一种类型都要发版。 加一个 wx:elif="{{isSimple}}",isSimple 由 JS 按后端返回的 form 字段算, 和上面那 9 种永不重叠。插在 photo 之后、提醒中心之前,没动任何现有分支的顺序。 备注给了逐类型的具体占位提示(「用了什么沐浴露、有没有吹干」), 比「请输入备注」有用得多——用户看到提示才知道这栏该写什么。 ## 一个不写就会静默出错的地方 dayToISO():后端用 time.Parse(time.RFC3339) 解 occurred_at,只给日期 ("2026-07-30")它解不出来会**静默回退成 time.Now()**——用户选「昨天洗澡」 会存成今天,而且不报错。 时区必须带真实偏移:转成 UTC 的 Z 形式,落库再转回本地时会把边界日期挪一天 (首页任务重复生成那个 bug 就是这么来的)。取正午同样是为了远离日界。 ## 四处写死的类型列表全删 RECORD_ITEMS record.js 记录页 9 宫格 QUICK_ITEMS home.js 首页 8 宫格 TYPE_TONE record.js 时间轴色调(9 种写死,新类型会全掉到橙色一片分不清) TASK_SHEETS 弹层 任务关联记录类型的下拉(只有 6 种,加了类型选不到) 全部改成读 utils/recordTypes.js 这份共用缓存。缓存 load() 只真正请求一次, 并发调用共用同一个 promise;isSimple/label/groups 是同步的,弹层打开时 不该再等一次网络。 首页 8 宫格改成取后端前 8 个(按分组顺序再按 sort),后台调 sort 就能换首屏 露出哪几个;全部 24 种在记录页。 分组色系(daily橙/health绿/care紫/clean蓝)留在前端,后端只存 group key—— 改配色不用动数据。 ## 联调(预生产库实跑) 15 种新类型 全部落库,且选「昨天」没被存成今天(15/15) 原有 9 种 逐一回归,写库 emoji 原样;weight 回写宠物档案 → 4.5kg ✓ 时间轴 24 条混排,色调分布 6/7/5/6,没有一条查不到类型 体重趋势 仍只有 1 个点(没被 24 条记录污染) 周报/首页汇总 正常 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>## 只砍入口,后端一行不动 10 个 AI 路由、service/ai_*.go、后台配额配置页、ai_usages / ai_messages 两张表全部保留。api.js 里那 5 个方法注释掉而不是删——想开回来解注释、 把入口接上就行,不用重写。 ## 5 处露出 pages/ai 聊天页 删页面 + 从 app.json 移除 首页「AI 今日建议」卡 整张删(含「问问 AI 养宠助手」按钮) 计划页「AI 计划」tab 删 tab + wxml 分支 + ?tab 深链;只剩「路线图」一项后 整条 seg-tabs 也藏了——单选的 tab 条是个没用的控件 异常观察的 AI 风险评估 见下 引导页「AI 日历」文案 改成「养护日历」 ## 异常观察这一处差点做错 原链路是:symptom 只收集表单 → AI 风险评估 → 用户在 risk 页再点保存 → buildRecord 的 case 'risk' 才真正落库。**symptom 自己从来不落库。** 拆掉中间环节如果只删 risk,结果就是「记了异常但没存下来」,而且不报错。 补了 case 'symptom' 让它自己存。连带发现第二个问题:category 存的是 AI 给的 风险等级,而 report.go:46 按 category='高' 统计周报的高风险数——不写这个字段 周报会永远是 0。改成让用户自己选严重程度(轻微/需留意/严重 → 低/中/高): 谁看着它谁最清楚,比规则化猜一个准,还顺手保住了周报。 ## FAB 首页 → 打开 24 项分组选择器(弹层新增 quickRecord 分支, 点某一项在同一个弹层内 setType 切过去,不关不跳) 社区 → 改成发帖(它本来就不该是记录入口) 记录/报告/我的/计划/学习 → 直接去掉,底部留白从 pad-b-fab 换成 pad-b-plain, 不然白留 330rpx fab 组件原来图标写死成 ai,加了 icon 属性——按钮干什么事图标就得是什么。 顺手删了 settings.js 里一个死的 onFab(Phase A 拆页时漏的,页面上根本没有 fab)。 .tg 分组样式从 record.wxss 提到 app.wxss:记录页和快速记录弹层都在用, 页面级 wxss 跨不了页(这个项目已经栽过三次)。 ## 顺手修了周报两个先前就有的 bug 验证时撞上的,和 AI 无关,但 AI 建议卡拆掉后周报权重变高了: 1. 摘要把「无高风险异常记录」写死,和它自己刚算出来的 highRisk 自相矛盾—— 记了 2 条高风险,摘要还说没有 2. next_week_focus 是一整句静态文案「第 2 针疫苗提醒、继续观察体重趋势、 避免频繁更换食物」,不管谁的宠物多大年纪都是这句,而「第 2 针疫苗」 对成年猫狗根本不适用。改成按真实数据拼:未来 7 天到期的提醒 + 有高风险就提就医 + 一周没称体重就提醒补记 ## 验证(预生产库实跑) 异常观察三档 低/中/高 各存一条,category 正确落库 周报 summary「有 2 条高风险异常记录」,不再自相矛盾 next_week_focus 有高风险的宠物 → 「继续观察上周记录的异常,必要时就医」 新建幼犬(提醒都在 7 天外、没称过体重)→ 「本周还没称体重,补记一次」 症状聚类洞察 仍然工作(insight.go 按 symptom 过滤,没受影响) 全站 grep 无 pages/ai / onFab / riskData / onGenRisk 残留 FAB 只剩首页(plus)和社区(edit)两处 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>## 分组那片色块是个 bug,不是设计 我把 tone-{{item.tone}} 加在分组外层是为了给组标题上色,但 .tone-N 在 app.wxss 里本身就带 background(`.tone-1{background:var(--primary-soft)}`), 于是整组被涂成一片色块——和视觉稿完全不是一回事。 分组着色单独走 g-1..g-4,只管 .tg-head 的文字色。格子的浅色底仍然是 .quick + .tone-N,那是它本来的用途。 ## 另外三处 三列 → 四列(视觉稿是四列,三列一行只放三个,洗护/清洁要多滚一屏) 去掉组右侧的数量(6/7/6/5 —— 用户不关心一个组里有几项) 组名去掉「记录」后缀(「日常记录」在标题位置念着啰嗦,「日常」就够) ## 去掉顶部的宠物切换和添加 这页只是个事项目录,点进去才是表单。记哪只宠物在添加页里选(那页第一行 就是「记录宠物」,多宠家庭最容易记错到别的宠物身上,放在表单里才对)。 record.json 跟着去掉 pet-switch 注册。 ## .tg 挪回页面级 历史页那份宫格上一批已经删了,现在只有记录页在用,全局层只放真正共用的。 哪天别的页面要用,检查器会报「跨页借用」提醒再挪上去。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>## pages/petform 三步向导 ① 名字 + 传头像 ② 猫还是狗 → 选品种 ③ 性别 / 生日 / 到家日期 / 体重 / 毛色 / 阶段 **编辑时不分步**,一屏全放开:用户是来改某一项的,不该被按顺序走一遍。 同一个页面两种形态,靠 ?id= 区分。 每一步只校验这一步的东西。三步全填完才校验的话,用户在第三步才被告知 第一步没填名字,还得翻回去。 进度条没用 seg-tabs:那是「可以随意切」的语义,这里有顺序, 点第三步跳过前两步会拿到一份没名字的档案。 ## 生日和到家日期分开 原来是一个「生日 / 到家时间」字段。领养的成年猫狗生日常常是不知道的, 而「一起生活了多少天」要按到家日算——合成一个字段两件事都说不准。 验过:到家 4 个月前 → 第 121 天;按生日算会是 801 天。 模型里 Birthday 和 ArrivedAt 本来就是两个字段,只是表单没露出来。 ## 品种:自由文本 → 后台可配的选择器 原来品种是个输入框,用户手打「柯基」「柯基犬」「威尔士柯基」算三个品种, 以后想按品种做体重基准、常见病提示这类事就没法做。 新增 breeds 表,预置 34 种猫 + 45 种狗,按首字母分组、右侧 A-Z 索引跳转、 首字母吸顶。留了「不确定」——领养的串串确实说不出品种,逼着选一个只会 得到假数据。换物种会清掉已选品种(原来那个一定不对了)。 **首字母存字段不运行时算**:Go 没有标准拼音库,而多音字自动转常出错—— 「藏獒」按 cang 还是 zang 分组、「柴犬」的柴,人来定比库来猜靠谱。 后台新增品种时手填这一位,服务端校验必须是单个 A-Z。 已有档案在用的品种不许删(删了那些档案的品种就变成查不到的字符串), 想隐藏用 enabled=false。同物种下不许重名——品种是统计口径,重名会把 同一种猫劈成两半。 预置数据自己填错了两条,实跑时看出来的:「苏格兰柯利犬」我按「柯利」 填了 K,应该按「苏格兰」算 S;「双血统边牧」根本不是品种,是血统描述。 seed 源和库里都改了。 ## 弹层继续瘦 addPet/editPet 两支删掉,连带 10 个方法、ageFromBirthday/GENDERS/STAGES 和 5 个 add* 字段。累计(从记录表单搬走那次算起): js 1110 → 734 行 wxml 485 → 307 行 多宠管理里「点某只去编辑」原来是 setType('editPet'),那支没了会开一个 空白弹层——改成关弹层再跳页。8 个页面的入口全换完,grep 无残留。 ## 验证(预生产库实跑) 品种接口 猫 34 种 16 个首字母、狗 45 种 17 个首字母;非法 species 返回 [] 建档 生日 / 到家日期 分别落库,品种毛色阶段都对 编辑 只改名字,到家日期没被冲掉 修正 柯利犬 K→S、双血统边牧已删 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>## 这两个页面是漏的,不是新需求 Step 2 我说了「记录类型后台可配」,接口加了(GET/POST/DELETE /api/admin/record-types)但 React 页面根本没建;品种那次同样只加了接口。 也就是说「可配」这件事在后台上一直是点不到的。补上。 ## 品种管理 猫/狗切换,按首字母分段列出(和小程序端的 A-Z 索引对得上)。 首字母是手填的输入框,配了一句说明:多音字自己定(藏獒 Z、柴犬 C)。 不做自动转拼音——Go 没有标准拼音库,而且多音字自动转常出错, 错了以后用户在小程序里按字母索引就找不到那个品种。 ## 记录事项 按分组列出 24 种,「表单字段」那列把 DSL 渲染成人话 (体重·数值kg / 状态·单选★),★ 表示这个单选落 category。 字段配置的写法说明直接放在编辑弹窗里,不是写进文档:这东西一年用两次, 去翻文档的成本比它本身还高。 内置那 9 种打了「内置」标签、隐藏删除按钮、code 输入框禁用 —— 但真正的 拦截在 service 层,前端只是别让人白点。 ## 报错原样透出 两个页面的保存失败都把后端消息显示出来,不写成「保存失败」。 后端那些消息是特意写具体的(第几行、错在哪、几个档案在用), 包成一句「失败」等于把它们扔了。 ## 验证(预生产库实跑 service 层) 品种 首字母两位/中文 → 拒;物种 bird → 拒;同物种重名「布偶猫」→ 拒 有 1 个档案在用时删 → 拒并报出数量;没人用 → 删成功 事项 两个字段都落 category → 「第 2 行」;options 没选项 → 「第 1 行」; 类型写成 num → 「第 1 行类型 num 不认识」;缺名称 → 「第 1 行缺名称」; 第二个加 :- → 通过 tsc --noEmit 通过;npm run build 后重启,/admin/ 返回的资源哈希 和新构建一致(go:embed 是编译期打包的,不重启看不到新页面) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>## 你问的「养了半年会不会自动切换」 原来的答案是「都不会,而且比这更糟」: age「2个月」 建档时算好存进库,养半年后首页还显示 2个月 stage「幼年期」同上,一年后还是幼年期 养护方案 按 stage 挑的,所以永远给幼猫那套 NormalizeStage 只在建档那一次调用(handler/pet.go:51),之后就是两个死字符串。 现在: age 改成计算字段(gorm:"-"),读取时现算。它没有手改需求,存下来只会过期。 填在 ownedPet 这个收口里,不是逐个函数补——那样漏一个就返回空年龄。 stage 保留存储(有正当的手改需求:领养的猫生日是估的,主人按体型判断更准), 但加了漂移检测 GET /pets/:id/stage-drift。 **系统负责发现,用户负责决定。** 不自动改的三个理由: 1. 用户可能手改过 2. 已经排好的这个月计划不该被推翻(套用是「往里加」不是「替换」) 3. 偷偷换会让用户莫名发现计划变了,找不到原因 ## 猫狗不能共用阈值 —— 这是这次最实质的发现 原来一刀切「<12月幼年,>=7岁老年」,对猫勉强,对狗两头都错: 吉娃娃 8 个月已性成熟、可换成犬粮;大丹犬 8 个月还在长骨头, 这时喂成犬粮(钙磷比和热量密度都不同)会加重关节负担。 吉娃娃 7 岁还在成年期(8 岁才进中年,能活 13-16 年); 大丹犬 7 岁已是老年(平均寿命 7-10 年)。 所以狗的阶段判定要看体型。体型不能靠当前体重(幼犬体重说明不了成年后多大), 只能靠品种 —— breeds 表加 size_class,45 个犬种逐个标了。 实测:同样 10 个月大 → 吉娃娃青年期、柴犬幼年期、金毛幼年期、大丹犬幼年期; 同样 7 岁 → 吉娃娃成年期、大丹犬老年期。 ## 阶段 3 段 → 5 段 幼年期 / 青年期 / 成年期 / 中年期 / 老年期。 标签做成物种中立(不分幼猫期/幼犬期)——物种本来是另一个字段, 分开会让 (species,stage) 的组合和方案配置都翻倍。 「刚到家 0-30 天」不进这五段:它和年龄正交(刚接回家的成年猫两套都需要), 代码里本来就是叠加处理的。 猫按 AAFP/AAHA 猫生命阶段指南那套,在成年里细分出中年。 狗五段的阈值按体型走四组,越大的犬种生长期越长、老得越早。 ## 划分标准和判定用同一张表 StageRules 那张表既驱动判定,也直接吐给前端当依据展示 (GET /api/life-stages?species=dog&size=giant)。 不是「代码里判定 + 文档里写标准」——后者一定会漂:改了阈值忘了改文档, 用户看到的说明和实际行为对不上,比没有说明更糟。 建档第三步的阶段选择器现在会显示「0-18 月 · 巨型犬生长期最长,18 个月还在长; 这阶段最忌过度补钙」,还会标出「巨型犬(成年 >40kg)——阶段阈值按体型算」。 前端和后台的 STAGES 硬编码列表都删了,改成读后端。 ## 10 套方案,内容按各阶段真实重点配 不是把同一份换个名字: 幼年期 疫苗序列 + 驱虫 + 社会化,大型犬还要控制生长速度 青年期 绝育窗口 + 换成年粮 成年期 维持期,把基线数据记下来 中年期 年度血检开始有意义,体重和牙结石重点看 老年期 体检半年一次;猫记饮水量(多饮多尿是慢性肾病最典型的早期表现)、 狗记精神食欲,都设成「重要」 老的 8 套(按 3 段配的)改名停用不删:万一新内容有问题还能翻回去看; 而且已套用出去的计划早就和模板断开了,停用不影响任何用户。 ## 验证(预生产库实跑) 体型判定 10 个月 / 7 岁两组对照,四个体型都落在预期阶段 age '13个月' 现算,不入库 漂移检测 手改成幼年期后 drifted=true,连带给出新阶段的依据和重点 划分标准 猫 5 段、狗 4 个体型各 5 段,老年起点 7/8/10/11 岁 全链路 3 个月大丹犬建档 → 幼年期 → 0 任务 + 4 条必要提醒 → 挑「幼犬标准照护」→ 套用下月 104 条节点, 周四那天正确出现每周项(检查爪垫和耳朵) 迁移 45 个犬种体型回填;现有 5 只宠物阶段重算后无变化(原本都对) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>后端上一轮做完了但小程序上看不到,这次接上。 ## 建档第四步:挑方案 方案按阶段取,所以必须排在「资料」之后(阶段是第三步定的)。卡片上按重复方式 分开报数(每天 3 项 / 每周 2 项 / 一次性 2 项),不报一个总数——4 条「每天」 套一整月就是 120 多条,只给总数用户会以为出 bug。 「先空着,我自己排」是个平等的选项,和方案卡长一样、就在下面一眼能看到, 不是藏起来的退路:已经养熟的宠物主人有自己的节奏,硬塞一套只会被删掉。 默认选第一套——多数用户是新手,给一个合理默认比让他面对空白好。 编辑档案时不出现第四步:改档案不该顺手改计划。 套方案失败不算建档失败:档案已经建好了,方案之后在计划页还能套, 不该把用户退回表单。 ## 计划页:空月份和平时都能套 空月份的提示条带「套一套方案」按钮 —— 空着让用户从零排,多数人会直接退出去。 但「+ 加一件」始终在,不能只给套用。 浮层里写明「套用是往里加,不会覆盖你已经排好的」,因为服务端就是按 「日期+标题」去重的,这句话是真的。套完 toast 报排了几件;一件都没排 (全都已存在)时说「这个月已经有这些安排了」,不说「成功」。 ## 阶段漂移提示 首页门面卡下面一张卡:「钞票已经 17个月,从幼年期进入成年期了」, 带上新阶段的划分依据、养护重点和体型说明,点进去到建档页改。 用主色不用红色 —— 这是「可以做一下」不是「出问题了」。 改完回首页 onShow 重拉一次,卡片自然消失。 不在首页直接改阶段:改完要不要套新方案是同一件事,放在建档页一起做才连贯。 ## 顺手把合并的尾巴清了 manageTasks 那一支删了:它管的是 DailyTask,而今日任务已经并进计划节点, 增删改都在计划页。留着会出现「这里加的任务」和「计划页加的节点」进不同的表。 连带清掉 7 个死方法 + taskSheets + 一个死 require(简易表单那套早随记录表单 搬到 pages/addrecord 了)。首页现在一个弹层都不开,bottom-sheet 组件也去掉了。 弹层累计:js 1110 → 689 行,wxml 485 → 278 行。 ## 验证(预生产库实跑) 建档四步 6 个月柯基 → 幼年期 → 拉到「幼犬标准照护」→ 套用本月 7 条 → 首页今日任务 4 条 先空着 今日任务 0 条,疫苗驱虫提醒仍然建 计划页套用 0 → 7 条;重复套用 0 条(去重生效) 漂移检测 17 个月柯基存着幼年期 → drifted=true,给出依据/重点/体型 改成成年期 → drifted=false 没生日 → 不提示(算不出就不猜) 「刚到家」→ 不提示(叠加层不是年龄段) 第一版验证脚本把阶段改成了和算出来一样的值,drifted=False 是对的, 但我的 print 无条件打了提示文案,看着像 bug。重测才验到真的。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>## 之前报告页的问题 它是个顶级 tab,却只有四张薄卡,而真正的分析全被埋起来: - 养宠账单里「记一笔」开 cost 弹层 —— 那分支上一轮随记录表单搬走了, 点了开空弹层,是个断按钮 - 养宠账单是我新建花销页的一个残缺子集,重复 - 后端有 6 条洞察 + 体重曲线,全在「历史页」,报告页只用一行链过去 - 健康摘要四行手拼字符串,很薄 ## 报告 = 数据看板 把埋在历史页的分析提上来,报告页成为真正「看数据」的地方: 本周概览 周报小结 + 完成/体重变化/高风险三个数 + 生成分享卡片 健康洞察 6 条洞察直接铺在报告页(按 alert>warn>info 上色), 点一条跳去记那一类。这是看板的主角 体重趋势 把那张 SVG 曲线图提上来(点圆点看每次记录) 健康摘要 疫苗/驱虫/体重/异常四行 + 导出就医摘要 养宠花销 只给「本月 ¥X + top 类别 + 分类条」概要,点整卡进花销页, 不再内嵌半套花销 UI,断掉的「记一笔」删了 全部记录 一行入口进历史页 ## 历史页降级成「全部记录」 洞察和体重曲线搬走了,历史页只留「时间轴流水 + 类型筛选」—— 分工清晰:报告=看分析,全部记录=翻流水。nav 标题改成「全部记录」, 去掉不再用的 bottom-sheet / fab / seg-tabs 之外的东西。 体重曲线的「记一笔」原来开 weight 弹层(已删),改成跳 addrecord?type=weight;洞察点击也从开弹层改成跳 addrecord。 ## 顺手 报告页去掉了 Pro 会员占位卡(支付未接入,就是个营销占位), 接入支付时再加回来。 ## 验证(预生产库实跑) 周报 需要关注 / 完成0 / 高风险1,小结文案对 洞察 1 条(食便相关),带 level=warn + action=food + 依据 体重趋势 字段验证只有 1 个点 → 曲线走空态「先记录几次」 健康摘要 疫苗 0/3 即将到期、驱虫下次日期、体重稳定增长、异常 1 次 账单 本月 ¥188、top 医疗、2 个类别 5 个接口全部返回真数据 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>