跳转至

DTL · L-20260825-04

文档编号:DOC-A04-DTL / L-20260825-04 维护人:听写 DT(DOC-A01 PMG V8.3 §人机协作模型) 关联 DTLL-20260825-03 撰写日期:2026-08-25 任务:WP-06 收尾——5 端 WhoamiProvider 注入 + 登录页接入 useWhoami().login + RN 版 Provider


一、任务背景

L-20260825-03 §3.4 列出 WP-06 后续 4 项 TODO: 1. 后端 /api/v2/bff/whoami 接口实现(依赖后端,本轮不做) 2. 5 端 layout.tsx 注入 WhoamiProvider ✅ 3. 5 端登录页接入 useWhoami().login ✅ 4. RN 端 RN 版 WhoamiProvider(不能用 'use client')✅

本轮在用户「上述工作全部顺序执行」指令下完成 #2/#3/#4(纯前端部分),#1 留待后端排期。


二、交付文件清单

2.1 新增文件(5)

文件(相对 dip1/frontend/) 类型 内容
packages/shared/src/client/whoami-context.rn.tsx 新增 RN 版 WhoamiProvider(无 'use client',内存态无持久化,0 新增 npm 依赖)
apps/mfr-portal/app/providers.tsx 新增 MFR Web 客户端 Providers 包裹器
apps/opr-admin/app/providers.tsx 新增 OPR Web 客户端 Providers 包裹器
apps/lgp-portal/app/providers.tsx 新增 LGP Web 客户端 Providers 包裹器
docs/工作日志/dtl/L-20260825-04(本日志) 新增 WP-06 收尾交付日志

2.2 修改文件(11)

文件(相对 dip1/frontend/) 变更
packages/shared/package.json exports 新增 ./client.rn 子路径(指向 whoami-context.rn.tsx)
packages/shared/src/client/index.ts "use client" 指令——否则 Next 14 SSR 时 re-export 的 WhoamiProvider/useWhoami 被替换为 undefined
apps/mfr-portal/app/layout.tsx body 内包裹 <Providers>{children}</Providers>
apps/mfr-portal/app/login/page.tsx setToken → useWhoami().login(token, "mfr-portal"),加 user=null 错误兜底
apps/mfr-portal/lib/auth.ts TOKEN_KEY 从 dip1.jwt 改为 dip1.token(对齐 shared TOKEN_STORAGE_KEY)
apps/mfr-portal/tsconfig.json paths 移除 @dip1/shared/* 通配符,让 webpack 走 node_modules + package.json exports 解析(修复 'use client' 边界)
apps/opr-admin/app/layout.tsx body 内包裹 <Providers>{children}</Providers>
apps/opr-admin/app/login/page.tsx setToken → useWhoami().login(token, "opr-admin"),加 user=null 错误兜底
apps/opr-admin/lib/auth.ts TOKEN_KEY 从 dip1.jwt 改为 dip1.token(对齐 shared TOKEN_STORAGE_KEY)
apps/opr-admin/components/Sidebar.tsx 加 useWhoami():显示 user.display_name + logout() 双清(clearToken + Provider logout)
apps/opr-admin/tsconfig.json 同 mfr-portal,paths 移除 @dip1/shared/* 通配符
apps/lgp-portal/app/layout.tsx body 内包裹 <Providers>{children}</Providers>
apps/lgp-portal/app/login/page.tsx 从占位页改为完整登录页(textarea + useWhoami().login(token, "lgp-portal") + 错误兜底)
apps/wkr-app/App.tsx 顶部 import WhoamiProvider from @dip1/shared/client.rn;export default 改为 AppWithProvider 包裹 <WhoamiProvider app="wkr-app">
apps/wkr-app/tsconfig.json moduleResolution: "bundler"(expo/tsconfig.base 默认不支持 package.json exports 子路径)
apps/cst-app/App.tsx 顶部 import WhoamiProvider;export default function App 内直接 <WhoamiProvider app="cst-app">…</WhoamiProvider> 包裹
apps/cst-app/tsconfig.json 同 wkr-app,加 moduleResolution: "bundler"

三、5 端注入矩阵

类型 layout/App 注入 登录页接入 TOKEN 存储
MFR 门户 Next 14 Web ✅ providers.tsx + layout.tsx ✅ useWhoami().login(token, "mfr-portal") ✅ dip1.token(对齐 shared)
OPR 后台 Next 14 Web ✅ providers.tsx + layout.tsx ✅ useWhoami().login(token, "opr-admin") ✅ dip1.token
LGP 门户 Next 14 Web ✅ providers.tsx + layout.tsx ✅ useWhoami().login(token, "lgp-portal") — (无 lib/auth)
WKR App Expo RN ✅ AppWithProvider wrapper — (无登录页,DEV 模式 Mock) 内存态(RN 版无 localStorage)
CST App Expo RN ✅ App 内直接包裹 — (无登录页) 内存态

四、关键设计决策

D1:RN 版 Provider 用内存态而非 AsyncStorage

  • 背景:RN 端无 localStorage,标准方案是引入 @react-native-async-storage/async-storage
  • 决策:内存态(Module-level state),App 重启会丢失
  • 理由
  • 保持 0 新增 npm 依赖红线(PMG V8.3 §前端工程红线)
  • 开发期通过 DEV_TEST_USERS Mock 重新登录即可恢复
  • P1 WP-04 后端接口就绪后再评估接入 AsyncStorage
  • 影响:RN 端 App 重启需重新登录(开发期可接受)

D2:3 个 Web 端 lib/auth.ts TOKEN_KEY 统一为 dip1.token

  • 背景:原 mfr-portal / opr-admin 用 dip1.jwt,shared 包用 dip1.token
  • 决策:统一为 dip1.token(对齐 shared TOKEN_STORAGE_KEY)
  • 理由
  • WhoamiProvider 写入 dip1.token,lib/auth.ts getToken 也读 dip1.token,保证 API client 自动携带 token
  • 兼容现有 register/page.tsx 等使用 setToken/getToken 的页面,无需改造
  • 影响:旧 dip1.jwt key 中的 token 会失效,用户需重新登录(开发期可接受)

D3:tsconfig paths 移除 @dip1/shared/* 通配符

  • 背景:原 paths 有 @dip1/shared/*../../packages/shared/src/*,让 webpack 通过 paths 解析子路径
  • 问题:paths 解析绕过了 node_modules + package.json exports,导致 'use client' 边界处理失效,SSR 时 WhoamiProvider 为 undefined
  • 决策:移除 @dip1/shared/* 通配符,仅保留 @dip1/shared 主路径
  • 结果:tsc + webpack 都通过 node_modules + package.json exports 解析 @dip1/shared/client,'use client' 边界正确传递
  • 验证:7/7 typecheck 通过 + 8/8 路由 200 OK

D4:packages/shared/src/client/index.ts 加 "use client"

  • 背景:client/index.ts 是 .ts 文件(无 JSX),re-export 了 'use client' 的 WhoamiProvider
  • 问题:Next 14 SSR 编译时,re-export 文件缺少 'use client' 指令,client reference 未传递,导致 WhoamiProvider 在 SSR 时为 undefined
  • 决策:在 client/index.ts 顶部加 "use client" 指令
  • 理由:Next 14 文档明确要求 re-export 'use client' 组件的文件也需要 'use client'
  • 验证:修复后 SSR 渲染正常

D5:RN 端 tsconfig 加 moduleResolution: "bundler"

  • 背景:expo/tsconfig.base 默认 moduleResolution 不支持 package.json exports 子路径
  • 问题:tsc 报 Cannot find module '@dip1/shared/client.rn'
  • 决策:在 cst-app 和 wkr-app 的 tsconfig.json 中显式加 moduleResolution: "bundler"
  • 结果:tsc 能解析 @dip1/shared/client.rn 子路径

五、阻塞与解决

B1:tsconfig paths 通配符绕过 'use client' 边界

  • 现象:OPR / MFR dev server 报 500,digest 2152732032,错误 "Element type is invalid: expected a string ... but got: undefined"
  • 根因:tsconfig paths @dip1/shared/* 让 webpack 通过源文件路径解析 @dip1/shared/client,绕过了 node_modules + package.json exports,导致 Next 14 的 'use client' 边界处理失效
  • 解法:移除 paths 通配符 @dip1/shared/*,仅保留主路径 @dip1/shared;让 tsc + webpack 都通过 node_modules + package.json exports 解析子路径
  • 验证:7/7 typecheck + 8/8 路由 200 OK

B2:client/index.ts re-export 缺 'use client'

  • 现象:即使 paths 修复后,SSR 仍然 500(digest 不变,说明 webpack 缓存命中)
  • 根因:client/index.ts 是 .ts 文件,re-export 'use client' 的 WhoamiProvider,但自身无 'use client' 指令,Next 14 SSR 时 client reference 未传递
  • 解法:client/index.ts 顶部加 "use client" 指令
  • 验证:清理 .next 缓存 + 重启 dev server 后 8/8 路由 200 OK

B3:webpack 缓存命中导致修复不生效

  • 现象:修改 client/index.ts 后,dev server 仍报相同 digest 错误
  • 根因:Next 14 dev server 的 webpack 缓存命中,没有重新编译
  • 解法:停掉 OPR / MFR / LGP 三个 dev server,删除 .next 缓存目录,重启 dev server
  • 验证:30s 编译完成后 8/8 路由 200 OK

六、验证结果

6.1 typecheck

7/7 projects 0 errors: - @dip1/shared ✅ - @dip1/ui-web ✅ - @dip1/cst-app ✅ - @dip1/wkr-app ✅ - @dip1/lgp-portal ✅ - @dip1/mfr-portal ✅ - @dip1/opr-admin ✅

6.2 冒烟测试(8 路由)

URL 状态 Length
http://localhost:3000/login (OPR) 200 6,696
http://localhost:3001/login (MFR) 200 7,253
http://localhost:3002/login (LGP) 200 6,830
http://localhost:3000/cpt (OPR) 200 26,650
http://localhost:3000/wkr-demo (OPR) 200 21,029
http://localhost:3001/quote/demo (MFR) 200 38,902
http://localhost:3000/dashboard (OPR) 200 9,272
http://localhost:3001/quote (MFR) 200 18,012

6.3 登录页关键元素验证

关键元素 结果
OPR /login page-title / page-sub / login-box / btn / Dev JWT / Keycloak / textarea / submit 8/8 ✅
MFR /login page-title / page-sub / login-box / btn / Dev JWT / PPL / textarea / register / mfr-test 9/9 ✅

七、本轮合规边界确认(PMG V8.3 三 AI 隔离)

行为 规则 本轮执行 合规性
听码 WDE git push / Gitee 远程 ❌ 严禁 ✅ 未执行 ✅ 合规
听码 WDE 生产部署 / ocm/ 目录 ❌ 严禁 ✅ 未执行 ✅ 合规
文档日志即时性 ✅ 强制 ✅ 任务完成立即写 DTL ✅ 合规
0 新增 npm 依赖 ✅ 红线 ✅ RN 版 Provider 用内存态,无 AsyncStorage ✅ 合规

八、WP-06 收尾后剩余 TODO

依赖 启动条件
后端 /api/v2/bff/whoami 接口实现 后端开发排期 后端就绪后
RN 端接入 AsyncStorage 持久化 P1 WP-04 后端接口 + AsyncStorage 依赖评估 P1 启动后
5 端登录页接入 Keycloak 25 OIDC 授权码流程 后端 Keycloak 配置 + RS256 V2 阶段

九、关联引用

  • 方法论文档ref/DOC-P33前端设计方法和部分关键需求.md(V3.0 MSC-FE)
  • PMGAGENTS.md(V8.3 §AI 行为规则)
  • 上游 DTL
  • L-20260825-03(5 阶段顺序执行全闭环)
  • L-20260825-02(f12 全通过 + WP-06 启动)
  • L-20260825-01(前端交付与测试环境手册)
  • L-20260824-08(前端 DOC-P33 重设计)

十、修订记录

版本 日期 修订人 修订内容
V1.0 2026-08-25 DT 初始版本:WP-06 收尾交付(5 端 Provider 注入 + 登录页接入 + RN 版 Provider + 5 设计决策 + 3 阻塞记录)

本日志为 WP-06 收尾最终交付。后续 P1 WP-04/05/07 启动需后端接口就绪 + 用户明确指令。