feat: 添加汇率服务和缓存服务,优化数据源和日志服务
refactor: 重构数据源工厂和类型定义,提升代码可维护性 fix: 修复类型转换和状态机文档中的错误 docs: 更新服务架构文档,添加新的服务闭环流程 test: 添加汇率服务单元测试 chore: 清理无用代码和注释,优化代码结构
This commit is contained in:
238
dashboard/src/services/dataSourceFactory.ts
Normal file
238
dashboard/src/services/dataSourceFactory.ts
Normal file
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* 统一 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';
|
||||
Reference in New Issue
Block a user