Files
makemd/server/src/api/controllers/LogisticsDecisionController.ts

218 lines
5.8 KiB
TypeScript
Raw Normal View History

/**
* [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: '物流追踪失败',
});
}
}
}