Files
yunhai.netcore.tool/yh-platform-dotnet建设方案v4.md
wanghu 8896b29c4d
ci / build-test (push) Successful in 3m0s
ci / publish (push) Successful in 14m30s
feat: SchTree 客户端对接 SchGroup/GetTeaTree,升版 1.0.0-preview.2
- 补齐 4226b04 未入库的 3 个新文件(TeaTreeVo/SchClassSchTreeClient/契约测试),修复该提交编译不过
- 版本升至 1.0.0-preview.2:Nexus nuget-hosted 禁止重传(ALLOW_ONCE),同版本不递增会被查重跳过
2026-09-08 16:36:33 +08:00

20 KiB
Raw Permalink Blame History

AIGC
AIGC
ContentProducer ContentPropagator Label ProduceID PropagateID ReservedCode1 ReservedCode2
001191110102MAD55U9H0F10002 001191110102MAD55U9H0F10002 1 0c3965b2-86bf-44ef-9ca7-c1be5394b48e 0c3965b2-86bf-44ef-9ca7-c1be5394b48e 841092c7-f9f4-4d86-b50c-f4b1e0e9974a 841092c7-f9f4-4d86-b50c-f4b1e0e9974a

yh-platform-dotnet 建设方案 v4 —— 移交文档(已做 / 未做全记录)

版本:v4.0 | 日期:2026-08-28 | 状态:移交文档(接手人从此文档开始) 定位:平台侧工件建设——与 Java yh-platform 同构的 .NET SDK(yh-platform-net,代码仓库名 BasePlatformDotnet),使平台基础能力(用户信息、/oapi 开放接口、应用签名、CAS SSO)具备跨语言辐射能力 v4 增量:建设阶段基本完成(四包 98 测试全绿、契约快照对拍机制建成、pack 产物验证通过)。本文档为移交版:完整记录已完成工作、待办事项、关键机制与快速上手路径 前置阅读:v1(架构决策)→ v2(工程落地)→ v3(自检与审计),见 yh-platform-dotnet建设方案.md


一、项目一句话说明

Java 侧有 yh-platform(Maven 聚合 SDK:取用户、调 /oapi 开放接口、应用签名、CAS SSO)。.NET 侧项目(如 GrowthHealth,由 dotnet core 迁入、部署于 projman 平台)原先用无源码的本地 BschOpenApi.dll 调旧接口。本项目产出 yh-platform-net 四个 NuGet 包,与 Java SDK 依赖同一份契约(UserBase /oapi + HMAC 签名协议),让 .NET 项目以正规 NuGet 依赖方式获得同等能力。

核心原则:不要让 .NET 去"依赖 Java",而是让两个 SDK 依赖同一份契约。

二、仓库与关联工程地图

路径 角色 状态
d:\SoftProject\yunhai\BasePlatformDotnet\ 本 SDK 仓库(本移交的主对象) 建设完成,待发布
d:\SoftProject\yunhai\BasePlatform\ Java 侧 yh-platform(yh-utils-core / yh-openapi-client / yh-app-auth-starter / yh-auth-starter) 零改动(契约对齐的参照系)
d:\SoftProject\yunhai\UserBase\ 用户中心服务(/oapi 契约权威源) 本项目改了 2 个文件(见 3.3)
d:\SoftProject\yunhai\GrowthHealth\ 未来首个 .NET 消费方(net5.0 + Newtonsoft + BschOpenApi.dll) 未接入(SDK 未发布的依赖项)

BasePlatformDotnet 仓库结构(实物核对 2026-08-28)

BasePlatformDotnet/
├── Directory.Build.props               # 共享编译属性:双目标 net5.0;net8.0、LangVersion、NuGet 元数据、YhPlatformNetVersion
├── Directory.Packages.props            # CPM 集中包版本(= Java BOM 等价物)
├── yh-platform-net.sln
├── .gitea/workflows/ci.yml             # CI:build → test → pack → push(仓库根,仅 main push 触发发布)
├── src/
│   ├── Yh.Platform.Abstractions/       # 模型与信封(对应 yh-utils-core)
│   │   ├── BoaUser.cs / BoaRoleInfo.cs / BoaGroup.cs / BoaModule.cs / BoaApp.cs / BoaAppType.cs
│   │   ├── ApiEnvelope.cs / DException.cs / ErrorCode.cs / IErrorCode.cs / ClientEnum.cs
│   ├── Yh.Platform.AppAuth/            # HMAC 签名 + 防重放(对应 yh-app-auth-starter)
│   │   ├── SignUtil.cs / SignCanonical.cs / AppSignHandler.cs
│   │   ├── SignKeyResolver.cs / SignKeyFallback.cs / ReplayGuard.cs / SignAppKeyPayload.cs
│   │   └── AppAuthOptions.cs / AppAuthServiceCollectionExtensions.cs
│   ├── Yh.Platform.OpenApi/            # /oapi 客户端(对应 yh-openapi-client)
│   │   ├── YschBaseOpenApi.cs          # 静态门面(对齐 Java 同名)
│   │   ├── BaseOpenApi.cs              # 实例形态 (baseUrl, appKey, appSecret)——复刻旧 BschOpenApi 构造习惯
│   │   ├── IOpenApiUserClient.cs / OpenApiUserClient.cs     # 用户域 + 身份域
│   │   ├── IOpenApiGroupClient.cs / OpenApiGroupClient.cs   # 组域
│   │   ├── OpenApiClientBase.cs        # 信封感知解析单点(unwrap 语义)
│   │   ├── SysUserRequests.cs / SysGroupRequests.cs         # 写操作请求模型
│   │   └── OpenApiOptions.cs / OpenApiServiceCollectionExtensions.cs / OpenApiErrorCode.cs / OpenApiException.cs
│   └── Yh.Platform.Auth/               # CAS SSO + 用户上下文(对应 yh-auth-starter)
│       ├── CasAuthenticationHandler.cs / CasServiceTicketValidator.cs
│       ├── BoaUserContext.cs / UserContextMiddleware.cs / UserDetailsProvider.cs
│       └── YhAuthOptions.cs / AuthServiceCollectionExtensions.cs
├── tests/                              # 4 个测试工程,共 98 个测试
│   ├── Yh.Platform.Abstractions.Tests/   # 8(模型序列化双序列化器对拍)
│   ├── Yh.Platform.AppAuth.Tests/        # 42(签名向量对拍、basePath 剥离、防重放、密钥链)
│   ├── Yh.Platform.OpenApi.Tests/        # 37(信封解析、客户端行为、契约快照对拍 15 个)
│   └── Yh.Platform.Auth.Tests/           # 11(CAS handler、上下文生命周期)
├── samples/Yh.Platform.Sample.Web/     # net8.0 示例应用(平台验收载体,配置样例在 appsettings.json)
├── contract/                           # 契约工件(单一事实源派生物)
│   ├── openapi-oapi.exported.json      # ★ 权威契约快照(UserBase springdoc 真实导出,对拍基线)
│   ├── openapi-oapi.yaml               # 手写契约(已与快照对齐,供人读)
│   ├── sign-protocol.md                # 签名协议规范(含 basePath 剥离条款)
│   └── fixtures/
│       ├── sign-vectors.json           # 共享签名测试向量(含 fullUrl/basePath 字段)
│       └── envelope-success.json / envelope-error.json   # 信封解析 fixture
└── README.md

三、已完成的工作(按时间序,全部可查证)

3.1 建设主体(P0~P6 全部完成)

阶段 内容 完成证据
P0 框架裁决 多目标 net5.0;net8.0(net5 覆盖 GrowthHealth 旧栈,net8 覆盖新项目);SDK 8.0 工具链构建;模型纯 POCO 零注解(Newtonsoft 大小写不敏感天然兼容 camelCase) Directory.Build.props
P1 契约提纯 sign-protocol.md(签名协议规范)+ openapi-oapi.yaml + sign-vectors.json(Java SignUtil 实际运行产出基线)+ envelope fixtures contract/ 目录
P2 脚手架 四包 + 四测试工程 + sample + CPM + CI 仓库根
P3 Abstractions 全模型(BoaUser 含 CurrentRole:BoaRoleInfo 多角色嵌套)+ 信封 + DException + 错误码 8 测试绿
P4 AppAuth SignUtil(HMAC-SHA256)+ AppSignHandler(DelegatingHandler 三头)+ SignKeyResolver(Redis→Memory→UserBase 兜底)+ ReplayGuard(5min 窗口) 42 测试绿
P5 OpenApi 用户/组/应用三域 client + 身份域(current/available/switch)+ 双形态门面 + AddYhOpenApi() 37 测试绿
P6 Auth CasAuthenticationHandler(自研,替换旧 dll)+ BoaUserContext(AsyncLocal)+ AddYhAuth() 11 测试绿

当前基线(2026-08-28 实测):双目标构建零 warning,测试 98/98 绿。

3.2 验收与缺陷修复记录(重要:都是真实踩过的坑,接手人必读)

第一轮验收(测试 67 绿时)发现:

级别 缺陷 修复
P1 阻断 AppSignHandler 签名未剥离 basePath:C# 用 uri.AbsolutePath(含 /userbase 前缀)签出 /userbase/oapi/...,而 Java 客户端(AppSignInterceptor.stripBasePath)与服务端(AppAuthFilter 剥 contextPath)口径都是 /oapi/...——UserBase 挂 context-path 时 C# 签名必被拒 SignCanonical.StripBasePath(逐行对齐 Java,含尾斜杠归一化/前缀不完整匹配不剥离边界);AppSignHandler 增加 basePath 参数并接线 OpenApiOptions.BasePath;sign-vectors.json 六个请求级向量重算;新增 6 个逐字节对拍测试。回归 83 绿
P2 DException.Data 遮蔽 Exception.Data(CS0114 警告,违反零 warning 门禁) 改名 EnvelopeData
P3 观察项 path + 解码差异:Java 拦截器 URLDecoder(+→空格)vs C# AbsolutePath(保留 +)——仅当路径参数含 + 时有影响 未修,列入待办(低概率场景)

第二轮(契约快照对拍上线)发现 4 个 SDK 真漂移,全部已修:

# 缺陷 危害 修复
1 SysUserRequest.Roles 序列化键 roles,服务端字段 roleIds 新增/编辑用户带角色时服务端静默丢弃(实现没错,是契约认知错误——67 个测试全绿也没发现,因为它只能靠权威快照暴露) 改名 RoleIds
2 SysUserRequest 缺 SchoolId 无法传参 补字段
3 SysGroupRequest 缺 Id 契约键不完整 补字段
4 缺 /oapi/identity/available 端点 多角色"可切换身份列表"能力缺失 新增 GetAvailableIdentitiesAsync

教训(写给接手人):手写翻译代码无法发现契约认知错误,只有权威快照对拍能。这就是 3.4 节机制存在的理由,请务必维持它。

3.3 UserBase 侧改动(共 2 个文件,已编译验证)

文件 改动
SwaggerConfig.java 新增两个 GroupedOpenApi:oapi 组(pathsToMatch /oapi/** + pathsToExclude /oapi/internal/**,契约权威源导出入口)+ api 组(/api/** 业务接口隔离)
application.yml 新增 springdoc 开关(默认开;生产用环境变量 SPRINGDOC_API_DOCS_ENABLED=false + SPRINGDOC_SWAGGER_UI_ENABLED=false 关闭)

部署注意:UserBase 生产环境部署时记得设置上述两个环境变量,否则文档端点外泄。

3.4 契约快照对拍机制(本项目最重要的长效机制,移交后必须维护)

双端同步流程:

UserBase 改 /oapi 契约
  → 起服务 GET /userbase/v3/api-docs/oapi
  → 覆盖保存 BasePlatformDotnet/contract/openapi-oapi.exported.json(入库)
  → cd BasePlatformDotnet && dotnet test
  → ContractSnapshotTests 红了 = 契约漂移
  → 二选一:修 SDK(同步四包实现) / 在测试的 acknowledged 白名单显式登记(有意不跟)

机制构成(ContractSnapshotTests.cs,15 个用例):

  • 端点对拍:SdkRoutes 路由表 ⊆ 快照(UserBase 删/改端点立刻红);快照多出的端点须显式登记 acknowledged 白名单否则红(防无声失联)
  • 模型对拍:ModelMappings 13 个 POCO 反射取属性集(camelCase 化)⇔ 快照 schema properties 键集,双向差额为零
  • 维护纪律:SDK 加端点 → 必须同步登记 SdkRoutes;加模型 → 必须登记 ModelMappings;UserBase 加端点 → 登记白名单并排期跟进

3.5 发布产物验证(pack 已跑通,未推送)

2026-08-28 实测 dotnet pack -c Release:

  • 4 包生成:Yh.Platform.Abstractions/AppAuth/OpenApi/Auth.1.0.0.nupkg(版本统一随 YhPlatformNetVersion)
  • 包内 lib/net5.0 + lib/net8.0 双 dll 齐全
  • 依赖元数据按 TFM 正确分组:net5.0 → 内部包 1.0.0 + Microsoft.Extensions.* 5.0.0;net8.0 → 8.0.x。GrowthHealth(net5.0)引用时自动命中 net5.0 组,老项目零升级
  • 模型纯 POCO,与 GrowthHealth 的 Newtonsoft 13.0.1 无冲突(有双序列化器对拍测试锁定)

3.6 配置契约(消费方视角,sample 已验证写法)

{
  "Yh": {
    "OpenApi": {
      "BaseHost": "http://userbase-host:port",
      "BasePath": "/userbase",
      "AppKey": "100xxx",
      "AppSecret": "xxx"
    }
  }
}
  • BasePath:UserBase context-path,签名剥离与 URL 拼接都依赖它(P1 缺陷修复后成为关键配置,挂根则留空)
  • 聚合入口 AddYhPlatform()(等价 Java yh-spring-boot-starter),也可按包拆引 AddYhOpenApi() / AddYhAppAuth() / AddYhAuth()
  • 超时默认对齐 Java:connect 1500ms / read 5000ms

四、未完成的工作(移交待办,按优先级)

4.1 🔴 发布链(阻塞所有消费方)—— ✅ 已完成(2026-09-02 实测)

  • Nexus 建仓:nuget-hosted(Version policy: Release;Deployment policy: Disable redeploy)、nuget-group(聚合)、nuget.org-proxy 均已在 Nexus 预先建好。注意:Nexus 实际地址带端口 http://nexus.yunhaixingtong.com:8081(本方案文档 URL 原缺端口);写策略枚举值为 ALLOW_ONCE(= Disable redeploy,禁止覆盖),已通过 REST API 确认/修正
  • 版本号裁决:采用 1.0.0-preview.1(核对表 #6~#9/#12 尚未真实环境实调,先发预发布版;实调通过后再改 Directory.Build.props 定 1.0.0)
  • 推送(5 个包全部推送成功):
    • Yh.Platform.Abstractions / AppAuth / OpenApi / Auth / Yh.SchClass.OpenApi 均 1.0.0-preview.1
    • 认证方式:Nexus 的 NuGet 推送走 HTTP Basic(账号+密码),不校验 api-key(--api-key 传任意值即可)
    • --skip-duplicate 对 Nexus 无效(重复包直接返回 400),推送脚本改为先调 Nexus REST API 查重、已存在则跳过
  • 推送后验证:空项目 dotnet add package Yh.Platform.OpenApi --version 1.0.0-preview.1 通过 nuget-group 成功还原并编译;http 源需在 nuget.config 显式加 allowInsecureConnections="true"
  • CI push 固化:Gitea 仓库 Secrets 配置 NEXUS_URL / NEXUS_USER / NEXUS_PASS;工作流已迁到仓库根 .gitea/workflows/ci.yml(BasePlatformDotnet/.gitea/ 子目录不触发),推送逻辑抽到 .gitea/scripts/push-to-nexus.sh(动态生成带凭据 nuget.config + 查重跳过);CI 运行 #643 全绿(build-test + publish,Push to Nexus 输出 pushed=0 skipped=5 failed=0——5 包已存在被正确跳过)

后续发新版本(如 1.0.0):改 Directory.Build.props 的 YhPlatformNetVersion → 推送 main → CI 自动 pack + push;同版本因 Disable redeploy 不可覆盖,务必递增版本号。

4.2 🟠 真实环境实调(依赖 UserBase 测试环境 + appKey 注册)

  • 核对表 #6:用户查询三入口(byId/byLoginName/byPhone)实调,返回字段一致
  • 核对表 #7:getUserRoleList 多角色字段完整
  • 核对表 #8:getCurrentIdentity 与 UserBase 状态一致
  • 核对表 #9:switchIdentity 白名单通过/拒绝行为
  • 核对表 #12:CAS 登录全流程(sample 浏览器实走,需 CAS server 联通 + 回调域名注册)
  • 8.3 运行自检整组(超时生效、防重放真实拒绝、SignKeyResolver 兜底实调)
  • appKey 注册:消费方(含 GrowthHealth 既有 100476 或新申请)须在 UserBase 侧注册 appKey+secret;身份切换端点另有白名单(现仅 100001/100002)

4.3 🟡 文档与治理

  • P8 消费方接入指南(原方案承诺未做):appKey 注册流程、配置样例、从 BschOpenApi.dll 迁移对照表(new BaseOpenApi(host,appKey,appSecret) → 新 SDK 双形态对照、BschBaseOpenApi.BoaUser → Yh.Platform.Abstractions.BoaUser、本地 CurrentRole → 服务端 currentRole)、EF Core/基础镜像升级建议
  • 兼容矩阵登记:yh-platform Java 版本 ↔ yh-platform-net 版本 ↔ /oapi 契约快照日期 三方对应关系表(首次发布时建立)
  • Java 侧 CI 补一步:重算 sign-vectors.json 防漂移(原方案第六节设想,未实施;当前防线只在 C# 侧)

4.4 ⚪ 长期/低优先

  • P3 观察项:path 参数含 + 时的解码差异(Java +→空格 vs C# 保留)——出现实际案例再修
  • 未决裁决 #1:net5.0 目标线去留(若 GrowthHealth 升级 .NET 8,可砍掉简化 SDK)
  • 未决裁决 #5:Java 双签名规则(数据级/请求级)长期收敛——不阻塞,属 Java 侧治理
  • GrowthHealth 实际接入(SDK 发布 + 接入指南就绪后启动,属消费方项目不属于本项目)

五、接手人快速上手(30 分钟路径)

# 前置:.NET SDK 8.0+(dotnet --version)
cd d:\SoftProject\yunhai\BasePlatformDotnet
dotnet build        # 双目标编译,预期零 warning
dotnet test         # 预期 98/98 绿
dotnet pack -c Release -o artifacts   # 预期 4 包生成

理解项目的阅读顺序:

  1. 本文档第三节(做了什么、踩过什么坑)
  2. contract/sign-protocol.md——签名协议(含 basePath 剥离条款,P1 缺陷的教训沉淀)
  3. ContractSnapshotTests.cs——双端同步机制(日常维护最主要触点)
  4. samples/Yh.Platform.Sample.Web/Program.cs + appsettings.json——SDK 用法样例
  5. Java 参照系:BasePlatform/yh-app-auth-spring-boot-starter(签名口径)、UserBase/controller/oapi(契约源头)

日常最可能的变更场景与操作:

场景 操作
UserBase 改了 /oapi 契约 重新导出快照 → dotnet test → 按红项修 SDK 或登记白名单 → 升版本 → push
SDK 加新端点 Client 实现 + SdkRoutes 登记 + (新模型则)ModelMappings 登记 + 测试
发新版本 改 Directory.Build.props 的 YhPlatformNetVersion → pack → push(Disable redeploy,同版本不可覆盖)

六、验证状态总览

验证维度 状态
双目标编译(net5.0;net8.0) ✅ 零 warning
单元测试 ✅ 98/98(Abstractions 8 / AppAuth 42 / OpenApi 37 / Auth 11)
签名正确性 ✅ 向量逐字节对拍 + 独立 HMAC 实现抽检 6/6 + handler 全链路对拍(P1 修复后)
信封解析 ✅ 成功裸数据/code≠0 抛 DException(EnvelopeData 携带),fixture 锁定
模型契约 ✅ 13 POCO ⇔ 权威快照双向对拍
双序列化器 ✅ Newtonsoft(大小写不敏感)+ STJ Web 逐节点对拍
pack 产物 ✅ 5 包(含 Yh.SchClass.OpenApi)、双 TFM lib、依赖分组正确(本地验证)
Nexus 发布 ✅ 5 包 1.0.0-preview.1 已推送(2026-09-02);CI #643 全绿自动发布链路打通;1.0.0-preview.2 增补 Yh.SchClass.OpenApi SchTree 教师组树(2026-09-08)
真实环境实调 ❌ 待 UserBase 测试环境(待 4.2,核对表 15 项中 11 过 4 待)
消费方实际接入 ❌ 未开始(待 4.3 指南 + 4.1 发布)

七、历史文档索引

文档 内容 位置
方案 v1~v3(含全部演进过程、9.1 十五项核对表、9.5 未决裁决) yh-platform-dotnet建设方案.md 仓库根
签名协议规范 contract/sign-protocol.md SDK 仓库
项目 README README.md SDK 仓库

变更记录

日期 版本 说明
2026-08-28 v1.0 平台工件定位、方案选型、架构、影响面实证
2026-08-28 v2.0 消费方实证(GrowthHealth net5.0 + BschOpenApi 考古)、脚手架命令、四包设计、分阶段实施
2026-08-28 v3.0 自检清单(工程/契约/运行)+ 15 项审计核对表
2026-08-28 v4.0 移交版:已完成(P0~P6 + 两轮缺陷修复 + 快照对拍机制 + pack 验证)与待办(发布链/实调/指南/长期项)全记录,含接手人快速上手路径

AI生成