Files
makemd/dashboard/src/services/userAssetDataSource.ts
wurenzhi aa2cf560c6 feat: 添加汇率服务和缓存服务,优化数据源和日志服务
refactor: 重构数据源工厂和类型定义,提升代码可维护性

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

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

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

chore: 清理无用代码和注释,优化代码结构
2026-03-19 14:19:01 +08:00

238 lines
8.8 KiB
TypeScript
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] 用户资产数据源
* AI注意: 这是Mock实现不是真实业务逻辑
* 仅在USE_MOCK=true时启用
*/
export type MemberLevel = 'BRONZE' | 'SILVER' | 'GOLD' | 'PLATINUM' | 'DIAMOND';
export interface UserAsset {
id: string;
tenantId: string;
userId: string;
userName: string;
email: string;
memberLevel: MemberLevel;
points: number;
totalSpent: number;
availableBalance: number;
frozenBalance: number;
totalOrders: number;
joinDate: string;
lastActiveDate: string;
status: 'ACTIVE' | 'INACTIVE' | 'SUSPENDED';
}
export interface MemberLevelConfig {
id: string;
level: MemberLevel;
name: string;
minSpent: number;
discount: number;
pointsMultiplier: number;
benefits: string[];
color: string;
icon: string;
}
export interface PointsRecord {
id: string;
userId: string;
userName: string;
type: 'EARN' | 'REDEEM' | 'EXPIRE' | 'ADJUST';
amount: number;
balance: number;
source: string;
description: string;
createdAt: string;
}
export interface IUserAssetDataSource {
fetchUserAssets(params?: { memberLevel?: string; status?: string; search?: string }): Promise<UserAsset[]>;
updateUserAsset(id: string, data: Partial<UserAsset>): Promise<UserAsset>;
fetchMemberLevelConfigs(): Promise<MemberLevelConfig[]>;
updateMemberLevelConfig(id: string, data: Partial<MemberLevelConfig>): Promise<MemberLevelConfig>;
fetchPointsRecords(params?: { userId?: string; type?: string }): Promise<PointsRecord[]>;
adjustPoints(userId: string, amount: number, reason: string): Promise<PointsRecord>;
}
class MockUserAssetDataSource implements IUserAssetDataSource {
private userAssets: UserAsset[] = [
{
id: '1',
tenantId: 'tenant_001',
userId: 'user_001',
userName: 'John Doe',
email: 'john@example.com',
memberLevel: 'GOLD',
points: 5000,
totalSpent: 15000,
availableBalance: 500,
frozenBalance: 100,
totalOrders: 25,
joinDate: '2025-01-15',
lastActiveDate: '2026-03-18',
status: 'ACTIVE',
},
{
id: '2',
tenantId: 'tenant_001',
userId: 'user_002',
userName: 'Jane Smith',
email: 'jane@example.com',
memberLevel: 'PLATINUM',
points: 15000,
totalSpent: 50000,
availableBalance: 1200,
frozenBalance: 0,
totalOrders: 85,
joinDate: '2024-06-20',
lastActiveDate: '2026-03-19',
status: 'ACTIVE',
},
];
private memberLevelConfigs: MemberLevelConfig[] = [
{ id: '1', level: 'BRONZE', name: 'Bronze', minSpent: 0, discount: 0, pointsMultiplier: 1, benefits: ['Basic support'], color: '#CD7F32', icon: '🥉' },
{ id: '2', level: 'SILVER', name: 'Silver', minSpent: 1000, discount: 5, pointsMultiplier: 1.2, benefits: ['Priority support', '5% discount'], color: '#C0C0C0', icon: '🥈' },
{ id: '3', level: 'GOLD', name: 'Gold', minSpent: 5000, discount: 10, pointsMultiplier: 1.5, benefits: ['VIP support', '10% discount', 'Free shipping'], color: '#FFD700', icon: '🥇' },
{ id: '4', level: 'PLATINUM', name: 'Platinum', minSpent: 20000, discount: 15, pointsMultiplier: 2, benefits: ['Dedicated support', '15% discount', 'Free express shipping', 'Birthday gift'], color: '#E5E4E2', icon: '💎' },
{ id: '5', level: 'DIAMOND', name: 'Diamond', minSpent: 50000, discount: 20, pointsMultiplier: 3, benefits: ['Personal account manager', '20% discount', 'Exclusive events'], color: '#B9F2FF', icon: '💠' },
];
private pointsRecords: PointsRecord[] = [
{ id: '1', userId: 'user_001', userName: 'John Doe', type: 'EARN', amount: 100, balance: 5000, source: 'Purchase', description: 'Order #ORD-001', createdAt: '2026-03-15' },
{ id: '2', userId: 'user_001', userName: 'John Doe', type: 'REDEEM', amount: -50, balance: 4950, source: 'Redemption', description: 'Coupon redemption', createdAt: '2026-03-16' },
];
async fetchUserAssets(params?: { memberLevel?: string; status?: string; search?: string }): Promise<UserAsset[]> {
await new Promise(resolve => setTimeout(resolve, 300));
let result = [...this.userAssets];
if (params?.memberLevel) {
result = result.filter(u => u.memberLevel === params.memberLevel);
}
if (params?.status) {
result = result.filter(u => u.status === params.status);
}
if (params?.search) {
result = result.filter(u => u.userName.toLowerCase().includes(params.search!.toLowerCase()) || u.email.toLowerCase().includes(params.search!.toLowerCase()));
}
return result;
}
async updateUserAsset(id: string, data: Partial<UserAsset>): Promise<UserAsset> {
await new Promise(resolve => setTimeout(resolve, 300));
const index = this.userAssets.findIndex(u => u.id === id);
if (index === -1) throw new Error('User asset not found');
this.userAssets[index] = { ...this.userAssets[index], ...data };
return this.userAssets[index];
}
async fetchMemberLevelConfigs(): Promise<MemberLevelConfig[]> {
await new Promise(resolve => setTimeout(resolve, 300));
return [...this.memberLevelConfigs];
}
async updateMemberLevelConfig(id: string, data: Partial<MemberLevelConfig>): Promise<MemberLevelConfig> {
await new Promise(resolve => setTimeout(resolve, 300));
const index = this.memberLevelConfigs.findIndex(c => c.id === id);
if (index === -1) throw new Error('Config not found');
this.memberLevelConfigs[index] = { ...this.memberLevelConfigs[index], ...data };
return this.memberLevelConfigs[index];
}
async fetchPointsRecords(params?: { userId?: string; type?: string }): Promise<PointsRecord[]> {
await new Promise(resolve => setTimeout(resolve, 300));
let result = [...this.pointsRecords];
if (params?.userId) {
result = result.filter(r => r.userId === params.userId);
}
if (params?.type) {
result = result.filter(r => r.type === params.type);
}
return result;
}
async adjustPoints(userId: string, amount: number, reason: string): Promise<PointsRecord> {
await new Promise(resolve => setTimeout(resolve, 300));
const user = this.userAssets.find(u => u.userId === userId);
if (!user) throw new Error('User not found');
const newBalance = user.points + amount;
const record: PointsRecord = {
id: `${Date.now()}`,
userId,
userName: user.userName,
type: amount > 0 ? 'EARN' : 'REDEEM',
amount,
balance: newBalance,
source: 'Manual Adjustment',
description: reason,
createdAt: new Date().toISOString().split('T')[0],
};
user.points = newBalance;
this.pointsRecords.push(record);
return record;
}
}
class ApiUserAssetDataSource implements IUserAssetDataSource {
private baseUrl = '/api/user-assets';
async fetchUserAssets(params?: { memberLevel?: string; status?: string; search?: string }): Promise<UserAsset[]> {
const response = await fetch(`${this.baseUrl}?${new URLSearchParams(params as any)}`);
if (!response.ok) throw new Error('Failed to fetch user assets');
return response.json();
}
async updateUserAsset(id: string, data: Partial<UserAsset>): Promise<UserAsset> {
const response = await fetch(`${this.baseUrl}/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!response.ok) throw new Error('Failed to update user asset');
return response.json();
}
async fetchMemberLevelConfigs(): Promise<MemberLevelConfig[]> {
const response = await fetch(`${this.baseUrl}/member-levels`);
if (!response.ok) throw new Error('Failed to fetch member level configs');
return response.json();
}
async updateMemberLevelConfig(id: string, data: Partial<MemberLevelConfig>): Promise<MemberLevelConfig> {
const response = await fetch(`${this.baseUrl}/member-levels/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!response.ok) throw new Error('Failed to update member level config');
return response.json();
}
async fetchPointsRecords(params?: { userId?: string; type?: string }): Promise<PointsRecord[]> {
const response = await fetch(`${this.baseUrl}/points?${new URLSearchParams(params as any)}`);
if (!response.ok) throw new Error('Failed to fetch points records');
return response.json();
}
async adjustPoints(userId: string, amount: number, reason: string): Promise<PointsRecord> {
const response = await fetch(`${this.baseUrl}/points/adjust`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId, amount, reason }),
});
if (!response.ok) throw new Error('Failed to adjust points');
return response.json();
}
}
const useMock = process.env.REACT_APP_USE_MOCK === 'true';
export const userAssetDataSource: IUserAssetDataSource = useMock
? new MockUserAssetDataSource()
: new ApiUserAssetDataSource();