Files
makemd/docs/ARCHIVE/01_Architecture/11_Mock_Architecture.md
wurenzhi 2b86715c09 refactor: 优化代码结构并修复类型问题
- 移除未使用的TabPane组件
- 修复类型定义和导入方式
- 优化mock数据源的环境变量判断逻辑
- 更新文档结构并归档旧文件
- 添加新的UI组件和Memo组件
- 调整API路径和响应处理
2026-03-23 12:41:35 +08:00

26 KiB
Raw Permalink Blame History

低侵入 Mock 架构设计

文档创建时间: 2026-03-19 (北京时间) 适用项目: Crawlful Hub 目标: 实现"不污染代码、AI上下文安全"的Mock方案


📋 目录

  1. 核心原则
  2. 架构概览
  3. 前端Mock方案
  4. 后端/BFF Mock方案
  5. 目录规范
  6. AI协作规范
  7. 实施任务

核心原则

1. Mock ≠ 写死数据

错误做法: 在组件中直接写死数据

const data = [{ id: 1, name: 'Mock商品' }]

正确做法: 通过数据源抽象层获取

const data = await productDataSource.list()

2. Mock 必须"可插拔"

Mock 应该是:

  • 可开启 / 可关闭
  • 可替换 / 可隔离
  • 环境变量控制

3. Mock 属于"数据源层",不是"业务层"

业务代码 → DataSource抽象层 → (Mock/真实API)

架构概览

三层数据架构

┌─────────────────────────────────────────────────────────────┐
│                    前端 (React + Umi)                        │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐     │
│  │   业务组件   │ → │ DataSource  │ → │  API/Mock   │     │
│  │             │    │   抽象层    │    │             │     │
│  └─────────────┘    └─────────────┘    └─────────────┘     │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│              BFF 层 (服务编排层)                              │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐     │
│  │   Service   │ → │  Mock切换   │ → │ 真实/Mock   │     │
│  │             │    │             │    │   服务      │     │
│  └─────────────┘    └─────────────┘    └─────────────┘     │
└─────────────────────────────────────────────────────────────┘
                            ↓
┌─────────────────────────────────────────────────────────────┐
│                    后端微服务层                               │
└─────────────────────────────────────────────────────────────┘

Mock 分布策略

层级 是否Mock 说明
前端 ⚠️ 可选 推荐用MSW保持业务代码纯净
DataSource 可切换 控制入口,环境变量驱动
BFF 主力Mock 最真实,前端无感知
后端 不建议 除非联调测试

前端Mock方案

项目实际采用的两种Mock方式

本项目采用两种Mock方式并存的策略,根据不同模块的特点选择合适的方案:

方式一DataSource内联Mock推荐用于简单模块

特点Mock数据直接写在DataSource文件中通过环境变量切换

适用场景

  • 数据结构简单,不需要复杂的网络拦截
  • 快速原型开发无需配置MSW
  • Mock逻辑简单不需要模拟网络延迟和错误

实现示例

// /dashboard/src/services/productDataSource.ts

class MockProductDataSource implements IProductDataSource {
  async fetchProducts(): Promise<Product[]> {
    // Mock数据直接内联
    return [
      { id: '1', name: '商品A', price: 99.99 },
      { id: '2', name: '商品B', price: 199.99 },
    ];
  }
}

class ApiProductDataSource implements IProductDataSource {
  async fetchProducts(): Promise<Product[]> {
    const res = await fetch('/api/products');
    return res.json();
  }
}

const useMock = process.env.REACT_APP_USE_MOCK === 'true';
export const productDataSource = useMock ? new MockProductDataSource() : new ApiProductDataSource();

已实现的模块

  • 产品模块 (productDataSource.ts)
  • 订单模块 (orderDataSource.ts)
  • 库存模块 (inventoryDataSource.ts)
  • 商户模块 (merchantDataSource.ts)
  • 供应商模块 (suppliersDataSource.ts)
  • 物流模块 (logisticsDataSource.ts)
  • 营销模块 (marketingDataSource.ts)
  • 财务模块 (financeDataSource.ts)
  • 报表模块 (reportsDataSource.ts)
  • 设置模块 (settingsDataSource.ts)
  • 售后模块 (afterSalesDataSource.ts)
  • 返回模块 (returnDataSource.ts)
  • 用户资产模块 (userAssetDataSource.ts)
  • 任务中心模块 (taskCenterDataSource.ts)
  • 分析模块 (analyticsDataSource.ts)
  • B2B贸易模块 (b2bTradeDataSource.ts)
  • 排行榜模块 (leaderboardDataSource.ts)
  • 动态定价模块 (dynamicPricingDataSource.ts)
  • 套利模块 (arbitrageDataSource.ts)
  • 店铺报表模块 (shopReportDataSource.ts)
  • 自动执行模块 (autoExecutionDataSource.ts)
  • 广告优化模块 (adOptimizationDataSource.ts)
  • AB测试模块 (abTestDataSource.ts)
  • 黑名单模块 (blacklistDataSource.ts)
  • 独立站点模块 (independentSiteDataSource.ts)

方式二MSW网络层Mock推荐用于复杂模块

特点使用MSW拦截HTTP请求Mock数据独立在 /mock/data/ 目录

适用场景

  • 需要模拟真实的网络请求和响应
  • 需要模拟网络延迟、错误状态
  • 需要支持复杂的查询参数和分页
  • 需要多个组件共享同一Mock数据

实现示例

// /dashboard/src/mock/data/certificate.mock.ts
export const mockCertificates = [...];

// /dashboard/src/mock/msw.ts
export const handlers = [
  rest.get('/api/v1/certificate/certificates', (req, res, ctx) => {
    return res(
      ctx.delay(300), // 模拟网络延迟
      ctx.json({ success: true, data: mockCertificates })
    );
  }),
];

// /dashboard/src/app.ts
if (process.env.REACT_APP_USE_MOCK === 'true') {
  const { worker } = await import('./mock/browser');
  await worker.start();
}

已实现的模块

  • 证书模块 (certificateDataSource.ts + msw.ts)
  • 自动选品模块 (productSelectionDataSource.ts + msw.ts)

方案对比

方案 侵入性 推荐度 适用场景
DataSource内联Mock 🥈 推荐 简单模块、快速原型
MSW网络层Mock 最低 🥇 最推荐 复杂模块、UI测试
本地Mock Server 🥉 可选 团队共享Mock
硬编码 🚫 不推荐 绝对禁止

推荐方案:根据模块复杂度选择

1. 目录结构

/dashboard/src
  /api                    ← 真实API封装可选
    *.ts
  /mock                   ← MSW Mock数据和拦截器
    /data                 ← Mock数据文件仅MSW方式使用
      certificate.mock.ts
      productSelection.mock.ts
    msw.ts                ← MSW初始化配置
    browser.ts            ← 浏览器环境启动
    README.md             ← Mock使用说明
  /services               ← 数据源抽象层(所有模块)
    *DataSource.ts        ← 包含Mock和API两种实现
  /types                  ← 类型定义
    datasource.ts         ← DataSource接口定义

2. DataSource接口定义

// /dashboard/src/types/datasource.ts

/**
 * [MOCK-001] DataSource抽象接口
 * 所有数据源必须实现此接口
 * AI注意: 这是抽象层,不涉及具体实现
 */
export interface IDataSource<T, QueryParams = any> {
  list(params?: QueryParams): Promise<T[]>;
  detail(id: string): Promise<T | null>;
  create(data: Partial<T>): Promise<T>;
  update(id: string, data: Partial<T>): Promise<T>;
  delete(id: string): Promise<void>;
}

export interface ProductQueryParams {
  keyword?: string;
  category?: string;
  status?: string;
  page?: number;
  pageSize?: number;
}

export interface CertificateQueryParams {
  status?: string;
  type?: string;
  page?: number;
  pageSize?: number;
}

3. DataSource抽象层实现

// /dashboard/src/services/certificateDataSource.ts

import { Certificate } from '@/types/certificate';
import { IDataSource, CertificateQueryParams } from '@/types/datasource';

/**
 * [MOCK-002] 证书数据源抽象层
 * 通过环境变量自动切换Mock/真实API
 * AI注意: 这是唯一入口,业务代码必须调用此层
 */

// 真实API实现
class ApiCertificateDataSource implements IDataSource<Certificate, CertificateQueryParams> {
  async list(params?: CertificateQueryParams): Promise<Certificate[]> {
    const query = new URLSearchParams(params as any).toString();
    const response = await fetch(`/api/v1/certificate/certificates?${query}`);
    const result = await response.json();
    return result.data || [];
  }

  async detail(id: string): Promise<Certificate | null> {
    const response = await fetch(`/api/v1/certificate/certificates/${id}`);
    const result = await response.json();
    return result.data || null;
  }

  async create(data: Partial<Certificate>): Promise<Certificate> {
    const response = await fetch('/api/v1/certificate/certificates', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    });
    const result = await response.json();
    return result.data;
  }

  async update(id: string, data: Partial<Certificate>): Promise<Certificate> {
    const response = await fetch(`/api/v1/certificate/certificates/${id}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    });
    const result = await response.json();
    return result.data;
  }

  async delete(id: string): Promise<void> {
    await fetch(`/api/v1/certificate/certificates/${id}`, {
      method: 'DELETE',
    });
  }
}

// Mock实现 (完全独立文件)
class MockCertificateDataSource implements IDataSource<Certificate, CertificateQueryParams> {
  private mockData: Certificate[] = [
    {
      id: '1',
      name: 'CE认证证书',
      type: 'PRODUCT_CERT',
      status: 'APPROVED',
      fileUrl: '/files/ce-cert.pdf',
      fileName: 'CE-Certificate-2026.pdf',
      uploadDate: '2026-03-15',
      expiryDate: '2027-03-15',
      approvedBy: 'admin',
      approvedDate: '2026-03-16',
      productId: 'P001',
      productName: '工业温度传感器',
      notes: '欧盟市场准入认证',
    },
    // ... 更多Mock数据
  ];

  async list(params?: CertificateQueryParams): Promise<Certificate[]> {
    // 模拟网络延迟
    await new Promise(resolve => setTimeout(resolve, 300));
    
    let result = [...this.mockData];
    
    if (params?.status) {
      result = result.filter(item => item.status === params.status);
    }
    if (params?.type) {
      result = result.filter(item => item.type === params.type);
    }
    
    return result;
  }

  async detail(id: string): Promise<Certificate | null> {
    await new Promise(resolve => setTimeout(resolve, 200));
    return this.mockData.find(item => item.id === id) || null;
  }

  async create(data: Partial<Certificate>): Promise<Certificate> {
    await new Promise(resolve => setTimeout(resolve, 500));
    const newCert: Certificate = {
      id: `${Date.now()}`,
      ...data as Certificate,
      status: 'PENDING',
      uploadDate: new Date().toISOString().split('T')[0],
    };
    this.mockData.unshift(newCert);
    return newCert;
  }

  async update(id: string, data: Partial<Certificate>): Promise<Certificate> {
    await new Promise(resolve => setTimeout(resolve, 300));
    const index = this.mockData.findIndex(item => item.id === id);
    if (index === -1) throw new Error('Certificate not found');
    this.mockData[index] = { ...this.mockData[index], ...data };
    return this.mockData[index];
  }

  async delete(id: string): Promise<void> {
    await new Promise(resolve => setTimeout(resolve, 200));
    this.mockData = this.mockData.filter(item => item.id !== id);
  }
}

// 导出数据源实例 (环境变量控制)
const useMock = process.env.REACT_APP_USE_MOCK === 'true';

export const certificateDataSource: IDataSource<Certificate, CertificateQueryParams> = useMock
  ? new MockCertificateDataSource()
  : new ApiCertificateDataSource();

// 标记Mock状态 (用于调试)
export const __MOCK__ = useMock;

4. MSW配置

// /dashboard/src/mock/msw.ts

import { setupWorker, rest } from 'msw';
import { mockCertificates } from './data/certificate.mock';

/**
 * [MOCK-003] MSW Mock配置
 * 拦截HTTP请求返回Mock数据
 * AI注意: 这是网络层拦截,业务代码完全无感知
 */

export const worker = setupWorker(
  // 证书列表
  rest.get('/api/v1/certificate/certificates', (req, res, ctx) => {
    const status = req.url.searchParams.get('status');
    const type = req.url.searchParams.get('type');
    
    let data = [...mockCertificates];
    if (status) data = data.filter(item => item.status === status);
    if (type) data = data.filter(item => item.type === type);
    
    return res(
      ctx.delay(300),
      ctx.json({
        success: true,
        data,
        total: data.length,
      })
    );
  }),

  // 证书详情
  rest.get('/api/v1/certificate/certificates/:id', (req, res, ctx) => {
    const { id } = req.params;
    const cert = mockCertificates.find(item => item.id === id);
    
    if (!cert) {
      return res(
        ctx.status(404),
        ctx.json({ success: false, error: '证书不存在' })
      );
    }
    
    return res(
      ctx.delay(200),
      ctx.json({ success: true, data: cert })
    );
  }),

  // 创建证书
  rest.post('/api/v1/certificate/certificates', async (req, res, ctx) => {
    const body = await req.json();
    const newCert = {
      id: `${Date.now()}`,
      ...body,
      status: 'PENDING',
      uploadDate: new Date().toISOString().split('T')[0],
    };
    
    return res(
      ctx.delay(500),
      ctx.json({ success: true, data: { id: newCert.id } })
    );
  }),

  // 更新证书状态
  rest.put('/api/v1/certificate/certificates/:id/status', async (req, res, ctx) => {
    const { id } = req.params;
    const { status, approvedBy } = await req.json();
    
    return res(
      ctx.delay(300),
      ctx.json({ success: true })
    );
  }),

  // 删除证书
  rest.delete('/api/v1/certificate/certificates/:id', (req, res, ctx) => {
    return res(
      ctx.delay(200),
      ctx.json({ success: true })
    );
  })
);
// /dashboard/src/mock/browser.ts
import { worker } from './msw';
export { worker };
// /dashboard/src/index.tsx (入口文件)

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

/**
 * [MOCK-004] MSW启动配置
 * 仅在REACT_APP_USE_MOCK=true时启用
 */
async function enableMocking() {
  if (process.env.REACT_APP_USE_MOCK !== 'true') {
    return;
  }
  
  const { worker } = await import('./mock/browser');
  return worker.start({
    onUnhandledRequest: 'bypass', // 未拦截的请求直接透传
  });
}

enableMocking().then(() => {
  const root = ReactDOM.createRoot(document.getElementById('root')!);
  root.render(
    <React.StrictMode>
      <App />
    </React.StrictMode>
  );
});

5. 业务组件使用示例

// /dashboard/src/pages/Compliance/index.tsx

import { certificateDataSource } from '@/services/certificateDataSource';

const ComplianceManagement: React.FC = () => {
  const fetchData = useCallback(async () => {
    setLoading(true);
    try {
      // 调用DataSource层完全无感知是Mock还是真实API
      const data = await certificateDataSource.list({
        status: activeStatus,
        type: activeType,
      });
      setCertificates(data);
    } catch (error) {
      message.error('获取证书列表失败');
    } finally {
      setLoading(false);
    }
  }, [activeStatus, activeType]);

  const handleUploadSubmit = async () => {
    try {
      const values = await uploadForm.validateFields();
      await certificateDataSource.create({
        name: values.name,
        type: values.type,
        expiryDate: values.expiryDate.format('YYYY-MM-DD'),
        productId: values.productId,
        productName: values.productName,
        notes: values.notes,
      });
      message.success('证书上传成功');
      fetchData();
    } catch (error) {
      message.error('上传失败');
    }
  };

  // ... 其他逻辑
};

后端/BFF Mock方案

架构设计

BFF层
  ├─ Service (业务逻辑)
  ├─ Mock切换器 (环境变量控制)
  ├─ 真实API客户端
  └─ Mock服务

实现示例

// /server/src/bff/services/certificateService.ts

import { CertificateService as RealCertificateService } from '../../services/CertificateService';
import { MockCertificateService } from '../mock/certificateMock';

/**
 * [MOCK-005] BFF层证书服务
 * 通过环境变量自动切换Mock/真实服务
 */

const useMock = process.env.USE_MOCK === 'true';

export const certificateService = useMock
  ? new MockCertificateService()
  : new RealCertificateService();

// 类型导出,确保调用方类型安全
export type ICertificateService = typeof certificateService;
// /server/src/bff/mock/certificateMock.ts

import { ICertificateService } from '../services/certificateService';

/**
 * [MOCK-006] BFF层Mock服务
 * 完全独立的Mock实现
 * AI注意: 这是Mock实现不是真实业务逻辑
 */

export class MockCertificateService implements ICertificateService {
  private mockData = [
    // ... Mock数据
  ];

  async query(options: any) {
    await this.delay(300);
    return {
      items: this.mockData,
      total: this.mockData.length,
    };
  }

  async getById(id: string, tenantId: string, shopId: string) {
    await this.delay(200);
    return this.mockData.find(item => item.id === id) || null;
  }

  async create(input: any) {
    await this.delay(500);
    const id = `mock-${Date.now()}`;
    // ... 创建逻辑
    return id;
  }

  private delay(ms: number) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

目录规范

前端目录规范

/dashboard/src
  /mock                   ← 所有Mock相关
    /data                 ← Mock数据定义
      *.mock.ts
    msw.ts                ← MSW配置
    browser.ts            ← 浏览器入口
    server.ts             ← Node/测试入口
    README.md             ← Mock使用说明
  /services               ← DataSource抽象层
    *DataSource.ts
  /api                    ← 真实API封装
    *.ts

后端目录规范

/server/src
  /bff                    ← BFF层
    /mock                 ← Mock服务
      *.ts
    /services             ← 服务层
      *.ts
    /api                  ← API客户端
      *.ts
  /services               ← 真实服务
    *.ts

标记规范

所有Mock文件必须包含以下标记:

/**
 * [MOCK-XXX] 功能描述
 * AI注意: 这是Mock实现不是真实业务逻辑
 * 仅在USE_MOCK=true时启用
 */

// 或者

// [MOCK] 此文件为Mock数据AI请勿当作真实业务逻辑

AI协作规范

1. Mock文件识别规则

AI在分析代码时:

  • 忽略 /mock 目录下所有文件
  • 忽略包含 [MOCK] 标记的文件
  • 忽略 __MOCK__ 变量为真的代码路径

2. 业务代码规范

AI生成业务代码时必须:

  • 调用 DataSource 抽象层
  • 不直接调用 API 或 Mock
  • 不硬编码任何数据

3. 文档声明

## Mock使用规范

1. DataSource层是唯一业务入口所有业务代码必须通过DataSource获取数据
2. Mock数据有两种存放方式
   - 简单模块直接写在DataSource文件中的Mock类
   - 复杂模块:独立在 `/mock/data/` 目录通过MSW拦截
3. `REACT_APP_USE_MOCK=true` 时才启用Mock
4. AI不得将Mock数据当作真实业务逻辑
5. Mock不代表真实接口结构仅用于UI开发
6. 新增模块时根据复杂度选择合适的Mock方式

实施任务

任务清单

任务ID 任务描述 涉及文件 优先级 状态
MOCK-001 创建DataSource接口定义 /dashboard/src/types/datasource.ts P0 已完成
MOCK-002 实现证书DataSource /dashboard/src/services/certificateDataSource.ts P0 已完成
MOCK-003 配置MSW拦截器 /dashboard/src/mock/msw.ts P0 已完成
MOCK-004 入口文件MSW启动 /dashboard/src/app.ts P0 已完成
MOCK-005 BFF层Mock切换器 /server/src/bff/services/certificateService.ts P0 已完成
MOCK-006 BFF层Mock服务 /server/src/bff/mock/certificateMock.ts P0 已完成
MOCK-007 产品模块DataSource /dashboard/src/services/productDataSource.ts P1 已完成
MOCK-008 订单模块DataSource /dashboard/src/services/orderDataSource.ts P1 已完成
MOCK-009 用户模块DataSource /dashboard/src/services/userDataSource.ts P1 待实现
MOCK-010 库存模块DataSource /dashboard/src/services/inventoryDataSource.ts P1 已完成
MOCK-016 商户模块DataSource /dashboard/src/services/merchantDataSource.ts P1 已完成
MOCK-017 供应商模块DataSource /dashboard/src/services/suppliersDataSource.ts P1 已完成
MOCK-018 物流模块DataSource /dashboard/src/services/logisticsDataSource.ts P1 已完成
MOCK-019 营销模块DataSource /dashboard/src/services/marketingDataSource.ts P1 已完成
MOCK-020 财务模块DataSource /dashboard/src/services/financeDataSource.ts P1 已完成
MOCK-021 报表模块DataSource /dashboard/src/services/reportsDataSource.ts P1 已完成
MOCK-022 设置模块DataSource /dashboard/src/services/settingsDataSource.ts P1 已完成
MOCK-023 售后模块DataSource /dashboard/src/services/afterSalesDataSource.ts P1 已完成
MOCK-024 返回模块DataSource /dashboard/src/services/returnDataSource.ts P1 已完成
MOCK-025 用户资产模块DataSource /dashboard/src/services/userAssetDataSource.ts P1 已完成
MOCK-026 任务中心模块DataSource /dashboard/src/services/taskCenterDataSource.ts P1 已完成
MOCK-027 分析模块DataSource /dashboard/src/services/analyticsDataSource.ts P1 已完成
MOCK-028 B2B贸易模块DataSource /dashboard/src/services/b2bTradeDataSource.ts P1 已完成
MOCK-029 排行榜模块DataSource /dashboard/src/services/leaderboardDataSource.ts P1 已完成
MOCK-030 动态定价模块DataSource /dashboard/src/services/dynamicPricingDataSource.ts P1 已完成
MOCK-031 套利模块DataSource /dashboard/src/services/arbitrageDataSource.ts P1 已完成
MOCK-032 店铺报表模块DataSource /dashboard/src/services/shopReportDataSource.ts P1 已完成
MOCK-033 自动执行模块DataSource /dashboard/src/services/autoExecutionDataSource.ts P1 已完成
MOCK-034 广告优化模块DataSource /dashboard/src/services/adOptimizationDataSource.ts P1 已完成
MOCK-035 AB测试模块DataSource /dashboard/src/services/abTestDataSource.ts P1 已完成
MOCK-036 黑名单模块DataSource /dashboard/src/services/blacklistDataSource.ts P1 已完成
MOCK-037 独立站点模块DataSource /dashboard/src/services/independentSiteDataSource.ts P1 已完成
MOCK-011 环境变量配置 .env.development, .env.production P0 已完成
MOCK-012 文档完善 docs/01_Architecture/Mock_Architecture.md P0 已完成
MOCK-013 自动选品模块Mock数据 /dashboard/src/mock/data/productSelection.mock.ts P0 已完成
MOCK-014 自动选品DataSource /dashboard/src/services/productSelectionDataSource.ts P0 已完成
MOCK-015 自动选品页面Mock集成 /dashboard/src/pages/AutoProductSelection/index.tsx P0 已完成

环境变量配置

# .env.development (开发环境)
REACT_APP_USE_MOCK=true          # 前端启用Mock
USE_MOCK=true                    # 后端启用Mock

# .env.production (生产环境)
REACT_APP_USE_MOCK=false         # 前端禁用Mock
USE_MOCK=false                   # 后端禁用Mock

快速启动命令

# 开发模式 (带Mock)
npm run dev:mock

# 开发模式 (真实API)
npm run dev

# 生产构建
npm run build

优势总结

特性 描述
🎯 零侵入 业务层完全不感知Mock
🔄 可切换 环境变量一键切换Mock/真实API
🤖 AI安全 Mock独立文件 + 文档规范
📦 可扩展 支持MSW、BFF Mock、本地Mock Server
🧪 可测试 统一接口便于单元测试
📝 可维护 目录清晰,职责分离

附录

相关文档

更新记录

时间 版本 更新内容
2026-03-19 v1.0 初始版本创建完整Mock架构设计
2026-03-19 v1.1 更新为两种Mock方式并存策略完善任务状态添加27个模块Mock实现

本文档遵循 Crawlful Hub 项目规范所有Mock实现必须遵守"低侵入、AI安全"原则。