From 28da7f6807574a5a057cbe00ef8e320a42ff579a Mon Sep 17 00:00:00 2001 From: hailin Date: Sun, 28 Dec 2025 23:29:04 -0800 Subject: [PATCH] =?UTF-8?q?feat(service-party-app):=20=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E6=97=B6=E8=87=AA=E5=8A=A8=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E5=88=B0=20Message=20Router=EF=BC=8C=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=9C=9F=E5=AE=9E=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 主要变更 ### 1. 启动时自动连接并注册 - main.ts: 添加 `connectAndRegisterToMessageRouter()` 函数 - 应用启动时自动连接到 Message Router 并注册为 temporary 角色 - 自动生成并持久化 partyId(使用 crypto.randomUUID) - 自动订阅会话事件 ### 2. 状态验证真实化 - appStore.ts: 重写 `checkAllServices()` 消息路由检测逻辑 - 不再只检测连接成功,而是: 1. 调用 isConnected() 检查连接状态 2. 调用 getPartyId() 检查是否已注册 3. 调用 getRegisteredParties() 从 Message Router 获取注册列表 4. 验证自己的 partyId 是否在列表中 - 状态显示更准确: - "未连接到 xxx" - 未连接 - "已连接但未注册" - 已连接但没注册 - "已注册 (在线)" - 完全正常 - "注册验证失败" - 注册了但验证失败 ### 3. 新增 IPC API - grpc:getPartyId - 获取当前 partyId - grpc:isConnected - 检查连接状态 - grpc:connect - 连接到 Message Router - grpc:register - 注册为参与方 ### 修改的文件 - electron/main.ts - electron/preload.ts - src/stores/appStore.ts - src/types/electron.d.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../service-party-app/electron/main.ts | 77 +++++++++++++++++-- .../service-party-app/electron/preload.ts | 13 ++++ .../service-party-app/src/stores/appStore.ts | 56 +++++++++++--- .../service-party-app/src/types/electron.d.ts | 8 ++ 4 files changed, 139 insertions(+), 15 deletions(-) diff --git a/backend/mpc-system/services/service-party-app/electron/main.ts b/backend/mpc-system/services/service-party-app/electron/main.ts index 73cd08f9..19adb1d3 100644 --- a/backend/mpc-system/services/service-party-app/electron/main.ts +++ b/backend/mpc-system/services/service-party-app/electron/main.ts @@ -1,6 +1,7 @@ import { app, BrowserWindow, ipcMain, shell, dialog } from 'electron'; import * as path from 'path'; import * as fs from 'fs'; +import * as crypto from 'crypto'; import express from 'express'; import { GrpcClient } from './modules/grpc-client'; import { DatabaseManager } from './modules/database'; @@ -83,14 +84,27 @@ function startHttpServer() { }); } +// 生成或获取持久化的 partyId +function getOrCreatePartyId(db: DatabaseManager): string { + const settings = db.getAllSettings(); + let partyId = settings['party_id']; + if (!partyId) { + // 生成一个新的 UUID 作为 partyId + partyId = crypto.randomUUID(); + db.setSetting('party_id', partyId); + console.log('Generated new partyId:', partyId); + } + return partyId; +} + // 初始化服务 async function initServices() { + // 初始化数据库 (必须首先初始化) + database = new DatabaseManager(); + // 初始化 gRPC 客户端 grpcClient = new GrpcClient(); - // 初始化数据库 - database = new DatabaseManager(); - // 初始化 Kava 交易服务 kavaTxService = new KavaTxService(KAVA_MAINNET_TX_CONFIG); @@ -102,6 +116,39 @@ async function initServices() { // 设置 IPC 处理器 setupIpcHandlers(); + + // 启动时自动连接并注册到 Message Router + await connectAndRegisterToMessageRouter(); +} + +// 连接并注册到 Message Router +async function connectAndRegisterToMessageRouter() { + if (!grpcClient || !database) { + console.error('gRPC client or database not initialized'); + return; + } + + try { + const settings = database.getAllSettings(); + const routerUrl = settings['message_router_url'] || 'mpc-grpc.szaiai.com:443'; + const partyId = getOrCreatePartyId(database); + const role = 'temporary'; // Service-Party-App 使用 temporary 角色 + + console.log(`Connecting to Message Router: ${routerUrl}...`); + await grpcClient.connect(routerUrl); + console.log('Connected to Message Router'); + + console.log(`Registering as party: ${partyId} (role: ${role})...`); + await grpcClient.registerParty(partyId, role); + console.log('Registered to Message Router successfully'); + + // 订阅会话事件 + grpcClient.subscribeSessionEvents(partyId); + console.log('Subscribed to session events'); + } catch (error) { + console.error('Failed to connect/register to Message Router:', (error as Error).message); + // 不抛出错误,允许应用继续启动,用户可以稍后手动重试 + } } // 设置 IPC 通信处理器 @@ -111,9 +158,9 @@ function setupIpcHandlers() { // =========================================================================== // gRPC 连接 - ipcMain.handle('grpc:connect', async (_event, { host, port }) => { + ipcMain.handle('grpc:connect', async (_event, { url }) => { try { - await grpcClient?.connect(host, port); + await grpcClient?.connect(url); return { success: true }; } catch (error) { return { success: false, error: (error as Error).message }; @@ -227,6 +274,26 @@ function setupIpcHandlers() { } }); + // gRPC - 获取当前 partyId + ipcMain.handle('grpc:getPartyId', async () => { + try { + const partyId = grpcClient?.getPartyId(); + return { success: true, partyId: partyId || null }; + } catch (error) { + return { success: false, error: (error as Error).message, partyId: null }; + } + }); + + // gRPC - 检查连接状态 + ipcMain.handle('grpc:isConnected', async () => { + try { + const connected = grpcClient?.isConnected() || false; + return { success: true, connected }; + } catch (error) { + return { success: false, error: (error as Error).message, connected: false }; + } + }); + // gRPC - 验证签名会话 (通过 Account 服务 HTTP API) ipcMain.handle('grpc:validateSigningSession', async (_event, { code }) => { try { diff --git a/backend/mpc-system/services/service-party-app/electron/preload.ts b/backend/mpc-system/services/service-party-app/electron/preload.ts index 2a903fb7..ea9d24bd 100644 --- a/backend/mpc-system/services/service-party-app/electron/preload.ts +++ b/backend/mpc-system/services/service-party-app/electron/preload.ts @@ -46,6 +46,19 @@ contextBridge.exposeInMainWorld('electronAPI', { getRegisteredParties: (roleFilter?: string, onlyOnline?: boolean) => ipcRenderer.invoke('grpc:getRegisteredParties', { roleFilter, onlyOnline }), + // 获取当前 partyId + getPartyId: () => ipcRenderer.invoke('grpc:getPartyId'), + + // 检查连接状态 + isConnected: () => ipcRenderer.invoke('grpc:isConnected'), + + // 连接到 Message Router + connect: (url: string) => ipcRenderer.invoke('grpc:connect', { url }), + + // 注册为参与方 + register: (partyId: string, role: string) => + ipcRenderer.invoke('grpc:register', { partyId, role }), + // 签名相关 validateSigningSession: (code: string) => ipcRenderer.invoke('grpc:validateSigningSession', { code }), diff --git a/backend/mpc-system/services/service-party-app/src/stores/appStore.ts b/backend/mpc-system/services/service-party-app/src/stores/appStore.ts index dc4dfab7..22172eec 100644 --- a/backend/mpc-system/services/service-party-app/src/stores/appStore.ts +++ b/backend/mpc-system/services/service-party-app/src/stores/appStore.ts @@ -101,25 +101,61 @@ export const useAppStore = create((set, get) => ({ checkAllServices: async () => { const { setMessageRouterStatus, setKavaApiStatus, setDatabaseStatus, setAppReady } = get(); - // 检测 Message Router + // 检测 Message Router - 真实验证注册状态 setMessageRouterStatus({ status: 'checking', message: '正在检测...' }); try { if (window.electronAPI) { const settings = await window.electronAPI.storage.getSettings(); const routerUrl = settings?.messageRouterUrl || 'mpc-grpc.szaiai.com:443'; - const result = await window.electronAPI.grpc.testConnection(routerUrl); - if (result.success) { + + // 1. 检查是否已连接 + const connResult = await window.electronAPI.grpc.isConnected(); + if (!connResult.connected) { setMessageRouterStatus({ - status: 'connected', - message: routerUrl, + status: 'disconnected', + message: '未连接到 ' + routerUrl, lastChecked: new Date(), }); } else { - setMessageRouterStatus({ - status: 'error', - message: result.error || '连接失败', - lastChecked: new Date(), - }); + // 2. 已连接,检查是否已注册 + const partyIdResult = await window.electronAPI.grpc.getPartyId(); + if (!partyIdResult.partyId) { + setMessageRouterStatus({ + status: 'disconnected', + message: '已连接但未注册', + lastChecked: new Date(), + }); + } else { + // 3. 已注册,从 Message Router 验证自己是否在注册列表中 + const partiesResult = await window.electronAPI.grpc.getRegisteredParties(); + if (partiesResult.success) { + const selfRegistered = partiesResult.parties.some( + (p) => p.partyId === partyIdResult.partyId + ); + if (selfRegistered) { + const selfParty = partiesResult.parties.find( + (p) => p.partyId === partyIdResult.partyId + ); + setMessageRouterStatus({ + status: 'connected', + message: `已注册 (${selfParty?.online ? '在线' : '离线'})`, + lastChecked: new Date(), + }); + } else { + setMessageRouterStatus({ + status: 'error', + message: '注册验证失败', + lastChecked: new Date(), + }); + } + } else { + setMessageRouterStatus({ + status: 'error', + message: partiesResult.error || '获取注册列表失败', + lastChecked: new Date(), + }); + } + } } } else { setMessageRouterStatus({ diff --git a/backend/mpc-system/services/service-party-app/src/types/electron.d.ts b/backend/mpc-system/services/service-party-app/src/types/electron.d.ts index 11d1ce9e..87d867f5 100644 --- a/backend/mpc-system/services/service-party-app/src/types/electron.d.ts +++ b/backend/mpc-system/services/service-party-app/src/types/electron.d.ts @@ -430,6 +430,14 @@ interface ElectronAPI { testConnection: (url: string) => Promise; // 获取已注册的参与方列表 getRegisteredParties: (roleFilter?: string, onlyOnline?: boolean) => Promise; + // 获取当前 partyId + getPartyId: () => Promise<{ success: boolean; partyId: string | null; error?: string }>; + // 检查连接状态 + isConnected: () => Promise<{ success: boolean; connected: boolean; error?: string }>; + // 连接到 Message Router + connect: (url: string) => Promise<{ success: boolean; error?: string }>; + // 注册为参与方 + register: (partyId: string, role: string) => Promise<{ success: boolean; error?: string }>; // 签名相关 validateSigningSession: (code: string) => Promise; joinSigningSession: (params: JoinSigningSessionParams) => Promise;