chore: 清理归档文件和文档模板

删除不再需要的归档文件和过时的文档模板,包括多个README、安全策略、前端集成蓝图等文件,同时移除了未使用的业务文档和项目结构文件。

优化项目结构,移除冗余文件,保持代码库整洁。主要删除archive/handover目录下的多个文件及doc目录下的部分文档模板。
This commit is contained in:
2026-03-18 01:21:15 +08:00
parent 56b8a2e2f8
commit 72cd7f6f45
147 changed files with 5982 additions and 16716 deletions

View File

@@ -0,0 +1,475 @@
# DOM Interaction (Crawlful Hub Plugin)
> **定位**Crawlful Hub 插件 DOM 交互文档 - 描述如何与电商平台页面进行 DOM 交互。
> **更新日期**: 2026-03-18
---
## 1. DOM 选择器策略
### 1.1 选择器类型
| 类型 | 示例 | 适用场景 |
|------|------|----------|
| ID | `#productTitle` | 唯一元素 |
| Class | `.a-price-whole` | 样式类 |
| Attribute | `[data-asin]` | 数据属性 |
| XPath | `//span[@id='price']` | 复杂结构 |
| CSS Selector | `div.product > h1` | 层级关系 |
### 1.2 平台选择器映射
#### Amazon
```typescript
const amazonSelectors = {
product: {
title: '#productTitle',
price: '.a-price-whole, .a-price .a-offscreen',
listPrice: '.a-text-price .a-offscreen',
images: '#landingImage, #imgTagWrapperId img',
description: '#feature-bullets ul, #productDescription',
brand: '#bylineInfo',
asin: '[data-asin]',
rating: '#acrPopover .a-icon-alt',
reviewCount: '#acrCustomerReviewText',
availability: '#availability span',
category: '#wayfinding-breadcrumbs_container ul',
},
search: {
results: '[data-component-type="s-search-result"]',
title: 'h2 a span',
price: '.a-price-whole',
image: '.s-image',
rating: '.a-icon-alt',
},
seller: {
name: '#merchant-info a:first-child',
rating: '#merchant-info .a-icon-alt',
},
};
```
#### eBay
```typescript
const ebaySelectors = {
product: {
title: 'h1[data-testid="x-item-title-label"]',
price: '.notranslate.vi-price .notranslate',
images: '#icImg, .vi-image-gallery__image',
description: '#desc_wrapper, #ds_div',
condition: '.u-flL.condText',
seller: '.mbg-nw',
sellerRating: '.mbg-l .mbg-fb',
quantity: '#qtyTextBox',
shipping: '#fshippingCost span',
},
search: {
results: '.s-item',
title: '.s-item__title',
price: '.s-item__price',
image: '.s-item__image img',
},
};
```
#### Shopify
```typescript
const shopifySelectors = {
product: {
title: 'h1.product-title, h1[data-product-title]',
price: '.product-price, [data-product-price]',
comparePrice: '.compare-price, [data-compare-price]',
images: '.product-image, .product__media img',
description: '.product-description, [data-product-description]',
variants: '[data-variant-id]',
inventory: '[data-inventory]',
sku: '[data-sku]',
barcode: '[data-barcode]',
},
admin: {
products: 'table tbody tr',
orders: 'table tbody tr',
title: 'td:first-child a',
status: 'td:nth-child(3) span',
},
};
```
---
## 2. DOM 操作工具
### 2.1 元素提取工具
```typescript
// src/content/utils/domUtils.ts
export class DOMUtils {
/**
* 安全地获取元素文本
*/
static getText(selector: string, context: Document | Element = document): string {
const element = context.querySelector(selector);
return element?.textContent?.trim() || '';
}
/**
* 获取元素属性
*/
static getAttr(selector: string, attr: string, context: Document | Element = document): string {
const element = context.querySelector(selector);
return element?.getAttribute(attr) || '';
}
/**
* 获取多个元素的文本列表
*/
static getTextList(selector: string, context: Document | Element = document): string[] {
const elements = context.querySelectorAll(selector);
return Array.from(elements).map(el => el.textContent?.trim() || '');
}
/**
* 获取图片 URL 列表
*/
static getImageUrls(selector: string, context: Document | Element = document): string[] {
const images = context.querySelectorAll(selector);
return Array.from(images)
.map(img => {
const src = img.getAttribute('src') || img.getAttribute('data-src');
return src ? this.resolveUrl(src) : '';
})
.filter(Boolean);
}
/**
* 等待元素出现
*/
static waitForElement(
selector: string,
timeout: number = 10000,
context: Document | Element = document
): Promise<Element | null> {
return new Promise((resolve) => {
const element = context.querySelector(selector);
if (element) {
resolve(element);
return;
}
const observer = new MutationObserver(() => {
const element = context.querySelector(selector);
if (element) {
observer.disconnect();
resolve(element);
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
setTimeout(() => {
observer.disconnect();
resolve(null);
}, timeout);
});
}
/**
* 解析相对 URL
*/
private static resolveUrl(url: string): string {
if (url.startsWith('http')) return url;
if (url.startsWith('//')) return `https:${url}`;
return new URL(url, window.location.href).href;
}
}
```
### 2.2 事件监听工具
```typescript
// src/content/utils/eventUtils.ts
export class EventUtils {
/**
* 监听页面变化
*/
static onPageChange(callback: (url: string) => void): void {
let currentUrl = window.location.href;
const observer = new MutationObserver(() => {
if (window.location.href !== currentUrl) {
currentUrl = window.location.href;
callback(currentUrl);
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
// 同时监听 popstate 事件
window.addEventListener('popstate', () => {
callback(window.location.href);
});
}
/**
* 监听元素出现
*/
static onElementAppear(
selector: string,
callback: (element: Element) => void
): void {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node instanceof Element) {
if (node.matches(selector)) {
callback(node);
}
node.querySelectorAll(selector).forEach(callback);
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
}
/**
* 模拟用户点击
*/
static simulateClick(element: Element): void {
const event = new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window,
});
element.dispatchEvent(event);
}
/**
* 模拟输入
*/
static simulateInput(element: HTMLInputElement, value: string): void {
element.value = value;
element.dispatchEvent(new Event('input', { bubbles: true }));
element.dispatchEvent(new Event('change', { bubbles: true }));
}
}
```
---
## 3. 平台特定交互
### 3.1 Amazon 交互
```typescript
// src/content/interactions/amazonInteraction.ts
export class AmazonInteraction {
/**
* 获取商品详情
*/
async getProductDetails(): Promise<ProductData> {
const title = DOMUtils.getText('#productTitle');
const priceText = DOMUtils.getText('.a-price-whole, .a-price .a-offscreen');
const price = this.parsePrice(priceText);
const images = DOMUtils.getImageUrls('#landingImage, #imgTagWrapperId img');
const description = DOMUtils.getText('#feature-bullets ul');
const asin = DOMUtils.getAttr('[data-asin]', 'data-asin');
return {
platform: 'AMAZON',
asin,
title,
price,
images,
description,
url: window.location.href,
};
}
/**
* 获取搜索列表
*/
async getSearchResults(): Promise<ProductData[]> {
const results: ProductData[] = [];
const items = document.querySelectorAll('[data-component-type="s-search-result"]');
items.forEach((item) => {
const title = DOMUtils.getText('h2 a span', item);
const priceText = DOMUtils.getText('.a-price-whole', item);
const price = this.parsePrice(priceText);
const asin = item.getAttribute('data-asin') || '';
results.push({
platform: 'AMAZON',
asin,
title,
price,
url: `https://www.amazon.com/dp/${asin}`,
});
});
return results;
}
/**
* 导航到刊登页面
*/
async navigateToListing(): Promise<void> {
window.location.href = 'https://sellercentral.amazon.com/inventory/add';
}
/**
* 填写刊登表单
*/
async fillListingForm(product: ProductData): Promise<void> {
// 等待页面加载
await DOMUtils.waitForElement('#title', 10000);
// 填写标题
const titleInput = document.querySelector('#title') as HTMLInputElement;
if (titleInput) {
EventUtils.simulateInput(titleInput, product.title);
}
// 填写价格
const priceInput = document.querySelector('#price') as HTMLInputElement;
if (priceInput) {
EventUtils.simulateInput(priceInput, product.price.toString());
}
// 填写描述
const descInput = document.querySelector('#description') as HTMLTextAreaElement;
if (descInput) {
EventUtils.simulateInput(descInput, product.description);
}
}
private parsePrice(priceText: string): number {
const match = priceText.replace(/[^\d.]/g, '').match(/(\d+\.?\d*)/);
return match ? parseFloat(match[1]) : 0;
}
}
```
### 3.2 eBay 交互
```typescript
// src/content/interactions/ebayInteraction.ts
export class EbayInteraction {
/**
* 获取商品详情
*/
async getProductDetails(): Promise<ProductData> {
const title = DOMUtils.getText('h1[data-testid="x-item-title-label"]');
const priceText = DOMUtils.getText('.notranslate.vi-price .notranslate');
const price = this.parsePrice(priceText);
const images = DOMUtils.getImageUrls('#icImg, .vi-image-gallery__image');
const description = DOMUtils.getText('#desc_wrapper');
const itemId = this.extractItemId();
return {
platform: 'EBAY',
itemId,
title,
price,
images,
description,
url: window.location.href,
};
}
private extractItemId(): string {
const match = window.location.pathname.match(/\/(\d+)$/);
return match ? match[1] : '';
}
private parsePrice(priceText: string): number {
const match = priceText.replace(/[^\d.]/g, '').match(/(\d+\.?\d*)/);
return match ? parseFloat(match[1]) : 0;
}
}
```
---
## 4. 异常处理
### 4.1 选择器失效处理
```typescript
export class SelectorFallback {
/**
* 尝试多个选择器
*/
static trySelectors(selectors: string[]): string {
for (const selector of selectors) {
const text = DOMUtils.getText(selector);
if (text) return text;
}
return '';
}
/**
* 使用 XPath 作为备选
*/
static queryXPath(xpath: string): string {
const result = document.evaluate(
xpath,
document,
null,
XPathResult.STRING_TYPE,
null
);
return result.stringValue || '';
}
}
```
### 4.2 页面变化检测
```typescript
export class PageChangeDetector {
private lastContent: string = '';
private checkInterval: number = 1000;
start(callback: () => void): void {
setInterval(() => {
const currentContent = document.body.innerHTML;
if (currentContent !== this.lastContent) {
this.lastContent = currentContent;
callback();
}
}, this.checkInterval);
}
}
```
---
## 5. 相关文档
- [Plugin Design](./Plugin_Design.md)
- [Automation Scripts](./Automation_Scripts.md)
---
*本文档基于实际平台页面结构,最后更新: 2026-03-18*

View File

@@ -0,0 +1,573 @@
# Plugin Design (Crawlful Hub)
> **定位**Crawlful Hub 浏览器插件架构设计文档 - 包含技术栈、目录结构、核心功能及开发规范。
> **更新日期**: 2026-03-18
> **最高优先级参考**: [Business_ClosedLoops.md](../00_Business/Business_ClosedLoops.md)
---
## 1. 技术栈 (Tech Stack)
| 层级 | 技术 | 版本 | 用途 |
|------|------|------|------|
| **Framework** | WebExtensions API | MV3 | 浏览器扩展标准 |
| **Language** | TypeScript | 5.x | 开发语言 |
| **Build Tool** | Vite | 5.x | 构建工具 |
| **Bundler** | Rollup | 4.x | 代码打包 |
| **UI** | React + Tailwind | 18.x + 3.x | 弹窗/选项页 UI |
| **Testing** | Vitest | 1.x | 单元测试 |
---
## 2. 目录结构 (Directory Structure)
```
extension/
├─ manifest.json # 扩展清单 (MV3)
├─ src/
│ │
│ ├─ background/ # Service Worker (后台脚本)
│ │ ├─ index.ts # 入口
│ │ ├─ handlers/
│ │ │ ├─ messageHandler.ts # 消息处理
│ │ │ ├─ alarmHandler.ts # 定时任务
│ │ │ └─ commandHandler.ts # 快捷键处理
│ │ └─ services/
│ │ ├─ crawlerService.ts # 网页采集服务无API平台
│ │ ├─ syncService.ts # 同步服务
│ │ └─ authService.ts # 认证服务
│ │
│ ├─ content/ # 内容脚本 (注入页面)
│ │ ├─ index.ts # 入口
│ │ ├─ crawlers/ # 采集器
│ │ │ ├─ amazonCrawler.ts
│ │ │ ├─ ebayCrawler.ts
│ │ │ ├─ shopifyCrawler.ts
│ │ │ └─ aliexpressCrawler.ts
│ │ ├─ automation/ # 自动化操作
│ │ │ ├─ listingAutomation.ts
│ │ │ ├─ orderAutomation.ts
│ │ │ └─ adAutomation.ts
│ │ └─ utils/
│ │ ├─ domUtils.ts
│ │ ├─ selectorUtils.ts
│ │ └─ eventUtils.ts
│ │
│ ├─ popup/ # 弹窗页面
│ │ ├─ index.tsx
│ │ ├─ components/
│ │ │ ├─ QuickActions.tsx
│ │ │ ├─ StatusPanel.tsx
│ │ │ └─ RecentTasks.tsx
│ │ └─ hooks/
│ │ └─ useBackground.ts
│ │
│ ├─ options/ # 选项页面
│ │ ├─ index.tsx
│ │ ├─ components/
│ │ │ ├─ GeneralSettings.tsx
│ │ │ ├─ PlatformSettings.tsx
│ │ │ ├─ AccountSettings.tsx
│ │ │ └─ AdvancedSettings.tsx
│ │ └─ stores/
│ │ └─ settingsStore.ts
│ │
│ ├─ shared/ # 共享资源
│ │ ├─ types/
│ │ │ ├─ messaging.ts # 消息类型定义
│ │ │ ├─ crawler.ts # 采集类型
│ │ │ └─ platform.ts # 平台类型
│ │ ├─ constants/
│ │ │ ├─ platforms.ts # 平台常量
│ │ │ └─ selectors.ts # 选择器常量
│ │ └─ utils/
│ │ ├─ logger.ts
│ │ ├─ storage.ts
│ │ └─ crypto.ts
│ │
│ └─ injected/ # 注入脚本 (隔离环境)
│ ├─ index.ts
│ └─ services/
│ └─ bridgeService.ts
├─ assets/ # 静态资源
│ ├─ icons/
│ │ ├─ icon-16.png
│ │ ├─ icon-32.png
│ │ ├─ icon-48.png
│ │ └─ icon-128.png
│ └─ styles/
│ └─ global.css
├─ _locales/ # 国际化
│ ├─ en/
│ │ └─ messages.json
│ └─ zh_CN/
│ └─ messages.json
└─ dist/ # 构建输出
```
---
## 3. 架构设计 (Architecture)
### 3.1 核心组件关系
```
┌─────────────────────────────────────────────────────────────┐
│ Browser Extension │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Popup │ │ Options │ │ Content │ │
│ │ (UI) │◄───►│ (Settings) │◄───►│ (Page) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └────────────────────┼────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Service Worker │ │
│ │ (Background) │ │
│ └────────┬─────────┘ │
│ │ │
│ ┌───────────────────┼───────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Crawler │ │ Sync │ │ Auth │ │
│ │ Engine │ │ Engine │ │ Engine │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
### 3.2 通信机制
#### 消息类型 (Messaging Types)
```typescript
// src/shared/types/messaging.ts
export enum MessageType {
// 采集相关
CRAWL_PRODUCT = 'CRAWL_PRODUCT',
CRAWL_ORDER = 'CRAWL_ORDER',
CRAWL_COMPLETE = 'CRAWL_COMPLETE',
// 自动化相关
AUTO_LISTING = 'AUTO_LISTING',
AUTO_ORDER = 'AUTO_ORDER',
AUTO_AD = 'AUTO_AD',
// 同步相关
SYNC_DATA = 'SYNC_DATA',
SYNC_STATUS = 'SYNC_STATUS',
// 认证相关
AUTH_LOGIN = 'AUTH_LOGIN',
AUTH_LOGOUT = 'AUTH_LOGOUT',
AUTH_REFRESH = 'AUTH_REFRESH',
// 设置相关
GET_SETTINGS = 'GET_SETTINGS',
SET_SETTINGS = 'SET_SETTINGS',
// 任务相关
TASK_STATUS = 'TASK_STATUS',
TASK_RESULT = 'TASK_RESULT',
}
export interface MessagePayload {
type: MessageType;
data?: any;
error?: string;
traceId?: string;
}
```
#### 通信流程
```
Content Script ──► Background Service Worker ──► Backend API
│ │
│◄─────────────────────┘
Popup/Options ◄─── Chrome Storage
```
---
## 4. 核心功能模块
### 4.1 数据采集模块 (Crawler)
**功能定位**
- 从各电商平台采集商品、订单数据
- **仅处理无API平台**TikTok Shop, Temu, 部分1688页面等
- **有API平台由后端处理**Amazon MWS, eBay API, Shopee Open API
- 支持沙箱模式(测试环境)
**采集策略矩阵**
| 平台 | 类型 | 采集方式 | 登录要求 | 反爬策略 |
|------|------|----------|----------|----------|
| Amazon | 有API | ❌ 后端处理 | OAuth | API限流 |
| eBay | 有API | ❌ 后端处理 | OAuth | API限流 |
| Shopee | 有API | ❌ 后端处理 | OAuth | API限流 |
| TikTok Shop | 无API | ✅ 插件采集 | 需登录 | 指纹隔离+代理IP |
| Temu | 无API | ✅ 插件采集 | 需登录 | 指纹隔离+代理IP |
| 1688(部分) | 无API | ✅ 插件采集 | 可选 | 频率控制 |
**⚠️ 重要约束**
- **后端严禁直接爬取**IP封禁风险、法律合规问题
- 所有网页级采集必须通过插件在用户浏览器端执行
- 插件必须实现店铺隔离一店一IP一指纹
**采集器实现**
```typescript
// src/content/crawlers/amazonCrawler.ts
export class AmazonCrawler {
private selectors = {
title: '#productTitle',
price: '.a-price-whole, .a-offscreen',
images: '#landingImage, .a-dynamic-image',
description: '#feature-bullets, #productDescription',
reviews: '#acrCustomerReviewText',
};
async crawlProduct(): Promise<ProductData> {
const title = this.extractText(this.selectors.title);
const price = this.extractPrice(this.selectors.price);
const images = this.extractImages(this.selectors.images);
const description = this.extractText(this.selectors.description);
return {
platform: 'AMAZON',
title,
price,
images,
description,
url: window.location.href,
crawledAt: new Date().toISOString(),
};
}
private extractText(selector: string): string {
const element = document.querySelector(selector);
return element?.textContent?.trim() || '';
}
private extractPrice(selector: string): number {
const element = document.querySelector(selector);
const text = element?.textContent?.replace(/[^\d.]/g, '') || '0';
return parseFloat(text);
}
private extractImages(selector: string): string[] {
const elements = document.querySelectorAll(selector);
return Array.from(elements)
.map(el => el.getAttribute('src') || el.getAttribute('data-src'))
.filter(Boolean) as string[];
}
}
```
**采集流程**
```
1. 用户点击采集按钮 / 定时任务触发
2. Content Script 注入采集器
3. 采集器解析 DOM 提取数据
4. 数据发送至 Background
5. Background 发送至后端 API
6. 返回采集结果
```
### 4.2 自动化操作模块 (Automation)
**功能定位**
- 自动刊登商品
- 自动处理订单
- 自动投放广告
**自动化实现**
```typescript
// src/content/automation/listingAutomation.ts
export class ListingAutomation {
async autoListing(productData: ProductData, platform: string): Promise<boolean> {
switch (platform) {
case 'AMAZON':
return this.listOnAmazon(productData);
case 'EBAY':
return this.listOnEbay(productData);
case 'SHOPIFY':
return this.listOnShopify(productData);
default:
throw new Error(`Unsupported platform: ${platform}`);
}
}
private async listOnAmazon(product: ProductData): Promise<boolean> {
// 1. 导航到刊登页面
await this.navigateTo('/inventory/add');
// 2. 填写商品信息
await this.fillInput('#title', product.title);
await this.fillInput('#price', product.price.toString());
await this.fillTextarea('#description', product.description);
// 3. 上传图片
for (const imageUrl of product.images) {
await this.uploadImage(imageUrl);
}
// 4. 提交刊登
await this.click('#submit-button');
// 5. 等待结果
return this.waitForSuccess();
}
private async navigateTo(path: string): Promise<void> {
window.location.href = `https://sellercentral.amazon.com${path}`;
await this.waitForElement('#title', 10000);
}
private async fillInput(selector: string, value: string): Promise<void> {
const input = document.querySelector(selector) as HTMLInputElement;
if (input) {
input.value = value;
input.dispatchEvent(new Event('input', { bubbles: true }));
}
}
private async waitForElement(selector: string, timeout: number): Promise<void> {
return new Promise((resolve, reject) => {
const startTime = Date.now();
const check = () => {
if (document.querySelector(selector)) {
resolve();
} else if (Date.now() - startTime > timeout) {
reject(new Error('Timeout waiting for element'));
} else {
setTimeout(check, 100);
}
};
check();
});
}
}
```
### 4.3 数据同步模块 (Sync)
**功能定位**
- 定时同步订单数据
- 同步库存状态
- 同步广告数据
**同步配置**
```typescript
// src/background/services/syncService.ts
export class SyncService {
private syncIntervals: Record<string, number> = {
orders: 5 * 60 * 1000, // 5分钟
inventory: 10 * 60 * 1000, // 10分钟
ads: 30 * 60 * 1000, // 30分钟
};
async startSync(): Promise<void> {
// 创建定时任务
chrome.alarms.create('syncOrders', { periodInMinutes: 5 });
chrome.alarms.create('syncInventory', { periodInMinutes: 10 });
chrome.alarms.create('syncAds', { periodInMinutes: 30 });
}
async handleAlarm(alarmName: string): Promise<void> {
switch (alarmName) {
case 'syncOrders':
await this.syncOrders();
break;
case 'syncInventory':
await this.syncInventory();
break;
case 'syncAds':
await this.syncAds();
break;
}
}
private async syncOrders(): Promise<void> {
const platforms = await this.getEnabledPlatforms();
for (const platform of platforms) {
try {
const orders = await this.crawlOrders(platform);
await this.sendToBackend('/api/v1/orders/sync', orders);
} catch (error) {
console.error(`Failed to sync orders for ${platform}:`, error);
}
}
}
}
```
---
## 5. 安全与隐私
### 5.1 数据存储
```typescript
// src/shared/utils/storage.ts
export class SecureStorage {
// 存储敏感数据(加密)
static async setSecure(key: string, value: string): Promise<void> {
const encrypted = await this.encrypt(value);
await chrome.storage.local.set({ [key]: encrypted });
}
// 读取敏感数据(解密)
static async getSecure(key: string): Promise<string | null> {
const result = await chrome.storage.local.get(key);
if (result[key]) {
return this.decrypt(result[key]);
}
return null;
}
// 存储普通数据
static async set(key: string, value: any): Promise<void> {
await chrome.storage.local.set({ [key]: value });
}
// 读取普通数据
static async get(key: string): Promise<any> {
const result = await chrome.storage.local.get(key);
return result[key];
}
private static async encrypt(text: string): Promise<string> {
// 使用 Chrome 的加密 API
// 实际实现需要更复杂的加密逻辑
return btoa(text);
}
private static async decrypt(encrypted: string): Promise<string> {
return atob(encrypted);
}
}
```
### 5.2 权限控制
```json
// manifest.json
{
"manifest_version": 3,
"name": "Crawlful Hub",
"version": "1.0.0",
"permissions": [
"storage",
"alarms",
"activeTab",
"scripting"
],
"host_permissions": [
"https://sellercentral.amazon.com/*",
"https://www.ebay.com/*",
"https://*.myshopify.com/*",
"https://*.tiktok.com/*"
],
"background": {
"service_worker": "src/background/index.ts"
},
"content_scripts": [
{
"matches": [
"https://sellercentral.amazon.com/*",
"https://www.ebay.com/*"
],
"js": ["src/content/index.ts"]
}
],
"action": {
"default_popup": "src/popup/index.html"
},
"options_page": "src/options/index.html"
}
```
---
## 6. 开发规范
### 6.1 代码规范
- 使用 TypeScript 严格模式
- 使用函数式组件 + Hooks
- 避免使用 `any` 类型
- 所有消息类型必须在 `messaging.ts` 中定义
### 6.2 测试规范
```typescript
// __tests__/crawler.test.ts
import { AmazonCrawler } from '../src/content/crawlers/amazonCrawler';
describe('AmazonCrawler', () => {
let crawler: AmazonCrawler;
beforeEach(() => {
crawler = new AmazonCrawler();
});
test('should extract product title', async () => {
// Mock DOM
document.body.innerHTML = `
<span id="productTitle">Test Product</span>
`;
const product = await crawler.crawlProduct();
expect(product.title).toBe('Test Product');
});
});
```
### 6.3 构建与发布
```bash
# 开发模式
npm run dev
# 生产构建
npm run build
# 打包扩展
npm run package
# 运行测试
npm run test
```
---
## 7. 相关文档
- [DOM Interaction](./DOM_Interaction.md)
- [Automation Scripts](./Automation_Scripts.md)
- [Backend Design](../02_Backend/Backend_Design.md)
- [Business ClosedLoops](../00_Business/Business_ClosedLoops.md)
---
*本文档基于业务闭环设计,最后更新: 2026-03-18*