20 KiB
AIGC
| AIGC | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
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白名单否则红(防无声失联) - 模型对拍:
ModelMappings13 个 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 包生成
理解项目的阅读顺序:
- 本文档第三节(做了什么、踩过什么坑)
- contract/sign-protocol.md——签名协议(含 basePath 剥离条款,P1 缺陷的教训沉淀)
- ContractSnapshotTests.cs——双端同步机制(日常维护最主要触点)
- samples/Yh.Platform.Sample.Web/Program.cs + appsettings.json——SDK 用法样例
- 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 全绿自动发布链路打通 |
| 真实环境实调 | ❌ 待 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生成