Files
makemd/docs/01_Architecture/Mock_Architecture.md
wurenzhi 0dac26d781 feat: 添加MSW模拟服务和数据源集成
refactor: 重构页面组件移除冗余Layout组件

feat: 实现WebSocket和事件总线系统

feat: 添加队列和调度系统

docs: 更新架构文档和服务映射

style: 清理重复接口定义使用数据源

chore: 更新依赖项配置

feat: 添加运行时系统和领域引导

ci: 配置ESLint边界检查规则

build: 添加Redis和WebSocket依赖

test: 添加MSW浏览器环境入口

perf: 优化数据获取逻辑使用统一数据源

fix: 修复类型定义和状态管理问题
2026-03-19 01:39:34 +08:00

709 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 低侵入 Mock 架构设计
> 文档创建时间: 2026-03-19 (北京时间)
> 适用项目: Crawlful Hub
> 目标: 实现"不污染代码、AI上下文安全"的Mock方案
---
## 📋 目录
1. [核心原则](#核心原则)
2. [架构概览](#架构概览)
3. [前端Mock方案](#前端mock方案)
4. [后端/BFF Mock方案](#后端bff-mock方案)
5. [目录规范](#目录规范)
6. [AI协作规范](#ai协作规范)
7. [实施任务](#实施任务)
---
## 核心原则
### 1. Mock ≠ 写死数据
**错误做法**: 在组件中直接写死数据
```typescript
const data = [{ id: 1, name: 'Mock商品' }]
```
**正确做法**: 通过数据源抽象层获取
```typescript
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方案
### 方案对比
| 方案 | 侵入性 | 推荐度 | 适用场景 |
|------|--------|--------|----------|
| MSW (Mock Service Worker) | ⭐ 最低 | 🥇 最推荐 | 前端开发、UI测试 |
| DataSource抽象 | ⭐⭐ 低 | 🥈 推荐 | 需要自定义逻辑 |
| 本地Mock Server | ⭐⭐⭐ 中 | 🥉 可选 | 团队共享Mock |
| 硬编码 | ❌ 高 | 🚫 不推荐 | 绝对禁止 |
### 推荐方案: MSW + DataSource抽象
#### 1. 目录结构
```
/dashboard/src
/api ← 真实API封装
product.ts
user.ts
order.ts
certificate.ts
/mock ← 所有Mock数据和拦截器
/data ← Mock数据文件
product.mock.ts
user.mock.ts
certificate.mock.ts
msw.ts ← MSW初始化配置
browser.ts ← 浏览器环境启动
server.ts ← Node/测试环境启动
/services ← 数据源抽象层
productDataSource.ts
userDataSource.ts
certificateDataSource.ts
/types ← 类型定义
datasource.ts ← DataSource接口定义
```
#### 2. DataSource接口定义
```typescript
// /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抽象层实现
```typescript
// /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配置
```typescript
// /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 })
);
})
);
```
```typescript
// /dashboard/src/mock/browser.ts
import { worker } from './msw';
export { worker };
```
```typescript
// /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. 业务组件使用示例
```typescript
// /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服务
```
### 实现示例
```typescript
// /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;
```
```typescript
// /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文件必须包含以下标记:
```typescript
/**
* [MOCK-XXX] 功能描述
* AI注意: 这是Mock实现不是真实业务逻辑
* 仅在USE_MOCK=true时启用
*/
// 或者
// [MOCK] 此文件为Mock数据AI请勿当作真实业务逻辑
```
---
## AI协作规范
### 1. Mock文件识别规则
AI在分析代码时:
- ✅ 忽略 `/mock` 目录下所有文件
- ✅ 忽略包含 `[MOCK]` 标记的文件
- ✅ 忽略 `__MOCK__` 变量为真的代码路径
### 2. 业务代码规范
AI生成业务代码时必须:
- ✅ 调用 DataSource 抽象层
- ✅ 不直接调用 API 或 Mock
- ✅ 不硬编码任何数据
### 3. 文档声明
```markdown
## Mock使用规范
1. 所有Mock数据必须放 `/mock` 目录
2. DataSource层是唯一业务入口
3. `USE_MOCK=true` 时才启用Mock
4. AI不得将Mock数据当作真实业务逻辑
5. Mock不代表真实接口结构仅用于UI开发
```
---
## 实施任务
### 任务清单
| 任务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/index.tsx` | P0 | ⏳ 待实现 |
| MOCK-005 | BFF层Mock切换器 | `/server/src/bff/services/*.ts` | P1 | ⏳ 待实现 |
| MOCK-006 | BFF层Mock服务 | `/server/src/bff/mock/*.ts` | P1 | ⏳ 待实现 |
| 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-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 | ✅ 已完成 |
### 环境变量配置
```bash
# .env.development (开发环境)
REACT_APP_USE_MOCK=true # 前端启用Mock
USE_MOCK=true # 后端启用Mock
# .env.production (生产环境)
REACT_APP_USE_MOCK=false # 前端禁用Mock
USE_MOCK=false # 后端禁用Mock
```
### 快速启动命令
```bash
# 开发模式 (带Mock)
npm run dev:mock
# 开发模式 (真实API)
npm run dev
# 生产构建
npm run build
```
---
## 优势总结
| 特性 | 描述 |
|------|------|
| 🎯 零侵入 | 业务层完全不感知Mock |
| 🔄 可切换 | 环境变量一键切换Mock/真实API |
| 🤖 AI安全 | Mock独立文件 + 文档规范 |
| 📦 可扩展 | 支持MSW、BFF Mock、本地Mock Server |
| 🧪 可测试 | 统一接口便于单元测试 |
| 📝 可维护 | 目录清晰,职责分离 |
---
## 附录
### 相关文档
- [项目特定规则](../.trae/rules/project-specific-rules.md)
- [AI协作协议](../.trae/rules/project-specific-rules.md#7-ai-协作协议)
- [Task_Overview](../00_Business/Task_Overview.md)
### 更新记录
| 时间 | 版本 | 更新内容 |
|------|------|----------|
| 2026-03-19 | v1.0 | 初始版本创建完整Mock架构设计 |
---
*本文档遵循 Crawlful Hub 项目规范所有Mock实现必须遵守"低侵入、AI安全"原则。*