feat: 添加MSW模拟服务和数据源集成
refactor: 重构页面组件移除冗余Layout组件 feat: 实现WebSocket和事件总线系统 feat: 添加队列和调度系统 docs: 更新架构文档和服务映射 style: 清理重复接口定义使用数据源 chore: 更新依赖项配置 feat: 添加运行时系统和领域引导 ci: 配置ESLint边界检查规则 build: 添加Redis和WebSocket依赖 test: 添加MSW浏览器环境入口 perf: 优化数据获取逻辑使用统一数据源 fix: 修复类型定义和状态管理问题
This commit is contained in:
@@ -1,47 +1,386 @@
|
||||
import { Router } from 'express';
|
||||
import { BizStrategyController } from '../controllers/BizStrategyController';
|
||||
import { requireTraceContext } from '../../core/guards/trace-context.guard';
|
||||
import { requirePermission } from '../../core/guards/rbac.guard';
|
||||
import { Router, Request, Response } from 'express';
|
||||
import StrategyService from '../../services/StrategyService';
|
||||
import StrategyRecommendationService from '../../services/StrategyRecommendationService';
|
||||
import { logger } from '../../utils/logger';
|
||||
import { authenticate, authorize } from '../middleware/auth';
|
||||
|
||||
const router = Router();
|
||||
|
||||
/**
|
||||
* [BIZ_AI_16] 交互式策略建议 (Actionable Insights)
|
||||
*/
|
||||
router.get('/advice', requireTraceContext, requirePermission('strategy:read'), BizStrategyController.getAdvice);
|
||||
router.post('/advice/approve', requireTraceContext, requirePermission('strategy:execute'), BizStrategyController.approveAdvice);
|
||||
router.get('/advice/:adviceId/explanation', requireTraceContext, requirePermission('strategy:read'), BizStrategyController.getAdviceExplanation);
|
||||
router.get('/strategies', authenticate, async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { category, isActive, isFeatured, limit, offset } = req.query;
|
||||
|
||||
/**
|
||||
* [UX_IAT_01] 自治控制中心:停机与模式切换
|
||||
*/
|
||||
router.post('/kill-switch', requireTraceContext, requirePermission('strategy:kill'), BizStrategyController.toggleKillSwitch);
|
||||
router.post('/mode', requireTraceContext, requirePermission('strategy:write'), BizStrategyController.updateAutonomousMode);
|
||||
router.get('/status', requireTraceContext, BizStrategyController.getAutonomousStatus);
|
||||
const result = await StrategyService.getAllStrategies({
|
||||
category: category as string,
|
||||
isActive: isActive === 'true' ? true : isActive === 'false' ? false : undefined,
|
||||
isFeatured: isFeatured === 'true' ? true : isFeatured === 'false' ? false : undefined,
|
||||
limit: limit ? parseInt(limit as string) : undefined,
|
||||
offset: offset ? parseInt(offset as string) : undefined
|
||||
});
|
||||
|
||||
/**
|
||||
* [FE_SB_01] 策略仿真沙盒 (Sandbox Dashboard)
|
||||
*/
|
||||
router.post('/sandbox/run', requireTraceContext, requirePermission('strategy:write'), BizStrategyController.runSandboxSimulation);
|
||||
router.get('/sandbox/results', requireTraceContext, requirePermission('strategy:read'), BizStrategyController.getSandboxResults);
|
||||
res.json({
|
||||
success: true,
|
||||
data: result.strategies,
|
||||
total: result.total
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[StrategyRoutes] Error fetching strategies:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to fetch strategies'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* [BIZ_SC_11] 供应链询盘流 (Semi-Inquiry)
|
||||
*/
|
||||
router.post('/inquiry/start', requireTraceContext, BizStrategyController.startInquiry);
|
||||
router.post('/inquiry/:inquiryId/review', requireTraceContext, BizStrategyController.reviewInquiry);
|
||||
router.post('/inquiry/:inquiryId/send', requireTraceContext, BizStrategyController.sendInquiry);
|
||||
router.post('/inquiry/:inquiryId/accept', requireTraceContext, BizStrategyController.acceptAndPurchase);
|
||||
router.get('/strategies/featured', authenticate, async (req: Request, res: Response) => {
|
||||
try {
|
||||
const limit = req.query.limit ? parseInt(req.query.limit as string) : 5;
|
||||
const strategies = await StrategyService.getFeaturedStrategies(limit);
|
||||
|
||||
/**
|
||||
* [BIZ_TRADE_02] 多仓库存编排 (Inventory Orchestration)
|
||||
*/
|
||||
router.get('/inventory/orchestrate', requireTraceContext, BizStrategyController.orchestrateInventory);
|
||||
router.post('/inventory/transfer', requireTraceContext, BizStrategyController.approveTransfer);
|
||||
res.json({
|
||||
success: true,
|
||||
data: strategies
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[StrategyRoutes] Error fetching featured strategies:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to fetch featured strategies'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* [BIZ_AIS_01] 独立站套利回流 (Pixel Feedback)
|
||||
*/
|
||||
router.post('/pixel/event', requireTraceContext, BizStrategyController.collectPixelEvent);
|
||||
router.get('/strategies/trending', authenticate, async (req: Request, res: Response) => {
|
||||
try {
|
||||
const limit = req.query.limit ? parseInt(req.query.limit as string) : 5;
|
||||
const strategies = await StrategyRecommendationService.getTrendingStrategies(limit);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: strategies
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[StrategyRoutes] Error fetching trending strategies:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to fetch trending strategies'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/strategies/search', authenticate, async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { q } = req.query;
|
||||
|
||||
if (!q || typeof q !== 'string') {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Search query is required'
|
||||
});
|
||||
}
|
||||
|
||||
const strategies = await StrategyService.searchStrategies(q);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: strategies
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[StrategyRoutes] Error searching strategies:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to search strategies'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/strategies/category/:category', authenticate, async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { category } = req.params;
|
||||
const strategies = await StrategyService.getStrategiesByCategory(category);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: strategies
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[StrategyRoutes] Error fetching strategies by category:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to fetch strategies by category'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/strategies/:id', authenticate, async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const strategy = await StrategyService.getStrategyById(id);
|
||||
|
||||
if (!strategy) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Strategy not found'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: strategy
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[StrategyRoutes] Error fetching strategy:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to fetch strategy'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/strategies/:id/similar', authenticate, async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const strategies = await StrategyRecommendationService.getSimilarStrategies(id);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: strategies
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[StrategyRoutes] Error fetching similar strategies:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to fetch similar strategies'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/strategies', authenticate, authorize('strategy:create'), async (req: Request, res: Response) => {
|
||||
try {
|
||||
const strategy = await StrategyService.createStrategy({
|
||||
...req.body,
|
||||
created_by: (req as any).user?.id || 'system'
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: strategy,
|
||||
message: 'Strategy created successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[StrategyRoutes] Error creating strategy:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to create strategy'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/strategies/:id/activate', authenticate, async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const tenantId = (req as any).user?.tenantId;
|
||||
|
||||
if (!tenantId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Tenant ID not found'
|
||||
});
|
||||
}
|
||||
|
||||
const merchantStrategy = await StrategyService.activateStrategy({
|
||||
tenantId,
|
||||
strategyId: id,
|
||||
config: req.body.config
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: merchantStrategy,
|
||||
message: 'Strategy activated successfully'
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[StrategyRoutes] Error activating strategy:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: error.message || 'Failed to activate strategy'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/my-strategies', authenticate, async (req: Request, res: Response) => {
|
||||
try {
|
||||
const tenantId = (req as any).user?.tenantId;
|
||||
const { status } = req.query;
|
||||
|
||||
if (!tenantId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Tenant ID not found'
|
||||
});
|
||||
}
|
||||
|
||||
const strategies = await StrategyService.getMerchantStrategies(
|
||||
tenantId,
|
||||
status as 'ACTIVE' | 'PAUSED' | 'COMPLETED' | 'FAILED'
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: strategies
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[StrategyRoutes] Error fetching merchant strategies:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to fetch merchant strategies'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/my-strategies/:id/pause', authenticate, async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const tenantId = (req as any).user?.tenantId;
|
||||
|
||||
if (!tenantId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Tenant ID not found'
|
||||
});
|
||||
}
|
||||
|
||||
await StrategyService.pauseStrategy(id, tenantId);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Strategy paused successfully'
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[StrategyRoutes] Error pausing strategy:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: error.message || 'Failed to pause strategy'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/my-strategies/:id/resume', authenticate, async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const tenantId = (req as any).user?.tenantId;
|
||||
|
||||
if (!tenantId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Tenant ID not found'
|
||||
});
|
||||
}
|
||||
|
||||
await StrategyService.resumeStrategy(id, tenantId);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Strategy resumed successfully'
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[StrategyRoutes] Error resuming strategy:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: error.message || 'Failed to resume strategy'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/my-strategies/:id/complete', authenticate, async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const tenantId = (req as any).user?.tenantId;
|
||||
const { roi, revenue } = req.body;
|
||||
|
||||
if (!tenantId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Tenant ID not found'
|
||||
});
|
||||
}
|
||||
|
||||
if (roi === undefined || revenue === undefined) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'ROI and revenue are required'
|
||||
});
|
||||
}
|
||||
|
||||
await StrategyService.completeStrategy(id, tenantId, { roi, revenue });
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Strategy completed successfully'
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[StrategyRoutes] Error completing strategy:', error);
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: error.message || 'Failed to complete strategy'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/recommendations', authenticate, async (req: Request, res: Response) => {
|
||||
try {
|
||||
const tenantId = (req as any).user?.tenantId;
|
||||
|
||||
if (!tenantId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Tenant ID not found'
|
||||
});
|
||||
}
|
||||
|
||||
const recommendations = await StrategyRecommendationService.getPersonalizedRecommendations(tenantId);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: recommendations
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[StrategyRoutes] Error fetching recommendations:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to fetch recommendations'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/recommendations/category/:category', authenticate, async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { category } = req.params;
|
||||
const tenantId = (req as any).user?.tenantId;
|
||||
|
||||
if (!tenantId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Tenant ID not found'
|
||||
});
|
||||
}
|
||||
|
||||
const recommendations = await StrategyRecommendationService.getCategoryRecommendations(
|
||||
category,
|
||||
tenantId
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: recommendations
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('[StrategyRoutes] Error fetching category recommendations:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to fetch category recommendations'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
||||
Reference in New Issue
Block a user