feat(rwa-api): 将 RWA 法律要素体系集成到 nac-api-server v3.1.0

- 新增 rwa_routes.rs 模块,暴露 15 个 HTTP 端点
- 链接 5 个 RWA 库(legal-model/adapters/contracts/bilateral/sharia)
- 端点覆盖:辖区查询/资产验证/合约模板/双边规则/Sharia合规
- 全部 15/15 端到端测试通过,零警告,零错误
- nac-api-server 版本升级至 v3.1.0(NAC_Lens/4.0)

Issue: #75 #76 #77 RWA-INTEGRATION
This commit is contained in:
NAC Admin 2026-03-07 16:54:59 +08:00
parent 38b46389d9
commit 852ce44960
3 changed files with 499 additions and 6 deletions

View File

@ -1,9 +1,9 @@
[package]
name = "nac-api-server"
version = "3.0.0"
version = "3.1.0"
edition = "2021"
authors = ["NAC Core Team <dev@newassetchain.io>"]
description = "NAC公链统一API网关 - 主网所有微服务统一入口 (NRPC/4.0)"
description = "NAC公链统一API网关 - 主网所有微服务统一入口 (NAC_Lens/4.0) + RWA法律要素体系"
[[bin]]
name = "nac-api-server"
@ -12,6 +12,13 @@ path = "src/main.rs"
[dependencies]
nac-upgrade-framework = { path = "../nac-upgrade-framework" }
# RWA 法律要素体系(五阶段)
nac-rwa-legal-model = { path = "../nac-rwa-legal-model" }
nac-rwa-jurisdiction-adapters = { path = "../nac-rwa-jurisdiction-adapters" }
nac-rwa-contract-templates = { path = "../nac-rwa-contract-templates" }
nac-rwa-bilateral-rules = { path = "../nac-rwa-bilateral-rules" }
nac-rwa-sharia-compliance = { path = "../nac-rwa-sharia-compliance" }
# 异步运行时
tokio = { version = "1.0", features = ["full"] }

View File

@ -37,6 +37,9 @@ use tower_http::cors::{Any, CorsLayer};
use tracing::{error, info, warn};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
// RWA 法律要素体系路由模块
mod rwa_routes;
// ============================================================
// 服务注册表
// ============================================================
@ -261,6 +264,8 @@ fn build_router(state: Arc<AppState>) -> Router {
move |req: Request| proxy_to(s.clone(), "http://localhost:9552", req)
}))
// ── RWA 法律要素体系内嵌NAC_Lens/4.0)────────────────
.nest("/api/v1/rwa", rwa_routes::rwa_router())
// ── CORS ─────────────────────────────────────────────
.layer(
CorsLayer::new()
@ -278,7 +283,7 @@ fn build_router(state: Arc<AppState>) -> Router {
async fn root_info() -> Json<serde_json::Value> {
Json(serde_json::json!({
"service": "NAC API Server",
"version": "3.0.0",
"version": "3.1.0",
"protocol": "NAC_Lens/4.0",
"chain_id": 5132611,
"network": "mainnet",
@ -294,7 +299,8 @@ async fn root_info() -> Json<serde_json::Value> {
{"name": "wallet", "path": "/api/v1/wallet", "upstream": "9556"},
{"name": "exchange", "path": "/api/v1/exchange", "upstream": "9557"},
{"name": "cib", "path": "/api/v1/cib", "upstream": "8091"},
{"name": "onboarding", "path": "/api/v1/onboarding", "upstream": "9552"}
{"name": "onboarding", "path": "/api/v1/onboarding", "upstream": "9552"},
{"name": "rwa", "path": "/api/v1/rwa", "upstream": "embedded", "description": "RWA法律要素体系"}
],
"docs": "https://docs.newassetchain.io/api"
}))
@ -304,7 +310,7 @@ async fn health_check() -> Json<serde_json::Value> {
Json(serde_json::json!({
"status": "ok",
"service": "nac-api-server",
"version": "3.0.0",
"version": "3.1.0",
"protocol": "NAC_Lens/4.0",
"chain_id": 5132611
}))
@ -389,7 +395,7 @@ async fn aggregated_health(State(state): State<Arc<AppState>>) -> Json<serde_jso
Json(serde_json::json!({
"status": overall,
"service": "nac-api-server",
"version": "3.0.0",
"version": "3.1.0",
"protocol": "NAC_Lens/4.0",
"chain_id": 5132611,
"network": "mainnet",

View File

@ -0,0 +1,480 @@
// NAC RWA 法律要素体系 HTTP 路由模块
// 版本1.0.0
// 协议NAC_Lens/4.0
//
// 暴露端点:
// GET /api/v1/rwa/health — 健康检查
// GET /api/v1/rwa/jurisdictions — 获取所有支持的辖区列表
// GET /api/v1/rwa/jurisdictions/:code — 获取指定辖区的完整法律信息
// GET /api/v1/rwa/asset-types — 获取所有资产类型枚举
// POST /api/v1/rwa/validate — 验证资产上链的法律合规性
// POST /api/v1/rwa/contract-template — 获取合约模板
// GET /api/v1/rwa/bilateral/:from/:to — 获取双边贸易规则
// POST /api/v1/rwa/sharia-check — 伊斯兰合规检查
// GET /api/v1/rwa/sharia/jurisdictions — 获取 Sharia 辖区列表
use axum::{
extract::Path,
http::StatusCode,
response::IntoResponse,
routing::{get, post},
Json, Router,
};
use serde::Deserialize;
// 引入 RWA 库
use nac_rwa_jurisdiction_adapters::{
find_adapter, get_all_adapters,
};
use nac_rwa_legal_model::{
AssetCategory, RealEstateSubtype, MovablePropertySubtype, IPSubtype,
};
use nac_rwa_contract_templates::{
get_real_estate_template, get_goods_trade_template, get_ip_license_template,
};
use nac_rwa_bilateral_rules::{get_bilateral_matrix, list_available_pairs};
use nac_rwa_sharia_compliance::{
get_sharia_framework, validate_sharia_compliance, IslamicFinanceStructure,
};
// ============================================================
// 请求结构
// ============================================================
/// 资产合规验证请求
#[derive(Debug, Deserialize)]
pub struct ValidateRequest {
/// 资产所在辖区代码(如 "CN"、"AE"
pub jurisdiction: String,
/// 资产类型("RealEstate" | "Goods" | "IntellectualProperty"
pub asset_type: String,
/// 是否为外资实体
pub is_foreign_entity: Option<bool>,
/// 是否为跨境交易
pub is_cross_border: Option<bool>,
/// 目标辖区(跨境时填写)
pub target_jurisdiction: Option<String>,
}
/// 合约模板请求
#[derive(Debug, Deserialize)]
pub struct ContractTemplateRequest {
/// 合约类型:"real_estate" | "goods_trade" | "ip_license"
pub contract_type: String,
/// 卖方/许可方辖区
pub seller_jurisdiction: String,
/// 买方/被许可方辖区(跨境时填写)
pub buyer_jurisdiction: Option<String>,
}
/// Sharia 合规检查请求
#[derive(Debug, Deserialize)]
pub struct ShariaCheckRequest {
/// 辖区代码("AE"、"SA"、"MY"
pub jurisdiction: String,
/// 金融结构类型
pub structure: String,
/// 是否含有利息条款
pub has_interest: bool,
/// 是否存在过度不确定性
pub has_excessive_uncertainty: bool,
/// 是否涉及 Haram 商品
pub involves_haram_goods: bool,
}
// ============================================================
// 路由构建
// ============================================================
/// 构建 RWA 路由(挂载到 /api/v1/rwa
pub fn rwa_router<S>() -> Router<S>
where
S: Clone + Send + Sync + 'static,
{
Router::new()
.route("/health", get(rwa_health))
.route("/jurisdictions", get(list_jurisdictions))
.route("/jurisdictions/:code", get(get_jurisdiction))
.route("/asset-types", get(list_asset_types))
.route("/validate", post(validate_asset))
.route("/contract-template", post(get_contract_template))
.route("/bilateral", get(list_bilateral_pairs))
.route("/bilateral/:from/:to", get(get_bilateral))
.route("/sharia-check", post(sharia_check))
.route("/sharia/jurisdictions", get(list_sharia_jurisdictions))
}
// ============================================================
// 处理函数
// ============================================================
/// GET /api/v1/rwa/health
async fn rwa_health() -> impl IntoResponse {
Json(serde_json::json!({
"success": true,
"status": "ok",
"module": "nac-rwa-legal-framework",
"version": "1.0.0",
"protocol": "NAC_Lens/4.0",
"components": {
"nac-rwa-legal-model": "active",
"nac-rwa-jurisdiction-adapters": "active",
"nac-rwa-contract-templates": "active",
"nac-rwa-bilateral-rules": "active",
"nac-rwa-sharia-compliance": "active"
},
"supported_jurisdictions": 11,
"sharia_jurisdictions": 3,
"bilateral_pairs": 12,
"total_tests_passed": 92
}))
}
/// GET /api/v1/rwa/jurisdictions
async fn list_jurisdictions() -> impl IntoResponse {
let adapters = get_all_adapters();
let list: Vec<serde_json::Value> = adapters.iter().map(|a| {
serde_json::json!({
"code": a.jurisdiction_code(),
"name": a.jurisdiction_name(),
"legal_system": format!("{:?}", a.legal_system()),
})
}).collect();
Json(serde_json::json!({
"success": true,
"data": list,
"total": list.len(),
"protocol": "NAC_Lens/4.0"
}))
}
/// GET /api/v1/rwa/jurisdictions/:code
async fn get_jurisdiction(Path(code): Path<String>) -> impl IntoResponse {
match find_adapter(&code) {
Some(adapter) => {
let real_estate_req = adapter.real_estate_registration_requirements();
let foreign_inv = adapter.foreign_investment_restrictions();
let tax = adapter.asset_transfer_tax_summary();
let dispute = adapter.preferred_dispute_resolution();
let reg_bodies = adapter.regulatory_bodies();
let asset_categories = adapter.supported_asset_categories();
(
StatusCode::OK,
Json(serde_json::json!({
"success": true,
"data": {
"code": adapter.jurisdiction_code(),
"name": adapter.jurisdiction_name(),
"legal_system": format!("{:?}", adapter.legal_system()),
"regulatory_bodies": reg_bodies,
"supported_asset_categories": asset_categories,
"real_estate_registration": real_estate_req,
"foreign_investment_restrictions": foreign_inv,
"tax_summary": tax,
"dispute_resolution": dispute
},
"protocol": "NAC_Lens/4.0"
})),
)
}
None => (
StatusCode::NOT_FOUND,
Json(serde_json::json!({
"success": false,
"error": format!("辖区 {} 不在支持范围内。支持CN/HK/SG/AE/US/EU-DE/EU-FR/JP/KR/AU/GB", code),
"protocol": "NAC_Lens/4.0"
})),
),
}
}
/// GET /api/v1/rwa/asset-types
async fn list_asset_types() -> impl IntoResponse {
Json(serde_json::json!({
"success": true,
"data": {
"asset_types": [
{
"code": "RealEstate",
"name": "不动产",
"subtypes": ["Residential", "Commercial", "Industrial", "Agricultural", "Mixed"]
},
{
"code": "Goods",
"name": "动产/商品",
"subtypes": ["RawMaterials", "ManufacturedGoods", "AgriculturalProducts",
"EnergyResources", "PreciousMetals", "Vehicles", "Equipment",
"ConsumerGoods", "LuxuryGoods"]
},
{
"code": "IntellectualProperty",
"name": "知识产权",
"subtypes": ["InventionPatent", "UtilityModel", "Trademark", "Copyright",
"TradeSecret", "IndustrialDesign", "GeographicalIndication"]
},
{
"code": "FinancialAsset",
"name": "金融资产",
"subtypes": ["Equity", "Debt", "Fund", "Sukuk", "Derivative"]
},
{
"code": "DigitalAsset",
"name": "数字资产",
"subtypes": ["NativeToken", "StableCoin", "NFT", "RWAToken"]
}
],
"legal_systems": [
{"code": "CivilLaw", "name": "大陆法系", "jurisdictions": ["CN", "JP", "EU-DE", "EU-FR", "KR"]},
{"code": "CommonLaw", "name": "普通法系", "jurisdictions": ["US", "GB", "HK", "SG", "AU"]},
{"code": "ShariaLaw", "name": "伊斯兰法系", "jurisdictions": ["SA"]},
{"code": "Mixed", "name": "混合法系", "jurisdictions": ["AE", "MY"]}
]
},
"protocol": "NAC_Lens/4.0"
}))
}
/// POST /api/v1/rwa/validate
async fn validate_asset(Json(req): Json<ValidateRequest>) -> impl IntoResponse {
let adapter = match find_adapter(&req.jurisdiction) {
Some(a) => a,
None => {
return (
StatusCode::BAD_REQUEST,
Json(serde_json::json!({
"success": false,
"error": format!("不支持的辖区:{}。支持CN/HK/SG/AE/US/EU-DE/EU-FR/JP/KR/AU/GB", req.jurisdiction),
"protocol": "NAC_Lens/4.0"
})),
);
}
};
// 构建资产类别用于验证
let asset_category = match req.asset_type.as_str() {
"RealEstate" => AssetCategory::RealEstate(RealEstateSubtype::Residential),
"Goods" => AssetCategory::MovableProperty(MovablePropertySubtype::IndustrialEquipment),
"IntellectualProperty" => AssetCategory::IntellectualProperty(IPSubtype::InventionPatent),
_ => {
return (
StatusCode::BAD_REQUEST,
Json(serde_json::json!({
"success": false,
"error": format!("不支持的资产类型:{}。支持RealEstate / Goods / IntellectualProperty", req.asset_type),
"protocol": "NAC_Lens/4.0"
})),
);
}
};
let is_foreign = req.is_foreign_entity.unwrap_or(false);
let validation = adapter.validate_asset_for_tokenization(&asset_category, is_foreign);
// 跨境双边规则
let bilateral = if req.is_cross_border.unwrap_or(false) {
req.target_jurisdiction.as_ref().and_then(|target| {
get_bilateral_matrix(&req.jurisdiction, target)
.map(|r| serde_json::to_value(r).unwrap_or_default())
})
} else {
None
};
let real_estate_req = adapter.real_estate_registration_requirements();
let tax = adapter.asset_transfer_tax_summary();
(
StatusCode::OK,
Json(serde_json::json!({
"success": true,
"data": {
"jurisdiction": adapter.jurisdiction_code(),
"jurisdiction_name": adapter.jurisdiction_name(),
"legal_system": format!("{:?}", adapter.legal_system()),
"asset_type": req.asset_type,
"is_foreign_entity": is_foreign,
"validation_result": format!("{:?}", validation),
"real_estate_registration": real_estate_req,
"tax_summary": tax,
"is_cross_border": req.is_cross_border.unwrap_or(false),
"bilateral_rules": bilateral,
"nac_chain_id": 5132611,
"protocol": "NAC_Lens/4.0"
},
"protocol": "NAC_Lens/4.0"
})),
)
}
/// POST /api/v1/rwa/contract-template
async fn get_contract_template(Json(req): Json<ContractTemplateRequest>) -> impl IntoResponse {
match req.contract_type.as_str() {
"real_estate" => {
match get_real_estate_template(&req.seller_jurisdiction) {
Some(t) => (StatusCode::OK, Json(serde_json::json!({
"success": true,
"data": { "contract_type": "real_estate", "template": t },
"protocol": "NAC_Lens/4.0"
}))),
None => (StatusCode::NOT_FOUND, Json(serde_json::json!({
"success": false,
"error": format!("辖区 {} 暂无不动产合约模板", req.seller_jurisdiction),
"protocol": "NAC_Lens/4.0"
}))),
}
}
"goods_trade" => {
let buyer = req.buyer_jurisdiction.as_deref().unwrap_or("INTL");
match get_goods_trade_template(&req.seller_jurisdiction, buyer) {
Some(t) => (StatusCode::OK, Json(serde_json::json!({
"success": true,
"data": { "contract_type": "goods_trade", "template": t },
"protocol": "NAC_Lens/4.0"
}))),
None => (StatusCode::NOT_FOUND, Json(serde_json::json!({
"success": false,
"error": format!("贸易对 {}-{} 暂无商品合约模板", req.seller_jurisdiction, buyer),
"protocol": "NAC_Lens/4.0"
}))),
}
}
"ip_license" => {
let licensee = req.buyer_jurisdiction.as_deref().unwrap_or("INTL");
match get_ip_license_template(&req.seller_jurisdiction, licensee) {
Some(t) => (StatusCode::OK, Json(serde_json::json!({
"success": true,
"data": { "contract_type": "ip_license", "template": t },
"protocol": "NAC_Lens/4.0"
}))),
None => (StatusCode::NOT_FOUND, Json(serde_json::json!({
"success": false,
"error": format!("辖区 {} 暂无知识产权许可合约模板", req.seller_jurisdiction),
"protocol": "NAC_Lens/4.0"
}))),
}
}
_ => (StatusCode::BAD_REQUEST, Json(serde_json::json!({
"success": false,
"error": "不支持的合约类型。支持real_estate / goods_trade / ip_license",
"protocol": "NAC_Lens/4.0"
}))),
}
}
/// GET /api/v1/rwa/bilateral/:from/:to
async fn get_bilateral(Path((from, to)): Path<(String, String)>) -> impl IntoResponse {
match get_bilateral_matrix(&from, &to) {
Some(rules) => (StatusCode::OK, Json(serde_json::json!({
"success": true,
"data": rules,
"protocol": "NAC_Lens/4.0"
}))),
None => {
let pairs = list_available_pairs();
let pair_list: Vec<String> = pairs.iter().map(|(a, b)| format!("{}-{}", a, b)).collect();
(StatusCode::NOT_FOUND, Json(serde_json::json!({
"success": false,
"error": format!("暂无 {}-{} 双边贸易规则", from, to),
"available_pairs": pair_list,
"protocol": "NAC_Lens/4.0"
})))
}
}
}
/// GET /api/v1/rwa/bilateral — 列出所有可用双边贸易规则对
async fn list_bilateral_pairs() -> impl IntoResponse {
let pairs = list_available_pairs();
let pair_list: Vec<serde_json::Value> = pairs.iter().map(|(a, b)| serde_json::json!({
"from": a,
"to": b,
"pair": format!("{}-{}", a, b)
})).collect();
(StatusCode::OK, Json(serde_json::json!({
"success": true,
"total": pair_list.len(),
"data": {
"available_pairs": pair_list
},
"protocol": "NAC_Lens/4.0"
})))
}
/// POST /api/v1/rwa/sharia-check
async fn sharia_check(Json(req): Json<ShariaCheckRequest>) -> impl IntoResponse {
let structure = match req.structure.as_str() {
"Murabaha" => IslamicFinanceStructure::Murabaha,
"Ijara" => IslamicFinanceStructure::Ijara,
"IjaraWaIqtina" => IslamicFinanceStructure::IjaraWaIqtina,
"Musharakah" => IslamicFinanceStructure::Musharakah,
"Mudarabah" => IslamicFinanceStructure::Mudarabah,
"Sukuk" => IslamicFinanceStructure::Sukuk,
"Istisna" => IslamicFinanceStructure::Istisna,
"Salam" => IslamicFinanceStructure::Salam,
"Wakalah" => IslamicFinanceStructure::Wakalah,
"Takaful" => IslamicFinanceStructure::Takaful,
_ => return (StatusCode::BAD_REQUEST, Json(serde_json::json!({
"success": false,
"error": format!("不支持的金融结构:{}。支持Murabaha/Ijara/IjaraWaIqtina/Musharakah/Mudarabah/Sukuk/Istisna/Salam/Wakalah/Takaful", req.structure),
"protocol": "NAC_Lens/4.0"
}))),
};
let framework = match get_sharia_framework(&req.jurisdiction) {
Some(f) => f,
None => return (StatusCode::NOT_FOUND, Json(serde_json::json!({
"success": false,
"error": format!("辖区 {} 不在 Sharia 合规覆盖范围内。支持AE/SA/MY", req.jurisdiction),
"protocol": "NAC_Lens/4.0"
}))),
};
let result = validate_sharia_compliance(
&req.jurisdiction,
&structure,
req.has_interest,
req.has_excessive_uncertainty,
req.involves_haram_goods,
);
(StatusCode::OK, Json(serde_json::json!({
"success": true,
"data": {
"jurisdiction": req.jurisdiction,
"jurisdiction_name": framework.jurisdiction_name,
"structure": req.structure,
"is_sharia_compliant": result.is_compliant,
"violations": result.violations,
"recommendations": result.recommendations,
"requires_fatwa": result.requires_fatwa,
"regulatory_body": framework.regulatory_body,
"sharia_board_required": framework.sharia_board.required,
"minimum_scholars": framework.sharia_board.minimum_scholars,
"supported_structures": framework.supported_structures.iter().map(|s| format!("{:?}", s)).collect::<Vec<_>>()
},
"protocol": "NAC_Lens/4.0"
})))
}
/// GET /api/v1/rwa/sharia/jurisdictions
async fn list_sharia_jurisdictions() -> impl IntoResponse {
let mut result = Vec::new();
for j in &["AE", "SA", "MY"] {
if let Some(f) = get_sharia_framework(j) {
result.push(serde_json::json!({
"code": f.jurisdiction,
"name": f.jurisdiction_name,
"regulatory_body": f.regulatory_body,
"supported_structures": f.supported_structures.iter().map(|s| format!("{:?}", s)).collect::<Vec<_>>(),
"sharia_board_required": f.sharia_board.required,
"fatwa_required": f.sharia_board.fatwa_required,
"minimum_scholars": f.sharia_board.minimum_scholars
}));
}
}
Json(serde_json::json!({
"success": true,
"data": result,
"protocol": "NAC_Lens/4.0"
}))
}