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
46 KiB
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:
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 声明区域添加:
mod error;
- Step 3: 编译验证
cargo build --manifest-path native/craft-core/Cargo.toml
Expected: 编译成功,无错误。
- Step 4: 提交
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:
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] 区域添加:
once_cell = "1"
- Step 3: 在 lib.rs 中注册 session 模块
在 native/craft-core/src/lib.rs 的 mod 声明区域添加:
mod session;
- Step 4: 编译验证
cargo build --manifest-path native/craft-core/Cargo.toml
Expected: 编译成功。
- Step 5: 提交
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:
pub mod bitanswer;
- Step 2: 创建 bitanswer.rs — BitAnswer C API 的 Rust extern 声明
写入 native/craft-core/src/ffi/bitanswer.rs:
// 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 声明区域添加:
mod ffi;
- Step 4: 编译验证
cargo build --manifest-path native/craft-core/Cargo.toml
Expected: 编译成功(extern 声明不会被链接检查,但语法正确)。
- Step 5: 提交
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:
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 添加:
pub mod bridge;
- Step 3: 编译验证
cargo build --manifest-path native/craft-core/Cargo.toml
Expected: 编译成功。
- Step 4: 提交
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:
// 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:
// 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:
// Activation logic — Phase 1: 使用 ffi::bridge::bridge_activate
// 文件保留用于后续 Phase 扩展
- Step 4: 编译验证
cargo build --manifest-path native/craft-core/Cargo.toml
Expected: 编译成功,无 warning。
- Step 5: 提交
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 接口声明前添加:
@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:
/**
* 授权能力的统一契约:初始化、激活、校验许可、查询特性与释放等生命周期方法。
* ...
* @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: 编译验证
mvn -f java/pom.xml -pl craftlabs-auth-core -am compile
Expected: 编译成功(有 deprecated warning 是预期行为)。
- Step 3: 提交
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:
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:
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:
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:
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:
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:
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: 编译验证
mvn -f java/pom.xml -pl craftlabs-auth-core -am compile
Expected: 编译成功。
- Step 8: 提交
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:
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 缺少新方法)
mvn -f java/pom.xml -pl craftlabs-auth-core -am compile 2>&1 | head -30
Expected: 编译失败,提示 NativeBridge 缺少 nativeLogin、nativeLogout 等方法。
- Step 3: 提交
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 方法声明
在现有方法后追加:
// ── 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: 编译验证
mvn -f java/pom.xml -pl craftlabs-auth-core -am compile
Expected: 编译成功(native 方法声明不要求立即有 JNI 实现,运行时才链接)。
- Step 3: 提交
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:
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: 编译验证
mvn -f java/pom.xml -pl craftlabs-auth-core -am compile
Expected: 编译成功。
- Step 3: 提交
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 测试
mvn -f java/pom.xml -B verify
Expected: 现有测试通过(AuthProvider 未移除,桩行为不变)。
- Step 2: 验证 deprecated 警告存在但不为 error
Expected: 编译输出包含 AuthProvider is deprecated 警告,但不阻止构建。
- Step 3: 提交
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
cargo build --manifest-path native/craft-core/Cargo.toml
Expected: 编译成功。
- Step 2: 编译 Rust release
cargo build --manifest-path native/craft-core/Cargo.toml --release
Expected: 编译成功。产物位于 native/target/release/libcraftlabs_auth_bitanswer.{so,dylib}。
- Step 3: 验证 C ABI 符号导出
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_loginC ABI 函数(对接bridge_login) - 实现
craft_login_exC ABI 函数(对接Bit_LoginEx) - 实现
craft_revokeC ABI 函数(对接Bit_Revoke) - 实现
craft_remove_snC ABI 函数(对接Bit_RemoveSn) - 实现
craft_read_featureC ABI 函数(对接Bit_ReadFeature) - 实现
craft_write_featureC ABI 函数(对接Bit_WriteFeature) - 实现
craft_query_featureC ABI 函数(对接Bit_QueryFeature) - 实现
craft_release_featureC 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方法全部对接 NativeBridgeCheckoutManager方法全部对接 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