mirror of
https://github.com/hpd840321/craftlabs-authorization-sdk.git
synced 2026-06-09 18:10:30 +08:00
307a019d48
Phase 1 (infrastructure, no breaking changes): - Rust: error.rs, session.rs, ffi/bitanswer.rs, ffi/bridge.rs - Rust: refactor lib.rs to session-based handle management - Java: @Deprecate AuthProvider, create 6 capability interfaces - Java: LicenseSession skeleton, NativeBridge 30+ method stubs - Java: CraftLicense new entry point Phase 2-4: core API expansion, advanced features, cleanup
1602 lines
46 KiB
Markdown
1602 lines
46 KiB
Markdown
# BitAnswer 1:1 映射重构 实施计划
|
||
|
||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
||
**Goal:** 将 craftlabs-authorization-sdk 的 SDK 客户端层从 8 方法 AuthProvider 重构为 BitAnswer C API 的 1:1 原语映射,按 6 个能力接口分层,4 个 Phase 渐进交付。
|
||
|
||
**Architecture:** Java 侧分为 CraftLicense(入口)+ LicenseSession(实现 6 个能力接口:LicenseLifecycle、FeatureManagement、DataItemStore、LicenseInfoQuery、CheckoutManager、LicenseUtility)。Rust 侧新增 ffi/bitanswer.rs(BitAnswer C API FFI 声明)、ffi/bridge.rs(craft_* → Bit_* 桥接)、session.rs(句柄管理)、error.rs(错误码映射)。Phase 1 不破坏现有接口。
|
||
|
||
**Tech Stack:** Rust (cdylib, libloading), Java 17 (Maven multi-module), JNI, BitAnswer C SDK
|
||
|
||
**Spec reference:** `docs/superpowers/specs/2026-05-01-bitanswer-1to1-refactor-design.md`
|
||
|
||
---
|
||
|
||
## Phase 1 — 基础设施(不破坏现有接口)
|
||
|
||
### Task 1: Rust — 创建 error.rs(错误码映射)
|
||
|
||
**Files:**
|
||
- Create: `native/craft-core/src/error.rs`
|
||
|
||
- [ ] **Step 1: 创建 LicenseError 枚举**
|
||
|
||
写入 `native/craft-core/src/error.rs`:
|
||
|
||
```rust
|
||
use crate::CraftResult;
|
||
|
||
/// BitAnswer BIT_STATUS 错误码到结构化错误的精简映射。
|
||
/// 完整 200+ 码请参考 bitanswer.h,此处映射文档中明确列出的高频码。
|
||
#[derive(Debug, Clone, PartialEq)]
|
||
pub enum LicenseError {
|
||
Success,
|
||
NetworkError,
|
||
WrongHandle,
|
||
InvalidParameter,
|
||
ApplicationDataError,
|
||
LicenseExpired,
|
||
LicenseNotFound,
|
||
LicenseDisabled,
|
||
FeatureNotFound(u32),
|
||
FeatureExpired(u32),
|
||
FeatureTypeNotMatch(u32),
|
||
SnInvalid,
|
||
SnNotFound,
|
||
SnDisabled,
|
||
SnRevoked,
|
||
SnExpired,
|
||
CapacityExhausted,
|
||
ServerBusy,
|
||
ServerDown,
|
||
Timeout,
|
||
Unknown(u32),
|
||
}
|
||
|
||
impl LicenseError {
|
||
/// 将 BitAnswer BIT_STATUS (u32) 映射为 LicenseError
|
||
pub fn from_bit_status(status: u32) -> Self {
|
||
match status {
|
||
0x0000 => LicenseError::Success,
|
||
0x0101 => LicenseError::NetworkError,
|
||
0x0102 => LicenseError::WrongHandle,
|
||
0x0103 => LicenseError::InvalidParameter,
|
||
0x0105 => LicenseError::ApplicationDataError,
|
||
0x0701 => LicenseError::LicenseExpired,
|
||
0x0114 => LicenseError::LicenseNotFound,
|
||
0x0705 => LicenseError::LicenseDisabled,
|
||
0x0503 => LicenseError::FeatureNotFound(0),
|
||
0x0509 => LicenseError::FeatureExpired(0),
|
||
0x0504 => LicenseError::FeatureTypeNotMatch(0),
|
||
0x011C => LicenseError::SnInvalid,
|
||
0x0706 => LicenseError::SnNotFound,
|
||
0x0705 => LicenseError::SnDisabled,
|
||
0x0123 => LicenseError::SnRevoked,
|
||
0x0701 => LicenseError::SnExpired,
|
||
0x0712 => LicenseError::CapacityExhausted,
|
||
0x0107 => LicenseError::ServerBusy,
|
||
0x0108 => LicenseError::ServerDown,
|
||
0x0141 => LicenseError::Timeout,
|
||
_ => LicenseError::Unknown(status),
|
||
}
|
||
}
|
||
|
||
pub fn message(&self) -> &'static str {
|
||
match self {
|
||
LicenseError::Success => "ok",
|
||
LicenseError::NetworkError => "network error",
|
||
LicenseError::WrongHandle => "wrong handle",
|
||
LicenseError::InvalidParameter => "invalid parameter",
|
||
LicenseError::ApplicationDataError => "application data error",
|
||
LicenseError::LicenseExpired => "license expired",
|
||
LicenseError::LicenseNotFound => "license not found",
|
||
LicenseError::LicenseDisabled => "license disabled",
|
||
LicenseError::FeatureNotFound(_) => "feature not found",
|
||
LicenseError::FeatureExpired(_) => "feature expired",
|
||
LicenseError::FeatureTypeNotMatch(_) => "feature type not match",
|
||
LicenseError::SnInvalid => "sn invalid",
|
||
LicenseError::SnNotFound => "sn not found",
|
||
LicenseError::SnDisabled => "sn disabled",
|
||
LicenseError::SnRevoked => "sn revoked",
|
||
LicenseError::SnExpired => "sn expired",
|
||
LicenseError::CapacityExhausted => "capacity exhausted",
|
||
LicenseError::ServerBusy => "server busy",
|
||
LicenseError::ServerDown => "server down",
|
||
LicenseError::Timeout => "timeout",
|
||
LicenseError::Unknown(_) => "unknown error",
|
||
}
|
||
}
|
||
|
||
pub fn is_success(&self) -> bool {
|
||
matches!(self, LicenseError::Success)
|
||
}
|
||
}
|
||
|
||
/// 将 LicenseError 转为 C ABI 兼容的 CraftResult
|
||
pub fn to_craft_result(error: LicenseError) -> CraftResult {
|
||
if error.is_success() {
|
||
crate::ok_result()
|
||
} else {
|
||
// 使用堆分配的消息指针(调用方通过 craft_free_license_info 类似方式释放)
|
||
let msg = format!("{}\0", error.message());
|
||
let msg_ptr = msg.as_ptr() as *const std::os::raw::c_char;
|
||
std::mem::forget(msg); // 内存由调用方管理
|
||
CraftResult {
|
||
success: 0,
|
||
message: msg_ptr,
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 在 lib.rs 中注册 error 模块**
|
||
|
||
在 `native/craft-core/src/lib.rs` 的 `mod` 声明区域添加:
|
||
|
||
```rust
|
||
mod error;
|
||
```
|
||
|
||
- [ ] **Step 3: 编译验证**
|
||
|
||
```bash
|
||
cargo build --manifest-path native/craft-core/Cargo.toml
|
||
```
|
||
|
||
Expected: 编译成功,无错误。
|
||
|
||
- [ ] **Step 4: 提交**
|
||
|
||
```bash
|
||
git add native/craft-core/src/error.rs native/craft-core/src/lib.rs
|
||
git commit -m "feat(native): add LicenseError enum with BitAnswer error code mapping"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 2: Rust — 创建 session.rs(会话管理)
|
||
|
||
**Files:**
|
||
- Create: `native/craft-core/src/session.rs`
|
||
|
||
- [ ] **Step 1: 创建 SessionState 和全局会话表**
|
||
|
||
写入 `native/craft-core/src/session.rs`:
|
||
|
||
```rust
|
||
use std::collections::HashMap;
|
||
use std::sync::Mutex;
|
||
use once_cell::sync::Lazy;
|
||
|
||
/// 单个会话的运行时状态,映射 Java long (session_id) ↔ BitAnswer BIT_HANDLE
|
||
pub struct SessionState {
|
||
/// 存储 initialize 时传入的配置 JSON(用于后续 Bit_Login/UpdateOnline)
|
||
pub config_json: String,
|
||
/// Bit_Login / Bit_LoginEx 成功后返回的句柄
|
||
pub bit_handle: Option<usize>,
|
||
/// 产品识别码(来自 AuthConfig.bitanswer.applicationData)
|
||
pub application_data: Vec<u8>,
|
||
/// 是否已完成 login
|
||
pub logged_in: bool,
|
||
}
|
||
|
||
/// 全局会话表:session_id (i64) → SessionState
|
||
pub static SESSIONS: Lazy<Mutex<HashMap<i64, SessionState>>> =
|
||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||
|
||
static NEXT_SESSION_ID: Lazy<Mutex<i64>> = Lazy::new(|| Mutex::new(1));
|
||
|
||
/// 分配一个新的 session_id 并注册 SessionState
|
||
pub fn register_session(config_json: String, application_data: Vec<u8>) -> i64 {
|
||
let mut next_id = NEXT_SESSION_ID.lock().unwrap();
|
||
let id = *next_id;
|
||
*next_id += 1;
|
||
let mut sessions = SESSIONS.lock().unwrap();
|
||
sessions.insert(id, SessionState {
|
||
config_json,
|
||
bit_handle: None,
|
||
application_data,
|
||
logged_in: false,
|
||
});
|
||
id
|
||
}
|
||
|
||
/// 获取 session 的可变引用
|
||
pub fn with_session<F, R>(session_id: i64, f: F) -> Option<R>
|
||
where
|
||
F: FnOnce(&mut SessionState) -> R,
|
||
{
|
||
let mut sessions = SESSIONS.lock().unwrap();
|
||
sessions.get_mut(&session_id).map(f)
|
||
}
|
||
|
||
/// 移除 session
|
||
pub fn remove_session(session_id: i64) {
|
||
let mut sessions = SESSIONS.lock().unwrap();
|
||
sessions.remove(&session_id);
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 添加 once_cell 依赖**
|
||
|
||
在 `native/craft-core/Cargo.toml` 的 `[dependencies]` 区域添加:
|
||
|
||
```toml
|
||
once_cell = "1"
|
||
```
|
||
|
||
- [ ] **Step 3: 在 lib.rs 中注册 session 模块**
|
||
|
||
在 `native/craft-core/src/lib.rs` 的 `mod` 声明区域添加:
|
||
|
||
```rust
|
||
mod session;
|
||
```
|
||
|
||
- [ ] **Step 4: 编译验证**
|
||
|
||
```bash
|
||
cargo build --manifest-path native/craft-core/Cargo.toml
|
||
```
|
||
|
||
Expected: 编译成功。
|
||
|
||
- [ ] **Step 5: 提交**
|
||
|
||
```bash
|
||
git add native/craft-core/src/session.rs native/craft-core/src/lib.rs native/craft-core/Cargo.toml
|
||
git commit -m "feat(native): add session management with global handle registry"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 3: Rust — 创建 ffi/bitanswer.rs(BitAnswer C API FFI 声明)
|
||
|
||
**Files:**
|
||
- Create: `native/craft-core/src/ffi/mod.rs`
|
||
- Create: `native/craft-core/src/ffi/bitanswer.rs`
|
||
|
||
- [ ] **Step 1: 创建 ffi/mod.rs**
|
||
|
||
写入 `native/craft-core/src/ffi/mod.rs`:
|
||
|
||
```rust
|
||
pub mod bitanswer;
|
||
```
|
||
|
||
- [ ] **Step 2: 创建 bitanswer.rs — BitAnswer C API 的 Rust extern 声明**
|
||
|
||
写入 `native/craft-core/src/ffi/bitanswer.rs`:
|
||
|
||
```rust
|
||
// BitAnswer C API 的 Rust FFI 声明
|
||
// 类型定义与 bitanswer.h 对齐
|
||
|
||
pub type BitHandle = *mut std::ffi::c_void;
|
||
pub type BitStatus = u32;
|
||
pub type BitUint32 = u32;
|
||
|
||
// 登录模式常量
|
||
pub const BIT_MODE_LOCAL: u32 = 0x01;
|
||
pub const BIT_MODE_REMOTE: u32 = 0x02;
|
||
pub const BIT_MODE_AUTO: u32 = 0x03;
|
||
|
||
// 绑定类型
|
||
pub const BINDING_EXISTING: u32 = 0;
|
||
pub const BINDING_LOCAL: u32 = 1;
|
||
|
||
// Session 信息类型
|
||
pub const XML_TYPE_SN_INFO: u32 = 3;
|
||
|
||
// Info 类型
|
||
pub const BIT_INFO_SN: u32 = 1;
|
||
pub const BIT_INFO_SN_FEATURE: u32 = 2;
|
||
|
||
// 特征查询模式
|
||
pub const BIT_QUERY_DEFAULT: u32 = 0x00;
|
||
pub const BIT_QUERY_AVAILABLE: u32 = 0x01;
|
||
|
||
// SetAttr 类型
|
||
pub const ATTR_WAIT_TIMEOUT: u32 = 0x2;
|
||
pub const ATTR_HB_INTERVAL: u32 = 0x8;
|
||
|
||
extern "C" {
|
||
/// 登录授权
|
||
pub fn Bit_Login(
|
||
sz_url: *const std::os::raw::c_char,
|
||
sz_sn: *const std::os::raw::c_char,
|
||
p_application_data: *const u8,
|
||
p_handle: *mut BitHandle,
|
||
mode: BitUint32,
|
||
) -> BitStatus;
|
||
|
||
/// 特征登录
|
||
pub fn Bit_LoginEx(
|
||
sz_url: *const std::os::raw::c_char,
|
||
sz_sn: *const std::os::raw::c_char,
|
||
feature_id: BitUint32,
|
||
sz_reserved: *const std::os::raw::c_char,
|
||
p_application_data: *const u8,
|
||
p_handle: *mut BitHandle,
|
||
mode: BitUint32,
|
||
) -> BitStatus;
|
||
|
||
/// Token 登录
|
||
pub fn Bit_LoginByToken(
|
||
sz_url: *const std::os::raw::c_char,
|
||
sz_access_token: *const std::os::raw::c_char,
|
||
p_application_data: *const u8,
|
||
p_handle: *mut BitHandle,
|
||
) -> BitStatus;
|
||
|
||
/// 登出
|
||
pub fn Bit_Logout(handle: BitHandle) -> BitStatus;
|
||
|
||
/// 在线激活/更新
|
||
pub fn Bit_UpdateOnline(
|
||
sz_url: *const std::os::raw::c_char,
|
||
sz_sn: *const std::os::raw::c_char,
|
||
p_application_data: *const u8,
|
||
) -> BitStatus;
|
||
|
||
/// 读取特征值
|
||
pub fn Bit_ReadFeature(
|
||
handle: BitHandle,
|
||
feature_id: BitUint32,
|
||
p_feature_value: *mut BitUint32,
|
||
) -> BitStatus;
|
||
|
||
/// 写入特征值
|
||
pub fn Bit_WriteFeature(
|
||
handle: BitHandle,
|
||
feature_id: BitUint32,
|
||
feature_value: BitUint32,
|
||
) -> BitStatus;
|
||
|
||
/// 查询特征可用量
|
||
pub fn Bit_QueryFeature(
|
||
handle: BitHandle,
|
||
feature_id: BitUint32,
|
||
p_capacity: *mut BitUint32,
|
||
) -> BitStatus;
|
||
|
||
/// 释放特征占用
|
||
pub fn Bit_ReleaseFeature(
|
||
handle: BitHandle,
|
||
feature_id: BitUint32,
|
||
p_capacity: *mut BitUint32,
|
||
) -> BitStatus;
|
||
|
||
/// 设置配置项
|
||
pub fn Bit_SetDataItem(
|
||
handle: BitHandle,
|
||
sz_data_item_name: *const std::os::raw::c_char,
|
||
p_data_item_value: *const std::ffi::c_void,
|
||
data_item_value_size: BitUint32,
|
||
) -> BitStatus;
|
||
|
||
/// 读取配置项
|
||
pub fn Bit_GetDataItem(
|
||
handle: BitHandle,
|
||
sz_data_item_name: *const std::os::raw::c_char,
|
||
p_data_item_value: *mut std::ffi::c_void,
|
||
p_data_item_value_size: *mut BitUint32,
|
||
) -> BitStatus;
|
||
|
||
/// 删除配置项
|
||
pub fn Bit_RemoveDataItem(
|
||
handle: BitHandle,
|
||
sz_data_item_name: *const std::os::raw::c_char,
|
||
) -> BitStatus;
|
||
|
||
/// 获取配置项数量
|
||
pub fn Bit_GetDataItemNum(
|
||
handle: BitHandle,
|
||
p_num: *mut BitUint32,
|
||
) -> BitStatus;
|
||
|
||
/// 按索引获取配置项名称
|
||
pub fn Bit_GetDataItemName(
|
||
handle: BitHandle,
|
||
index: BitUint32,
|
||
p_data_item_name: *mut std::os::raw::c_char,
|
||
p_data_item_name_size: *mut BitUint32,
|
||
) -> BitStatus;
|
||
|
||
/// 获取会话信息
|
||
pub fn Bit_GetSessionInfo(
|
||
handle: BitHandle,
|
||
session_type: BitUint32,
|
||
p_session_info: *mut std::os::raw::c_char,
|
||
p_session_info_size: *mut BitUint32,
|
||
) -> BitStatus;
|
||
|
||
/// 获取授权信息
|
||
pub fn Bit_GetInfo(
|
||
sz_sn: *const std::os::raw::c_char,
|
||
p_application_data: *const u8,
|
||
info_type: BitUint32,
|
||
p_info: *mut std::os::raw::c_char,
|
||
p_info_size: *mut BitUint32,
|
||
) -> BitStatus;
|
||
|
||
/// 浮动迁出 SN
|
||
pub fn Bit_CheckOutSn(
|
||
sz_url: *const std::os::raw::c_char,
|
||
feature_id: BitUint32,
|
||
p_application_data: *const u8,
|
||
n_duration_days: BitUint32,
|
||
) -> BitStatus;
|
||
|
||
/// 浮动归还
|
||
pub fn Bit_CheckIn(
|
||
sz_url: *const std::os::raw::c_char,
|
||
feature_id: BitUint32,
|
||
p_application_data: *const u8,
|
||
) -> BitStatus;
|
||
|
||
/// 撤销授权
|
||
pub fn Bit_Revoke(
|
||
sz_url: *const std::os::raw::c_char,
|
||
sz_sn: *const std::os::raw::c_char,
|
||
p_application_data: *const u8,
|
||
p_revocation_info: *mut std::os::raw::c_char,
|
||
p_revocation_info_size: *mut BitUint32,
|
||
) -> BitStatus;
|
||
|
||
/// 删除本机 SN
|
||
pub fn Bit_RemoveSn(
|
||
sz_sn: *const std::os::raw::c_char,
|
||
p_application_data: *const u8,
|
||
) -> BitStatus;
|
||
|
||
/// 手动心跳
|
||
pub fn Bit_Heartbeat(
|
||
handle: BitHandle,
|
||
p_reconnects_num: *mut BitUint32,
|
||
) -> BitStatus;
|
||
|
||
/// 设置属性
|
||
pub fn Bit_SetAttr(
|
||
handle: BitHandle,
|
||
attr_type: BitUint32,
|
||
p_value: *const std::ffi::c_void,
|
||
) -> BitStatus;
|
||
|
||
/// 获取库版本
|
||
pub fn Bit_GetVersion(p_version: *mut BitUint32) -> BitStatus;
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 3: 在 lib.rs 中注册 ffi 模块**
|
||
|
||
在 `native/craft-core/src/lib.rs` 的 `mod` 声明区域添加:
|
||
|
||
```rust
|
||
mod ffi;
|
||
```
|
||
|
||
- [ ] **Step 4: 编译验证**
|
||
|
||
```bash
|
||
cargo build --manifest-path native/craft-core/Cargo.toml
|
||
```
|
||
|
||
Expected: 编译成功(extern 声明不会被链接检查,但语法正确)。
|
||
|
||
- [ ] **Step 5: 提交**
|
||
|
||
```bash
|
||
git add native/craft-core/src/ffi/
|
||
git commit -m "feat(native): add BitAnswer C API FFI declarations"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 4: Rust — 创建 ffi/bridge.rs(craft_* → Bit_* 桥接)
|
||
|
||
**Files:**
|
||
- Create: `native/craft-core/src/ffi/bridge.rs`
|
||
|
||
- [ ] **Step 1: 创建 bridge.rs — Phase 1 核心桥接函数**
|
||
|
||
写入 `native/craft-core/src/ffi/bridge.rs`:
|
||
|
||
```rust
|
||
use crate::error::{LicenseError, to_craft_result};
|
||
use crate::ffi::bitanswer;
|
||
use crate::session;
|
||
use crate::CraftResult;
|
||
use std::ffi::CString;
|
||
use std::os::raw::c_char;
|
||
|
||
/// 激活:调用 Bit_UpdateOnline
|
||
pub fn bridge_activate(session_id: i64, license_key: &str) -> CraftResult {
|
||
let result = session::with_session(session_id, |state| {
|
||
// 从配置中解析 URL(简化版:使用固定空 URL → 使用默认服务器)
|
||
let url = std::ptr::null(); // NULL 表示使用默认服务器
|
||
let sn = match CString::new(license_key) {
|
||
Ok(s) => s,
|
||
Err(_) => return LicenseError::InvalidParameter,
|
||
};
|
||
let app_data = state.application_data.as_ptr();
|
||
|
||
let status = unsafe {
|
||
bitanswer::Bit_UpdateOnline(url, sn.as_ptr(), app_data)
|
||
};
|
||
|
||
if status == 0 {
|
||
LicenseError::Success
|
||
} else {
|
||
LicenseError::from_bit_status(status)
|
||
}
|
||
});
|
||
|
||
match result {
|
||
Some(err) => to_craft_result(err),
|
||
None => to_craft_result(LicenseError::WrongHandle),
|
||
}
|
||
}
|
||
|
||
/// 登录:调用 Bit_Login
|
||
pub fn bridge_login(session_id: i64, sn_str: &str, mode: u32) -> CraftResult {
|
||
let result = session::with_session(session_id, |state| {
|
||
let url = std::ptr::null();
|
||
let sn = match CString::new(sn_str) {
|
||
Ok(s) => s,
|
||
Err(_) => return LicenseError::InvalidParameter,
|
||
};
|
||
let app_data = state.application_data.as_ptr();
|
||
let mut handle: bitanswer::BitHandle = std::ptr::null_mut();
|
||
|
||
let status = unsafe {
|
||
bitanswer::Bit_Login(url, sn.as_ptr(), app_data, &mut handle, mode)
|
||
};
|
||
|
||
if status == 0 {
|
||
state.bit_handle = Some(handle as usize);
|
||
state.logged_in = true;
|
||
LicenseError::Success
|
||
} else {
|
||
LicenseError::from_bit_status(status)
|
||
}
|
||
});
|
||
|
||
match result {
|
||
Some(err) => to_craft_result(err),
|
||
None => to_craft_result(LicenseError::WrongHandle),
|
||
}
|
||
}
|
||
|
||
/// 登出:调用 Bit_Logout
|
||
pub fn bridge_logout(session_id: i64) -> CraftResult {
|
||
let result = session::with_session(session_id, |state| {
|
||
let handle = match state.bit_handle {
|
||
Some(h) => h as bitanswer::BitHandle,
|
||
None => return LicenseError::WrongHandle,
|
||
};
|
||
|
||
let status = unsafe { bitanswer::Bit_Logout(handle) };
|
||
state.bit_handle = None;
|
||
state.logged_in = false;
|
||
|
||
if status == 0 {
|
||
LicenseError::Success
|
||
} else {
|
||
LicenseError::from_bit_status(status)
|
||
}
|
||
});
|
||
|
||
match result {
|
||
Some(err) => to_craft_result(err),
|
||
None => to_craft_result(LicenseError::WrongHandle),
|
||
}
|
||
}
|
||
|
||
/// 心跳:调用 Bit_Heartbeat
|
||
pub fn bridge_heartbeat(session_id: i64) -> CraftResult {
|
||
let result = session::with_session(session_id, |state| {
|
||
let handle = match state.bit_handle {
|
||
Some(h) => h as bitanswer::BitHandle,
|
||
None => return LicenseError::WrongHandle,
|
||
};
|
||
|
||
let mut reconnects: u32 = 0;
|
||
let status = unsafe { bitanswer::Bit_Heartbeat(handle, &mut reconnects) };
|
||
|
||
if status == 0 {
|
||
LicenseError::Success
|
||
} else {
|
||
LicenseError::from_bit_status(status)
|
||
}
|
||
});
|
||
|
||
match result {
|
||
Some(err) => to_craft_result(err),
|
||
None => to_craft_result(LicenseError::WrongHandle),
|
||
}
|
||
}
|
||
|
||
/// 读取特征值:调用 Bit_ReadFeature
|
||
pub fn bridge_read_feature(session_id: i64, feature_id: u32) -> i32 {
|
||
let result = session::with_session(session_id, |state| {
|
||
let handle = match state.bit_handle {
|
||
Some(h) => h as bitanswer::BitHandle,
|
||
None => return -1i32,
|
||
};
|
||
|
||
let mut value: u32 = 0;
|
||
let status = unsafe { bitanswer::Bit_ReadFeature(handle, feature_id, &mut value) };
|
||
if status == 0 { value as i32 } else { -1i32 }
|
||
});
|
||
|
||
result.unwrap_or(-1)
|
||
}
|
||
|
||
/// 检查许可:调用 Bit_GetSessionInfo
|
||
pub fn bridge_check_license(session_id: i64) -> CraftResult {
|
||
let result = session::with_session(session_id, |state| {
|
||
let handle = match state.bit_handle {
|
||
Some(h) => h as bitanswer::BitHandle,
|
||
None => return LicenseError::WrongHandle,
|
||
};
|
||
|
||
let mut buf = vec![0u8; 4096];
|
||
let mut size: u32 = 4096;
|
||
let status = unsafe {
|
||
bitanswer::Bit_GetSessionInfo(handle, bitanswer::XML_TYPE_SN_INFO, buf.as_mut_ptr() as *mut c_char, &mut size)
|
||
};
|
||
|
||
if status == 0 {
|
||
LicenseError::Success
|
||
} else {
|
||
LicenseError::from_bit_status(status)
|
||
}
|
||
});
|
||
|
||
match result {
|
||
Some(err) => to_craft_result(err),
|
||
None => to_craft_result(LicenseError::WrongHandle),
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 在 ffi/mod.rs 中注册 bridge**
|
||
|
||
在 `native/craft-core/src/ffi/mod.rs` 添加:
|
||
|
||
```rust
|
||
pub mod bridge;
|
||
```
|
||
|
||
- [ ] **Step 3: 编译验证**
|
||
|
||
```bash
|
||
cargo build --manifest-path native/craft-core/Cargo.toml
|
||
```
|
||
|
||
Expected: 编译成功。
|
||
|
||
- [ ] **Step 4: 提交**
|
||
|
||
```bash
|
||
git add native/craft-core/src/ffi/bridge.rs native/craft-core/src/ffi/mod.rs
|
||
git commit -m "feat(native): add craft_* to Bit_* bridge functions"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 5: Rust — 重构 lib.rs 使用 session-based 句柄管理
|
||
|
||
**Files:**
|
||
- Modify: `native/craft-core/src/lib.rs`
|
||
|
||
- [ ] **Step 1: 重写 lib.rs**
|
||
|
||
用以下内容覆写 `native/craft-core/src/lib.rs`:
|
||
|
||
```rust
|
||
// CraftLabs 授权核心库 — Rust 实现
|
||
// 导出 C ABI 接口,基于 session 管理,对接 BitAnswer C API
|
||
// 对齐 docs/平台架构思路.md §3.1
|
||
|
||
use std::ffi::CStr;
|
||
use std::os::raw::c_char;
|
||
use std::ptr;
|
||
|
||
mod activate;
|
||
mod error;
|
||
mod ffi;
|
||
mod heartbeat;
|
||
mod license;
|
||
mod security;
|
||
mod session;
|
||
|
||
/// Java long 别名 — session_id
|
||
type SessionId = i64;
|
||
|
||
#[repr(C)]
|
||
pub struct CraftResult {
|
||
pub success: i32,
|
||
pub message: *const c_char,
|
||
}
|
||
|
||
#[repr(C)]
|
||
pub struct LicenseInfo {
|
||
pub is_licensed: i32,
|
||
pub expiration_date: i64,
|
||
pub feature_names: *const *const c_char,
|
||
pub feature_values: *const i32,
|
||
pub feature_count: i32,
|
||
}
|
||
|
||
unsafe fn c_str_to_string(ptr: *const c_char) -> String {
|
||
if ptr.is_null() {
|
||
String::new()
|
||
} else {
|
||
CStr::from_ptr(ptr).to_string_lossy().into_owned()
|
||
}
|
||
}
|
||
|
||
static OK_MSG: &[u8] = b"ok\0";
|
||
static FAIL_MSG: &[u8] = b"failure\0";
|
||
|
||
fn ok_result() -> CraftResult {
|
||
CraftResult { success: 1, message: OK_MSG.as_ptr() as *const c_char }
|
||
}
|
||
|
||
fn fail_result() -> CraftResult {
|
||
CraftResult { success: 0, message: FAIL_MSG.as_ptr() as *const c_char }
|
||
}
|
||
|
||
// ── 现有 9 个 C ABI 函数(签名不变,内部改为 session 管理)──
|
||
|
||
#[no_mangle]
|
||
pub extern "C" fn craft_initialize(config_json: *const c_char) -> SessionId {
|
||
let config = unsafe { c_str_to_string(config_json) };
|
||
|
||
#[cfg(feature = "security-hardening")]
|
||
{
|
||
security::anti_debug::anti_debug_check();
|
||
let _ = security::integrity::integrity_check();
|
||
}
|
||
|
||
// 注册 session,返回 session_id
|
||
session::register_session(config, Vec::new())
|
||
}
|
||
|
||
#[no_mangle]
|
||
pub extern "C" fn craft_activate(
|
||
session_id: SessionId,
|
||
license_key: *const c_char,
|
||
_config_json: *const c_char,
|
||
) -> CraftResult {
|
||
if session_id == 0 { return fail_result(); }
|
||
let key = unsafe { c_str_to_string(license_key) };
|
||
ffi::bridge::bridge_activate(session_id, &key)
|
||
}
|
||
|
||
#[no_mangle]
|
||
pub extern "C" fn craft_check_license(session_id: SessionId) -> CraftResult {
|
||
if session_id == 0 { return fail_result(); }
|
||
ffi::bridge::bridge_check_license(session_id)
|
||
}
|
||
|
||
#[no_mangle]
|
||
pub extern "C" fn craft_get_license_info(session_id: SessionId) -> *mut LicenseInfo {
|
||
if session_id == 0 { return ptr::null_mut(); }
|
||
license::get_license_info_stub()
|
||
}
|
||
|
||
#[no_mangle]
|
||
pub extern "C" fn craft_free_license_info(info: *mut LicenseInfo) {
|
||
if !info.is_null() {
|
||
unsafe { drop(Box::from_raw(info)); }
|
||
}
|
||
}
|
||
|
||
#[no_mangle]
|
||
pub extern "C" fn craft_has_feature(
|
||
session_id: SessionId,
|
||
feature_name: *const c_char,
|
||
) -> i32 {
|
||
if session_id == 0 { return 0; }
|
||
let name = unsafe { c_str_to_string(feature_name) };
|
||
// Phase 1: 仍返回 1(桩);Phase 2 对接 Bit_ReadFeature
|
||
if license::has_feature_stub(&name) { 1 } else { 0 }
|
||
}
|
||
|
||
#[no_mangle]
|
||
pub extern "C" fn craft_release(session_id: SessionId) -> CraftResult {
|
||
if session_id == 0 { return fail_result(); }
|
||
ffi::bridge::bridge_logout(session_id)
|
||
}
|
||
|
||
#[no_mangle]
|
||
pub extern "C" fn craft_heartbeat(session_id: SessionId) -> CraftResult {
|
||
if session_id == 0 { return fail_result(); }
|
||
ffi::bridge::bridge_heartbeat(session_id)
|
||
}
|
||
|
||
#[no_mangle]
|
||
pub extern "C" fn craft_destroy(session_id: SessionId) {
|
||
if session_id != 0 {
|
||
// 如果还登录着,先 logout
|
||
let _ = ffi::bridge::bridge_logout(session_id);
|
||
session::remove_session(session_id);
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 更新 license.rs — 适配新签名**
|
||
|
||
覆写 `native/craft-core/src/license.rs`:
|
||
|
||
```rust
|
||
// License state management — Phase 1 stubs
|
||
|
||
use crate::LicenseInfo;
|
||
use std::ptr;
|
||
|
||
pub fn get_license_info_stub() -> *mut LicenseInfo {
|
||
let info = Box::new(LicenseInfo {
|
||
is_licensed: 1,
|
||
expiration_date: 0,
|
||
feature_names: ptr::null(),
|
||
feature_values: ptr::null(),
|
||
feature_count: 0,
|
||
});
|
||
Box::into_raw(info)
|
||
}
|
||
|
||
pub fn has_feature_stub(_feature_name: &str) -> bool {
|
||
true
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 3: 更新 activate.rs — Phase 1 清空**
|
||
|
||
覆写 `native/craft-core/src/activate.rs`:
|
||
|
||
```rust
|
||
// Activation logic — Phase 1: 使用 ffi::bridge::bridge_activate
|
||
// 文件保留用于后续 Phase 扩展
|
||
```
|
||
|
||
- [ ] **Step 4: 编译验证**
|
||
|
||
```bash
|
||
cargo build --manifest-path native/craft-core/Cargo.toml
|
||
```
|
||
|
||
Expected: 编译成功,无 warning。
|
||
|
||
- [ ] **Step 5: 提交**
|
||
|
||
```bash
|
||
git add native/craft-core/src/lib.rs native/craft-core/src/license.rs native/craft-core/src/activate.rs
|
||
git commit -m "refactor(native): switch to session-based handle management in lib.rs"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 6: Java — 标记 AuthProvider 为 @Deprecated
|
||
|
||
**Files:**
|
||
- Modify: `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/AuthProvider.java`
|
||
|
||
- [ ] **Step 1: 添加 @Deprecated 注解**
|
||
|
||
在 `AuthProvider` 接口声明前添加:
|
||
|
||
```java
|
||
@Deprecated(since = "1.1.0", forRemoval = true)
|
||
```
|
||
|
||
完整修改(在第 15 行 `public interface AuthProvider` 前插入):
|
||
|
||
`native/craft-core/src/activate.rs` 不需要修改,直接修改 Java 文件。
|
||
|
||
在 `/Users/huangping/Documents/workspace/craftlabs-authorization-sdk/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/AuthProvider.java` 第 14 行(`public interface AuthProvider extends AutoCloseable {` 上方)插入 `@Deprecated`:
|
||
|
||
```java
|
||
/**
|
||
* 授权能力的统一契约:初始化、激活、校验许可、查询特性与释放等生命周期方法。
|
||
* ...
|
||
* @deprecated 自 1.1.0 起,请使用 {@link cn.craftlabs.auth.CraftLicense} 及其
|
||
* {@link cn.craftlabs.auth.session.LicenseSession} 替代。
|
||
* 将在 Phase 4 移除。
|
||
*/
|
||
@Deprecated(since = "1.1.0", forRemoval = true)
|
||
public interface AuthProvider extends AutoCloseable {
|
||
```
|
||
|
||
- [ ] **Step 2: 编译验证**
|
||
|
||
```bash
|
||
mvn -f java/pom.xml -pl craftlabs-auth-core -am compile
|
||
```
|
||
|
||
Expected: 编译成功(有 deprecated warning 是预期行为)。
|
||
|
||
- [ ] **Step 3: 提交**
|
||
|
||
```bash
|
||
git add java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/AuthProvider.java
|
||
git commit -m "deprecate: mark AuthProvider as @Deprecated, guide to CraftLicense"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 7: Java — 创建 6 个能力接口
|
||
|
||
**Files:**
|
||
- Create: `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseLifecycle.java`
|
||
- Create: `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/FeatureManagement.java`
|
||
- Create: `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/DataItemStore.java`
|
||
- Create: `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseInfoQuery.java`
|
||
- Create: `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/CheckoutManager.java`
|
||
- Create: `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseUtility.java`
|
||
|
||
- [ ] **Step 1: 创建 LicenseLifecycle**
|
||
|
||
写入 `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseLifecycle.java`:
|
||
|
||
```java
|
||
package cn.craftlabs.auth.session;
|
||
|
||
import cn.craftlabs.auth.AuthResult;
|
||
|
||
/**
|
||
* 认证与会话生命周期接口,对齐 BitAnswer C API 的 Login/Logout/Revoke 等。
|
||
*
|
||
* @since 1.1.0
|
||
*/
|
||
public interface LicenseLifecycle {
|
||
/** 在线激活/更新授权。对应 {@code Bit_UpdateOnline}。 */
|
||
AuthResult activate(String licenseKey);
|
||
|
||
/** 登录授权。对应 {@code Bit_Login}。 */
|
||
AuthResult login(String sn, int mode);
|
||
|
||
/** 特征登录。对应 {@code Bit_LoginEx}。 */
|
||
AuthResult loginEx(String sn, int featureId, String reserved, int mode);
|
||
|
||
/** Token 登录。对应 {@code Bit_LoginByToken}。 */
|
||
AuthResult loginByToken(String accessToken);
|
||
|
||
/** 登出。对应 {@code Bit_Logout}。 */
|
||
AuthResult logout();
|
||
|
||
/** 撤销授权。对应 {@code Bit_Revoke}。 */
|
||
AuthResult revoke(String sn);
|
||
|
||
/** 删除本机 SN 数据。对应 {@code Bit_RemoveSn}。 */
|
||
void removeSn(String sn);
|
||
|
||
/** 手动心跳。对应 {@code Bit_Heartbeat}。 */
|
||
AuthResult heartbeat();
|
||
|
||
/** 释放许可占用。对应 {@code Bit_Logout} + 资源清理。 */
|
||
AuthResult release();
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 创建 FeatureManagement**
|
||
|
||
写入 `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/FeatureManagement.java`:
|
||
|
||
```java
|
||
package cn.craftlabs.auth.session;
|
||
|
||
/**
|
||
* 特征项管理接口,对齐 BitAnswer C API 的特征读写/加解密/占用释放。
|
||
*
|
||
* @since 1.1.0
|
||
*/
|
||
public interface FeatureManagement {
|
||
/** 读取特征值。对应 {@code Bit_ReadFeature}。 */
|
||
int readFeature(int featureId);
|
||
|
||
/** 写入特征值。对应 {@code Bit_WriteFeature}。 */
|
||
void writeFeature(int featureId, int value);
|
||
|
||
/** 查询特征可用量。对应 {@code Bit_QueryFeature}。 */
|
||
int queryFeature(int featureId);
|
||
|
||
/** 释放特征占用。对应 {@code Bit_ReleaseFeature}。 */
|
||
int releaseFeature(int featureId);
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 3: 创建 DataItemStore**
|
||
|
||
写入 `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/DataItemStore.java`:
|
||
|
||
```java
|
||
package cn.craftlabs.auth.session;
|
||
|
||
/**
|
||
* 配置项(Data Item)存储接口,对齐 BitAnswer C API 的 Set/Get/Remove/Enum。
|
||
*
|
||
* @since 1.1.0
|
||
*/
|
||
public interface DataItemStore {
|
||
/** 设置配置项。对应 {@code Bit_SetDataItem}。 */
|
||
void setDataItem(String name, byte[] value);
|
||
|
||
/** 读取配置项。对应 {@code Bit_GetDataItem}。 */
|
||
byte[] getDataItem(String name);
|
||
|
||
/** 删除配置项。对应 {@code Bit_RemoveDataItem}。 */
|
||
void removeDataItem(String name);
|
||
|
||
/** 获取配置项数量。对应 {@code Bit_GetDataItemNum}。 */
|
||
int getDataItemCount();
|
||
|
||
/** 按索引获取配置项名称。对应 {@code Bit_GetDataItemName}。 */
|
||
String getDataItemName(int index);
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 4: 创建 LicenseInfoQuery**
|
||
|
||
写入 `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseInfoQuery.java`:
|
||
|
||
```java
|
||
package cn.craftlabs.auth.session;
|
||
|
||
import cn.craftlabs.auth.LicenseInfo;
|
||
|
||
/**
|
||
* 许可信息查询接口,对齐 BitAnswer C API 的 GetSessionInfo/GetInfo/GetServerInfo。
|
||
*
|
||
* @since 1.1.0
|
||
*/
|
||
public interface LicenseInfoQuery {
|
||
/** 获取许可快照。对应 {@code Bit_GetInfo(BIT_INFO_SN)}。 */
|
||
LicenseInfo getLicenseInfo();
|
||
|
||
/** 获取会话信息。对应 {@code Bit_GetSessionInfo}。 */
|
||
String getSessionInfo(int sessionType);
|
||
|
||
/** 获取授权详情。对应 {@code Bit_GetInfo}。 */
|
||
String getInfo(int infoType);
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 5: 创建 CheckoutManager**
|
||
|
||
写入 `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/CheckoutManager.java`:
|
||
|
||
```java
|
||
package cn.craftlabs.auth.session;
|
||
|
||
/**
|
||
* 浮动授权借出/归还接口,对齐 BitAnswer C API 的 CheckOut/CheckIn 系列。
|
||
*
|
||
* @since 1.1.0
|
||
*/
|
||
public interface CheckoutManager {
|
||
/** 浮动迁出 SN。对应 {@code Bit_CheckOutSn}。 */
|
||
void checkOutSn(String url, int featureId, int durationDays);
|
||
|
||
/** 浮动归还。对应 {@code Bit_CheckIn}。 */
|
||
void checkIn(String url, int featureId);
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 6: 创建 LicenseUtility**
|
||
|
||
写入 `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseUtility.java`:
|
||
|
||
```java
|
||
package cn.craftlabs.auth.session;
|
||
|
||
/**
|
||
* 工具方法接口,对齐 BitAnswer C API 的 SetAttr/CustomInfo/Version 等。
|
||
*
|
||
* @since 1.1.0
|
||
*/
|
||
public interface LicenseUtility {
|
||
/** 设置句柄属性。对应 {@code Bit_SetAttr}。 */
|
||
void setAttr(int type, byte[] value);
|
||
|
||
/** 获取库版本。对应 {@code Bit_GetVersion}。 */
|
||
int getVersion();
|
||
|
||
/** 获取上一次错误码。对应 {@code Bit_GetLastError}。 */
|
||
int getLastError();
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 7: 编译验证**
|
||
|
||
```bash
|
||
mvn -f java/pom.xml -pl craftlabs-auth-core -am compile
|
||
```
|
||
|
||
Expected: 编译成功。
|
||
|
||
- [ ] **Step 8: 提交**
|
||
|
||
```bash
|
||
git add java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/
|
||
git commit -m "feat(java): add 6 capability interfaces for BitAnswer 1:1 mapping"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 8: Java — 创建 LicenseSession(骨架实现 6 个接口)
|
||
|
||
**Files:**
|
||
- Create: `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseSession.java`
|
||
|
||
- [ ] **Step 1: 创建 LicenseSession**
|
||
|
||
写入 `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseSession.java`:
|
||
|
||
```java
|
||
package cn.craftlabs.auth.session;
|
||
|
||
import cn.craftlabs.auth.AuthResult;
|
||
import cn.craftlabs.auth.LicenseInfo;
|
||
import cn.craftlabs.auth.internal.NativeBridge;
|
||
|
||
/**
|
||
* 授权会话,实现 6 个能力接口。
|
||
* Phase 1: 仅实现 AuthProvider 兼容方法(委托 NativeBridge),
|
||
* 其他能力接口方法为骨架(返回默认值或抛出 UnsupportedOperationException)。
|
||
*
|
||
* @since 1.1.0
|
||
*/
|
||
public final class LicenseSession
|
||
implements LicenseLifecycle, FeatureManagement, DataItemStore,
|
||
LicenseInfoQuery, CheckoutManager, LicenseUtility, AutoCloseable {
|
||
|
||
private final long sessionId;
|
||
private boolean closed;
|
||
|
||
LicenseSession(long sessionId) {
|
||
this.sessionId = sessionId;
|
||
this.closed = false;
|
||
}
|
||
|
||
long getSessionId() { return sessionId; }
|
||
|
||
// ── LicenseLifecycle ──
|
||
|
||
@Override public AuthResult activate(String licenseKey) {
|
||
ensureOpen();
|
||
return NativeBridge.nativeActivate(sessionId, licenseKey);
|
||
}
|
||
|
||
@Override public AuthResult login(String sn, int mode) {
|
||
ensureOpen();
|
||
return NativeBridge.nativeLogin(sessionId, sn, mode);
|
||
}
|
||
|
||
@Override public AuthResult loginEx(String sn, int featureId, String reserved, int mode) {
|
||
ensureOpen();
|
||
return NativeBridge.nativeLoginEx(sessionId, sn, featureId, reserved, mode);
|
||
}
|
||
|
||
@Override public AuthResult loginByToken(String accessToken) {
|
||
ensureOpen();
|
||
return NativeBridge.nativeLoginByToken(sessionId, accessToken);
|
||
}
|
||
|
||
@Override public AuthResult logout() {
|
||
ensureOpen();
|
||
return NativeBridge.nativeLogout(sessionId);
|
||
}
|
||
|
||
@Override public AuthResult revoke(String sn) {
|
||
ensureOpen();
|
||
return NativeBridge.nativeRevoke(sessionId, sn);
|
||
}
|
||
|
||
@Override public void removeSn(String sn) {
|
||
ensureOpen();
|
||
NativeBridge.nativeRemoveSn(sessionId, sn);
|
||
}
|
||
|
||
@Override public AuthResult heartbeat() {
|
||
ensureOpen();
|
||
return NativeBridge.nativeHeartbeat(sessionId);
|
||
}
|
||
|
||
@Override public AuthResult release() {
|
||
ensureOpen();
|
||
return NativeBridge.nativeRelease(sessionId);
|
||
}
|
||
|
||
// ── FeatureManagement ──
|
||
|
||
@Override public int readFeature(int featureId) {
|
||
ensureOpen();
|
||
return NativeBridge.nativeReadFeature(sessionId, featureId);
|
||
}
|
||
|
||
@Override public void writeFeature(int featureId, int value) {
|
||
ensureOpen();
|
||
NativeBridge.nativeWriteFeature(sessionId, featureId, value);
|
||
}
|
||
|
||
@Override public int queryFeature(int featureId) {
|
||
ensureOpen();
|
||
return NativeBridge.nativeQueryFeature(sessionId, featureId);
|
||
}
|
||
|
||
@Override public int releaseFeature(int featureId) {
|
||
ensureOpen();
|
||
return NativeBridge.nativeReleaseFeature(sessionId, featureId);
|
||
}
|
||
|
||
// ── DataItemStore ──
|
||
|
||
@Override public void setDataItem(String name, byte[] value) {
|
||
ensureOpen();
|
||
NativeBridge.nativeSetDataItem(sessionId, name, value);
|
||
}
|
||
|
||
@Override public byte[] getDataItem(String name) {
|
||
ensureOpen();
|
||
return NativeBridge.nativeGetDataItem(sessionId, name);
|
||
}
|
||
|
||
@Override public void removeDataItem(String name) {
|
||
ensureOpen();
|
||
NativeBridge.nativeRemoveDataItem(sessionId, name);
|
||
}
|
||
|
||
@Override public int getDataItemCount() {
|
||
ensureOpen();
|
||
return NativeBridge.nativeGetDataItemNum(sessionId);
|
||
}
|
||
|
||
@Override public String getDataItemName(int index) {
|
||
ensureOpen();
|
||
return NativeBridge.nativeGetDataItemName(sessionId, index);
|
||
}
|
||
|
||
// ── LicenseInfoQuery ──
|
||
|
||
@Override public LicenseInfo getLicenseInfo() {
|
||
ensureOpen();
|
||
return NativeBridge.nativeGetLicenseInfo(sessionId);
|
||
}
|
||
|
||
@Override public String getSessionInfo(int sessionType) {
|
||
ensureOpen();
|
||
return NativeBridge.nativeGetSessionInfo(sessionId, sessionType);
|
||
}
|
||
|
||
@Override public String getInfo(int infoType) {
|
||
ensureOpen();
|
||
return NativeBridge.nativeGetInfo(sessionId, infoType);
|
||
}
|
||
|
||
// ── CheckoutManager ──
|
||
|
||
@Override public void checkOutSn(String url, int featureId, int durationDays) {
|
||
ensureOpen();
|
||
NativeBridge.nativeCheckOutSn(sessionId, url, featureId, durationDays);
|
||
}
|
||
|
||
@Override public void checkIn(String url, int featureId) {
|
||
ensureOpen();
|
||
NativeBridge.nativeCheckIn(sessionId, url, featureId);
|
||
}
|
||
|
||
// ── LicenseUtility ──
|
||
|
||
@Override public void setAttr(int type, byte[] value) {
|
||
ensureOpen();
|
||
NativeBridge.nativeSetAttr(sessionId, type, value);
|
||
}
|
||
|
||
@Override public int getVersion() {
|
||
return NativeBridge.nativeGetVersion();
|
||
}
|
||
|
||
@Override public int getLastError() {
|
||
ensureOpen();
|
||
return NativeBridge.nativeGetLastError(sessionId);
|
||
}
|
||
|
||
// ── Lifecycle ──
|
||
|
||
@Override public void close() {
|
||
if (!closed) {
|
||
NativeBridge.nativeDestroy(sessionId);
|
||
closed = true;
|
||
}
|
||
}
|
||
|
||
public boolean isClosed() { return closed; }
|
||
|
||
private void ensureOpen() {
|
||
if (closed) throw new IllegalStateException("Session is closed");
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 编译验证(预期失败 — NativeBridge 缺少新方法)**
|
||
|
||
```bash
|
||
mvn -f java/pom.xml -pl craftlabs-auth-core -am compile 2>&1 | head -30
|
||
```
|
||
|
||
Expected: 编译失败,提示 `NativeBridge` 缺少 `nativeLogin`、`nativeLogout` 等方法。
|
||
|
||
- [ ] **Step 3: 提交**
|
||
|
||
```bash
|
||
git add java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/session/LicenseSession.java
|
||
git commit -m "feat(java): add LicenseSession skeleton implementing 6 capability interfaces"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 9: Java — 扩展 NativeBridge(添加 Phase 1 新 JNI 方法)
|
||
|
||
**Files:**
|
||
- Modify: `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/internal/NativeBridge.java`
|
||
|
||
- [ ] **Step 1: 在 NativeBridge 中添加所有新 native 方法声明**
|
||
|
||
在现有方法后追加:
|
||
|
||
```java
|
||
// ── Phase 1 新增:会话/认证 ──
|
||
public static native AuthResult nativeLogin(long handle, String sn, int mode);
|
||
public static native AuthResult nativeLoginEx(long handle, String sn, int featureId, String reserved, int mode);
|
||
public static native AuthResult nativeLoginByToken(long handle, String accessToken);
|
||
public static native AuthResult nativeLogout(long handle);
|
||
|
||
// ── Phase 1 新增:特征项 ──
|
||
public static native int nativeReadFeature(long handle, int featureId);
|
||
public static native void nativeWriteFeature(long handle, int featureId, int value);
|
||
public static native int nativeQueryFeature(long handle, int featureId);
|
||
public static native int nativeReleaseFeature(long handle, int featureId);
|
||
|
||
// ── Phase 1 新增:配置项 ──
|
||
public static native void nativeSetDataItem(long handle, String name, byte[] value);
|
||
public static native byte[] nativeGetDataItem(long handle, String name);
|
||
public static native void nativeRemoveDataItem(long handle, String name);
|
||
public static native int nativeGetDataItemNum(long handle);
|
||
public static native String nativeGetDataItemName(long handle, int index);
|
||
|
||
// ── Phase 1 新增:信息查询 ──
|
||
public static native String nativeGetSessionInfo(long handle, int sessionType);
|
||
public static native String nativeGetInfo(long handle, int infoType);
|
||
|
||
// ── Phase 1 新增:浮动授权 ──
|
||
public static native void nativeCheckOutSn(long handle, String url, int featureId, int durationDays);
|
||
public static native void nativeCheckIn(long handle, String url, int featureId);
|
||
|
||
// ── Phase 1 新增:撤销/删除 ──
|
||
public static native AuthResult nativeRevoke(long handle, String sn);
|
||
public static native void nativeRemoveSn(long handle, String sn);
|
||
|
||
// ── Phase 1 新增:工具 ──
|
||
public static native void nativeSetAttr(long handle, int type, byte[] value);
|
||
public static native int nativeGetVersion();
|
||
public static native int nativeGetLastError(long handle);
|
||
```
|
||
|
||
- [ ] **Step 2: 编译验证**
|
||
|
||
```bash
|
||
mvn -f java/pom.xml -pl craftlabs-auth-core -am compile
|
||
```
|
||
|
||
Expected: 编译成功(native 方法声明不要求立即有 JNI 实现,运行时才链接)。
|
||
|
||
- [ ] **Step 3: 提交**
|
||
|
||
```bash
|
||
git add java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/internal/NativeBridge.java
|
||
git commit -m "feat(java): extend NativeBridge with Phase 1 JNI method declarations (30+ new methods)"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 10: Java — 创建 CraftLicense(新顶层入口)
|
||
|
||
**Files:**
|
||
- Create: `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/CraftLicense.java`
|
||
|
||
- [ ] **Step 1: 创建 CraftLicense**
|
||
|
||
写入 `java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/CraftLicense.java`:
|
||
|
||
```java
|
||
package cn.craftlabs.auth;
|
||
|
||
import cn.craftlabs.auth.internal.NativeBridge;
|
||
import cn.craftlabs.auth.session.LicenseSession;
|
||
|
||
/**
|
||
* CraftLabs 授权 SDK 的顶层入口,替代 {@link AuthProvider}。
|
||
*
|
||
* <p>用法:
|
||
* <pre>{@code
|
||
* CraftLicense license = new CraftLicense();
|
||
* try (LicenseSession session = license.initialize("{\"schemaVersion\":1,...}")) {
|
||
* session.activate("SN-XXXX");
|
||
* int val = session.readFeature(1);
|
||
* }
|
||
* }</pre>
|
||
*
|
||
* @since 1.1.0
|
||
*/
|
||
public final class CraftLicense {
|
||
|
||
static {
|
||
System.loadLibrary("craftlabs_auth_bitanswer");
|
||
}
|
||
|
||
/**
|
||
* 使用 JSON 配置初始化授权会话。
|
||
*
|
||
* @param configJson 符合 {@code schemas/craftlabs-auth-config.schema.json} 的配置字符串
|
||
* @return 新的 {@link LicenseSession}
|
||
*/
|
||
public LicenseSession initialize(String configJson) {
|
||
String cfg = configJson != null ? configJson : "{}";
|
||
long sessionId = NativeBridge.nativeInitialize(cfg);
|
||
return new LicenseSession(sessionId);
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **Step 2: 编译验证**
|
||
|
||
```bash
|
||
mvn -f java/pom.xml -pl craftlabs-auth-core -am compile
|
||
```
|
||
|
||
Expected: 编译成功。
|
||
|
||
- [ ] **Step 3: 提交**
|
||
|
||
```bash
|
||
git add java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/CraftLicense.java
|
||
git commit -m "feat(java): add CraftLicense as new entry point replacing AuthProvider"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 11: Java — 运行现有测试确保没有回归
|
||
|
||
**Files:**
|
||
- (验证,不修改)
|
||
- 测试文件: `java/craftlabs-auth-tests/src/test/java/cn/craftlabs/auth/BitAnswerProviderTest.java`
|
||
|
||
- [ ] **Step 1: 运行现有 Java SDK 测试**
|
||
|
||
```bash
|
||
mvn -f java/pom.xml -B verify
|
||
```
|
||
|
||
Expected: 现有测试通过(`AuthProvider` 未移除,桩行为不变)。
|
||
|
||
- [ ] **Step 2: 验证 deprecated 警告存在但不为 error**
|
||
|
||
Expected: 编译输出包含 `AuthProvider is deprecated` 警告,但不阻止构建。
|
||
|
||
- [ ] **Step 3: 提交**
|
||
|
||
```bash
|
||
git add -A
|
||
git commit -m "verify: existing tests pass after Phase 1 refactor — no regression"
|
||
```
|
||
|
||
---
|
||
|
||
### Task 12: Rust — 编译验证并确保 C ABI 兼容
|
||
|
||
**Files:**
|
||
- (验证,不修改)
|
||
|
||
- [ ] **Step 1: 编译 Rust debug**
|
||
|
||
```bash
|
||
cargo build --manifest-path native/craft-core/Cargo.toml
|
||
```
|
||
|
||
Expected: 编译成功。
|
||
|
||
- [ ] **Step 2: 编译 Rust release**
|
||
|
||
```bash
|
||
cargo build --manifest-path native/craft-core/Cargo.toml --release
|
||
```
|
||
|
||
Expected: 编译成功。产物位于 `native/target/release/libcraftlabs_auth_bitanswer.{so,dylib}`。
|
||
|
||
- [ ] **Step 3: 验证 C ABI 符号导出**
|
||
|
||
```bash
|
||
nm native/target/debug/libcraftlabs_auth_bitanswer.dylib 2>/dev/null | grep " T _craft_" || \
|
||
nm native/target/debug/libcraftlabs_auth_bitanswer.so 2>/dev/null | grep " T craft_" || \
|
||
echo "Check platform-specific symbol listing"
|
||
```
|
||
|
||
Expected: 输出包含 `craft_initialize`、`craft_activate`、`craft_heartbeat` 等符号。
|
||
|
||
---
|
||
|
||
## Phase 1 完成检查点
|
||
|
||
Phase 1 完成后:
|
||
- ✅ Rust: error.rs、session.rs、ffi/bitanswer.rs、ffi/bridge.rs 已创建
|
||
- ✅ Rust: lib.rs 重构为 session-based 句柄管理
|
||
- ✅ Rust: 现有 9 个 C ABI 函数签名不变,内部桥接到 BitAnswer
|
||
- ✅ Java: AuthProvider 标记 @Deprecated
|
||
- ✅ Java: 6 个能力接口已定义
|
||
- ✅ Java: LicenseSession 骨架实现
|
||
- ✅ Java: NativeBridge 新增 30+ JNI 方法声明
|
||
- ✅ Java: CraftLicense 新入口已创建
|
||
- ✅ 现有测试通过,无回归
|
||
|
||
---
|
||
|
||
## Phase 2 — 核心 API 扩展
|
||
|
||
> Phase 2 任务在 Phase 1 通过后执行,将骨架实现替换为真实调用。
|
||
|
||
### Task 13: Rust — 扩展 activate.rs、heartbeat.rs、license.rs 为真实调用
|
||
|
||
- [ ] 实现 `craft_login` C ABI 函数(对接 `bridge_login`)
|
||
- [ ] 实现 `craft_login_ex` C ABI 函数(对接 `Bit_LoginEx`)
|
||
- [ ] 实现 `craft_revoke` C ABI 函数(对接 `Bit_Revoke`)
|
||
- [ ] 实现 `craft_remove_sn` C ABI 函数(对接 `Bit_RemoveSn`)
|
||
- [ ] 实现 `craft_read_feature` C ABI 函数(对接 `Bit_ReadFeature`)
|
||
- [ ] 实现 `craft_write_feature` C ABI 函数(对接 `Bit_WriteFeature`)
|
||
- [ ] 实现 `craft_query_feature` C ABI 函数(对接 `Bit_QueryFeature`)
|
||
- [ ] 实现 `craft_release_feature` C ABI 函数(对接 `Bit_ReleaseFeature`)
|
||
- [ ] 每个函数添加 Rust 单元测试(mock BitAnswer extern)
|
||
- [ ] 编译验证:`cargo test --manifest-path native/craft-core/Cargo.toml`
|
||
|
||
### Task 14: Java — BitAnswerProvider 委托到 LicenseSession
|
||
|
||
- [ ] 修改 `BitAnswerProvider` 的 `initialize` 使用 `CraftLicense`
|
||
- [ ] 保留 `AuthProvider` 接口兼容性
|
||
- [ ] 编译验证:`mvn -f java/pom.xml -B verify`
|
||
|
||
---
|
||
|
||
## Phase 3 — 高级功能
|
||
|
||
### Task 15: Rust — DataItem + Checkout 桥接
|
||
|
||
- [ ] 实现 `craft_set_data_item` / `craft_get_data_item` / `craft_remove_data_item` 等
|
||
- [ ] 实现 `craft_check_out_sn` / `craft_check_in` 等
|
||
- [ ] 实现离线升级流程(`GetRequestInfo` → `GetUpdateInfo` → `ApplyUpdateInfo`)
|
||
|
||
### Task 16: Java — DataItemStore + CheckoutManager 完整实现
|
||
|
||
- [ ] `DataItemStore` 方法全部对接 NativeBridge
|
||
- [ ] `CheckoutManager` 方法全部对接 NativeBridge
|
||
- [ ] 添加集成测试
|
||
|
||
---
|
||
|
||
## Phase 4 — 清理
|
||
|
||
### Task 17: 移除废弃代码
|
||
|
||
- [ ] 删除 `AuthProvider` 接口
|
||
- [ ] 删除 `.deprecated-cmake/` 目录
|
||
- [ ] 更新 `BitAnswerProvider` 以在新接口下工作(或标记废弃)
|
||
- [ ] 全量回归:`mvn -f java/pom.xml -B verify && cargo test`
|
||
```
|
||
|
||
(Plan too long; 668 lines exceeds maximum 500 lines set in preferences. You are seeing the truncated version (500 lines). To see the full plan, read the file /Users/huangping/Documents/workspace/craftlabs-authorization-sdk/docs/superpowers/plans/2026-05-01-bitanswer-1to1-refactor.md directly. Lines 501-668 were not written.)<|end▁of▁thinking|>文件被截断了。让我确认完整写入:
|
||
|
||
<||DSML||tool_calls>
|
||
<||DSML||invoke name="bash">
|
||
<||DSML||parameter name="description" string="true">Check plan file line count |