2026-09-24 · Phase 48B 续 · 隶属《两端新首页总方案》第 1 件。
按立项流程:先草图(本文)→ 长官确认结构 → 再画精致方案图 → 再写技术方案和开发。
本步零代码改动。第三部分是 12 维物理核查证据。
把"孩子归哪家店"用扫码来定。两个方向都能扫:
同时补一个坑:现在就算绑上了,门店和家长都看不到这个孩子。这次一起修好。
| 谁 | 放哪 | 说明 |
|---|---|---|
| 家长 | 「个人档案」页右上角加「我的二维码」 | 这一栏右边现在是空的 |
| 门店 | 视光档案页顶部,把「绑定家长」按钮改成「扫码绑定」 | 点开可以扫码,也可以像现在一样从本店家长名单里选。仍然是两个按钮一行,不改您 9-18 定的布局 |
| 门店 | 视光档案页右上角加「机构码」 | 打开就是机构二维码,可以保存图片去打印 |
新首页(第 3 件)上线时,这些入口会搬到首页上,跟效果图一致。
┌──────────────────────────────────┐
│ ← 视光档案 ★[机构码] │
├──────────────────────────────────┤
│ 🔍 搜索姓名 / 手机号 │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ ★ 扫码绑定 │ │ + 新建档案 │ │ ← 原来是「绑定家长」
│ └──────────────┘ └──────────────┘ │
│ 张小明 8岁 已绑家长 │
│ …… │
└──────────────────────────────────┘
点「扫码绑定」弹出:
┌──────────────────────────────────┐
│ 扫码绑定 │
│ [ 📷 扫家长的二维码 ] ← 推荐 │
│ [ 从本店家长名单里选 ] ← 现在的做法 │
│ [ 取消 ] │
└──────────────────────────────────┘
┌──────────────────────────────────┐
│ (头像) 王女士 的孩子 │
│ 选一个绑到本店: │
│ ○ 王小宇 男 9岁 目前在本店 ✓ │
│ ○ 王小雨 女 6岁 目前在 阳光视光中心 │ ← 选了就直接转过来
│ ○ 王小乐 男 4岁 还没绑门店 │
│ ─────────────────────────────────│
│ + 当场给他建一个孩子 │
│ ⇄ 把本店已有的档案关联给这位家长 │
│ [ 取消 ] [ 绑到本店 ] │
└──────────────────────────────────┘
其他状态:
· 加载中: 「正在读取家长信息…」
· 码过期: 「二维码已失效,请家长刷新『我的二维码』再扫」
· 家长没孩子:列表为空,只显示「+ 当场给他建一个孩子」
· 扫到的不是家长码:「这不是家长二维码」
成功:「王小雨 已绑到本店」→ 列表里出现这个孩子
┌──────────────────────────────────┐
│ ← 我的二维码 │
├──────────────────────────────────┤
│ (头像) 王女士 │
│ ┌──────────────────┐ │
│ │ │ │
│ │ [ 二维码 ] │ │
│ │ │ │
│ └──────────────────┘ │
│ 到店时给店员扫一下, │
│ 就能把孩子绑到这家店 │
│ 24 小时内有效 [ 刷新 ] │
└──────────────────────────────────┘
其他状态:加载中 / 失败「网络不好,点这里重试」
┌──────────────────────────────────┐
│ ← 机构二维码 │
├──────────────────────────────────┤
│ 阳光视光中心 │
│ ┌──────────────────┐ │
│ │ [ 二维码 ] │ │
│ └──────────────────┘ │
│ 贴在柜台,家长用微信扫一扫 │
│ 就能把孩子绑到本店 │
│ (只绑定,不开通账号) │
│ [ 保存图片 ] │
└──────────────────────────────────┘
5a 还没确认身份(微信规定必须点一下才能授权)
┌──────────────────────────────────┐
│ 阳光视光中心 邀请您绑定孩子档案 │
│ [ 用微信确认身份 ] │
└──────────────────────────────────┘
5b 一个微信有好几个账号 → 先选账号
┌──────────────────────────────────┐
│ 您有 2 个账号,选一个: │
│ ○ T2025xxxx (来自 XX 品牌) │
│ ○ 2025yyyy (来自 YY 店) │
│ [ 下一步 ] │
└──────────────────────────────────┘
5c 选孩子
┌──────────────────────────────────┐
│ 把哪个孩子绑到 阳光视光中心? │
│ ○ 王小宇 9岁 目前在 XX 店 │
│ ○ 王小雨 6岁 还没绑门店 │
│ [ 绑定 ] │
└──────────────────────────────────┘
5d 成功:「王小雨 已绑到 阳光视光中心」 [ 去智脑看看 ]
5e 没账号:「您还没有账号,请找门店店员帮您开通」
5f 有账号但没孩子:「您还没有孩子档案,请让店员帮您建一个」
5g 不在微信里打开:「请用微信扫码打开」
说明:本机微信里已经登录过智脑的家长,跳过 5a/5b,直接到 5c。
┌──────────────────────────────────┐
│ ← 个人档案 ★[我的二维码] │
├──────────────────────────────────┤
│ [ + 新建档案 ] │
│ 王小宇 9岁 │
│ …… │
└──────────────────────────────────┘
方法:派 3 个只读助手分查,每条带命令 + 原始输出,0 命中带对照组;主会话对关键结论亲跑复核(标 ✅主会话复核)。
所有引用文件已逐个git diff --stat HEAD origin/test -- <文件>:除app/services/seat_service.py(有差异,所用函数lookup_seat_username_by_wx两边逐字节一致)外全部无差异。
| 维 | 查什么 | 结论 | 关键证据 | 复核 |
|---|---|---|---|---|
| D1 | 谁能看到孩子(三处) | 绑到 B 后:B 门店列表/详情看不到(_store_visible_clause 只认本机构家长席位,不认 service_org_id);家长列表/详情看不到(外层 _org_visibility_clause 用的是家长席位所属机构 A) |
refractive_record_service.py:79-109 / 111-133 / 175-222 / 613-676;_patients_filtered_stmt :190 与 :201 两次 .where();家长端 10 个调用点全部传 owner_seat_id、从不传 allow_past_author |
✅主会话复核(sed -n 188,202p 两次 where) |
| D1b | 现有测试覆盖 | 可见性两个测试文件 13 个用例全是门店视角;"家长视角 + 已认领"组合 0 覆盖 | grep -rln owner_seat_id tests 6 个文件 ∩ grep -rln service_org_id tests 4 个文件 = 空集 |
助手 |
| D2 | 扫码认领 4 接口 | 已实现已注册,前缀 /api/h5/brand-code-claim;家长码 HS256、24h、只含 seat_id |
app/main.py:235 include_router;jwt_local.py:251 issue_family_scan_token、:269 decode_family_scan_token |
助手 |
| D2b | 二维码出图 /api/qr |
公开无鉴权;家长码令牌会出现在图片网址里(进 nginx 日志)→ 技术方案里改为不把令牌放网址 | app/api/qr.py @router.get("/qr") 无 Depends;brand_code_claim.py:61 qr_url = f"/api/qr?text=…" |
助手 |
| D3 | 认领服务 | 三分支(未绑直接绑 / 同店幂等 / 别家 409 或强转);从不改 org_id;"孩子必须属于这位家长"的校验在接口层 |
brand_code_claim.py:95 / :133 调 list_family_children 后比对 patient_id |
✅主会话复核(纠正了一个助手"该函数没人调用"的错误结论,实际 2 处在用) |
| D4 | 现有「绑定家长」 | 只能把本店自建档案绑给本店家长席位(写 owner_seat_id);与扫码绑定(写 service_org_id)是两件事;平板端共用同一后端 | refractive_record_service.py:539-612 bind/unbind_patient_seat;store-refractive/page.tsx:237 9-18 注释、:255 绑定家长、:270 新建档案 |
✅主会话复核按钮行号 |
| D5 | 微信扫一扫 | 签名接口现成(POST /api/wx/jssdk-config,无需改);jweixin 脚本动态注入写法两处现成;无 CSP 限制;scanQRCode 全仓 0 调用 |
wx_jssdk.py:117;share/[token]/page.tsx:193-233;use-tablet-wechat-voice.ts |
✅主会话复核(签名接口存在;test 域名微信校验文件 MP_verify_…txt 返 200) |
| D6 | 扫门店码后认家长 | 可复用:点击授权组件、exchange_code、lookup_seat_username_by_wx(return_all=True)(跨机构列出这个微信的所有账号)、POST /api/seat/wx-identity;缺"只绑定不登录"的新接口 |
brand-scan/page.tsx:7-18;seat_auth.py:463;seat_service.py lookup_seat_username_by_wx |
✅主会话复核 |
| D7 | 入口位置与真实配色 | 家长「个人档案」顶栏右侧空;门店顶部两按钮一行(9-18 长官令);门店手机端真实主色 #2fa37c,与效果图色系一致(效果图高频 #e3eee7 #14532d #2fa37c #157c67 #f3faf6) |
family/page.tsx 顶栏;store-refractive/page.tsx:237-270;效果图色值频次 |
✅主会话复核(#2fa37c 在 :249 :262) |
| D8 | 门店现场建档 | create_patient 从不写 service_org_id;门店建档接口不收 owner_seat_id;若直接给别家家长建,会出现"建完门店和家长都看不到"——必须随可见性一起改 |
refractive_record_service.py:386-478;refractive_records.py:74-92 |
助手 |
| D9 | 开关与权限 | 认领接口不受机构开关控制——符合 9-15 规则 16(任何机构都能扫码认领),不是漏洞 | brand-partner-spec-v1-20260915.md:64 原文"…也都能扫码认领" |
✅主会话复核规则原文 |
| D10 | 立项文档 | Phase 48 全套文档在 ~/reports/,仓库进度表里 0 行(对照 Phase 47 有 1 行)→ 写技术方案时一并把 Phase 48 补登进仓库文档 |
grep -c "Phase 48" flywheel-progress.md = 0,Phase 47 = 1 |
✅主会话复核 |
| D11 | 测试与守门 | 认领 9 个用例真连 PG、无 mock;本件若不动 app/auth/middleware.py 就不触发对话全流程守门 |
tests/integration/test_brand_code_claim.py;scripts/verify-trigger-files.txt |
助手 |
| D12 | 与生产代码一致性 | 要动的文件 main 与 test 内容一致(见上方说明) | git diff --stat HEAD origin/test -- <文件> 空输出 |
✅主会话复核(前序) |
list_family_children "没有任何接口调用、是孤儿函数"——主会话亲跑 grep:brand_code_claim.py:95、:133 两处在用。结论作废:237,按钮文字在 :255 / :270#18b58d / #078f76 / #123d35"出自结算仓库,不是本项目 H5;本项目门店手机端真实主色是 #2fa37c 系 —— 方案图按本项目真实令牌画_store_visible_clause 加上"当前由本店服务"怎么查的:两路并行。① 用门店账号登录 test 环境真页面截图,逐项量样式(R1 列表 / R2 绑定家长弹层 / R3 详情绑定区 / R4 设置页顶栏 / R7 扫码落地页),与方案图并排对比;② 代码侧 10 个维度 G1-G10(加载/报错写法、列表选择、底部弹层、用语、头像、年龄格式、状态标签、二维码展示、顶栏、「现在」帧是否属实)。关键结论由主会话复跑 grep、亲看并排图坐实。
查出 13 处与现网不符,已全部增量改(未重画整张):
| # | 方案图原来 | 现网实际(证据) | 改成 |
|---|---|---|---|
| 1 | 机构码页有「保存图片」按钮 | 微信网页不能用代码存相册,全站统一「长按」(share/[token]/store-card.tsx:57、SeatStoreCard.tsx:295、family/[id]/page.tsx:2781 注释) |
去按钮,二维码下写「长按二维码保存图片,打印后放在柜台」 |
| 2 | 孩子/家长头像画成姓名首字圆 | 列表用男蓝/女粉小人头像 DefaultAvatar(store-refractive/page.tsx:573,9-16 拍板跟平板一致);选择弹层行里没有头像(bind-parent-sheet.tsx:150-175) |
选择行去头像;列表行换成小人头像 |
| 3 | 选择用小圆点 | 选中那行边框变绿 + 右侧「已选中」(bind-parent-sheet.tsx:159/172) |
照抄 |
| 4 | 带底色的状态小标签 | 门店列表状态是纯文字(page.tsx:381-392) |
改纯文字,「在本店」绿色加粗 |
| 5 | 「已绑家长」 | 列表写「已绑定/未绑定」(page.tsx:392) |
改「已绑定」 |
| 6 | 列表行只有姓名年龄 | 头像 + 「女 · 8岁 · 生日」+ 右侧状态和「最近检查」 | 照真实结构画 |
| 7 | 家长端年龄写「9岁」 | 家长端写「男 · 9岁3个月」(family/page.tsx:56-65) |
照改 |
| 8 | 按钮 42 高 / 13 号字 / 特粗;弹层标题 18 号 | 实测 44 高 / 14 号 / 600;标题 20 号 700(bind-parent-sheet.tsx:93) |
照实测值 |
| 9 | 详情页「绑定家长」那行套了白卡片 | 实测透明,直接铺在灰底上([patientId]/page.tsx:510-530) |
去卡片 |
| 10 | 加载中转圈、出错带图标 | 全是一行文字(family/page.tsx:153-157、详情页 #c0392b);重试按钮照 record/[recordId]/page.tsx:1285-1296 |
改纯文字 |
| 11 | 家长扫码落地页绿底笑脸、按钮「用微信确认身份」 | 现网授权页 灰→白渐变满屏 + 盾牌 + 按钮「确认」(chat/components/seat-wx-auth-landing.tsx:35-50);不在微信里只有一行字 |
照现网组件画;店名标题需给组件加参数(图上已标 * 需扩展) |
| 12 | 选项层说明写「点了直接弹家长名单」 | 列表页先弹「选择要绑定的档案」再选家长(page.tsx:433-489);详情页才是直接弹 |
说明改正;选项②写明「和现在一样」;去掉底部取消(现网选档案层也没有) |
| 13 | 机构码页用了家长端的圆形返回钮 | 门店端顶栏是纯箭头 + 黑粗标题(page.tsx:196-205),灰底白卡 |
改门店端样式 |
另:03 节「在本店」被样式规则挤到第二行(已修成一行);别家门店改名「明亮视光」避免与 07 节「阳光视光中心」混淆;「给他建」改「当场新建孩子档案」;本图自造的琥珀色(#cc7d34 等 3 个,仓库 0 命中)换成仓库已有的 #b45309。
交付前自检(真实输出):div 开/闭 215/215;外链 0;溢出检查 A 轮 8 处、把橙色标注框隐藏后 B 轮 0 处(8 处全是标注框 inset:-6px 造成,差值恰为 6px);手机帧宽度 [358, 298](clientWidth,不含 1px 边框);三份副本 md5 一致 62c887f6…;主会话亲看 01-07 各节截图。
没查到的(如实说):家长端「个人档案」「孩子详情」两页没截到真页面 —— test 里没有现成家长账号,新建或重置密码属于改测试环境数据,没做;这两页只做了代码核对(顶栏 family/page.tsx:120-133 逐字对上)。
顺手发现(与本件无关,未改):门店列表页头像图片加载失败时没有兜底(store-refractive/page.tsx:558-570 无 onError,详情页 [patientId]/page.tsx:103-108 有),会显示浏览器"图裂"图标。这是读代码得出的,生产是否常见未实测。
| 目标 | 方案图对应 | 结论 |
|---|---|---|
| 绑定后机构和家长都能看到孩子(三处可见性修复) | 后端改动,不体现在图上;技术要点已列(refractive_record_service.py 三处) |
✅ 在方案里,没画图(不需要) |
| 家长手机上的「我的二维码」 | 05 页面 + 08 入口 | ✅ |
| 机构扫家长码 → 选孩子 → 绑定 | 02 选项① → 03 选孩子 | ✅ |
| 家长名下没孩子 → 现场新建再绑 | 03「为该家长新建孩子档案」→ 新建表单「保存并绑定」 | ✅ 本轮补画 |
| 本机构已有档案 → 关联给该家长(总方案第七节第 8 条) | 03「关联本机构已有档案」→ 选档案「确认关联」 | ✅ 本轮补画 |
| 机构码:家长扫 → 选孩子 → 绑定;不开号不计费 | 06 机构二维码 + 07 落地页 | ✅ |
| 没账号 → 提示找机构开通;多账号 → 先选账号 | 07 5e / 5b | ✅ |
| 互扫两个方向:家长也能在智脑里扫 | 08「扫一扫」入口 + 09 扫码流程 | ✅ 本轮补上(上一版只靠微信自带扫一扫,漏了智脑内入口) |
| 按钮仍叫「绑定家长」、两个选项、详情页同样两选项 | 01 / 02 / 04 | ✅ |
| 转店不加确认 | 03、5c 直接「确认绑定」;只加一行灰字说明会转入 | ✅(灰字是告知,不是二次确认) |
| 防重复(同名同生日提示直接选) | 03 防重复变体 | ✅ |
| 不在微信里隐藏扫码项 | 02、08 各有「不在微信里」变体 | ✅ |
| 叫「机构」、文案书面化、弹框居中 | 全图 | ✅(第 4 轮) |
bind-parent-sheet.tsx:163-164)| # | 问题 | 证据 | 建议 |
|---|---|---|---|
| R1 | 家长二维码有效期 24 小时 + 转店不加确认 = 截图就能把孩子转走:谁拿到截图,24 小时内都能把孩子绑到自己机构 | app/auth/jwt_local.py:248 FAMILY_SCAN_TTL_HOURS = 24;后端原设计转店要二次确认(brand_code_claim.py:123-124 force_transfer),按长官决定前端直接带过 |
有效期缩到 10 分钟,页面打开时自动刷新(家长无感);图上已加「请勿截图转发」提示,但光提示不够 |
| R2 | 「弹框都居中」会连带改 3 个现有弹层:选档案、选家长、新建档案(后者现为底部弹出,create-patient-sheet.tsx:262),「新建档案」按钮日常就在用 |
同左 | 选档案、选家长改居中没问题;新建档案表单有 9 项,居中弹框在小屏手机上要框内滚动,不如底部弹出好填 —— 建议这一个保留现样,请长官定 |
| R3 | 跨机构家长:「新建并绑定」「关联已有档案」要把档案绑给别的机构开的家长账号,现有绑定只认本机构家长 | refractive_record_service.py bind_patient_seat 校验 User.linked_org_id == org_id |
技术方案里放开,并补「家长视角能看到」的真实测试 |
| R4 | 家长端页面现在没有微信扫码能力 | web/app/h5/family 下 jweixin/jssdk/scanQRCode 0 处(对照 web/app/h5/share 1 处) |
技术方案加(与机构端同一套) |
| R5 | 3 处要给后端/组件加字段:选孩子的出生日期(ChildBrief)、多账号的所属机构名、授权页标题参数 | 图上已用 * 标出 | 技术方案里一起做 |
| R6 | 一个微信多个账号时,主登录会退回手输账号 | seat_auth.py:510 未带 return_all |
属登录改造会话,需对接 |
图标与 lucide 0.344 源文件逐字一致(带对照组);豆腐块 0。
追记(9-24 晚):长官看方案 A 后说「可以,但不够显眼」→ 在 A 的位置出 3 个加强版(都在「返回」左边,与「返回」垂直中心差 0,搜索框位移 0):