2026-09-24 · 阶段 3 architecture · 基线
origin/testfb0dc9d7· 分支feature/scan-binding-48b
上游:需求原文~/reports/brand-partner-spec-v1-20260915.md(Phase 48 · 规则 9/10/13/14/16/18)·
总方案~/reports/home-redesign-master-plan-20260924.md第五节 · 方案~/reports/scan-binding-plan-v1-20260924.md·
方案图scan-binding-realpage-v1-20260924.html(长官 9-24 晚过目通过,「还没有账号」提示页保留;9-24 深夜定 D1–D3,图随之增量补 5g、改一行)
本文不写测试代码、不写完整实现代码。所有「文件:行号」均为本分支物理 grep/sed 结果(见第二节证据)。
| 项 | 结论 |
|---|---|
| 数据库 | 不建表、不加列、无 alembic 迁移。patients.service_org_id / service_org_bound_at 已存在(test/生产 information_schema 实查) |
| 后端 | 改 5 个文件:refractive_record_service.py(档案与记录读写门 6 处 + 3 个末位参)、parent_patients.py(家长看记录详情 1 处)、brand_code_claim_service.py、brand_code_claim.py(新端点全挂在已注册路由上)、jwt_local.py(机构码令牌)。不改 app/main.py、seat_service.py、seat_auth.py(登录改造批次五正在改这几处) |
| 前端 | 改 6 个现有文件,新建 3 个页面 + 3 个组件 + 1 个扫码工具 |
| 存量影响 | 生产:service_org_id 非空 0 行;家长名下孩子所属机构≠家长账号机构 0 行;检查记录机构≠档案机构 0 行 → 可见性改动对现有数据结果集零变化(第二节 V14) |
| 发版独立性 | 本件要动的后端/前端文件 origin/main 与 origin/test 内容一致(git diff --stat 空)→ 可单独发生产 |
~/reports/,仓库文档此前 0 行)。48A 品牌码 + 机构扫码认领后端:origin/main 上 phase48a 提交 23 个,代码注释统一写「Phase 48A 批N」;本期 48B = 扫码绑定(两端扫码 + 可见性修复 + 现场新建)。flywheel-progress.md / flywheel-impl.md 补登 Phase 48 一行(48A 已上生产 · 48B 🔄)。seat_service.py / seat_auth.py / app/main.py;48B 只调用、不修改这三个文件(第八节冲突面)。方法:派 5 个只读助手分查(第 5 个为独立审查后补查记录级读写),每路把全部命令写成可重跑脚本,输出原样落盘;主会话亲手重跑 5 个脚本并 diff:VA/VB/VD/VE 逐字节一致,VC 仅第 4 行时间戳不同。另派 1 个没参与的审查员对照代码挑错:查出记录级读写遗漏(已补为 V15 与 4.1 第 5、6 条)、两处行号偏差(已改);它报的
seat_auth.py路由行号偏差经主会话复核不成立(装饰器就在 :463)。数据库 3 路由主会话亲跑(生产走 seatops 通道,避开db-prod的 RLS 假 0)。
证据归档:~/reports/48b-verify-20260924/(VA–VE 的.sh/.out/ 结论,md5 与/tmp/48b-verify/源一致)。0 命中项均带对照组。
| 维 | 查什么 | 结论 | 关键证据 |
|---|---|---|---|
| V1 | 模型字段 | Patient 有 org_id / owner_type / owner_seat_id / service_org_id / service_org_bound_at / is_sample,无软删除字段(0 命中,对照组 org_id 5 命中);users 有 seat_type / linked_org_id / seat_bound_openid / seat_bound_unionid |
app/models/flywheel.py:16-97;VA ### A-1 |
| V2 | 门店可见性 | _store_visible_clause:门店自建(owner_type NULL/'seat')恒可见;家长自建要求家长席位 linked_org_id==本机构。不认 service_org_id |
refractive_record_service.py:79;VA ### A-2 |
| V3 | 家长可见性 | _patients_filtered_stmt(:175)与 get_org_patient(:613)先套机构外层门 _org_visibility_clause(:111),再 AND 家长 own_clause。家长端 org_id 恒取家长席位的 linked_org_id |
VA ### A-4、A-8(python 现场编译 SQL 证 AND);middleware.py:322 require_org_seat |
| V4 | 家长端接口 | parent_patients.py 12 个接口全部经 get_org_patient / patient_timeline / create_record 门控,无旁路 |
VA ### A-5 |
| V5 | 现有「绑定家长」 | bind_patient_seat(:539)只许绑本机构家长席位(User.linked_org_id == org_id),已绑 409;内部自带 commit(:582) |
VD ### D-5 |
| V6 | 建档 | create_patient(:386)传 owner_seat_id 即 owner_type='parent'(:445),内部自带 commit(:458/:498);门店建档路由 refractive_records.py:74 先过 svc.require_feature |
VD ### D-5 |
| V7 | 认领接口 | 路由 /api/h5/brand-code-claim(brand_code_claim.py:39,main.py:235 已注册):GET /family-code(:57)、POST /family-scan/resolve(:87)、/claim(:117)、/release(:161);claim 校验孩子必须在该家长名下(防越权);服务三分支(未认领写入 / 同机构幂等 / 别家 409 或 force_transfer 转入),只写 service_org_id + bound_at(brand_code_claim_service.py:104,commit :136),无操作人留痕 |
VB ### B-1、B-2;VD ### D-6 |
| V8 | 令牌 | family_scan 令牌 24h 只装 seat_id(jwt_local.py:248/251/269);wx_identity 令牌 10 分钟(:30/:151/:175),均 HS256 + JWT_SECRET_KEY + type 区分 |
VB ### B-3 |
| V9 | 二维码出图 | /api/qr(qr.py:26)公开 GET,文本在网址里;/family-code 把家长令牌拼进 qr_url(→ 进 nginx 日志),前端 0 处消费;qrcode.react 已是依赖且 4 处在用(前端出图可行) |
VB ### B-4 + 主会话 grep |
| V10 | 微信扫一扫 | POST /api/wx/jssdk-config(wx_jssdk.py:117)现成;scanQRCode 代码调用 0(对照组 checkJsApi 22 命中);web/app/h5/family 下 JS-SDK 0(对照组 share 3 处);注入写法可抄 share/[token]/page.tsx:181-236(jweixin-1.6.0 :232),签名地址统一 location.href.split("#")[0] |
VB ### B-5;VD ### D-8 |
| V11 | 微信认人 | POST /api/seat/wx-identity(seat_auth.py:463)换 code → 签 10 分钟身份令牌,不写库;lookup_seat_username_by_wx(return_all=True)(seat_service.py:721)返回 [{username, seat_type}],不按机构过滤;落地组件 SeatWxAuthLanding 只有 onStart 一个参数,标题副标题写死 |
VB ### B-6、B-7 |
| V12 | 前端入口 | 门店列表「返回」:213、「绑定家长」:255、选档案层 :433、BindParentSheet :496;详情页「绑定家长/解绑」:512-528、:693;家长页门禁 :76-79(非家长席位退回 /h5/chat)、「新建档案」按钮 :143-149;三个新路由 git ls-files 0(对照组 1) |
VC ### C-1、C-2;VD ### D-9 |
| V13 | 编号 / 测试 / 冲突 | 仓库文档 Phase 48 进度 0 行;现有测试 test_brand_code_claim.py 9 条、test_refractive_visibility_service_org.py 7 条,均真 PG;verify-trigger-files.txt 不含本件任何文件;登录改造本地分支改了 app/main.py(3 行) + seat_service.py(102 行),本件都不动 |
VC ### C-4、C-6、C-7 |
| V14 | 数据底数 | 见下表 | 主会话亲跑 |
| V15 | 记录级读写(独立审查补查) | 记录门 _fetch_record(:929-959):记录是本机构录的,或孩子由本机构服务即放行,不看孩子现在归谁 → 原机构仍能改 / 完成 / 删除 / 重跑分析旧记录;调用方 11 处(5 处带 for_update=True 写 + trigger_analysis :3203)。家长侧三处按记录机构过滤:时间线 :750-756、记录详情 parent_patients.py:629-634、标已读 :3441-3447(唯一调用方 parent_patients.py:841) |
VE ### E-1、E-2(主会话重跑 diff 一致)+ 主会话 grep |
V14 数据底数(2026-09-24 22:0x 实查)
| 指标 | test | 生产 |
|---|---|---|
| 孩子档案总数 | 56 | 14 |
| 已被扫码认领(service_org_id 非空) | 0 | 0 |
| 家长名下孩子(owner_seat_id 非空) | 14(seat 8 / parent 6) | 3(seat 3) |
| 家长名下孩子所属机构 ≠ 家长账号机构 | 0 | 0 |
| 家长名下孩子无所属机构(org_id 空) | 0 | 0 |
| 检查记录机构 ≠ 档案机构(非示范) | 0 / 189 | 0 / 52 |
| 家长席位 / 门店席位 | 65 / 238 | 20 / 153 |
| 一个微信(openid)绑 2 个以上家长席位 | 1 | 1(同一微信另挂 4 个门店席位,疑似内部测试号) |
| 一个微信(unionid)绑 2 个以上家长席位 | 0 | 0 |
⚠️ 纠正:第 9 轮写的「一个微信绑多个家长账号,生产 0 个」(引自登录改造会话)按 openid 实查是 1 个。不影响「默认用当前登录账号」的决定,但没登录时的多账号兜底必须做(第九节 D2)。
场景:孩子 P,org_id=A,家长席位 S(S.linked_org_id=A)名下,被机构 B 扫码认领后 service_org_id=B。读现有代码推演(VA ### A-8,静态推断,48B.1 第一步用真 PG 测试复现):
| 谁 | 列表 / 详情 / 录记录 / 改资料 | 原因 |
|---|---|---|
| 机构 B(新机构) | 全看不到、改不了 | 外层门过了(service_org_id==B),但 _store_visible_clause(B) 要求家长属于 B |
| 机构 A(原机构) | 列表看不到(符合规则 10);但仍能改、完成、删除、重跑分析自己当初录的记录 | 外层门:已被认领只认 service_org_id;记录门 _fetch_record 只看记录是谁录的 |
| 家长 S 本人 | 自己的孩子全看不到、改不了 | 家长端 org_id=A,外层门对 A 为假,AND 后整体为假 |
| 家长 S(上一行修好之后) | 看不到新机构 B 录的记录,打不开详情,标不了已读 | 时间线对「非当前机构」只放本机构录的记录;记录详情、标已读都按记录机构 = 家长账号机构过滤 |
即:48A 的认领接口一旦被真正用起来,孩子会同时从新机构和家长本人那里消失;原机构还能继续改旧记录。生产认领 0 行,所以还没人踩到。
refractive_record_service.py + parent_patients.py)_patients_filtered_stmt :175、get_org_patient :613)owner_seat_id is not None)只用原有 own_clause(及原有 is_sample 豁免),不再 AND _org_visibility_clause;门店分支原样保留「外层门 AND _store_visible_clause」。org_id 参数在家长分支不再参与过滤),11 个调用点不用改。owner_seat_id == 本席位 id,席位 id 唯一,家长只能看到自己名下的孩子;示范档案豁免逻辑逐字不变。_store_visible_clause :79)Patient.service_org_id == org_id。allow_past_author 小门(规则 10)。_org_ever_authored_clause :135)~_parent_created_clause()(:156)。create_record 把记录的 org_id 记成家长账号机构(:1063 起,org_id=org_id)→ 不加这条,原机构会因为「家长自己录的记录」重新获得只读访问。规则 10 原文是「只剩自己当初录的那几条」。get_org_patient(owner_seat_id=S) → 随第 1 条恢复。allow_past_author → 仍不能改(记录级见第 6 条)。bind/unbind_patient_seat 限定 owner_type∈(NULL,'seat') → 家长名下的孩子 B 不能解绑家长(与现状一致)。patient_timeline(:750-756):家长视角(owner_seat_id 有值)恒走「全部记录」分支;门店视角两个分支原样不动。parent_patients.py:629-634):去掉 RefractiveRecord.org_id == seat["org_id"],保留 id + patient_id(上一步 get_org_patient(owner_seat_id) 已证明孩子是本人的);「家长不见草稿」等原有限制不动。mark_conclusion_read(:3441-3447,唯一调用方 parent_patients.py:841,调用前已 :836 验归属):去掉按记录机构的过滤,保留 id + patient_id。parent_patients.py:1115-1117)不动:只允许改家长本人录的,这些记录的机构恒为家长账号机构。_fetch_record :929)require_current_org: bool = False:为 True 时追加「孩子当前归本机构」= EXISTS(Patient.id == 记录.patient_id AND (_org_visibility_clause(org_id) OR is_sample))。示范档案保留放行,照旧由拿到记录后的 _reject_if_sample_record 给 403。patch_record(:1126)、patch_conclusion(:3391)、push_conclusion(:3409)、delete_draft_shell(:3518)、complete_record(:3636)、trigger_analysis(:3203)。get_record_dto、parent_view_dto 不传 → 原机构仍能看自己录的旧记录(规则 10)。bind_patient_seat :539)allow_cross_org_parent: bool = False:为 True 时,目标席位只校验 role='org_seat'、seat_type='parent',不校验 linked_org_id == org_id(:569 那一条)。/patients/{id}/bind(refractive_records.py:167)不变,仍只能绑本机构家长。is_active(:565-574,老问题)。跨机构路径的席位一律来自 _get_family_seat_or_404(brand_code_claim_service.py:49,已校验 is_active),所以这个老问题不会因放宽而扩大;本期不动它。create_patient :386)service_org_id: Optional[int] = None:有值时在同一次 commit 里写 service_org_id 与 service_org_bound_at=now。默认 None → 现有调用逐字节不变。owner_type='parent',org_id=S.linked_org_id)、由 B 服务」的状态,与家长自己建档后再被 B 认领的状态完全一样。只有一套状态,可见性和解绑回落(规则 18)都不用另写。brand_code_claim_service.py + brand_code_claim.py,全部挂在已注册路由 /api/h5/brand-code-claim 上,不改 main.py)服务层
| 改动 | 说明 |
|---|---|
_patient_brief(:65)加字段 |
birth_date(选孩子显示年龄 · R5)、org_id + org_name(带进来的机构) |
list_family_children(:76) |
查机构名时把 org_id 一起查;返回加 family_username_masked(前 4 位 + xxxx + 后 2 位,与方案图「0012xxxx09」一致) |
claim_patient(:104)加一个幂等分支 |
service_org_id 为空且 org_id == 认领机构 → 原样返回不写(本机构自己的孩子,不写无意义的 bound_at) |
新 create_child_for_family |
调 create_patient(org_id=S.linked_org_id, owner_seat_id=S.id, service_org_id=B, …) |
新 link_child_for_family |
调 bind_patient_seat(org_id=B, patient_id, S.id, allow_cross_org_parent=True) |
| 留痕 | 认领 / 新建 / 关联三个写动作打一行结构化 logger.info(操作席位、孩子、原机构→新机构)。不复用 auth_service._write_audit(:487):它写的是登录审计表,且内部自带 commit,会把业务事务提前提交 |
接口
| 端 | 方法 路径 | 鉴权 | 说明 |
|---|---|---|---|
| 机构 | POST /family-scan/resolve(已有 :87) |
门店席位 | 返回体随服务层加字段 |
| 机构 | POST /family-scan/claim(已有 :117) |
门店席位 | 前端恒传 force_transfer=true(长官定:转店不加确认) |
| 机构 | POST /family-scan/create-child |
门店席位 | {family_token, 建档字段同 PatientCreateIn};与 refractive_records.py:74 一样先过 svc.require_feature |
| 机构 | POST /family-scan/link |
门店席位 | {family_token, patient_id};前置检查照抄 refractive_records.py:167 绑定路由 |
| 机构 | GET /org-code |
门店席位 | 返回 {org_name, landing_url, qr_url};landing_url = {host}/h5/org-bind?ot=<机构码令牌>,host 算法照 admin_brand_code.py:47 _resolve_host;qr_url 用 /api/qr(<img> 才能长按保存) |
| 家长 | GET /family-code(已有 :57) |
家长席位 | 去掉 qr_url 字段,前端用 qrcode.react 本地出图,家长令牌不再进网址和日志(前端 0 处消费,已查) |
| 家长 | POST /org-scan/resolve |
公开 | {org_token} → {org_name},给「确认身份」页显示机构名 |
| 家长 | POST /org-scan/children |
家长席位 | {org_token} → {org_name, children}(已登录路径) |
| 家长 | POST /org-scan/claim |
家长席位 | {org_token, patient_id} |
| 家长 | POST /org-scan/children-wx |
公开 | {org_token, wx_identity_token}(没登录路径) |
| 家长 | POST /org-scan/claim-wx |
公开 | {org_token, wx_identity_token, patient_id} |
没登录时怎么认人
wx_identity_token,调 lookup_seat_username_by_wx(unionid, openid, db, return_all=True),只留 seat_type=='parent'。{code: "no_parent_account"} → 前端显示「还没有开通智脑账号」{code: "multi_parent_account"} → 前端显示 5g 提示页claim 接口都复用现有防越权:孩子必须在 list_family_children(席位) 里,否则 404;认领恒 force_transfer=True(家长本人扫码即本人意愿)。
lookup_seat_username_by_wx不按机构过滤。品牌码开通特意不用它(brand_code_service.py文件头:防止把两家机构的席位错误合并);本件不建号、不合并,只是找这个微信本人的家长账号并做跨机构绑定,所以正需要不过滤机构。
app/auth/jwt_local.py,照 :251/:269 家长码写法)issue_org_bind_token(org_id) 和 decode_org_bind_token(token)。{type: "org_bind", org_id, iat, jti},HS256 + JWT_SECRET_KEY;类型不符或验签失败 → 401。共用扫码工具(新) web/app/h5/chat/lib/wx-scan.ts,放在 wx-oauth.ts 旁边:
share/[token]/page.tsx:232)→ POST /api/wx/jssdk-config {url: location.href.split("#")[0]} → wx.config({jsApiList:["scanQRCode"]}) → wx.scanQRCode({needResult:1, scanType:["qrCode"]}),返回 resultStr;取消、报错分别 reject。isInWeChat(),wx-oauth.ts:22):一律隐藏扫码入口(长官早先定:不在微信里隐藏扫码项)。| # | 文件 | 改什么 |
|---|---|---|
| 1 | store-refractive/page.tsx(「返回」:213 左侧) |
加「机构码」白底小胶囊(A3 定稿:白底、1px #e2e8f0、圆角 999、高 28、左右 10、13px/600 #2fa37c、qr-code 14px、距「返回」12px)→ /h5/store-refractive/org-code |
| 2 | 同上「绑定家长」:255 | 先弹新组件 _components/bind-method-dialog.tsx(居中):①「扫描家长二维码」(仅微信内)②「从本机构家长中选择」→ 现有选档案层 |
| 3 | 新组件 _components/select-child-dialog.tsx |
扫到后选孩子。每行显示:姓名 · 性别 · 年龄(birth_date)+ 所在机构。当前机构 = service_org_id ?? org_id:等于本机构 → 灰色「已在本机构」不可选;否则「当前机构:XX」(XX = 在服务的机构;没被认领过就是开家长账号的机构或扫品牌码的机构,长官 9-24 确认)。底部两个浅绿按钮「新建孩子档案」「关联已有档案」+「确认绑定」→ claim(force_transfer:true)。顶部「家长:昵称 · 账号(打码)」 |
| 4 | _components/create-patient-sheet.tsx(Props :127) |
保持底部弹出。加 3 个可选参数:parentLabel(多一行「家长:王女士」)、submitLabel(「保存并绑定」)、submit(替换默认建档请求 → /family-scan/create-child);不传时现有行为不变。防重复:提交前若该家长名下已有同名同生日的孩子,用现有 app/h5/components/confirm-dialog.tsx 提示直接选 |
| 5 | 选档案层(page.tsx:433-489) | 改居中;加「关联模式」:选中后调 /family-scan/link,不再进 BindParentSheet |
| 6 | _components/bind-parent-sheet.tsx |
底部弹层改居中(只改外壳,照 SeatStoreCard.tsx:922-933;尺寸口径见方案 md 第六部分:宽 328 / 四角 20 / 藏拖动条 / 标题距上沿 16) |
| 7 | [patientId]/page.tsx(:528 按钮、:693 BindParentSheet) |
按钮不变,点开同第 2 行弹框;① 扫完直接调 /family-scan/link 把当前档案绑给扫到的家长,不再选孩子(长官 9-24 定 D1);成功后绑定行沿用现有「已绑定 · 账号」写法(:512) |
| 8 | 新页 store-refractive/org-code/page.tsx |
GET /org-code,<img src=qr_url>,下方「长按二维码可保存图片」+ 两行「打印贴在前台,家长微信扫码即可绑定孩子」「只绑定孩子,不开通账号」;顶栏照 page.tsx:196-205 |
| 9 | family/page.tsx(「新建档案」按钮 :143-149 下方) |
一排「扫一扫」(仅微信内)「我的二维码」;有档案、没档案两种状态都显示 |
| 10 | 新页 family/my-qr/page.tsx |
GET /family-code → QRCodeSVG(family_token);灰字「有效期 24 小时 · 请勿截图转发」+「刷新二维码」 |
| 11 | 新页 h5/org-bind/page.tsx + seat-wx-auth-landing.tsx 加可选 title/subtitle |
详见下方「落地页状态」 |
| 12 | family/page.tsx 扫一扫结果 |
是 …/h5/org-bind?ot=… → router.push 到落地页(已登录,直接到选孩子);否则居中提示「未识别为机构二维码」+「取消 / 重新扫描」 |
SeatWxAuthLanding 的新参数默认值为现有写死文案,现有 3 处页面调用(brand-scan/page.tsx、chat/page.tsx、tablet/page.tsx)和组件测试 chat/components/__tests__/seat-wx-auth-landing.spec.tsx 不受影响。
落地页状态(第 11 行 · h5/org-bind/page.tsx)
getSeatToken() 有值且 getSeatType()==="parent")→ org-scan/children → 选孩子(5c);没有孩子档案 → 5f。startSeatWxOAuth(当前地址)(wx-oauth.ts:36)。POST /api/seat/wx-identity 取 wx_identity_token → org-scan/children-wx。claim 或 claim-wx → 5d 绑定成功。/h5/family/{id};没登录 → /h5/family(现有门禁 :76-79 会带去登录)。/h5/family/new。seat-wx-auth-landing.tsx:55,不动)| 情况 | 处理 |
|---|---|
| 家长码过期(24h)/ 机构扫到的不是家长码 | resolve 返回 401(「二维码已失效」/「令牌类型错误」,:269 现有文案)→ 弹框里一行文字 + 重新扫描 |
| 家长名下 0 个孩子 | 选孩子弹框显示空态,只留「新建孩子档案」 |
| 孩子已在本机构 | 该行不可选;服务端 claim 同样幂等 |
| 孩子在别家 | 直接转入,不二次确认;原机构列表消失,历史只读(规则 10) |
| 同名同生日 | 前端提示直接选(不硬拦,同现有建档「疑似重复提示不硬拦」口径) |
| 示范档案 | 不属于任何席位,list_family_children 天然不含 |
| 两家机构同时认领同一孩子 | 后写的生效(转店不加确认的必然结果),日志两条都在 |
| 机构码令牌被篡改 | 验签失败 401 |
| 门店席位账号的微信扫机构码 | 按「没有家长账号」处理(显示 5e) |
解除认领(/family-scan/release 已有) |
本期不加界面 |
tests/integration/test_scan_binding_48b.py)先写失败测试证明第三节的问题(RED),再改代码(GREEN):
| # | 用例 | 期望 |
|---|---|---|
| T1 | 家长 S 名下孩子被 B 认领、B 录了记录后,S 调列表 / 详情 / 时间线(含 B 的记录)/ B 那条记录详情 / 标已读 / 录记录 / 改资料 | 全部成功(改前 404 或缺记录) |
| T2 | B 列表 / 详情 / 录记录 / 改资料 | 全部成功(改前 404) |
| T3 | A 列表不含;A 曾录记录 → 档案详情与该记录只读可看,改档案 / 改记录 / 完成 / 删草稿 / 改结论 / 推结论 / 重跑分析一律 404;A 没录过、只有家长自录记录 → 详情 404 | 规则 10 |
| T4 | 现有 tests/integration/test_brand_code_claim.py(9)、tests/integration/test_refractive_visibility_service_org.py(7)、tests/api/test_phase74_parent_patients.py(20)、tests/api/test_phase74_parent_patients_update.py(12) |
全绿不回归(family-code 去 qr_url 同步改断言) |
| T5 | /family-scan/create-child |
一次写成 parent 名下 + service=B;S、B 可见,A 不可见;家长机构 = S.linked_org_id |
| T6 | /family-scan/link 跨机构 |
成功;现有 /patients/{id}/bind 绑别家家长仍 404(回归) |
| T7 | 机构码令牌 | 往返成功;拿家长码 / 身份令牌冒充 → 401 |
| T8 | org-scan/children、claim(家长登录) |
成功;claim 别人家的孩子 → 404 |
| T9 | children-wx(真 issue_wx_identity_token 签令牌,造 0 / 1 / 2 个家长席位绑同一 openid) |
404 no_parent_account / 成功 / 409 multi_parent_account;响应里没有任何登录令牌字段 |
| T10 | 本机构自己的孩子 claim | 不写 service_org_id |
next build 必过(9-21 教训:dev/tsc 查不出多导出等构建错)。refractive_record_service.py、parent_patients.py、brand_code_claim_service.py、brand_code_claim.py、jwt_local.pystore-refractive/page.tsx、[patientId]/page.tsx、_components/create-patient-sheet.tsx、_components/bind-parent-sheet.tsx、family/page.tsx、chat/components/seat-wx-auth-landing.tsxchat/lib/wx-scan.ts;_components/bind-method-dialog.tsx、_components/select-child-dialog.tsx;页面 store-refractive/org-code、family/my-qr、org-bindapp/main.py、seat_service.py,本件都不动;本件动的文件不在 verify-trigger-files.txt,不触发对话全流程守门。feature/scan-binding-48b → release-to-test --yes → 长官真机扫码 → release-to-prod --mr <号>。| # | 问题 | 定了什么 |
|---|---|---|
| D1 | 档案详情页点「绑定家长 → 扫描家长二维码」扫完怎么走 | 直接绑定当前档案,不再选孩子 |
| D2 | 没登录的家长扫前台码,微信下有多个家长账号 | 显示 5g 提示页:先登录要绑定的账号,再扫一次;「去登录」按钮。方案图已补这一页 |
| D3 | 选孩子弹框第三个孩子写「尚未绑定机构」 | 现实不会出现:家长自建档默认归开家长账号的机构,品牌码家长归扫码的品牌机构(与 create_my_child 取 seat.linked_org_id 一致)。改为「当前机构:XX」 |
| 子步骤 | 内容 |
|---|---|
| 48B.1 | 档案与记录读写门 6 处(4.1)+ T1–T4(RED → GREEN) |
| 48B.2 | 认领扩展:简要字段 / 幂等 / 留痕 / create_patient 末位参 / bind_patient_seat 末位参 / create-child / link + T5、T6、T10 |
| 48B.3 | 机构码令牌 + org-code + org-scan 5 个接口 + family-code 去网址 + T7–T9 |
| 48B.4 | 机构端前端(改动一览第 1–8 行)+ 扫码工具 |
| 48B.5 | 家长端前端(第 9–12 行)+ 落地组件加参数 |
| 48B.6 | next build + 全量相关 pytest -v + 发 test + 长官真机验收 |