This commit is contained in:
wanghu
2026-09-02 09:38:45 +08:00
commit 8774b6cc44
128 changed files with 9382 additions and 0 deletions
+257
View File
@@ -0,0 +1,257 @@
# 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](file:///d:/SoftProject/yunhai/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
├── 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](file:///d:/SoftProject/yunhai/BasePlatformDotnet/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](file:///d:/SoftProject/yunhai/UserBase/service/src/main/java/com/yunhai/userbase/config/SwaggerConfig.java) | 新增两个 `GroupedOpenApi`:**oapi 组**(pathsToMatch `/oapi/**` + pathsToExclude `/oapi/internal/**`,契约权威源导出入口)+ api 组(`/api/**` 业务接口隔离) |
| [application.yml](file:///d:/SoftProject/yunhai/UserBase/service/src/main/resources/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](file:///d:/SoftProject/yunhai/BasePlatformDotnet/tests/Yh.Platform.OpenApi.Tests/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 已验证写法)
```json
{
"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 🔴 发布链(阻塞所有消费方,预计半天)
- [ ] **Nexus 建仓**(需管理员):登录 `nexus.yunhaixingtong.com` → Repositories → Create → `nuget-hosted`(Version policy: Release;Deployment policy: **Disable redeploy**)→ 拿 API Key。可选:`nuget-proxy`(代理 nuget.org)+ `nuget-public`(group)
- [ ] **版本号裁决**:当前 `YhPlatformNetVersion=1.0.0`。因 9.1 核对表 #6~#9/#12 尚未真实环境实调,**建议先发 `1.0.0-preview.1`**(改 Directory.Build.props 后重 pack),实调通过再定 1.0.0;或直接 1.0.0 接受风险
- [ ] **推送**:
```powershell
dotnet nuget add source "http://nexus.yunhaixingtong.com/repository/nuget-hosted/index.json" `
--name nexus-nuget --username <账号> --password <密码> --store-password-in-clear-text
dotnet nuget push "d:\SoftProject\yunhai\BasePlatformDotnet\artifacts\*.nupkg" `
--source nexus-nuget --api-key <APIKey> --skip-duplicate
```
- [ ] **推送后验证**:空项目 `dotnet add package Yh.Platform.OpenApi` 能还原;注意 http 源在新 SDK 需 `nuget.config` 加 `allowInsecureConnections="true"`
- [ ] **CI push 固化**:Gitea 仓库 Secrets 加 `NEXUS_NUGET_APIKEY`/`NEXUS_NUGET_URL`,ci.yml 的 pack 步骤后追加 push 步骤(仅 main 分支)
### 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 分钟路径)
```powershell
# 前置:.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](file:///d:/SoftProject/yunhai/BasePlatformDotnet/contract/sign-protocol.md)——签名协议(含 basePath 剥离条款,P1 缺陷的教训沉淀)
3. [ContractSnapshotTests.cs](file:///d:/SoftProject/yunhai/BasePlatformDotnet/tests/Yh.Platform.OpenApi.Tests/ContractSnapshotTests.cs)——双端同步机制(日常维护最主要触点)
4. [samples/Yh.Platform.Sample.Web/Program.cs](file:///d:/SoftProject/yunhai/BasePlatformDotnet/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 产物 | ✅ 4 包、双 TFM lib、依赖分组正确(本地验证) |
| Nexus 发布 | ❌ 未推送(待 4.1) |
| 真实环境实调 | ❌ 待 UserBase 测试环境(待 4.2,核对表 15 项中 11 过 4 待) |
| 消费方实际接入 | ❌ 未开始(待 4.3 指南 + 4.1 发布) |
## 七、历史文档索引
| 文档 | 内容 | 位置 |
|---|---|---|
| 方案 v1~v3(含全部演进过程、9.1 十五项核对表、9.5 未决裁决) | [yh-platform-dotnet建设方案.md](file:///d:/SoftProject/yunhai/yh-platform-dotnet建设方案.md) | 仓库根 |
| 签名协议规范 | contract/sign-protocol.md | SDK 仓库 |
| 项目 README | [README.md](file:///d:/SoftProject/yunhai/BasePlatformDotnet/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 验证)与待办(发布链/实调/指南/长期项)全记录,含接手人快速上手路径 |