Files
makemd/dashboard/src/services/userAssetDataSource.ts
wurenzhi 2748456d8a refactor(services): 重构服务文件结构,将服务按功能分类到不同目录
- 将服务文件按功能分类到core、ai、analytics、security等目录
- 修复logger导入路径问题,统一使用相对路径
- 更新相关文件的导入路径引用
- 添加新的批量操作组件导出文件
- 修复dashboard页面中的类型错误
- 添加dotenv依赖到package.json
2026-03-25 13:46:26 +08:00

289 lines
9.5 KiB
TypeScript
Raw Permalink 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.
import { http } from './http';
/**
* [MOCK] 用户资产数据<E695B0>? * AI注意: 这是Mock实现不是真实业务逻辑
* 仅在USE_MOCK=true时启<E697B6>? */
export type MemberLevel = 'BRONZE' | 'SILVER' | 'GOLD' | 'PLATINUM' | 'DIAMOND';
export interface UserAsset {
id: string;
tenantId: string;
userId: string;
userName: string;
email: string;
userEmail?: string; // <20>?email 同义
memberLevel: MemberLevel;
points: number;
memberScore?: number; // <20>?points 同义
totalSpent: number;
availableBalance: number;
availablePoints?: number; // <20>?availableBalance 同义
frozenBalance: number;
frozenPoints?: number; // <20>?frozenBalance 同义
cashbackBalance?: number;
couponCount?: 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 type PointsSourceType =
| 'PURCHASE'
| 'REVIEW'
| 'REFERRAL'
| 'PROMOTION'
| 'ADMIN_ADJUST'
| 'EXPIRED'
| 'REFUND'
| 'MANUAL_DEDUCT';
export type PointsStatus = 'PENDING' | 'CONFIRMED' | 'FROZEN' | 'EXPIRED' | 'CANCELLED';
export interface PointsRecord {
id: string;
userId: string;
userName: string;
type: 'EARN' | 'REDEEM' | 'EXPIRE' | 'ADJUST';
amount: number;
balance: number;
source: string;
description: string;
createdAt: string;
// 额外字段<EFBC8C>?PointsManage 页面
tenantId?: string;
shopId?: string;
traceId?: string;
businessType?: 'TOC' | 'TOB';
points?: number; // <20>?amount 同义
sourceType?: string; // <20>?source 同义
status?: 'PENDING' | 'CONFIRMED' | 'FROZEN' | 'EXPIRED' | 'CANCELLED';
expiredAt?: string;
sourceId?: 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',
// 额外字段
tenantId: 'T001',
shopId: 'S001',
traceId: 'TR001',
businessType: 'TOC',
points: 100,
sourceType: 'PURCHASE',
status: 'CONFIRMED'
},
{
id: '2',
userId: 'user_001',
userName: 'John Doe',
type: 'REDEEM',
amount: -50,
balance: 4950,
source: 'Redemption',
description: 'Coupon redemption',
createdAt: '2026-03-16',
// 额外字段
tenantId: 'T001',
shopId: 'S001',
traceId: 'TR002',
businessType: 'TOC',
points: -50,
sourceType: 'MANUAL_DEDUCT',
status: 'CONFIRMED'
},
];
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 http.get(`${this.baseUrl}?${new URLSearchParams(params as any)}`);
return response.data;
}
async updateUserAsset(id: string, data: Partial<UserAsset>): Promise<UserAsset> {
const response = await http.put(`${this.baseUrl}/${id}`, data);
return response.data;
}
async fetchMemberLevelConfigs(): Promise<MemberLevelConfig[]> {
const response = await http.get(`${this.baseUrl}/member-levels`);
return response.data;
}
async updateMemberLevelConfig(id: string, data: Partial<MemberLevelConfig>): Promise<MemberLevelConfig> {
const response = await http.put(`${this.baseUrl}/member-levels/${id}`, data);
return response.data;
}
async fetchPointsRecords(params?: { userId?: string; type?: string }): Promise<PointsRecord[]> {
const response = await http.get(`${this.baseUrl}/points?${new URLSearchParams(params as any)}`);
return response.data;
}
async adjustPoints(userId: string, amount: number, reason: string): Promise<PointsRecord> {
const response = await http.post(`${this.baseUrl}/points/adjust`, { userId, amount, reason });
return response.data;
}
}
const useMock = process.env.NODE_ENV === 'development' || process.env.REACT_APP_USE_MOCK === 'true';
export const userAssetDataSource: IUserAssetDataSource = useMock
? new MockUserAssetDataSource()
: new ApiUserAssetDataSource();