diff --git a/docs/superpowers/specs/2026-05-01-bitanswer-1to1-refactor-design.md b/docs/superpowers/specs/2026-05-01-bitanswer-1to1-refactor-design.md new file mode 100644 index 0000000..e267967 --- /dev/null +++ b/docs/superpowers/specs/2026-05-01-bitanswer-1to1-refactor-design.md @@ -0,0 +1,423 @@ +# BitAnswer 1:1 映射重构设计 + +> **状态**: 待审核 +> **日期**: 2026-05-01 +> **触发**: 架构审计发现 `AuthProvider` (8 方法) 与 BitAnswer C API (50+ 函数) 之间存在显著缺口 +> **策略**: 大幅重构为 BitAnswer 1:1 原语映射,按能力域拆分接口 + +--- + +## 1. 审计摘要 + +### 1.1 当前状态 + +``` +Java AuthProvider (8 methods) + └─ BitAnswerProvider / SelfHostedAuthProvider + └─ NativeBridge (JNI, 9 native methods) + └─ craft-core cdylib (Rust, 9 craft_* C ABI functions) + └─ ⚠️ 全部返回 ok_result(桩实现,未调用真实 BitAnswer API) + └─ [已废弃] .deprecated-cmake/ bitanswer_adapter(空实现) +``` + +### 1.2 覆盖度缺口 + +| BitAnswer API 分组 | 原生函数数 | 当前覆盖 | 缺失 | +|---|---|---|---| +| 认证与会话 | 10+ | 3 (activate/release/close) | LoginEx, LoginByToken, Revoke, RemoveSn, SessionControl | +| 激活与升级 | 6 | 1 (activate) | 离线升级三步骤 | +| **特征项** | **15+** | **1 (hasFeature)** | Read/Write/Query/Release/Encrypt/Decrypt/Convert/Sign/Batch | +| 配置项(Data) | 5 | **0** | Set/Get/Remove/Enum | +| 借出/归还 | 9 | **0** | CheckOut/In, Borrow | +| 信息查询 | 5+ | 1 (getLicenseInfo) | SessionInfo, ServerInfo, FeatureInfo | +| 工具类 | 8+ | **0** | SetRootPath, SetProxy, SetAttr, CustomInfo | + +--- + +## 2. 目标架构 + +### 2.1 接口分层(6 能力接口 + 1 入口) + +``` +CraftLicense (顶层入口) + - initialize(configJson) → LicenseSession + - getVersion() → String + - setRootPath(path) / setProxy(...) / setLocalServer(...) + +LicenseSession (会话句柄,实现 5 个能力接口) + ├─ LicenseLifecycle — 认证/激活/心跳/释放 + ├─ FeatureManagement — 特征项读写/加解密/占用释放 + ├─ DataItemStore — 配置项(Data Item)存储 + ├─ LicenseInfoQuery — 信息查询 + ├─ CheckoutManager — 浮动授权借出/归还 + ├─ LicenseUtility — 工具方法(SetAttr, CustomInfo, SessionState) + └─ close() / isClosed() / getNativeHandle() +``` + +### 2.2 能力接口详细定义 + +#### LicenseLifecycle +```java +public interface LicenseLifecycle { + LoginResult login(LoginRequest request); + LoginResult loginEx(LoginExRequest request); + LoginResult loginByToken(LoginByTokenRequest request); + LoginResult loginByPassword(LoginByPasswordRequest request); + ActivationResult activate(ActivationRequest request); + UpdateResult updateOnline(String url, String sn); + OfflineUpdateRequest getRequestInfo(String sn, BindingType type); + UpdateInfo getUpdateInfo(String url, String sn, String requestInfo); + ApplyResult applyUpdateInfo(String updateInfo); + HeartbeatResult heartbeat(); + ReleaseResult release(); + RevokeResult revoke(String sn); + void removeSn(String sn); + void sessionControl(String url, String sessionId, SessionCtlType type); +} +``` + +#### FeatureManagement +```java +public interface FeatureManagement { + int readFeature(int featureId); + void writeFeature(int featureId, int value); + int convertFeature(int featureId, int p1, int p2, int p3, int p4); + byte[] encryptFeature(int featureId, byte[] plainData); + byte[] decryptFeature(int featureId, byte[] cipherData); + int queryFeature(int featureId); + int queryFeatureEx(int featureId, QueryMode mode, int required, String scope); + Ticket queryFeatureEx2(String featureName, QueryMode mode, int required, String scope); + int releaseFeature(int featureId); + int releaseFeatureEx(int featureId, int consumed, String scope); + void releaseFeatureEx2(Ticket ticket, int consumed); + FeatureInfo getFeatureInfo(int featureId); + int getFeatureInfo2(String featureName, String scope); + FeatureInfoEx getFeatureInfoEx2(String featureName, String scope); + TicketInfo getTicketInfo(Ticket ticket, TicketInfoType type); + byte[] signFeature(int featureId, byte[] data); + FeatureInfo getFeatureInfoByIndex(int index); + // 批量操作 + BatchResult batchBegin(BatchMode mode); + BatchResult batchEnd(); +} +``` + +#### DataItemStore +```java +public interface DataItemStore { + void setDataItem(String name, byte[] value); + byte[] getDataItem(String name); + void removeDataItem(String name); + int getDataItemCount(); + String getDataItemName(int index); +} +``` + +#### LicenseInfoQuery +```java +public interface LicenseInfoQuery { + LicenseInfo getLicenseInfo(); + String getSessionInfo(SessionType type); + String getInfo(InfoType type); + String getServerInfo(String url, String sn, String scope, ServerInfoType type); +} +``` + +#### CheckoutManager +```java +public interface CheckoutManager { + void checkOut(String url, String scope, String featureList, int durationDays); + void checkOutSn(String url, int featureId, int durationDays); + void checkOutSnEx(String url, int featureId, String scope, int durationDays); + void checkOutFeatures(String url, int[] featureIds, int durationDays); + void checkIn(String url, int featureId); + void checkInEx(String url, int featureId, String scope); + String getBorrowRequest(String sn, int durationDays); + String getBorrowFeatureRequest(int durationDays, String scope); + void applyBorrowInfo(String borrowInfo); +} +``` + +#### LicenseUtility +```java +public interface LicenseUtility { + void setAttr(int type, byte[] value); + void setCustomInfo(int infoId, byte[] data); + void setSessionState(int state); + String getProductPath(); + int getLastError(); + String getErrorMessage(); + void testBitService(String url, String sn, int featureId); +} +``` + +--- + +## 3. 原生 Rust 层重构 + +### 3.1 目录结构 + +``` +native/craft-core/src/ +├── lib.rs # C ABI 入口,craft_* 函数声明 +├── ffi/ +│ ├── mod.rs +│ ├── bitanswer.rs # BitAnswer C API 的 Rust extern 声明 +│ └── bridge.rs # craft_* → Bit_* 的桥接实现 +├── session.rs # 会话管理,BIT_HANDLE 映射 +├── activate.rs # 对接 Bit_Login / Bit_UpdateOnline +├── license.rs # 对接 Bit_GetSessionInfo / Bit_GetInfo +├── feature.rs # 对接 Bit_ReadFeature / Bit_WriteFeature / Bit_QueryFeature 等 +├── data_item.rs # 对接 Bit_SetDataItem / Bit_GetDataItem 等 +├── checkout.rs # 对接 Bit_CheckOutSn / Bit_CheckIn 等 +├── heartbeat.rs # 对接 Bit_Heartbeat +├── security/ # 安全模块(保持不变) +│ ├── mod.rs +│ ├── anti_debug.rs +│ ├── dynamic_api.rs +│ ├── integrity.rs +│ ├── obfuscation.rs +│ └── string_encrypt.rs +└── error.rs # BIT_ERROR_CODES → LicenseError 映射 +``` + +### 3.2 JNI 映射表(NativeBridge 扩展) + +| Java 方法 | Rust craft_* | BitAnswer C API | +|---|---|---| +| `nativeInitialize(String)` | `craft_initialize` | 内部引导 | +| `nativeLogin(long, String, int)` | `craft_login` | `Bit_Login` | +| `nativeLoginEx(long, String, int, String, int)` | `craft_login_ex` | `Bit_LoginEx` | +| `nativeLoginByToken(long, String, String)` | `craft_login_by_token` | `Bit_LoginByToken` | +| `nativeLoginByPassword(long, ...)` | `craft_login_by_password` | `Bit_LoginByPassword` | +| `nativeLogout(long)` | `craft_logout` | `Bit_Logout` | +| `nativeActivate(long, String)` | `craft_activate` | `Bit_UpdateOnline` | +| `nativeGetRequestInfo(long, String, int)` | `craft_get_request_info` | `Bit_GetRequestInfo` | +| `nativeGetUpdateInfo(long, String, String, String)` | `craft_get_update_info` | `Bit_GetUpdateInfo` | +| `nativeApplyUpdateInfo(long, String)` | `craft_apply_update_info` | `Bit_ApplyUpdateInfo` | +| `nativeReadFeature(long, int)` | `craft_read_feature` | `Bit_ReadFeature` | +| `nativeWriteFeature(long, int, int)` | `craft_write_feature` | `Bit_WriteFeature` | +| `nativeConvertFeature(long, int, int, int, int, int)` | `craft_convert_feature` | `Bit_ConvertFeature` | +| `nativeEncryptFeature(long, int, byte[], int)` | `craft_encrypt_feature` | `Bit_EncryptFeature` | +| `nativeDecryptFeature(long, int, byte[], int)` | `craft_decrypt_feature` | `Bit_DecryptFeature` | +| `nativeQueryFeature(long, int)` | `craft_query_feature` | `Bit_QueryFeature` | +| `nativeQueryFeatureEx(long, int, int, int, String)` | `craft_query_feature_ex` | `Bit_QueryFeatureEx` | +| `nativeReleaseFeature(long, int)` | `craft_release_feature` | `Bit_ReleaseFeature` | +| `nativeSignFeature(long, int, byte[], int)` | `craft_sign_feature` | `Bit_SignFeature` | +| `nativeSetDataItem(long, String, byte[], int)` | `craft_set_data_item` | `Bit_SetDataItem` | +| `nativeGetDataItem(long, String)` | `craft_get_data_item` | `Bit_GetDataItem` | +| `nativeRemoveDataItem(long, String)` | `craft_remove_data_item` | `Bit_RemoveDataItem` | +| `nativeGetDataItemNum(long)` | `craft_get_data_item_num` | `Bit_GetDataItemNum` | +| `nativeGetDataItemName(long, int)` | `craft_get_data_item_name` | `Bit_GetDataItemName` | +| `nativeCheckLicense(long)` | `craft_check_license` | `Bit_GetSessionInfo` | +| `nativeGetLicenseInfo(long)` | `craft_get_license_info` | `Bit_GetInfo` | +| `nativeGetSessionInfo(long, int)` | `craft_get_session_info` | `Bit_GetSessionInfo` | +| `nativeGetServerInfo(long, String, String, String, int)` | `craft_get_server_info` | `Bit_GetServerInfo` | +| `nativeGetFeatureInfo(long, int)` | `craft_get_feature_info` | `Bit_GetFeatureInfo` | +| `nativeCheckOutSn(long, String, int, int)` | `craft_check_out_sn` | `Bit_CheckOutSn` | +| `nativeCheckOutFeatures(long, String, int[], int, int)` | `craft_check_out_features` | `Bit_CheckOutFeatures` | +| `nativeCheckIn(long, String, int)` | `craft_check_in` | `Bit_CheckIn` | +| `nativeHeartbeat(long)` | `craft_heartbeat` | `Bit_Heartbeat` | +| `nativeRevoke(long, String)` | `craft_revoke` | `Bit_Revoke` | +| `nativeRemoveSn(long, String)` | `craft_remove_sn` | `Bit_RemoveSn` | +| `nativeSetAttr(long, int, byte[])` | `craft_set_attr` | `Bit_SetAttr` | +| `nativeSetCustomInfo(long, int, byte[])` | `craft_set_custom_info` | `Bit_SetCustomInfo` | +| `nativeSetRootPath(long, String)` | `craft_set_root_path` | `Bit_SetRootPath` | +| `nativeSetProxy(...)` | `craft_set_proxy` | `Bit_SetProxy` | +| `nativeSetLocalServer(...)` | `craft_set_local_server` | `Bit_SetLocalServer` | +| `nativeGetProductPath(long)` | `craft_get_product_path` | `Bit_GetProductPath` | +| `nativeGetVersion()` | `craft_get_version` | `Bit_GetVersion` | +| `nativeGetLastError(long)` | `craft_get_last_error` | `Bit_GetLastError` | +| `nativeDestroy(long)` | `craft_destroy` | 资源释放 | + +### 3.3 句柄管理 + +```rust +// session.rs +struct SessionState { + bit_handle: BIT_HANDLE, // Bit_Login 返回的句柄 + config: AuthConfig, // 解析后的配置 + application_data: Vec, // 产品识别码(来自 AuthConfig) + logged_in: bool, +} + +static SESSIONS: Lazy>> = ...; +``` + +--- + +## 4. 数据流 + +### 4.1 完整调用链(浮动作业激活) + +``` +Java: CraftLicense.initialize(configJson) + └─> Rust: craft_initialize(config_json) + ├─ 解析 JSON → AuthConfig + ├─ 安全加固检查(anti_debug, integrity) + ├─ 创建 SessionState,分配 session_id + └─ 返回 session_id (i64) + +Java: session.activate(ActivationRequest { sn: "SN-XXXX" }) + └─> Rust: craft_activate(session_id, sn) + ├─ 从 AuthConfig 获取 bitanswer.url, loginMode + ├─ 调用 Bit_UpdateOnline(url, sn, &app_data) + └─ 返回 ActivationResult + +Java: session.login(LoginRequest { sn: "SN-XXXX", mode: AUTO }) + └─> Rust: craft_login(session_id, sn, mode) + ├─ 调用 Bit_Login(url, sn, &app_data, &bit_handle, BIT_MODE_AUTO) + ├─ 存储 bit_handle → SessionState + └─ 返回 LoginResult { handle } + +Java: session.readFeature(FACE_FEATURE_ID) + └─> Rust: craft_read_feature(session_id, feature_id) + ├─ 获取 bit_handle + ├─ 调用 Bit_ReadFeature(bit_handle, feature_id, &value) + └─ 返回 value (i32) + +Java: session.checkOutSn(url, FEATURE_ZERO, 30) + └─> Rust: craft_check_out_sn(session_id, url, feature_id, duration) + ├─ 调用 Bit_CheckOutSn(url, feature_id, &app_data, duration) + └─ 返回 CraftResult + +Java: session.release() + └─> Rust: craft_release(session_id) + ├─ 调用 Bit_Logout(bit_handle) + └─ 返回 CraftResult + +Java: session.close() + └─> Rust: craft_destroy(session_id) + ├─ 如果未 logout,调用 Bit_Logout + ├─ 移除 SessionState + └─ 释放内存 +``` + +### 4.2 错误码映射 + +Rust 侧将 200+ 个 `BIT_ERROR_CODES` 分组映射为 `LicenseError` 枚举: + +```rust +pub enum LicenseError { + Success, + NetworkError, + WrongHandle, + InvalidParameter, + ApplicationDataError, + LicenseExpired, + LicenseNotFound, + LicenseDisabled, + FeatureNotFound(i32), + FeatureExpired(i32), + FeatureTypeNotMatch(i32), + SnInvalid, + SnNotFound, + SnDisabled, + SnRevoked, + SnExpired, + CapacityExhausted, + ServerBusy, + ServerDown, + Revoked, + Timeout, + TokenError, + BorrowError, + Unknown(i32), // 兜底 +} +``` + +Java 侧使用 sealed interface 提供类型安全的结果: + +```java +public sealed interface LicenseResult { + boolean isSuccess(); + Optional error(); + String message(); +} +``` + +--- + +## 5. 配置模型适配 + +`AuthConfig` 已有正确结构,重构后需将字段驱动到运行时: + +| AuthConfig 字段 | 驱动行为 | +|---|---| +| `bitanswer.url` | `Bit_Login` / `Bit_UpdateOnline` 的 `szURL` | +| `bitanswer.loginMode` | `Bit_Login` 的 `mode` 参数 | +| `bitanswer.rootPath` | `Bit_SetRootPath` | +| `bitanswer.applicationData` | 替代硬编码的 `application_data[]`,使不同产品可携带不同识别码 | +| `features[].bitanswerFeatureId` | `readFeature(featureId)` / `queryFeature(featureId)` | +| `features[].bitanswerFeatureName` | `queryFeatureEx2(featureName, ...)` | +| `floating.projectId` | floating 场景校验(schema 已有) | +| `school.edgeDeviceId` | school 场景标识 | + +--- + +## 6. 迁移路径 + +### Phase 1 — 基础设施(不破坏现有接口) +- Rust: `ffi/bitanswer.rs` FFI 声明 + `bridge.rs` 桥接实现 +- Rust: 实现 `craft_activate`, `craft_check_license`, `craft_heartbeat` 的真实调用 +- Java: `AuthProvider` 标记 `@Deprecated`,内部委托给 `CraftLicense` +- Java: 新增 `CraftLicense` + `LicenseSession` + 5 个能力接口(空实现) +- 测试: 现有测试保持通过 + +### Phase 2 — 核心 API 扩展 +- Java: 实现 `LicenseLifecycle`(login, loginEx, revoke, removeSn) +- Java: 实现 `FeatureManagement`(read, write, query, encrypt 等) +- Rust: 实现对应的 `craft_*` 函数 +- 测试: 每个能力域独立集成测试 + +### Phase 3 — 高级功能 +- Java: 实现 `DataItemStore`(set/get/remove/enum) +- Java: 实现 `CheckoutManager`(checkOut/In 系列) +- Rust: 实现离线升级流程(GetRequestInfo → GetUpdateInfo → ApplyUpdateInfo) +- 文档: 更新 `bitanswer-client-api-overview.md` 映射表 + +### Phase 4 — 清理 +- 移除 `@Deprecated AuthProvider` +- 移除 `.deprecated-cmake/` 下的旧适配器代码 +- 全量回归测试 + +--- + +## 7. 兼容性 + +- `schemas/craftlabs-auth-config.schema.json` — **不变** +- 现有 `examples/config/*.json` — **不变** +- `AuthConfig` / `AuthConfigs` — **不变** +- `AuthProvider` — Phase 1-3 保持可用(`@Deprecated`),Phase 4 移除 +- `NativeBridge` — 现有 9 个方法保留,新增 30+ 方法 +- `craft-core` C ABI — 现有 9 个函数签名不变,新增 30+ 函数 + +--- + +## 8. 风险与缓解 + +| 风险 | 缓解 | +|------|------| +| Rust FFI 调用 BitAnswer .so/.dll 的链接问题 | Phase 1 先用 `libloading` 动态加载 BitAnswer 库,验证 ABI 兼容性 | +| 200+ 错误码映射不完整 | 只映射文档中明确列出的错误码,其余走 `Unknown(code)` | +| 多线程安全(BIT_HANDLE 是线程局部的) | `LicenseSession` 文档注明非线程安全,建议调用方池化或加锁 | +| native 库缺失时测试无法运行 | 单元测试 mock native 层,集成测试用 `@EnabledIfNativeLibraryPresent` | + +--- + +## 附录 A:审计发现记录 + +审计过程中发现的具体代码问题: + +1. **Rust `activate.rs:core_activate`** — 返回硬编码 `ok_result`,未调用任何 BitAnswer API +2. **Rust `license.rs:check_license`** — 返回硬编码 `ok_result` +3. **Rust `license.rs:get_license_info`** — 返回固定数据(`is_licensed=1`, `expiration_date="2099-12-31"`, `feature_count=0`) +4. **Rust `license.rs:has_feature`** — 无条件返回 `true` +5. **Rust `heartbeat.rs:do_heartbeat`** — 返回硬编码 `ok_result` +6. **Java `BitAnswerProvider.initialize`** — 配置 JSON 未被传递给 native 层做运行时行为驱动 +7. **`NativeBridge`** — 只有 9 个 JNI 方法,BitAnswer 有 50+ 函数 +8. **`.deprecated-cmake/bitanswer_adapter.cpp`** — `bitanswer_adapter_register()` 是空函数 + +## 附录 B:BitAnswer C API 完整清单 + +见 `examples/vcsample/bitanswer.h`(1211 行),包含 50+ 个 `Bit_*` 函数声明和 200+ 个错误码枚举。