Files
makemd/dashboard/src/services/inventoryDataSource.ts

552 lines
16 KiB
TypeScript
Raw Normal View History

/**
* [MOCK-010] DataSource
* AI注意: 这是Mock实现
* USE_MOCK=true时启用
*/
import { Inventory, InventoryStatus } from '../types/inventory';
export interface Warehouse {
id: string;
name: string;
code: string;
address: string;
city: string;
country: string;
capacity: number;
currentUsage: number;
status: 'active' | 'inactive' | 'maintenance';
manager: string;
phone: string;
email: string;
createdAt: string;
updatedAt: string;
}
export interface InventoryForecast {
id: string;
productId: string;
productName: string;
sku: string;
currentStock: number;
forecastedDemand: number;
recommendedOrder: number;
leadTime: number;
riskLevel: 'low' | 'medium' | 'high';
date: string;
}
export interface InventoryQueryParams {
productId?: string;
status?: InventoryStatus;
warehouseId?: string;
page?: number;
pageSize?: number;
}
export interface InventoryDataSource {
list(params?: InventoryQueryParams): Promise<{ data: Inventory[]; total: number }>;
get(id: string): Promise<Inventory | null>;
create(inventory: Omit<Inventory, 'id' | 'createdAt' | 'updatedAt'>): Promise<Inventory>;
update(id: string, inventory: Partial<Inventory>): Promise<Inventory | null>;
updateStock(id: string, quantity: number): Promise<Inventory | null>;
getByProductId(productId: string): Promise<Inventory[]>;
getByWarehouseId(warehouseId: string): Promise<Inventory[]>;
getLowStockAlerts(threshold: number): Promise<Inventory[]>;
getInventoryStatistics(): Promise<{
totalItems: number;
totalStock: number;
lowStockItems: number;
outOfStockItems: number;
warehouseDistribution: Record<string, number>;
}>;
fetchWarehouses(): Promise<Warehouse[]>;
getInventoryForecasts(): Promise<InventoryForecast[]>;
}
export class InventoryDataSourceImpl implements InventoryDataSource {
async list(params?: InventoryQueryParams): Promise<{ data: Inventory[]; total: number }> {
const mockInventory: Inventory[] = [
{
id: 'inv_1',
productId: '1',
productName: '智能手表',
quantity: 100,
threshold: 20,
status: InventoryStatus.IN_STOCK,
warehouseId: 'warehouse_1',
warehouseName: '北京仓库',
lastStockUpdate: new Date('2026-03-10'),
createdAt: new Date('2026-02-01'),
updatedAt: new Date('2026-03-10'),
},
{
id: 'inv_2',
productId: '2',
productName: '无线耳机',
quantity: 200,
threshold: 30,
status: InventoryStatus.IN_STOCK,
warehouseId: 'warehouse_1',
warehouseName: '北京仓库',
lastStockUpdate: new Date('2026-03-11'),
createdAt: new Date('2026-02-02'),
updatedAt: new Date('2026-03-11'),
},
{
id: 'inv_3',
productId: '3',
productName: '运动鞋',
quantity: 150,
threshold: 25,
status: InventoryStatus.IN_STOCK,
warehouseId: 'warehouse_2',
warehouseName: '上海仓库',
lastStockUpdate: new Date('2026-03-12'),
createdAt: new Date('2026-02-03'),
updatedAt: new Date('2026-03-12'),
},
{
id: 'inv_4',
productId: '4',
productName: '智能手机',
quantity: 5,
threshold: 10,
status: InventoryStatus.LOW_STOCK,
warehouseId: 'warehouse_1',
warehouseName: '北京仓库',
lastStockUpdate: new Date('2026-03-09'),
createdAt: new Date('2026-02-04'),
updatedAt: new Date('2026-03-09'),
},
{
id: 'inv_5',
productId: '5',
productName: '平板电脑',
quantity: 0,
threshold: 15,
status: InventoryStatus.OUT_OF_STOCK,
warehouseId: 'warehouse_2',
warehouseName: '上海仓库',
lastStockUpdate: new Date('2026-03-08'),
createdAt: new Date('2026-02-05'),
updatedAt: new Date('2026-03-08'),
},
];
let filteredInventory = mockInventory;
if (params?.productId) {
filteredInventory = filteredInventory.filter(i => i.productId === params.productId);
}
if (params?.status) {
filteredInventory = filteredInventory.filter(i => i.status === params.status);
}
if (params?.warehouseId) {
filteredInventory = filteredInventory.filter(i => i.warehouseId === params.warehouseId);
}
const page = params?.page || 1;
const pageSize = params?.pageSize || 10;
const start = (page - 1) * pageSize;
const end = start + pageSize;
const paginatedInventory = filteredInventory.slice(start, end);
return {
data: paginatedInventory,
total: filteredInventory.length,
};
}
async get(id: string): Promise<Inventory | null> {
const mockInventory: Inventory[] = [
{
id: 'inv_1',
productId: '1',
productName: '智能手表',
quantity: 100,
threshold: 20,
status: InventoryStatus.IN_STOCK,
warehouseId: 'warehouse_1',
warehouseName: '北京仓库',
lastStockUpdate: new Date('2026-03-10'),
createdAt: new Date('2026-02-01'),
updatedAt: new Date('2026-03-10'),
},
{
id: 'inv_2',
productId: '2',
productName: '无线耳机',
quantity: 200,
threshold: 30,
status: InventoryStatus.IN_STOCK,
warehouseId: 'warehouse_1',
warehouseName: '北京仓库',
lastStockUpdate: new Date('2026-03-11'),
createdAt: new Date('2026-02-02'),
updatedAt: new Date('2026-03-11'),
},
];
const inventory = mockInventory.find(i => i.id === id);
return inventory || null;
}
async create(inventory: Omit<Inventory, 'id' | 'createdAt' | 'updatedAt'>): Promise<Inventory> {
const newInventory: Inventory = {
...inventory,
id: `inv_${Date.now()}`,
createdAt: new Date(),
updatedAt: new Date(),
};
return newInventory;
}
async update(id: string, inventory: Partial<Inventory>): Promise<Inventory | null> {
const existingInventory = await this.get(id);
if (!existingInventory) {
return null;
}
const updatedInventory: Inventory = {
...existingInventory,
...inventory,
updatedAt: new Date(),
};
return updatedInventory;
}
async updateStock(id: string, quantity: number): Promise<Inventory | null> {
const existingInventory = await this.get(id);
if (!existingInventory) {
return null;
}
const newQuantity = existingInventory.quantity + quantity;
let status: InventoryStatus;
if (newQuantity <= 0) {
status = InventoryStatus.OUT_OF_STOCK;
} else if (newQuantity < existingInventory.threshold) {
status = InventoryStatus.LOW_STOCK;
} else {
status = InventoryStatus.IN_STOCK;
}
const updatedInventory: Inventory = {
...existingInventory,
quantity: newQuantity,
status,
lastStockUpdate: new Date(),
updatedAt: new Date(),
};
return updatedInventory;
}
async getByProductId(productId: string): Promise<Inventory[]> {
const mockInventory: Inventory[] = [
{
id: 'inv_1',
productId: '1',
productName: '智能手表',
quantity: 100,
threshold: 20,
status: InventoryStatus.IN_STOCK,
warehouseId: 'warehouse_1',
warehouseName: '北京仓库',
lastStockUpdate: new Date('2026-03-10'),
createdAt: new Date('2026-02-01'),
updatedAt: new Date('2026-03-10'),
},
{
id: 'inv_2',
productId: '2',
productName: '无线耳机',
quantity: 200,
threshold: 30,
status: InventoryStatus.IN_STOCK,
warehouseId: 'warehouse_1',
warehouseName: '北京仓库',
lastStockUpdate: new Date('2026-03-11'),
createdAt: new Date('2026-02-02'),
updatedAt: new Date('2026-03-11'),
},
];
return mockInventory.filter(i => i.productId === productId);
}
async getByWarehouseId(warehouseId: string): Promise<Inventory[]> {
const mockInventory: Inventory[] = [
{
id: 'inv_1',
productId: '1',
productName: '智能手表',
quantity: 100,
threshold: 20,
status: InventoryStatus.IN_STOCK,
warehouseId: 'warehouse_1',
warehouseName: '北京仓库',
lastStockUpdate: new Date('2026-03-10'),
createdAt: new Date('2026-02-01'),
updatedAt: new Date('2026-03-10'),
},
{
id: 'inv_2',
productId: '2',
productName: '无线耳机',
quantity: 200,
threshold: 30,
status: InventoryStatus.IN_STOCK,
warehouseId: 'warehouse_1',
warehouseName: '北京仓库',
lastStockUpdate: new Date('2026-03-11'),
createdAt: new Date('2026-02-02'),
updatedAt: new Date('2026-03-11'),
},
{
id: 'inv_3',
productId: '3',
productName: '运动鞋',
quantity: 150,
threshold: 25,
status: InventoryStatus.IN_STOCK,
warehouseId: 'warehouse_2',
warehouseName: '上海仓库',
lastStockUpdate: new Date('2026-03-12'),
createdAt: new Date('2026-02-03'),
updatedAt: new Date('2026-03-12'),
},
];
return mockInventory.filter(i => i.warehouseId === warehouseId);
}
async getLowStockAlerts(threshold: number): Promise<Inventory[]> {
const mockInventory: Inventory[] = [
{
id: 'inv_1',
productId: '1',
productName: '智能手表',
quantity: 100,
threshold: 20,
status: InventoryStatus.IN_STOCK,
warehouseId: 'warehouse_1',
warehouseName: '北京仓库',
lastStockUpdate: new Date('2026-03-10'),
createdAt: new Date('2026-02-01'),
updatedAt: new Date('2026-03-10'),
},
{
id: 'inv_4',
productId: '4',
productName: '智能手机',
quantity: 5,
threshold: 10,
status: InventoryStatus.LOW_STOCK,
warehouseId: 'warehouse_1',
warehouseName: '北京仓库',
lastStockUpdate: new Date('2026-03-09'),
createdAt: new Date('2026-02-04'),
updatedAt: new Date('2026-03-09'),
},
{
id: 'inv_5',
productId: '5',
productName: '平板电脑',
quantity: 0,
threshold: 15,
status: InventoryStatus.OUT_OF_STOCK,
warehouseId: 'warehouse_2',
warehouseName: '上海仓库',
lastStockUpdate: new Date('2026-03-08'),
createdAt: new Date('2026-02-05'),
updatedAt: new Date('2026-03-08'),
},
];
return mockInventory.filter(i => i.quantity <= threshold);
}
async getInventoryStatistics(): Promise<{
totalItems: number;
totalStock: number;
lowStockItems: number;
outOfStockItems: number;
warehouseDistribution: Record<string, number>;
}> {
const mockInventory: Inventory[] = [
{
id: 'inv_1',
productId: '1',
productName: '智能手表',
quantity: 100,
threshold: 20,
status: InventoryStatus.IN_STOCK,
warehouseId: 'warehouse_1',
warehouseName: '北京仓库',
lastStockUpdate: new Date('2026-03-10'),
createdAt: new Date('2026-02-01'),
updatedAt: new Date('2026-03-10'),
},
{
id: 'inv_2',
productId: '2',
productName: '无线耳机',
quantity: 200,
threshold: 30,
status: InventoryStatus.IN_STOCK,
warehouseId: 'warehouse_1',
warehouseName: '北京仓库',
lastStockUpdate: new Date('2026-03-11'),
createdAt: new Date('2026-02-02'),
updatedAt: new Date('2026-03-11'),
},
{
id: 'inv_3',
productId: '3',
productName: '运动鞋',
quantity: 150,
threshold: 25,
status: InventoryStatus.IN_STOCK,
warehouseId: 'warehouse_2',
warehouseName: '上海仓库',
lastStockUpdate: new Date('2026-03-12'),
createdAt: new Date('2026-02-03'),
updatedAt: new Date('2026-03-12'),
},
{
id: 'inv_4',
productId: '4',
productName: '智能手机',
quantity: 5,
threshold: 10,
status: InventoryStatus.LOW_STOCK,
warehouseId: 'warehouse_1',
warehouseName: '北京仓库',
lastStockUpdate: new Date('2026-03-09'),
createdAt: new Date('2026-02-04'),
updatedAt: new Date('2026-03-09'),
},
{
id: 'inv_5',
productId: '5',
productName: '平板电脑',
quantity: 0,
threshold: 15,
status: InventoryStatus.OUT_OF_STOCK,
warehouseId: 'warehouse_2',
warehouseName: '上海仓库',
lastStockUpdate: new Date('2026-03-08'),
createdAt: new Date('2026-02-05'),
updatedAt: new Date('2026-03-08'),
},
];
const totalItems = mockInventory.length;
const totalStock = mockInventory.reduce((sum, item) => sum + item.quantity, 0);
const lowStockItems = mockInventory.filter(i => i.status === InventoryStatus.LOW_STOCK).length;
const outOfStockItems = mockInventory.filter(i => i.status === InventoryStatus.OUT_OF_STOCK).length;
const warehouseDistribution: Record<string, number> = {};
mockInventory.forEach(item => {
if (item.warehouseName) {
warehouseDistribution[item.warehouseName] = (warehouseDistribution[item.warehouseName] || 0) + item.quantity;
}
});
return {
totalItems,
totalStock,
lowStockItems,
outOfStockItems,
warehouseDistribution,
};
}
async fetchWarehouses(): Promise<Warehouse[]> {
return [
{
id: 'warehouse_1',
name: '北京仓库',
code: 'BJ-WH-001',
address: '北京市朝阳区建国路88号',
city: '北京',
country: '中国',
capacity: 10000,
currentUsage: 7500,
status: 'active',
manager: '张三',
phone: '010-12345678',
email: 'zhangsan@example.com',
createdAt: '2026-01-01',
updatedAt: '2026-03-15',
},
{
id: 'warehouse_2',
name: '上海仓库',
code: 'SH-WH-001',
address: '上海市浦东新区张江路100号',
city: '上海',
country: '中国',
capacity: 15000,
currentUsage: 12000,
status: 'active',
manager: '李四',
phone: '021-87654321',
email: 'lisi@example.com',
createdAt: '2026-01-15',
updatedAt: '2026-03-16',
},
];
}
async getInventoryForecasts(): Promise<InventoryForecast[]> {
return [
{
id: 'forecast_1',
productId: '1',
productName: '智能手表',
sku: 'WATCH-001',
currentStock: 100,
forecastedDemand: 150,
recommendedOrder: 50,
leadTime: 7,
riskLevel: 'low',
date: '2026-03-21',
},
{
id: 'forecast_2',
productId: '2',
productName: '无线耳机',
sku: 'EARPHONE-001',
currentStock: 200,
forecastedDemand: 180,
recommendedOrder: 0,
leadTime: 5,
riskLevel: 'low',
date: '2026-03-21',
},
{
id: 'forecast_3',
productId: '4',
productName: '智能手机',
sku: 'PHONE-001',
currentStock: 5,
forecastedDemand: 50,
recommendedOrder: 45,
leadTime: 10,
riskLevel: 'high',
date: '2026-03-21',
},
];
}
}
export const inventoryDataSource = new InventoryDataSourceImpl();