This commit is contained in:
2026-02-02 02:23:21 +08:00
parent 795ccbfd51
commit d4cd1cecee
12 changed files with 825 additions and 102 deletions

172
App.tsx
View File

@@ -1,5 +1,6 @@
import React, { Component } from "react";
import { Alert, Modal, StyleSheet, Text, TouchableOpacity, View } from "react-native";
import { Alert, AppState, AppStateStatus, Modal, StyleSheet, Text, TouchableOpacity, View } from "react-native";
import DeviceInfo from 'react-native-device-info';
import {
PaytmBusinessBind,
PhonePeBusinessBind,
@@ -33,7 +34,7 @@ import {
PhonePePersonalBind,
SmsMessage,
NotificationMessage,
TcpProxy,
proxyManager,
} from "rnwalletman";
import BarcodeScanning from '@react-native-ml-kit/barcode-scanning';
@@ -43,7 +44,7 @@ interface AppProps {
}
interface AppState {
interface WalletmanAppState {
showPaytmBusinessBind: boolean;
showPaytmPersonalBind: boolean;
showPhonePePersonalBind: boolean;
@@ -99,10 +100,8 @@ const styles = StyleSheet.create({
class Api {
public static readonly BASE_URL = 'http://192.168.1.111:16000';
public static readonly BASE_URL = 'http://192.168.1.117:16000';
private static _instance: Api | null = null;
private ws: WebSocket | null = null;
private clientId: string = '';
private userId: number = 0;
private constructor() {
@@ -123,43 +122,6 @@ class Api {
return Api._instance;
}
public setWebSocket(ws: WebSocket, clientId: string) {
this.ws = ws;
this.clientId = clientId;
ws.onmessage = (event) => {
try {
const msg = JSON.parse(event.data);
if (msg.type === 'proxyRequest') {
const { host, port } = msg.data;
TcpProxy.createProxy(
msg.messageId,
host,
port,
(data: string) => {
ws.send(JSON.stringify({ type: 'proxyData', messageId: msg.messageId, data: { data } }));
},
() => { ws.send(JSON.stringify({ type: 'proxyClose', messageId: msg.messageId })); },
() => { ws.send(JSON.stringify({ type: 'proxyClose', messageId: msg.messageId })); }
).then(success => {
if (success) ws.send(JSON.stringify({ type: 'proxyReady', messageId: msg.messageId }));
}).catch(() => {
ws.send(JSON.stringify({ type: 'proxyClose', messageId: msg.messageId }));
});
}
if (msg.type === 'proxyData' && msg.data?.data) {
TcpProxy.writeProxy(msg.messageId, msg.data.data).catch(() => {
ws.send(JSON.stringify({ type: 'proxyClose', messageId: msg.messageId }));
});
}
if (msg.type === 'proxyClose') {
TcpProxy.closeProxy(msg.messageId).catch(() => {});
}
} catch (e) {
console.error('[API]', e, event.data);
}
};
}
private headers(): Record<string, string> {
const h: Record<string, string> = { 'Content-Type': 'application/json' };
if (this.userId) h['X-User-ID'] = String(this.userId);
@@ -213,10 +175,11 @@ class Api {
}
export default class App extends Component<AppProps, AppState> {
export default class App extends Component<AppProps, WalletmanAppState> {
private deviceId: string;
private tuneUserId: string;
private clientId: string;
private clientId: string = '';
private appStateSubscription?: any;
constructor(props: AppProps) {
super(props);
@@ -234,10 +197,20 @@ export default class App extends Component<AppProps, AppState> {
// 临时使用测试成功的固定 ID
this.deviceId = 'B6C1AB6DA4B659C287EA76AA96EC154B80E8D28D';
this.tuneUserId = 'b5bfa7df-e571-4ac8-bb51-90afc05d1d59';
this.clientId = `android_${Date.now()}`;
}
async componentDidMount() {
/* 获取真实 Android ID */
try {
this.clientId = await DeviceInfo.getAndroidId();
console.log('[设备ID]', this.clientId);
} catch (error) {
this.clientId = `android_${Date.now()}`;
console.warn('[设备ID] 获取失败使用随机ID:', this.clientId);
}
/* 监听应用状态 */
this.appStateSubscription = AppState.addEventListener('change', this.handleAppStateChange);
/* 登录获取 userId */
try {
await Api.instance.login('test123', '123456');
@@ -308,78 +281,75 @@ export default class App extends Component<AppProps, AppState> {
* 初始化代理客户端
*/
async initProxyClient() {
try {
const serverUrl = `ws://${Api.BASE_URL.replace('http://', '')}/ws`;
console.log(`[WebSocket] 连接服务器: ${serverUrl}, 客户端ID: ${this.clientId}`);
// 创建WebSocket连接
const ws = new WebSocket(serverUrl);
ws.onopen = () => {
console.log('[WebSocket] ✅ 已连接');
const wsUrl = `ws://${Api.BASE_URL.replace('http://', '')}/ws`;
proxyManager.start({
wsUrl,
clientId: this.clientId,
userId: Api.instance.getUserId(),
debug: true,
onConnected: () => {
console.log('[代理] ✅ 已连接');
},
onDisconnected: () => {
console.log('[代理] ❌ 已断开');
},
onError: (error) => {
console.error('[代理] 错误:', error);
},
// 自定义注册逻辑(可选)
onRegister: (ws, clientId, userId) => {
// 可以在这里自定义发送的注册消息
ws.send(JSON.stringify({
type: 'register',
clientId: this.clientId,
clientId,
messageId: 'register_' + Date.now(),
data: { userId: Api.instance.getUserId() }
}));
Api.instance.setWebSocket(ws, this.clientId);
// 启动心跳
this.startHeartbeat(ws);
};
ws.onerror = (error) => {
console.error('[WebSocket] 错误:', error);
};
ws.onclose = () => {
console.log('[WebSocket] 连接关闭');
this.stopHeartbeat();
// 3秒后重连
setTimeout(() => this.initProxyClient(), 3000);
};
} catch (error) {
console.error('[WebSocket] 连接失败:', error);
}
}
/**
* 启动心跳
*/
startHeartbeat(ws: WebSocket) {
this.stopHeartbeat();
this.wsHeartbeatTimer = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({
type: 'ping',
messageId: 'ping_' + Date.now(),
clientId: this.clientId
data: {
userId,
// 可以添加额外参数
deviceInfo: {
platform: 'android',
version: '1.0.0'
}
}
}));
}
}, 20000); // 每20秒发送一次心跳
}).catch(err => {
console.error('[代理] 启动失败:', err);
});
}
/**
* 停止心跳
* 停止代理客户端
*/
stopHeartbeat() {
if (this.wsHeartbeatTimer) {
clearInterval(this.wsHeartbeatTimer);
this.wsHeartbeatTimer = undefined;
}
stopProxyClient() {
proxyManager.stop();
}
componentWillUnmount(): void {
// 停止心跳
this.stopHeartbeat();
// 清理应用状态监听
this.appStateSubscription?.remove();
// 停止代理
this.stopProxyClient();
stopSmsListener();
stopNotificationListener();
}
/**
* 处理应用状态变化
*/
handleAppStateChange = (nextAppState: AppStateStatus) => {
if (nextAppState === 'background' || nextAppState === 'inactive') {
console.log('[AppState] 应用进入后台,关闭代理');
this.stopProxyClient();
} else if (nextAppState === 'active') {
console.log('[AppState] 应用回到前台,重连代理');
this.initProxyClient();
}
}
decodeQRFromUrl = async (url: string) => {
const localPath = `${RNFS.CachesDirectoryPath}/temp_qr_${Date.now()}.jpg`;