feat: 初始化项目结构并添加核心功能模块

- 新增文档模板和导航结构
- 实现服务器基础API路由和控制器
- 添加扩展插件配置和前端框架
- 引入多租户和权限管理模块
- 集成日志和数据库配置
- 添加核心业务模型和类型定义
This commit is contained in:
2026-03-17 22:07:19 +08:00
parent c0870dce50
commit 136c2fa579
728 changed files with 107690 additions and 5614 deletions

View File

@@ -0,0 +1,456 @@
import { Request, Response } from 'express';
import { logger } from '../../utils/logger';
import { ConsumerOrderService, ConsumerOrder } from '../../domains/Trade/ConsumerOrderService';
import { OrderService } from '../../services/OrderService';
/**
* [BIZ_OPS_01] 多平台订单 Webhook 接收器 (Order Webhook Receiver)
* @description 接收来自不同平台 (Amazon, AliExpress, Shopify, TikTok) 的订单 Webhook 推送,
* 并将其映射为系统统一的 cf_order 模型进行入库处理。
*/
export class OrderController {
/**
* 平台 Webhook 入口
* POST /api/v1/orders/webhook/:platform
*/
static async handlePlatformWebhook(req: Request, res: Response) {
const { platform } = req.params;
const payload = req.body;
const tenantId = req.headers['x-tenant-id'] as string || 'default-tenant';
const shopId = req.headers['x-shop-id'] as string || 'default-shop';
logger.info(`[OrderWebhook] Received webhook from platform: ${platform} for Tenant: ${tenantId}`);
try {
// 1. 根据不同平台解析 Payload (Mapping)
const normalizedOrder = this.mapPlatformPayloadToOrder(platform, payload, tenantId, shopId);
if (!normalizedOrder) {
return res.status(400).json({ success: false, error: `Unsupported or invalid payload for platform: ${platform}` });
}
// 2. 调用 ConsumerOrderService 进行同步
const orderId = await ConsumerOrderService.syncPlatformOrder(normalizedOrder);
logger.info(`[OrderWebhook] Successfully synced order ${normalizedOrder.platform_order_id} to internal ID: ${orderId}`);
// 3. 返回平台要求的 200 OK
return res.json({ success: true, orderId });
} catch (err: any) {
logger.error(`[OrderWebhook] Webhook handling failed for ${platform}: ${err.message}`);
return res.status(500).json({ success: false, error: 'Internal server error' });
}
}
/**
* 将各平台原始 Payload 映射为系统统一模型
*/
private static mapPlatformPayloadToOrder(platform: string, payload: any, tenantId: string, shopId: string): Partial<ConsumerOrder> | null {
const upperPlatform = platform.toUpperCase();
switch (upperPlatform) {
case 'SHOPIFY':
return {
tenant_id: tenantId,
shop_id: shopId,
platform: 'SHOPIFY',
platform_order_id: String(payload.id || payload.name),
customer_name: payload.customer?.first_name ? `${payload.customer.first_name} ${payload.customer.last_name}` : 'Unknown',
customer_email: payload.customer?.email,
shipping_address: payload.shipping_address,
items: payload.line_items?.map((item: any) => ({
skuId: item.sku,
title: item.title,
price: Number(item.price),
quantity: item.quantity,
costPrice: item.cost_price || 0
})),
total_amount: Number(payload.total_price),
currency: payload.currency || 'USD',
status: this.mapShopifyStatus(payload.financial_status, payload.fulfillment_status),
payment_status: payload.financial_status === 'paid' ? 'COMPLETED' : 'PENDING',
fulfillment_status: payload.fulfillment_status === 'fulfilled' ? 'SHIPPED' : 'PENDING',
trace_id: `webhook-${Date.now()}`
};
case 'AMAZON':
// 模拟 Amazon SP-API 结构
return {
tenant_id: tenantId,
shop_id: shopId,
platform: 'AMAZON',
platform_order_id: payload.AmazonOrderId,
customer_name: payload.BuyerInfo?.BuyerName || 'Amazon Customer',
customer_email: payload.BuyerInfo?.BuyerEmail,
shipping_address: payload.ShippingAddress,
items: payload.OrderItems?.map((item: any) => ({
skuId: item.SellerSKU,
title: item.Title,
price: Number(item.ItemPrice?.Amount || 0),
quantity: item.QuantityOrdered,
costPrice: 0
})),
total_amount: Number(payload.OrderTotal?.Amount || 0),
currency: payload.OrderTotal?.CurrencyCode || 'USD',
status: payload.OrderStatus === 'Shipped' ? 'SHIPPED' : 'PAID',
payment_status: 'COMPLETED',
fulfillment_status: payload.OrderStatus === 'Shipped' ? 'SHIPPED' : 'PENDING',
trace_id: `webhook-${Date.now()}`
};
case 'TIKTOK':
// 模拟 TikTok Shop API 结构
return {
tenant_id: tenantId,
shop_id: shopId,
platform: 'TIKTOK',
platform_order_id: payload.order_id,
customer_name: payload.recipient_address?.name || 'TikTok Customer',
shipping_address: payload.recipient_address,
items: payload.item_list?.map((item: any) => ({
skuId: item.sku_id,
title: item.product_name,
price: Number(item.sku_sale_price),
quantity: 1,
costPrice: 0
})),
total_amount: Number(payload.total_amount),
currency: payload.currency || 'USD',
status: payload.order_status === 100 ? 'PAID' : 'UNPAID',
payment_status: payload.order_status >= 100 ? 'COMPLETED' : 'PENDING',
fulfillment_status: 'PENDING',
trace_id: `webhook-${Date.now()}`
};
default:
return null;
}
}
private static mapShopifyStatus(financial: string, fulfillment: string): 'UNPAID' | 'PAID' | 'SHIPPED' | 'DELIVERED' {
if (fulfillment === 'fulfilled') return 'SHIPPED';
if (financial === 'paid') return 'PAID';
return 'UNPAID';
}
/**
* 手动触发同步 (API 入口)
* POST /api/v1/orders/sync
*/
static async triggerManualSync(req: Request, res: Response) {
try {
const { platform, shopId } = req.body;
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
logger.info(`[OrderSync] Manual sync triggered for Platform: ${platform}, Shop: ${shopId}`);
// 此处应调用各平台 Connector 的 supportsOrderPull 能力
// 示例Mock 拉取成功
res.json({ success: true, message: `Manual sync triggered for ${platform}` });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 获取订单统计
* GET /api/v1/orders/stats
*/
static async getStats(req: Request, res: Response) {
try {
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
const stats = await ConsumerOrderService.getOrderStats(tenantId);
res.json({ success: true, data: stats });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 创建订单
* POST /api/v1/orders
*/
static async createOrder(req: Request, res: Response) {
try {
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
const orderData = { ...req.body, tenant_id: tenantId };
const orderId = await OrderService.createOrder(orderData);
res.json({ success: true, orderId });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 获取订单详情
* GET /api/v1/orders/:id
*/
static async getOrderById(req: Request, res: Response) {
try {
const { id } = req.params;
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
const order = await OrderService.getOrderById(id, tenantId);
if (order) {
res.json({ success: true, data: order });
} else {
res.status(404).json({ success: false, error: 'Order not found' });
}
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 更新订单
* PUT /api/v1/orders/:id
*/
static async updateOrder(req: Request, res: Response) {
try {
const { id } = req.params;
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
await OrderService.updateOrder(id, tenantId, req.body);
res.json({ success: true, message: 'Order updated successfully' });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 删除订单
* DELETE /api/v1/orders/:id
*/
static async deleteOrder(req: Request, res: Response) {
try {
const { id } = req.params;
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
await OrderService.deleteOrder(id, tenantId);
res.json({ success: true, message: 'Order deleted successfully' });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 获取订单列表
* GET /api/v1/orders
*/
static async getOrders(req: Request, res: Response) {
try {
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
const params = {
page: parseInt(req.query.page as string) || 1,
pageSize: parseInt(req.query.pageSize as string) || 20,
status: req.query.status as string,
platform: req.query.platform as string,
startDate: req.query.startDate as string,
endDate: req.query.endDate as string,
};
const result = await OrderService.getOrders(tenantId, params);
res.json({ success: true, data: result });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 批量更新订单
* PUT /api/v1/orders/batch
*/
static async batchUpdateOrders(req: Request, res: Response) {
try {
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
const { orderIds, updates } = req.body;
const result = await OrderService.batchUpdateOrders(tenantId, orderIds, updates);
res.json({ success: true, data: result });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 订单状态流转
* POST /api/v1/orders/:id/status
*/
static async transitionOrderStatus(req: Request, res: Response) {
try {
const { id } = req.params;
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
const { status, reason } = req.body;
await OrderService.transitionOrderStatus(tenantId, id, status, reason);
res.json({ success: true, message: 'Order status updated successfully' });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 批量审核订单
* POST /api/v1/orders/batch/audit
*/
static async batchAuditOrders(req: Request, res: Response) {
try {
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
const { orderIds } = req.body;
const result = await OrderService.batchAuditOrders(tenantId, orderIds);
res.json({ success: true, data: result });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 批量发货
* POST /api/v1/orders/batch/ship
*/
static async batchShipOrders(req: Request, res: Response) {
try {
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
const { orderIds } = req.body;
const result = await OrderService.batchShipOrders(tenantId, orderIds);
res.json({ success: true, data: result });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 标记订单为异常
* POST /api/v1/orders/:id/exception
*/
static async markOrderAsException(req: Request, res: Response) {
try {
const { id } = req.params;
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
const { reason } = req.body;
await OrderService.markOrderAsException(tenantId, id, reason);
res.json({ success: true, message: 'Order marked as exception' });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 自动改派订单
* POST /api/v1/orders/:id/reroute
*/
static async autoRerouteOrder(req: Request, res: Response) {
try {
const { id } = req.params;
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
await OrderService.autoRerouteOrder(tenantId, id);
res.json({ success: true, message: 'Order rerouted successfully' });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 重试异常订单
* POST /api/v1/orders/:id/retry
*/
static async retryExceptionOrder(req: Request, res: Response) {
try {
const { id } = req.params;
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
await OrderService.retryExceptionOrder(tenantId, id);
res.json({ success: true, message: 'Order retried successfully' });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 取消订单
* POST /api/v1/orders/:id/cancel
*/
static async cancelOrder(req: Request, res: Response) {
try {
const { id } = req.params;
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
const { reason } = req.body;
await OrderService.cancelOrder(tenantId, id, reason);
res.json({ success: true, message: 'Order cancelled successfully' });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 申请退款
* POST /api/v1/orders/:id/refund
*/
static async requestRefund(req: Request, res: Response) {
try {
const { id } = req.params;
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
const { reason, amount } = req.body;
const refundId = await OrderService.requestRefund(tenantId, id, reason, amount);
res.json({ success: true, refundId });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 审批退款
* POST /api/v1/orders/refund/:id/approve
*/
static async approveRefund(req: Request, res: Response) {
try {
const { id } = req.params;
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
const { approved, note } = req.body;
await OrderService.approveRefund(tenantId, id, approved, note);
res.json({ success: true, message: 'Refund processed successfully' });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 申请售后
* POST /api/v1/orders/:id/after-sales
*/
static async requestAfterSales(req: Request, res: Response) {
try {
const { id } = req.params;
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
const { type, reason, items } = req.body;
const serviceId = await OrderService.requestAfterSales(tenantId, id, type, reason, items);
res.json({ success: true, serviceId });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 处理售后申请
* POST /api/v1/orders/after-sales/:id/process
*/
static async processAfterSales(req: Request, res: Response) {
try {
const { id } = req.params;
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
const { action, note } = req.body;
await OrderService.processAfterSales(tenantId, id, action, note);
res.json({ success: true, message: 'After-sales service processed successfully' });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
/**
* 完成订单
* POST /api/v1/orders/:id/complete
*/
static async completeOrder(req: Request, res: Response) {
try {
const { id } = req.params;
const { tenantId } = (req as any).traceContext || { tenantId: 'default-tenant' };
await OrderService.completeOrder(tenantId, id);
res.json({ success: true, message: 'Order completed successfully' });
} catch (err: any) {
res.status(500).json({ success: false, error: err.message });
}
}
}