feat: 添加汇率服务和缓存服务,优化数据源和日志服务

refactor: 重构数据源工厂和类型定义,提升代码可维护性

fix: 修复类型转换和状态机文档中的错误

docs: 更新服务架构文档,添加新的服务闭环流程

test: 添加汇率服务单元测试

chore: 清理无用代码和注释,优化代码结构
This commit is contained in:
2026-03-19 14:19:01 +08:00
parent 0dac26d781
commit aa2cf560c6
120 changed files with 33383 additions and 4347 deletions

View File

@@ -10,6 +10,7 @@
import { Certificate } from '@/types/certificate';
import { IDataSource, CertificateQueryParams } from '@/types/datasource';
import { BaseDataSource, BaseMockDataSource, DataSourceFactory } from './dataSourceFactory';
// ============================================
// 真实API实现
@@ -19,104 +20,9 @@ import { IDataSource, CertificateQueryParams } from '@/types/datasource';
* 证书API数据源
* 调用真实后端API
*/
class ApiCertificateDataSource implements IDataSource<Certificate, CertificateQueryParams> {
private baseUrl = '/api/v1/certificate';
async list(params?: CertificateQueryParams): Promise<Certificate[]> {
const query = new URLSearchParams();
if (params?.status) query.append('status', params.status);
if (params?.type) query.append('type', params.type);
if (params?.keyword) query.append('keyword', params.keyword);
if (params?.page) query.append('page', params.page.toString());
if (params?.pageSize) query.append('pageSize', params.pageSize.toString());
const response = await fetch(`${this.baseUrl}/certificates?${query.toString()}`, {
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<Certificate | null> {
const response = await fetch(`${this.baseUrl}/certificates/${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<Certificate>): Promise<Certificate> {
const response = await fetch(`${this.baseUrl}/certificates`, {
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<Certificate>): Promise<Certificate> {
const response = await fetch(`${this.baseUrl}/certificates/${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}/certificates/${id}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
class ApiCertificateDataSource extends BaseDataSource<Certificate, CertificateQueryParams> {
constructor() {
super('/api/v1/certificate/certificates');
}
/**
@@ -126,7 +32,7 @@ class ApiCertificateDataSource implements IDataSource<Certificate, CertificateQu
* @param approvedBy 审核人
*/
async updateStatus(id: string, status: string, approvedBy?: string): Promise<Certificate> {
const response = await fetch(`${this.baseUrl}/certificates/${id}/status`, {
const response = await fetch(`${this.baseUrl}/${id}/status`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
@@ -157,13 +63,12 @@ class ApiCertificateDataSource implements IDataSource<Certificate, CertificateQu
* AI注意: 这是Mock实现不是真实业务逻辑
* 仅在REACT_APP_USE_MOCK=true时启用
*/
class MockCertificateDataSource implements IDataSource<Certificate, CertificateQueryParams> {
/** Mock标记 */
readonly __MOCK__ = true as const;
class MockCertificateDataSource extends BaseMockDataSource<Certificate, CertificateQueryParams> {
/** Mock数据源名称 */
readonly __MOCK_NAME__ = 'MockCertificateDataSource';
private mockData: Certificate[] = [
/** Mock数据 */
protected mockData: Certificate[] = [
{
id: '1',
name: 'CE认证证书',
@@ -248,89 +153,12 @@ class MockCertificateDataSource implements IDataSource<Certificate, CertificateQ
},
];
private delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
async list(params?: CertificateQueryParams): Promise<Certificate[]> {
// 模拟网络延迟
await this.delay(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);
}
// 关键词搜索
if (params?.keyword) {
const keyword = params.keyword.toLowerCase();
result = result.filter(
item =>
item.name.toLowerCase().includes(keyword) ||
item.productName?.toLowerCase().includes(keyword) ||
item.notes?.toLowerCase().includes(keyword)
);
}
// 分页
const page = params?.page || 1;
const pageSize = params?.pageSize || 10;
const start = (page - 1) * pageSize;
const end = start + pageSize;
return result.slice(start, end);
}
async detail(id: string): Promise<Certificate | null> {
await this.delay(200);
return this.mockData.find(item => item.id === id) || null;
}
async create(data: Partial<Certificate>): Promise<Certificate> {
await this.delay(500);
const newCert: Certificate = {
id: `${Date.now()}`,
name: data.name || '',
type: data.type || 'OTHER',
status: 'PENDING',
fileUrl: data.fileUrl || '/files/uploaded.pdf',
fileName: data.fileName || 'uploaded.pdf',
uploadDate: new Date().toISOString().split('T')[0],
expiryDate: data.expiryDate || '',
productId: data.productId,
productName: data.productName,
notes: data.notes,
};
this.mockData.unshift(newCert);
return newCert;
}
async update(id: string, data: Partial<Certificate>): Promise<Certificate> {
await this.delay(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 this.delay(200);
this.mockData = this.mockData.filter(item => item.id !== id);
}
/**
* 更新证书状态(审核)
* @param id 证书ID
* @param status 新状态
* @param approvedBy 审核人
*/
async updateStatus(id: string, status: string, approvedBy?: string): Promise<Certificate> {
await this.delay(300);
@@ -354,40 +182,22 @@ class MockCertificateDataSource implements IDataSource<Certificate, CertificateQ
}
// ============================================
// 导出数据源实例 (环境变量控制)
// 导出数据源
// ============================================
const useMock = process.env.REACT_APP_USE_MOCK === 'true';
/**
* 证书数据源实例
* 根据环境变量自动切换Mock/真实API
*
* 使用示例:
* ```typescript
* import { certificateDataSource } from '@/services/certificateDataSource';
*
* // 查询列表
* const certificates = await certificateDataSource.list({ status: 'APPROVED' });
*
* // 获取详情
* const cert = await certificateDataSource.detail('1');
*
* // 创建
* const newCert = await certificateDataSource.create({ name: '新证书', ... });
* ```
*/
export const certificateDataSource: IDataSource<Certificate, CertificateQueryParams> & {
updateStatus?(id: string, status: string, approvedBy?: string): Promise<Certificate>;
} = useMock ? new MockCertificateDataSource() : new ApiCertificateDataSource();
export const certificateDataSource = DataSourceFactory.createWithMethods<
Certificate,
CertificateQueryParams,
{
updateStatus(id: string, status: string, approvedBy?: string): Promise<Certificate>;
}
>({
apiDataSource: ApiCertificateDataSource,
mockDataSource: MockCertificateDataSource,
});
/**
* Mock状态标记
* 用于调试和开发环境识别
*/
export const __MOCK__ = useMock;
/**
* 当前数据源类型
*/
export const __DATA_SOURCE_TYPE__ = useMock ? 'mock' : 'api';
export { __MOCK__, __DATA_SOURCE_TYPE__ } from './dataSourceFactory';