refactor(services): 重构服务文件结构,将服务按功能分类到不同目录
- 将服务文件按功能分类到core、ai、analytics、security等目录 - 修复logger导入路径问题,统一使用相对路径 - 更新相关文件的导入路径引用 - 添加新的批量操作组件导出文件 - 修复dashboard页面中的类型错误 - 添加dotenv依赖到package.json
This commit is contained in:
217
server/src/api/controllers/LogisticsDecisionController.ts
Normal file
217
server/src/api/controllers/LogisticsDecisionController.ts
Normal file
@@ -0,0 +1,217 @@
|
||||
/**
|
||||
* [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: '物流追踪失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user