本教程创建 Acme.ServiceDesk:多租户、Aspire 编排、SqlSugar、无可选平台模块和行业扩展。结果包含 API、ServiceDefaults、AppHost、Business Starter Contracts/Application、Unit Tests、Architecture Tests 及组合证据。
[!NOTE] 新工程优先考虑 bitz 脚手架:
bitz new提供场景预设向导、正交维度组合、SqlSugar/EF Core 两个持久化 ORM 适配器,并支持bitz add/remove module增删模块。本页保留dotnet new bitzorcas-host模板矩阵教程;两通道以同一已验证消费者工程为契约基线,并存期的退役条件登记在0004-template-upgrade-map.json。
选择中性 Profile 是为了先跑通基础闭包。Authorization、MasterData 与行业变体在同一基础拓扑上增加专属包、配置和测试,应在基础流程通过后单独采用。
最终拓扑
AppHost 等待数据库可用,再等待 schema-migrator 成功退出,最后启动 API。Schema 失败时 API 不应继续启动。
1. 核对环境与 Feed
dotnet --versiondotnet nuget list sourcetest -n "$BITZORCAS_COMMERCIAL_FEED_URL"SDK 应符合仓库 global.json 的 10.0.302 / latestFeature 策略。Feed 源键使用 BitzOrcasCommercial,凭据由 Credential Provider 或 CI Secret 提供。完整配置见环境准备。
2. 安装并确认模板合同
dotnet new install BitzOrcas.Modern.Templates@1.0.0-alpha1dotnet new bitzorcas-host --helpHelp 应公开 ProfileChoice 和 RuntimeAdapter,并列出 default-business-multi 与 sqlsugar。发现额外业务选择器时先确认安装版本,不要继续按旧命令生成。
3. 在空目录生成
# 一次确定 Multi Business、Aspire 拓扑与 SqlSugar Provider。dotnet new bitzorcas-host \ --name Acme.ServiceDesk \ --output ./Acme.ServiceDesk \ --ProfileChoice default-business-multi \ --RuntimeAdapter sqlsugar
cd Acme.ServiceDesk命令完成即代表原生模板文件已生成;没有额外物化阶段。客户输出不应包含 tools/、Python 文件或底座 Framework/Platform 源码。
下面终端窗是第 2–4 步的真实运行效果(安装确认、生成与 manifest/plan 核对):
4. 核对最终组合
# 第一组字段证明调用者选择被正确解析。# 后半组字段证明可选扩展和物理项目闭包。jq '{ profileChoice, profileId, runtimeAdapter, tenancy, deployment, platformModule, industryExtension, hosts, projects}' composition-manifest.json本案例应满足:
| 字段 | 期望值 |
|---|---|
profileChoice | default-business-multi |
profileId | default-business |
runtimeAdapter | sqlsugar |
tenancy | multi |
deployment | aspire |
platformModule / industryExtension | none / none |
hosts | api、service-defaults、apphost |
继续检查计划与文件:
# plan 证明选择了哪些项目与入口。jq '{selectedProjects, selectedEndpoints, selectedJobs}' \ composition-plan.json# 物理断言同时证明 Starter、AppHost 存在且维护工具未泄漏。test -f src/Modules/Business/Starter/Acme.ServiceDesk.Modules.Business.Starter.Contracts/WorkItem.cstest -f src/Hosts/Acme.ServiceDesk.AppHost/Program.cstest ! -d tools5. Restore 商业包
dotnet restore Acme.ServiceDesk.slnxfind . -name packages.lock.json -print首次 Restore 生成锁文件。审阅包 ID、版本与来源后提交。恢复失败时按稳定边界处理:
BITZFEED001:Feed URL 未注入;401/403或认证型NU1301:凭据缺失、过期或被撤销;NU1101/NU1102:entitlement 或版本通道不覆盖;NU3000:签名或证书策略失败;- locked mode:请求闭包与已审阅锁文件不同。
禁止把 Token 追加到 Feed URL、命令参数或诊断日志。
6. Build 与 Test
# Release Build 只消费已恢复的锁定闭包。dotnet build Acme.ServiceDesk.slnx \ --configuration Release \ --no-restore
# 全 Solution 测试包括 Unit 与 Architecture 门禁。dotnet test Acme.ServiceDesk.slnx \ --configuration Release \ --no-build \ --no-restore生成测试至少覆盖:
- Profile、License feature 与选中 ORM 的闭包;
- Starter Aggregate 的标题和租户不变量;
- Schema 采用;
- Framework/Platform 源码不得复制到 Consumer;
- Application 不得引用 ORM 或 Host;
- Handler、Endpoint、Aggregate 与 Owner 边界保持本地闭环。
测试通过证明生成基线有效,不证明后续业务规则、权限和生产配置已经完成。
7. 配置 AppHost 参数
AppHost 将两个参数注入常驻 API:
# WorkerId 是同一 DataCenter 内的实例级唯一值。dotnet user-secrets \ --project src/Hosts/Acme.ServiceDesk.AppHost \ set "Parameters:persistence-worker-id" "1"
# DataCenterId 是部署级分配值,不能按实例随机生成。dotnet user-secrets \ --project src/Hosts/Acme.ServiceDesk.AppHost \ set "Parameters:persistence-data-center-id" "0"WorkerId 范围 1..31,同一 DataCenter 内每个并发实例必须唯一;DataCenterId 范围 0..31,按部署分配。缺失或越界会以 Host.Persistence.Identity.Invalid 在启动期失败。
Development 配置含本地 JWT 示例值。Production 必须通过 Secret Store 提供 Authentication:Jwt:Issuer、Audience 和至少 32 UTF-8 bytes 的 SigningKey。
8. 运行 Aspire
dotnet run --project \ src/Hosts/Acme.ServiceDesk.AppHost/Acme.ServiceDesk.AppHost.csproj在 Aspire Dashboard 核对顺序:
sql与PrimaryDatabase就绪;schema-migrator以--migrate-schema apply成功退出;api随后启动;- API 收到
ConnectionStrings:PrimaryDatabase与两个 Persistence 参数。
AppHost 负责开发编排,不改变 API 的配置合同。生产部署仍需显式提供相同配置键并执行受控 Schema Runbook。
9. 验证健康端点与 OpenAPI
从 Dashboard 取得 API 地址:
curl --fail http://localhost:<api-port>/health/livecurl --fail http://localhost:<api-port>/health/readycurl --fail http://localhost:<api-port>/openapi/v1.json端点含义:
| 路径 | 当前含义 |
|---|---|
/health/live | 进程活性 |
/health/ready | 基础运行依赖,不因 License 缺失单独失败 |
/health/license | Runtime License readiness |
/scalar/v1 | Development 默认的交互文档 |
模板只注册 JWT Bearer,没有内置登录页。Scalar 调试需要从客户自己的认证入口取得访问令牌。
10. 检查 Business Starter
Starter 不是空占位。它包含 WorkItem 租户聚合、CreateWorkItemCommand、Handler、生成式 Endpoint、统一仓储与稳定错误:
rg -n 'WorkItem|CreateWorkItem|BusinessStarterErrors|GenerateEndpoint' \ src/Modules/Business/StarterWorkItem 同时是领域与持久化事实模型;元数据驱动 ORM 生成,不需要另建 Entity、Mapper、DataPort 或 Infrastructure 项目。客户应以该切片为结构参考,替换示例业务语义,而不是在 Host 中堆业务逻辑。
11. Schema 运维
Multi AppHost 已在开发启动链中执行 apply。生产发布前仍需单独保留命令与退出码证据:
dotnet run --project \ src/Hosts/Acme.ServiceDesk.Api/Acme.ServiceDesk.Api.csproj \ -- --migrate-schema applySchema 模式只加载 ConnectionStrings:PrimaryDatabase,不要求 JWT、租户、License 或 ServiceDefaults。错误 action 返回 2,缺少数据库返回 3,执行异常返回 1。
12. 许可证与生产边界
Development 默认 Licensing:Runtime:Enabled=false。启用正式 Runtime License 时,必须配置 Product、Version、Environment、TenancyMode、Deployment identity、Cache path 与至少一个 TrustedPublicKey。缺项以 Host.Licensing.Configuration.Invalid 失败。
模板完成后还必须补齐:
- 真实业务聚合、权限目录和租户/data-scope 测试;
- 正式 JWT 签发与密钥轮换;
- Runtime License 获取、缓存、吊销和离线策略;
- Schema 备份、回滚、互斥与生产审批;
- 反向代理、OpenAPI 访问策略、监控与告警;
- 前端、部署制品和数据迁移。
完成标准
- manifest 与命令的 Profile/ORM 完全一致;
- 输出无维护脚本和底座源码副本;
- 商业包锁文件已审阅;
- Release Build、Unit Tests、Architecture Tests 通过;
- AppHost 严格按 Database → Migrator → API 启动;
- live、ready 与 license 健康含义没有混用;
- Starter 的统一聚合/仓储/Endpoint 路径可定位;
- Secret、Feed Token 和 License 私钥未进入仓库;
- 未完成的生产工作进入团队 Backlog 与 Runbook。