Files
makemd/dashboard/src/services/dataSourceFactory.ts

239 lines
5.7 KiB
TypeScript
Raw Normal View History

/**
* 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';