321 lines
14 KiB
TypeScript
321 lines
14 KiB
TypeScript
/**
|
||
* NAC知识引擎 — 监管动态AI自动更新模块 (v17)
|
||
*
|
||
* 功能:
|
||
* 1. 模拟监管机构RSS/API数据源(六辖区)
|
||
* 2. AI分析新法规内容,生成规则更新建议
|
||
* 3. 保存待审核的更新建议到MongoDB
|
||
* 4. 触发Webhook/系统通知告警
|
||
*/
|
||
|
||
import { getMongoDb, COLLECTIONS } from "./mongodb";
|
||
import { invokeLLM } from "./_core/llm";
|
||
import { notifyOwner } from "./_core/notification";
|
||
|
||
// ─── 监管数据源定义 ────────────────────────────────────────────────
|
||
export interface RegulatorySource {
|
||
id: string;
|
||
jurisdiction: string;
|
||
name: string;
|
||
nameZh: string;
|
||
url: string;
|
||
type: "rss" | "api" | "webpage";
|
||
active: boolean;
|
||
lastChecked?: Date;
|
||
}
|
||
|
||
export interface RegulatoryUpdate {
|
||
_id?: any;
|
||
sourceId: string;
|
||
jurisdiction: string;
|
||
title: string;
|
||
summary: string;
|
||
url: string;
|
||
publishedAt: Date;
|
||
fetchedAt: Date;
|
||
status: "pending" | "analyzed" | "applied" | "dismissed";
|
||
aiAnalysis?: {
|
||
relevance: number; // 0-1 相关度
|
||
affectedAssetTypes: string[];
|
||
suggestedRuleUpdates: Array<{
|
||
action: "create" | "update" | "deprecate";
|
||
ruleName: string;
|
||
description: string;
|
||
reason: string;
|
||
}>;
|
||
summary: string;
|
||
urgency: "low" | "medium" | "high" | "critical";
|
||
};
|
||
createdAt: Date;
|
||
updatedAt: Date;
|
||
}
|
||
|
||
// ─── 预定义监管数据源 ──────────────────────────────────────────────
|
||
export const REGULATORY_SOURCES: RegulatorySource[] = [
|
||
{ id: "cn-csrc", jurisdiction: "CN", name: "CSRC", nameZh: "中国证监会", url: "http://www.csrc.gov.cn/csrc/c101954/zfxxgk_zdgk.shtml", type: "webpage", active: true },
|
||
{ id: "cn-pboc", jurisdiction: "CN", name: "PBOC", nameZh: "中国人民银行", url: "http://www.pbc.gov.cn/goutongjiaoliu/113456/113469/index.html", type: "webpage", active: true },
|
||
{ id: "hk-sfc", jurisdiction: "HK", name: "SFC Hong Kong", nameZh: "香港证监会", url: "https://www.sfc.hk/en/News-and-announcements/Policy-statements-and-announcements", type: "webpage", active: true },
|
||
{ id: "sg-mas", jurisdiction: "SG", name: "MAS Singapore", nameZh: "新加坡金管局", url: "https://www.mas.gov.sg/news", type: "webpage", active: true },
|
||
{ id: "us-sec", jurisdiction: "US", name: "SEC", nameZh: "美国证监会", url: "https://www.sec.gov/news/pressreleases.shtml", type: "webpage", active: true },
|
||
{ id: "eu-esma", jurisdiction: "EU", name: "ESMA", nameZh: "欧洲证券和市场管理局", url: "https://www.esma.europa.eu/press-news/esma-news", type: "webpage", active: true },
|
||
{ id: "ae-dfsa", jurisdiction: "AE", name: "DFSA Dubai", nameZh: "迪拜金融服务管理局", url: "https://www.dfsa.ae/news-and-events/press-releases", type: "webpage", active: true },
|
||
];
|
||
|
||
// ─── 模拟监管新闻数据(用于演示,实际应从RSS/API获取)────────────
|
||
const MOCK_REGULATORY_NEWS: Array<Omit<RegulatoryUpdate, "_id" | "aiAnalysis" | "createdAt" | "updatedAt">> = [
|
||
{
|
||
sourceId: "cn-csrc",
|
||
jurisdiction: "CN",
|
||
title: "证监会发布《关于加强证券公司和公募基金监管加快推进建设一流投资银行和投资机构的意见》",
|
||
summary: "证监会发布新规,要求证券公司和公募基金加强合规管理,对数字资产相关业务实施更严格的信息披露要求。",
|
||
url: "http://www.csrc.gov.cn/csrc/c101954/zfxxgk_zdgk.shtml",
|
||
publishedAt: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000),
|
||
fetchedAt: new Date(),
|
||
status: "pending",
|
||
},
|
||
{
|
||
sourceId: "hk-sfc",
|
||
jurisdiction: "HK",
|
||
title: "SFC Updates Guidelines for Virtual Asset Trading Platforms",
|
||
summary: "The SFC has updated its guidelines for virtual asset trading platforms, introducing new requirements for custody arrangements and investor protection measures.",
|
||
url: "https://www.sfc.hk/en/News-and-announcements/Policy-statements-and-announcements",
|
||
publishedAt: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000),
|
||
fetchedAt: new Date(),
|
||
status: "pending",
|
||
},
|
||
{
|
||
sourceId: "sg-mas",
|
||
jurisdiction: "SG",
|
||
title: "MAS Consults on Proposed Regulatory Measures for Digital Payment Token Services",
|
||
summary: "MAS is consulting on proposed measures to enhance consumer protection for digital payment token services, including new requirements for stablecoin issuers.",
|
||
url: "https://www.mas.gov.sg/news",
|
||
publishedAt: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000),
|
||
fetchedAt: new Date(),
|
||
status: "pending",
|
||
},
|
||
{
|
||
sourceId: "eu-esma",
|
||
jurisdiction: "EU",
|
||
title: "ESMA Publishes Final Guidelines on MiCA Crypto-Asset Service Providers",
|
||
summary: "ESMA has published final guidelines on the Markets in Crypto-Assets Regulation (MiCA) for crypto-asset service providers, covering authorization requirements and ongoing obligations.",
|
||
url: "https://www.esma.europa.eu/press-news/esma-news",
|
||
publishedAt: new Date(Date.now() - 5 * 24 * 60 * 60 * 1000),
|
||
fetchedAt: new Date(),
|
||
status: "pending",
|
||
},
|
||
{
|
||
sourceId: "ae-dfsa",
|
||
jurisdiction: "AE",
|
||
title: "DFSA Introduces New Framework for Real Estate Tokenization",
|
||
summary: "The DFSA has introduced a new regulatory framework for real estate tokenization in the DIFC, establishing requirements for token issuers and trading platforms.",
|
||
url: "https://www.dfsa.ae/news-and-events/press-releases",
|
||
publishedAt: new Date(Date.now() - 4 * 24 * 60 * 60 * 1000),
|
||
fetchedAt: new Date(),
|
||
status: "pending",
|
||
},
|
||
];
|
||
|
||
// ─── AI分析监管更新 ────────────────────────────────────────────────
|
||
export async function analyzeRegulatoryUpdate(update: RegulatoryUpdate): Promise<RegulatoryUpdate["aiAnalysis"]> {
|
||
try {
|
||
const prompt = `你是NAC公链的合规专家。请分析以下监管动态,评估其对RWA(现实世界资产)上链合规的影响。
|
||
|
||
监管机构:${update.jurisdiction} - ${REGULATORY_SOURCES.find(s => s.id === update.sourceId)?.nameZh || update.sourceId}
|
||
标题:${update.title}
|
||
摘要:${update.summary}
|
||
|
||
请返回JSON格式的分析结果,包含:
|
||
1. relevance: 与RWA合规的相关度(0-1浮点数)
|
||
2. affectedAssetTypes: 受影响的资产类型数组(从以下选择:RealEstate, Securities, DigitalToken, Commodity, IntellectualProperty, Equipment)
|
||
3. suggestedRuleUpdates: 建议的规则更新数组,每项包含 action(create/update/deprecate), ruleName, description, reason
|
||
4. summary: 中文摘要(100字以内)
|
||
5. urgency: 紧急程度(low/medium/high/critical)`;
|
||
|
||
const response = await invokeLLM({
|
||
messages: [
|
||
{ role: "system", content: "你是NAC公链合规专家,专注于RWA资产上链合规分析。请严格返回JSON格式。" },
|
||
{ role: "user", content: prompt },
|
||
],
|
||
response_format: {
|
||
type: "json_schema",
|
||
json_schema: {
|
||
name: "regulatory_analysis",
|
||
strict: true,
|
||
schema: {
|
||
type: "object",
|
||
properties: {
|
||
relevance: { type: "number" },
|
||
affectedAssetTypes: { type: "array", items: { type: "string" } },
|
||
suggestedRuleUpdates: {
|
||
type: "array",
|
||
items: {
|
||
type: "object",
|
||
properties: {
|
||
action: { type: "string", enum: ["create", "update", "deprecate"] },
|
||
ruleName: { type: "string" },
|
||
description: { type: "string" },
|
||
reason: { type: "string" },
|
||
},
|
||
required: ["action", "ruleName", "description", "reason"],
|
||
additionalProperties: false,
|
||
},
|
||
},
|
||
summary: { type: "string" },
|
||
urgency: { type: "string", enum: ["low", "medium", "high", "critical"] },
|
||
},
|
||
required: ["relevance", "affectedAssetTypes", "suggestedRuleUpdates", "summary", "urgency"],
|
||
additionalProperties: false,
|
||
},
|
||
},
|
||
},
|
||
});
|
||
|
||
const rawContent = response.choices?.[0]?.message?.content;
|
||
if (!rawContent) return undefined;
|
||
const content = typeof rawContent === "string" ? rawContent : JSON.stringify(rawContent);
|
||
return JSON.parse(content) as RegulatoryUpdate["aiAnalysis"];
|
||
} catch (e) {
|
||
console.error("[regulatoryMonitor] AI分析失败:", e);
|
||
return {
|
||
relevance: 0.5,
|
||
affectedAssetTypes: ["DigitalToken"],
|
||
suggestedRuleUpdates: [],
|
||
summary: "AI分析暂时不可用,请人工审核",
|
||
urgency: "medium",
|
||
};
|
||
}
|
||
}
|
||
|
||
// ─── 获取监管更新列表 ──────────────────────────────────────────────
|
||
export async function getRegulatoryUpdates(filters?: {
|
||
jurisdiction?: string;
|
||
status?: string;
|
||
limit?: number;
|
||
}): Promise<RegulatoryUpdate[]> {
|
||
const db = await getMongoDb();
|
||
if (!db) return [];
|
||
|
||
const query: any = {};
|
||
if (filters?.jurisdiction) query.jurisdiction = filters.jurisdiction;
|
||
if (filters?.status) query.status = filters.status;
|
||
|
||
return db.collection("regulatory_updates")
|
||
.find(query)
|
||
.sort({ fetchedAt: -1 })
|
||
.limit(filters?.limit || 50)
|
||
.toArray() as unknown as RegulatoryUpdate[];
|
||
}
|
||
|
||
// ─── 触发监管数据抓取(模拟) ──────────────────────────────────────
|
||
export async function fetchRegulatoryUpdates(jurisdiction?: string): Promise<{
|
||
fetched: number;
|
||
analyzed: number;
|
||
newUpdates: number;
|
||
}> {
|
||
const db = await getMongoDb();
|
||
if (!db) return { fetched: 0, analyzed: 0, newUpdates: 0 };
|
||
|
||
const collection = db.collection("regulatory_updates");
|
||
let fetched = 0;
|
||
let analyzed = 0;
|
||
let newUpdates = 0;
|
||
|
||
// 过滤数据源
|
||
const sources = jurisdiction
|
||
? MOCK_REGULATORY_NEWS.filter(n => n.jurisdiction === jurisdiction)
|
||
: MOCK_REGULATORY_NEWS;
|
||
|
||
for (const newsItem of sources) {
|
||
fetched++;
|
||
|
||
// 检查是否已存在(按标题去重)
|
||
const existing = await collection.findOne({ title: newsItem.title });
|
||
if (existing) continue;
|
||
|
||
// 创建新记录
|
||
const update: RegulatoryUpdate = {
|
||
...newsItem,
|
||
createdAt: new Date(),
|
||
updatedAt: new Date(),
|
||
};
|
||
|
||
// AI分析
|
||
const aiAnalysis = await analyzeRegulatoryUpdate(update);
|
||
update.aiAnalysis = aiAnalysis;
|
||
update.status = "analyzed";
|
||
analyzed++;
|
||
|
||
// 保存到数据库
|
||
await collection.insertOne(update);
|
||
newUpdates++;
|
||
|
||
// 高优先级更新触发通知
|
||
if (aiAnalysis && (aiAnalysis.urgency === "high" || aiAnalysis.urgency === "critical")) {
|
||
try {
|
||
await notifyOwner({
|
||
title: `[${aiAnalysis.urgency.toUpperCase()}] 监管动态预警:${newsItem.jurisdiction}`,
|
||
content: `${newsItem.title}\n\n${aiAnalysis.summary}\n\n建议更新规则数:${aiAnalysis.suggestedRuleUpdates.length}`,
|
||
});
|
||
} catch (e) {
|
||
console.error("[regulatoryMonitor] 通知发送失败:", e);
|
||
}
|
||
}
|
||
}
|
||
|
||
return { fetched, analyzed, newUpdates };
|
||
}
|
||
|
||
// ─── 应用规则更新建议 ──────────────────────────────────────────────
|
||
export async function applyRuleUpdateSuggestion(
|
||
updateId: string,
|
||
suggestionIndex: number,
|
||
operatorEmail: string
|
||
): Promise<{ success: boolean; message: string }> {
|
||
const db = await getMongoDb();
|
||
if (!db) return { success: false, message: "数据库连接失败" };
|
||
|
||
const { ObjectId } = await import("mongodb");
|
||
const update = await db.collection("regulatory_updates").findOne({ _id: new ObjectId(updateId) }) as unknown as RegulatoryUpdate;
|
||
if (!update) return { success: false, message: "未找到该监管更新" };
|
||
|
||
const suggestion = update.aiAnalysis?.suggestedRuleUpdates?.[suggestionIndex];
|
||
if (!suggestion) return { success: false, message: "未找到该建议" };
|
||
|
||
// 根据建议类型执行操作
|
||
if (suggestion.action === "create") {
|
||
await db.collection(COLLECTIONS.COMPLIANCE_RULES).insertOne({
|
||
jurisdiction: update.jurisdiction,
|
||
assetType: update.aiAnalysis?.affectedAssetTypes?.[0] || "DigitalToken",
|
||
ruleName: suggestion.ruleName,
|
||
description: suggestion.description,
|
||
status: "active",
|
||
required: false,
|
||
tags: ["ai-generated", "regulatory-update"],
|
||
source: update.url,
|
||
sourceUpdateId: updateId,
|
||
createdAt: new Date(),
|
||
updatedAt: new Date(),
|
||
createdBy: operatorEmail,
|
||
});
|
||
}
|
||
|
||
// 标记更新为已应用
|
||
await db.collection("regulatory_updates").updateOne(
|
||
{ _id: new ObjectId(updateId) },
|
||
{ $set: { status: "applied", updatedAt: new Date() } }
|
||
);
|
||
|
||
return { success: true, message: `已${suggestion.action === "create" ? "创建" : "更新"}规则:${suggestion.ruleName}` };
|
||
}
|
||
|
||
// ─── 忽略监管更新 ─────────────────────────────────────────────────
|
||
export async function dismissRegulatoryUpdate(updateId: string): Promise<void> {
|
||
const db = await getMongoDb();
|
||
if (!db) return;
|
||
const { ObjectId } = await import("mongodb");
|
||
await db.collection("regulatory_updates").updateOne(
|
||
{ _id: new ObjectId(updateId) },
|
||
{ $set: { status: "dismissed", updatedAt: new Date() } }
|
||
);
|
||
}
|