- 移除extension模块,将功能迁移至node-agent - 修复类型导出问题,使用export type明确类型导出 - 统一数据库连接方式,从直接导入改为使用config/database - 更新文档中的项目结构描述 - 添加多个服务的实用方法,如getForecast、getBalances等 - 修复类型错误和TS1205警告 - 优化RedisService调用方式 - 添加新的实体类型定义 - 更新审计日志格式,统一字段命名
264 lines
7.4 KiB
Markdown
264 lines
7.4 KiB
Markdown
# Node Agent 设计文档
|
|
|
|
> **定位**: Win Node Agent - 无 API 平台执行代理
|
|
> **更新日期**: 2026-03-20
|
|
> **最高优先级参考**: [Business_ClosedLoops.md](../00_Business/Business_ClosedLoops.md)
|
|
|
|
---
|
|
|
|
## 1. 概述
|
|
|
|
### 1.1 背景
|
|
|
|
Extension (浏览器插件) 方案存在以下局限性:
|
|
- Manifest V3 权限限制
|
|
- 反检测能力不足
|
|
- 无法多实例并发
|
|
- 任务调度不可靠
|
|
|
|
**Node Agent** 基于 Playwright 构建,提供更强大的自动化能力。
|
|
|
|
### 1.2 核心能力
|
|
|
|
| 能力 | 说明 |
|
|
|------|------|
|
|
| **无 API 平台采集** | TikTok Shop, Temu, 1688 等 |
|
|
| **自动化操作** | 刊登、订单处理、广告投放 |
|
|
| **反检测** | 指纹隔离、代理 IP、浏览器上下文隔离 |
|
|
| **多实例并发** | 支持多店铺同时运行 |
|
|
|
|
---
|
|
|
|
## 2. 技术栈
|
|
|
|
| 层级 | 技术 | 版本 | 用途 |
|
|
|------|------|------|------|
|
|
| **Runtime** | Node.js | 18+ | 运行环境 |
|
|
| **Automation** | Playwright | 1.58+ | 浏览器自动化 |
|
|
| **Language** | TypeScript | 5.x | 开发语言 |
|
|
| **Communication** | Axios | - | HTTP 通信 |
|
|
|
|
---
|
|
|
|
## 3. 架构设计
|
|
|
|
### 3.1 系统架构
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ Crawlful Hub │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
│ │ Server │ │ Hub │ │ Dashboard │ │
|
|
│ │ (业务逻辑) │◄───►│ (任务调度) │◄───►│ (控制台) │ │
|
|
│ └──────────────┘ └──────┬───────┘ └──────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌──────────────────┐ │
|
|
│ │ Node Agent │ │
|
|
│ │ (Playwright) │ │
|
|
│ └────────┬─────────┘ │
|
|
│ │ │
|
|
│ ┌───────────────────┼───────────────────┐ │
|
|
│ ▼ ▼ ▼ │
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
│ │ Browser #1 │ │ Browser #2 │ │ Browser #N │ │
|
|
│ │ (店铺A) │ │ (店铺B) │ │ (店铺N) │ │
|
|
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 3.2 核心类设计
|
|
|
|
```typescript
|
|
export class NodeAgent {
|
|
private hubUrl: string;
|
|
private nodeId: string;
|
|
private heartbeatInterval: number = 30000;
|
|
private pollInterval: number = 10000;
|
|
|
|
async start(): Promise<void>;
|
|
async register(): Promise<void>;
|
|
async heartbeat(): Promise<void>;
|
|
async pollTasks(): Promise<void>;
|
|
async executeTask(task: NodeTask): Promise<void>;
|
|
async reportReceipt(receipt: any): Promise<void>;
|
|
}
|
|
```
|
|
|
|
### 3.3 任务类型
|
|
|
|
```typescript
|
|
export enum TaskType {
|
|
COLLECT_PRODUCT = 'COLLECT_PRODUCT',
|
|
COLLECT_ORDER = 'COLLECT_ORDER',
|
|
PUBLISH_PRODUCT = 'PUBLISH_PRODUCT',
|
|
PROCESS_ORDER = 'PROCESS_ORDER',
|
|
SYNC_INVENTORY = 'SYNC_INVENTORY',
|
|
MANAGE_AD = 'MANAGE_AD',
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 4. 核心功能
|
|
|
|
### 4.1 节点注册
|
|
|
|
```typescript
|
|
private async register() {
|
|
await axios.post(`${this.hubUrl}/api/v1/nodes/register`, {
|
|
nodeId: this.nodeId,
|
|
version: '1.0.0',
|
|
os: os.type(),
|
|
hostname: os.hostname(),
|
|
shops: []
|
|
});
|
|
}
|
|
```
|
|
|
|
### 4.2 任务轮询
|
|
|
|
```typescript
|
|
private async pollTasks() {
|
|
const response = await axios.get(
|
|
`${this.hubUrl}/api/v1/nodes/pull-task?nodeId=${this.nodeId}`
|
|
);
|
|
|
|
if (response.data?.task) {
|
|
await this.executeTask(response.data.task);
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4.3 任务执行
|
|
|
|
```typescript
|
|
private async executeTask(task: NodeTask) {
|
|
try {
|
|
const browser = await chromium.launch({
|
|
headless: false,
|
|
proxy: { server: task.proxyUrl }
|
|
});
|
|
|
|
const context = await browser.newContext({
|
|
viewport: { width: 1920, height: 1080 }
|
|
});
|
|
|
|
const page = await context.newPage();
|
|
|
|
// 执行具体任务...
|
|
|
|
await browser.close();
|
|
|
|
await this.reportReceipt({
|
|
taskId: task.id,
|
|
traceId: task.traceId,
|
|
status: 'success',
|
|
resultData: {}
|
|
});
|
|
} catch (err) {
|
|
await this.reportReceipt({
|
|
taskId: task.id,
|
|
traceId: task.traceId,
|
|
status: 'failed',
|
|
errorMessage: err.message
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. 店铺隔离策略
|
|
|
|
### 5.1 一店一上下文
|
|
|
|
```typescript
|
|
interface ShopContext {
|
|
shopId: string;
|
|
profileDir: string;
|
|
proxy: ProxyConfig;
|
|
fingerprintPolicy: FingerprintConfig;
|
|
}
|
|
```
|
|
|
|
### 5.2 同店任务串行
|
|
|
|
```typescript
|
|
class TaskQueue {
|
|
private queues: Map<string, TaskQueue> = new Map();
|
|
|
|
async addTask(shopId: string, task: Task) {
|
|
if (!this.queues.has(shopId)) {
|
|
this.queues.set(shopId, new TaskQueue());
|
|
}
|
|
await this.queues.get(shopId).add(task);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. 反检测策略
|
|
|
|
| 策略 | 说明 |
|
|
|------|------|
|
|
| **指纹隔离** | 每个店铺独立浏览器指纹 |
|
|
| **代理 IP** | 每个店铺独立代理 |
|
|
| **行为模拟** | 随机延迟、鼠标轨迹 |
|
|
| **User-Agent** | 随机 UA 轮换 |
|
|
|
|
---
|
|
|
|
## 7. 目录结构
|
|
|
|
```
|
|
node-agent/
|
|
├── src/
|
|
│ ├── main.ts # 入口
|
|
│ ├── index.ts # NodeAgent 类
|
|
│ ├── tasks/ # 任务处理器
|
|
│ │ ├── collectProduct.ts
|
|
│ │ ├── publishProduct.ts
|
|
│ │ └── processOrder.ts
|
|
│ ├── platforms/ # 平台适配器
|
|
│ │ ├── tiktok.ts
|
|
│ │ ├── temu.ts
|
|
│ │ └── ali1688.ts
|
|
│ └── utils/ # 工具函数
|
|
│ ├── logger.ts
|
|
│ └── fingerprint.ts
|
|
├── package.json
|
|
└── tsconfig.json
|
|
```
|
|
|
|
---
|
|
|
|
## 8. 配置
|
|
|
|
```typescript
|
|
interface AgentConfig {
|
|
hubUrl: string;
|
|
nodeId: string;
|
|
heartbeatInterval: number;
|
|
pollInterval: number;
|
|
maxConcurrentBrowsers: number;
|
|
proxyPool: ProxyConfig[];
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 9. 相关文档
|
|
|
|
- [DOM Interaction](02_DOM_Interaction.md)
|
|
- [Backend Design](../02_Backend/Backend_Design.md)
|
|
- [Business ClosedLoops](../00_Business/Business_ClosedLoops.md)
|
|
|
|
---
|
|
|
|
*本文档基于业务闭环设计,最后更新: 2026-03-20*
|