feat(v1.0.1): tangent_pull + error codes + version + license management
CI / Build & Test (push) Failing after 39s
CI / Release Build (push) Failing after 33s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled

Remaining P3 item:
- tangent_pull: maintain G1 tangency when pulling faces

System infrastructure:
- Unified ErrorCode (0-7xxx): ErrorInfo with Chinese messages
- Version system: VDE_VERSION_MAJOR/MINOR/PATCH macros,
  vde_version() → VersionInfo, vde_version_string()
- License management: LicenseTier (Community/Professional/Enterprise),
  LicenseKey (VDE-XXXX-XXXX, Base32 + HMAC-SHA256 signature),
  LicenseManager singleton with trial support
- Convenience header: vde/vde_version.h

Tests: 43 total (26 license + 17 version)
This commit is contained in:
茂之钳
2026-07-27 21:54:46 +08:00
parent 4b90438315
commit 5bfcbcb8e9
14 changed files with 2355 additions and 24 deletions
+278
View File
@@ -0,0 +1,278 @@
#pragma once
/**
* @file license_manager.h
* @brief ViewDesignEngine 许可证管理系统
*
* 提供三层许可证体系:
* - Community(社区版):基础 B-Rep + STEP/IGES 导入导出
* - Professional(专业版):+ 5轴CAM + 高级曲面 + GD&T
* - Enterprise(企业版):+ 技术支持 + 自定义格式
*
* LicenseKey 格式:VDE-XXXX-XXXX-XXXXBase32编码)
* 包含:版本号 + tier + 过期时间 + 签名(HMAC-SHA256
*
* @ingroup core
*/
#include <cstdint>
#include <string>
#include <vector>
#include <set>
#include <ctime>
#include <mutex>
namespace vde::core {
// ═══════════════════════════════════════════════════════════════
// LicenseTier 枚举
// ═══════════════════════════════════════════════════════════════
/// 许可证层级
enum class LicenseTier : int {
Community = 0, ///< 社区版
Professional = 1, ///< 专业版
Enterprise = 2, ///< 企业版
};
/// 将 LicenseTier 转为字符串
inline const char* license_tier_name(LicenseTier tier) {
switch (tier) {
case LicenseTier::Community: return "Community";
case LicenseTier::Professional: return "Professional";
case LicenseTier::Enterprise: return "Enterprise";
}
return "Unknown";
}
/// 从字符串解析 LicenseTier,失败返回 -1
int parse_license_tier(const std::string& name);
// ═══════════════════════════════════════════════════════════════
// Feature 枚举(按 tier 区分)
// ═══════════════════════════════════════════════════════════════
/// 功能特性标识
enum class Feature : int {
// Community 级别
BASIC_BREP = 0, ///< 基础B-Rep建模
STEP_IMPORT_EXPORT = 1, ///< STEP导入导出
IGES_IMPORT_EXPORT = 2, ///< IGES导入导出
BASE_IO_FORMATS = 3, ///< 基础格式IOOBJ/STL/PLY
MESH_OPERATIONS = 4, ///< 基本网格操作
SKETCH_SOLVER = 5, ///< 草图约束求解
// Professional 级别
CAM_5AXIS = 10, ///< 5轴CAM加工
ADVANCED_SURFACING = 11, ///< 高级曲面建模(A级曲面等)
GD_T = 12, ///< GD&T 几何尺寸和公差
PMI_MBD = 13, ///< PMI / 基于模型的定义
ASSEMBLY_DESIGN = 14, ///< 装配设计
SHEET_METAL = 15, ///< 钣金设计
ADVANCED_HEALING = 16, ///< 高级修复
FEA_MESH = 17, ///< FEA网格生成
IGA_ANALYSIS = 18, ///< IGA等几何分析
COLLISION_DETECTION = 19, ///< 碰撞检测
DIRECT_MODELING = 20, ///< 直接建模
SURFACE_FAIRING = 21, ///< 曲面光顺
// Enterprise 级别
TECHNICAL_SUPPORT = 30, ///< 技术支持
CUSTOM_FORMATS = 31, ///< 自定义格式支持
SCRIPTING_API = 32, ///< 脚本API
GPU_ACCELERATION = 33, ///< GPU加速
CLOUD_COLLABORATION = 34, ///< 云端协作
SDF_MODELING = 35, ///< SDF隐式建模
LICENSE_MANAGER_SDK = 36, ///< License SDK分发
};
/// Feature 转字符串
const char* feature_name(Feature f);
/// 字符串转 Feature,失败返回 -1
int parse_feature(const std::string& name);
/// 获取指定 tier 包含的所有 feature
std::set<Feature> features_for_tier(LicenseTier tier);
/// 检查某个 feature 是否属于指定 tier
bool feature_available_in_tier(Feature f, LicenseTier tier);
// ═══════════════════════════════════════════════════════════════
// LicenseInfo 结构体
// ═══════════════════════════════════════════════════════════════
/// 许可证信息
struct LicenseInfo {
LicenseTier tier; ///< 层级
std::string licensee; ///< 被许可方(公司/个人名称)
std::time_t expiry; ///< 过期时间(Unix时间戳,0=永久)
std::set<Feature> features; ///< 启用的功能列表
std::string key_string; ///< 原始License Key字符串
int version; ///< License版本号
/// 默认构造(Community试用版)
LicenseInfo()
: tier(LicenseTier::Community)
, licensee("Trial User")
, expiry(0)
, version(1) {}
/// 是否已过期
[[nodiscard]] bool is_expired() const;
/// 距离过期还剩多少天(返回 -1 表示永久有效)
[[nodiscard]] int days_until_expiry() const;
};
// ═══════════════════════════════════════════════════════════════
// LicenseKey 类
// ═══════════════════════════════════════════════════════════════
/**
* @brief LicenseKey 生成/验证/解析类
*
* License Key 格式:VDE-XXXX-XXXX-XXXX
* 其中 XXXX 段为 Base32 编码的二进制数据。
*
* 二进制数据布局(17字节):
* - byte 0: version (1)
* - byte 1: tier (0=Community, 1=Professional, 2=Enterprise)
* - byte 2-9: expiry (uint64_t, Unix timestamp, big-endian)
* - byte 10-16: reserved (0)
*
* 签名:HMAC-SHA256 的前 4 字节附加到 Base32 编码后的最后一段。
*
* 完整的 Key 编码流程:
* payload(17B) → HMAC-SHA256 → 取前4B → payload+signature(21B) → Base32 → VDE-XXXX-XXXX-XXXX
*/
class LicenseKey {
public:
/// 默认构造(无效Key
LicenseKey() = default;
/**
* @brief 从字符串解析 LicenseKey
* @param key_str 如 "VDE-ABCD-EFGH-IJKL"
* @return 如果格式有效返回 true
*/
bool parse(const std::string& key_str);
/**
* @brief 生成 LicenseKey
* @param tier 许可证层级
* @param expiry 过期时间(Unix时间戳,0=永久)
* @param licensee 被许可方名称
* @return 格式化的License Key字符串
*/
static std::string generate(LicenseTier tier, std::time_t expiry,
const std::string& licensee);
/**
* @brief 验证 LicenseKey 是否有效
* @param key_str License Key 字符串
* @return 有效则返回 true
*/
static bool validate(const std::string& key_str);
/// 获取解析后的 LicenseInfo
[[nodiscard]] const LicenseInfo& info() const { return info_; }
/// 原始 Key 字符串
[[nodiscard]] const std::string& key_string() const { return key_string_; }
/// 签名验证是否通过
[[nodiscard]] bool signature_valid() const { return sig_valid_; }
private:
/// Base32 编解码(RFC 4648,无填充)
static std::string base32_encode(const std::vector<uint8_t>& data);
static std::vector<uint8_t> base32_decode(const std::string& encoded);
/// HMAC-SHA256 简化实现(使用占位密钥)
static std::vector<uint8_t> compute_hmac(const std::vector<uint8_t>& data);
LicenseInfo info_;
std::string key_string_;
bool sig_valid_ = false;
};
// ═══════════════════════════════════════════════════════════════
// LicenseManager 单例
// ═══════════════════════════════════════════════════════════════
/**
* @brief LicenseManager 全局许可证管理器(单例)
*
* 管理当前运行时使用的许可证,提供功能查询接口。
*
* 用法:
* @code
* auto& mgr = LicenseManager::instance();
* mgr.set_license("VDE-ABCD-EFGH-IJKL");
* if (mgr.has_feature("CAM_5AXIS")) { ... }
* if (mgr.is_expired()) { ... }
* @endcode
*/
class LicenseManager {
public:
/// 获取单例实例
static LicenseManager& instance();
// ── 禁止拷贝和移动 ──────────────────────────────
LicenseManager(const LicenseManager&) = delete;
LicenseManager& operator=(const LicenseManager&) = delete;
/**
* @brief 设置 License Key
* @param key License Key 字符串
* @return true 设置成功(Key有效且未过期)
*/
bool set_license(const std::string& key);
/**
* @brief 进入试用模式(30天Community试用)
* @param days 试用天数(默认30
*/
void start_trial(int days = 30);
/// 当前许可证层级
[[nodiscard]] LicenseTier current_tier() const;
/**
* @brief 检查是否拥有某个功能
* @param feature_name 功能名称字符串(如 "CAM_5AXIS"
* @return true 表示该功能可用
*/
[[nodiscard]] bool has_feature(const std::string& feature_name) const;
/**
* @brief 检查 Feature 枚举是否可用
* @param f Feature 枚举值
* @return true 表示该功能可用
*/
[[nodiscard]] bool has_feature(Feature f) const;
/// 许可证是否已过期
[[nodiscard]] bool is_expired() const;
/// 试用剩余天数(-1 表示已过期或无试用)
[[nodiscard]] int trial_days_left() const;
/// 获取当前许可证信息
[[nodiscard]] const LicenseInfo& current_license() const;
/// 是否处于试用模式
[[nodiscard]] bool is_trial() const;
private:
LicenseManager();
~LicenseManager() = default;
LicenseInfo current_;
bool trial_mode_ = false;
std::time_t trial_start_ = 0;
int trial_days_ = 30;
mutable std::mutex mutex_;
};
} // namespace vde::core
+176
View File
@@ -0,0 +1,176 @@
#pragma once
/**
* @file version.h
* @brief ViewDesignEngine 版本信息
*
* 提供编译时版本宏和运行时版本查询函数。
* 编译时间戳通过 CMake 的 configure_file 在构建时注入。
*
* @ingroup core
*/
#include <cstdint>
#include <string>
// 包含 CMake 生成的构建配置
#if __has_include("vde_build_config.h")
#include "vde_build_config.h"
#endif
// ═══════════════════════════════════════════════════════════════
// 编译时版本宏
// ═══════════════════════════════════════════════════════════════
/// 主版本号(不兼容的 API 变更)
#define VDE_VERSION_MAJOR 1
/// 次版本号(向后兼容的功能新增)
#define VDE_VERSION_MINOR 0
/// 修订版本号(向后兼容的问题修复)
#define VDE_VERSION_PATCH 1
/// 版本字符串
#define VDE_VERSION_STRING "1.0.1"
// ── 版本号整型(用于比较) ────────────────────────────
/// 版本号编码为大整数: VDE_VERSION(major, minor, patch) = major*10000 + minor*100 + patch
#define VDE_VERSION_CODE(major, minor, patch) \
((major) * 10000 + (minor) * 100 + (patch))
/// 当前版本号整型编码
#define VDE_VERSION_NUM VDE_VERSION_CODE(1, 0, 1)
// ═══════════════════════════════════════════════════════════════
// 构建信息(由 CMake configure_file 注入)
// ═══════════════════════════════════════════════════════════════
#ifndef VDE_BUILD_TYPE
/// 构建类型:Debug / Release / RelWithDebInfo / MinSizeRel
#ifdef NDEBUG
#define VDE_BUILD_TYPE "Release"
#else
#define VDE_BUILD_TYPE "Debug"
#endif
#endif
#ifndef VDE_BUILD_TIMESTAMP
/// 构建时间戳(ISO 8601 格式,如 "2026-07-27T12:00:00Z"
/// 由 CMake 的 configure_file 注入
#define VDE_BUILD_TIMESTAMP "unknown"
#endif
#ifndef VDE_COMPILER_INFO
/// 编译器信息字符串
#if defined(__clang__)
#define VDE_COMPILER_INFO "Clang " __clang_version__
#elif defined(__GNUC__) || defined(__GNUG__)
#define VDE_COMPILER_INFO "GCC " __VERSION__
#elif defined(_MSC_VER)
#define VDE_COMPILER_INFO "MSVC " _MSC_VER_STR
#else
#define VDE_COMPILER_INFO "Unknown Compiler"
#endif
#endif
// ═══════════════════════════════════════════════════════════════
// C++ 标准信息
// ═══════════════════════════════════════════════════════════════
/// C++ 标准字符串(如 "C++20"
#if __cplusplus >= 202002L
#define VDE_CPP_STANDARD_STRING "C++20"
#elif __cplusplus >= 201703L
#define VDE_CPP_STANDARD_STRING "C++17"
#elif __cplusplus >= 201402L
#define VDE_CPP_STANDARD_STRING "C++14"
#elif __cplusplus >= 201103L
#define VDE_CPP_STANDARD_STRING "C++11"
#else
#define VDE_CPP_STANDARD_STRING "C++03"
#endif
namespace vde::core {
// ═══════════════════════════════════════════════════════════════
// VersionInfo 结构体
// ═══════════════════════════════════════════════════════════════
/**
* @brief 版本信息结构体
*
* 包含库版本号、构建类型、时间戳和编译器信息。
*/
struct VersionInfo {
int major; ///< 主版本号
int minor; ///< 次版本号
int patch; ///< 修订版本号
std::string version_string; ///< 版本字符串 "1.0.1"
std::string build_type; ///< 构建类型 "Debug"/"Release"
std::string build_timestamp;///< 构建时间戳
std::string compiler_info; ///< 编译器信息
std::string cpp_standard; ///< C++ 标准字符串 "C++20"
/// 默认构造(使用编译时宏填充)
VersionInfo()
: major(VDE_VERSION_MAJOR)
, minor(VDE_VERSION_MINOR)
, patch(VDE_VERSION_PATCH)
, version_string(VDE_VERSION_STRING)
, build_type(VDE_BUILD_TYPE)
, build_timestamp(VDE_BUILD_TIMESTAMP)
, compiler_info(VDE_COMPILER_INFO)
, cpp_standard(VDE_CPP_STANDARD_STRING) {}
};
// ═══════════════════════════════════════════════════════════════
// API 函数
// ═══════════════════════════════════════════════════════════════
/**
* @brief 获取版本信息结构体
* @return VersionInfo 包含完整的版本和构建信息
*/
inline VersionInfo vde_version() {
return VersionInfo{};
}
/**
* @brief 获取人类可读的版本字符串
*
* 格式:"ViewDesignEngine v1.0.1 (C++20, GCC 11.4)"
*
* @return 版本信息字符串
*/
inline std::string vde_version_string() {
std::string result = "ViewDesignEngine v";
result += VDE_VERSION_STRING;
result += " (";
result += VDE_CPP_STANDARD_STRING;
result += ", ";
result += VDE_COMPILER_INFO;
result += ")";
return result;
}
/**
* @brief 比较两个版本号
*
* 返回负值表示 lhs < rhs,零表示相等,正值表示 lhs > rhs。
*
* @param lhs_major 左主版本
* @param lhs_minor 左次版本
* @param lhs_patch 左修订版本
* @param rhs_major 右主版本
* @param rhs_minor 右次版本
* @param rhs_patch 右修订版本
* @return 比较结果
*/
inline int vde_version_compare(int lhs_major, int lhs_minor, int lhs_patch,
int rhs_major, int rhs_minor, int rhs_patch) {
int lhs = VDE_VERSION_CODE(lhs_major, lhs_minor, lhs_patch);
int rhs = VDE_VERSION_CODE(rhs_major, rhs_minor, rhs_patch);
return lhs - rhs;
}
} // namespace vde::core