132 lines
5.7 KiB
TypeScript
132 lines
5.7 KiB
TypeScript
|
|
/**
|
||
|
|
* [MOCK-INVENTORY] Inventory模块DataSource
|
||
|
|
*/
|
||
|
|
|
||
|
|
export interface InventoryItem {
|
||
|
|
id: string;
|
||
|
|
sku: string;
|
||
|
|
productName: string;
|
||
|
|
quantity: number;
|
||
|
|
availableQty: number;
|
||
|
|
reservedQty: number;
|
||
|
|
warehouseId: string;
|
||
|
|
warehouseName: string;
|
||
|
|
status: 'in_stock' | 'low_stock' | 'out_of_stock';
|
||
|
|
reorderPoint: number;
|
||
|
|
lastRestocked: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface Warehouse {
|
||
|
|
id: string;
|
||
|
|
name: string;
|
||
|
|
location: string;
|
||
|
|
capacity: number;
|
||
|
|
usedCapacity: number;
|
||
|
|
status: 'active' | 'inactive' | 'maintenance';
|
||
|
|
contact: string;
|
||
|
|
phone: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface InventoryForecast {
|
||
|
|
sku: string;
|
||
|
|
productName: string;
|
||
|
|
currentStock: number;
|
||
|
|
avgDailySales: number;
|
||
|
|
daysOfStock: number;
|
||
|
|
suggestedReorderQty: number;
|
||
|
|
estimatedStockoutDate: string;
|
||
|
|
confidence: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface IInventoryDataSource {
|
||
|
|
fetchInventory(params?: { warehouseId?: string; status?: string }): Promise<InventoryItem[]>;
|
||
|
|
updateInventory(id: string, data: Partial<InventoryItem>): Promise<InventoryItem>;
|
||
|
|
|
||
|
|
fetchWarehouses(): Promise<Warehouse[]>;
|
||
|
|
saveWarehouse(data: Partial<Warehouse>): Promise<Warehouse>;
|
||
|
|
deleteWarehouse(id: string): Promise<void>;
|
||
|
|
|
||
|
|
fetchForecast(): Promise<InventoryForecast[]>;
|
||
|
|
adjustStock(id: string, quantity: number, reason: string): Promise<void>;
|
||
|
|
}
|
||
|
|
|
||
|
|
class MockInventoryDataSource implements IInventoryDataSource {
|
||
|
|
async fetchInventory(params?: { warehouseId?: string; status?: string }): Promise<InventoryItem[]> {
|
||
|
|
return [
|
||
|
|
{ id: 'inv_001', sku: 'SKU-001', productName: 'Wireless Headphones', quantity: 500, availableQty: 450, reservedQty: 50, warehouseId: 'wh_001', warehouseName: 'US East', status: 'in_stock', reorderPoint: 100, lastRestocked: '2026-03-15' },
|
||
|
|
{ id: 'inv_002', sku: 'SKU-002', productName: 'USB-C Cable', quantity: 80, availableQty: 75, reservedQty: 5, warehouseId: 'wh_001', warehouseName: 'US East', status: 'low_stock', reorderPoint: 100, lastRestocked: '2026-03-10' },
|
||
|
|
{ id: 'inv_003', sku: 'SKU-003', productName: 'Phone Case', quantity: 0, availableQty: 0, reservedQty: 0, warehouseId: 'wh_002', warehouseName: 'US West', status: 'out_of_stock', reorderPoint: 50, lastRestocked: '2026-03-01' },
|
||
|
|
{ id: 'inv_004', sku: 'SKU-004', productName: 'Smart Watch', quantity: 200, availableQty: 180, reservedQty: 20, warehouseId: 'wh_002', warehouseName: 'US West', status: 'in_stock', reorderPoint: 50, lastRestocked: '2026-03-12' },
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
async updateInventory(id: string, data: Partial<InventoryItem>): Promise<InventoryItem> {
|
||
|
|
return { id, ...data } as InventoryItem;
|
||
|
|
}
|
||
|
|
|
||
|
|
async fetchWarehouses(): Promise<Warehouse[]> {
|
||
|
|
return [
|
||
|
|
{ id: 'wh_001', name: 'US East Warehouse', location: 'New Jersey, USA', capacity: 10000, usedCapacity: 6500, status: 'active', contact: 'John Smith', phone: '+1-555-0100' },
|
||
|
|
{ id: 'wh_002', name: 'US West Warehouse', location: 'California, USA', capacity: 8000, usedCapacity: 4200, status: 'active', contact: 'Jane Doe', phone: '+1-555-0200' },
|
||
|
|
{ id: 'wh_003', name: 'EU Warehouse', location: 'Frankfurt, Germany', capacity: 5000, usedCapacity: 2800, status: 'active', contact: 'Hans Mueller', phone: '+49-555-0300' },
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
async saveWarehouse(data: Partial<Warehouse>): Promise<Warehouse> {
|
||
|
|
return { ...data, id: data.id || `wh_${Date.now()}` } as Warehouse;
|
||
|
|
}
|
||
|
|
|
||
|
|
async deleteWarehouse(id: string): Promise<void> {}
|
||
|
|
|
||
|
|
async fetchForecast(): Promise<InventoryForecast[]> {
|
||
|
|
return [
|
||
|
|
{ sku: 'SKU-001', productName: 'Wireless Headphones', currentStock: 500, avgDailySales: 25, daysOfStock: 20, suggestedReorderQty: 750, estimatedStockoutDate: '2026-04-07', confidence: 0.92 },
|
||
|
|
{ sku: 'SKU-002', productName: 'USB-C Cable', currentStock: 80, avgDailySales: 15, daysOfStock: 5.3, suggestedReorderQty: 450, estimatedStockoutDate: '2026-03-23', confidence: 0.88 },
|
||
|
|
{ sku: 'SKU-004', productName: 'Smart Watch', currentStock: 200, avgDailySales: 8, daysOfStock: 25, suggestedReorderQty: 240, estimatedStockoutDate: '2026-04-12', confidence: 0.95 },
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
async adjustStock(id: string, quantity: number, reason: string): Promise<void> {}
|
||
|
|
}
|
||
|
|
|
||
|
|
class ApiInventoryDataSource implements IInventoryDataSource {
|
||
|
|
private baseUrl = '/api/inventory';
|
||
|
|
|
||
|
|
async fetchInventory(params?: { warehouseId?: string; status?: string }): Promise<InventoryItem[]> {
|
||
|
|
const query = new URLSearchParams(params as any).toString();
|
||
|
|
const res = await fetch(`${this.baseUrl}?${query}`);
|
||
|
|
return res.json();
|
||
|
|
}
|
||
|
|
|
||
|
|
async updateInventory(id: string, data: Partial<InventoryItem>): Promise<InventoryItem> {
|
||
|
|
const res = await fetch(`${this.baseUrl}/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) });
|
||
|
|
return res.json();
|
||
|
|
}
|
||
|
|
|
||
|
|
async fetchWarehouses(): Promise<Warehouse[]> {
|
||
|
|
const res = await fetch(`${this.baseUrl}/warehouses`);
|
||
|
|
return res.json();
|
||
|
|
}
|
||
|
|
|
||
|
|
async saveWarehouse(data: Partial<Warehouse>): Promise<Warehouse> {
|
||
|
|
const res = await fetch(`${this.baseUrl}/warehouses`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) });
|
||
|
|
return res.json();
|
||
|
|
}
|
||
|
|
|
||
|
|
async deleteWarehouse(id: string): Promise<void> {
|
||
|
|
await fetch(`${this.baseUrl}/warehouses/${id}`, { method: 'DELETE' });
|
||
|
|
}
|
||
|
|
|
||
|
|
async fetchForecast(): Promise<InventoryForecast[]> {
|
||
|
|
const res = await fetch(`${this.baseUrl}/forecast`);
|
||
|
|
return res.json();
|
||
|
|
}
|
||
|
|
|
||
|
|
async adjustStock(id: string, quantity: number, reason: string): Promise<void> {
|
||
|
|
await fetch(`${this.baseUrl}/${id}/adjust`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ quantity, reason }) });
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const useMock = process.env.REACT_APP_USE_MOCK === 'true';
|
||
|
|
export const inventoryDataSource: IInventoryDataSource = useMock ? new MockInventoryDataSource() : new ApiInventoryDataSource();
|