Files
mama-box/AGENTS.md

139 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AGENTS.md · mama-box(兜爸心选 H5)
> 在本仓库工作前先读。**跨仓库的权威约定在 `box_app/AGENTS.md`**(`/home/jone/workspace/box_app/AGENTS.md`)——
> 三仓布局、身份契约、接口命名、部署与踩坑总表都在那里;本文件只写本仓库特有的部分。
---
## 1. 这是什么
微信小程序里的**首页 H5**:小程序首页壳(`crmeb/pages/index/index.vue`)用 web-view 加载本站。
⚠️ 2026-10-09 起首页地址**由 box_app 后台动态配置**(`GET /api/mp/public/home/config` → `result.url`),
不再硬编码;当前指向 `https://mp.yizhezhe.com/mama-box-preview/`(用户确认),默认/兜底仍是 `/mama-box/`。它同时是 **H5 应用宫格**入口(30+ 第三方应用的门面)。
- 纯静态站点:**无框架、无构建、无测试**,`index.html` 一个页面用 `.screen` 切换多个屏。
- 仓库 `jone/mama-box`;本地 `/home/jone/workspace/mama-box`;生产 `/opt/app/mama-box`(nginx alias 托管,非 box_app 挂载)。
- 原型参考(只读,别当改动目标):`/home/jone/workspace/dailiang-app-03`。
## 2. 文件地图
| 文件 | 作用 |
| --- | --- |
| `index.html` | 单页多屏的全部结构 + 样式引用/脚本引用(1550+ 行) |
| `js/app.js` | **全部业务逻辑**(3000+ 行):屏切换、远程数据、宫格、授权、跳转、last_app |
| `js/wx-auth.js` | **共享助手,别的 H5 应用也引用它**:环境判定 / 读授权结果 / `requestProfile` / `goMall` / `reportLocation`。改动等于改所有接入方,务必保守 |
| `mama-box.css` | 样式 |
| `assets/` | 图片/资源。⚠️ `assets/img/` 下**文件名 = 文件内容 md5 的前 12 位**(`md5sum 新图` 取前 12 位),内容寻址:换图即换 URL,天然不会被上一版图缓存顶住。别起描述性名字 —— 全目录 35 张都按这个规则(2026-09-22 换顾问二维码时实测确认) |
| `js/core.js` | ⚠️ **本仓库内已无任何引用**(MPA 原型带过来的遗留)。新代码不要基于它,改前先确认没有隐藏引用 |
## 3. 发布
```bash
git push origin main # 完成后在服务器:
cd /opt/app/mama-box && git fetch && git reset --hard origin/main # 静态站,无需重启
```
nginx:`root /opt/app` + `Cache-Control: no-cache` + `etag on` —— 缓存靠 ETag,所以**改完必须 bump 版本号**。
**版本号规则(必做)**:改了 `js/*.js` 或 `mama-box.css` 时,两处同步 bump,且 commit message 带版本:
- `index.html` 顶部 `<!-- V:YYMMDDNN 缓存版本标记… -->`
- `js/app.js` 顶部 `var VER = 'YYMMDDNN';`
`YYMMDD` = 当天日期,`NN` = 当日两位序号(从 `01` 起)。例:`v26092202`。
## 4. 硬约定
1. **身份只从 URL 读**:宿主把 `#openid=…&unionid=…` 注入进来,用 `hashParam(name)` 读
(它同时读 hash 与 query,并会剥掉历史 bug 粘上的 `?t=`)。`openid` 是唯一身份键。
2. **拿不到 openid 就显示提示条**(`showIdNotice`),**不要造假数据**:宿主拿不到身份时不会加载本页;
真出现空身份要让人一眼看出来,而不是显示 `--`/演示值。
3. **跳其它 H5 应用一律走 `jumpApp(url, identifier)`**(或档案页的 `goArchiveH5()`):它负责
带身份(query)、上报分享地址、上报 last_app,再 `location.href`。**禁止裸 `location.href` 跳应用**。
4. **不要在本仓库写死应用名单**:能不能出现、叫什么、链接是什么,全部由 box_app 后台 App 表决定
(`/api/mp/apps?isPublished=1`)。要下架某应用去后台下架,别在前端加过滤(历史教训:`wx-h5-demo` 过滤已按要求移除,别加回来)。
5. **授权走 `wxAuth.requestProfile()`**:它用 `wx.miniProgram.navigateTo` 跳小程序独立授权页并带 `backUrl`;
结果由宿主写回 URL 的 **hash**。**不要用 postMessage 做授权请求**(延迟批量投递,点了没反应)。
6. **分享位置上报 `reportHostLocation()`**:postMessage 非实时(微信攒到后退/销毁/分享才派发),
所以要在**换页/跳转前**主动上报目标地址,宿主的分享卡片才能在分享那一刻拿到正确地址。
7. **换应用前上报 last_app**(`reportLastApp()`,`navigator.sendBeacon`):这是「退出小程序再进能回到上次应用」的
数据来源,新增任何跳应用的入口都要走 `jumpApp()` 以继承这个行为。
8. **返回首页要清 last_app**(`clearLastAppOnBackHome()` → `DELETE /api/mp/public/last-app`):
用户已经从 H5 应用退回首页,首页才是他最后待的地方 —— 不清的话下次冷启动又把他送回刚退出的应用。
⚠️ **只认「返回回到本页」三种情形**(bfcache 的 `pageshow.persisted`、后退重载的
`navigation.type === 'back_forward'`、从应用页 `location.href` 回来的 `document.referrer`);
**绝不能改成「每次加载都清」** —— 宿主恢复会把本页当返回落点加载,误清会毁掉刚用上的记录。
9. **保存资料失败必须提示,不能静默**(2026-09-22「档案以上游为准」):
`PUT /profile` 改**预产期/昵称/宝宝小名**时,后端会**先推上游用户中心、成功才落库**,推失败返回 `code=400`
且**不落库**。H5 必须弹提示(`_putProfile` 已按此判断 `res.code`)——静默的话下次加载会被上游值覆盖回去,
用户以为"改了没生效"。改这个接口的调用方时别再把失败吞掉。
10. **产后态必须先判 `postpartum`**(2026-09-22):`/pregnancy` 的产后返回体里 `week` 也是 `null`,
`applyRemoteProfile` 与 `syncPregnancy` 都必须**先判 `state.postpartum` 再判 `empty || week == null`**,
否则会给**已经生产**的用户弹「完善孕期信息」引导;
并且 `syncPregnancy()` 之后那条按 `state.week` 重算的链(`syncStaticDates` 等)要一并拦住 ——
`week=null` 会让它算出「看着正常但全错」的日期,页面上看不出来。文案集中在 `syncPregnancy` 的产后分支。
11. **首页那 5 张工具卡的数据来自 box_app**(2026-09-22,v26092213 起):`GET /api/mp/public/mama-box/tool-cards`
(box_app 封装数胎动平台的 `app-cards`;H5 **不直连上游、不持有 token**)。两条纪律:
① **接口优先、失败回退本地** —— 接口 `ok=false` 或某槽缺失时保留卡上现有值,页面绝不因上游抖动变空;
两个来源并行返回、先后不定,所以用 `cardsOwned` 旗子定优先级(`#kickTodayHome`、`#calNext` 归接口,
curve 卡写值后**摘掉 `data-wk`**),**别赌返回顺序**;
② 卡片的**名称与链接仍以 App 表为准**(`MINI_APP_MAP` + `APP_BY_ID`),接口回的 `appId/path` 只作诊断 ——
上游 `app_id` 是 URL slug(`taier-guzhong`),与 App 表 `identifier`(`taierguzhong`)**刻意不同**,别互相推导。
⚠️ 卡是固定高度 bento,只有 kick / weight 有位置放第二行(`.mc-extra`);给 fweight/cal/curve 加第二行前
先重排那笔高度账(`158+10+64=232`,见 `mama-box.css`)。
12. **⚠️ 第 2 条「不造假数据」的唯一登记例外:首页「此刻,孕妈们在做什么」卡(2026-10-01 起)**
- 内容:该卡的总人数 / 今日三项 / 匿名动态在**模拟阶段**为 box_app 按规则生成的模拟数字(`GET /pulse`,确定性哈希、不落库、不进任何报表)。
- 决策人:产品负责人(用户本人),2026-10-01 拍板「前期先用模拟数据,要显示人多;正式页先用模拟,真实数据跟上后替换」;已书面告知虚假宣传风险(《反不正当竞争法》第 8 条)。
- 期限:**最晚 2026-12-31** 复核(≤ 3 个月);真实数据接通后在后台把 `MamaBoxConfig.pulseMode` 改成 `real`(或 `off`),无需发版。
- 2026-10-02 扩展:卡内新增「同周悄悄话」(`GET/POST /whisper`、`POST /whisper/hug`)。mock 模式下真实悄悄话与 box_app 生成的**模拟悄悄话**(id 以 `m_` 开头,确定性哈希、不落库)混排,跟随同一个 `pulseMode` 开关;real 只出真实的。⚠️ 模拟句是「假装有人说过的话」,比模拟数字更敏感,切 real 时务必一并去掉。
- ⚠️ 悄悄话后端(`models/mamaBoxWhisper.py`、`services/mama_box_whisper_serv.py`、`mama_box_router.py` 与 `models/__init__.py` 的改动)是 2026-10-02 **直接改在服务器 `/opt/app/box_app` 上的,没进 git**(当时的账号无 box_app 推送权限;改前备份在 `/opt/app/box_app_bak_202610021000`)。服务器下次 `git reset --hard` 前,必须由有权限的人先把这几个文件提交进仓库,否则接口会丢。
- 边界:**只限这张卡**。孕期盲盒的数字(已收集、本周开盒人数)一律真实;任何别处都不得引用这里的模拟数字,前端也不得在接口失败时自造数字兜底。
13. **跳其它 H5 应用 / 调第三方接口一律同源:只写相对路径 `/<slug>/`,不写域名**(2026-10-09 用户定)
- 背景:小程序 web-view 只放行业务域名 `mp.yizhezhe.com`,直连 `taidong.douba.top`(或任何 `*.douba.top`)会被拦;
首页直接 `fetch` 别的域名还会跨域。所有第三方 H5 都由 mp 前置机(8.145.39.61)nginx 反代过去,路径不变。
- 写法:`const XXX_URL = '/<slug>/';`、接口 `'/<slug>/api/...'`。确实需要完整地址的地方(如 `return_url`、上报 last_app)
用 `location.origin` 或 `new URL(path, location.origin)` 拼,**也不要手写域名**;万不得已写死时只能是 `https://mp.yizhezhe.com/...`。
- **新接一个应用前,先 `curl -s -o /dev/null -w '%{http_code}' https://mp.yizhezhe.com/<slug>/` 测:**
- 200 → 直接用相对路径;
- **404 → 说明 mp 还没反代这个目录**(不是「小程序白名单没加」,别等别人)。去 mp 的 `/etc/nginx/conf.d/default.conf`
把 slug 加进「第三方 H5 应用统一反代」那条 `location ~ ^/(...)/`(页面和 `/api` 都在目录下时一条就够),
再加一行 `location = /<slug> { return 301 /<slug>/; }`。改前 `cp -a` 备份,`nginx -t` 通过才 `nginx -s reload`,
之后对比 `mp` 和 `taidong` 两边同一路径的内容应一致,并复查 `/mama-box/`、`/mama-box-preview/`、`/api/mp/...` 仍 200。
⚠️ 这台机同时跑正式站,改 nginx 前先告诉用户。
- 应用的页面或接口不在 `/<slug>/` 目录下(比如接口在根路径 `/api/xxx`)→ 别在 mp 上加兜底,先找该应用的会话改成目录内。
- 历史:2026-10-09 一次补了 `chengzhang-dangan`、`muying-xiaoxiao`、`yinger-fang`、`doudou-duihuan` 四个(备份 `default.conf.bak.4proxy-20261009222036`),
首页这 4 个入口同日改为相对路径(v26100906)。例外:微信 JS SDK `res.wx.qq.com` 是官方脚本,不在此列。
14. **任何 H5 应用「返回首页」一律调 `wxAuth.goHome()`,禁止写死首页地址**(2026-10-09 用户定)
- 原因:首页地址是后台动态配置的(见第 1 节),写死 `https://mp.yizhezhe.com/mama-box/` 会把用户送回旧首页。
`goHome()`(`js/wx-auth.js` ⑦)先拉 `/api/mp/public/home/config` 再跳,自动续接 openid/unionid;1.5s 超时或接口坏 → 回退 `/mama-box/`。
- 写法:`if (window.wxAuth && typeof wxAuth.goHome === 'function') { wxAuth.goHome(); return; }`,
原来拼身份的写死地址只允许留作 **wxAuth 没加载时的兜底**。
⚠️ `goHome()` 的身份只认 URL + sessionStorage(`identity()`)。应用若把身份从 URL 抹掉、存在 localStorage 等别处,
站内跳几次后回首页会带空身份 → 调 `goHome()` 前先 `wxAuth.rememberIdentity(openid, unionid)`(数胎动 2026-10-10 首个这么做)。`wx-auth.js` 用绝对地址 `https://mp.yizhezhe.com/mama-box/js/wx-auth.js` 引入。
- 首页带 `return_url` 跳过去的应用(档案 / 婴儿房 / 商城)返回时回 `return_url` 即可(它就是当时的首页地址);**没带 `return_url` 时的兜底也要走 `goHome()`**,别 `history.back()`。
- 本仓库自己:首页内部不要用 `goHome()`(那是给别的应用回来用的);`wx-auth.js` 里 `DEFAULT_HOME` 是唯一允许写死首页的地方。
- 2026-10-09 普查(48 个目录):主返回写死 `/mama-box/`、已通知各自会话改的有 数胎动 taidong(预览版已改,正式待批)、孕检日历 chanjian-rili(10-10 已上线,复扫通过)、待产清单 daichan-qingdan、
体重 yunqi-tizhong、血糖 yunqi-xuetang、数宫缩 gongsuo-pinlv;laixin、laixin/youju、yunqi-nengbuchi、shengao-tizhong 已先调 goHome(写死仅兜底)。
新接应用前照此检查:`curl -s https://mp.yizhezhe.com/<slug>/ | grep -n "mama-box/'\|goHome"`。
## 5. 踩坑
- **不要给别的应用追加 `#openid`**:部分应用自带 hash 路由(如 `/baobao-yingji`),追加 hash 会被当非法路由 → 页面未找到。身份一律走 query(`withIdentity()` 已封装)。
- **`bustReload` 重载必须把 hash 拼回去**,否则丢身份、误报「未获取到微信身份」(v26092009 修过)。
- **首屏渲染与异步数据的竞态**:`init()` 负责渲染,远程档案在 `init()` **末尾**才拉(先拉会被 init 覆盖)。新增远程数据请照这个顺序。
- **`resumeLastApp()` 是兜底,不是主恢复**:主恢复在小程序首页壳(冷启动自己读 last-app 直接进容器页)。
本页只在「首页壳取数失败回落加载」或「浏览器直访」时恢复;被宿主加了 `#norestore=1` 时必须**既不跳、也用
sessionStorage 闸门占住**(否则宿主 `?t=` 重载会把返回落点也换成目标应用,back 回不到首页)。
- **改 `js/wx-auth.js` 等于改全生态**:它是所有接入 H5 应用共用的授权助手,先看 `docs/h5-auth-standard.md` 的协议再动。
## 6. 自测
无构建、无自动化测试。改完:
```bash
node --check js/app.js # 语法
```
真机/微信开发者工具里走一遍:身份注入是否正常、宫格跳应用是否带身份、分享卡片地址、授权回填、
退出小程序再进是否能回到上次应用。相关接口契约与验证清单见 `box_app/AGENTS.md` §6。