2026-03-17 22:07:19 +08:00
|
|
|
import { Router } from 'express';
|
|
|
|
|
import { requirePermission } from '../../core/guards/rbac.guard';
|
|
|
|
|
import { requireTraceContext } from '../../core/guards/trace-context.guard';
|
|
|
|
|
import { AutonomousSettlementService } from '../../core/pipeline/AutonomousSettlementService';
|
|
|
|
|
import { SourcingRoutingService } from '../../domains/Trade/SourcingRoutingService';
|
|
|
|
|
import { TradeService } from '../../domains/Trade/TradeService';
|
2026-03-25 13:46:26 +08:00
|
|
|
import { AutonomousEcoService } from '../../services/core/AutonomousEcoService';
|
|
|
|
|
import { AutonomousSourcingService } from '../../services/core/AutonomousSourcingService';
|
|
|
|
|
import { DecentralizedArbitrationService } from '../../services/core/DecentralizedArbitrationService';
|
|
|
|
|
import { EcoValueSharingService } from '../../services/core/EcoValueSharingService';
|
|
|
|
|
import { FulfillmentConsensusService } from '../../services/core/FulfillmentConsensusService';
|
|
|
|
|
import { InventoryService } from '../../services/inventory/InventoryService';
|
|
|
|
|
import { SovereigntyReputationService } from '../../services/sovereignty/SovereigntyReputationService';
|
|
|
|
|
import { WarehouseService } from '../../services/inventory/WarehouseService';
|
2026-03-17 22:07:19 +08:00
|
|
|
|
|
|
|
|
const router = Router();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BIZ_SOV_05] 发布匿名声誉评价
|
|
|
|
|
*/
|
|
|
|
|
router.post('/reputation/publish', requireTraceContext, requirePermission('trade:write'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId, traceId } = (req as any).traceContext;
|
|
|
|
|
const { targetEntityId, rating, feedback, orderVolume } = req.body;
|
|
|
|
|
|
|
|
|
|
await SovereigntyReputationService.publishAnonymousRating(
|
|
|
|
|
tenantId, targetEntityId, rating, feedback, traceId, orderVolume
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
res.json({ success: true, message: 'Reputation published anonymously' });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BIZ_SOV_05] 获取实体声誉报告
|
|
|
|
|
*/
|
|
|
|
|
router.get('/reputation/:entityId/report', requireTraceContext, requirePermission('trade:read'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { entityId } = req.params;
|
2026-03-18 13:38:05 +08:00
|
|
|
const report = await SovereigntyReputationService.getReputationReport(entityId as string);
|
2026-03-17 22:07:19 +08:00
|
|
|
|
|
|
|
|
if (!report) {
|
|
|
|
|
return res.status(404).json({ success: false, error: 'No reputation data found for this entity' });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res.json({ success: true, data: report });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BIZ_SOV_02] 去中心化贸易纠纷仲裁
|
|
|
|
|
*/
|
|
|
|
|
router.post('/arbitration/start', requireTraceContext, requirePermission('trade:admin'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId, traceId } = (req as any).traceContext;
|
|
|
|
|
const { disputeId } = req.body;
|
|
|
|
|
await DecentralizedArbitrationService.startArbitration(tenantId, disputeId, traceId);
|
|
|
|
|
res.json({ success: true, message: 'Decentralized arbitration process started' });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
router.get('/arbitration/history', requireTraceContext, requirePermission('trade:read'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
const history = await DecentralizedArbitrationService.getArbitrationHistory(tenantId);
|
|
|
|
|
res.json({ success: true, data: history });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BIZ_TRADE_20] 物流履约去中心化共识
|
|
|
|
|
*/
|
|
|
|
|
router.post('/fulfillment/consensus/event', requireTraceContext, requirePermission('trade:write'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { traceId } = (req as any).traceContext;
|
|
|
|
|
const { orderId, nodeId, eventType, signature } = req.body;
|
|
|
|
|
await FulfillmentConsensusService.registerNodeEvent(orderId, nodeId, eventType, signature, traceId);
|
|
|
|
|
res.json({ success: true, message: 'Node verification event registered' });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
router.get('/fulfillment/:orderId/consensus', requireTraceContext, requirePermission('trade:read'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { orderId } = req.params;
|
2026-03-18 13:38:05 +08:00
|
|
|
const chain = await FulfillmentConsensusService.getConsensusChain(orderId as string);
|
2026-03-17 22:07:19 +08:00
|
|
|
res.json({ success: true, data: chain });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BIZ_SUP_15] 多源供应比价与路由优化 (Sourcing Optimization)
|
|
|
|
|
*/
|
|
|
|
|
router.post('/sourcing/optimize', requireTraceContext, requirePermission('trade:read'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
const { productTitle, imageUrl, targetQuantity, maxDays, priority } = req.body;
|
|
|
|
|
|
|
|
|
|
const result = await SourcingRoutingService.optimizeSourcing({
|
|
|
|
|
productTitle,
|
|
|
|
|
imageUrl,
|
|
|
|
|
targetQuantity,
|
|
|
|
|
maxDays,
|
|
|
|
|
priority,
|
|
|
|
|
tenantId
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
res.json({ success: true, data: result });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BIZ_ECO_03] 自治寻源与自动签约
|
|
|
|
|
*/
|
|
|
|
|
router.post('/sourcing/autonomous/start', requireTraceContext, requirePermission('trade:write'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId, traceId } = (req as any).traceContext;
|
|
|
|
|
const { category } = req.body;
|
|
|
|
|
await AutonomousSourcingService.startSourcing(tenantId, category, traceId);
|
|
|
|
|
res.json({ success: true, message: 'Autonomous sourcing and contracting triggered' });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
router.get('/sourcing/contracts', requireTraceContext, requirePermission('trade:read'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
const contracts = await AutonomousEcoService.getContracts(tenantId);
|
|
|
|
|
res.json({ success: true, data: contracts });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BIZ_ECO_02] 供应商价值共享博弈
|
|
|
|
|
*/
|
|
|
|
|
router.post('/eco/share/calculate', requireTraceContext, requirePermission('trade:admin'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId, traceId } = (req as any).traceContext;
|
|
|
|
|
const { supplierId, totalProfit } = req.body;
|
|
|
|
|
const amount = await EcoValueSharingService.calculateAndShare(tenantId, supplierId, totalProfit, traceId);
|
|
|
|
|
res.json({ success: true, data: { sharedAmount: amount } });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
router.get('/eco/share/history', requireTraceContext, requirePermission('trade:read'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
const history = await EcoValueSharingService.getSharingHistory(tenantId);
|
2026-03-18 13:38:05 +08:00
|
|
|
res.json({ success: true, data: history });
|
2026-03-17 22:07:19 +08:00
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BIZ_SOV_10] 触发自治结算
|
|
|
|
|
*/
|
|
|
|
|
router.post('/settlement/trigger', requireTraceContext, requirePermission('finance:write'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
const { targetTenantId, amount, currency, fulfillmentHash } = req.body;
|
|
|
|
|
const settlementId = await AutonomousSettlementService.triggerSettlement({
|
|
|
|
|
sourceTenantId: tenantId, targetTenantId, amount, currency, fulfillmentHash
|
|
|
|
|
});
|
|
|
|
|
res.json({ success: true, data: { settlementId } });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [CORE_TRADE_01] 获取仓库列表
|
|
|
|
|
*/
|
|
|
|
|
router.get('/warehouses', requireTraceContext, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
2026-03-18 13:38:05 +08:00
|
|
|
const warehouses = await WarehouseService.listByTenant(tenantId as string);
|
2026-03-17 22:07:19 +08:00
|
|
|
res.json({ success: true, data: warehouses });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [CORE_TRADE_01] 创建仓库
|
|
|
|
|
*/
|
|
|
|
|
router.post('/warehouses', requireTraceContext, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
const { id, name, type, countryCode, address } = req.body;
|
|
|
|
|
|
2026-03-18 13:38:05 +08:00
|
|
|
await WarehouseService.createWarehouse({
|
|
|
|
|
id,
|
|
|
|
|
tenantId: tenantId as string,
|
|
|
|
|
name,
|
|
|
|
|
type,
|
|
|
|
|
countryCode,
|
|
|
|
|
address,
|
|
|
|
|
isActive: true
|
2026-03-17 22:07:19 +08:00
|
|
|
});
|
|
|
|
|
|
2026-03-18 13:38:05 +08:00
|
|
|
res.json({ success: true, data: { warehouseId: id } });
|
2026-03-17 22:07:19 +08:00
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BIZ_TRADE_02] 创建调拨单
|
|
|
|
|
*/
|
|
|
|
|
router.post('/transfers', requireTraceContext, async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId, traceId } = (req as any).traceContext;
|
|
|
|
|
const { productId, skuId, fromWarehouseId, toWarehouseId, quantity } = req.body;
|
|
|
|
|
|
|
|
|
|
const transferId = await TradeService.createTransferOrder({
|
|
|
|
|
tenantId,
|
|
|
|
|
productId,
|
|
|
|
|
skuId,
|
|
|
|
|
fromWarehouseId,
|
|
|
|
|
toWarehouseId,
|
|
|
|
|
quantity,
|
|
|
|
|
traceId
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
res.json({ success: true, data: { transferId } });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [FE_INV_01] 全球库存分布热力图 API
|
|
|
|
|
*/
|
|
|
|
|
router.get('/inventory/hotmap', requireTraceContext, requirePermission('inventory:read'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
const data = await InventoryService.getInventoryHotmap(tenantId);
|
|
|
|
|
res.json({ success: true, data });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-25 13:46:26 +08:00
|
|
|
import { B2BTradeService } from '../../services/core/B2BTradeService';
|
2026-03-18 09:38:09 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BE-TOB001] 阶梯价计算接口
|
|
|
|
|
*/
|
|
|
|
|
router.post('/b2b/tiered-price', requireTraceContext, requirePermission('trade:read'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
|
|
|
|
const { productId, quantity, customerId } = req.body;
|
|
|
|
|
|
2026-03-18 13:38:05 +08:00
|
|
|
const result = await B2BTradeService.calculateTieredPrice({
|
2026-03-18 09:38:09 +08:00
|
|
|
productId,
|
|
|
|
|
quantity,
|
|
|
|
|
customerId,
|
2026-03-18 13:38:05 +08:00
|
|
|
tenantId: tenantId as string,
|
|
|
|
|
shopId: shopId as string,
|
|
|
|
|
traceId,
|
|
|
|
|
businessType: 'TOB'
|
|
|
|
|
});
|
2026-03-18 09:38:09 +08:00
|
|
|
|
|
|
|
|
res.json({ success: true, data: result });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BE-TOB001] 信用额度检查
|
|
|
|
|
*/
|
|
|
|
|
router.get('/b2b/credit/:customerId', requireTraceContext, requirePermission('trade:read'), async (req, res) => {
|
|
|
|
|
try {
|
2026-03-18 13:38:05 +08:00
|
|
|
const { tenantId, traceId } = (req as any).traceContext;
|
2026-03-18 09:38:09 +08:00
|
|
|
const { customerId } = req.params;
|
|
|
|
|
|
2026-03-18 13:38:05 +08:00
|
|
|
const result = await B2BTradeService.checkCreditLimit(customerId as string, tenantId as string, traceId as string);
|
2026-03-18 09:38:09 +08:00
|
|
|
res.json({ success: true, data: result });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BE-TOB002] 批量订单处理
|
|
|
|
|
*/
|
|
|
|
|
router.post('/b2b/batch-order', requireTraceContext, requirePermission('trade:write'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
|
|
|
|
const { customerId, items } = req.body;
|
|
|
|
|
|
|
|
|
|
const result = await B2BTradeService.createBatchOrder(
|
|
|
|
|
customerId,
|
|
|
|
|
items,
|
|
|
|
|
{ tenantId, shopId, traceId, businessType: 'TOB' }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
res.json({ success: true, data: result });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BE-TOB002] 获取批量订单详情
|
|
|
|
|
*/
|
|
|
|
|
router.get('/b2b/batch-order/:orderId', requireTraceContext, requirePermission('trade:read'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
const { orderId } = req.params;
|
|
|
|
|
|
2026-03-18 13:38:05 +08:00
|
|
|
const result = await B2BTradeService.getBatchOrderById(orderId as string, tenantId as string);
|
2026-03-18 09:38:09 +08:00
|
|
|
if (!result) {
|
|
|
|
|
return res.status(404).json({ success: false, error: 'Order not found' });
|
|
|
|
|
}
|
|
|
|
|
res.json({ success: true, data: result });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BE-TOB002] 更新支付状态
|
|
|
|
|
*/
|
|
|
|
|
router.put('/b2b/batch-order/:orderId/payment-status', requireTraceContext, requirePermission('trade:write'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
|
|
|
|
const { orderId } = req.params;
|
|
|
|
|
const { status } = req.body;
|
|
|
|
|
|
2026-03-18 13:38:05 +08:00
|
|
|
await B2BTradeService.updatePaymentStatus(orderId as string, status, { tenantId: tenantId as string, shopId: shopId as string, traceId });
|
2026-03-18 09:38:09 +08:00
|
|
|
res.json({ success: true, message: 'Payment status updated' });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BE-TOB003] 账期管理 - 设置账期
|
|
|
|
|
*/
|
|
|
|
|
router.post('/b2b/payment-terms', requireTraceContext, requirePermission('trade:write'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
|
|
|
|
const { customerId, days, autoApprove } = req.body;
|
|
|
|
|
|
|
|
|
|
const result = await B2BTradeService.setPaymentTerms(
|
|
|
|
|
customerId,
|
|
|
|
|
{ days, autoApprove },
|
2026-03-18 13:38:05 +08:00
|
|
|
{ tenantId: tenantId as string, traceId }
|
2026-03-18 09:38:09 +08:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
res.json({ success: true, data: result });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BE-TOB003] 检查逾期订单
|
|
|
|
|
*/
|
|
|
|
|
router.post('/b2b/check-overdue', requireTraceContext, requirePermission('trade:admin'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
const overdueIds = await B2BTradeService.checkOverdueOrders(tenantId);
|
|
|
|
|
res.json({ success: true, data: { overdueCount: overdueIds.length, overdueIds } });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BE-TOB] 创建B2B客户
|
|
|
|
|
*/
|
|
|
|
|
router.post('/b2b/customers', requireTraceContext, requirePermission('trade:write'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
|
|
|
|
const customerData = req.body;
|
|
|
|
|
|
|
|
|
|
const customerId = await B2BTradeService.createCustomer(customerData, { tenantId, shopId, traceId });
|
|
|
|
|
res.json({ success: true, data: { customerId } });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BE-TOB] 创建阶梯价格
|
|
|
|
|
*/
|
|
|
|
|
router.post('/b2b/tiered-prices', requireTraceContext, requirePermission('trade:write'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
|
|
|
|
const priceData = req.body;
|
|
|
|
|
|
|
|
|
|
const priceId = await B2BTradeService.createTieredPrice(priceData, { tenantId, shopId, traceId });
|
|
|
|
|
res.json({ success: true, data: { priceId } });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BE-TOB] 初始化B2B数据库表
|
|
|
|
|
*/
|
|
|
|
|
router.post('/b2b/init-tables', requireTraceContext, requirePermission('admin'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
await B2BTradeService.initTables();
|
|
|
|
|
res.json({ success: true, message: 'B2B tables initialized' });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-25 13:46:26 +08:00
|
|
|
import { LogisticsService } from '../../services/logistics/LogisticsService';
|
2026-03-18 09:38:09 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BE-LOG001] 物流策略引擎 - 获取物流策略
|
|
|
|
|
*/
|
|
|
|
|
router.post('/logistics/strategy', requireTraceContext, requirePermission('trade:read'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
|
|
|
|
const { orderId, destination } = req.body;
|
|
|
|
|
|
|
|
|
|
const result = await LogisticsService.getLogisticsStrategy({
|
|
|
|
|
orderId,
|
|
|
|
|
destination,
|
|
|
|
|
tenantId,
|
|
|
|
|
shopId,
|
|
|
|
|
traceId,
|
|
|
|
|
businessType: 'TOC',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
res.json({ success: true, data: result });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BE-LOG002] 渠道选择算法 - 选择物流渠道
|
|
|
|
|
*/
|
|
|
|
|
router.post('/logistics/channel', requireTraceContext, requirePermission('trade:write'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
|
|
|
|
const { orderId, logisticsOption } = req.body;
|
|
|
|
|
|
|
|
|
|
const result = await LogisticsService.selectChannel(
|
|
|
|
|
orderId,
|
|
|
|
|
logisticsOption,
|
|
|
|
|
{ tenantId, shopId, traceId }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
res.json({ success: true, data: result });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BE-LOG003] 运费计算接口 - 计算运费
|
|
|
|
|
*/
|
|
|
|
|
router.post('/logistics/freight', requireTraceContext, requirePermission('trade:read'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
const { tenantId, shopId, traceId } = (req as any).traceContext;
|
|
|
|
|
const { productId, quantity, destination } = req.body;
|
|
|
|
|
|
|
|
|
|
const result = await LogisticsService.calculateFreight(
|
|
|
|
|
productId,
|
|
|
|
|
quantity,
|
|
|
|
|
destination,
|
|
|
|
|
{ tenantId, shopId, traceId }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
res.json({ success: true, data: result });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* [BE-LOG] 初始化物流数据库表
|
|
|
|
|
*/
|
|
|
|
|
router.post('/logistics/init-tables', requireTraceContext, requirePermission('admin'), async (req, res) => {
|
|
|
|
|
try {
|
|
|
|
|
await LogisticsService.initTables();
|
|
|
|
|
res.json({ success: true, message: 'Logistics tables initialized' });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2026-03-17 22:07:19 +08:00
|
|
|
export default router;
|