NAC_Blockchain/nac-ai-valuation/valuation-ui/server.js

410 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use strict';
/**
* NAC AI 资产估值问答界面 - 服务端
* 职责:
* 1. 静态文件服务index.html
* 2. /api/v4/* → 代理到估值引擎 v4.0:3003
* 3. /api/inference/stream → 代理到推理引擎(:3001支持 SSE 流式输出
* 4. /api/v4/xtzh-price → XTZH 实时价格(带本地缓存)
* 5. /api/inference/ask → 普通问答(非流式)
*/
const express = require('express');
const http = require('http');
const path = require('path');
const app = express();
const PORT = process.env.PORT || 3005;
// ============================================================
// 中间件
// ============================================================
app.use(express.json({ limit: '2mb' }));
app.use(express.static(path.join(__dirname, 'public')));
// CORS内部服务
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Authorization');
if (req.method === 'OPTIONS') return res.sendStatus(204);
next();
});
// ============================================================
// 配置
// ============================================================
const VALUATION_ENGINE = {
host: '127.0.0.1',
port: 3003,
timeout: 30000
};
const INFERENCE_ENGINE = {
host: '127.0.0.1',
port: 3001,
timeout: 60000
};
// XTZH 价格缓存5分钟
let xtzhPriceCache = null;
let xtzhPriceCacheTime = 0;
const XTZH_CACHE_TTL = 5 * 60 * 1000;
// ============================================================
// XTZH 实时价格
// ============================================================
app.get('/api/v4/xtzh-price', async (req, res) => {
try {
// 检查缓存
if (xtzhPriceCache && Date.now() - xtzhPriceCacheTime < XTZH_CACHE_TTL) {
return res.json(xtzhPriceCache);
}
// 从估值引擎获取
const data = await proxyRequest('GET', VALUATION_ENGINE, '/api/v4/xtzh-price', null, 5000);
xtzhPriceCache = data;
xtzhPriceCacheTime = Date.now();
return res.json(data);
} catch(e) {
// 降级:返回静态价格
const fallback = {
usd: 4.3944,
source: 'NAC_SDR_MODEL_V4_FALLBACK',
goldCoverage: 1.25,
sdrBasket: { USD: 0.5813, EUR: 0.1886, CNY: 0.1042, JPY: 0.0895, GBP: 0.0864 },
timestamp: new Date().toISOString(),
note: '估值引擎离线,使用最后已知价格'
};
return res.json(fallback);
}
});
// ============================================================
// 估值引擎代理
// ============================================================
app.post('/api/v4/valuate', async (req, res) => {
try {
const data = await proxyRequest('POST', VALUATION_ENGINE, '/api/v4/valuate', req.body, VALUATION_ENGINE.timeout);
return res.json(data);
} catch(e) {
console.error('[ValuationProxy] Error:', e.message);
return res.status(502).json({ error: '估值引擎暂时不可用', detail: e.message });
}
});
// ============================================================
// 推理引擎 SSE 流式输出
// ============================================================
app.post('/api/inference/stream', (req, res) => {
const body = JSON.stringify(req.body);
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.setHeader('X-Accel-Buffering', 'no');
const options = {
hostname: INFERENCE_ENGINE.host,
port: INFERENCE_ENGINE.port,
path: '/api/inference/stream',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(body)
},
timeout: INFERENCE_ENGINE.timeout
};
const proxyReq = http.request(options, (proxyRes) => {
proxyRes.on('data', chunk => {
if (!res.writableEnded) res.write(chunk);
});
proxyRes.on('end', () => {
if (!res.writableEnded) {
res.write('data: [DONE]\n\n');
res.end();
}
});
proxyRes.on('error', (err) => {
if (!res.writableEnded) {
res.write(`data: ${JSON.stringify({ type: 'error', message: err.message })}\n\n`);
res.end();
}
});
});
proxyReq.on('error', (err) => {
console.error('[InferenceSSE] Proxy error:', err.message);
if (!res.writableEnded) {
// 降级:使用本地估值问答
handleLocalValuationQA(req.body, res);
}
});
proxyReq.on('timeout', () => {
proxyReq.destroy();
if (!res.writableEnded) {
res.write(`data: ${JSON.stringify({ type: 'error', message: '推理引擎响应超时' })}\n\n`);
res.end();
}
});
proxyReq.write(body);
proxyReq.end();
req.on('close', () => {
if (!proxyReq.destroyed) proxyReq.destroy();
});
});
// ============================================================
// 本地估值问答降级处理(推理引擎不可用时)
// ============================================================
function handleLocalValuationQA(body, res) {
const question = body.question || '';
const jurisdiction = body.jurisdiction || 'HK';
const assetType = body.assetType || 'real_estate';
const mode = body.mode || 'valuation';
const lang = body.language || 'zh';
let answer = '';
if (lang === 'zh') {
if (question.includes('XTZH') || question.includes('定价') || question.includes('SDR')) {
answer = generateXTZHExplanation(jurisdiction, lang);
} else if (question.includes('上链') || question.includes('TOKEN') || question.includes('流程')) {
answer = generateOnChainGuide(jurisdiction, lang);
} else if (question.includes('方法') || question.includes('市场法') || question.includes('收益法')) {
answer = generateMethodologyExplanation(assetType, lang);
} else if (question.includes('辖区') || question.includes('对比') || question.includes('比较')) {
answer = generateJurisdictionComparison(lang);
} else {
answer = generateGeneralValuationResponse(question, jurisdiction, assetType, lang);
}
} else {
answer = generateGeneralValuationResponse(question, jurisdiction, assetType, lang);
}
// 流式输出(按行分块,确保 Markdown 表格行完整传输)
const lines = answer.split('\n');
let i = 0;
const interval = setInterval(() => {
if (i >= lines.length) {
clearInterval(interval);
res.write('data: [DONE]\n\n');
res.end();
return;
}
// 每行加上换行符发送
const lineContent = lines[i] + (i < lines.length - 1 ? '\n' : '');
res.write(`data: ${JSON.stringify({ type: 'chunk', content: lineContent })}\n\n`);
i++;
}, 40);
}
function generateXTZHExplanation(jurisdiction, lang) {
return `## XTZH 统一定价机制
**XTZH新资产链稳定权益代币** 采用SDR锚定篮子动态计算确保全球资产估值的一致性和稳定性。
### SDR五货币篮子权重
| 货币 | 权重 | 说明 |
|------|------|------|
| 美元 USD | 58.13% | 全球储备主导货币 |
| 欧元 EUR | 18.86% | 欧洲区域货币 |
| 人民币 CNY | 10.42% | 新兴市场代表 |
| 日元 JPY | 8.95% | 亚太货币 |
| 英镑 GBP | 8.64% | 英联邦货币 |
### 当前定价参数
- **XTZH/USD 汇率**$4.3944
- **黄金覆盖率**125%(超额储备保障)
- **定价模型**NAC_SDR_MODEL_V4
- **更新频率**:实时(每分钟)
### 80%质押规则
> 资产估值 × 80% = 可发行权益化代币数量
这确保了链上资产的超额抵押,保障代币持有者权益。`;
}
function generateOnChainGuide(jurisdiction, lang) {
return `## 资产上链完整流程
### 第一步AI估值
1. 提交资产描述(类型、位置、面积、市值)
2. AI引擎自动识别GNACS分类
3. 多方法融合估值(市场法/收益法/成本法)
4. 输出 **finalXTZH** 和置信度
### 第二步:合规审批
- 辖区合规验证(${jurisdiction}监管要求)
- KYC/AML核查
- 资产所有权证明
- 合规评分 ≥ 80分方可上链
### 第三步TOKEN生成
- 智能合约部署Charter语言NVM虚拟机
- GNACS编码注册
- ACC-20协议代币铸造
### 第四步:权证发行
- 资产权证NFT生成
- 链上存证CBPP共识确认
- 权证与实物资产绑定
### 第五步:代币流通
- 80%质押规则执行
- 二级市场流通
- 持续合规监控
> **注意**完整上链流程通常需要3-7个工作日具体时间取决于辖区审批速度。`;
}
function generateMethodologyExplanation(assetType, lang) {
const methods = {
real_estate: `## 不动产估值方法论
### 市场比较法权重40%
- 选取3-5个可比交易案例
- 调整因素:位置、面积、楼层、装修
- 适用:住宅、商业地产
### 收益资本化法权重35%
- 净营业收入 ÷ 资本化率
- 资本化率参考HK 3-4%SG 3.5-4.5%AE 6-8%
- 适用:商业地产、租赁物业
### 成本法权重25%
- 土地价值 + 建筑重置成本 - 折旧
- 适用:特殊用途物业、新建物业
### XTZH换算
> finalUSD ÷ XTZH价格($4.3944) = finalXTZH`,
financial: `## 金融资产估值方法论
### DCF折现现金流法权重50%
- 预测未来现金流
- 折现率WACC + 风险溢价
- 适用:股权、债券、基金
### 市场乘数法权重30%
- P/E、P/B、EV/EBITDA
- 对标可比公司
### 净资产法权重20%
- 账面价值调整
- 适用:清算场景`
};
return methods[assetType] || methods.real_estate;
}
function generateJurisdictionComparison(lang) {
return `## 主要辖区估值对比
| 辖区 | 资本化率 | 辖区乘数 | 主要法规 | 合规难度 |
|------|---------|---------|---------|---------|
| 🇭🇰 香港 | 3.0-4.0% | 1.05 | HKMA/SFC | ★★★☆☆ |
| 🇸🇬 新加坡 | 3.5-4.5% | 1.03 | MAS | ★★★☆☆ |
| 🇦🇪 阿联酋 | 6.0-8.0% | 1.08 | ADGM/DIFC | ★★☆☆☆ |
| 🇺🇸 美国 | 4.0-6.0% | 0.95 | SEC/FINRA | ★★★★★ |
| 🇬🇧 英国 | 4.5-5.5% | 0.98 | FCA | ★★★★☆ |
| 🇯🇵 日本 | 3.0-4.5% | 1.00 | FSA | ★★★★☆ |
| 🇨🇳 中国 | 4.0-5.0% | 0.90 | CBIRC | ★★★★★ |
> **辖区乘数**对最终XTZH估值的调整系数反映各辖区市场流动性和监管环境。`;
}
function generateGeneralValuationResponse(question, jurisdiction, assetType, lang) {
return `## NAC AI 资产估值引擎
感谢您的咨询我是NAC NewAssetChain的AI资产估值专家。
**当前配置:**
- 辖区:${jurisdiction}
- 资产类型:${assetType}
- XTZH价格$4.3944 USDSDR锚定
**我可以帮您:**
1. **资产估值** - 描述您的资产类型、位置、面积、市值我将给出XTZH估值
2. **XTZH定价** - 解释SDR锚定机制和当前价格计算
3. **上链流程** - 从估值到TOKEN生成的完整流程
4. **辖区对比** - 不同司法辖区的估值差异
5. **方法论** - 市场法/收益法/成本法详解
请告诉我您想了解的具体问题,或直接描述您的资产信息!`;
}
// ============================================================
// 工具函数HTTP代理请求
// ============================================================
function proxyRequest(method, target, path, body, timeout) {
return new Promise((resolve, reject) => {
const bodyStr = body ? JSON.stringify(body) : null;
const options = {
hostname: target.host,
port: target.port,
path: path,
method: method,
headers: {
'Content-Type': 'application/json',
...(bodyStr ? { 'Content-Length': Buffer.byteLength(bodyStr) } : {})
},
timeout: timeout || 10000
};
const req = http.request(options, (res) => {
let data = '';
res.on('data', chunk => data += chunk);
res.on('end', () => {
try {
resolve(JSON.parse(data));
} catch(e) {
reject(new Error('无效的JSON响应: ' + data.substring(0, 100)));
}
});
});
req.on('error', reject);
req.on('timeout', () => { req.destroy(); reject(new Error('请求超时')); });
if (bodyStr) req.write(bodyStr);
req.end();
});
}
// ============================================================
// 静态文件服务(主页)
// ============================================================
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
// 健康检查
app.get('/health', (req, res) => {
res.json({
status: 'ok',
service: 'nac-valuation-ui',
version: '1.0.0',
timestamp: new Date().toISOString(),
engines: {
valuation: `${VALUATION_ENGINE.host}:${VALUATION_ENGINE.port}`,
inference: `${INFERENCE_ENGINE.host}:${INFERENCE_ENGINE.port}`
}
});
});
// ============================================================
// 启动
// ============================================================
app.listen(PORT, '0.0.0.0', () => {
console.log(`[NAC Valuation UI] 服务启动 → http://0.0.0.0:${PORT}`);
console.log(`[NAC Valuation UI] 估值引擎 → ${VALUATION_ENGINE.host}:${VALUATION_ENGINE.port}`);
console.log(`[NAC Valuation UI] 推理引擎 → ${INFERENCE_ENGINE.host}:${INFERENCE_ENGINE.port}`);
});