2026-03-19 01:39:34 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* [MOCK] 商户管理数据源
|
|
|
|
|
|
* AI注意: 这是Mock实现,不是真实业务逻辑
|
|
|
|
|
|
* 仅在USE_MOCK=true时启用
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
export interface Merchant {
|
|
|
|
|
|
id: string;
|
|
|
|
|
|
tenantId: string;
|
|
|
|
|
|
companyName: string;
|
|
|
|
|
|
businessLicense: string;
|
|
|
|
|
|
contactEmail: string;
|
|
|
|
|
|
contactPhone: string;
|
|
|
|
|
|
status: 'PENDING' | 'ACTIVE' | 'SUSPENDED' | 'TERMINATED';
|
|
|
|
|
|
tier: 'BASIC' | 'PRO' | 'ENTERPRISE';
|
|
|
|
|
|
traceId: string;
|
|
|
|
|
|
createdAt: string;
|
|
|
|
|
|
updatedAt: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface MerchantShop {
|
|
|
|
|
|
id: string;
|
|
|
|
|
|
merchantId: string;
|
|
|
|
|
|
shopName: string;
|
|
|
|
|
|
platform: string;
|
|
|
|
|
|
shopUrl: string;
|
|
|
|
|
|
status: 'ACTIVE' | 'INACTIVE' | 'SUSPENDED';
|
|
|
|
|
|
createdAt: string;
|
|
|
|
|
|
updatedAt: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface MerchantOrder {
|
|
|
|
|
|
id: string;
|
|
|
|
|
|
merchantId: string;
|
2026-03-22 11:25:28 +08:00
|
|
|
|
merchantName: string;
|
2026-03-19 01:39:34 +08:00
|
|
|
|
orderId: string;
|
2026-03-22 11:25:28 +08:00
|
|
|
|
platform: string;
|
2026-03-19 01:39:34 +08:00
|
|
|
|
totalAmount: number;
|
|
|
|
|
|
status: string;
|
|
|
|
|
|
createdAt: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface MerchantSettlement {
|
|
|
|
|
|
id: string;
|
|
|
|
|
|
merchantId: string;
|
|
|
|
|
|
merchantName: string;
|
|
|
|
|
|
periodStart: string;
|
|
|
|
|
|
periodEnd: string;
|
|
|
|
|
|
totalAmount: number;
|
|
|
|
|
|
status: 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'FAILED';
|
|
|
|
|
|
createdAt: string;
|
|
|
|
|
|
updatedAt: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface IMerchantDataSource {
|
|
|
|
|
|
fetchMerchants(params?: { status?: string; tier?: string; search?: string }): Promise<Merchant[]>;
|
|
|
|
|
|
createMerchant(data: Partial<Merchant>): Promise<Merchant>;
|
|
|
|
|
|
updateMerchant(id: string, data: Partial<Merchant>): Promise<Merchant>;
|
|
|
|
|
|
deleteMerchant(id: string): Promise<void>;
|
|
|
|
|
|
|
|
|
|
|
|
fetchShops(params?: { merchantId?: string; platform?: string; status?: string }): Promise<MerchantShop[]>;
|
|
|
|
|
|
createShop(data: Partial<MerchantShop>): Promise<MerchantShop>;
|
|
|
|
|
|
updateShop(id: string, data: Partial<MerchantShop>): Promise<MerchantShop>;
|
|
|
|
|
|
deleteShop(id: string): Promise<void>;
|
|
|
|
|
|
|
|
|
|
|
|
fetchMerchantOrders(merchantId: string, params?: { status?: string }): Promise<MerchantOrder[]>;
|
|
|
|
|
|
fetchSettlements(params?: { merchantId?: string; status?: string }): Promise<MerchantSettlement[]>;
|
|
|
|
|
|
processSettlement(id: string, action: 'APPROVE' | 'REJECT'): Promise<MerchantSettlement>;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class MockMerchantDataSource implements IMerchantDataSource {
|
|
|
|
|
|
private merchants: Merchant[] = [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: '1',
|
|
|
|
|
|
tenantId: 'tenant_001',
|
|
|
|
|
|
companyName: 'Test Company A',
|
|
|
|
|
|
businessLicense: 'BL-001',
|
|
|
|
|
|
contactEmail: 'contact@company-a.com',
|
|
|
|
|
|
contactPhone: '+1 (123) 456-7890',
|
|
|
|
|
|
status: 'ACTIVE',
|
|
|
|
|
|
tier: 'ENTERPRISE',
|
|
|
|
|
|
traceId: 'trace_001',
|
|
|
|
|
|
createdAt: '2026-03-01',
|
|
|
|
|
|
updatedAt: '2026-03-01',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: '2',
|
|
|
|
|
|
tenantId: 'tenant_002',
|
|
|
|
|
|
companyName: 'Test Company B',
|
|
|
|
|
|
businessLicense: 'BL-002',
|
|
|
|
|
|
contactEmail: 'contact@company-b.com',
|
|
|
|
|
|
contactPhone: '+1 (234) 567-8901',
|
|
|
|
|
|
status: 'ACTIVE',
|
|
|
|
|
|
tier: 'PRO',
|
|
|
|
|
|
traceId: 'trace_002',
|
|
|
|
|
|
createdAt: '2026-03-05',
|
|
|
|
|
|
updatedAt: '2026-03-05',
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
private shops: MerchantShop[] = [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: '1',
|
|
|
|
|
|
merchantId: '1',
|
|
|
|
|
|
shopName: 'Shop A - Amazon',
|
|
|
|
|
|
platform: 'Amazon',
|
|
|
|
|
|
shopUrl: 'https://amazon.com/shop-a',
|
|
|
|
|
|
status: 'ACTIVE',
|
|
|
|
|
|
createdAt: '2026-03-01',
|
|
|
|
|
|
updatedAt: '2026-03-01',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: '2',
|
|
|
|
|
|
merchantId: '1',
|
|
|
|
|
|
shopName: 'Shop A - Shopify',
|
|
|
|
|
|
platform: 'Shopify',
|
|
|
|
|
|
shopUrl: 'https://shop-a.myshopify.com',
|
|
|
|
|
|
status: 'ACTIVE',
|
|
|
|
|
|
createdAt: '2026-03-02',
|
|
|
|
|
|
updatedAt: '2026-03-02',
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
private orders: MerchantOrder[] = [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: '1',
|
|
|
|
|
|
merchantId: '1',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
merchantName: 'Test Company A',
|
2026-03-19 01:39:34 +08:00
|
|
|
|
orderId: 'ORD-2026-001',
|
2026-03-22 11:25:28 +08:00
|
|
|
|
platform: 'Amazon',
|
2026-03-19 01:39:34 +08:00
|
|
|
|
totalAmount: 1500.00,
|
|
|
|
|
|
status: 'COMPLETED',
|
|
|
|
|
|
createdAt: '2026-03-15',
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
async fetchMerchants(params?: { status?: string; tier?: string; search?: string }): Promise<Merchant[]> {
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
|
|
|
|
let result = [...this.merchants];
|
|
|
|
|
|
if (params?.status) {
|
|
|
|
|
|
result = result.filter(m => m.status === params.status);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (params?.tier) {
|
|
|
|
|
|
result = result.filter(m => m.tier === params.tier);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (params?.search) {
|
|
|
|
|
|
result = result.filter(m => m.companyName.toLowerCase().includes(params.search!.toLowerCase()));
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async createMerchant(data: Partial<Merchant>): Promise<Merchant> {
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
|
|
|
|
const newMerchant: Merchant = {
|
|
|
|
|
|
id: `${Date.now()}`,
|
|
|
|
|
|
tenantId: `tenant_${Date.now()}`,
|
|
|
|
|
|
companyName: data.companyName || '',
|
|
|
|
|
|
businessLicense: data.businessLicense || '',
|
|
|
|
|
|
contactEmail: data.contactEmail || '',
|
|
|
|
|
|
contactPhone: data.contactPhone || '',
|
|
|
|
|
|
status: 'PENDING',
|
|
|
|
|
|
tier: 'BASIC',
|
|
|
|
|
|
traceId: `trace_${Date.now()}`,
|
|
|
|
|
|
createdAt: new Date().toISOString().split('T')[0],
|
|
|
|
|
|
updatedAt: new Date().toISOString().split('T')[0],
|
|
|
|
|
|
...data,
|
|
|
|
|
|
};
|
|
|
|
|
|
this.merchants.push(newMerchant);
|
|
|
|
|
|
return newMerchant;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async updateMerchant(id: string, data: Partial<Merchant>): Promise<Merchant> {
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
|
|
|
|
const index = this.merchants.findIndex(m => m.id === id);
|
|
|
|
|
|
if (index === -1) throw new Error('Merchant not found');
|
|
|
|
|
|
this.merchants[index] = { ...this.merchants[index], ...data, updatedAt: new Date().toISOString().split('T')[0] };
|
|
|
|
|
|
return this.merchants[index];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async deleteMerchant(id: string): Promise<void> {
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
|
|
|
|
const index = this.merchants.findIndex(m => m.id === id);
|
|
|
|
|
|
if (index !== -1) {
|
|
|
|
|
|
this.merchants.splice(index, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async fetchShops(params?: { merchantId?: string; platform?: string; status?: string }): Promise<MerchantShop[]> {
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
|
|
|
|
let result = [...this.shops];
|
|
|
|
|
|
if (params?.merchantId) {
|
|
|
|
|
|
result = result.filter(s => s.merchantId === params.merchantId);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (params?.platform) {
|
|
|
|
|
|
result = result.filter(s => s.platform === params.platform);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (params?.status) {
|
|
|
|
|
|
result = result.filter(s => s.status === params.status);
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async createShop(data: Partial<MerchantShop>): Promise<MerchantShop> {
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
|
|
|
|
const newShop: MerchantShop = {
|
|
|
|
|
|
id: `${Date.now()}`,
|
|
|
|
|
|
merchantId: data.merchantId || '',
|
|
|
|
|
|
shopName: data.shopName || '',
|
|
|
|
|
|
platform: data.platform || '',
|
|
|
|
|
|
shopUrl: data.shopUrl || '',
|
|
|
|
|
|
status: 'ACTIVE',
|
|
|
|
|
|
createdAt: new Date().toISOString().split('T')[0],
|
|
|
|
|
|
updatedAt: new Date().toISOString().split('T')[0],
|
|
|
|
|
|
...data,
|
|
|
|
|
|
};
|
|
|
|
|
|
this.shops.push(newShop);
|
|
|
|
|
|
return newShop;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async updateShop(id: string, data: Partial<MerchantShop>): Promise<MerchantShop> {
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
|
|
|
|
const index = this.shops.findIndex(s => s.id === id);
|
|
|
|
|
|
if (index === -1) throw new Error('Shop not found');
|
|
|
|
|
|
this.shops[index] = { ...this.shops[index], ...data, updatedAt: new Date().toISOString().split('T')[0] };
|
|
|
|
|
|
return this.shops[index];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async deleteShop(id: string): Promise<void> {
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
|
|
|
|
const index = this.shops.findIndex(s => s.id === id);
|
|
|
|
|
|
if (index !== -1) {
|
|
|
|
|
|
this.shops.splice(index, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async fetchMerchantOrders(merchantId: string, params?: { status?: string }): Promise<MerchantOrder[]> {
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
|
|
|
|
let result = this.orders.filter(o => o.merchantId === merchantId);
|
|
|
|
|
|
if (params?.status) {
|
|
|
|
|
|
result = result.filter(o => o.status === params.status);
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private settlements: MerchantSettlement[] = [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: '1',
|
|
|
|
|
|
merchantId: '1',
|
|
|
|
|
|
merchantName: 'Test Company A',
|
|
|
|
|
|
periodStart: '2026-03-01',
|
|
|
|
|
|
periodEnd: '2026-03-15',
|
|
|
|
|
|
totalAmount: 25000.00,
|
|
|
|
|
|
status: 'PENDING',
|
|
|
|
|
|
createdAt: '2026-03-16',
|
|
|
|
|
|
updatedAt: '2026-03-16',
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
async fetchSettlements(params?: { merchantId?: string; status?: string }): Promise<MerchantSettlement[]> {
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
|
|
|
|
let result = [...this.settlements];
|
|
|
|
|
|
if (params?.merchantId) {
|
|
|
|
|
|
result = result.filter(s => s.merchantId === params.merchantId);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (params?.status) {
|
|
|
|
|
|
result = result.filter(s => s.status === params.status);
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async processSettlement(id: string, action: 'APPROVE' | 'REJECT'): Promise<MerchantSettlement> {
|
|
|
|
|
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
|
|
|
|
const index = this.settlements.findIndex(s => s.id === id);
|
|
|
|
|
|
if (index === -1) throw new Error('Settlement not found');
|
|
|
|
|
|
this.settlements[index] = {
|
|
|
|
|
|
...this.settlements[index],
|
|
|
|
|
|
status: action === 'APPROVE' ? 'COMPLETED' : 'FAILED',
|
|
|
|
|
|
updatedAt: new Date().toISOString().split('T')[0],
|
|
|
|
|
|
};
|
|
|
|
|
|
return this.settlements[index];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class ApiMerchantDataSource implements IMerchantDataSource {
|
|
|
|
|
|
private baseUrl = '/api/merchants';
|
|
|
|
|
|
|
|
|
|
|
|
async fetchMerchants(params?: { status?: string; tier?: string; search?: string }): Promise<Merchant[]> {
|
|
|
|
|
|
const response = await fetch(`${this.baseUrl}?${new URLSearchParams(params as any)}`);
|
|
|
|
|
|
if (!response.ok) throw new Error('Failed to fetch merchants');
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async createMerchant(data: Partial<Merchant>): Promise<Merchant> {
|
|
|
|
|
|
const response = await fetch(this.baseUrl, {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
|
body: JSON.stringify(data),
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!response.ok) throw new Error('Failed to create merchant');
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async updateMerchant(id: string, data: Partial<Merchant>): Promise<Merchant> {
|
|
|
|
|
|
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 merchant');
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async deleteMerchant(id: string): Promise<void> {
|
|
|
|
|
|
const response = await fetch(`${this.baseUrl}/${id}`, { method: 'DELETE' });
|
|
|
|
|
|
if (!response.ok) throw new Error('Failed to delete merchant');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async fetchShops(params?: { merchantId?: string; platform?: string; status?: string }): Promise<MerchantShop[]> {
|
|
|
|
|
|
const response = await fetch(`${this.baseUrl}/shops?${new URLSearchParams(params as any)}`);
|
|
|
|
|
|
if (!response.ok) throw new Error('Failed to fetch shops');
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async createShop(data: Partial<MerchantShop>): Promise<MerchantShop> {
|
|
|
|
|
|
const response = await fetch(`${this.baseUrl}/shops`, {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
|
body: JSON.stringify(data),
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!response.ok) throw new Error('Failed to create shop');
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async updateShop(id: string, data: Partial<MerchantShop>): Promise<MerchantShop> {
|
|
|
|
|
|
const response = await fetch(`${this.baseUrl}/shops/${id}`, {
|
|
|
|
|
|
method: 'PUT',
|
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
|
body: JSON.stringify(data),
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!response.ok) throw new Error('Failed to update shop');
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async deleteShop(id: string): Promise<void> {
|
|
|
|
|
|
const response = await fetch(`${this.baseUrl}/shops/${id}`, { method: 'DELETE' });
|
|
|
|
|
|
if (!response.ok) throw new Error('Failed to delete shop');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async fetchMerchantOrders(merchantId: string, params?: { status?: string }): Promise<MerchantOrder[]> {
|
|
|
|
|
|
const response = await fetch(`${this.baseUrl}/${merchantId}/orders?${new URLSearchParams(params as any)}`);
|
|
|
|
|
|
if (!response.ok) throw new Error('Failed to fetch merchant orders');
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async fetchSettlements(params?: { merchantId?: string; status?: string }): Promise<MerchantSettlement[]> {
|
|
|
|
|
|
const response = await fetch(`${this.baseUrl}/settlements?${new URLSearchParams(params as any)}`);
|
|
|
|
|
|
if (!response.ok) throw new Error('Failed to fetch settlements');
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async processSettlement(id: string, action: 'APPROVE' | 'REJECT'): Promise<MerchantSettlement> {
|
|
|
|
|
|
const response = await fetch(`${this.baseUrl}/settlements/${id}/process`, {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
|
body: JSON.stringify({ action }),
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!response.ok) throw new Error('Failed to process settlement');
|
|
|
|
|
|
return response.json();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const useMock = process.env.REACT_APP_USE_MOCK === 'true';
|
|
|
|
|
|
export const merchantDataSource: IMerchantDataSource = useMock
|
|
|
|
|
|
? new MockMerchantDataSource()
|
|
|
|
|
|
: new ApiMerchantDataSource();
|