From 91aabb500ce42813ff452bef814f3152418cd426 Mon Sep 17 00:00:00 2001 From: huangping Date: Mon, 18 May 2026 22:20:14 +0800 Subject: [PATCH] feat: extend Java config, Schema, and DB for selfhosted licensing SDK --- examples/config/school.selfhosted.json | 8 +- .../craftlabs/auth/config/FeatureMapping.java | 3 +- .../auth/config/SelfhostedConfigSection.java | 11 ++- .../selfhosted/SelfHostedAuthProvider.java | 2 +- schemas/craftlabs-auth-config.schema.json | 21 ++++- .../db/migration/V6__selfhosted_licensing.sql | 94 +++++++++++++++++++ 6 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 services/delivery-platform-api/src/main/resources/db/migration/V6__selfhosted_licensing.sql diff --git a/examples/config/school.selfhosted.json b/examples/config/school.selfhosted.json index f27cc74..3c1d192 100644 --- a/examples/config/school.selfhosted.json +++ b/examples/config/school.selfhosted.json @@ -4,11 +4,13 @@ "scenario": "school", "selfhosted": { "baseUrl": "https://license.internal.example/api/v1", - "tenantKey": "district-west" + "tenantKey": "district-west", + "offlineGraceDays": 7, + "heartbeatIntervalHours": 24 }, "features": { - "face": {}, - "expression": {} + "face": { "selfhostedFeatureKey": "face" }, + "expression": { "selfhostedFeatureKey": "expression" } }, "school": { "edgeDeviceId": "gate-02" diff --git a/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/config/FeatureMapping.java b/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/config/FeatureMapping.java index 893d0ab..2e1a549 100644 --- a/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/config/FeatureMapping.java +++ b/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/config/FeatureMapping.java @@ -11,4 +11,5 @@ import com.fasterxml.jackson.annotation.JsonProperty; @JsonInclude(JsonInclude.Include.NON_NULL) public record FeatureMapping( @JsonProperty("bitanswerFeatureId") Integer bitanswerFeatureId, - @JsonProperty("bitanswerFeatureName") String bitanswerFeatureName) {} + @JsonProperty("bitanswerFeatureName") String bitanswerFeatureName, + @JsonProperty("selfhostedFeatureKey") String selfhostedFeatureKey) {} diff --git a/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/config/SelfhostedConfigSection.java b/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/config/SelfhostedConfigSection.java index 52b1ee4..40a0c77 100644 --- a/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/config/SelfhostedConfigSection.java +++ b/java/craftlabs-auth-core/src/main/java/cn/craftlabs/auth/config/SelfhostedConfigSection.java @@ -11,4 +11,13 @@ import com.fasterxml.jackson.annotation.JsonProperty; @JsonInclude(JsonInclude.Include.NON_NULL) public record SelfhostedConfigSection( @JsonProperty("baseUrl") String baseUrl, - @JsonProperty("tenantKey") String tenantKey) {} + @JsonProperty("tenantKey") String tenantKey, + @JsonProperty("offlineGraceDays") Integer offlineGraceDays, + @JsonProperty("heartbeatIntervalHours") Integer heartbeatIntervalHours, + @JsonProperty("publicKeyPem") String publicKeyPem) { + + public SelfhostedConfigSection { + offlineGraceDays = offlineGraceDays != null ? offlineGraceDays : 7; + heartbeatIntervalHours = heartbeatIntervalHours != null ? heartbeatIntervalHours : 24; + } +} diff --git a/java/craftlabs-auth-selfhosted/src/main/java/cn/craftlabs/auth/selfhosted/SelfHostedAuthProvider.java b/java/craftlabs-auth-selfhosted/src/main/java/cn/craftlabs/auth/selfhosted/SelfHostedAuthProvider.java index 884e6f0..1fd4dbe 100644 --- a/java/craftlabs-auth-selfhosted/src/main/java/cn/craftlabs/auth/selfhosted/SelfHostedAuthProvider.java +++ b/java/craftlabs-auth-selfhosted/src/main/java/cn/craftlabs/auth/selfhosted/SelfHostedAuthProvider.java @@ -17,7 +17,7 @@ import cn.craftlabs.auth.internal.NativeBridge; */ public final class SelfHostedAuthProvider implements AuthProvider { static { - System.loadLibrary("craftlabs_auth_bitanswer"); + System.loadLibrary("craftlabs_auth_core"); } private long nativeHandle; diff --git a/schemas/craftlabs-auth-config.schema.json b/schemas/craftlabs-auth-config.schema.json index 5ba7735..7104379 100644 --- a/schemas/craftlabs-auth-config.schema.json +++ b/schemas/craftlabs-auth-config.schema.json @@ -61,6 +61,24 @@ "tenantKey": { "type": "string", "description": "可选;租户或调用方标识。" + }, + "offlineGraceDays": { + "type": "integer", + "minimum": 0, + "maximum": 365, + "default": 7, + "description": "离线宽限期(天),断网后允许继续使用的最大天数。" + }, + "heartbeatIntervalHours": { + "type": "integer", + "minimum": 1, + "maximum": 720, + "default": 24, + "description": "心跳间隔(小时)。" + }, + "publicKeyPem": { + "type": "string", + "description": "可选;RSA 公钥 PEM。不填则使用 SDK 内嵌公钥。" } } }, @@ -72,7 +90,8 @@ "additionalProperties": false, "properties": { "bitanswerFeatureId": { "type": "integer" }, - "bitanswerFeatureName": { "type": "string" } + "bitanswerFeatureName": { "type": "string" }, + "selfhostedFeatureKey": { "type": "string" } } } }, diff --git a/services/delivery-platform-api/src/main/resources/db/migration/V6__selfhosted_licensing.sql b/services/delivery-platform-api/src/main/resources/db/migration/V6__selfhosted_licensing.sql new file mode 100644 index 0000000..b90aad3 --- /dev/null +++ b/services/delivery-platform-api/src/main/resources/db/migration/V6__selfhosted_licensing.sql @@ -0,0 +1,94 @@ +-- V6__selfhosted_licensing.sql +-- 自研授权 SDK 许可证管理表 + +-- RSA 密钥对管理 +CREATE TABLE IF NOT EXISTS platform_license_keys ( + id UUID PRIMARY KEY, + key_id VARCHAR(32) NOT NULL UNIQUE, + public_key TEXT NOT NULL, + private_key TEXT NOT NULL, + algorithm VARCHAR(16) NOT NULL DEFAULT 'RS256', + status VARCHAR(16) NOT NULL DEFAULT 'active' + CHECK (status IN ('active', 'rotated', 'revoked')), + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + rotated_at TIMESTAMPTZ +); + +-- 许可证策略模板 +CREATE TABLE IF NOT EXISTS platform_license_policies ( + id UUID PRIMARY KEY, + name VARCHAR(128) NOT NULL, + grant_type VARCHAR(20) NOT NULL DEFAULT 'subscription', + default_validity_days INT, + default_max_devices INT NOT NULL DEFAULT 1, + default_offline_grace_days INT NOT NULL DEFAULT 7, + feature_defaults TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +-- 许可证主表 +CREATE TABLE IF NOT EXISTS platform_licenses ( + id UUID PRIMARY KEY, + license_id VARCHAR(26) NOT NULL UNIQUE, + tenant_id VARCHAR(64) NOT NULL, + contract_id UUID, + policy_id UUID REFERENCES platform_license_policies(id), + grant_type VARCHAR(20) NOT NULL, + not_before TIMESTAMPTZ NOT NULL, + not_after TIMESTAMPTZ, + offline_grace_days INT NOT NULL DEFAULT 7, + heartbeat_interval_hours INT NOT NULL DEFAULT 24, + max_devices INT NOT NULL DEFAULT 1, + max_concurrent_users INT NOT NULL DEFAULT 0, + max_activations INT NOT NULL DEFAULT 0, + status VARCHAR(20) NOT NULL DEFAULT 'active' + CHECK (status IN ('active', 'revoked', 'expired')), + issued_at TIMESTAMPTZ NOT NULL, + revoked_at TIMESTAMPTZ, + revoked_reason VARCHAR(255), + signed_payload TEXT NOT NULL, + key_id VARCHAR(32) NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +-- 许可证特性 +CREATE TABLE IF NOT EXISTS platform_license_features ( + license_id VARCHAR(26) NOT NULL REFERENCES platform_licenses(license_id) ON DELETE CASCADE, + feature_key VARCHAR(64) NOT NULL, + enabled BOOLEAN NOT NULL, + PRIMARY KEY (license_id, feature_key) +); + +-- 终端激活记录 +CREATE TABLE IF NOT EXISTS platform_license_activations ( + id UUID PRIMARY KEY, + license_id VARCHAR(26) NOT NULL REFERENCES platform_licenses(license_id) ON DELETE CASCADE, + device_hash VARCHAR(128) NOT NULL, + device_info TEXT, + stability_score SMALLINT NOT NULL DEFAULT 0, + server_uuid VARCHAR(64), + status VARCHAR(20) NOT NULL DEFAULT 'active' + CHECK (status IN ('active', 'released', 'deactivated')), + first_seen_at TIMESTAMPTZ NOT NULL, + last_heartbeat TIMESTAMPTZ, + deactivated_at TIMESTAMPTZ, + UNIQUE (license_id, device_hash) +); + +-- 心跳审计(可选,视量级决定) +CREATE TABLE IF NOT EXISTS platform_license_heartbeats ( + id BIGSERIAL, + license_id VARCHAR(26) NOT NULL, + device_hash VARCHAR(128) NOT NULL, + heartbeat_at TIMESTAMPTZ NOT NULL DEFAULT now(), + online_status BOOLEAN NOT NULL, + response_code VARCHAR(10) +); +CREATE INDEX IF NOT EXISTS idx_heartbeats_license_time + ON platform_license_heartbeats(license_id, heartbeat_at DESC); + +-- 索引 +CREATE INDEX IF NOT EXISTS idx_licenses_status ON platform_licenses(status); +CREATE INDEX IF NOT EXISTS idx_licenses_tenant ON platform_licenses(tenant_id); +CREATE INDEX IF NOT EXISTS idx_activations_license ON platform_license_activations(license_id);