2026-03-19 01:39:34 +08:00
|
|
|
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';
|
2026-03-17 22:07:19 +08:00
|
|
|
|
|
|
|
|
const router = Router();
|
|
|
|
|
|
2026-03-19 01:39:34 +08:00
|
|
|
router.get('/strategies', authenticate, async (req: Request, res: Response) => {
|
|
|
|
|
try {
|
|
|
|
|
const { category, isActive, isFeatured, limit, offset } = req.query;
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
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'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
2026-03-17 22:07:19 +08:00
|
|
|
|
|
|
|
|
export default router;
|