feat: 添加部门管理功能、主题切换和多语言支持

refactor(dashboard): 重构用户管理页面和路由结构

feat(server): 实现部门管理API和RBAC增强功能

docs: 更新用户手册和管理员指南文档

style: 统一图标使用和组件命名规范

test: 添加部门服务和数据隔离测试用例

chore: 更新依赖和配置文件
This commit is contained in:
2026-03-28 22:52:12 +08:00
parent 22308fe042
commit d327706087
87 changed files with 21372 additions and 4806 deletions

View File

@@ -1,386 +1,213 @@
/**
* [MOCK-ANALYTICS-001] Analytics数据源抽象层
* AI注意: 这是数据源抽象层,业务组件通过此层获取数据
* 仅在REACT_APP_USE_MOCK=true时启用Mock
*
* @module services/analyticsDataSource
* @created 2026-03-19
*/
import { message } from 'antd';
import { http } from './http';
import { IDataSource } from '@/types/datasource';
export interface MetricData {
key: string;
label: string;
value: number;
unit: string;
change: number;
changeType: 'up' | 'down' | 'neutral';
icon: string;
color: string;
// 类型定义
export interface UserActivity {
userId: string;
userName: string;
loginCount: number;
totalSessionTime: number;
lastLogin: string;
activityLevel: 'high' | 'medium' | 'low';
}
export interface ChartData {
date: string;
sales: number;
orders: number;
visitors: number;
conversion: number;
export interface UserActivityResponse {
data: UserActivity[];
stats: {
totalLogins: number;
activeUsers: number;
avgSessionTime: number;
peakHour: number;
};
}
export interface ProductAnalytics {
export interface FeatureUsage {
feature: string;
usageCount: number;
usagePercentage: number;
}
export interface TenantUsage {
shopCount: number;
maxShops: number;
userCount: number;
maxUsers: number;
storageUsed: number;
storageQuota: number;
apiCalls: number;
maxApiCalls: number;
featureUsage: FeatureUsage[];
activeDays: number;
}
export interface PermissionUsage {
permissionName: string;
usageCount: number;
usageFrequency: number;
lastUsed: string;
userCount: number;
}
export interface SecurityAudit {
id: string;
productId: string;
name: string;
image: string;
category: string;
sales: number;
revenue: number;
profit: number;
profitMargin: number;
views: number;
conversionRate: number;
returnRate: number;
trend: 'up' | 'down' | 'stable';
}
export interface OrderAnalytics {
id: string;
orderId: string;
platform: string;
status: string;
amount: number;
profit: number;
createdAt: string;
customerType: 'new' | 'returning';
shippingCost: number;
platformFee: number;
}
export interface ProfitAnalytics {
id: string;
period: string;
revenue: number;
cost: number;
grossProfit: number;
netProfit: number;
margin: number;
platformFees: number;
shippingCost: number;
adSpend: number;
otherCosts: number;
}
export interface RealtimeMetrics {
timestamp: string;
activeUsers: number;
ordersPerMinute: number;
revenuePerMinute: number;
conversionRate: number;
eventType: 'login' | 'logout' | 'permission' | 'data_access' | 'error';
userId: string;
action: string;
ipAddress: string;
status: 'success' | 'failure';
details?: string;
}
export interface AlertRule {
id: string;
name: string;
metric: string;
condition: '>' | '<' | '=' | '>=' | '<=';
threshold: number;
enabled: boolean;
notifyChannels: string[];
lastTriggered?: string;
}
export interface Dashboard {
id: string;
name: string;
widgets: Widget[];
isDefault: boolean;
createdAt: string;
}
export interface Widget {
id: string;
type: 'metric' | 'chart' | 'table' | 'alert';
title: string;
config: any;
position: { x: number; y: number; w: number; h: number };
}
export interface AnalyticsQueryParams {
startDate?: string;
endDate?: string;
shopId?: string;
platform?: string;
}
export interface AnalyticsData {
metrics: MetricData[];
chartData: ChartData[];
productAnalytics: ProductAnalytics[];
orderAnalytics: OrderAnalytics[];
profitAnalytics: ProfitAnalytics[];
realtimeMetrics: RealtimeMetrics[];
alertRules: AlertRule[];
dashboards: Dashboard[];
}
export interface IAnalyticsDataSource {
fetchAllData(params?: AnalyticsQueryParams): Promise<AnalyticsData>;
fetchMetrics(): Promise<MetricData[]>;
fetchChartData(): Promise<ChartData[]>;
fetchProductAnalytics(): Promise<ProductAnalytics[]>;
fetchOrderAnalytics(): Promise<OrderAnalytics[]>;
fetchProfitAnalytics(): Promise<ProfitAnalytics[]>;
fetchRealtimeData(): Promise<RealtimeMetrics[]>;
fetchAlertRules(): Promise<AlertRule[]>;
fetchDashboards(): Promise<Dashboard[]>;
createAlertRule(data: Partial<AlertRule>): Promise<AlertRule>;
updateAlertRule(id: string, data: Partial<AlertRule>): Promise<AlertRule>;
deleteAlertRule(id: string): Promise<void>;
toggleAlertRule(id: string, enabled: boolean): Promise<void>;
}
class ApiAnalyticsDataSource implements IAnalyticsDataSource {
async fetchAllData(params?: AnalyticsQueryParams): Promise<AnalyticsData> {
const [metrics, chartData, productAnalytics, orderAnalytics, profitAnalytics, realtimeMetrics, alertRules, dashboards] = await Promise.all([
this.fetchMetrics(),
this.fetchChartData(),
this.fetchProductAnalytics(),
this.fetchOrderAnalytics(),
this.fetchProfitAnalytics(),
this.fetchRealtimeData(),
this.fetchAlertRules(),
this.fetchDashboards(),
]);
return { metrics, chartData, productAnalytics, orderAnalytics, profitAnalytics, realtimeMetrics, alertRules, dashboards };
}
async fetchMetrics(): Promise<MetricData[]> {
const response = await http.post('/api/v1/analytics/metrics');
const result = response;
return result.data;
}
async fetchChartData(): Promise<ChartData[]> {
const response = await http.get('/api/v1/analytics/chart');
const result = response;
return result.data;
}
async fetchProductAnalytics(): Promise<ProductAnalytics[]> {
const response = await http.get('/api/v1/analytics/products');
const result = response;
return result.data;
}
async fetchOrderAnalytics(): Promise<OrderAnalytics[]> {
const response = await http.get('/api/v1/analytics/orders');
const result = response;
return result.data;
}
async fetchProfitAnalytics(): Promise<ProfitAnalytics[]> {
const response = await http.get('/api/v1/analytics/profit');
const result = response;
return result.data;
}
async fetchRealtimeData(): Promise<RealtimeMetrics[]> {
const response = await http.get('/api/v1/analytics/realtime');
const result = response;
return result.data;
}
async fetchAlertRules(): Promise<AlertRule[]> {
const response = await http.get('/api/v1/analytics/alerts');
const result = response;
return result.data;
}
async fetchDashboards(): Promise<Dashboard[]> {
const response = await http.get('/api/v1/analytics/dashboards');
const result = response;
return result.data;
}
async createAlertRule(data: Partial<AlertRule>): Promise<AlertRule> {
const response = await http.post('/api/v1/analytics/alerts', data);
return response.data;
}
async updateAlertRule(id: string, data: Partial<AlertRule>): Promise<AlertRule> {
const response = await http.put(`/api/v1/analytics/alerts/${id}`, data);
const result = response;
return result.data;
}
async deleteAlertRule(id: string): Promise<void> {
await fetch(`/api/v1/analytics/alerts/${id}`, { method: 'DELETE' });
}
async toggleAlertRule(id: string, enabled: boolean): Promise<void> {
await http.post(`/api/v1/analytics/alerts/${id}/toggle`, { enabled });
}
}
class MockAnalyticsDataSource implements IAnalyticsDataSource {
readonly __MOCK__ = true as const;
readonly __MOCK_NAME__ = 'MockAnalyticsDataSource';
private mockMetrics: MetricData[] = [
{ key: '1', label: '总销售额', value: 1285000, unit: '¥', change: 12.5, changeType: 'up', icon: 'DollarOutlined', color: '#52c41a' },
{ key: '2', label: '订单数量', value: 3256, unit: '单', change: 8.3, changeType: 'up', icon: 'ShoppingCartOutlined', color: '#1890ff' },
{ key: '3', label: '访客数量', value: 45680, unit: '人', change: -2.1, changeType: 'down', icon: 'TeamOutlined', color: '#722ed1' },
{ key: '4', label: '转化率', value: 7.12, unit: '%', change: 0.5, changeType: 'up', icon: 'FundOutlined', color: '#fa8c16' },
{ key: '5', label: '平均客单价', value: 394.5, unit: '¥', change: 3.8, changeType: 'up', icon: 'BarChartOutlined', color: '#13c2c2' },
{ key: '6', label: '净利润', value: 285600, unit: '¥', change: 15.2, changeType: 'up', icon: 'LineChartOutlined', color: '#eb2f96' },
// Mock 数据生成函数
const generateMockUserActivity = (): UserActivityResponse => {
const users = [
{ userId: 'U001', userName: '张三', loginCount: 25, totalSessionTime: 120, lastLogin: '2026-03-27 14:30:00', activityLevel: 'high' as const },
{ userId: 'U002', userName: '李四', loginCount: 15, totalSessionTime: 80, lastLogin: '2026-03-26 10:15:00', activityLevel: 'medium' as const },
{ userId: 'U003', userName: '王五', loginCount: 8, totalSessionTime: 45, lastLogin: '2026-03-25 09:00:00', activityLevel: 'medium' as const },
{ userId: 'U004', userName: '赵六', loginCount: 3, totalSessionTime: 20, lastLogin: '2026-03-20 16:45:00', activityLevel: 'low' as const },
{ userId: 'U005', userName: '钱七', loginCount: 40, totalSessionTime: 180, lastLogin: '2026-03-27 16:20:00', activityLevel: 'high' as const },
];
private mockAlertRules: AlertRule[] = [
{ id: '1', name: '销售额下降告警', metric: 'daily_revenue', condition: '<', threshold: 30000, enabled: true, notifyChannels: ['email', 'sms'], lastTriggered: '2026-03-17 08:00:00' },
{ id: '2', name: '转化率过低告警', metric: 'conversion_rate', condition: '<', threshold: 5, enabled: true, notifyChannels: ['email'], lastTriggered: '2026-03-16 14:30:00' },
{ id: '3', name: '库存不足告警', metric: 'inventory_level', condition: '<', threshold: 100, enabled: true, notifyChannels: ['email', 'app'], lastTriggered: '2026-03-18 09:00:00' },
{ id: '4', name: '退货率过高告警', metric: 'return_rate', condition: '>', threshold: 10, enabled: false, notifyChannels: ['email'], lastTriggered: undefined },
return {
data: users,
stats: {
totalLogins: users.reduce((sum, user) => sum + user.loginCount, 0),
activeUsers: users.length,
avgSessionTime: users.reduce((sum, user) => sum + user.totalSessionTime, 0) / users.length,
peakHour: 10,
},
};
};
const generateMockTenantUsage = (): TenantUsage => {
return {
shopCount: 8,
maxShops: 10,
userCount: 15,
maxUsers: 20,
storageUsed: 1536,
storageQuota: 2048,
apiCalls: 1200,
maxApiCalls: 2000,
featureUsage: [
{ feature: '商品管理', usageCount: 250, usagePercentage: 85 },
{ feature: '订单管理', usageCount: 200, usagePercentage: 70 },
{ feature: '财务管理', usageCount: 150, usagePercentage: 50 },
{ feature: '数据分析', usageCount: 80, usagePercentage: 30 },
{ feature: '权限管理', usageCount: 50, usagePercentage: 20 },
],
activeDays: 30,
};
};
const generateMockPermissionUsage = (): PermissionUsage[] => {
return [
{ permissionName: 'product:read', usageCount: 500, usageFrequency: 0.9, lastUsed: '2026-03-27 15:00:00', userCount: 10 },
{ permissionName: 'order:read', usageCount: 450, usageFrequency: 0.8, lastUsed: '2026-03-27 14:30:00', userCount: 8 },
{ permissionName: 'product:write', usageCount: 300, usageFrequency: 0.6, lastUsed: '2026-03-27 13:45:00', userCount: 6 },
{ permissionName: 'order:write', usageCount: 250, usageFrequency: 0.5, lastUsed: '2026-03-27 12:30:00', userCount: 5 },
{ permissionName: 'finance:read', usageCount: 150, usageFrequency: 0.3, lastUsed: '2026-03-27 11:15:00', userCount: 3 },
{ permissionName: 'user:manage', usageCount: 100, usageFrequency: 0.2, lastUsed: '2026-03-27 10:00:00', userCount: 2 },
];
};
private mockDashboards: Dashboard[] = [
{ id: '1', name: '默认看板', widgets: [], isDefault: true, createdAt: '2026-01-01' },
{ id: '2', name: '销售分析看板', widgets: [], isDefault: false, createdAt: '2026-02-15' },
{ id: '3', name: '利润监控看板', widgets: [], isDefault: false, createdAt: '2026-03-01' },
const generateMockSecurityAudit = (): SecurityAudit[] => {
return [
{ id: 'A001', timestamp: '2026-03-27 15:30:00', eventType: 'login', userId: 'U001', action: '用户登录', ipAddress: '192.168.1.100', status: 'success' },
{ id: 'A002', timestamp: '2026-03-27 15:25:00', eventType: 'data_access', userId: 'U002', action: '访问订单数据', ipAddress: '192.168.1.101', status: 'success' },
{ id: 'A003', timestamp: '2026-03-27 15:20:00', eventType: 'permission', userId: 'U003', action: '权限变更', ipAddress: '192.168.1.102', status: 'success' },
{ id: 'A004', timestamp: '2026-03-27 15:15:00', eventType: 'error', userId: 'U004', action: '登录失败', ipAddress: '192.168.1.103', status: 'failure', details: '密码错误' },
{ id: 'A005', timestamp: '2026-03-27 15:10:00', eventType: 'logout', userId: 'U005', action: '用户登出', ipAddress: '192.168.1.104', status: 'success' },
];
};
async fetchAllData(params?: AnalyticsQueryParams): Promise<AnalyticsData> {
await this.simulateDelay(300);
const [metrics, chartData, productAnalytics, orderAnalytics, profitAnalytics, realtimeMetrics, alertRules, dashboards] = await Promise.all([
this.fetchMetrics(),
this.fetchChartData(),
this.fetchProductAnalytics(),
this.fetchOrderAnalytics(),
this.fetchProfitAnalytics(),
this.fetchRealtimeData(),
this.fetchAlertRules(),
this.fetchDashboards(),
]);
return { metrics, chartData, productAnalytics, orderAnalytics, profitAnalytics, realtimeMetrics, alertRules, dashboards };
}
async fetchMetrics(): Promise<MetricData[]> {
await this.simulateDelay(200);
return [...this.mockMetrics];
}
async fetchChartData(): Promise<ChartData[]> {
await this.simulateDelay(300);
return Array.from({ length: 30 }, (_, i) => ({
date: `2026-03-${String(i + 1).padStart(2, '0')}`,
sales: Math.floor(Math.random() * 50000) + 30000,
orders: Math.floor(Math.random() * 200) + 100,
visitors: Math.floor(Math.random() * 2000) + 1000,
conversion: parseFloat((Math.random() * 5 + 5).toFixed(2)),
}));
}
async fetchProductAnalytics(): Promise<ProductAnalytics[]> {
await this.simulateDelay(250);
return [
{ id: '1', productId: 'PROD-001', name: 'Wireless Bluetooth Headphones', image: 'https://via.placeholder.com/60', category: 'Electronics', sales: 1250, revenue: 62500, profit: 18750, profitMargin: 30, views: 15000, conversionRate: 8.3, returnRate: 2.1, trend: 'up' },
{ id: '2', productId: 'PROD-002', name: 'Smart Watch Series 5', image: 'https://via.placeholder.com/60', category: 'Electronics', sales: 890, revenue: 178000, profit: 44500, profitMargin: 25, views: 12000, conversionRate: 7.4, returnRate: 3.2, trend: 'up' },
{ id: '3', productId: 'PROD-003', name: 'Portable Charger 20000mAh', image: 'https://via.placeholder.com/60', category: 'Electronics', sales: 2100, revenue: 42000, profit: 12600, profitMargin: 30, views: 25000, conversionRate: 8.4, returnRate: 1.8, trend: 'stable' },
{ id: '4', productId: 'PROD-004', name: 'Laptop Stand Aluminum', image: 'https://via.placeholder.com/60', category: 'Accessories', sales: 650, revenue: 32500, profit: 9750, profitMargin: 30, views: 8000, conversionRate: 8.1, returnRate: 2.5, trend: 'down' },
{ id: '5', productId: 'PROD-005', name: 'USB-C Hub 7-in-1', image: 'https://via.placeholder.com/60', category: 'Accessories', sales: 980, revenue: 49000, profit: 14700, profitMargin: 30, views: 11000, conversionRate: 8.9, returnRate: 2.0, trend: 'up' },
];
}
async fetchOrderAnalytics(): Promise<OrderAnalytics[]> {
await this.simulateDelay(200);
return [
{ id: '1', orderId: 'ORD-2026-001', platform: 'AMAZON', status: 'COMPLETED', amount: 299.99, profit: 89.99, createdAt: '2026-03-18 10:30:00', customerType: 'returning', shippingCost: 15, platformFee: 45 },
{ id: '2', orderId: 'ORD-2026-002', platform: 'EBAY', status: 'COMPLETED', amount: 159.99, profit: 47.99, createdAt: '2026-03-18 09:15:00', customerType: 'new', shippingCost: 12, platformFee: 24 },
{ id: '3', orderId: 'ORD-2026-003', platform: 'SHOPIFY', status: 'SHIPPED', amount: 499.99, profit: 149.99, createdAt: '2026-03-18 08:45:00', customerType: 'returning', shippingCost: 20, platformFee: 0 },
{ id: '4', orderId: 'ORD-2026-004', platform: 'SHOPEE', status: 'PENDING', amount: 89.99, profit: 26.99, createdAt: '2026-03-18 08:00:00', customerType: 'new', shippingCost: 8, platformFee: 13.5 },
{ id: '5', orderId: 'ORD-2026-005', platform: 'LAZADA', status: 'COMPLETED', amount: 199.99, profit: 59.99, createdAt: '2026-03-17 16:30:00', customerType: 'returning', shippingCost: 15, platformFee: 30 },
];
}
async fetchProfitAnalytics(): Promise<ProfitAnalytics[]> {
await this.simulateDelay(200);
return [
{ id: '1', period: '2026-03-18', revenue: 45000, cost: 28000, grossProfit: 17000, netProfit: 12500, margin: 27.8, platformFees: 2500, shippingCost: 1500, adSpend: 800, otherCosts: 200 },
{ id: '2', period: '2026-03-17', revenue: 52000, cost: 32000, grossProfit: 20000, netProfit: 14800, margin: 28.5, platformFees: 2900, shippingCost: 1800, adSpend: 950, otherCosts: 250 },
{ id: '3', period: '2026-03-16', revenue: 48000, cost: 30000, grossProfit: 18000, netProfit: 13200, margin: 27.5, platformFees: 2700, shippingCost: 1650, adSpend: 900, otherCosts: 220 },
{ id: '4', period: '2026-03-15', revenue: 55000, cost: 34000, grossProfit: 21000, netProfit: 15500, margin: 28.2, platformFees: 3100, shippingCost: 1900, adSpend: 1000, otherCosts: 280 },
{ id: '5', period: '2026-03-14', revenue: 42000, cost: 26000, grossProfit: 16000, netProfit: 11800, margin: 28.1, platformFees: 2350, shippingCost: 1400, adSpend: 750, otherCosts: 180 },
];
}
async fetchRealtimeData(): Promise<RealtimeMetrics[]> {
await this.simulateDelay(100);
return Array.from({ length: 10 }, (_, i) => ({
timestamp: new Date(Date.now() - i * 60000).toISOString(),
activeUsers: Math.floor(Math.random() * 500) + 200,
ordersPerMinute: Math.floor(Math.random() * 20) + 5,
revenuePerMinute: Math.floor(Math.random() * 2000) + 500,
conversionRate: parseFloat((Math.random() * 3 + 4).toFixed(2)),
}));
}
async fetchAlertRules(): Promise<AlertRule[]> {
await this.simulateDelay(150);
return [...this.mockAlertRules];
}
async fetchDashboards(): Promise<Dashboard[]> {
await this.simulateDelay(100);
return [...this.mockDashboards];
}
async createAlertRule(data: Partial<AlertRule>): Promise<AlertRule> {
await this.simulateDelay(300);
const newRule: AlertRule = {
id: `${Date.now()}`,
name: data.name || '',
metric: data.metric || '',
condition: data.condition || '<',
threshold: data.threshold || 0,
enabled: data.enabled ?? true,
notifyChannels: data.notifyChannels || [],
};
this.mockAlertRules.push(newRule);
return newRule;
}
async updateAlertRule(id: string, data: Partial<AlertRule>): Promise<AlertRule> {
await this.simulateDelay(200);
const index = this.mockAlertRules.findIndex(r => r.id === id);
if (index === -1) throw new Error('Alert rule not found');
this.mockAlertRules[index] = { ...this.mockAlertRules[index], ...data };
return this.mockAlertRules[index];
}
async deleteAlertRule(id: string): Promise<void> {
await this.simulateDelay(150);
this.mockAlertRules = this.mockAlertRules.filter(r => r.id !== id);
}
async toggleAlertRule(id: string, enabled: boolean): Promise<void> {
await this.simulateDelay(100);
const index = this.mockAlertRules.findIndex(r => r.id === id);
if (index !== -1) {
this.mockAlertRules[index].enabled = enabled;
// 数据源服务
export const analyticsDataSource = {
/**
* 获取用户活跃度数据
*/
async getUserActivity(params: { tenantId?: string; dateRange?: [string, string] }): Promise<UserActivityResponse> {
try {
// 模拟API调用延迟
await new Promise(resolve => setTimeout(resolve, 500));
return generateMockUserActivity();
} catch (error: any) {
message.error(error.message || '获取用户活跃度数据失败');
return {
data: [],
stats: {
totalLogins: 0,
activeUsers: 0,
avgSessionTime: 0,
peakHour: 0,
},
};
}
}
},
private simulateDelay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
/**
* 获取租户使用情况
*/
async getTenantUsage(params: { tenantId?: string; dateRange?: [string, string] }): Promise<TenantUsage> {
try {
// 模拟API调用延迟
await new Promise(resolve => setTimeout(resolve, 500));
return generateMockTenantUsage();
} catch (error: any) {
message.error(error.message || '获取租户使用情况失败');
return {
shopCount: 0,
maxShops: 0,
userCount: 0,
maxUsers: 0,
storageUsed: 0,
storageQuota: 0,
apiCalls: 0,
maxApiCalls: 0,
featureUsage: [],
activeDays: 0,
};
}
},
const useMock = process.env.NODE_ENV === 'development' || process.env.REACT_APP_USE_MOCK === 'true';
/**
* 获取权限使用情况
*/
async getPermissionUsage(params: { tenantId?: string; dateRange?: [string, string] }): Promise<PermissionUsage[]> {
try {
// 模拟API调用延迟
await new Promise(resolve => setTimeout(resolve, 500));
return generateMockPermissionUsage();
} catch (error: any) {
message.error(error.message || '获取权限使用情况失败');
return [];
}
},
export const analyticsDataSource: IAnalyticsDataSource = useMock
? new MockAnalyticsDataSource()
: new ApiAnalyticsDataSource();
/**
* 获取安全审计日志
*/
async getSecurityAudit(params: { tenantId?: string; dateRange?: [string, string] }): Promise<SecurityAudit[]> {
try {
// 模拟API调用延迟
await new Promise(resolve => setTimeout(resolve, 500));
return generateMockSecurityAudit();
} catch (error: any) {
message.error(error.message || '获取安全审计日志失败');
return [];
}
},
export const __MOCK__ = useMock;
export const __DATA_SOURCE_TYPE__ = useMock ? 'mock' : 'api';
/**
* 导出分析数据
*/
async exportAnalytics(params: { type: string; tenantId?: string; dateRange?: [string, string] }): Promise<void> {
try {
// 模拟导出操作
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('Exporting analytics data:', params);
} catch (error: any) {
throw new Error(error.message || '导出分析数据失败');
}
},
};

View File

@@ -1,4 +1,5 @@
import { http } from './http';
import { DataSourceFactory } from './baseDataSource';
import { PlatformAccount, PlatformAccountStats } from '@/types/platform';
export interface PlatformAuthDataSource {
@@ -57,9 +58,9 @@ export class PlatformAuthMockDataSource implements PlatformAuthDataSource {
id: '1',
tenantId: 'tenant_1',
platform: 'AMAZON',
accountName: 'My Amazon Store',
accountId: 'amazon_123',
status: 'ACTIVE',
accountName: 'Amazon美国主店',
accountId: 'amazon_us_001',
status: 'active',
lastSyncAt: '2024-01-15T10:30:00Z',
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-15T10:30:00Z',
@@ -68,9 +69,9 @@ export class PlatformAuthMockDataSource implements PlatformAuthDataSource {
id: '2',
tenantId: 'tenant_1',
platform: 'SHOPIFY',
accountName: 'my-shop.myshopify.com',
accountName: 'my-brand.myshopify.com',
accountId: 'shopify_456',
status: 'ACTIVE',
status: 'active',
lastSyncAt: '2024-01-15T09:15:00Z',
createdAt: '2024-01-01T00:00:00Z',
updatedAt: '2024-01-15T09:15:00Z',
@@ -79,9 +80,9 @@ export class PlatformAuthMockDataSource implements PlatformAuthDataSource {
id: '3',
tenantId: 'tenant_1',
platform: 'SHOPEE',
accountName: 'My Shopee Shop',
accountId: 'shopee_789',
status: 'EXPIRED',
accountName: 'Shopee新加坡旗舰店',
accountId: 'shopee_sg_789',
status: 'expired',
lastSyncAt: '2023-12-01T08:00:00Z',
createdAt: '2023-06-01T00:00:00Z',
updatedAt: '2024-01-01T08:00:00Z',
@@ -90,12 +91,99 @@ export class PlatformAuthMockDataSource implements PlatformAuthDataSource {
id: '4',
tenantId: 'tenant_1',
platform: 'EBAY',
accountName: 'My eBay Store',
accountId: 'ebay_101',
status: 'INACTIVE',
accountName: 'eBay全球店',
accountId: 'ebay_global_101',
status: 'inactive',
createdAt: '2024-01-10T00:00:00Z',
updatedAt: '2024-01-10T00:00:00Z',
},
{
id: '5',
tenantId: 'tenant_1',
platform: 'TIKTOK',
accountName: 'TikTok Shop官方店',
accountId: 'tiktok_official_202',
status: 'active',
lastSyncAt: '2024-01-15T14:20:00Z',
createdAt: '2023-12-15T00:00:00Z',
updatedAt: '2024-01-15T14:20:00Z',
},
{
id: '6',
tenantId: 'tenant_1',
platform: 'LAZADA',
accountName: 'Lazada泰国店',
accountId: 'lazada_th_303',
status: 'active',
lastSyncAt: '2024-01-14T16:45:00Z',
createdAt: '2023-11-20T00:00:00Z',
updatedAt: '2024-01-14T16:45:00Z',
},
{
id: '7',
tenantId: 'tenant_1',
platform: 'TEMU_FULL',
accountName: 'Temu全托管旗舰店',
accountId: 'temu_full_404',
status: 'active',
lastSyncAt: '2024-01-15T11:00:00Z',
createdAt: '2023-10-01T00:00:00Z',
updatedAt: '2024-01-15T11:00:00Z',
},
{
id: '8',
tenantId: 'tenant_1',
platform: 'SHEIN',
accountName: 'SHEIN官方店',
accountId: 'shein_official_505',
status: 'error',
lastSyncAt: '2024-01-10T09:30:00Z',
createdAt: '2023-09-15T00:00:00Z',
updatedAt: '2024-01-12T10:00:00Z',
},
{
id: '9',
tenantId: 'tenant_1',
platform: 'ALIEXPRESS',
accountName: '速卖通俄罗斯店',
accountId: 'aliexpress_ru_606',
status: 'active',
lastSyncAt: '2024-01-15T08:20:00Z',
createdAt: '2023-08-01T00:00:00Z',
updatedAt: '2024-01-15T08:20:00Z',
},
{
id: '10',
tenantId: 'tenant_1',
platform: 'WALMART',
accountName: 'Walmart美国店',
accountId: 'walmart_us_707',
status: 'inactive',
createdAt: '2024-01-05T00:00:00Z',
updatedAt: '2024-01-05T00:00:00Z',
},
{
id: '11',
tenantId: 'tenant_1',
platform: 'OZON',
accountName: 'Ozon俄罗斯店',
accountId: 'ozon_ru_808',
status: 'active',
lastSyncAt: '2024-01-15T07:15:00Z',
createdAt: '2023-07-01T00:00:00Z',
updatedAt: '2024-01-15T07:15:00Z',
},
{
id: '12',
tenantId: 'tenant_1',
platform: 'COUPANG',
accountName: 'Coupang韩国店',
accountId: 'coupang_kr_909',
status: 'expired',
lastSyncAt: '2023-11-15T06:00:00Z',
createdAt: '2023-05-01T00:00:00Z',
updatedAt: '2023-12-01T00:00:00Z',
},
];
async list(filters?: { platform?: string; status?: string; shopId?: string }): Promise<PlatformAccount[]> {
@@ -120,7 +208,7 @@ export class PlatformAuthMockDataSource implements PlatformAuthDataSource {
platform: data.platform,
accountName: data.accountName,
accountId: data.accountId,
status: 'INACTIVE',
status: 'inactive',
shopId: data.shopId,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
@@ -135,7 +223,7 @@ export class PlatformAuthMockDataSource implements PlatformAuthDataSource {
async refresh(id: string): Promise<{ success: boolean; message: string }> {
const account = this.mockAccounts.find(a => a.id === id);
if (account) {
account.status = 'ACTIVE';
account.status = 'active';
account.lastSyncAt = new Date().toISOString();
}
return { success: true, message: 'Token refreshed successfully' };
@@ -144,7 +232,7 @@ export class PlatformAuthMockDataSource implements PlatformAuthDataSource {
async disconnect(id: string): Promise<void> {
const account = this.mockAccounts.find(a => a.id === id);
if (account) {
account.status = 'INACTIVE';
account.status = 'inactive';
}
}
@@ -155,14 +243,16 @@ export class PlatformAuthMockDataSource implements PlatformAuthDataSource {
async getStats(): Promise<PlatformAccountStats> {
return {
total: this.mockAccounts.length,
active: this.mockAccounts.filter(a => a.status === 'ACTIVE').length,
inactive: this.mockAccounts.filter(a => a.status === 'INACTIVE').length,
expired: this.mockAccounts.filter(a => a.status === 'EXPIRED').length,
error: this.mockAccounts.filter(a => a.status === 'ERROR').length,
active: this.mockAccounts.filter(a => a.status === 'active').length,
inactive: this.mockAccounts.filter(a => a.status === 'inactive').length,
expired: this.mockAccounts.filter(a => a.status === 'expired').length,
error: this.mockAccounts.filter(a => a.status === 'error').length,
};
}
}
export const platformAuthDataSource = process.env.REACT_APP_USE_MOCK === 'true'
export const platformAuthDataSource = process.env.REACT_APP_USE_MOCK !== 'false'
? new PlatformAuthMockDataSource()
: new PlatformAuthApiDataSource();
DataSourceFactory.register('platformAuth', platformAuthDataSource);

View File

@@ -229,7 +229,7 @@ const MOCK_PRODUCTS: Product[] = [
platformStatus: { Amazon: 'FAILED' },
shopId: 'shop-temu-1',
shopName: 'Temu旗舰店',
platform: 'Temu',
platform: 'TemuFull',
createdAt: '2026-03-05',
updatedAt: '2026-03-18',
},
@@ -252,8 +252,161 @@ const MOCK_PRODUCTS: Product[] = [
createdAt: '2025-11-20',
updatedAt: '2026-02-28',
},
{
id: '8',
sku: 'TP-LEVEL-008',
name: '液位传感器',
image: 'https://via.placeholder.com/80x80?text=Product',
category: '工业自动化',
price: 69.99,
costPrice: 35.00,
profit: 34.99,
roi: 99.97,
stock: 150,
status: 'LIVE',
platformStatus: { Amazon: 'LIVE', TikTok: 'LIVE' },
shopId: 'shop-amazon-3',
shopName: 'Amazon日本店',
platform: 'Amazon',
createdAt: '2026-03-08',
updatedAt: '2026-03-18',
},
{
id: '9',
sku: 'TP-RELAY-009',
name: '继电器模块',
image: 'https://via.placeholder.com/80x80?text=Product',
category: '电子元器件',
price: 29.99,
costPrice: 15.00,
profit: 14.99,
roi: 99.93,
stock: 1000,
status: 'LIVE',
platformStatus: { Shopee: 'LIVE', Lazada: 'LIVE' },
shopId: 'shop-shopee-2',
shopName: 'Shopee官方店B',
platform: 'Shopee',
createdAt: '2026-03-12',
updatedAt: '2026-03-17',
},
{
id: '10',
sku: 'TP-FUSE-010',
name: '熔断器',
image: 'https://via.placeholder.com/80x80?text=Product',
category: '电子元器件',
price: 19.99,
costPrice: 10.00,
profit: 9.99,
roi: 99.90,
stock: 2000,
status: 'LIVE',
platformStatus: { eBay: 'LIVE', AliExpress: 'LIVE' },
shopId: 'shop-ebay-2',
shopName: 'eBay全球店B',
platform: 'eBay',
createdAt: '2026-03-05',
updatedAt: '2026-03-16',
},
{
id: '11',
sku: 'TP-METER-011',
name: '万用表 Digital',
image: 'https://via.placeholder.com/80x80?text=Product',
category: '仪器仪表',
price: 149.99,
costPrice: 75.00,
profit: 74.99,
roi: 99.99,
stock: 80,
status: 'PRICED',
platformStatus: {},
shopId: 'shop-lazada-1',
shopName: 'Lazada官方店A',
platform: 'Lazada',
createdAt: '2026-03-14',
updatedAt: '2026-03-15',
},
{
id: '12',
sku: 'TP-TOOL-012',
name: '电动螺丝刀套装',
image: 'https://via.placeholder.com/80x80?text=Product',
category: '工具设备',
price: 89.99,
costPrice: 45.00,
profit: 44.99,
roi: 99.98,
stock: 120,
status: 'LISTED',
platformStatus: { Amazon: 'LISTED' },
shopId: 'shop-amazon-4',
shopName: 'Amazon欧洲店D',
platform: 'Amazon',
createdAt: '2026-03-03',
updatedAt: '2026-03-17',
},
{
id: '13',
sku: 'TP-CAM-013',
name: '安防摄像头',
image: 'https://via.placeholder.com/80x80?text=Product',
category: '安防设备',
price: 199.99,
costPrice: 100.00,
profit: 99.99,
roi: 99.99,
stock: 50,
status: 'DRAFT',
platformStatus: {},
shopId: 'shop-tiktok-2',
shopName: 'TikTok旗舰店B',
platform: 'TikTok',
createdAt: '2026-03-16',
updatedAt: '2026-03-16',
},
{
id: '14',
sku: 'TP-ALARM-014',
name: '烟雾报警器',
image: 'https://via.placeholder.com/80x80?text=Product',
category: '安防设备',
price: 49.99,
costPrice: 25.00,
profit: 24.99,
roi: 99.96,
stock: 180,
status: 'LIVE',
platformStatus: { Shopee: 'LIVE', TemuFull: 'LIVE' },
shopId: 'shop-temu-2',
shopName: 'Temu旗舰店B',
platform: 'TemuFull',
createdAt: '2026-03-07',
updatedAt: '2026-03-18',
},
{
id: '15',
sku: 'TP-SENSOR-015',
name: '湿度传感器',
image: 'https://via.placeholder.com/80x80?text=Product',
category: '工业自动化',
price: 59.99,
costPrice: 30.00,
profit: 29.99,
roi: 99.97,
stock: 200,
status: 'SYNCING',
platformStatus: { Amazon: 'SYNCING' },
shopId: 'shop-amazon-5',
shopName: 'Amazon日本店E',
platform: 'Amazon',
createdAt: '2026-03-17',
updatedAt: '2026-03-18',
},
];
export class ProductDataSourceImpl extends BaseDataSource implements ProductDataSource {
private products: Product[] = MOCK_PRODUCTS;
@@ -306,6 +459,10 @@ export class ProductDataSourceImpl extends BaseDataSource implements ProductData
if (filter.minStock !== undefined) {
filtered = filtered.filter(product => product.stock >= filter.minStock!);
}
if (filter.platform && filter.platform.length > 0) {
filtered = filtered.filter(product => filter.platform!.includes(product.platform));
}
}
// 应用排序
@@ -457,6 +614,13 @@ export class ProductDataSourceImpl extends BaseDataSource implements ProductData
});
this.products.forEach(product => {
// 统计基于商品的 platform 字段
const productPlatform = product.platform;
if (stats[productPlatform]) {
stats[productPlatform].total++;
}
// 统计状态
const platforms = Object.keys(product.platformStatus);
if (platforms.length === 0) {
stats.unpublished.total++;
@@ -465,16 +629,21 @@ export class ProductDataSourceImpl extends BaseDataSource implements ProductData
if (!stats[platform]) {
stats[platform] = { total: 0, live: 0, pending: 0, failed: 0 };
}
stats[platform].total++;
const status = product.platformStatus[platform];
if (status === 'LIVE') {
stats[platform].live++;
if (platform === productPlatform) {
stats[platform].live++;
}
stats.all.live++;
} else if (status === 'PENDING' || status === 'SYNCING' || status === 'LISTED') {
stats[platform].pending++;
if (platform === productPlatform) {
stats[platform].pending++;
}
stats.all.pending++;
} else if (status === 'FAILED' || status === 'SYNC_FAILED') {
stats[platform].failed++;
if (platform === productPlatform) {
stats[platform].failed++;
}
stats.all.failed++;
}
});

View File

@@ -0,0 +1,166 @@
import { http } from '@/services/http';
/**
* 角色管理数据源
*/
export interface Role {
code: string;
name: string;
permissions: string[];
}
export interface Permission {
id: string;
name: string;
description: string;
module: string;
action: 'READ' | 'WRITE' | 'DELETE' | 'EXECUTE';
}
/**
* 创建角色
*/
export const createRole = async (
roleCode: string,
roleName: string,
permissions: string[]
): Promise<boolean> => {
try {
const response = await http.post('/api/roles', {
roleCode,
roleName,
permissions,
});
return response.data.success;
} catch (error) {
console.error('Failed to create role:', error);
throw error;
}
};
/**
* 更新角色
*/
export const updateRole = async (
roleCode: string,
roleName: string,
permissions: string[]
): Promise<boolean> => {
try {
const response = await http.put(`/api/roles/${roleCode}`, {
roleName,
permissions,
});
return response.data.success;
} catch (error) {
console.error('Failed to update role:', error);
throw error;
}
};
/**
* 删除角色
*/
export const deleteRole = async (roleCode: string): Promise<boolean> => {
try {
const response = await http.delete(`/api/roles/${roleCode}`);
return response.data.success;
} catch (error) {
console.error('Failed to delete role:', error);
throw error;
}
};
/**
* 获取角色详情
*/
export const getRole = async (roleCode: string): Promise<Role> => {
try {
const response = await http.get(`/api/roles/${roleCode}`);
return response.data.role;
} catch (error) {
console.error('Failed to get role:', error);
throw error;
}
};
/**
* 获取所有角色
*/
export const getAllRoles = async (): Promise<Role[]> => {
try {
const response = await http.get('/api/roles');
return response.data.roles;
} catch (error) {
console.error('Failed to get roles:', error);
// 模拟数据
return [
{
code: 'ADMIN',
name: '管理员',
permissions: ['admin:all'],
},
{
code: 'MANAGER',
name: '运营主管',
permissions: [
'product:read', 'product:write', 'product:publish', 'product:score',
'order:read', 'order:write', 'order:cancel',
'inventory:read', 'inventory:write', 'inventory:sync', 'inventory:alert',
'finance:read', 'finance:report',
'ad:read', 'ad:write', 'ad:launch', 'ad:optimize',
'sourcing:read', 'sourcing:write',
'logistics:read', 'logistics:track',
'ai:read', 'ai:scoring', 'ai:arbitrage', 'ai:pricing', 'ai:monitor',
'collection:read', 'collection:write', 'collection:execute',
'user:read',
'audit:read'
],
},
{
code: 'OPERATOR',
name: '运营专员',
permissions: [
'product:read', 'product:write', 'product:publish',
'order:read', 'order:write',
'inventory:read', 'inventory:sync',
'ad:read', 'ad:write',
'sourcing:read',
'logistics:read', 'logistics:track',
'ai:read', 'ai:scoring',
'collection:read', 'collection:execute'
],
},
];
}
};
/**
* 获取权限定义
*/
export const getPermissionDefinitions = async (): Promise<Permission[]> => {
try {
const response = await http.get('/api/permissions');
return response.data.permissions;
} catch (error) {
console.error('Failed to get permissions:', error);
// 模拟数据
return [
{ id: 'admin:all', name: '超级管理员', description: '拥有所有系统权限', module: 'SYSTEM', action: 'EXECUTE' },
{ id: 'product:read', name: '查看商品', description: '允许查看商品列表与详情', module: 'PRODUCT', action: 'READ' },
{ id: 'product:write', name: '编辑商品', description: '允许创建和修改商品信息', module: 'PRODUCT', action: 'WRITE' },
{ id: 'product:delete', name: '删除商品', description: '允许删除商品', module: 'PRODUCT', action: 'DELETE' },
{ id: 'product:publish', name: '发布商品', description: '允许执行商品刊登任务', module: 'PRODUCT', action: 'EXECUTE' },
{ id: 'order:read', name: '查看订单', description: '允许查看销售订单', module: 'ORDER', action: 'READ' },
{ id: 'order:write', name: '创建订单', description: '允许创建订单', module: 'ORDER', action: 'WRITE' },
{ id: 'order:cancel', name: '取消订单', description: '允许取消订单', module: 'ORDER', action: 'EXECUTE' },
{ id: 'inventory:read', name: '查看库存', description: '允许查看库存数据', module: 'INVENTORY', action: 'READ' },
{ id: 'inventory:write', name: '编辑库存', description: '允许调整库存', module: 'INVENTORY', action: 'WRITE' },
{ id: 'finance:read', name: '查看财务', description: '允许查看财务数据', module: 'FINANCE', action: 'READ' },
{ id: 'finance:report', name: '财务报表', description: '允许查看财务报表', module: 'FINANCE', action: 'READ' },
{ id: 'user:read', name: '查看用户', description: '允许查看用户列表', module: 'USER', action: 'READ' },
{ id: 'user:write', name: '编辑用户', description: '允许创建和修改用户', module: 'USER', action: 'WRITE' },
{ id: 'audit:read', name: '查看审计', description: '允许查看审计日志', module: 'AUDIT', action: 'READ' },
];
}
};