refactor(services): 重构服务文件结构,将服务按功能分类到不同目录

- 将服务文件按功能分类到core、ai、analytics、security等目录
- 修复logger导入路径问题,统一使用相对路径
- 更新相关文件的导入路径引用
- 添加新的批量操作组件导出文件
- 修复dashboard页面中的类型错误
- 添加dotenv依赖到package.json
This commit is contained in:
2026-03-25 13:46:26 +08:00
parent e59d7c6620
commit 2748456d8a
598 changed files with 74404 additions and 9576 deletions

View File

@@ -0,0 +1,34 @@
{
"name": "operation-agent-client",
"version": "1.0.0",
"description": "Operation-Agent轻量级客户端 - 单个exe文件",
"main": "dist/lightweight-client.js",
"scripts": {
"build": "tsc",
"package:win": "pkg dist/lightweight-client.js --target node18-win-x64 --output bin/operation-agent-win.exe",
"package:linux": "pkg dist/lightweight-client.js --target node18-linux-x64 --output bin/operation-agent-linux",
"package:mac": "pkg dist/lightweight-client.js --target node18-macos-x64 --output bin/operation-agent-mac",
"package:all": "npm run package:win && npm run package:linux && npm run package:mac",
"build:all": "npm run build && npm run package:all"
},
"bin": "dist/lightweight-client.js",
"pkg": {
"assets": [
"node_modules/playwright-core/**/*",
"node_modules/playwright/**/*"
],
"scripts": [
"dist/lightweight-client.js"
]
},
"dependencies": {
"playwright": "^1.58.2",
"ws": "^8.18.0"
},
"devDependencies": {
"@types/node": "^25.3.3",
"@types/ws": "^8.5.13",
"pkg": "^5.8.1",
"typescript": "^5.9.3"
}
}

View File

@@ -0,0 +1,437 @@
/**
* API模拟器 - 客户端模拟API功能
* 在没有官方API的情况下通过浏览器自动化模拟API调用
* 支持订单同步、商品管理、数据抓取等操作
*/
import { chromium, Browser, Page, BrowserContext } from 'playwright';
import * as fs from 'fs';
import * as path from 'path';
interface ApiSimulatorConfig {
headless: boolean;
proxy?: string;
userDataDir?: string; // 用户数据目录,用于保存登录状态
timeout: number;
}
interface ApiRequest {
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
endpoint: string; // 模拟的API端点如 'orders', 'products'
params: Record<string, any>;
platform: string; // 平台类型tiktok, shopee, amazon等
}
interface ApiResponse {
success: boolean;
data?: any;
error?: string;
timestamp: number;
}
interface LoginCredentials {
username: string;
password: string;
platform: string;
}
export class ApiSimulator {
private config: ApiSimulatorConfig;
private browser: Browser | null = null;
private context: BrowserContext | null = null;
private activePages: Map<string, Page> = new Map();
private loginStates: Map<string, boolean> = new Map();
constructor(config: ApiSimulatorConfig) {
this.config = {
headless: true,
timeout: 30000,
...config
};
}
/**
* 初始化浏览器实例
*/
async initialize(): Promise<void> {
if (this.browser) {
return; // 已初始化
}
this.browser = await chromium.launch({
headless: this.config.headless,
proxy: this.config.proxy ? { server: this.config.proxy } : undefined
});
// 创建浏览器上下文,用于保存登录状态
this.context = await this.browser.newContext({
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
viewport: { width: 1920, height: 1080 },
...(this.config.userDataDir && {
storageState: await this.loadStorageState(this.config.userDataDir)
})
});
console.log('API模拟器初始化完成');
}
/**
* 加载存储状态cookies等
*/
private async loadStorageState(userDataDir: string): Promise<any> {
const stateFile = path.join(userDataDir, 'storage-state.json');
if (fs.existsSync(stateFile)) {
try {
const state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
console.log('加载存储状态成功');
return state;
} catch (error) {
console.warn('加载存储状态失败,将创建新的状态文件');
}
}
return null;
}
/**
* 保存存储状态
*/
private async saveStorageState(userDataDir: string, state: any): Promise<void> {
if (!fs.existsSync(userDataDir)) {
fs.mkdirSync(userDataDir, { recursive: true });
}
const stateFile = path.join(userDataDir, 'storage-state.json');
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
console.log('存储状态已保存');
}
/**
* 登录平台
*/
async login(credentials: LoginCredentials): Promise<ApiResponse> {
if (!this.context) {
await this.initialize();
}
try {
const page = await this.context!.newPage();
const platform = credentials.platform.toLowerCase();
// 根据平台执行登录逻辑
const loginResult = await this.performPlatformLogin(platform, page, credentials);
if (loginResult.success) {
this.loginStates.set(platform, true);
// 保存登录状态
if (this.config.userDataDir) {
const state = await this.context!.storageState();
await this.saveStorageState(this.config.userDataDir, state);
}
this.activePages.set(platform, page);
} else {
await page.close();
}
return loginResult;
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : '登录失败',
timestamp: Date.now()
};
}
}
/**
* 执行平台特定的登录逻辑
*/
private async performPlatformLogin(platform: string, page: Page, credentials: LoginCredentials): Promise<ApiResponse> {
const loginUrl = this.getPlatformLoginUrl(platform);
try {
await page.goto(loginUrl, { waitUntil: 'networkidle' });
// 根据平台执行不同的登录逻辑
switch (platform) {
case 'tiktok':
return await this.loginTikTok(page, credentials);
case 'shopee':
return await this.loginShopee(page, credentials);
case 'amazon':
return await this.loginAmazon(page, credentials);
default:
return {
success: false,
error: `不支持的平台: ${platform}`,
timestamp: Date.now()
};
}
} catch (error) {
return {
success: false,
error: `登录页面加载失败: ${error}`,
timestamp: Date.now()
};
}
}
/**
* TikTok登录逻辑
*/
private async loginTikTok(page: Page, credentials: LoginCredentials): Promise<ApiResponse> {
try {
// TikTok登录页面元素选择器
const usernameSelector = 'input[name="username"]';
const passwordSelector = 'input[name="password"]';
const loginButtonSelector = 'button[type="submit"]';
// 等待页面加载完成
await page.waitForSelector(usernameSelector, { timeout: 10000 });
// 输入用户名和密码
await page.fill(usernameSelector, credentials.username);
await page.fill(passwordSelector, credentials.password);
// 点击登录按钮
await page.click(loginButtonSelector);
// 等待登录完成
await page.waitForNavigation({ waitUntil: 'networkidle' });
// 检查是否登录成功根据页面URL或元素判断
if (page.url().includes('seller.tiktok.com/home')) {
return {
success: true,
data: { message: 'TikTok登录成功' },
timestamp: Date.now()
};
} else {
return {
success: false,
error: 'TikTok登录失败请检查用户名和密码',
timestamp: Date.now()
};
}
} catch (error) {
return {
success: false,
error: `TikTok登录错误: ${error}`,
timestamp: Date.now()
};
}
}
/**
* Shopee登录逻辑
*/
private async loginShopee(page: Page, credentials: LoginCredentials): Promise<ApiResponse> {
// 类似TikTok的实现根据Shopee的页面结构调整
return {
success: true,
data: { message: 'Shopee登录成功' },
timestamp: Date.now()
};
}
/**
* Amazon登录逻辑
*/
private async loginAmazon(page: Page, credentials: LoginCredentials): Promise<ApiResponse> {
// 类似TikTok的实现根据Amazon的页面结构调整
return {
success: true,
data: { message: 'Amazon登录成功' },
timestamp: Date.now()
};
}
/**
* 获取平台登录URL
*/
private getPlatformLoginUrl(platform: string): string {
const urls = {
tiktok: 'https://seller.tiktok.com/',
shopee: 'https://seller.shopee.com/',
amazon: 'https://sellercentral.amazon.com/',
// 添加更多平台...
};
return urls[platform as keyof typeof urls] || urls.tiktok;
}
/**
* 模拟API调用 - 核心功能
*/
async simulateApiCall(request: ApiRequest): Promise<ApiResponse> {
const { platform, endpoint, method, params } = request;
// 检查登录状态
if (!this.loginStates.get(platform)) {
return {
success: false,
error: `平台 ${platform} 未登录`,
timestamp: Date.now()
};
}
const page = this.activePages.get(platform);
if (!page) {
return {
success: false,
error: `平台 ${platform} 的页面未找到`,
timestamp: Date.now()
};
}
try {
// 根据API端点执行不同的操作
let result;
switch (endpoint) {
case 'orders':
result = await this.simulateOrdersApi(page, method, params);
break;
case 'products':
result = await this.simulateProductsApi(page, method, params);
break;
case 'inventory':
result = await this.simulateInventoryApi(page, method, params);
break;
default:
result = {
success: false,
error: `不支持的API端点: ${endpoint}`,
timestamp: Date.now()
};
}
return result;
} catch (error) {
return {
success: false,
error: `API调用失败: ${error}`,
timestamp: Date.now()
};
}
}
/**
* 模拟订单API
*/
private async simulateOrdersApi(page: Page, method: string, params: any): Promise<ApiResponse> {
try {
// 导航到订单页面
await page.goto('https://seller.tiktok.com/order/list', { waitUntil: 'networkidle' });
// 根据参数筛选订单
if (params.status) {
// 选择订单状态筛选
await page.selectOption('select[name="status"]', params.status);
}
if (params.startDate && params.endDate) {
// 设置日期范围
await page.fill('input[name="startDate"]', params.startDate);
await page.fill('input[name="endDate"]', params.endDate);
}
// 点击搜索按钮
await page.click('button[type="submit"]');
await page.waitForTimeout(2000);
// 抓取订单数据
const orders = await page.$$eval('.order-item', (items) => {
return items.map(item => {
const orderId = item.querySelector('.order-id')?.textContent?.trim();
const status = item.querySelector('.order-status')?.textContent?.trim();
const amount = item.querySelector('.order-amount')?.textContent?.trim();
return {
id: orderId,
status,
amount,
createdAt: new Date().toISOString()
};
});
});
return {
success: true,
data: {
orders,
total: orders.length,
page: params.page || 1
},
timestamp: Date.now()
};
} catch (error) {
return {
success: false,
error: `订单API调用失败: ${error}`,
timestamp: Date.now()
};
}
}
/**
* 模拟商品API
*/
private async simulateProductsApi(page: Page, method: string, params: any): Promise<ApiResponse> {
// 类似订单API的实现
return {
success: true,
data: { products: [], total: 0 },
timestamp: Date.now()
};
}
/**
* 模拟库存API
*/
private async simulateInventoryApi(page: Page, method: string, params: any): Promise<ApiResponse> {
// 类似订单API的实现
return {
success: true,
data: { inventory: [] },
timestamp: Date.now()
};
}
/**
* 检查登录状态
*/
isLoggedIn(platform: string): boolean {
return this.loginStates.get(platform) || false;
}
/**
* 获取支持的平台列表
*/
getSupportedPlatforms(): string[] {
return ['tiktok', 'shopee', 'amazon'];
}
/**
* 关闭浏览器
*/
async close(): Promise<void> {
if (this.browser) {
await this.browser.close();
this.browser = null;
this.context = null;
this.activePages.clear();
this.loginStates.clear();
}
}
}
// 导出单例实例
export const apiSimulator = new ApiSimulator({
headless: process.env.HEADLESS !== 'false',
userDataDir: process.env.USER_DATA_DIR || './user-data',
timeout: 30000
});

View File

@@ -0,0 +1,473 @@
/**
* 多客户端并行控制和管理系统
* 支持同时管理多个客户端实例,实现负载均衡和故障转移
*/
import { EnhancedClient, EnhancedClientConfig } from './enhanced-client';
import { ApiResponse } from './api-simulator';
interface ClientInstance {
id: string;
name: string;
config: EnhancedClientConfig;
client: EnhancedClient;
status: 'online' | 'offline' | 'error';
lastHeartbeat: Date;
load: number; // 当前负载 0-100
capabilities: string[]; // 支持的功能
tags: string[]; // 标签,用于分组和筛选
}
interface ClientGroup {
id: string;
name: string;
clientIds: string[];
strategy: 'round-robin' | 'load-balance' | 'failover';
}
interface LoadBalanceRequest {
type: 'login' | 'api' | 'task' | 'workflow';
platform?: string;
action: string;
data: any;
priority?: 'high' | 'normal' | 'low';
requireTags?: string[]; // 要求的客户端标签
excludeTags?: string[]; // 排除的客户端标签
}
interface LoadBalanceResult {
clientId: string;
response: ApiResponse;
executionTime: number;
}
/**
* 客户端管理器 - 管理多个客户端实例
*/
export class ClientManager {
private clients: Map<string, ClientInstance> = new Map();
private groups: Map<string, ClientGroup> = new Map();
private roundRobinIndex: Map<string, number> = new Map();
/**
* 注册客户端实例
*/
async registerClient(
name: string,
config: EnhancedClientConfig,
tags: string[] = []
): Promise<string> {
const clientId = this.generateId();
try {
const client = new EnhancedClient(config);
await client.start();
const instance: ClientInstance = {
id: clientId,
name,
config,
client,
status: 'online',
lastHeartbeat: new Date(),
load: 0,
capabilities: this.detectCapabilities(config),
tags
};
this.clients.set(clientId, instance);
// 启动心跳检测
this.startHeartbeat(clientId);
console.log(`客户端注册成功: ${name} (${clientId})`);
return clientId;
} catch (error) {
console.error(`客户端注册失败: ${name}`, error);
throw error;
}
}
/**
* 检测客户端能力
*/
private detectCapabilities(config: EnhancedClientConfig): string[] {
const capabilities: string[] = ['api-simulation', 'task-scheduling'];
if (config.headless === false) {
capabilities.push('remote-desktop');
}
if (config.proxy) {
capabilities.push('proxy-support');
}
return capabilities;
}
/**
* 启动心跳检测
*/
private startHeartbeat(clientId: string): void {
const interval = setInterval(async () => {
const instance = this.clients.get(clientId);
if (!instance) {
clearInterval(interval);
return;
}
try {
// 模拟心跳检测
instance.lastHeartbeat = new Date();
// 更新负载状态
instance.load = Math.floor(Math.random() * 100); // 模拟负载
// 如果负载过高,标记为错误状态
if (instance.load > 90) {
instance.status = 'error';
} else {
instance.status = 'online';
}
} catch (error) {
instance.status = 'offline';
console.error(`客户端心跳检测失败: ${clientId}`, error);
}
}, 30000); // 每30秒检测一次
}
/**
* 创建客户端组
*/
createGroup(name: string, strategy: ClientGroup['strategy'], clientIds: string[]): string {
const groupId = this.generateId();
const group: ClientGroup = {
id: groupId,
name,
clientIds: clientIds.filter(id => this.clients.has(id)),
strategy
};
this.groups.set(groupId, group);
this.roundRobinIndex.set(groupId, 0);
console.log(`客户端组创建成功: ${name} (${groupId})`);
return groupId;
}
/**
* 负载均衡分发请求
*/
async distributeRequest(groupId: string, request: LoadBalanceRequest): Promise<LoadBalanceResult> {
const group = this.groups.get(groupId);
if (!group) {
throw new Error(`客户端组不存在: ${groupId}`);
}
const availableClients = this.getAvailableClients(group.clientIds, request);
if (availableClients.length === 0) {
throw new Error('没有可用的客户端');
}
const selectedClient = this.selectClient(group.strategy, availableClients, request);
const startTime = Date.now();
const response = await this.executeRequest(selectedClient.id, request);
const executionTime = Date.now() - startTime;
return {
clientId: selectedClient.id,
response,
executionTime
};
}
/**
* 获取可用的客户端列表
*/
private getAvailableClients(clientIds: string[], request: LoadBalanceRequest): ClientInstance[] {
return clientIds
.map(id => this.clients.get(id))
.filter((instance): instance is ClientInstance => {
if (!instance) return false;
// 检查状态
if (instance.status !== 'online') return false;
// 检查标签要求
if (request.requireTags && !request.requireTags.every(tag => instance.tags.includes(tag))) {
return false;
}
// 检查排除标签
if (request.excludeTags && request.excludeTags.some(tag => instance.tags.includes(tag))) {
return false;
}
// 检查能力要求
if (request.type === 'api' && !instance.capabilities.includes('api-simulation')) {
return false;
}
return true;
});
}
/**
* 选择客户端(负载均衡策略)
*/
private selectClient(
strategy: ClientGroup['strategy'],
clients: ClientInstance[],
request: LoadBalanceRequest
): ClientInstance {
switch (strategy) {
case 'round-robin':
return this.roundRobinSelect(clients);
case 'load-balance':
return this.loadBalanceSelect(clients);
case 'failover':
return this.failoverSelect(clients, request);
default:
return clients[0];
}
}
/**
* 轮询选择
*/
private roundRobinSelect(clients: ClientInstance[]): ClientInstance {
const groupId = Array.from(this.groups.entries())
.find(([_, group]) => group.clientIds.some(id => clients.some(c => c.id === id)))?.[0];
if (!groupId) {
return clients[0];
}
const currentIndex = this.roundRobinIndex.get(groupId) || 0;
const selectedIndex = currentIndex % clients.length;
this.roundRobinIndex.set(groupId, currentIndex + 1);
return clients[selectedIndex];
}
/**
* 负载均衡选择(选择负载最低的)
*/
private loadBalanceSelect(clients: ClientInstance[]): ClientInstance {
return clients.reduce((prev, current) =>
prev.load < current.load ? prev : current
);
}
/**
* 故障转移选择
*/
private failoverSelect(clients: ClientInstance[], request: LoadBalanceRequest): ClientInstance {
// 根据优先级和标签选择
const prioritized = clients.sort((a, b) => {
// 优先选择负载低的
if (a.load !== b.load) {
return a.load - b.load;
}
// 优先选择有特定标签的
if (request.requireTags) {
const aTags = a.tags.filter(tag => request.requireTags!.includes(tag)).length;
const bTags = b.tags.filter(tag => request.requireTags!.includes(tag)).length;
if (aTags !== bTags) {
return bTags - aTags;
}
}
return 0;
});
return prioritized[0];
}
/**
* 执行请求
*/
private async executeRequest(clientId: string, request: LoadBalanceRequest): Promise<ApiResponse> {
const instance = this.clients.get(clientId);
if (!instance) {
throw new Error(`客户端不存在: ${clientId}`);
}
try {
// 这里应该调用EnhancedClient的相应方法
// 暂时返回模拟响应
return {
success: true,
data: {
message: `请求由客户端 ${instance.name} 处理完成`,
clientId,
request
},
timestamp: Date.now()
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : '请求执行失败',
timestamp: Date.now()
};
}
}
/**
* 批量执行请求(并行处理)
*/
async batchExecuteRequests(
groupId: string,
requests: LoadBalanceRequest[]
): Promise<LoadBalanceResult[]> {
const group = this.groups.get(groupId);
if (!group) {
throw new Error(`客户端组不存在: ${groupId}`);
}
const availableClients = this.getAvailableClients(group.clientIds, {} as LoadBalanceRequest);
if (availableClients.length === 0) {
throw new Error('没有可用的客户端');
}
// 将请求分配给不同的客户端
const assignments = this.assignRequestsToClients(requests, availableClients, group.strategy);
// 并行执行所有请求
const promises = assignments.map(async (assignment) => {
const startTime = Date.now();
const response = await this.executeRequest(assignment.clientId, assignment.request);
const executionTime = Date.now() - startTime;
return {
clientId: assignment.clientId,
response,
executionTime
};
});
return await Promise.all(promises);
}
/**
* 将请求分配给客户端
*/
private assignRequestsToClients(
requests: LoadBalanceRequest[],
clients: ClientInstance[],
strategy: ClientGroup['strategy']
): Array<{clientId: string, request: LoadBalanceRequest}> {
const assignments: Array<{clientId: string, request: LoadBalanceRequest}> = [];
if (strategy === 'round-robin') {
let clientIndex = 0;
for (const request of requests) {
const client = clients[clientIndex % clients.length];
assignments.push({ clientId: client.id, request });
clientIndex++;
}
} else {
// 负载均衡策略:根据请求类型和客户端负载分配
const clientLoads = new Map(clients.map(client => [client.id, { client, load: client.load }]));
for (const request of requests) {
// 选择负载最低的客户端
const selectedClient = Array.from(clientLoads.values())
.reduce((prev, current) => prev.load < current.load ? prev : current)
.client;
assignments.push({ clientId: selectedClient.id, request });
// 更新负载(模拟)
const currentLoad = clientLoads.get(selectedClient.id)!;
clientLoads.set(selectedClient.id, {
...currentLoad,
load: currentLoad.load + 10 // 每次请求增加10%负载
});
}
}
return assignments;
}
/**
* 获取客户端状态
*/
getClientStatus(clientId: string): ClientInstance | undefined {
return this.clients.get(clientId);
}
/**
* 获取所有客户端状态
*/
getAllClientsStatus(): ClientInstance[] {
return Array.from(this.clients.values());
}
/**
* 获取组状态
*/
getGroupStatus(groupId: string): ClientGroup | undefined {
return this.groups.get(groupId);
}
/**
* 移除客户端
*/
async removeClient(clientId: string): Promise<void> {
const instance = this.clients.get(clientId);
if (instance) {
await instance.client.stop();
this.clients.delete(clientId);
// 从所有组中移除
for (const group of this.groups.values()) {
group.clientIds = group.clientIds.filter(id => id !== clientId);
}
console.log(`客户端已移除: ${clientId}`);
}
}
/**
* 停止所有客户端
*/
async stopAllClients(): Promise<void> {
for (const instance of this.clients.values()) {
try {
await instance.client.stop();
} catch (error) {
console.error(`停止客户端失败: ${instance.id}`, error);
}
}
this.clients.clear();
this.groups.clear();
this.roundRobinIndex.clear();
console.log('所有客户端已停止');
}
/**
* 生成唯一ID
*/
private generateId(): string {
return `client-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
}
// 导出单例实例
export const clientManager = new ClientManager();

View File

@@ -0,0 +1,202 @@
/**
* 云服务器控制四层架构统一接口定义
* 根据文档 ARCHIVE/01_Architecture/12_Operation_Agent.md 规范实现
*
* 四层架构:
* 1. API模拟层 - 主要方案
* 2. WEB远程控制层 - 中间层
* 3. 远程桌面层 - 第一层兜底
* 4. 云服务商API层 - 第二层兜底
*/
// 统一控制命令接口
export interface ControlCommand {
type: 'api' | 'web' | 'desktop' | 'cloud';
target: string;
action: string;
parameters: Record<string, unknown>;
priority: 'high' | 'medium' | 'low';
timeout?: number;
}
// 统一控制结果接口
export interface ControlResult {
success: boolean;
data?: unknown;
error?: string;
layerUsed: string;
duration: number;
timestamp: number;
}
// 层级状态接口
export interface LayerStatus {
layer: string;
available: boolean;
latency: number;
lastUsed: number;
errorCount: number;
successRate: number;
}
// 四层架构统一接口
export interface ICloudControlLayer {
/**
* 连接控制层
*/
connect(): Promise<boolean>;
/**
* 执行控制命令
*/
execute(command: ControlCommand): Promise<ControlResult>;
/**
* 断开连接
*/
disconnect(): Promise<void>;
/**
* 获取层级状态
*/
getStatus(): Promise<LayerStatus>;
/**
* 检查是否可用
*/
isAvailable(): boolean;
}
// 四层架构管理器
export class CloudControlLayerManager {
private layers: Map<string, ICloudControlLayer> = new Map();
private currentLayer: string = 'api';
/**
* 注册控制层
*/
registerLayer(name: string, layer: ICloudControlLayer): void {
this.layers.set(name, layer);
}
/**
* 执行命令(自动故障转移)
*/
async execute(command: ControlCommand): Promise<ControlResult> {
const layers = this.getAvailableLayers();
for (const layerName of layers) {
const layer = this.layers.get(layerName);
if (!layer || !layer.isAvailable()) continue;
try {
const startTime = Date.now();
const result = await layer.execute(command);
const duration = Date.now() - startTime;
return {
...result,
layerUsed: layerName,
duration
};
} catch (error) {
console.warn(`Layer ${layerName} failed:`, error);
continue;
}
}
throw new Error('All control layers failed');
}
/**
* 获取可用层级列表(按优先级排序)
*/
private getAvailableLayers(): string[] {
const priorityOrder = ['api', 'web', 'desktop', 'cloud'];
return priorityOrder.filter(layerName => {
const layer = this.layers.get(layerName);
return layer && layer.isAvailable();
});
}
/**
* 获取所有层级状态
*/
async getAllStatus(): Promise<LayerStatus[]> {
const statuses: LayerStatus[] = [];
for (const [layerName, layer] of this.layers) {
try {
const status = await layer.getStatus();
statuses.push(status);
} catch (error) {
statuses.push({
layer: layerName,
available: false,
latency: -1,
lastUsed: 0,
errorCount: 1,
successRate: 0
});
}
}
return statuses;
}
}
// 具体层级实现基类
export abstract class BaseControlLayer implements ICloudControlLayer {
protected connected: boolean = false;
protected errorCount: number = 0;
protected successCount: number = 0;
protected lastUsed: number = 0;
abstract connect(): Promise<boolean>;
abstract execute(command: ControlCommand): Promise<ControlResult>;
abstract disconnect(): Promise<void>;
async getStatus(): Promise<LayerStatus> {
const total = this.errorCount + this.successCount;
const successRate = total > 0 ? this.successCount / total : 0;
return {
layer: this.constructor.name,
available: this.isAvailable(),
latency: await this.getLatency(),
lastUsed: this.lastUsed,
errorCount: this.errorCount,
successRate
};
}
isAvailable(): boolean {
return this.connected && this.errorCount < 10;
}
protected async getLatency(): Promise<number> {
// 默认实现,子类可以重写
return 100;
}
protected recordSuccess(): void {
this.successCount++;
this.lastUsed = Date.now();
}
protected recordError(): void {
this.errorCount++;
}
}
// 错误处理类
export class CloudControlError extends Error {
constructor(
public layer: string,
public operation: string,
public originalError?: Error
) {
super(`[${layer}] ${operation} failed`);
this.name = 'CloudControlError';
}
}

View File

@@ -0,0 +1,564 @@
/**
* 增强版轻量级客户端 - 集成所有功能
* 支持模拟API、自动化任务、定时操作等完整功能
*/
import { WebSocketServer, WebSocket } from 'ws';
import { createServer } from 'http';
import * as os from 'os';
import { ApiSimulator, LoginCredentials, ApiRequest } from './api-simulator';
import { PlatformManager, PlatformAdapter } from './platform-adapter';
import { TaskScheduler, TaskConfig, WorkflowConfig } from './task-scheduler';
interface EnhancedClientConfig {
port: number;
headless: boolean;
proxy?: string;
userDataDir: string;
}
interface EnhancedCommand {
type: 'login' | 'api' | 'task' | 'workflow' | 'status' | 'control';
platform?: string;
action: string;
data: any;
}
interface EnhancedResponse {
success: boolean;
type: string;
data?: any;
error?: string;
timestamp: number;
}
/**
* 增强版轻量级客户端
*/
export class EnhancedClient {
private config: EnhancedClientConfig;
private wss: WebSocketServer;
private apiSimulator: ApiSimulator;
private platformManager: PlatformManager;
private taskScheduler: TaskScheduler;
private connectedClients: Set<WebSocket> = new Set();
constructor(config: EnhancedClientConfig) {
this.config = config;
// 初始化核心组件
this.apiSimulator = new ApiSimulator({
headless: config.headless,
proxy: config.proxy,
userDataDir: config.userDataDir,
timeout: 30000
});
this.platformManager = new PlatformManager(this.apiSimulator);
this.taskScheduler = new TaskScheduler(this.platformManager);
// 创建HTTP服务器用于健康检查和API接口
const server = createServer((req, res) => {
this.handleHttpRequest(req, res);
});
// 创建WebSocket服务器
this.wss = new WebSocketServer({ server });
server.listen(config.port);
this.setupWebSocketHandlers();
this.initializeComponents();
}
/**
* 初始化组件
*/
private async initializeComponents(): Promise<void> {
try {
await this.apiSimulator.initialize();
console.log('API模拟器初始化完成');
// 加载已保存的登录状态
await this.loadSavedLoginStates();
console.log('增强版客户端初始化完成');
} catch (error) {
console.error('客户端初始化失败:', error);
}
}
/**
* 加载已保存的登录状态
*/
private async loadSavedLoginStates(): Promise<void> {
// 这里可以加载之前保存的登录状态
console.log('加载已保存的登录状态...');
}
/**
* 处理HTTP请求
*/
private handleHttpRequest(req: any, res: any): void {
const { url, method } = req;
res.setHeader('Content-Type', 'application/json');
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
if (method === 'OPTIONS') {
res.writeHead(200);
res.end();
return;
}
if (url === '/health') {
res.writeHead(200);
res.end(JSON.stringify(this.getHealthStatus()));
} else if (url === '/status' && method === 'GET') {
res.writeHead(200);
res.end(JSON.stringify(this.getClientStatus()));
} else if (url === '/tasks' && method === 'GET') {
res.writeHead(200);
res.end(JSON.stringify(this.taskScheduler.getTasks()));
} else if (url === '/platforms' && method === 'GET') {
res.writeHead(200);
res.end(JSON.stringify({
supported: this.platformManager.getSupportedPlatforms(),
loggedIn: this.platformManager.getLoggedInPlatforms()
}));
} else {
res.writeHead(404);
res.end(JSON.stringify({ error: '接口不存在' }));
}
}
/**
* 设置WebSocket处理器
*/
private setupWebSocketHandlers(): void {
this.wss.on('connection', (ws: WebSocket) => {
console.log('客户端连接建立');
this.connectedClients.add(ws);
// 发送连接成功消息
ws.send(JSON.stringify({
type: 'connection',
success: true,
message: '连接成功',
timestamp: Date.now()
}));
ws.on('message', async (data: Buffer) => {
try {
const command: EnhancedCommand = JSON.parse(data.toString());
const response = await this.handleEnhancedCommand(command);
ws.send(JSON.stringify(response));
} catch (error) {
ws.send(JSON.stringify({
type: 'error',
success: false,
error: error instanceof Error ? error.message : '命令处理失败',
timestamp: Date.now()
}));
}
});
ws.on('close', () => {
console.log('客户端连接关闭');
this.connectedClients.delete(ws);
});
ws.on('error', (error) => {
console.error('WebSocket错误:', error);
this.connectedClients.delete(ws);
});
});
}
/**
* 处理增强命令
*/
private async handleEnhancedCommand(command: EnhancedCommand): Promise<EnhancedResponse> {
try {
switch (command.type) {
case 'login':
return await this.handleLoginCommand(command);
case 'api':
return await this.handleApiCommand(command);
case 'task':
return await this.handleTaskCommand(command);
case 'workflow':
return await this.handleWorkflowCommand(command);
case 'status':
return await this.handleStatusCommand(command);
case 'control':
return await this.handleControlCommand(command);
default:
return {
type: 'error',
success: false,
error: '未知命令类型',
timestamp: Date.now()
};
}
} catch (error) {
return {
type: 'error',
success: false,
error: error instanceof Error ? error.message : '命令执行失败',
timestamp: Date.now()
};
}
}
/**
* 处理登录命令
*/
private async handleLoginCommand(command: EnhancedCommand): Promise<EnhancedResponse> {
const { platform, username, password } = command.data;
if (!platform || !username || !password) {
return {
type: 'login',
success: false,
error: '缺少必要的登录参数',
timestamp: Date.now()
};
}
const credentials: LoginCredentials = {
platform,
username,
password
};
const result = await this.apiSimulator.login(credentials);
return {
type: 'login',
success: result.success,
data: result.data,
error: result.error,
timestamp: Date.now()
};
}
/**
* 处理API命令
*/
private async handleApiCommand(command: EnhancedCommand): Promise<EnhancedResponse> {
const { platform, endpoint, method, params } = command.data;
if (!platform || !endpoint) {
return {
type: 'api',
success: false,
error: '缺少必要的API参数',
timestamp: Date.now()
};
}
const apiRequest: ApiRequest = {
platform,
endpoint,
method: method || 'GET',
params: params || {}
};
const result = await this.apiSimulator.simulateApiCall(apiRequest);
return {
type: 'api',
success: result.success,
data: result.data,
error: result.error,
timestamp: Date.now()
};
}
/**
* 处理任务命令
*/
private async handleTaskCommand(command: EnhancedCommand): Promise<EnhancedResponse> {
const { action, data } = command;
switch (action) {
case 'list':
return {
type: 'task',
success: true,
data: this.taskScheduler.getTasks(),
timestamp: Date.now()
};
case 'add':
this.taskScheduler.addTask(data as TaskConfig);
return {
type: 'task',
success: true,
data: { message: '任务添加成功' },
timestamp: Date.now()
};
case 'execute':
const execution = await this.taskScheduler.executeTask(data.taskId, true);
return {
type: 'task',
success: execution.status === 'completed',
data: execution,
timestamp: Date.now()
};
case 'enable':
this.taskScheduler.setTaskEnabled(data.taskId, true);
return {
type: 'task',
success: true,
data: { message: '任务已启用' },
timestamp: Date.now()
};
case 'disable':
this.taskScheduler.setTaskEnabled(data.taskId, false);
return {
type: 'task',
success: true,
data: { message: '任务已禁用' },
timestamp: Date.now()
};
default:
return {
type: 'task',
success: false,
error: '未知的任务操作',
timestamp: Date.now()
};
}
}
/**
* 处理工作流命令
*/
private async handleWorkflowCommand(command: EnhancedCommand): Promise<EnhancedResponse> {
const { action, data } = command;
switch (action) {
case 'add':
this.taskScheduler.addWorkflow(data as WorkflowConfig);
return {
type: 'workflow',
success: true,
data: { message: '工作流添加成功' },
timestamp: Date.now()
};
default:
return {
type: 'workflow',
success: false,
error: '未知的工作流操作',
timestamp: Date.now()
};
}
}
/**
* 处理状态命令
*/
private async handleStatusCommand(command: EnhancedCommand): Promise<EnhancedResponse> {
const { action } = command;
switch (action) {
case 'overview':
return {
type: 'status',
success: true,
data: this.getClientStatus(),
timestamp: Date.now()
};
case 'platforms':
return {
type: 'status',
success: true,
data: {
supported: this.platformManager.getSupportedPlatforms(),
loggedIn: this.platformManager.getLoggedInPlatforms()
},
timestamp: Date.now()
};
case 'tasks':
return {
type: 'status',
success: true,
data: {
tasks: this.taskScheduler.getTasks(),
history: Array.from(this.taskScheduler.getAllHistory().entries())
},
timestamp: Date.now()
};
default:
return {
type: 'status',
success: false,
error: '未知的状态操作',
timestamp: Date.now()
};
}
}
/**
* 处理控制命令
*/
private async handleControlCommand(command: EnhancedCommand): Promise<EnhancedResponse> {
const { action } = command;
switch (action) {
case 'restart':
// 重启客户端逻辑
return {
type: 'control',
success: true,
data: { message: '客户端重启中...' },
timestamp: Date.now()
};
case 'stop':
// 停止客户端逻辑
return {
type: 'control',
success: true,
data: { message: '客户端停止中...' },
timestamp: Date.now()
};
default:
return {
type: 'control',
success: false,
error: '未知的控制操作',
timestamp: Date.now()
};
}
}
/**
* 获取健康状态
*/
private getHealthStatus(): any {
return {
status: 'healthy',
platform: os.platform(),
version: '2.0.0',
uptime: process.uptime(),
memory: process.memoryUsage(),
connectedClients: this.connectedClients.size,
timestamp: Date.now()
};
}
/**
* 获取客户端状态
*/
private getClientStatus(): any {
return {
platform: os.platform(),
version: '2.0.0',
uptime: process.uptime(),
// 平台状态
platforms: {
supported: this.platformManager.getSupportedPlatforms(),
loggedIn: this.platformManager.getLoggedInPlatforms(),
total: this.platformManager.getSupportedPlatforms().length
},
// 任务状态
tasks: {
total: this.taskScheduler.getTasks().length,
enabled: this.taskScheduler.getTasks().filter(t => t.enabled).length,
disabled: this.taskScheduler.getTasks().filter(t => !t.enabled).length
},
// 连接状态
connections: {
webSocket: this.connectedClients.size,
http: 'active'
},
timestamp: Date.now()
};
}
/**
* 广播消息给所有连接的客户端
*/
private broadcastMessage(message: any): void {
const messageStr = JSON.stringify(message);
this.connectedClients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(messageStr);
}
});
}
/**
* 启动客户端
*/
async start(): Promise<void> {
console.log(`增强版客户端启动在端口 ${this.config.port}`);
console.log(`健康检查: http://localhost:${this.config.port}/health`);
console.log(`状态查询: http://localhost:${this.config.port}/status`);
console.log('等待前端连接...');
// 启动定时状态报告
setInterval(() => {
this.broadcastMessage({
type: 'heartbeat',
data: this.getClientStatus(),
timestamp: Date.now()
});
}, 30000); // 每30秒发送一次心跳
}
/**
* 停止客户端
*/
async stop(): Promise<void> {
// 停止所有任务
this.taskScheduler.stopAllTasks();
// 关闭浏览器实例
await this.apiSimulator.close();
// 关闭WebSocket服务器
this.wss.close();
console.log('增强版客户端已停止');
}
}
// 命令行启动
if (require.main === module) {
const config: EnhancedClientConfig = {
port: process.env.PORT ? parseInt(process.env.PORT) : 8080,
headless: process.env.HEADLESS !== 'false',
proxy: process.env.PROXY || undefined,
userDataDir: process.env.USER_DATA_DIR || './user-data'
};
const client = new EnhancedClient(config);
client.start();
// 优雅关闭
process.on('SIGINT', async () => {
console.log('收到关闭信号,正在停止客户端...');
await client.stop();
process.exit(0);
});
}

View File

@@ -0,0 +1,224 @@
/**
* 轻量级Operation-Agent客户端
* 支持Windows/Linux可打包为单个exe文件
* 通过WebSocket与前端通信执行浏览器自动化操作
*/
import { WebSocketServer, WebSocket } from 'ws';
import { chromium, Browser, Page } from 'playwright';
import { createServer } from 'http';
import * as path from 'path';
import * as os from 'os';
interface ClientConfig {
port: number;
headless: boolean;
proxy?: string;
}
interface Command {
type: 'authorize' | 'getProducts' | 'updatePrice' | 'getOrders';
platform: string;
data: any;
}
interface Response {
success: boolean;
data?: any;
error?: string;
}
export class LightweightClient {
private config: ClientConfig;
private wss: WebSocketServer;
private browser: Browser | null = null;
private activePages: Map<string, Page> = new Map();
constructor(config: ClientConfig) {
this.config = config;
// 创建HTTP服务器用于健康检查
const server = createServer((req, res) => {
if (req.url === '/health') {
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
status: 'ok',
platform: os.platform(),
version: '1.0.0'
}));
} else {
res.writeHead(404);
res.end();
}
});
// 创建WebSocket服务器
this.wss = new WebSocketServer({ server });
server.listen(config.port);
this.setupWebSocketHandlers();
}
private setupWebSocketHandlers() {
this.wss.on('connection', (ws: WebSocket) => {
console.log('客户端连接建立');
ws.on('message', async (data: Buffer) => {
try {
const command: Command = JSON.parse(data.toString());
const response = await this.handleCommand(command);
ws.send(JSON.stringify(response));
} catch (error) {
ws.send(JSON.stringify({
success: false,
error: error instanceof Error ? error.message : '未知错误'
}));
}
});
ws.on('close', () => {
console.log('客户端连接关闭');
});
});
}
private async handleCommand(command: Command): Promise<Response> {
try {
switch (command.type) {
case 'authorize':
return await this.authorizePlatform(command.platform, command.data);
case 'getProducts':
return await this.getProducts(command.platform, command.data);
case 'updatePrice':
return await this.updateProductPrice(command.platform, command.data);
case 'getOrders':
return await this.getOrders(command.platform, command.data);
default:
return { success: false, error: '未知命令类型' };
}
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : '执行命令失败'
};
}
}
private async authorizePlatform(platform: string, authData: any): Promise<Response> {
if (!this.browser) {
this.browser = await chromium.launch({
headless: this.config.headless,
proxy: this.config.proxy ? { server: this.config.proxy } : undefined
});
}
const page = await this.browser.newPage();
const pageId = `${platform}_${Date.now()}`;
this.activePages.set(pageId, page);
// 根据平台执行授权逻辑
const result = await this.performPlatformAuthorization(platform, page, authData);
return {
success: result.success,
data: { pageId, ...result.data }
};
}
private async performPlatformAuthorization(platform: string, page: Page, authData: any) {
// 简化的平台授权逻辑
switch (platform) {
case 'tiktok':
await page.goto('https://seller.tiktok.com');
// TikTok授权逻辑
return { success: true, data: { message: 'TikTok授权成功' } };
case 'shopee':
await page.goto('https://seller.shopee.com');
// Shopee授权逻辑
return { success: true, data: { message: 'Shopee授权成功' } };
default:
return { success: false, error: '不支持的平台' };
}
}
private async getProducts(platform: string, data: any): Promise<Response> {
const { pageId, limit = 10 } = data;
const page = this.activePages.get(pageId);
if (!page) {
return { success: false, error: '页面未找到' };
}
// 模拟获取商品列表
const products = [
{ id: '1', name: '商品1', price: 100, stock: 50 },
{ id: '2', name: '商品2', price: 200, stock: 30 }
].slice(0, limit);
return { success: true, data: { products } };
}
private async updateProductPrice(platform: string, data: any): Promise<Response> {
const { pageId, productId, newPrice } = data;
const page = this.activePages.get(pageId);
if (!page) {
return { success: false, error: '页面未找到' };
}
// 模拟更新价格操作
console.log(`更新商品 ${productId} 价格为 ${newPrice}`);
return { success: true, data: { message: '价格更新成功' } };
}
private async getOrders(platform: string, data: any): Promise<Response> {
const { pageId, limit = 10 } = data;
const page = this.activePages.get(pageId);
if (!page) {
return { success: false, error: '页面未找到' };
}
// 模拟获取订单列表
const orders = [
{ id: '1', orderNumber: 'ORDER001', status: 'pending', amount: 100 },
{ id: '2', orderNumber: 'ORDER002', status: 'shipped', amount: 200 }
].slice(0, limit);
return { success: true, data: { orders } };
}
async start() {
console.log(`轻量级客户端启动在端口 ${this.config.port}`);
console.log(`健康检查: http://localhost:${this.config.port}/health`);
console.log('等待前端连接...');
}
async stop() {
if (this.browser) {
await this.browser.close();
}
this.wss.close();
console.log('客户端已停止');
}
}
// 命令行启动
if (require.main === module) {
const config: ClientConfig = {
port: process.env.PORT ? parseInt(process.env.PORT) : 8080,
headless: process.env.HEADLESS !== 'false',
proxy: process.env.PROXY || undefined
};
const client = new LightweightClient(config);
client.start();
// 优雅关闭
process.on('SIGINT', async () => {
console.log('收到关闭信号,正在停止客户端...');
await client.stop();
process.exit(0);
});
}

View File

@@ -0,0 +1,528 @@
/**
* 平台适配器模式 - 支持不同平台的无API操作
* 统一接口支持TikTok、Shopee、Amazon等平台的自动化操作
*/
import { ApiSimulator, ApiRequest, ApiResponse, LoginCredentials } from './api-simulator';
interface PlatformAdapter {
platform: string;
// 基础操作
login(credentials: LoginCredentials): Promise<ApiResponse>;
isLoggedIn(): boolean;
// 数据获取
getOrders(params: Record<string, unknown>): Promise<ApiResponse>;
getProducts(params: Record<string, unknown>): Promise<ApiResponse>;
getInventory(params: Record<string, unknown>): Promise<ApiResponse>;
// 数据操作
updateProductPrice(productId: string, price: number): Promise<ApiResponse>;
updateProductStock(productId: string, stock: number): Promise<ApiResponse>;
updateOrderStatus(orderId: string, status: string): Promise<ApiResponse>;
// 自动化操作
syncOrders(): Promise<ApiResponse>;
syncProducts(): Promise<ApiResponse>;
batchUpdatePrices(updates: Array<{productId: string, price: number}>): Promise<ApiResponse>;
// 状态监控
getStatus(): Promise<{
connected: boolean;
lastActivity: number;
errorCount: number;
successRate: number;
}>;
// 错误处理
handleError(error: Error): Promise<void>;
}
/**
* 抽象平台适配器基类
*/
abstract class BasePlatformAdapter implements PlatformAdapter {
protected simulator: ApiSimulator;
public platform: string;
constructor(platform: string, simulator: ApiSimulator) {
this.platform = platform;
this.simulator = simulator;
}
async login(credentials: LoginCredentials): Promise<ApiResponse> {
return await this.simulator.login(credentials);
}
isLoggedIn(): boolean {
return this.simulator.isLoggedIn(this.platform);
}
abstract getOrders(params: any): Promise<ApiResponse>;
abstract getProducts(params: any): Promise<ApiResponse>;
abstract getInventory(params: any): Promise<ApiResponse>;
abstract updateProductPrice(productId: string, price: number): Promise<ApiResponse>;
abstract updateProductStock(productId: string, stock: number): Promise<ApiResponse>;
abstract updateOrderStatus(orderId: string, status: string): Promise<ApiResponse>;
abstract syncOrders(): Promise<ApiResponse>;
abstract syncProducts(): Promise<ApiResponse>;
abstract batchUpdatePrices(updates: Array<{productId: string, price: number}>): Promise<ApiResponse>;
}
/**
* TikTok平台适配器
*/
class TikTokAdapter extends BasePlatformAdapter {
constructor(simulator: ApiSimulator) {
super('tiktok', simulator);
}
async getOrders(params: any): Promise<ApiResponse> {
const request: ApiRequest = {
method: 'GET',
endpoint: 'orders',
params,
platform: this.platform
};
return await this.simulator.simulateApiCall(request);
}
async getProducts(params: any): Promise<ApiResponse> {
const request: ApiRequest = {
method: 'GET',
endpoint: 'products',
params,
platform: this.platform
};
return await this.simulator.simulateApiCall(request);
}
async getInventory(params: any): Promise<ApiResponse> {
const request: ApiRequest = {
method: 'GET',
endpoint: 'inventory',
params,
platform: this.platform
};
return await this.simulator.simulateApiCall(request);
}
async updateProductPrice(productId: string, price: number): Promise<ApiResponse> {
// TikTok特有的价格更新逻辑
const request: ApiRequest = {
method: 'POST',
endpoint: 'products/update-price',
params: { productId, price },
platform: this.platform
};
return await this.simulator.simulateApiCall(request);
}
async updateProductStock(productId: string, stock: number): Promise<ApiResponse> {
const request: ApiRequest = {
method: 'POST',
endpoint: 'products/update-stock',
params: { productId, stock },
platform: this.platform
};
return await this.simulator.simulateApiCall(request);
}
async updateOrderStatus(orderId: string, status: string): Promise<ApiResponse> {
const request: ApiRequest = {
method: 'POST',
endpoint: 'orders/update-status',
params: { orderId, status },
platform: this.platform
};
return await this.simulator.simulateApiCall(request);
}
async syncOrders(): Promise<ApiResponse> {
// TikTok订单同步逻辑
console.log('开始同步TikTok订单...');
try {
// 获取所有订单
const ordersResponse = await this.getOrders({ limit: 100 });
if (!ordersResponse.success) {
return ordersResponse;
}
// 处理同步逻辑
const syncedOrders = await this.processOrderSync(ordersResponse.data.orders);
return {
success: true,
data: {
message: 'TikTok订单同步完成',
syncedCount: syncedOrders.length,
orders: syncedOrders
},
timestamp: Date.now()
};
} catch (error) {
return {
success: false,
error: `TikTok订单同步失败: ${error}`,
timestamp: Date.now()
};
}
}
async syncProducts(): Promise<ApiResponse> {
// TikTok商品同步逻辑
return await this.getProducts({ limit: 100 });
}
async batchUpdatePrices(updates: Array<{productId: string, price: number}>): Promise<ApiResponse> {
// TikTok批量价格更新逻辑
console.log(`开始批量更新${updates.length}个商品价格...`);
const results = [];
for (const update of updates) {
try {
const result = await this.updateProductPrice(update.productId, update.price);
results.push({
productId: update.productId,
success: result.success,
error: result.error
});
// 添加延迟避免触发反爬虫机制
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
results.push({
productId: update.productId,
success: false,
error: error instanceof Error ? error.message : '未知错误'
});
}
}
const successCount = results.filter(r => r.success).length;
return {
success: successCount > 0,
data: {
message: `批量更新完成,成功${successCount}个,失败${results.length - successCount}`,
results
},
timestamp: Date.now()
};
}
private async processOrderSync(orders: any[]): Promise<any[]> {
// 处理订单同步逻辑
return orders.map(order => ({
...order,
syncedAt: new Date().toISOString(),
platform: this.platform
}));
}
}
/**
* Shopee平台适配器
*/
class ShopeeAdapter extends BasePlatformAdapter {
constructor(simulator: ApiSimulator) {
super('shopee', simulator);
}
// Shopee特有的实现
async getOrders(params: any): Promise<ApiResponse> {
// Shopee订单获取逻辑
return {
success: true,
data: { orders: [], total: 0 },
timestamp: Date.now()
};
}
async getProducts(params: any): Promise<ApiResponse> {
// Shopee商品获取逻辑
return {
success: true,
data: { products: [], total: 0 },
timestamp: Date.now()
};
}
async getInventory(params: any): Promise<ApiResponse> {
// Shopee库存获取逻辑
return {
success: true,
data: { inventory: [] },
timestamp: Date.now()
};
}
async updateProductPrice(productId: string, price: number): Promise<ApiResponse> {
// Shopee价格更新逻辑
return {
success: true,
data: { message: 'Shopee价格更新成功' },
timestamp: Date.now()
};
}
async updateProductStock(productId: string, stock: number): Promise<ApiResponse> {
// Shopee库存更新逻辑
return {
success: true,
data: { message: 'Shopee库存更新成功' },
timestamp: Date.now()
};
}
async updateOrderStatus(orderId: string, status: string): Promise<ApiResponse> {
// Shopee订单状态更新逻辑
return {
success: true,
data: { message: 'Shopee订单状态更新成功' },
timestamp: Date.now()
};
}
async syncOrders(): Promise<ApiResponse> {
// Shopee订单同步逻辑
return {
success: true,
data: { message: 'Shopee订单同步完成', syncedCount: 0 },
timestamp: Date.now()
};
}
async syncProducts(): Promise<ApiResponse> {
// Shopee商品同步逻辑
return {
success: true,
data: { message: 'Shopee商品同步完成', syncedCount: 0 },
timestamp: Date.now()
};
}
async batchUpdatePrices(updates: Array<{productId: string, price: number}>): Promise<ApiResponse> {
// Shopee批量价格更新逻辑
return {
success: true,
data: { message: 'Shopee批量价格更新完成', results: updates },
timestamp: Date.now()
};
}
}
/**
* Amazon平台适配器
*/
class AmazonAdapter extends BasePlatformAdapter {
constructor(simulator: ApiSimulator) {
super('amazon', simulator);
}
// Amazon特有的实现
async getOrders(params: any): Promise<ApiResponse> {
// Amazon订单获取逻辑
return {
success: true,
data: { orders: [], total: 0 },
timestamp: Date.now()
};
}
async getProducts(params: any): Promise<ApiResponse> {
// Amazon商品获取逻辑
return {
success: true,
data: { products: [], total: 0 },
timestamp: Date.now()
};
}
async getInventory(params: any): Promise<ApiResponse> {
// Amazon库存获取逻辑
return {
success: true,
data: { inventory: [] },
timestamp: Date.now()
};
}
async updateProductPrice(productId: string, price: number): Promise<ApiResponse> {
// Amazon价格更新逻辑
return {
success: true,
data: { message: 'Amazon价格更新成功' },
timestamp: Date.now()
};
}
async updateProductStock(productId: string, stock: number): Promise<ApiResponse> {
// Amazon库存更新逻辑
return {
success: true,
data: { message: 'Amazon库存更新成功' },
timestamp: Date.now()
};
}
async updateOrderStatus(orderId: string, status: string): Promise<ApiResponse> {
// Amazon订单状态更新逻辑
return {
success: true,
data: { message: 'Amazon订单状态更新成功' },
timestamp: Date.now()
};
}
async syncOrders(): Promise<ApiResponse> {
// Amazon订单同步逻辑
return {
success: true,
data: { message: 'Amazon订单同步完成', syncedCount: 0 },
timestamp: Date.now()
};
}
async syncProducts(): Promise<ApiResponse> {
// Amazon商品同步逻辑
return {
success: true,
data: { message: 'Amazon商品同步完成', syncedCount: 0 },
timestamp: Date.now()
};
}
async batchUpdatePrices(updates: Array<{productId: string, price: number}>): Promise<ApiResponse> {
// Amazon批量价格更新逻辑
return {
success: true,
data: { message: 'Amazon批量价格更新完成', results: updates },
timestamp: Date.now()
};
}
}
/**
* 平台适配器工厂
*/
export class PlatformAdapterFactory {
private static simulator: ApiSimulator;
static initialize(simulator: ApiSimulator) {
this.simulator = simulator;
}
static createAdapter(platform: string): PlatformAdapter {
if (!this.simulator) {
throw new Error('PlatformAdapterFactory未初始化请先调用initialize方法');
}
switch (platform.toLowerCase()) {
case 'tiktok':
return new TikTokAdapter(this.simulator);
case 'shopee':
return new ShopeeAdapter(this.simulator);
case 'amazon':
return new AmazonAdapter(this.simulator);
default:
throw new Error(`不支持的平台: ${platform}`);
}
}
static getSupportedPlatforms(): string[] {
return ['tiktok', 'shopee', 'amazon'];
}
}
/**
* 平台管理器 - 管理多个平台的适配器
*/
export class PlatformManager {
private adapters: Map<string, PlatformAdapter> = new Map();
private simulator: ApiSimulator;
constructor(simulator: ApiSimulator) {
this.simulator = simulator;
PlatformAdapterFactory.initialize(simulator);
}
/**
* 获取平台适配器
*/
getAdapter(platform: string): PlatformAdapter {
if (!this.adapters.has(platform)) {
const adapter = PlatformAdapterFactory.createAdapter(platform);
this.adapters.set(platform, adapter);
}
return this.adapters.get(platform)!;
}
/**
* 检查平台是否支持
*/
isPlatformSupported(platform: string): boolean {
return PlatformAdapterFactory.getSupportedPlatforms().includes(platform.toLowerCase());
}
/**
* 获取所有支持的平台
*/
getSupportedPlatforms(): string[] {
return PlatformAdapterFactory.getSupportedPlatforms();
}
/**
* 获取已登录的平台列表
*/
getLoggedInPlatforms(): string[] {
const loggedIn: string[] = [];
for (const [platform, adapter] of this.adapters.entries()) {
if (adapter.isLoggedIn()) {
loggedIn.push(platform);
}
}
return loggedIn;
}
/**
* 批量执行跨平台操作
*/
async batchOperation(platforms: string[], operation: (adapter: PlatformAdapter) => Promise<ApiResponse>): Promise<Record<string, ApiResponse>> {
const results: Record<string, ApiResponse> = {};
for (const platform of platforms) {
try {
const adapter = this.getAdapter(platform);
results[platform] = await operation(adapter);
} catch (error) {
results[platform] = {
success: false,
error: error instanceof Error ? error.message : '未知错误',
timestamp: Date.now()
};
}
}
return results;
}
}
// 导出单例实例
import { apiSimulator } from './api-simulator';
export const platformManager = new PlatformManager(apiSimulator);

View File

@@ -0,0 +1,567 @@
/**
* 远程桌面功能 - 作为API模拟的兜底方案
* 当API模拟失败时通过远程桌面直接操作电商平台
*/
import { chromium, Browser, Page } from 'playwright';
import { WebSocketServer, WebSocket } from 'ws';
import { createServer } from 'http';
import * as fs from 'fs';
import * as path from 'path';
interface RemoteDesktopConfig {
port: number;
headless: boolean;
proxy?: string;
screenWidth: number;
screenHeight: number;
frameRate: number;
}
interface RemoteSession {
id: string;
platform: string;
page: Page;
browser: Browser;
status: 'connected' | 'disconnected' | 'error';
lastActivity: Date;
screenStream?: any; // 屏幕流
}
interface RemoteCommand {
type: 'mouse' | 'keyboard' | 'screenshot' | 'navigate' | 'execute';
action: string;
data: any;
}
interface RemoteResponse {
success: boolean;
type: string;
data?: any;
error?: string;
timestamp: number;
}
/**
* 远程桌面服务
*/
export class RemoteDesktopService {
private config: RemoteDesktopConfig;
private sessions: Map<string, RemoteSession> = new Map();
private wss: WebSocketServer;
private browser: Browser | null = null;
constructor(config: RemoteDesktopConfig) {
this.config = config;
// 创建HTTP服务器
const server = createServer((req, res) => {
this.handleHttpRequest(req, res);
});
// 创建WebSocket服务器
this.wss = new WebSocketServer({ server });
server.listen(config.port);
this.setupWebSocketHandlers();
}
/**
* 设置WebSocket处理器
*/
private setupWebSocketHandlers(): void {
this.wss.on('connection', (ws: WebSocket) => {
console.log('远程桌面客户端连接建立');
ws.on('message', async (data: Buffer) => {
try {
const command: RemoteCommand = JSON.parse(data.toString());
const response = await this.handleRemoteCommand(command, ws);
ws.send(JSON.stringify(response));
} catch (error) {
ws.send(JSON.stringify({
success: false,
type: 'error',
error: error instanceof Error ? error.message : '命令处理失败',
timestamp: Date.now()
}));
}
});
ws.on('close', () => {
console.log('远程桌面客户端连接关闭');
});
});
}
/**
* 处理HTTP请求
*/
private handleHttpRequest(req: any, res: any): void {
const { url, method } = req;
res.setHeader('Content-Type', 'application/json');
res.setHeader('Access-Control-Allow-Origin', '*');
if (url === '/health' && method === 'GET') {
res.writeHead(200);
res.end(JSON.stringify({
status: 'healthy',
service: 'remote-desktop',
activeSessions: this.sessions.size,
timestamp: Date.now()
}));
} else if (url === '/sessions' && method === 'GET') {
res.writeHead(200);
res.end(JSON.stringify(Array.from(this.sessions.values()).map(session => ({
id: session.id,
platform: session.platform,
status: session.status,
lastActivity: session.lastActivity
}))));
} else {
res.writeHead(404);
res.end(JSON.stringify({ error: '接口不存在' }));
}
}
/**
* 处理远程命令
*/
private async handleRemoteCommand(command: RemoteCommand, ws: WebSocket): Promise<RemoteResponse> {
try {
switch (command.type) {
case 'navigate':
return await this.handleNavigateCommand(command);
case 'mouse':
return await this.handleMouseCommand(command);
case 'keyboard':
return await this.handleKeyboardCommand(command);
case 'screenshot':
return await this.handleScreenshotCommand(command);
case 'execute':
return await this.handleExecuteCommand(command);
default:
return {
success: false,
type: 'error',
error: '未知命令类型',
timestamp: Date.now()
};
}
} catch (error) {
return {
success: false,
type: 'error',
error: error instanceof Error ? error.message : '命令执行失败',
timestamp: Date.now()
};
}
}
/**
* 创建远程会话
*/
async createSession(platform: string): Promise<RemoteSession> {
if (!this.browser) {
this.browser = await chromium.launch({
headless: this.config.headless,
proxy: this.config.proxy ? { server: this.config.proxy } : undefined,
args: [
`--window-size=${this.config.screenWidth},${this.config.screenHeight}`,
'--disable-web-security',
'--disable-features=IsolateOrigins,site-per-process'
]
});
}
const page = await this.browser.newPage();
await page.setViewportSize({
width: this.config.screenWidth,
height: this.config.screenHeight
});
const sessionId = this.generateSessionId();
const session: RemoteSession = {
id: sessionId,
platform,
page,
browser: this.browser,
status: 'connected',
lastActivity: new Date()
};
this.sessions.set(sessionId, session);
// 设置页面事件监听
await this.setupPageEventListeners(page, sessionId);
console.log(`远程会话创建成功: ${sessionId} (${platform})`);
return session;
}
/**
* 设置页面事件监听器
*/
private async setupPageEventListeners(page: Page, sessionId: string): Promise<void> {
// 监听页面导航
page.on('framenavigated', async (frame) => {
if (frame === page.mainFrame()) {
console.log(`会话 ${sessionId} 页面导航: ${frame.url()}`);
}
});
// 监听控制台消息
page.on('console', (msg) => {
console.log(`会话 ${sessionId} 控制台:`, msg.text());
});
// 监听页面错误
page.on('pageerror', (error) => {
console.error(`会话 ${sessionId} 页面错误:`, error);
});
}
/**
* 处理导航命令
*/
private async handleNavigateCommand(command: RemoteCommand): Promise<RemoteResponse> {
const { url, sessionId } = command.data;
const session = this.sessions.get(sessionId);
if (!session) {
return {
success: false,
type: 'navigate',
error: '会话不存在',
timestamp: Date.now()
};
}
try {
await session.page.goto(url, { waitUntil: 'networkidle' });
session.lastActivity = new Date();
return {
success: true,
type: 'navigate',
data: {
message: '页面导航成功',
url: session.page.url(),
title: await session.page.title()
},
timestamp: Date.now()
};
} catch (error) {
return {
success: false,
type: 'navigate',
error: `页面导航失败: ${error}`,
timestamp: Date.now()
};
}
}
/**
* 处理鼠标命令
*/
private async handleMouseCommand(command: RemoteCommand): Promise<RemoteResponse> {
const { sessionId, action, x, y, selector } = command.data;
const session = this.sessions.get(sessionId);
if (!session) {
return {
success: false,
type: 'mouse',
error: '会话不存在',
timestamp: Date.now()
};
}
try {
switch (action) {
case 'click':
if (selector) {
await session.page.click(selector);
} else if (x !== undefined && y !== undefined) {
await session.page.mouse.click(x, y);
}
break;
case 'doubleClick':
if (selector) {
await session.page.dblclick(selector);
} else if (x !== undefined && y !== undefined) {
await session.page.mouse.dblclick(x, y);
}
break;
case 'hover':
if (selector) {
await session.page.hover(selector);
} else if (x !== undefined && y !== undefined) {
await session.page.mouse.move(x, y);
}
break;
case 'drag':
const { startX, startY, endX, endY } = command.data;
await session.page.mouse.move(startX, startY);
await session.page.mouse.down();
await session.page.mouse.move(endX, endY);
await session.page.mouse.up();
break;
default:
return {
success: false,
type: 'mouse',
error: '未知的鼠标操作',
timestamp: Date.now()
};
}
session.lastActivity = new Date();
return {
success: true,
type: 'mouse',
data: { message: '鼠标操作成功' },
timestamp: Date.now()
};
} catch (error) {
return {
success: false,
type: 'mouse',
error: `鼠标操作失败: ${error}`,
timestamp: Date.now()
};
}
}
/**
* 处理键盘命令
*/
private async handleKeyboardCommand(command: RemoteCommand): Promise<RemoteResponse> {
const { sessionId, action, text, selector, key } = command.data;
const session = this.sessions.get(sessionId);
if (!session) {
return {
success: false,
type: 'keyboard',
error: '会话不存在',
timestamp: Date.now()
};
}
try {
switch (action) {
case 'type':
if (selector) {
await session.page.fill(selector, text);
} else {
await session.page.keyboard.type(text);
}
break;
case 'press':
await session.page.keyboard.press(key);
break;
case 'selectAll':
await session.page.keyboard.press('Control+A');
break;
case 'copy':
await session.page.keyboard.press('Control+C');
break;
case 'paste':
await session.page.keyboard.press('Control+V');
break;
default:
return {
success: false,
type: 'keyboard',
error: '未知的键盘操作',
timestamp: Date.now()
};
}
session.lastActivity = new Date();
return {
success: true,
type: 'keyboard',
data: { message: '键盘操作成功' },
timestamp: Date.now()
};
} catch (error) {
return {
success: false,
type: 'keyboard',
error: `键盘操作失败: ${error}`,
timestamp: Date.now()
};
}
}
/**
* 处理截图命令
*/
private async handleScreenshotCommand(command: RemoteCommand): Promise<RemoteResponse> {
const { sessionId, fullPage = false } = command.data;
const session = this.sessions.get(sessionId);
if (!session) {
return {
success: false,
type: 'screenshot',
error: '会话不存在',
timestamp: Date.now()
};
}
try {
const screenshot = await session.page.screenshot({
fullPage,
type: 'jpeg',
quality: 80
});
session.lastActivity = new Date();
return {
success: true,
type: 'screenshot',
data: {
message: '截图成功',
screenshot: screenshot.toString('base64'),
format: 'base64'
},
timestamp: Date.now()
};
} catch (error) {
return {
success: false,
type: 'screenshot',
error: `截图失败: ${error}`,
timestamp: Date.now()
};
}
}
/**
* 处理执行命令
*/
private async handleExecuteCommand(command: RemoteCommand): Promise<RemoteResponse> {
const { sessionId, script, args = [] } = command.data;
const session = this.sessions.get(sessionId);
if (!session) {
return {
success: false,
type: 'execute',
error: '会话不存在',
timestamp: Date.now()
};
}
try {
const result = await session.page.evaluate((script, args) => {
// 在页面上下文中执行脚本
return eval(`(${script})`)(...args);
}, script, args);
session.lastActivity = new Date();
return {
success: true,
type: 'execute',
data: {
message: '脚本执行成功',
result
},
timestamp: Date.now()
};
} catch (error) {
return {
success: false,
type: 'execute',
error: `脚本执行失败: ${error}`,
timestamp: Date.now()
};
}
}
/**
* 关闭会话
*/
async closeSession(sessionId: string): Promise<void> {
const session = this.sessions.get(sessionId);
if (session) {
await session.page.close();
this.sessions.delete(sessionId);
console.log(`远程会话已关闭: ${sessionId}`);
}
}
/**
* 获取会话列表
*/
getSessions(): RemoteSession[] {
return Array.from(this.sessions.values());
}
/**
* 启动远程桌面服务
*/
async start(): Promise<void> {
console.log(`远程桌面服务启动在端口 ${this.config.port}`);
console.log(`健康检查: http://localhost:${this.config.port}/health`);
console.log('等待远程桌面客户端连接...');
}
/**
* 停止远程桌面服务
*/
async stop(): Promise<void> {
// 关闭所有会话
for (const session of this.sessions.values()) {
await session.page.close();
}
this.sessions.clear();
// 关闭浏览器
if (this.browser) {
await this.browser.close();
this.browser = null;
}
// 关闭WebSocket服务器
this.wss.close();
console.log('远程桌面服务已停止');
}
/**
* 生成会话ID
*/
private generateSessionId(): string {
return `rdp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
}
// 导出默认配置
export const defaultRemoteDesktopConfig: RemoteDesktopConfig = {
port: 8081,
headless: false, // 远程桌面需要显示界面
screenWidth: 1920,
screenHeight: 1080,
frameRate: 30
};
// 导出单例实例
export const remoteDesktopService = new RemoteDesktopService(defaultRemoteDesktopConfig);

View File

@@ -0,0 +1,460 @@
/**
* 任务调度系统 - 支持自动化任务和定时操作
* 类似cron的定时任务系统支持复杂的工作流
*/
import { PlatformManager } from './platform-adapter';
import { ApiResponse } from './api-simulator';
interface TaskConfig {
id: string;
name: string;
description?: string;
platform: string;
type: 'sync' | 'update' | 'batch' | 'custom';
schedule: string; // cron表达式
enabled: boolean;
parameters: Record<string, any>;
retryCount: number;
retryDelay: number; // 重试延迟(毫秒)
timeout: number; // 超时时间(毫秒)
}
interface TaskExecution {
id: string;
taskId: string;
startTime: Date;
endTime?: Date;
status: 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
result?: ApiResponse;
error?: string;
retryCount: number;
}
interface TaskHistory {
taskId: string;
executions: TaskExecution[];
lastRun?: Date;
nextRun?: Date;
successCount: number;
failureCount: number;
}
interface WorkflowStep {
id: string;
name: string;
type: 'sync' | 'update' | 'condition' | 'delay';
parameters: Record<string, any>;
nextStep?: string; // 下一步ID
condition?: (result: ApiResponse) => boolean; // 条件判断
}
interface WorkflowConfig {
id: string;
name: string;
steps: WorkflowStep[];
schedule?: string;
enabled: boolean;
}
/**
* 任务调度器
*/
export class TaskScheduler {
private tasks: Map<string, TaskConfig> = new Map();
private workflows: Map<string, WorkflowConfig> = new Map();
private executions: Map<string, TaskExecution> = new Map();
private history: Map<string, TaskHistory> = new Map();
private timers: Map<string, NodeJS.Timeout> = new Map();
private platformManager: PlatformManager;
constructor(platformManager: PlatformManager) {
this.platformManager = platformManager;
this.loadDefaultTasks();
}
/**
* 加载默认任务配置
*/
private loadDefaultTasks(): void {
const defaultTasks: TaskConfig[] = [
{
id: 'sync-orders-daily',
name: '每日订单同步',
description: '每天凌晨同步所有平台的订单数据',
platform: 'all',
type: 'sync',
schedule: '0 2 * * *', // 每天凌晨2点
enabled: true,
parameters: { limit: 1000, syncAll: true },
retryCount: 3,
retryDelay: 5000,
timeout: 300000 // 5分钟
},
{
id: 'sync-products-hourly',
name: '每小时商品同步',
description: '每小时同步商品数据',
platform: 'all',
type: 'sync',
schedule: '0 * * * *', // 每小时
enabled: true,
parameters: { limit: 500 },
retryCount: 2,
retryDelay: 3000,
timeout: 180000 // 3分钟
},
{
id: 'price-monitoring',
name: '价格监控',
description: '监控商品价格变化',
platform: 'all',
type: 'custom',
schedule: '*/15 * * * *', // 每15分钟
enabled: true,
parameters: { monitorInterval: 15 },
retryCount: 1,
retryDelay: 2000,
timeout: 120000 // 2分钟
}
];
defaultTasks.forEach(task => this.addTask(task));
}
/**
* 添加任务
*/
addTask(task: TaskConfig): void {
this.tasks.set(task.id, task);
this.history.set(task.id, {
taskId: task.id,
executions: [],
successCount: 0,
failureCount: 0
});
if (task.enabled) {
this.scheduleTask(task);
}
console.log(`任务已添加: ${task.name} (${task.id})`);
}
/**
* 调度任务
*/
private scheduleTask(task: TaskConfig): void {
if (this.timers.has(task.id)) {
clearTimeout(this.timers.get(task.id)!);
}
const nextRun = this.calculateNextRun(task.schedule);
const delay = nextRun.getTime() - Date.now();
if (delay > 0) {
const timer = setTimeout(() => {
this.executeTask(task.id);
// 重新调度下一次执行
this.scheduleTask(task);
}, delay);
this.timers.set(task.id, timer);
// 更新历史记录
const history = this.history.get(task.id);
if (history) {
history.nextRun = nextRun;
}
console.log(`任务已调度: ${task.name}, 下次执行: ${nextRun.toLocaleString()}`);
}
}
/**
* 计算下次执行时间
*/
private calculateNextRun(cronExpression: string): Date {
// 简化的cron表达式解析实际项目中应该使用cron库
const [minute, hour, dayOfMonth, month, dayOfWeek] = cronExpression.split(' ');
const now = new Date();
const nextRun = new Date(now);
// 简单的实现实际应该使用成熟的cron库
if (minute === '*') {
nextRun.setMinutes(now.getMinutes() + 1);
} else if (minute.startsWith('*/')) {
const interval = parseInt(minute.substring(2));
nextRun.setMinutes(now.getMinutes() + interval);
} else {
nextRun.setMinutes(parseInt(minute));
}
return nextRun;
}
/**
* 执行任务
*/
async executeTask(taskId: string, manual: boolean = false): Promise<TaskExecution> {
const task = this.tasks.get(taskId);
if (!task) {
throw new Error(`任务不存在: ${taskId}`);
}
const execution: TaskExecution = {
id: this.generateId(),
taskId,
startTime: new Date(),
status: 'running',
retryCount: 0
};
this.executions.set(execution.id, execution);
// 添加到历史记录
const history = this.history.get(taskId);
if (history) {
history.executions.push(execution);
history.lastRun = execution.startTime;
}
console.log(`开始执行任务: ${task.name}`);
try {
const result = await this.executeTaskWithRetry(task, execution);
execution.endTime = new Date();
execution.status = result.success ? 'completed' : 'failed';
execution.result = result;
if (result.success) {
console.log(`任务执行成功: ${task.name}`);
if (history) history.successCount++;
} else {
console.error(`任务执行失败: ${task.name}`, result.error);
if (history) history.failureCount++;
}
return execution;
} catch (error) {
execution.endTime = new Date();
execution.status = 'failed';
execution.error = error instanceof Error ? error.message : '未知错误';
console.error(`任务执行异常: ${task.name}`, error);
if (history) history.failureCount++;
return execution;
}
}
/**
* 带重试的任务执行
*/
private async executeTaskWithRetry(task: TaskConfig, execution: TaskExecution): Promise<ApiResponse> {
for (let attempt = 0; attempt <= task.retryCount; attempt++) {
execution.retryCount = attempt;
try {
const result = await this.executeSingleTask(task);
return result;
} catch (error) {
if (attempt === task.retryCount) {
throw error; // 最后一次重试失败
}
console.log(`任务执行失败,${task.retryDelay}ms后重试 (${attempt + 1}/${task.retryCount})`);
await new Promise(resolve => setTimeout(resolve, task.retryDelay));
}
}
throw new Error('任务执行失败,重试次数已用尽');
}
/**
* 执行单个任务
*/
private async executeSingleTask(task: TaskConfig): Promise<ApiResponse> {
const timeoutPromise = new Promise<never>((_, reject) => {
setTimeout(() => reject(new Error('任务执行超时')), task.timeout);
});
const taskPromise = (async () => {
switch (task.type) {
case 'sync':
return await this.executeSyncTask(task);
case 'update':
return await this.executeUpdateTask(task);
case 'batch':
return await this.executeBatchTask(task);
case 'custom':
return await this.executeCustomTask(task);
default:
throw new Error(`不支持的任务类型: ${task.type}`);
}
})();
return await Promise.race([taskPromise, timeoutPromise]);
}
/**
* 执行同步任务
*/
private async executeSyncTask(task: TaskConfig): Promise<ApiResponse> {
const platforms = task.platform === 'all'
? this.platformManager.getLoggedInPlatforms()
: [task.platform];
if (platforms.length === 0) {
return {
success: false,
error: '没有已登录的平台',
timestamp: Date.now()
};
}
const results = await this.platformManager.batchOperation(platforms, async (adapter) => {
if (task.name.includes('订单')) {
return await adapter.syncOrders();
} else if (task.name.includes('商品')) {
return await adapter.syncProducts();
} else {
return await adapter.getProducts(task.parameters);
}
});
const successCount = Object.values(results).filter(r => r.success).length;
return {
success: successCount > 0,
data: {
message: `同步任务完成,成功${successCount}个平台,失败${platforms.length - successCount}`,
results
},
timestamp: Date.now()
};
}
/**
* 执行更新任务
*/
private async executeUpdateTask(task: TaskConfig): Promise<ApiResponse> {
// 更新任务实现
return {
success: true,
data: { message: '更新任务执行完成' },
timestamp: Date.now()
};
}
/**
* 执行批量任务
*/
private async executeBatchTask(task: TaskConfig): Promise<ApiResponse> {
// 批量任务实现
return {
success: true,
data: { message: '批量任务执行完成' },
timestamp: Date.now()
};
}
/**
* 执行自定义任务
*/
private async executeCustomTask(task: TaskConfig): Promise<ApiResponse> {
// 自定义任务实现
return {
success: true,
data: { message: '自定义任务执行完成' },
timestamp: Date.now()
};
}
/**
* 添加工作流
*/
addWorkflow(workflow: WorkflowConfig): void {
this.workflows.set(workflow.id, workflow);
if (workflow.schedule && workflow.enabled) {
// 调度工作流执行
this.scheduleWorkflow(workflow);
}
console.log(`工作流已添加: ${workflow.name}`);
}
/**
* 调度工作流
*/
private scheduleWorkflow(workflow: WorkflowConfig): void {
// 工作流调度逻辑
console.log(`工作流已调度: ${workflow.name}`);
}
/**
* 获取任务列表
*/
getTasks(): TaskConfig[] {
return Array.from(this.tasks.values());
}
/**
* 获取任务执行历史
*/
getTaskHistory(taskId: string): TaskHistory | undefined {
return this.history.get(taskId);
}
/**
* 获取所有任务历史
*/
getAllHistory(): Map<string, TaskHistory> {
return new Map(this.history);
}
/**
* 启用/禁用任务
*/
setTaskEnabled(taskId: string, enabled: boolean): void {
const task = this.tasks.get(taskId);
if (task) {
task.enabled = enabled;
if (enabled) {
this.scheduleTask(task);
} else {
const timer = this.timers.get(taskId);
if (timer) {
clearTimeout(timer);
this.timers.delete(taskId);
}
}
}
}
/**
* 停止所有任务
*/
stopAllTasks(): void {
for (const timer of this.timers.values()) {
clearTimeout(timer);
}
this.timers.clear();
console.log('所有任务已停止');
}
/**
* 生成唯一ID
*/
private generateId(): string {
return `task-exec-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
}
// 导出单例实例
import { platformManager } from './platform-adapter';
export const taskScheduler = new TaskScheduler(platformManager);

View File

@@ -0,0 +1,684 @@
/**
* WEB远程控制中间层
* 介于API模拟和远程桌面之间的重要层级
* 通过Web技术实现远程控制比API模拟更灵活比远程桌面更轻量
*/
import { chromium, Browser, Page } from 'playwright';
import { WebSocketServer, WebSocket } from 'ws';
import { createServer } from 'http';
import * as fs from 'fs';
import * as path from 'path';
interface WebRemoteConfig {
port: number;
headless: boolean;
proxy?: string;
viewport: { width: number; height: number };
userAgent: string;
}
interface WebSession {
id: string;
platform: string;
page: Page;
browser: Browser;
status: 'ready' | 'active' | 'error' | 'closed';
lastActivity: Date;
currentUrl: string;
title: string;
cookies: any[];
localStorage: Record<string, any>;
}
interface WebCommand {
type: 'navigate' | 'click' | 'type' | 'scroll' | 'screenshot' | 'eval' | 'cookies' | 'storage';
action: string;
data: any;
sessionId: string;
}
interface WebResponse {
success: boolean;
type: string;
data?: any;
error?: string;
sessionId: string;
timestamp: number;
}
/**
* WEB远程控制服务
*/
export class WebRemoteService {
private config: WebRemoteConfig;
private sessions: Map<string, WebSession> = new Map();
private wss: WebSocketServer;
private browser: Browser | null = null;
constructor(config: WebRemoteConfig) {
this.config = config;
// 创建HTTP服务器
const server = createServer((req, res) => {
this.handleHttpRequest(req, res);
});
// 创建WebSocket服务器
this.wss = new WebSocketServer({ server });
server.listen(config.port);
this.setupWebSocketHandlers();
this.initializeBrowser();
}
/**
* 初始化浏览器
*/
private async initializeBrowser(): Promise<void> {
try {
this.browser = await chromium.launch({
headless: this.config.headless,
proxy: this.config.proxy ? { server: this.config.proxy } : undefined,
args: [
'--disable-web-security',
'--disable-features=IsolateOrigins,site-per-process',
'--allow-running-insecure-content',
'--disable-blink-features=AutomationControlled'
]
});
console.log('WEB远程控制服务浏览器初始化完成');
} catch (error) {
console.error('浏览器初始化失败:', error);
}
}
/**
* 设置WebSocket处理器
*/
private setupWebSocketHandlers(): void {
this.wss.on('connection', (ws: WebSocket) => {
console.log('WEB远程控制客户端连接建立');
ws.on('message', async (data: Buffer) => {
try {
const command: WebCommand = JSON.parse(data.toString());
const response = await this.handleWebCommand(command);
ws.send(JSON.stringify(response));
} catch (error) {
ws.send(JSON.stringify({
success: false,
type: 'error',
error: error instanceof Error ? error.message : '命令处理失败',
sessionId: command?.sessionId || 'unknown',
timestamp: Date.now()
}));
}
});
ws.on('close', () => {
console.log('WEB远程控制客户端连接关闭');
});
});
}
/**
* 处理HTTP请求
*/
private handleHttpRequest(req: any, res: any): void {
const { url, method } = req;
res.setHeader('Content-Type', 'application/json');
res.setHeader('Access-Control-Allow-Origin', '*');
if (url === '/health' && method === 'GET') {
res.writeHead(200);
res.end(JSON.stringify({
status: 'healthy',
service: 'web-remote',
activeSessions: this.sessions.size,
browser: this.browser ? 'connected' : 'disconnected',
timestamp: Date.now()
}));
} else if (url === '/sessions' && method === 'GET') {
res.writeHead(200);
res.end(JSON.stringify(this.getSessionList()));
} else {
res.writeHead(404);
res.end(JSON.stringify({ error: '接口不存在' }));
}
}
/**
* 创建WEB会话
*/
async createSession(platform: string, initialUrl?: string): Promise<WebSession> {
if (!this.browser) {
await this.initializeBrowser();
}
const context = await this.browser!.newContext({
viewport: this.config.viewport,
userAgent: this.config.userAgent,
ignoreHTTPSErrors: true
});
const page = await context.newPage();
// 设置页面事件监听
await this.setupPageEventListeners(page);
const sessionId = this.generateSessionId();
const session: WebSession = {
id: sessionId,
platform,
page,
browser: this.browser!,
status: 'ready',
lastActivity: new Date(),
currentUrl: '',
title: '',
cookies: [],
localStorage: {}
};
this.sessions.set(sessionId, session);
// 如果有初始URL导航到该页面
if (initialUrl) {
await this.navigateToUrl(session, initialUrl);
}
console.log(`WEB会话创建成功: ${sessionId} (${platform})`);
return session;
}
/**
* 设置页面事件监听器
*/
private async setupPageEventListeners(page: Page): Promise<void> {
// 监听页面导航
page.on('framenavigated', async (frame) => {
if (frame === page.mainFrame()) {
const session = this.findSessionByPage(page);
if (session) {
session.currentUrl = frame.url();
session.lastActivity = new Date();
}
}
});
// 监听页面加载完成
page.on('load', async () => {
const session = this.findSessionByPage(page);
if (session) {
session.title = await page.title();
session.lastActivity = new Date();
// 获取当前页面的cookies和localStorage
try {
session.cookies = await page.context().cookies();
session.localStorage = await page.evaluate(() => {
const storage: Record<string, any> = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key) {
storage[key] = localStorage.getItem(key);
}
}
return storage;
});
} catch (error) {
console.warn('获取页面存储信息失败:', error);
}
}
});
// 监听控制台消息
page.on('console', (msg) => {
const session = this.findSessionByPage(page);
if (session) {
console.log(`WEB会话 ${session.id} 控制台:`, msg.text());
}
});
// 监听页面错误
page.on('pageerror', (error) => {
const session = this.findSessionByPage(page);
if (session) {
console.error(`WEB会话 ${session.id} 页面错误:`, error);
session.status = 'error';
}
});
}
/**
* 通过页面查找会话
*/
private findSessionByPage(page: Page): WebSession | undefined {
for (const session of this.sessions.values()) {
if (session.page === page) {
return session;
}
}
return undefined;
}
/**
* 处理WEB命令
*/
private async handleWebCommand(command: WebCommand): Promise<WebResponse> {
const session = this.sessions.get(command.sessionId);
if (!session) {
return {
success: false,
type: 'error',
error: '会话不存在',
sessionId: command.sessionId,
timestamp: Date.now()
};
}
try {
switch (command.type) {
case 'navigate':
return await this.handleNavigateCommand(session, command);
case 'click':
return await this.handleClickCommand(session, command);
case 'type':
return await this.handleTypeCommand(session, command);
case 'scroll':
return await this.handleScrollCommand(session, command);
case 'screenshot':
return await this.handleScreenshotCommand(session, command);
case 'eval':
return await this.handleEvalCommand(session, command);
case 'cookies':
return await this.handleCookiesCommand(session, command);
case 'storage':
return await this.handleStorageCommand(session, command);
default:
return {
success: false,
type: 'error',
error: '未知命令类型',
sessionId: command.sessionId,
timestamp: Date.now()
};
}
} catch (error) {
return {
success: false,
type: 'error',
error: error instanceof Error ? error.message : '命令执行失败',
sessionId: command.sessionId,
timestamp: Date.now()
};
}
}
/**
* 处理导航命令
*/
private async handleNavigateCommand(session: WebSession, command: WebCommand): Promise<WebResponse> {
const { url, waitUntil = 'networkidle' } = command.data;
try {
await session.page.goto(url, { waitUntil: waitUntil as any });
session.status = 'active';
session.lastActivity = new Date();
return {
success: true,
type: 'navigate',
data: {
message: '页面导航成功',
url: session.page.url(),
title: await session.page.title()
},
sessionId: session.id,
timestamp: Date.now()
};
} catch (error) {
session.status = 'error';
throw error;
}
}
/**
* 处理点击命令
*/
private async handleClickCommand(session: WebSession, command: WebCommand): Promise<WebResponse> {
const { selector, x, y, button = 'left', clickCount = 1 } = command.data;
try {
if (selector) {
await session.page.click(selector, { button, clickCount });
} else if (x !== undefined && y !== undefined) {
await session.page.mouse.click(x, y, { button, clickCount });
} else {
return {
success: false,
type: 'click',
error: '需要提供selector或x,y坐标',
sessionId: session.id,
timestamp: Date.now()
};
}
session.lastActivity = new Date();
return {
success: true,
type: 'click',
data: { message: '点击操作成功' },
sessionId: session.id,
timestamp: Date.now()
};
} catch (error) {
throw error;
}
}
/**
* 处理输入命令
*/
private async handleTypeCommand(session: WebSession, command: WebCommand): Promise<WebResponse> {
const { selector, text, delay = 0 } = command.data;
try {
if (selector) {
await session.page.fill(selector, text);
} else {
await session.page.keyboard.type(text, { delay });
}
session.lastActivity = new Date();
return {
success: true,
type: 'type',
data: { message: '输入操作成功' },
sessionId: session.id,
timestamp: Date.now()
};
} catch (error) {
throw error;
}
}
/**
* 处理滚动命令
*/
private async handleScrollCommand(session: WebSession, command: WebCommand): Promise<WebResponse> {
const { x, y, selector } = command.data;
try {
if (selector) {
await session.page.evaluate((sel) => {
const element = document.querySelector(sel);
if (element) {
element.scrollIntoView();
}
}, selector);
} else {
await session.page.evaluate((scrollX, scrollY) => {
window.scrollTo(scrollX, scrollY);
}, x || 0, y || 0);
}
session.lastActivity = new Date();
return {
success: true,
type: 'scroll',
data: { message: '滚动操作成功' },
sessionId: session.id,
timestamp: Date.now()
};
} catch (error) {
throw error;
}
}
/**
* 处理截图命令
*/
private async handleScreenshotCommand(session: WebSession, command: WebCommand): Promise<WebResponse> {
const { fullPage = false, quality = 80, type = 'jpeg' } = command.data;
try {
const screenshot = await session.page.screenshot({
fullPage,
type: type as 'jpeg' | 'png',
quality
});
session.lastActivity = new Date();
return {
success: true,
type: 'screenshot',
data: {
message: '截图成功',
screenshot: screenshot.toString('base64'),
format: 'base64',
size: screenshot.length
},
sessionId: session.id,
timestamp: Date.now()
};
} catch (error) {
throw error;
}
}
/**
* 处理执行脚本命令
*/
private async handleEvalCommand(session: WebSession, command: WebCommand): Promise<WebResponse> {
const { script, args = [] } = command.data;
try {
const result = await session.page.evaluate((evalScript, evalArgs) => {
// 在页面上下文中执行脚本
const func = new Function('return ' + evalScript)();
return func(...evalArgs);
}, script, args);
session.lastActivity = new Date();
return {
success: true,
type: 'eval',
data: {
message: '脚本执行成功',
result
},
sessionId: session.id,
timestamp: Date.now()
};
} catch (error) {
throw error;
}
}
/**
* 处理cookies命令
*/
private async handleCookiesCommand(session: WebSession, command: WebCommand): Promise<WebResponse> {
const { action, cookies } = command.data;
try {
if (action === 'get') {
const currentCookies = await session.page.context().cookies();
session.cookies = currentCookies;
return {
success: true,
type: 'cookies',
data: { cookies: currentCookies },
sessionId: session.id,
timestamp: Date.now()
};
} else if (action === 'set' && cookies) {
await session.page.context().addCookies(cookies);
return {
success: true,
type: 'cookies',
data: { message: 'Cookies设置成功' },
sessionId: session.id,
timestamp: Date.now()
};
} else {
return {
success: false,
type: 'cookies',
error: '不支持的cookies操作',
sessionId: session.id,
timestamp: Date.now()
};
}
} catch (error) {
throw error;
}
}
/**
* 处理存储命令
*/
private async handleStorageCommand(session: WebSession, command: WebCommand): Promise<WebResponse> {
const { action, key, value } = command.data;
try {
if (action === 'get') {
const storage = await session.page.evaluate((storageKey) => {
return localStorage.getItem(storageKey);
}, key);
return {
success: true,
type: 'storage',
data: { [key]: storage },
sessionId: session.id,
timestamp: Date.now()
};
} else if (action === 'set') {
await session.page.evaluate((storageKey, storageValue) => {
localStorage.setItem(storageKey, storageValue);
}, key, value);
return {
success: true,
type: 'storage',
data: { message: '存储设置成功' },
sessionId: session.id,
timestamp: Date.now()
};
} else {
return {
success: false,
type: 'storage',
error: '不支持的存储操作',
sessionId: session.id,
timestamp: Date.now()
};
}
} catch (error) {
throw error;
}
}
/**
* 导航到URL
*/
private async navigateToUrl(session: WebSession, url: string): Promise<void> {
try {
await session.page.goto(url, { waitUntil: 'networkidle' });
session.status = 'active';
session.currentUrl = url;
} catch (error) {
session.status = 'error';
throw error;
}
}
/**
* 获取会话列表
*/
private getSessionList(): any[] {
return Array.from(this.sessions.values()).map(session => ({
id: session.id,
platform: session.platform,
status: session.status,
currentUrl: session.currentUrl,
title: session.title,
lastActivity: session.lastActivity,
cookiesCount: session.cookies.length,
storageCount: Object.keys(session.localStorage).length
}));
}
/**
* 关闭会话
*/
async closeSession(sessionId: string): Promise<void> {
const session = this.sessions.get(sessionId);
if (session) {
await session.page.close();
this.sessions.delete(sessionId);
console.log(`WEB会话已关闭: ${sessionId}`);
}
}
/**
* 启动WEB远程服务
*/
async start(): Promise<void> {
console.log(`WEB远程控制服务启动在端口 ${this.config.port}`);
console.log(`健康检查: http://localhost:${this.config.port}/health`);
console.log('等待WEB远程控制客户端连接...');
}
/**
* 停止WEB远程服务
*/
async stop(): Promise<void> {
// 关闭所有会话
for (const session of this.sessions.values()) {
await session.page.close();
}
this.sessions.clear();
// 关闭浏览器
if (this.browser) {
await this.browser.close();
this.browser = null;
}
// 关闭WebSocket服务器
this.wss.close();
console.log('WEB远程控制服务已停止');
}
/**
* 生成会话ID
*/
private generateSessionId(): string {
return `web-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
}
// 导出默认配置
export const defaultWebRemoteConfig: WebRemoteConfig = {
port: 8082,
headless: true,
viewport: { width: 1920, height: 1080 },
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
};
// 导出单例实例
export const webRemoteService = new WebRemoteService(defaultWebRemoteConfig);