/** * [BE-CTL-019] 物流决策AI控制器 * 提供运费审核和物流延误的AI介入RESTful API接口 * AI注意: 所有物流AI决策必须通过此控制器 */ import { Request, Response } from 'express'; import { makeAIDecision, DecisionType, DecisionResult, } from '../middleware/AIDecisionMiddleware'; import { logger } from '../../utils/logger'; export class LogisticsDecisionController { /** * POST /api/v1/logistics/ai-decision * 生成物流AI决策 */ static async makeDecision(req: Request, res: Response) { try { const { tenantId, operatorId, orderId, operationType, customParams } = req.body; if (!tenantId || !operatorId) { return res.status(400).json({ success: false, error: 'MISSING_REQUIRED_FIELDS', message: '缺少必要字段', }); } const aiDecision = makeAIDecision({ tenantId, operatorId, businessType: DecisionType.FREIGHT, operationType: operationType || 'FREIGHT_AUDIT', targetIds: orderId ? [orderId] : [], customParams, }); logger.info(`[LogisticsDecisionController] AI decision: ${aiDecision.requiredAction}`); return res.json({ success: true, data: aiDecision, }); } catch (error) { logger.error('[LogisticsDecisionController] makeDecision error:', error); return res.status(500).json({ success: false, error: 'DECISION_ERROR', message: '物流AI决策失败', }); } } /** * POST /api/v1/logistics/freight/audit * 审核运费 */ static async auditFreight(req: Request, res: Response) { try { const { tenantId, orderId, expectedFreight, actualFreight, customParams } = req.body; if (!tenantId || !orderId) { return res.status(400).json({ success: false, error: 'MISSING_REQUIRED_FIELDS', message: '缺少必要字段', }); } const diff = actualFreight - expectedFreight; const diffPercent = expectedFreight > 0 ? Math.abs(diff / expectedFreight) * 100 : 0; const aiDecision = makeAIDecision({ tenantId, operatorId: 'system', businessType: DecisionType.FREIGHT, operationType: 'FREIGHT_AUDIT', targetIds: [orderId], customParams: { ...customParams, expectedFreight, actualFreight, diff, diffPercent, isAbnormal: diffPercent > 15, }, }); if (aiDecision.requiredAction === DecisionResult.HUMAN_ONLY) { return res.status(403).json({ success: false, error: 'AI_NOT_ALLOWED', message: '此运费审核仅支持人工处理', aiDecision, }); } const action = diffPercent <= 5 ? 'AUTO_PASS' : diffPercent <= 15 ? 'NEED_REVIEW' : 'NEED_HUMAN_REVIEW'; logger.info(`[LogisticsDecisionController] Freight audit: ${orderId}, action: ${action}`); return res.json({ success: true, data: { orderId, expectedFreight, actualFreight, diff, diffPercent, action, aiDecision, }, }); } catch (error) { logger.error('[LogisticsDecisionController] auditFreight error:', error); return res.status(500).json({ success: false, error: 'AUDIT_ERROR', message: '运费审核失败', }); } } /** * POST /api/v1/logistics/delay/warn * 物流延误预警 */ static async warnDelay(req: Request, res: Response) { try { const { tenantId, orderId, expectedDeliveryDate, currentStatus, delayRisk } = req.body; if (!tenantId || !orderId) { return res.status(400).json({ success: false, error: 'MISSING_REQUIRED_FIELDS', message: '缺少必要字段', }); } const aiDecision = makeAIDecision({ tenantId, operatorId: 'system', businessType: DecisionType.FREIGHT, operationType: 'DELAY_WARNING', targetIds: [orderId], customParams: { expectedDeliveryDate, currentStatus, delayRisk, }, }); logger.info(`[LogisticsDecisionController] Delay warning: ${orderId}, risk: ${delayRisk}`); return res.json({ success: true, data: { orderId, expectedDeliveryDate, currentStatus, delayRisk, aiDecision, recommendedAction: delayRisk > 0.7 ? 'NOTIFY_BUYER' : 'MONITOR', }, }); } catch (error) { logger.error('[LogisticsDecisionController] warnDelay error:', error); return res.status(500).json({ success: false, error: 'WARN_ERROR', message: '延误预警失败', }); } } /** * GET /api/v1/logistics/trace/:orderId * 追踪物流 */ static async traceLogistics(req: Request, res: Response) { try { const { orderId } = req.params; if (!orderId) { return res.status(400).json({ success: false, error: 'MISSING_ORDER_ID', message: '缺少订单ID', }); } return res.json({ success: true, data: { orderId, status: 'IN_TRANSIT', events: [ { timestamp: new Date(), location: 'Warehouse', status: 'SHIPPED' }, { timestamp: new Date(), location: 'Distribution Center', status: 'IN_TRANSIT' }, ], estimatedDelivery: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000), }, }); } catch (error) { logger.error('[LogisticsDecisionController] traceLogistics error:', error); return res.status(500).json({ success: false, error: 'TRACE_ERROR', message: '物流追踪失败', }); } } }