diff --git a/XP.Common/Documents/License.README.md b/XP.Common/Documents/License.README.md new file mode 100644 index 0000000..fec5ada --- /dev/null +++ b/XP.Common/Documents/License.README.md @@ -0,0 +1,184 @@ +# 授权服务使用指南 | License Service Usage Guide + +## 概述 | Overview + +XplorePlane 通过 `XP.Common.License` 命名空间下的 `ILicenseService` 提供统一的授权管理。底层基于海克斯康 CLMS(Computational License Management System)SDK,使用 `MORCODE.dll` 进行许可证校验。 + +## 产品授权信息 | Product License Information + +| 项目 | 值 | +| --- | --- | +| CLMS 模块 ID(Module ID) | `4` | +| 零件号(Part Number) | `LS950-0071-5-1` | + +`Module ID` 是 CLMS 在 SDK 中标识本产品的唯一编号,调用 `CLM_ModuleIsLicensed` 时必须传入此值;`Part Number` 是产品在 CLMS 许可发行系统中的物料编号,用于申请、签发、续期许可证。 + +## 授权模式 | License Modes + +`LicenseMode` 枚举定义见 `XP.Common.License.Enums.LicenseMode`: + +| 模式 | 枚举值 | 数值 | 说明 | +| --- | --- | --- | --- | +| CLMS 正式授权 | `Clms` | `0` | 通过 CLMS SDK 进行完整授权校验,包含登录、许可范围、模块、SMA、到期日期等检查 | +| 临时测试模式 | `TemporaryTest` | `885` | 不调用 CLMS SDK,直接放行 15 分钟,用于研发/测试场景 | + +## 配置项 | Configuration Items + +授权配置位于主应用 `App.config` 中,键前缀为 `License:`,对应 `XP.Common.License.Configs.LicenseConfig`: + +```xml + + + + + + + +``` + +| 键 | 类型 | 默认值 | 有效值 | 说明 | +| --- | --- | --- | --- | --- | +| `License:LicenseMode` | int | `0` | `0`、`885` | 授权模式 | +| `License:ModuleId` | ushort | `4` | `1` ~ `65535` | CLMS 模块 ID | +| `License:UseSma` | bool | `false` | `true`、`false` | 是否启用 SMA(软件维护协议)校验 | +| `License:LicenseState` | int | `20` | `10`、`20` | 上次运行时的授权结果,由服务自动维护 | + +> 配置中无效或缺失的键会回退到默认值,详见 `XP.Common.License.Configs.ConfigLoader`。 + +## 正式授权流程 | Formal Authorization Flow + +`LicenseMode = 0`(`Clms`)时,`LicenseService.CheckAuthorization()` 依次执行以下步骤: + +1. **系统时间检查**(可选):调用 `CLM_CheckSystemTime`,老版本 SDK 缺失此入口点时跳过。 +2. **登录验证**:调用 `CLM_Login`(核心步骤,必须存在)。 +3. **许可范围检查**:调用 `CLM_Login_Scope`(核心步骤)。 +4. **SMA 验证**(可选):仅当 `License:UseSma = true` 时执行。比较 SMA 年份/季度与当前软件主版本号/次版本号,季度不匹配会判定失败。 +5. **浮动许可信息**(可选):通过 `CLM_GetIP` 获取 IP 与端口。 +6. **错误信息读取**(可选):通过 `CLM_GetError` 拉取 SDK 端错误描述。 +7. **模块授权检查**:调用 `CLM_ModuleIsLicensed`,传入 `Module ID = 4`。 +8. **到期日期获取**:调用 `CLM_GetWarrantyExpiration`,剩余 ≤ 30 天时记录警告。 + +任意核心步骤失败即视为授权失败,写回 `License:LicenseState = 20`,主应用弹窗提示并退出。 + +## 临时测试模式 | Temporary Test Mode + +`LicenseMode = 885`(`TemporaryTest`)用于跳过 CLMS 校验,便于离线开发和功能演示。 + +### 行为 | Behavior + +- 不加载 `MORCODE.dll`,不调用任何 CLMS API,授权直接通过。 +- 启动后立刻开启 15 分钟(900 秒)倒计时,到期触发 `TestModeTimeout` 事件,主应用应执行优雅关闭。 +- 倒计时途中分别在剩余 5 分钟、1 分钟时触发 `TestModeWarning5Min`、`TestModeWarning1Min` 事件用于提醒。 +- 上述三个事件只在临时测试模式下被触发,正式授权(`Clms`)下不会创建计时器。 + +### 启用方式 | How to Enable + +修改主应用 `App.config`: + +```xml + +``` + +启动后将看到提示:「当前为临时测试模式,软件将在 15 分钟后自动关闭」。 + +> **注意**:临时测试模式仅用于研发与内部测试场景,**禁止用于生产或交付**。发布前请务必将 `License:LicenseMode` 还原为 `0`。 + +## 使用示例 | Usage Examples + +### 注入并校验 | Inject and Verify + +```csharp +using XP.Common.License.Interfaces; +using XP.Common.License.Enums; + +public class StartupChecker +{ + private readonly ILicenseService _licenseService; + private readonly ILoggerService _logger; + + public StartupChecker(ILicenseService licenseService, ILoggerService logger) + { + _licenseService = licenseService ?? throw new ArgumentNullException(nameof(licenseService)); + _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger)); + } + + public bool Run() + { + var result = _licenseService.CheckAuthorization(); + if (!result.IsAuthorized) + { + _logger.Error(null, "授权失败:{Message} | License failed: {Message}", result.Message); + return false; + } + + // 仅在临时测试模式下订阅倒计时事件 | Subscribe countdown events only in test mode + if (_licenseService.LicenseMode == LicenseMode.TemporaryTest) + { + _licenseService.TestModeWarning5Min += (s, e) => _logger.Warn("临时测试模式剩余 5 分钟"); + _licenseService.TestModeWarning1Min += (s, e) => _logger.Warn("临时测试模式剩余 1 分钟"); + _licenseService.TestModeTimeout += (s, e) => Application.Current.Shutdown(); + } + return true; + } +} +``` + +### 检查特定模块授权 | Check Module Authorization + +```csharp +const ushort XplorePlaneModuleId = 4; +if (!_licenseService.IsModuleLicensed(XplorePlaneModuleId)) +{ + _logger.Warn("模块 {ModuleId} 未授权 | Module {ModuleId} not licensed", XplorePlaneModuleId); +} +``` + +### 读取授权信息 | Read License Information + +```csharp +DateTime? expiration = _licenseService.GetExpirationDate(); // 授权到期日期 +DateTime? sma = _licenseService.GetSmaDate(); // SMA 到期日期 +int remaining = _licenseService.GetRemainingTestTime(); // 临时测试剩余秒数;非测试模式返回 -1 +``` + +## 接口与事件 | Interface & Events + +`ILicenseService` 提供以下成员(详见 `XP.Common.License.Interfaces.ILicenseService`): + +- `LicenseCheckResult CheckAuthorization()`:执行完整授权校验。 +- `bool IsAuthorized`:当前会话是否已授权。 +- `LicenseMode LicenseMode`:当前授权模式。 +- `DateTime? GetExpirationDate()`:授权到期日期。 +- `DateTime? GetSmaDate()`:SMA 到期日期。 +- `bool IsModuleLicensed(ushort moduleId)`:检查指定模块是否被授权。 +- `int GetRemainingTestTime()`:临时测试模式剩余秒数。 +- 事件:`TestModeWarning5Min`、`TestModeWarning1Min`、`TestModeTimeout`。 + +## 常见问题 | FAQ + +### 1. 启动时提示 `MORCODE.dll 加载失败` | `Failed to load MORCODE.dll` + +确认 `MORCODE.dll` 已随 `ReleaseFiles` 一同部署,并位于主程序同级目录。研发阶段可临时切换到临时测试模式绕过。 + +### 2. 提示「模块号码 4 不可用」| `Module 4 unavailable` + +CLMS 服务器签发的许可证未包含 `Module ID = 4`(零件号 `LS950-0071-5-1`)。请联系海克斯康许可团队确认许可范围。 + +### 3. SMA 校验失败 | SMA validation failed + +SMA 年份必须 ≥ 软件主版本号;同年时 SMA 季度必须 ≥ 软件次版本号。若需临时绕过,可将 `License:UseSma` 设为 `false`,但发布版本仍应保持 SMA 校验启用。 + +### 4. 授权成功后是否还有倒计时?| Will the countdown still trigger after a successful formal authorization? + +不会。倒计时仅在 `LicenseMode = TemporaryTest` 时启动,`Clms` 正式授权下三个事件永远不会被触发。 + +## 相关文件 | Related Files + +- `XP.Common/License/Interfaces/ILicenseService.cs` — 服务接口 +- `XP.Common/License/Implementations/LicenseService.cs` — 服务实现 +- `XP.Common/License/Configs/LicenseConfig.cs` — 配置实体 +- `XP.Common/License/Configs/ConfigLoader.cs` — 配置加载器 +- `XP.Common/License/Enums/LicenseMode.cs` — 授权模式枚举 +- `XP.Common/License/Enums/LicenseState.cs` — 授权状态枚举 +- `XP.Common/License/Native/NativeMethods.cs` — CLMS SDK P/Invoke 封装 +- `XplorePlane/App.xaml.cs` — 主应用启动时调用 `PerformLicenseCheck()` 的入口