Skip to content
bitzorcas
中EN

Concept

许可证管理

负责运行时许可证控制面,包括申请、四眼复核、持久化异步签名、下载与吊销。

Last updated

负责运行时许可证控制面,包括申请、四眼复核、持久化异步签名、下载与吊销。

一张图读懂主路径

创建不可变申请

由另一位操作员审批

排入幂等签名操作

Worker 取得租约并调用远程签名器

验证并发布签名封装

珊瑚色节点表示本模块的核心决策或状态边界。

能力边界

  • 不可变许可证申请事实与复核状态
  • 四眼审批与控制面租户边界
  • 持久化签名/吊销操作与签名封装下载

模块明确不包含:LicenseManagement 负责签发已签名封装;运行时执行归 Licensing 管线与产品组合。私钥必须留在 LicenseSigner、KMS 或 HSM,不能进入本模块数据库或 API 进程。

代码地图

项目主要职责
BitzOrcas.Platform.LicenseManagement.Contracts公开契约、端口、DTO 与事件
BitzOrcas.Platform.LicenseManagement.Application命令、查询、处理器与应用策略
BitzOrcas.Platform.LicenseManagement.Infrastructure持久化、连接器与框架适配器

源码位置: src/Platform/LicenseManagement

核心用例

  • CreateRuntimeLicenseCommand
  • ApproveRuntimeLicenseCommand
  • RejectRuntimeLicenseCommand
  • IssueRuntimeLicenseCommand
  • RevokeRuntimeLicenseCommand
  • DownloadRuntimeLicenseQuery
  • GetLicenseSignerReadinessQuery

阅读用例时,先看请求契约和权限声明,再跟进处理器调用的聚合或端口,最后检查提交后的事件、缓存和审计。

关键模型与契约

关键类型阅读重点
RuntimeLicenseIssuance控制面聚合
IRuntimeLicenseIssuanceCommandStore原子命令与租约端口
ILicenseEnvelopeSigner远程签名边界
ILicenseManagementControlPlaneTenantPolicy专用控制面租户守卫

用例边界示例

字段说明
代表性契约IssueRuntimeLicenseCommand
返回结果Result<RuntimeLicenseIssuanceDto>
审查重点先裁决控制面租户,再按幂等键把签发请求冻结进持久队列;HTTP 请求内不能直接持有私钥或同步签名。

示例伪代码

IssueRuntimeLicenseCommand 伪代码
// ① 幂等键缺失即拒绝;RequireActor 内部执行控制面租户策略(IsTrustedTenant)裁决。
if (string.IsNullOrWhiteSpace(command.IdempotencyKey)
|| command.IdempotencyKey.Length > 128)
return LicenseManagementErrors.IdempotencyKeyRequired;
var actor = LicenseManagementApplicationSupport.RequireActor(currentUser, tenantPolicy);
if (actor.IsFailure) return Result<RuntimeLicenseIssuanceDto>.Failure(actor.Error);
// ② 已 Issued 的聚合直接回读读模型,重复请求天然幂等。
var found = await store.FindAsync(command.LicenseId, cancellationToken);
if (found.IsFailure) return Result<RuntimeLicenseIssuanceDto>.Failure(found.Error);
var issuance = found.GetValueOrThrow();
if (issuance.Status == RuntimeLicenseIssuanceStatus.Issued)
return await readModels.GetAsync(issuance.LicenseId, cancellationToken);
// ③ QueueIssuance 冻结首次请求并入持久队列;重复同一活跃操作返回 false 再走读模型。
var queued = issuance.QueueIssuance(
actor.GetValueOrThrow(), clock.UtcNow, command.IdempotencyKey);
if (queued.IsFailure) return Result<RuntimeLicenseIssuanceDto>.Failure(queued.Error);
if (!queued.GetValueOrThrow())
return await readModels.GetAsync(issuance.LicenseId, cancellationToken);
// ④ SaveAsync 同时持久化聚合与内嵌签名操作;私钥只存在于 LicenseSigner 进程。
await store.SaveAsync(issuance, cancellationToken);
return await readModels.GetAsync(issuance.LicenseId, cancellationToken);

集成关系

API 只负责排入签名任务;RuntimeLicenseSigningWorker 领取有界租约并调用 ILicenseEnvelopeSigner。只有载荷未变且签名能由配置公钥验证时,远端结果才会被接受。

控制面状态机、LicenseSigner 网络边界、密钥托管和故障演练的完整操作说明见许可证签发;运行时验证与降级策略见运行时许可证。

跨模块协作遵循以下方向:

  • 调用方依赖本模块公开 Contracts 或窄端口。
  • 状态变化通过版本化集成事件传播。
  • 具体数据库、消息、缓存、文件或厂商 SDK 留在 Infrastructure。
  • 组合根先注册失败关闭默认实现,再接入生产适配器。

安全、租户与隐私

所有操作在访问 Store 前都必须通过 LicenseManagement:ControlPlane:TenantId 校验。申请人不能审批自己的申请;路由使用逐操作权限;下载只暴露签名封装与 SHA-256;签名器故障时失败关闭。

模块保存租户数据时必须写入 TenantId,列表和详情使用同一授权规则。日志、事件和审计只保留诊断所需字段。

失败语义、幂等与可观测性

签发与吊销使用带幂等键、租约、有界重试和终态失败的持久化操作。Worker 提交封装前,还会确认聚合与操作仍然匹配。

情况处理原则
校验、未找到、冲突或禁止返回类型化 Result/Error,并由统一映射生成 Problem Details
适配器或关键状态不可用安全或高价值操作失败关闭;只读降级必须显式标记
重复请求或重复事件使用稳定幂等键,数据库唯一约束作为最后防线
外部超时传播取消,记录脱敏诊断,仅对安全操作执行有界重试

测试矩阵

#必须覆盖建议层级
1控制面租户不匹配且不得访问 Store单元 + 集成
2申请人不能自批集成/契约
3签名器篡改载荷或返回无效签名集成/契约
4租约到期、重试、幂等与终态失败集成/契约

测试还要固定一条通用红线:租户 A 的标识、缓存键、事件或查询条件不能让租户 B 读取或修改数据。

源码导航与变更检查

在 BitzOrcasVNext 仓库根目录运行:

Terminal window
# 列出公开类型,确认新增契约是否真的属于模块边界。
rg -n "^public (sealed |abstract |static |partial )*(record|class|interface|enum)" src/Platform/LicenseManagement -g '*.cs'
# 检查跨层引用;调用方不应依赖另一个模块的 Infrastructure。
rg -n "ProjectReference|PackageReference" src/Platform/LicenseManagement -g '*.csproj'
# 查找待补实现和省略式代码;预期没有命中。
rg -n "TODO|FIXME|// \.\.\.|省略" src/Platform/LicenseManagement -g '*.cs'

扩展与发布检查

  1. 新能力是否由明确的 Contracts、权限和 Feature Key 表达?
  2. 持久化、连接器和 Provider 是否可以通过窄适配器替换?
  3. 生产组合是否仍命中 Null/Unavailable 默认实现?健康检查会不会误报绿色?
  4. 事务、事件、缓存失效、审计和后台任务是否有失败与重试证据?
  5. 中英文文档、图表、契约测试和运维手册是否随代码一起更新?

返回平台模块目录 · 查看模块依赖图 · 新增模块指南

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%