- 14 Jul, 2026 8 commits
-
-
摄入刻意不限诊所:cohort 无诊所过滤;consult/return_visit 用 `org IN (SELECT DISTINCT organization_id FROM fact_emr_treatment_out)` = EMR 出现的所有诊所(2026-07 实测 64 家,非 5 家)。原"5 试点"注释误导, 易被当作 scope 越权 bug —— 全部改成"全瑞尔/随 DW 全量";头部 5 家 id 保留作参考, 非过滤白名单。纯注释,一行逻辑未动。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed -
- deploy/gen-env.sh:生成 .env —— JWT 密钥就地强随机、生产开关一把设、 业务值从环境变量注入、缺失报 __FILL_ME__;防覆盖已有(FORCE=1)。 - deploy/first-import.sh:数据首灌一条龙(seed → 存量 cold-import → 重算画像/计划), 支持 DRY_RUN、ALLOW_PARTIAL 守卫。 - deploy/README.md:精简为维护部署手册(日常/新环境/运维/回滚/监控 5 节), systemd 长流程折叠;明确代码根任意目录(脚本相对定位,不写死 /opt/pac)。 - docker-compose.prod.yml:pac-asr 加 profile=asr → 默认不启(省 2G,可 --profile asr 恢复)。 - deploy-prod.sh:默认 SERVICES 去掉 pac-asr,改 WITH_ASR=1 才带上。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed -
- exchange-code 由 getDel(消费一次即删)改为 get:code 在 60s TTL 内可重复换、 返同一票,到点自然失效 → 根治宿主重载/双开的「登录已过期」(旧逻辑消费即删 + 20s 宽限,超窗重载即 MISS)。去掉 grace 副本。 - 新增 bootstrap 全分支诊断埋点(前端 client-diag 信标 → 服务端 [client-diag] 日志), 带存储/环境探针 storageWritable/isEmbedded/hostOrigin/isReload/ua。 - 会话失效(no-credentials/refresh-fail)推企微监控群(复用 AlertService,不去重): 四类根因自动判(存储分区/重载复用/旧码/全新码到手即死),含 code 足迹 (最新那张/已消费过/code 龄);全码只落服务端 MISS 日志(脱敏,不进群)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed -
契约:{ source:"pac", type:"action", action, payload:{patientId} };此前代码漏了 type。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>luoqi committed -
潜在治疗/原始档案链接原垂直居中于两行左栏(名字+年龄),改为与第一行(名字)顶对齐。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed -
患者卡片右上角「潜在治疗」后加「原始档案 →」—— 走宿主 VIEW_PATIENT 跳转, 占位 {patientId}(userId)/{medicalRecordNumber}(fileNumber),与 EMR「原始病历」同一套 (resolveActionUrl + target=_top);未配 VIEW_PATIENT 则不渲染。 HOST_ACTION_META.VIEW_PATIENT 占位更新为 {patientId}+{medicalRecordNumber}。 本地验证:配 /customerInfo?userId={patientId}&fileNumber={medicalRecordNumber} 后链接正常渲染。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>luoqi committed -
首开「登录已过期」到底是「宿主首屏双消费」还是「code 过期」,现有日志判不了 (BizError 不落日志)。加三条日志: - consumed(首次):正常首次换码 - IDEMPOTENT 幂等命中:同一 code 宽限窗内被换第二次 = 双消费实锤(幂等修复已静默兜住) - MISS 失效:code 既不在池也无幂等副本 = 过期/未知(非双消费,幂等治不了) 只记 code 前 8 位,不泄全码。对方下次一复现即可从生产日志判定根因。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed -
根因:全新浏览器(无 localStorage)下,幸存 iframe 拿到的 code 已被消费(宿主预取/ 首屏双渲染中首个已消费但没落地/宿主自己先验一次)→ 第二次 exchange 得 10102 → 无票 可退 → SessionExpired。90aefc7d 只挡了"首个成功落地"的情况,挡不住这种。 改:exchangeCode 首次消费后落一份短期幂等副本(CODE_RESULT_PREFIX,EXCHANGE_GRACE_SECONDS=20s); 同一 code 在宽限窗内重复换返回同一票,不报 10102;过窗即真失效,把可重放窗口压到最小。 模拟宿主(4100)复现 + 验证:已消费 code + 清 localStorage 首开,修复前报「登录已过期」, 修复后工作台正常加载;API 层同一 code 两次 exchange 均返回 code=0。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed
-
- 13 Jul, 2026 8 commits
-
-
详情页患者卡片:手机号从「脱敏 + 眼睛 toggle」改为直接显示 patient.phone 明文; 移除 eye/eye-off 按钮及 reveal 状态。保留真/假角标 + 复制按钮。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed -
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed -
- 左栏 PatientRow:诊所名变长后「待认领」被挤换行 → StatusPill 加 whitespace-nowrap、 诊所名 min-w-0 truncate、电话定宽,布局稳住 - 通用占位替换 lib/action-url.ts(fillActionUrl/resolveActionUrl):扫 {key} 从 ctx 取值 URL 编码,缺失清空;替代原来写死的 {patientId} 单占位 - 治疗历史卡「详情」槽:配了 VIEW_MEDICAL_RECORD 就打开宿主电子病历原文(占位 {brandId}/{clinicId}/{medicalRecordNumber}/{patientId}),隐藏原 PAC 时间轴抽屉; 未配置回退原详情。CREATE_APPOINTMENT 也切到通用引擎(同样支持这些 id 变量) - 后端 plan-aggregate 下发 patient.brandId(source_unit → host.orgAliases 反解品牌 GUID); 前端补 patient.brandId / plan.targetClinicId 贯穿 raw→adapt→mock 类型链 - HOST_ACTION_META.VIEW_MEDICAL_RECORD 占位更新为 brandId/clinicId/medicalRecordNumber/patientId Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>luoqi committed -
新增 Host.clinicNames(诊所 id→名),从摄入源派生;/auth/session 合并进 dictionary.clinics 下发(宿主换票传的 dictionary.clinics 优先覆盖,服务端补齐其余)。 前端 loadSession 合并 session.dictionary → visibleClinics 直接显示中文名,GUID 不再裸露。 - manifest 加 clinic_directory: { table, id_field, name_field } 声明诊所名来源 - refresh-clinic-names CLI:按 clinic_directory 从 DW(sql_source)或 CSV(文件源) SELECT DISTINCT 出 id→名,幂等 upsert host.clinicNames;可接 cron 定期刷 - jvs-dw 源 = DW fact_emr_treatment_out.organization_name 顺带发现 preset 把 dad2f04a 硬编码成"杭州高德",DW 真名是"欧美中心诊所"—— 再证手维护/登录传的名字会过时,派生才是单一真理源。 本地验证:CLI 派生 5 家写入;合并逻辑(登录覆盖+服务端补齐)已测;typecheck 干净。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>luoqi committed -
契约约定 orgScope 传宿主组织节点 id(品牌级=品牌 GUID),但 PAC org 树品牌节点 id 用的是名字(source_unit=瑞尔/瑞泰),宿主传品牌 GUID(ba67e6cf…=瑞尔)对不上 → 品牌制租户 fail-closed → 品牌主管(如岳丽丽 leader)登录后一条数据看不到。 诊所层无此问题(节点 id 本就是诊所 GUID)。 改动: - OrgNode 加 aliases[];expandOrgScope 按 id+aliases 建索引(名字/GUID 等价命中) - buildOrgTree 接受 brandAliases(品牌名→GUID),挂到品牌节点 aliases - Host 加 org_aliases(Json):PAC 节点 id→宿主契约 id 映射;deriveTree 读它传入 - jvs-dw-preset 补品牌 GUID;seed 回填 jvs-dw.org_aliases(瑞尔=ba67e6cf/瑞泰=77057aed) - 模拟登录品牌级改传品牌 GUID(真正模拟契约,旧版传品牌名自洽掩盖了此 bug) 不动 source_unit、不影响诊所层与 friday(source_unit 本就是 GUID)。 本地验证:真实 OrgTreeService.expandScope(品牌 GUID ba67e6cf) → sourceUnits=[瑞尔] + 瑞尔全部诊所;品牌名/诊所 GUID 照常;typecheck 干净。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed -
FRIDAY appointment_base → PAC appointment(全部预约)+ encounter(已到诊接诊事件), 与第一个宿主 jvs-dw 同款设计(一个预约源喂两张 canonical)。要点: - status 编码两家一致(1/2/3/4/5/6/8/9);status=10=草稿(查 friday-saas origin/hotfix AppointmentDataCommons draft==1 实锤)→ filter 丢弃 - scheduledAt 用 appointment_date + appointment_start 经 derive 拼(datetime_str 是区间且 14% 空,不用) - 已到诊(status 4/5/6 且 in_time 非空)拆 appointment_encounter_rows → encounter, startedAt=in_time;externalId=预约 id,将来 EMR(med_emr_info.appointmentId)据此回挂 本地验证:809 appointment + 640 encounter,1449 txns/1449 facts,0 失败 0 drift; patient timeline 正确重建「预约→到诊」就诊史。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed -
lookup operator(Layer A.5 第 8 个):按外键 join 子表、按 order_by 挑一条、 把字段附加到主表——补上"1:N 子表归约成标量"的空(join_arrays 只产数组, 其余 operator 全是单表内变换)。纯增量,不改任何现有 operator,不影响 jvs-dw。 FRIDAY(第二宿主)patient 首版:方案 B(tenant=friday-market + 品牌 source_unit), 宿主纯导原始表 customer_basic_info + customer_contacts,phone 由 lookup 挑默认号 (is_default→本人→最早)填入。本地 450 患者/2 品牌真实落库验证:source_unit、 挑号(is_default 胜出)、gender/病历号/时区全对。CSV 含 PII 不入 git。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed -
api-client 清 session 分支未按 auth 门控,导致一次性 code 被重放/过期(10102)时 误清刚建立的会话。宿主 iframe 首屏用同一 code 换两次时,第二次 10102 把第一次 存好的有效会话清空 → SessionExpired。改为仅「带 token 的受保护请求」失败才清; 公开端点失败交由 bootstrap / doRefresh 各自按语义决定,兜底 fall-through 生效。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed
-
- 09 Jul, 2026 2 commits
-
-
有提交过执行记录的患者,在列表行左上角按最近一次 outcome 的分组打标签, 分组/文案/配色复用 @pac/types EXECUTION_OUTCOME_META 单一真理源。 列表接口批量带出每 plan 最近 outcome;详情页提交回访后左栏角标即时反映(不重拉)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed -
- 新增两个 postMessage 动作 OPEN_POTENTIAL_TREATMENT / OPEN_RETURN_VISIT(入 HostActionKey + HOST_ACTION_META,admin 下拉可选);HOST_ORIGIN 作 postMessage targetOrigin,走 host 配置 (会话下发的 actionUrls.HOST_ORIGIN),禁用 '*' - 患者卡片右上角「影像/档案」→「潜在治疗」;通话结果「拨打」→「回访」,固定就是这套(不判断 iframe) - 信封只含 { source:'pac', action, payload:{patientId} };patientId=external_id - postToHost 只在未配 HOST_ORIGIN 时提示;宿主侧监听校验 origin + source 后弹自己的组件 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>luoqi committed
-
- 08 Jul, 2026 1 commit
-
-
- /auth/session 返回当前 host 的 actionUrls(AuthService.getActionUrls),所有角色可拿, 不再依赖 admin 端点;SessionResponse 加 actionUrls 字段 - 前端 loadSession 把 actionUrls 存进会话 user;「新建预约」读 actionUrls.CREATE_APPOINTMENT → 替换 {patientId}(=external_id)/{doctorId} → window.open(_top);未配置则提示去配 - 宿主动作 URL 仍是宿主在 admin 配的数据,代码零硬编码 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>luoqi committed
-
- 07 Jul, 2026 3 commits
-
-
上次把按钮从占位 toast 改成了 window.open('http://localhost:4100/appointment','_top'), 硬编码 URL 随部署上了生产 —— 线上点击会 _top 跳到用户本机 localhost 死链。 回退成原占位 toast;删除 action-urls.ts(带写死默认值的配置)。真实接入时 URL 由 宿主 actionUrls / env 下发,代码不写死。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>luoqi committed -
会话前已存在、本次一并提交:cold-import.service / sync-incremental scheduler / clinical-gap potential-treatment-gap SQL / cold-import manifest.schema / dw-lag-monitor / canonical-codes / ingestion·monitoring 文档 / jvs-dw manifest.yaml / package.json / pnpm-lock / .gitignore(next-env.d.ts)/ docs/friday-saas-mapping-temp。 非本次会话产出,随此次部署一并落地。
luoqi committed -
- 嵌入检测(window.top):隐藏 PAC 品牌/用户/登出(IdentityCluster),会话失效显示 「登录已过期+重试」而非 dev 快速登录(SessionExpired);模拟登录嵌入态不出现 - 动作插槽(新建预约等):PAC 直接 window.open 宿主链接,不 iframe 嵌、不 postMessage、 无 returnUrl;患者上下文经 query 传,回跳靠 history.back()/宿主自己的召回页 - org-tree 派生:启动预热(OnApplicationBootstrap)+ loose index-scan 改写, 冷派生 ~3.8s→~1ms,消除首个 scoped 用户登录尾延迟 - docs: overview §4/§5 补 actionUrls 打开方式/回跳、sandbox 权限、嵌入隐藏、会话失效 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed
-
- 03 Jul, 2026 1 commit
-
-
把 push(形态 A/C)打磨成宿主唯一接入通道,存量=按批重放的增量: 摄入一致性(消除同源分叉): - 形态 C 复用 normalizeCanonical(金额元→分 / 无时区时间补 offset),含 envelope occurredAt/updatedAt —— 与形态 A/pull 同一转,宿主用自己系统的格式发,不做转换。 - 业务事件先于患者主档到达 → dispatcher stub 自建空壳患者(对齐 form A),主档后到补全 同一行,不双人;push 跨批顺序无关。 幂等/乱序: - patient_facts 加 source_updated_at,fact-writer 按它裁决:更旧版本晚到 → stale_skipped 跳过,不旧覆新。关掉"补推快照 vs 实时推送并发交错"的乱序窗口。 体制/性能: - 限批 500(A 从 2000 对齐 C);persona 入队去抖(5min 桶去重,存量批次合并重算)。 - 同 host 并发闸(信号量,PAC_PUSH_CONCURRENCY 默认 4;幂等+裁决保证并发语义=串行)。 - 单飞锁索引收窄 WHERE direction='pull' —— 原索引把并发 push 批次拦成 P2002/500。 异常/可观测: - 整批全失败 → 拒收 30802 + SyncLog FAILED;失败率/疑似改表 → 告警。 - 未知 source → 10001(非 90000,避免宿主对永久错误无限重试)。 - push 断流监控(超 26h 无 push 告警);form A 响应透出 mappingMisses/suspectFields 供自检。 文档 channel-push 按宿主视角重写(subjectType 单一口径、金额时间统一"原样发 PAC 归一")。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed
-
- 02 Jul, 2026 2 commits
-
-
请求参数按 role 表那样逐字段列出(字段/类型/必填/说明),对接方更易懂。 dictionary 标注为选填(schema 是 .optional(),不传换票仍成功,仅显示原始 id)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
luoqi committed -
执行结果回调(opt-in,宿主配 callbackUrl 才投递): - Host 加 callbackUrl + callbackSecrets(存原文,PAC 签名用);迁移加两列。 - ExecutionService.submit 提交后入 execution-callback 队列(6 次退避重试,死信可见)。 - ExecutionCallbackService:查 execution→plan→patient 拼 payload,HMAC-SHA256("{ts}.{body}") 签名(与 push 入站同配方),fetch+8s 超时,非 2xx/超时抛错交 BullMQ 重试;jobId=executionId 幂等。 - 凭据页新增「执行结果回执」区:callbackUrl 输入 + Callback Secrets(add/rotate/grace/删), suffix 用 sha256 指纹不回显原文。对接文档同步。 顺手修 push 密钥存储 bug: - create/rotate 原本存 scrypt 哈希,但 HmacVerifier 把它当原文做 HMAC key → 宿主拿明文、 PAC 拿哈希,两边 key 不一致,push 验签永远过不了。改为存原文(对齐 seed 与 verifier 预期)。 - 本地实测:轮换后用返回的明文签名 → 验签通过;错误签名 → 10106 拒绝。 修凭据页「刷新即整页重载」: - useHostAdmin.refresh 一进来就切 loading,父组件把整页换成占位、卸载 HostAdminApp, 连带把"仅显示一次"的明文 secret 弹窗刷没(来不及复制)。改 stale-while-revalidate: 已有数据不切 loading,旧数据留屏后台重取。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>luoqi committed
-
- 01 Jul, 2026 6 commits
-
-
code/accessToken/refreshToken 有效期不写清楚,接入方不知道过期后该调哪个接口; 另外"用 code 拼 URL"具体怎么拼、嵌哪条路径此前只字未提,补链接到 §5。
luoqi committed -
getStats() 并发跑 10 条查询,其中两条缺覆盖索引: - patient_transactions.count(hostId, occurredAt>=X):(patient_id, occurred_at) 索引带不动"只按时间不带 patient_id"的过滤,近乎全扫索引。线上冷执行 8.3s。 - patient_facts.count(hostId, status='active'):(hostId, tenantId) 没 status、 (patientId, status) 没 hostId,两个都覆盖不了。线上冷执行 18.9s。 三列(非二列):tenant-guard 扩展对未带 tenantId 的查询自动注入 tenantId 过滤, 二列索引配上这条自动追加的谓词后 planner 直接弃用索引退回全表扫,三列才稳定命中 index-only scan。 两条索引都用 CREATE INDEX CONCURRENTLY(表已 379万/371万行,避免锁表阻塞摄入写入)。 线上手动建索引 + 验证已完成(index-only scan,查询从 8~19s 降到 <1s;端到端 GET /admin/host/self 28s → 0.9s);IF NOT EXISTS 保证下次 `prisma migrate deploy` 在 pac-migrate 里重跑时是空操作,只补 `_prisma_migrations` 记录。
luoqi committed -
tenant-group 重构后每请求都派生 org 树(全扫 patient_transactions 数十万行)解析用户 scope, 冷缓存时登录长时间卡(首次慢、之后正常)。修: - 空 orgScope(全集团用户,最常见)直接短路返回不限,不派生树(400ms→50ms) - 非空 scope 仍需树:getTree 加单飞(并发 miss 共享一次,防惊群)+ TTL 10min→6h(org 数据日更) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
fix(docs): auth-login 链接指向真实 endpoint 页 —— /docs/api/auth 是 tag 目录无 index(404),改 AuthController_exchangeToken Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
committed pac.json server 改回 localhost:3101(本地开箱即对); pac-docs Dockerfile build 期用 DOCS_API_URL 覆盖 servers;compose.prod 传生产域名。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed
-
- 30 Jun, 2026 4 commits
-
-
build-document 加 .addServer(env);重新生成 pac.json,servers=https://pac.jarvismedical.asia(paths 已含 /pac/v1)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
资源引用走 /docs/_next/*,避免与 pac-web 的 /_next 撞;仅生产生效。 nginx 侧加 /docs/、/docs/_next/(rewrite)、/api/search、/og/docs/、/llms 几条 location 兜到 3102。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
文档(apps/pac-docs,首次纳入仓库): - 接入/运维/参考全面重写,脱敏、对齐现有代码 - 运维拆为 部署/数据摄入/监控/故障排查;新增「设计系统·DESIGN.md」(三层 token + oklch) - 过时文档归档 → docs/_archive;ADR → docs/adr 代码: - perf(prisma): PrismaService 连接池按 PAC_COHORT_CONCURRENCY 自动推导 connection_limit(免手调) - chore(prisma): schema 注释更正(payloadHash 不参与 source_event_id 合成) - chore: docker-compose 增 pac-docs 服务 - 含 feat/tenant-group-p0 分支在途 WIP(pac-service / packages/types) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed
-
- 28 Jun, 2026 2 commits
-
-
并发重算(--concurrency>1)时发现:缓存空/过期瞬间,N 个 recompute 同时调 getMonetaryQuantiles → 同时发 N 份全租户聚合 $queryRaw(370万 facts 实测单跑 4.1s) → 打爆 4 核 DB + 连接池 → 进程卡死(stat=S,0 吞吐)。串行版无事因缓存命中只跑 1 次; 且缓存 30min 过期,长批(~1.6h)半路会再次惊群。 修:single-flight —— 缓存 miss 时把在飞 Promise 存 map,并发调用复用同一个查询, 只跑 1 份。等待者 await JS promise(不占 DB 连接)。根治,也利好增量 cohort 路径。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
实测推翻旧注记"persona 重算 CPU-bound、异步并发不提速":每患者 recompute 含 ~15 次 串行 DB 往返(2 次全量 fact 读 + 多次 findFirst),实为 **I/O-bound**。单进程分块 Promise.all 并发可重叠这些 DB 等待 —— 本地 500 患者 17s(串行)→ 4s(并发16),~4.5x, concurrency 8-16 后 plateau(DB 成瓶颈)。正确性验证:500/500 各 1 active persona、 0 重复版本、0 空特征(不同患者独立写 + 同患者不并发,无竞争)。 - --concurrency=N:分块并发(默认 1 向后兼容) - --shard=i/k:多进程分片(按患者序号取模,吃多核;DB 先饱和时用) - 大批(>500)只打进度行不逐条刷屏;orderBy id 稳定序供分片 全量重算 131689 患者:7h(串行)→ ~1.6h(并发12)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed
-
- 27 Jun, 2026 3 commits
-
-
后端: - plan.recomputeByPatient:patient 守卫补 source_unit(与其它 by-id 守卫对齐;防跨品牌触发重算 + 存在性探测) - persona.getByVersion:删(无 scope 的 findUnique by-id,死代码,属之前修过的泄漏模式) - cold-import:改"sourceUnit:''(过渡)"陷阱注释为真实语义(从 manifest 解析,勿退回 '') - auth.allClinics:去"恰好两品牌"硬编码,改 Object.values(MOCK_PRESETS) 遍历;refresh/mcp 陈旧注释(clinicIds→orgScope) 前端: - auth-store:setTokens 同 sub 沿用上次 clinicIds 占位,消除 silent refresh 时诊所筛选闪空(M2 竞态) - visibleClinics(user) helper:clinicIds 空(集团级=不限)→ 回退字典全部诊所,修集团登录后诊所筛选空白(M1); 4 处(plans-list/patient-picker 筛选 + plans/plan-detail 计数)统一用它 两端 tsc 0 错。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
agent 审查(3 个独立都挖到)的真·安全洞:expandOrgScope 对树里找不到的 ref 兜底成 "当裸 clinicId、sourceUnits 留空"。而瑞尔/瑞泰共用 tenantId,sourceUnits=[] 被下游当 "不限品牌" → 跨品牌看全部患者(含真号 reveal)。触发:退化树(无数据)、新诊所未进树、 10min 缓存陈旧。 - org-scope:探测 branded(树有非空 source_unit);品牌制租户 + 非空 scope 却没解析出 任何品牌 → 塞 DENY_SOURCE_UNIT 哨兵(患者查询返回空),不退化成不限。命中集团根仍合法不限。 单命名空间/退化树(无品牌)→ 维持 [] 不限(正确)。回传 unresolved 供刷新重试。 - org-tree:展开有 unresolved → 强制重派生树一次再展开(避免误拒刚摄入的新诊所合法用户); forceRefresh 30s 防抖,挡 bogus ref 刷库。 单测:品牌ref/诊所ref正常、集团根→不限、未知ref→DENY、混合(含已解析品牌)→限该品牌不DENY、 单命名空间未知ref→不限。活体三级 + REST 无回归。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
之前 org-tree 复用 jvs-dw-preset 的硬编码 clinic UUID 建树 —— 把通用隔离路径绑死到 mock 演示数据 + 写死 id(换环境/换 DW 即废)。改为从摄入数据派生: - OrgTreeService(注入 Prisma):$queryRaw 从 patient_transactions.clinic_id JOIN patients.source_unit 派生「品牌→诊所」,按 (hostId,tenantId) 缓存(10min TTL); 无数据→退化树。对任何 host/环境自适应,零硬编码 id。 - AuthModule 导出 OrgTreeService;tenant-scope 拦截器(from+mergeMap 异步展开,ALS 不变) 与 mcp-auth 注入它,每请求 expandScope。 - jvs-dw-preset 退回**仅 mockLogin 用**(dev,env 门控):集团/品牌 slug 是稳定逻辑名; clinic UUID/中文名仅演示展示,无数据源,换环境降级成 id —— 隔离仍由 DB 派生树保证正确。 验证(DB 派生树):诊所级石琳 sourceUnits=['瑞尔']/2诊所、品牌瑞尔/3、瑞泰/2、集团不限、 REST /plans 仅命中本诊所 —— 与原硬编码结果一致。tsc 0 错。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed
-