refactor(services): 重构服务模块结构,按功能分类移动文件
将服务文件按功能分类移动到对应子目录,包括财务、营销、订单等模块 更新相关路由和导入路径,修复文件引用错误 归档旧版任务文档,更新README和任务统计信息
This commit is contained in:
182
server/src/api/controllers/AfterSalesController.ts
Normal file
182
server/src/api/controllers/AfterSalesController.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { AfterSalesService, AfterSalesContext } from '../../services/AfterSalesService';
|
||||
|
||||
export class AfterSalesController {
|
||||
static async initTables(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
await AfterSalesService.initTables();
|
||||
res.json({ success: true, message: 'After-sales tables initialized' });
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async createRequest(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const context = AfterSalesController.getContext(req);
|
||||
const { orderId, type, reason, description, items, customerId, customerName, customerEmail } = req.body;
|
||||
|
||||
const result = await AfterSalesService.createRequest(context, {
|
||||
orderId,
|
||||
type,
|
||||
reason,
|
||||
description,
|
||||
items,
|
||||
customerId,
|
||||
customerName,
|
||||
customerEmail
|
||||
});
|
||||
|
||||
res.status(201).json(result);
|
||||
} catch (error: any) {
|
||||
res.status(400).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async getRequests(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const context = AfterSalesController.getContext(req);
|
||||
const { status, type, orderId, page, pageSize } = req.query;
|
||||
|
||||
const result = await AfterSalesService.getRequests(context.tenantId, {
|
||||
status: status as any,
|
||||
type: type as any,
|
||||
orderId: orderId as string,
|
||||
page: page ? parseInt(page as string) : 1,
|
||||
pageSize: pageSize ? parseInt(pageSize as string) : 20
|
||||
});
|
||||
|
||||
res.json(result);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async getRequestById(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const context = AfterSalesController.getContext(req);
|
||||
const { id } = req.params;
|
||||
|
||||
const request = await AfterSalesService.getRequestById(context.tenantId, id);
|
||||
|
||||
if (!request) {
|
||||
res.status(404).json({ success: false, error: 'After-sales request not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json(request);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async approveRequest(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const context = AfterSalesController.getContext(req);
|
||||
const { id } = req.params;
|
||||
const { approved, approvedBy, rejectedReason, refundAmount } = req.body;
|
||||
|
||||
const result = await AfterSalesService.approveRequest(context, id, {
|
||||
approved,
|
||||
approvedBy,
|
||||
rejectedReason,
|
||||
refundAmount
|
||||
});
|
||||
|
||||
res.json(result);
|
||||
} catch (error: any) {
|
||||
res.status(400).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async processRefund(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const context = AfterSalesController.getContext(req);
|
||||
const { refundId } = req.params;
|
||||
const { transactionId, refundMethod } = req.body;
|
||||
|
||||
const result = await AfterSalesService.processRefund(context, refundId, {
|
||||
transactionId,
|
||||
refundMethod
|
||||
});
|
||||
|
||||
res.json(result);
|
||||
} catch (error: any) {
|
||||
res.status(400).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async getRefundById(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const context = AfterSalesController.getContext(req);
|
||||
const { refundId } = req.params;
|
||||
|
||||
const refund = await AfterSalesService.getRefundById(context.tenantId, refundId);
|
||||
|
||||
if (!refund) {
|
||||
res.status(404).json({ success: false, error: 'Refund not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json(refund);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async processReturn(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const context = AfterSalesController.getContext(req);
|
||||
const { returnId } = req.params;
|
||||
const { action, ...actionData } = req.body;
|
||||
|
||||
const result = await AfterSalesService.processReturn(context, returnId, action, actionData);
|
||||
|
||||
res.json(result);
|
||||
} catch (error: any) {
|
||||
res.status(400).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async getReturnById(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const context = AfterSalesController.getContext(req);
|
||||
const { returnId } = req.params;
|
||||
|
||||
const returnRecord = await AfterSalesService.getReturnById(context.tenantId, returnId);
|
||||
|
||||
if (!returnRecord) {
|
||||
res.status(404).json({ success: false, error: 'Return not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json(returnRecord);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async closeRequest(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const context = AfterSalesController.getContext(req);
|
||||
const { id } = req.params;
|
||||
const { reason } = req.body;
|
||||
|
||||
const result = await AfterSalesService.closeRequest(context, id, reason);
|
||||
|
||||
res.json(result);
|
||||
} catch (error: any) {
|
||||
res.status(400).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
private static getContext(req: Request): AfterSalesContext {
|
||||
return {
|
||||
tenantId: req.headers['x-tenant-id'] as string || 'default',
|
||||
shopId: req.headers['x-shop-id'] as string || 'default',
|
||||
taskId: req.headers['x-task-id'] as string || `task-${Date.now()}`,
|
||||
traceId: req.headers['x-trace-id'] as string || `trace-${Date.now()}`,
|
||||
businessType: (req.headers['x-business-type'] as 'TOC' | 'TOB') || 'TOC'
|
||||
};
|
||||
}
|
||||
}
|
||||
136
server/src/api/controllers/OrderCentralController.ts
Normal file
136
server/src/api/controllers/OrderCentralController.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { OrderCentralService, OrderCentralContext, Platform } from '../../services/OrderCentralService';
|
||||
|
||||
export class OrderCentralController {
|
||||
static async initTables(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
await OrderCentralService.initTables();
|
||||
res.json({ success: true, message: 'Order central tables initialized' });
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async registerPlatform(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const context = OrderCentralController.getContext(req);
|
||||
const { platform, shopId, shopName, credentials, syncEnabled, syncInterval } = req.body;
|
||||
|
||||
const result = await OrderCentralService.registerPlatform(context, {
|
||||
platform,
|
||||
shopId,
|
||||
shopName,
|
||||
credentials,
|
||||
syncEnabled: syncEnabled ?? true,
|
||||
syncInterval: syncInterval ?? 30
|
||||
});
|
||||
|
||||
res.status(201).json(result);
|
||||
} catch (error: any) {
|
||||
res.status(400).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async collectOrders(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const context = OrderCentralController.getContext(req);
|
||||
const { platform, shopId, startDate, endDate } = req.body;
|
||||
|
||||
const result = await OrderCentralService.collectOrders(context, {
|
||||
platform,
|
||||
shopId,
|
||||
startDate: startDate ? new Date(startDate) : undefined,
|
||||
endDate: endDate ? new Date(endDate) : undefined
|
||||
});
|
||||
|
||||
res.status(201).json(result);
|
||||
} catch (error: any) {
|
||||
res.status(400).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async getOrders(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const context = OrderCentralController.getContext(req);
|
||||
const { platforms, shopIds, funnelStage, status, startDate, endDate, search, page, pageSize } = req.query;
|
||||
|
||||
const result = await OrderCentralService.getCentralizedOrders(context.tenantId, {
|
||||
platforms: platforms ? (platforms as string).split(',') as Platform[] : undefined,
|
||||
shopIds: shopIds ? (shopIds as string).split(',') : undefined,
|
||||
funnelStage: funnelStage as any,
|
||||
status: status as string,
|
||||
startDate: startDate ? new Date(startDate as string) : undefined,
|
||||
endDate: endDate ? new Date(endDate as string) : undefined,
|
||||
search: search as string,
|
||||
page: page ? parseInt(page as string) : 1,
|
||||
pageSize: pageSize ? parseInt(pageSize as string) : 20
|
||||
});
|
||||
|
||||
res.json(result);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async getFunnelAnalysis(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const context = OrderCentralController.getContext(req);
|
||||
const { platforms, startDate, endDate } = req.query;
|
||||
|
||||
const result = await OrderCentralService.getFunnelAnalysis(context.tenantId, {
|
||||
platforms: platforms ? (platforms as string).split(',') as Platform[] : undefined,
|
||||
startDate: startDate ? new Date(startDate as string) : undefined,
|
||||
endDate: endDate ? new Date(endDate as string) : undefined
|
||||
});
|
||||
|
||||
res.json(result);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async getPlatformStats(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const context = OrderCentralController.getContext(req);
|
||||
const { platforms, startDate, endDate } = req.query;
|
||||
|
||||
const result = await OrderCentralService.getPlatformStats(context.tenantId, {
|
||||
platforms: platforms ? (platforms as string).split(',') as Platform[] : undefined,
|
||||
startDate: startDate ? new Date(startDate as string) : undefined,
|
||||
endDate: endDate ? new Date(endDate as string) : undefined
|
||||
});
|
||||
|
||||
res.json(result);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async getCollectionHistory(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const context = OrderCentralController.getContext(req);
|
||||
const { platform, status, page, pageSize } = req.query;
|
||||
|
||||
const result = await OrderCentralService.getCollectionHistory(context.tenantId, {
|
||||
platform: platform as Platform,
|
||||
status: status as any,
|
||||
page: page ? parseInt(page as string) : 1,
|
||||
pageSize: pageSize ? parseInt(pageSize as string) : 20
|
||||
});
|
||||
|
||||
res.json(result);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
private static getContext(req: Request): OrderCentralContext {
|
||||
return {
|
||||
tenantId: req.headers['x-tenant-id'] as string || 'default',
|
||||
shopId: req.headers['x-shop-id'] as string || 'default',
|
||||
taskId: req.headers['x-task-id'] as string || `task-${Date.now()}`,
|
||||
traceId: req.headers['x-trace-id'] as string || `trace-${Date.now()}`,
|
||||
businessType: (req.headers['x-business-type'] as 'TOC' | 'TOB') || 'TOC'
|
||||
};
|
||||
}
|
||||
}
|
||||
300
server/src/api/controllers/OrderFulfillmentController.ts
Normal file
300
server/src/api/controllers/OrderFulfillmentController.ts
Normal file
@@ -0,0 +1,300 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { OrderFulfillmentService, OrderFulfillmentContext, OrderFulfillmentStatus } from '../services/OrderFulfillmentService';
|
||||
import { logger } from '../utils/logger';
|
||||
|
||||
export class OrderFulfillmentController {
|
||||
static async initTables(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
await OrderFulfillmentService.initTables();
|
||||
res.json({ success: true, message: 'Order fulfillment tables initialized' });
|
||||
} catch (error: any) {
|
||||
logger.error(`[OrderFulfillmentController] Init tables failed: ${error.message}`);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async pullOrder(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { businessType, ...orderData } = req.body;
|
||||
|
||||
const context: OrderFulfillmentContext = {
|
||||
tenantId,
|
||||
shopId,
|
||||
taskId: `TASK-${Date.now()}`,
|
||||
traceId,
|
||||
businessType: businessType || 'TOC'
|
||||
};
|
||||
|
||||
const result = await OrderFulfillmentService.pullOrder(context, orderData);
|
||||
res.json({ success: result.success, data: result });
|
||||
} catch (error: any) {
|
||||
logger.error(`[OrderFulfillmentController] Pull order failed: ${error.message}`);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async reviewOrder(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { id } = req.params;
|
||||
const { approved, reason, note } = req.body;
|
||||
|
||||
const context: OrderFulfillmentContext = {
|
||||
tenantId,
|
||||
shopId,
|
||||
taskId: `TASK-${Date.now()}`,
|
||||
traceId,
|
||||
businessType: 'TOC'
|
||||
};
|
||||
|
||||
const result = await OrderFulfillmentService.reviewOrder(context, id, {
|
||||
approved,
|
||||
reason,
|
||||
note
|
||||
});
|
||||
res.json({ success: result.success, data: result });
|
||||
} catch (error: any) {
|
||||
logger.error(`[OrderFulfillmentController] Review order failed: ${error.message}`);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async allocateWarehouse(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { id } = req.params;
|
||||
|
||||
const context: OrderFulfillmentContext = {
|
||||
tenantId,
|
||||
shopId,
|
||||
taskId: `TASK-${Date.now()}`,
|
||||
traceId,
|
||||
businessType: 'TOC'
|
||||
};
|
||||
|
||||
const result = await OrderFulfillmentService.allocateWarehouse(context, id);
|
||||
res.json({ success: result.success, data: result });
|
||||
} catch (error: any) {
|
||||
logger.error(`[OrderFulfillmentController] Allocate warehouse failed: ${error.message}`);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async prepareShipment(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { id } = req.params;
|
||||
const { logisticsOption } = req.body;
|
||||
|
||||
const context: OrderFulfillmentContext = {
|
||||
tenantId,
|
||||
shopId,
|
||||
taskId: `TASK-${Date.now()}`,
|
||||
traceId,
|
||||
businessType: 'TOC'
|
||||
};
|
||||
|
||||
const result = await OrderFulfillmentService.prepareShipment(context, id, logisticsOption);
|
||||
res.json({ success: result.success, data: result });
|
||||
} catch (error: any) {
|
||||
logger.error(`[OrderFulfillmentController] Prepare shipment failed: ${error.message}`);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async shipOrder(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { id } = req.params;
|
||||
const { trackingNumber, carrier, estimatedDelivery } = req.body;
|
||||
|
||||
const context: OrderFulfillmentContext = {
|
||||
tenantId,
|
||||
shopId,
|
||||
taskId: `TASK-${Date.now()}`,
|
||||
traceId,
|
||||
businessType: 'TOC'
|
||||
};
|
||||
|
||||
const result = await OrderFulfillmentService.shipOrder(context, id, {
|
||||
trackingNumber,
|
||||
carrier,
|
||||
estimatedDelivery: estimatedDelivery ? new Date(estimatedDelivery) : undefined
|
||||
});
|
||||
res.json({ success: result.success, data: result });
|
||||
} catch (error: any) {
|
||||
logger.error(`[OrderFulfillmentController] Ship order failed: ${error.message}`);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async confirmDelivery(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { id } = req.params;
|
||||
const { deliveredAt, signature } = req.body;
|
||||
|
||||
const context: OrderFulfillmentContext = {
|
||||
tenantId,
|
||||
shopId,
|
||||
taskId: `TASK-${Date.now()}`,
|
||||
traceId,
|
||||
businessType: 'TOC'
|
||||
};
|
||||
|
||||
const result = await OrderFulfillmentService.confirmDelivery(context, id, {
|
||||
deliveredAt: deliveredAt ? new Date(deliveredAt) : undefined,
|
||||
signature
|
||||
});
|
||||
res.json({ success: result.success, data: result });
|
||||
} catch (error: any) {
|
||||
logger.error(`[OrderFulfillmentController] Confirm delivery failed: ${error.message}`);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async markAsException(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { id } = req.params;
|
||||
const { reason } = req.body;
|
||||
|
||||
const context: OrderFulfillmentContext = {
|
||||
tenantId,
|
||||
shopId,
|
||||
taskId: `TASK-${Date.now()}`,
|
||||
traceId,
|
||||
businessType: 'TOC'
|
||||
};
|
||||
|
||||
const result = await OrderFulfillmentService.markAsException(context, id, reason);
|
||||
res.json({ success: result.success, data: result });
|
||||
} catch (error: any) {
|
||||
logger.error(`[OrderFulfillmentController] Mark as exception failed: ${error.message}`);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async cancelOrder(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { id } = req.params;
|
||||
const { reason } = req.body;
|
||||
|
||||
const context: OrderFulfillmentContext = {
|
||||
tenantId,
|
||||
shopId,
|
||||
taskId: `TASK-${Date.now()}`,
|
||||
traceId,
|
||||
businessType: 'TOC'
|
||||
};
|
||||
|
||||
const result = await OrderFulfillmentService.cancelOrder(context, id, reason);
|
||||
res.json({ success: result.success, data: result });
|
||||
} catch (error: any) {
|
||||
logger.error(`[OrderFulfillmentController] Cancel order failed: ${error.message}`);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async getOrder(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tenantId } = (req as any).traceContext;
|
||||
const { id } = req.params;
|
||||
|
||||
const order = await OrderFulfillmentService.getOrderById(tenantId, id);
|
||||
if (!order) {
|
||||
res.status(404).json({ success: false, error: 'Order not found' });
|
||||
return;
|
||||
}
|
||||
res.json({ success: true, data: order });
|
||||
} catch (error: any) {
|
||||
logger.error(`[OrderFulfillmentController] Get order failed: ${error.message}`);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async getOrders(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tenantId } = (req as any).traceContext;
|
||||
const { status, shopId, startDate, endDate, page, pageSize } = req.query;
|
||||
|
||||
const result = await OrderFulfillmentService.getOrders(tenantId, {
|
||||
status: status as OrderFulfillmentStatus,
|
||||
shopId: shopId as string,
|
||||
startDate: startDate ? new Date(startDate as string) : undefined,
|
||||
endDate: endDate ? new Date(endDate as string) : undefined,
|
||||
page: page ? parseInt(page as string) : 1,
|
||||
pageSize: pageSize ? parseInt(pageSize as string) : 20
|
||||
});
|
||||
res.json({ success: true, data: result });
|
||||
} catch (error: any) {
|
||||
logger.error(`[OrderFulfillmentController] Get orders failed: ${error.message}`);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async batchReview(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { orderIds } = req.body;
|
||||
|
||||
const context: OrderFulfillmentContext = {
|
||||
tenantId,
|
||||
shopId,
|
||||
taskId: `TASK-${Date.now()}`,
|
||||
traceId,
|
||||
businessType: 'TOC'
|
||||
};
|
||||
|
||||
const result = await OrderFulfillmentService.batchReview(context, orderIds);
|
||||
res.json({ success: true, data: result });
|
||||
} catch (error: any) {
|
||||
logger.error(`[OrderFulfillmentController] Batch review failed: ${error.message}`);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async batchAllocate(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { orderIds } = req.body;
|
||||
|
||||
const context: OrderFulfillmentContext = {
|
||||
tenantId,
|
||||
shopId,
|
||||
taskId: `TASK-${Date.now()}`,
|
||||
traceId,
|
||||
businessType: 'TOC'
|
||||
};
|
||||
|
||||
const result = await OrderFulfillmentService.batchAllocate(context, orderIds);
|
||||
res.json({ success: true, data: result });
|
||||
} catch (error: any) {
|
||||
logger.error(`[OrderFulfillmentController] Batch allocate failed: ${error.message}`);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
static async batchShip(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { shipments } = req.body;
|
||||
|
||||
const context: OrderFulfillmentContext = {
|
||||
tenantId,
|
||||
shopId,
|
||||
taskId: `TASK-${Date.now()}`,
|
||||
traceId,
|
||||
businessType: 'TOC'
|
||||
};
|
||||
|
||||
const result = await OrderFulfillmentService.batchShip(context, shipments);
|
||||
res.json({ success: true, data: result });
|
||||
} catch (error: any) {
|
||||
logger.error(`[OrderFulfillmentController] Batch ship failed: ${error.message}`);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,13 @@ import { ConfigService } from '../../services/ConfigService';
|
||||
import { CrawlerService } from '../../services/CrawlerService';
|
||||
import { DynamicPricingService } from '../../services/DynamicPricingService';
|
||||
import { MultiPlatformProductService } from '../../services/MultiPlatformProductService';
|
||||
import { ProductCollectionService } from '../../services/ProductCollectionService';
|
||||
import { ProductListingService } from '../../services/ProductListingService';
|
||||
import { ProductService } from '../../services/ProductService';
|
||||
import { SupplierInquiryService } from '../../services/SupplierInquiryService';
|
||||
import { SupplyChainService } from '../../services/SupplyChainService';
|
||||
import { CrawlerWorker } from '../../workers/CrawlerWorker';
|
||||
import { logger } from '../../utils/logger';
|
||||
|
||||
|
||||
export class ProductController {
|
||||
@@ -833,4 +836,205 @@ export class ProductController {
|
||||
res.status(500).json({ success: false, error: errorMessage });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [BE-P101] 创建数据采集任务
|
||||
*/
|
||||
static async createCollectionTask(req: Request, res: Response) {
|
||||
const { platform, sourceUrl, businessType } = req.body;
|
||||
const traceContext = req.traceContext;
|
||||
|
||||
if (!traceContext) {
|
||||
res.status(400).json({ success: false, error: 'Missing trace context' });
|
||||
return;
|
||||
}
|
||||
|
||||
const { tenantId, shopId, traceId, userId } = traceContext;
|
||||
|
||||
try {
|
||||
const collectionService = new ProductCollectionService();
|
||||
const taskId = await collectionService.createTask(
|
||||
tenantId,
|
||||
shopId || '',
|
||||
businessType as 'TOC' | 'TOB',
|
||||
platform,
|
||||
sourceUrl,
|
||||
traceId
|
||||
);
|
||||
|
||||
await AuditService.log({
|
||||
tenantId,
|
||||
shopId: shopId || '',
|
||||
traceId,
|
||||
userId,
|
||||
module: 'PRODUCT',
|
||||
action: 'CREATE_COLLECTION_TASK',
|
||||
resourceType: 'collection_task',
|
||||
resourceId: taskId,
|
||||
afterSnapshot: { platform, sourceUrl, businessType },
|
||||
result: 'success',
|
||||
source: 'console'
|
||||
});
|
||||
|
||||
res.status(201).json({ success: true, data: { taskId } });
|
||||
} catch (err: unknown) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
||||
res.status(500).json({ success: false, error: errorMessage });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [BE-P101] 执行数据采集任务
|
||||
*/
|
||||
static async executeCollectionTask(req: Request, res: Response) {
|
||||
const { taskId } = req.params;
|
||||
const traceContext = req.traceContext;
|
||||
|
||||
if (!traceContext) {
|
||||
res.status(400).json({ success: false, error: 'Missing trace context' });
|
||||
return;
|
||||
}
|
||||
|
||||
const { tenantId, userId } = traceContext;
|
||||
|
||||
try {
|
||||
const collectionService = new ProductCollectionService();
|
||||
|
||||
// 异步执行采集任务
|
||||
collectionService.executeCollection(taskId).catch((error) => {
|
||||
logger.error(`[ProductController] Collection task failed: ${taskId}`, error);
|
||||
});
|
||||
|
||||
await AuditService.log({
|
||||
tenantId,
|
||||
shopId: '',
|
||||
traceId: traceContext.traceId,
|
||||
userId,
|
||||
module: 'PRODUCT',
|
||||
action: 'EXECUTE_COLLECTION_TASK',
|
||||
resourceType: 'collection_task',
|
||||
resourceId: taskId,
|
||||
result: 'success',
|
||||
source: 'console'
|
||||
});
|
||||
|
||||
res.json({ success: true, data: { taskId, status: 'started' } });
|
||||
} catch (err: unknown) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
||||
res.status(500).json({ success: false, error: errorMessage });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [BE-P101] 获取采集任务列表
|
||||
*/
|
||||
static async getCollectionTasks(req: Request, res: Response) {
|
||||
const traceContext = req.traceContext;
|
||||
|
||||
if (!traceContext) {
|
||||
res.status(400).json({ success: false, error: 'Missing trace context' });
|
||||
return;
|
||||
}
|
||||
|
||||
const { tenantId } = traceContext;
|
||||
const { status, platform, limit = '20', offset = '0' } = req.query;
|
||||
|
||||
try {
|
||||
const collectionService = new ProductCollectionService();
|
||||
const tasks = await collectionService.getTasks(tenantId, {
|
||||
status: status as any,
|
||||
platform: platform as string,
|
||||
limit: parseInt(limit as string),
|
||||
offset: parseInt(offset as string)
|
||||
});
|
||||
|
||||
res.json({ success: true, data: tasks });
|
||||
} catch (err: unknown) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
||||
res.status(500).json({ success: false, error: errorMessage });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [BE-P101] 重试失败的采集任务
|
||||
*/
|
||||
static async retryCollectionTask(req: Request, res: Response) {
|
||||
const { taskId } = req.params;
|
||||
const traceContext = req.traceContext;
|
||||
|
||||
if (!traceContext) {
|
||||
res.status(400).json({ success: false, error: 'Missing trace context' });
|
||||
return;
|
||||
}
|
||||
|
||||
const { tenantId, userId } = traceContext;
|
||||
|
||||
try {
|
||||
const collectionService = new ProductCollectionService();
|
||||
await collectionService.retryTask(taskId);
|
||||
|
||||
await AuditService.log({
|
||||
tenantId,
|
||||
shopId: '',
|
||||
traceId: traceContext.traceId,
|
||||
userId,
|
||||
module: 'PRODUCT',
|
||||
action: 'RETRY_COLLECTION_TASK',
|
||||
resourceType: 'collection_task',
|
||||
resourceId: taskId,
|
||||
result: 'success',
|
||||
source: 'console'
|
||||
});
|
||||
|
||||
res.json({ success: true, data: { taskId, status: 'retrying' } });
|
||||
} catch (err: unknown) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
||||
res.status(500).json({ success: false, error: errorMessage });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [BE-P101] 批量创建采集任务
|
||||
*/
|
||||
static async batchCreateCollectionTasks(req: Request, res: Response) {
|
||||
const { tasks, businessType } = req.body;
|
||||
const traceContext = req.traceContext;
|
||||
|
||||
if (!traceContext) {
|
||||
res.status(400).json({ success: false, error: 'Missing trace context' });
|
||||
return;
|
||||
}
|
||||
|
||||
const { tenantId, shopId, traceId, userId } = traceContext;
|
||||
|
||||
try {
|
||||
const collectionService = new ProductCollectionService();
|
||||
const taskIds = await collectionService.batchCreateTasks(
|
||||
tenantId,
|
||||
shopId || '',
|
||||
businessType as 'TOC' | 'TOB',
|
||||
tasks as Array<{ platform: string; sourceUrl: string }>,
|
||||
traceId
|
||||
);
|
||||
|
||||
await AuditService.log({
|
||||
tenantId,
|
||||
shopId: shopId || '',
|
||||
traceId,
|
||||
userId,
|
||||
module: 'PRODUCT',
|
||||
action: 'BATCH_CREATE_COLLECTION_TASKS',
|
||||
resourceType: 'collection_task',
|
||||
resourceId: taskIds.join(','),
|
||||
afterSnapshot: { count: taskIds.length },
|
||||
result: 'success',
|
||||
source: 'console'
|
||||
});
|
||||
|
||||
res.status(201).json({ success: true, data: { taskIds, count: taskIds.length } });
|
||||
} catch (err: unknown) {
|
||||
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
||||
res.status(500).json({ success: false, error: errorMessage });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
216
server/src/api/controllers/ReconciliationController.ts
Normal file
216
server/src/api/controllers/ReconciliationController.ts
Normal file
@@ -0,0 +1,216 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { ReconciliationService } from '../../services/reconciliationService';
|
||||
import { CreateReconciliationParams } from '../../services/FinanceReconciliationService';
|
||||
|
||||
export class ReconciliationController {
|
||||
/**
|
||||
* 创建对账记录
|
||||
*/
|
||||
static async createReconciliation(req: Request, res: Response) {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { platform, periodStart, periodEnd, expectedAmount, actualAmount, businessType } = req.body;
|
||||
|
||||
if (!platform || !periodStart || !periodEnd || expectedAmount === undefined || actualAmount === undefined || !businessType) {
|
||||
return res.status(400).json({ success: false, error: 'Missing required fields' });
|
||||
}
|
||||
|
||||
const params: CreateReconciliationParams = {
|
||||
tenantId,
|
||||
shopId,
|
||||
platform,
|
||||
periodStart: new Date(periodStart),
|
||||
periodEnd: new Date(periodEnd),
|
||||
expectedAmount,
|
||||
actualAmount,
|
||||
traceId,
|
||||
taskId: `reconciliation_${Date.now()}`,
|
||||
businessType: businessType as 'TOC' | 'TOB'
|
||||
};
|
||||
|
||||
const result = await ReconciliationService.createReconciliation(params);
|
||||
res.json({ success: true, data: result });
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对账记录列表
|
||||
*/
|
||||
static async getReconciliations(req: Request, res: Response) {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { status, platform, startDate, endDate } = req.query;
|
||||
|
||||
const reconciliations = await ReconciliationService.getReconciliations(
|
||||
tenantId,
|
||||
shopId,
|
||||
status as any,
|
||||
platform as string,
|
||||
startDate ? new Date(startDate as string) : undefined,
|
||||
endDate ? new Date(endDate as string) : undefined,
|
||||
traceId
|
||||
);
|
||||
|
||||
res.json({ success: true, data: reconciliations });
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对账记录详情
|
||||
*/
|
||||
static async getReconciliationById(req: Request, res: Response) {
|
||||
try {
|
||||
const { traceId } = (req as any).traceContext;
|
||||
const { id } = req.params;
|
||||
|
||||
if (!id) {
|
||||
return res.status(400).json({ success: false, error: 'Missing reconciliation ID' });
|
||||
}
|
||||
|
||||
const reconciliation = await ReconciliationService.getReconciliationById(id, traceId);
|
||||
if (!reconciliation) {
|
||||
return res.status(404).json({ success: false, error: 'Reconciliation not found' });
|
||||
}
|
||||
|
||||
res.json({ success: true, data: reconciliation });
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理异常对账
|
||||
*/
|
||||
static async handleException(req: Request, res: Response) {
|
||||
try {
|
||||
const { traceId } = (req as any).traceContext;
|
||||
const { id } = req.params;
|
||||
const { action } = req.body;
|
||||
|
||||
if (!id || !action) {
|
||||
return res.status(400).json({ success: false, error: 'Missing reconciliation ID or action' });
|
||||
}
|
||||
|
||||
const result = await ReconciliationService.handleException(id, action, traceId);
|
||||
res.json({ success: true, data: result });
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量对账
|
||||
*/
|
||||
static async batchReconciliation(req: Request, res: Response) {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { platforms, periodStart, periodEnd } = req.body;
|
||||
|
||||
if (!platforms || !Array.isArray(platforms) || !periodStart || !periodEnd) {
|
||||
return res.status(400).json({ success: false, error: 'Missing required fields' });
|
||||
}
|
||||
|
||||
const results = await ReconciliationService.batchReconciliation(
|
||||
tenantId,
|
||||
shopId,
|
||||
platforms,
|
||||
new Date(periodStart),
|
||||
new Date(periodEnd),
|
||||
traceId
|
||||
);
|
||||
|
||||
res.json({ success: true, data: results });
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对账汇总
|
||||
*/
|
||||
static async getReconciliationSummary(req: Request, res: Response) {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { startDate, endDate } = req.query;
|
||||
|
||||
if (!startDate || !endDate) {
|
||||
return res.status(400).json({ success: false, error: 'Missing startDate or endDate' });
|
||||
}
|
||||
|
||||
const summary = await ReconciliationService.getReconciliationSummary(
|
||||
tenantId,
|
||||
shopId,
|
||||
new Date(startDate as string),
|
||||
new Date(endDate as string),
|
||||
traceId
|
||||
);
|
||||
|
||||
res.json({ success: true, data: summary });
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动对账任务
|
||||
*/
|
||||
static async autoReconciliation(req: Request, res: Response) {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { platforms } = req.body;
|
||||
|
||||
if (!platforms || !Array.isArray(platforms)) {
|
||||
return res.status(400).json({ success: false, error: 'Missing platforms' });
|
||||
}
|
||||
|
||||
await ReconciliationService.autoReconciliationTask(tenantId, shopId, platforms, traceId);
|
||||
res.json({ success: true, message: 'Auto reconciliation task started' });
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出对账报告
|
||||
*/
|
||||
static async exportReconciliationReport(req: Request, res: Response) {
|
||||
try {
|
||||
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
||||
const { startDate, endDate } = req.query;
|
||||
|
||||
if (!startDate || !endDate) {
|
||||
return res.status(400).json({ success: false, error: 'Missing startDate or endDate' });
|
||||
}
|
||||
|
||||
const report = await ReconciliationService.exportReconciliationReport(
|
||||
tenantId,
|
||||
shopId,
|
||||
new Date(startDate as string),
|
||||
new Date(endDate as string),
|
||||
traceId
|
||||
);
|
||||
|
||||
res.setHeader('Content-Type', 'text/csv');
|
||||
res.setHeader('Content-Disposition', `attachment; filename=reconciliation_report_${new Date().toISOString().split('T')[0]}.csv`);
|
||||
res.send(report);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化对账表
|
||||
*/
|
||||
static async initReconciliationTable(req: Request, res: Response) {
|
||||
try {
|
||||
await ReconciliationService.initTable();
|
||||
res.json({ success: true, message: 'Reconciliation table initialized' });
|
||||
} catch (error: any) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
}
|
||||
28
server/src/api/routes/afterSales.ts
Normal file
28
server/src/api/routes/afterSales.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { Router } from 'express';
|
||||
import { AfterSalesController } from '../controllers/AfterSalesController';
|
||||
import { requireTraceContext } from '../../core/guards/trace-context.guard';
|
||||
import { requirePermission } from '../../core/guards/rbac.guard';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post('/init-tables', requireTraceContext, requirePermission('admin'), AfterSalesController.initTables);
|
||||
|
||||
router.post('/', requireTraceContext, requirePermission('after_sales:write'), AfterSalesController.createRequest);
|
||||
|
||||
router.get('/', requireTraceContext, requirePermission('after_sales:read'), AfterSalesController.getRequests);
|
||||
|
||||
router.get('/:id', requireTraceContext, requirePermission('after_sales:read'), AfterSalesController.getRequestById);
|
||||
|
||||
router.post('/:id/approve', requireTraceContext, requirePermission('after_sales:write'), AfterSalesController.approveRequest);
|
||||
|
||||
router.post('/:id/close', requireTraceContext, requirePermission('after_sales:write'), AfterSalesController.closeRequest);
|
||||
|
||||
router.post('/refunds/:refundId/process', requireTraceContext, requirePermission('after_sales:write'), AfterSalesController.processRefund);
|
||||
|
||||
router.get('/refunds/:refundId', requireTraceContext, requirePermission('after_sales:read'), AfterSalesController.getRefundById);
|
||||
|
||||
router.post('/returns/:returnId/process', requireTraceContext, requirePermission('after_sales:write'), AfterSalesController.processReturn);
|
||||
|
||||
router.get('/returns/:returnId', requireTraceContext, requirePermission('after_sales:read'), AfterSalesController.getReturnById);
|
||||
|
||||
export default router;
|
||||
@@ -7,6 +7,7 @@ import { SovereignCreditPoolService } from '../../services/SovereignCreditPoolSe
|
||||
import { SovereigntySettlementService } from '../../services/SovereigntySettlementService';
|
||||
import { PricingController } from '../controllers/PricingController';
|
||||
import { SettlementController } from '../controllers/SettlementController';
|
||||
import { ReconciliationController } from '../controllers/ReconciliationController';
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -131,4 +132,17 @@ router.get('/pool/active', requireTraceContext, requirePermission('finance:read'
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* [BE-F101] 资金对账闭环
|
||||
*/
|
||||
router.post('/reconciliation', requireTraceContext, requirePermission('finance:write'), ReconciliationController.createReconciliation);
|
||||
router.get('/reconciliation', requireTraceContext, requirePermission('finance:read'), ReconciliationController.getReconciliations);
|
||||
router.get('/reconciliation/:id', requireTraceContext, requirePermission('finance:read'), ReconciliationController.getReconciliationById);
|
||||
router.post('/reconciliation/:id/exception', requireTraceContext, requirePermission('finance:write'), ReconciliationController.handleException);
|
||||
router.post('/reconciliation/batch', requireTraceContext, requirePermission('finance:write'), ReconciliationController.batchReconciliation);
|
||||
router.get('/reconciliation/summary', requireTraceContext, requirePermission('finance:read'), ReconciliationController.getReconciliationSummary);
|
||||
router.post('/reconciliation/auto', requireTraceContext, requirePermission('finance:write'), ReconciliationController.autoReconciliation);
|
||||
router.get('/reconciliation/export', requireTraceContext, requirePermission('finance:read'), ReconciliationController.exportReconciliationReport);
|
||||
router.post('/reconciliation/init', requireTraceContext, requirePermission('finance:admin'), ReconciliationController.initReconciliationTable);
|
||||
|
||||
export default router;
|
||||
|
||||
22
server/src/api/routes/orderCentral.ts
Normal file
22
server/src/api/routes/orderCentral.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Router } from 'express';
|
||||
import { OrderCentralController } from '../controllers/OrderCentralController';
|
||||
import { requireTraceContext } from '../../core/guards/trace-context.guard';
|
||||
import { requirePermission } from '../../core/guards/rbac.guard';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post('/init-tables', requireTraceContext, requirePermission('admin'), OrderCentralController.initTables);
|
||||
|
||||
router.post('/platforms', requireTraceContext, requirePermission('order:config'), OrderCentralController.registerPlatform);
|
||||
|
||||
router.post('/collect', requireTraceContext, requirePermission('order:write'), OrderCentralController.collectOrders);
|
||||
|
||||
router.get('/orders', requireTraceContext, requirePermission('order:read'), OrderCentralController.getOrders);
|
||||
|
||||
router.get('/funnel-analysis', requireTraceContext, requirePermission('order:read'), OrderCentralController.getFunnelAnalysis);
|
||||
|
||||
router.get('/platform-stats', requireTraceContext, requirePermission('order:read'), OrderCentralController.getPlatformStats);
|
||||
|
||||
router.get('/collections', requireTraceContext, requirePermission('order:read'), OrderCentralController.getCollectionHistory);
|
||||
|
||||
export default router;
|
||||
36
server/src/api/routes/orderFulfillment.ts
Normal file
36
server/src/api/routes/orderFulfillment.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Router } from 'express';
|
||||
import { OrderFulfillmentController } from '../controllers/OrderFulfillmentController';
|
||||
import { requireTraceContext } from '../../core/guards/trace-context.guard';
|
||||
import { requirePermission } from '../../core/guards/rbac.guard';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post('/init-tables', requireTraceContext, requirePermission('admin'), OrderFulfillmentController.initTables);
|
||||
|
||||
router.post('/pull', requireTraceContext, requirePermission('order:write'), OrderFulfillmentController.pullOrder);
|
||||
|
||||
router.get('/', requireTraceContext, requirePermission('order:read'), OrderFulfillmentController.getOrders);
|
||||
|
||||
router.get('/:id', requireTraceContext, requirePermission('order:read'), OrderFulfillmentController.getOrder);
|
||||
|
||||
router.post('/:id/review', requireTraceContext, requirePermission('order:write'), OrderFulfillmentController.reviewOrder);
|
||||
|
||||
router.post('/:id/allocate', requireTraceContext, requirePermission('order:write'), OrderFulfillmentController.allocateWarehouse);
|
||||
|
||||
router.post('/:id/prepare-shipment', requireTraceContext, requirePermission('order:write'), OrderFulfillmentController.prepareShipment);
|
||||
|
||||
router.post('/:id/ship', requireTraceContext, requirePermission('order:write'), OrderFulfillmentController.shipOrder);
|
||||
|
||||
router.post('/:id/confirm-delivery', requireTraceContext, requirePermission('order:write'), OrderFulfillmentController.confirmDelivery);
|
||||
|
||||
router.post('/:id/exception', requireTraceContext, requirePermission('order:write'), OrderFulfillmentController.markAsException);
|
||||
|
||||
router.post('/:id/cancel', requireTraceContext, requirePermission('order:write'), OrderFulfillmentController.cancelOrder);
|
||||
|
||||
router.post('/batch/review', requireTraceContext, requirePermission('order:write'), OrderFulfillmentController.batchReview);
|
||||
|
||||
router.post('/batch/allocate', requireTraceContext, requirePermission('order:write'), OrderFulfillmentController.batchAllocate);
|
||||
|
||||
router.post('/batch/ship', requireTraceContext, requirePermission('order:write'), OrderFulfillmentController.batchShip);
|
||||
|
||||
export default router;
|
||||
@@ -76,4 +76,11 @@ router.post('/batch', requireTraceContext, requirePermission('product:write'), P
|
||||
router.post('/:productId/sync-inventory', requireTraceContext, requirePermission('product:write'), ProductController.syncInventory);
|
||||
router.get('/:productId/sync-status', requireTraceContext, ProductController.getSyncStatus);
|
||||
|
||||
// [BE-P101] Product Collection APIs
|
||||
router.post('/collection-tasks', requireTraceContext, requirePermission('product:write'), ProductController.createCollectionTask);
|
||||
router.get('/collection-tasks', requireTraceContext, ProductController.getCollectionTasks);
|
||||
router.post('/collection-tasks/batch', requireTraceContext, requirePermission('product:write'), ProductController.batchCreateCollectionTasks);
|
||||
router.post('/collection-tasks/:taskId/execute', requireTraceContext, requirePermission('product:write'), ProductController.executeCollectionTask);
|
||||
router.post('/collection-tasks/:taskId/retry', requireTraceContext, requirePermission('product:write'), ProductController.retryCollectionTask);
|
||||
|
||||
export default router;
|
||||
|
||||
Reference in New Issue
Block a user