239 lines
5.7 KiB
TypeScript
239 lines
5.7 KiB
TypeScript
|
|
/**
|
|||
|
|
* 统一 DataSource 工厂模式
|
|||
|
|
* 消除前端数据源重复代码,提供统一的创建和管理机制
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
import { IDataSource } from '@/types/datasource';
|
|||
|
|
|
|||
|
|
// 环境变量判断
|
|||
|
|
const useMock = process.env.REACT_APP_USE_MOCK === 'true';
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 基础 DataSource 抽象类
|
|||
|
|
* 提供通用的 CRUD 方法实现
|
|||
|
|
*/
|
|||
|
|
export abstract class BaseDataSource<T, P = any> implements IDataSource<T, P> {
|
|||
|
|
protected baseUrl: string;
|
|||
|
|
|
|||
|
|
constructor(baseUrl: string) {
|
|||
|
|
this.baseUrl = baseUrl;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async list(params?: P): Promise<T[]> {
|
|||
|
|
const query = this.buildQueryParams(params);
|
|||
|
|
const response = await fetch(`${this.baseUrl}?${query}`, {
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error(`API Error: ${response.status}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const result = await response.json();
|
|||
|
|
return result.data || [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async detail(id: string): Promise<T | null> {
|
|||
|
|
const response = await fetch(`${this.baseUrl}/${id}`, {
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!response.ok) {
|
|||
|
|
if (response.status === 404) return null;
|
|||
|
|
throw new Error(`API Error: ${response.status}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const result = await response.json();
|
|||
|
|
return result.data || null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async create(data: Partial<T>): Promise<T> {
|
|||
|
|
const response = await fetch(this.baseUrl, {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify(data),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error(`API Error: ${response.status}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const result = await response.json();
|
|||
|
|
|
|||
|
|
// 创建成功后获取完整数据
|
|||
|
|
if (result.data?.id) {
|
|||
|
|
const created = await this.detail(result.data.id);
|
|||
|
|
if (created) return created;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result.data;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async update(id: string, data: Partial<T>): Promise<T> {
|
|||
|
|
const response = await fetch(`${this.baseUrl}/${id}`, {
|
|||
|
|
method: 'PUT',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
body: JSON.stringify(data),
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error(`API Error: ${response.status}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const result = await response.json();
|
|||
|
|
|
|||
|
|
// 更新成功后获取完整数据
|
|||
|
|
const updated = await this.detail(id);
|
|||
|
|
if (updated) return updated;
|
|||
|
|
|
|||
|
|
return result.data;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async delete(id: string): Promise<void> {
|
|||
|
|
const response = await fetch(`${this.baseUrl}/${id}`, {
|
|||
|
|
method: 'DELETE',
|
|||
|
|
headers: {
|
|||
|
|
'Content-Type': 'application/json',
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (!response.ok) {
|
|||
|
|
throw new Error(`API Error: ${response.status}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 构建查询参数
|
|||
|
|
*/
|
|||
|
|
protected buildQueryParams(params?: P): string {
|
|||
|
|
const query = new URLSearchParams();
|
|||
|
|
if (params) {
|
|||
|
|
Object.entries(params).forEach(([key, value]) => {
|
|||
|
|
if (value !== undefined && value !== null) {
|
|||
|
|
query.append(key, String(value));
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
return query.toString();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 模拟延迟(用于 Mock 实现)
|
|||
|
|
*/
|
|||
|
|
protected delay(ms: number): Promise<void> {
|
|||
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 基础 Mock DataSource 类
|
|||
|
|
*/
|
|||
|
|
export abstract class BaseMockDataSource<T, P = any> implements IDataSource<T, P> {
|
|||
|
|
/** Mock标记 */
|
|||
|
|
readonly __MOCK__ = true as const;
|
|||
|
|
/** Mock数据源名称 */
|
|||
|
|
abstract readonly __MOCK_NAME__: string;
|
|||
|
|
/** Mock数据 */
|
|||
|
|
protected abstract mockData: T[];
|
|||
|
|
|
|||
|
|
async list(params?: P): Promise<T[]> {
|
|||
|
|
await this.delay(200);
|
|||
|
|
return this.mockData;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async detail(id: string): Promise<T | null> {
|
|||
|
|
await this.delay(100);
|
|||
|
|
return this.mockData.find(item => (item as any).id === id) || null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async create(data: Partial<T>): Promise<T> {
|
|||
|
|
await this.delay(300);
|
|||
|
|
|
|||
|
|
const newItem = {
|
|||
|
|
id: `${Date.now()}`,
|
|||
|
|
...data,
|
|||
|
|
} as T;
|
|||
|
|
|
|||
|
|
this.mockData.unshift(newItem);
|
|||
|
|
return newItem;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async update(id: string, data: Partial<T>): Promise<T> {
|
|||
|
|
await this.delay(300);
|
|||
|
|
|
|||
|
|
const index = this.mockData.findIndex(item => (item as any).id === id);
|
|||
|
|
if (index === -1) {
|
|||
|
|
throw new Error('Item not found');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
this.mockData[index] = { ...this.mockData[index], ...data };
|
|||
|
|
return this.mockData[index];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async delete(id: string): Promise<void> {
|
|||
|
|
await this.delay(200);
|
|||
|
|
this.mockData = this.mockData.filter(item => (item as any).id !== id);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 模拟延迟
|
|||
|
|
*/
|
|||
|
|
protected delay(ms: number): Promise<void> {
|
|||
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* DataSource 工厂类
|
|||
|
|
* 负责根据环境变量创建相应的 DataSource 实例
|
|||
|
|
*/
|
|||
|
|
export class DataSourceFactory {
|
|||
|
|
/**
|
|||
|
|
* 创建 DataSource 实例
|
|||
|
|
* @param apiDataSource API实现类
|
|||
|
|
* @param mockDataSource Mock实现类
|
|||
|
|
* @returns DataSource 实例
|
|||
|
|
*/
|
|||
|
|
static create<T, P = any>(
|
|||
|
|
apiDataSource: new () => IDataSource<T, P>,
|
|||
|
|
mockDataSource: new () => IDataSource<T, P>
|
|||
|
|
): IDataSource<T, P> {
|
|||
|
|
return useMock ? new mockDataSource() : new apiDataSource();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 创建带有自定义方法的 DataSource 实例
|
|||
|
|
* @param apiDataSource API实现类
|
|||
|
|
* @param mockDataSource Mock实现类
|
|||
|
|
* @returns DataSource 实例
|
|||
|
|
*/
|
|||
|
|
static createWithMethods<T, P = any, M extends Record<string, any>>({
|
|||
|
|
apiDataSource,
|
|||
|
|
mockDataSource,
|
|||
|
|
}: {
|
|||
|
|
apiDataSource: new () => IDataSource<T, P> & M;
|
|||
|
|
mockDataSource: new () => IDataSource<T, P> & M;
|
|||
|
|
}): IDataSource<T, P> & M {
|
|||
|
|
return useMock ? new mockDataSource() : new apiDataSource();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* Mock状态标记
|
|||
|
|
* 用于调试和开发环境识别
|
|||
|
|
*/
|
|||
|
|
export const __MOCK__ = useMock;
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 当前数据源类型
|
|||
|
|
*/
|
|||
|
|
export const __DATA_SOURCE_TYPE__ = useMock ? 'mock' : 'api';
|