交付出件箱

← 返回出件箱

第 1 件 · 扫码绑定 · 方案 v1 + 草图(给长官过目)

2026-09-24 · Phase 48B 续 · 隶属《两端新首页总方案》第 1 件。
按立项流程:先草图(本文)→ 长官确认结构 → 再画精致方案图 → 再写技术方案和开发。
本步零代码改动。第三部分是 12 维物理核查证据。


第一部分 · 大白话方案

这一件做什么

把"孩子归哪家店"用扫码来定。两个方向都能扫:

  1. 门店扫家长:家长打开「我的二维码」给店员看 → 店员用「扫一扫」一扫 → 选孩子 → 这个孩子就绑到本店了
  2. 家长扫门店:柜台贴着「机构二维码」→ 家长用微信扫一扫 → 选自己的孩子 → 绑到这家店

同时补一个坑:现在就算绑上了,门店和家长都看不到这个孩子。这次一起修好。

规矩(都是已经定过的)

入口放哪(新首页上线前先放在现有页面)

谁 放哪 说明
家长 「个人档案」页右上角加「我的二维码」 这一栏右边现在是空的
门店 视光档案页顶部,把「绑定家长」按钮改成「扫码绑定」 点开可以扫码,也可以像现在一样从本店家长名单里选。仍然是两个按钮一行,不改您 9-18 定的布局
门店 视光档案页右上角加「机构码」 打开就是机构二维码,可以保存图片去打印

新首页(第 3 件)上线时,这些入口会搬到首页上,跟效果图一致。


第二部分 · 草图(线框,只看结构不看颜色)

画面 1 · 门店:视光档案页顶部(改动处用 ★ 标出)

┌──────────────────────────────────┐
│ ←  视光档案              ★[机构码] │
├──────────────────────────────────┤
│ 🔍 搜索姓名 / 手机号                │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ ★ 扫码绑定    │ │ + 新建档案    │ │   ← 原来是「绑定家长」
│ └──────────────┘ └──────────────┘ │
│  张小明  8岁  已绑家长               │
│  ……                               │
└──────────────────────────────────┘
点「扫码绑定」弹出:
┌──────────────────────────────────┐
│  扫码绑定                           │
│  [ 📷 扫家长的二维码 ]  ← 推荐        │
│  [ 从本店家长名单里选 ]  ← 现在的做法   │
│                          [ 取消 ]  │
└──────────────────────────────────┘

画面 2 · 门店:扫到家长码以后,选孩子

┌──────────────────────────────────┐
│  (头像) 王女士 的孩子                 │
│  选一个绑到本店:                     │
│  ○ 王小宇  男 9岁   目前在本店 ✓     │
│  ○ 王小雨  女 6岁   目前在 阳光视光中心  │  ← 选了就直接转过来
│  ○ 王小乐  男 4岁   还没绑门店        │
│ ─────────────────────────────────│
│  + 当场给他建一个孩子                 │
│  ⇄ 把本店已有的档案关联给这位家长      │
│            [ 取消 ]   [ 绑到本店 ]   │
└──────────────────────────────────┘
其他状态:
 · 加载中:  「正在读取家长信息…」
 · 码过期:  「二维码已失效,请家长刷新『我的二维码』再扫」
 · 家长没孩子:列表为空,只显示「+ 当场给他建一个孩子」
 · 扫到的不是家长码:「这不是家长二维码」
成功:「王小雨 已绑到本店」→ 列表里出现这个孩子

画面 3 · 家长:我的二维码

┌──────────────────────────────────┐
│ ←  我的二维码                        │
├──────────────────────────────────┤
│        (头像)  王女士                 │
│     ┌──────────────────┐           │
│     │                  │           │
│     │    [ 二维码 ]     │           │
│     │                  │           │
│     └──────────────────┘           │
│   到店时给店员扫一下,                  │
│   就能把孩子绑到这家店                  │
│   24 小时内有效   [ 刷新 ]            │
└──────────────────────────────────┘
其他状态:加载中 / 失败「网络不好,点这里重试」

画面 4 · 门店:机构二维码

┌──────────────────────────────────┐
│ ←  机构二维码                        │
├──────────────────────────────────┤
│         阳光视光中心                    │
│     ┌──────────────────┐           │
│     │    [ 二维码 ]     │           │
│     └──────────────────┘           │
│   贴在柜台,家长用微信扫一扫             │
│   就能把孩子绑到本店                    │
│   (只绑定,不开通账号)                 │
│        [ 保存图片 ]                  │
└──────────────────────────────────┘

画面 5 · 家长:扫了门店码以后打开的页面

5a 还没确认身份(微信规定必须点一下才能授权)
┌──────────────────────────────────┐
│   阳光视光中心 邀请您绑定孩子档案         │
│   [ 用微信确认身份 ]                  │
└──────────────────────────────────┘
5b 一个微信有好几个账号 → 先选账号
┌──────────────────────────────────┐
│   您有 2 个账号,选一个:              │
│   ○ T2025xxxx  (来自 XX 品牌)       │
│   ○ 2025yyyy   (来自 YY 店)         │
│                        [ 下一步 ]   │
└──────────────────────────────────┘
5c 选孩子
┌──────────────────────────────────┐
│   把哪个孩子绑到 阳光视光中心?           │
│   ○ 王小宇  9岁   目前在 XX 店        │
│   ○ 王小雨  6岁   还没绑门店          │
│                        [ 绑定 ]     │
└──────────────────────────────────┘
5d 成功:「王小雨 已绑到 阳光视光中心」 [ 去智脑看看 ]
5e 没账号:「您还没有账号,请找门店店员帮您开通」
5f 有账号但没孩子:「您还没有孩子档案,请让店员帮您建一个」
5g 不在微信里打开:「请用微信扫码打开」

说明:本机微信里已经登录过智脑的家长,跳过 5a/5b,直接到 5c。

画面 6 · 家长:个人档案页右上角

┌──────────────────────────────────┐
│ ←  个人档案            ★[我的二维码] │
├──────────────────────────────────┤
│  [ + 新建档案 ]                      │
│  王小宇  9岁                         │
│  ……                               │
└──────────────────────────────────┘

✅ 长官 9-24 已确认(替代上面画面 1 的「扫码绑定」叫法)


第三部分 · 12 维物理核查证据

方法:派 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 -- <文件> 空输出 ✅主会话复核(前序)

核查中抓到的助手错误(已纠正,不进结论)

  1. 一个助手称 list_family_children "没有任何接口调用、是孤儿函数"——主会话亲跑 grep:brand_code_claim.py:95、:133 两处在用。结论作废
  2. 一个助手给的门店按钮注释行号偏了(说 226-228 行)——主会话亲跑:注释在 :237,按钮文字在 :255 / :270
  3. 出图规范里写的"平台手机端 #18b58d / #078f76 / #123d35"出自结算仓库,不是本项目 H5;本项目门店手机端真实主色是 #2fa37c 系 —— 方案图按本项目真实令牌画

技术要点(写技术方案时展开,这里先记下)

  1. 家长看自己孩子:只认"孩子属于这个家长账号",不再叠加机构门(安全:账号编号唯一)
  2. 门店看孩子:_store_visible_clause 加上"当前由本店服务"
  3. 家长写路径(改孩子资料、录检查)随可见性一起核对,避免"看得到存不了"
  4. 门店现场建档 + 关联别家家长:新接口,凭家长码令牌放行跨机构关联
  5. 机构码:服务端签名的机构令牌做成网址,不建新表
  6. 家长扫门店码绑定:新接口,凭微信身份令牌 + 选定账号放行,只绑定不签登录令牌
  7. 家长码图片不再把令牌放进公开网址
  8. 必补测试:家长视角 + 已认领;B 店可见;跨机构关联;现场建档;门店码全流程
  9. 文档:Phase 48 补登仓库进度表(48A 已上生产、48B 进行中)

第四部分 · 方案图对照现网格式自检(9-24 傍晚 · 第 3 轮增量修)

怎么查的:两路并行。① 用门店账号登录 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 有),会显示浏览器"图裂"图标。这是读代码得出的,生产是否常见未实测。

第五部分 · 对照目标自检(9-24 晚 · 第 4-5 轮改图后)

一、按目标逐条核对(总方案第五节 + 已拍板事项)

目标 方案图对应 结论
绑定后机构和家长都能看到孩子(三处可见性修复) 后端改动,不体现在图上;技术要点已列(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 轮)

二、本轮查出并已改的问题

  1. 选孩子弹框默认选中了「已在本机构」的孩子,点「确认绑定」等于什么都没做 → 该行置灰不可选
  2. 家长端选的是王小宇,成功页写王小雨 → 统一
  3. 选项②写「先选择档案」,从详情页进入时档案已确定,说法不对 → 改「在本机构已开通的家长账号中查找」
  4. 选项①写「扫码后立即绑定」,实际扫完还要选孩子 → 改「请家长出示「我的二维码」」
  5. 弹框里没显示扫到的是谁 → 加「家长:王女士 · 账号」,名字取法同现有绑定弹层(备注名 > 微信昵称 > 账号,bind-parent-sheet.tsx:163-164)
  6. 家长端顶栏只放图标,家长看不懂 → 改成带文字的「扫一扫」「我的二维码」
  7. 搜索框图标画成了相机,现网是放大镜 → 改
  8. 多账号选择按账号号码区分家长认不出 → 改为按所属机构显示,账号放第二行

三、不合适 / 有风险、需要长官定的

# 问题 证据 建议
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 属登录改造会话,需对接

四、没核实到的(如实)

五、长官 9-24 晚拍板(第 6 轮)

六、第 8 轮(9-24 晚):新功能放进测试环境真页面

七、第 9 轮(9-24 晚):长官挑出两处不合理 + 自查