2026-03-20 09:43:50 +08:00
|
|
|
import { Request, Response } from 'express';
|
2026-03-20 17:53:46 +08:00
|
|
|
import { WinNodeService } from '../../services/WinNodeService';
|
|
|
|
|
import { TaskCenterService } from '../../services/TaskCenterService';
|
|
|
|
|
import { IndependentSiteService } from '../../services/IndependentSiteService';
|
|
|
|
|
import { CostTemplateService } from '../../services/CostTemplateService';
|
|
|
|
|
import { PlatformAccountService } from '../../services/PlatformAccountService';
|
|
|
|
|
import { ReturnService } from '../../services/ReturnService';
|
|
|
|
|
import { AuditService } from '../../services/AuditService';
|
|
|
|
|
import { logger } from '../../utils/logger';
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
export class WinNodeController {
|
|
|
|
|
static async list(req: Request, res: Response) {
|
|
|
|
|
const { status, shopId } = req.query;
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
2026-03-20 17:53:46 +08:00
|
|
|
const statusStr = Array.isArray(status) ? status[0] : status;
|
|
|
|
|
const shopIdStr = Array.isArray(shopId) ? shopId[0] : shopId;
|
|
|
|
|
|
|
|
|
|
const nodes = await WinNodeService.list(tenantId, { status: statusStr as string, shopId: shopIdStr as string });
|
2026-03-20 09:43:50 +08:00
|
|
|
res.json({ success: true, data: nodes });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[WinNodeController] List failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async getById(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const node = await WinNodeService.getById(id);
|
|
|
|
|
if (!node) {
|
|
|
|
|
res.status(404).json({ success: false, error: 'Node not found' });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
res.json({ success: true, data: node });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[WinNodeController] Get by id failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async create(req: Request, res: Response) {
|
|
|
|
|
const { tenantId, userId, traceId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const node = await WinNodeService.create(tenantId, req.body);
|
|
|
|
|
await AuditService.log({
|
|
|
|
|
tenantId,
|
|
|
|
|
shopId: req.body.shopId,
|
|
|
|
|
taskId: '',
|
|
|
|
|
traceId,
|
|
|
|
|
userId,
|
|
|
|
|
module: 'WINNODE',
|
|
|
|
|
action: 'CREATE',
|
|
|
|
|
resourceType: 'winnode',
|
|
|
|
|
metadata: { nodeId: node.id, name: node.name },
|
|
|
|
|
result: 'success',
|
|
|
|
|
source: 'console',
|
|
|
|
|
});
|
|
|
|
|
res.json({ success: true, data: node });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[WinNodeController] Create failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async update(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
const { tenantId, userId, traceId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const node = await WinNodeService.update(id, req.body);
|
|
|
|
|
await AuditService.log({
|
|
|
|
|
tenantId,
|
|
|
|
|
shopId: node.shopId,
|
|
|
|
|
taskId: '',
|
|
|
|
|
traceId,
|
|
|
|
|
userId,
|
|
|
|
|
module: 'WINNODE',
|
|
|
|
|
action: 'UPDATE',
|
|
|
|
|
resourceType: 'winnode',
|
|
|
|
|
metadata: { nodeId: id },
|
|
|
|
|
result: 'success',
|
|
|
|
|
source: 'console',
|
|
|
|
|
});
|
|
|
|
|
res.json({ success: true, data: node });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[WinNodeController] Update failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async delete(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
const { tenantId, userId, traceId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await WinNodeService.delete(id);
|
|
|
|
|
await AuditService.log({
|
|
|
|
|
tenantId,
|
|
|
|
|
shopId: '',
|
|
|
|
|
taskId: '',
|
|
|
|
|
traceId,
|
|
|
|
|
userId,
|
|
|
|
|
module: 'WINNODE',
|
|
|
|
|
action: 'DELETE',
|
|
|
|
|
resourceType: 'winnode',
|
|
|
|
|
metadata: { nodeId: id },
|
|
|
|
|
result: 'success',
|
|
|
|
|
source: 'console',
|
|
|
|
|
});
|
|
|
|
|
res.json({ success: true });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[WinNodeController] Delete failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async testConnection(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const result = await WinNodeService.testConnection(id);
|
|
|
|
|
res.json({ success: true, data: result });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[WinNodeController] Test connection failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async restart(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const result = await WinNodeService.restart(id);
|
|
|
|
|
res.json({ success: true, data: result });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[WinNodeController] Restart failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async heartbeat(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await WinNodeService.heartbeat(id, req.body);
|
|
|
|
|
res.json({ success: true });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[WinNodeController] Heartbeat failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async getStats(req: Request, res: Response) {
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const stats = await WinNodeService.getStats(tenantId);
|
|
|
|
|
res.json({ success: true, data: stats });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[WinNodeController] Get stats failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class TaskCenterController {
|
|
|
|
|
static async list(req: Request, res: Response) {
|
|
|
|
|
const { status, taskType, shopId } = req.query;
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
2026-03-20 17:53:46 +08:00
|
|
|
const statusStr = Array.isArray(status) ? status[0] : status;
|
|
|
|
|
const taskTypeStr = Array.isArray(taskType) ? taskType[0] : taskType;
|
|
|
|
|
const shopIdStr = Array.isArray(shopId) ? shopId[0] : shopId;
|
|
|
|
|
|
2026-03-20 09:43:50 +08:00
|
|
|
const tasks = await TaskCenterService.list(tenantId, {
|
2026-03-20 17:53:46 +08:00
|
|
|
status: statusStr as string,
|
|
|
|
|
taskType: taskTypeStr as string,
|
|
|
|
|
shopId: shopIdStr as string,
|
2026-03-20 09:43:50 +08:00
|
|
|
});
|
|
|
|
|
res.json({ success: true, data: tasks });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[TaskCenterController] List failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async getById(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const task = await TaskCenterService.getById(id);
|
|
|
|
|
if (!task) {
|
|
|
|
|
res.status(404).json({ success: false, error: 'Task not found' });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
res.json({ success: true, data: task });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[TaskCenterController] Get by id failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async create(req: Request, res: Response) {
|
|
|
|
|
const { tenantId, userId, traceId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const task = await TaskCenterService.create(tenantId, { ...req.body, traceId });
|
|
|
|
|
await AuditService.log({
|
|
|
|
|
tenantId,
|
|
|
|
|
shopId: req.body.shopId,
|
|
|
|
|
taskId: task.id,
|
|
|
|
|
traceId,
|
|
|
|
|
userId,
|
|
|
|
|
module: 'TASKCENTER',
|
|
|
|
|
action: 'CREATE',
|
|
|
|
|
resourceType: 'task',
|
|
|
|
|
metadata: { taskId: task.id, taskType: task.taskType },
|
|
|
|
|
result: 'success',
|
|
|
|
|
source: 'console',
|
|
|
|
|
});
|
|
|
|
|
res.json({ success: true, data: task });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[TaskCenterController] Create failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async updateStatus(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
const { status, progress, output, error } = req.body;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const task = await TaskCenterService.updateStatus(id, status, progress, output, error);
|
|
|
|
|
res.json({ success: true, data: task });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[TaskCenterController] Update status failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async cancel(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await TaskCenterService.cancel(id);
|
|
|
|
|
res.json({ success: true });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[TaskCenterController] Cancel failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async retry(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const task = await TaskCenterService.retry(id);
|
|
|
|
|
res.json({ success: true, data: task });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[TaskCenterController] Retry failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async getStats(req: Request, res: Response) {
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const stats = await TaskCenterService.getStats(tenantId);
|
|
|
|
|
res.json({ success: true, data: stats });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[TaskCenterController] Get stats failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class IndependentSiteController {
|
|
|
|
|
static async listSites(req: Request, res: Response) {
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const sites = await IndependentSiteService.listSites(tenantId);
|
|
|
|
|
res.json({ success: true, data: sites });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[IndependentSiteController] List sites failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async getSiteById(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const site = await IndependentSiteService.getSiteById(id);
|
|
|
|
|
if (!site) {
|
|
|
|
|
res.status(404).json({ success: false, error: 'Site not found' });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
res.json({ success: true, data: site });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[IndependentSiteController] Get site failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async createSite(req: Request, res: Response) {
|
|
|
|
|
const { tenantId, userId, traceId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const site = await IndependentSiteService.createSite(tenantId, req.body);
|
|
|
|
|
await AuditService.log({
|
|
|
|
|
tenantId,
|
|
|
|
|
shopId: '',
|
|
|
|
|
taskId: '',
|
|
|
|
|
traceId,
|
|
|
|
|
userId,
|
|
|
|
|
module: 'INDEPENDENTSITE',
|
|
|
|
|
action: 'CREATE',
|
|
|
|
|
resourceType: 'site',
|
|
|
|
|
metadata: { siteId: site.id, domain: site.domain },
|
|
|
|
|
result: 'success',
|
|
|
|
|
source: 'console',
|
|
|
|
|
});
|
|
|
|
|
res.json({ success: true, data: site });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[IndependentSiteController] Create site failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async updateSite(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
const { tenantId, userId, traceId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const site = await IndependentSiteService.updateSite(id, req.body);
|
|
|
|
|
await AuditService.log({
|
|
|
|
|
tenantId,
|
|
|
|
|
shopId: '',
|
|
|
|
|
taskId: '',
|
|
|
|
|
traceId,
|
|
|
|
|
userId,
|
|
|
|
|
module: 'INDEPENDENTSITE',
|
|
|
|
|
action: 'UPDATE',
|
|
|
|
|
resourceType: 'site',
|
|
|
|
|
metadata: { siteId: id },
|
|
|
|
|
result: 'success',
|
|
|
|
|
source: 'console',
|
|
|
|
|
});
|
|
|
|
|
res.json({ success: true, data: site });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[IndependentSiteController] Update site failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async deleteSite(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
const { tenantId, userId, traceId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await IndependentSiteService.deleteSite(id);
|
|
|
|
|
await AuditService.log({
|
|
|
|
|
tenantId,
|
|
|
|
|
shopId: '',
|
|
|
|
|
taskId: '',
|
|
|
|
|
traceId,
|
|
|
|
|
userId,
|
|
|
|
|
module: 'INDEPENDENTSITE',
|
|
|
|
|
action: 'DELETE',
|
|
|
|
|
resourceType: 'site',
|
|
|
|
|
metadata: { siteId: id },
|
|
|
|
|
result: 'success',
|
|
|
|
|
source: 'console',
|
|
|
|
|
});
|
|
|
|
|
res.json({ success: true });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[IndependentSiteController] Delete site failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async syncProducts(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
const { productIds } = req.body;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const results = await IndependentSiteService.syncProducts(id, productIds);
|
|
|
|
|
res.json({ success: true, data: results });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[IndependentSiteController] Sync products failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async getSiteProducts(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const products = await IndependentSiteService.getSiteProducts(id);
|
|
|
|
|
res.json({ success: true, data: products });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[IndependentSiteController] Get products failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async getSiteOrders(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
const { status } = req.query;
|
|
|
|
|
|
|
|
|
|
try {
|
2026-03-20 17:53:46 +08:00
|
|
|
const statusStr = Array.isArray(status) ? status[0] : status;
|
|
|
|
|
const orders = await IndependentSiteService.getSiteOrders(id, { status: statusStr as string });
|
2026-03-20 09:43:50 +08:00
|
|
|
res.json({ success: true, data: orders });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[IndependentSiteController] Get orders failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async getSiteAnalytics(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
const { start, end } = req.query;
|
|
|
|
|
|
|
|
|
|
try {
|
2026-03-20 17:53:46 +08:00
|
|
|
const startStr = Array.isArray(start) ? start[0] : start;
|
|
|
|
|
const endStr = Array.isArray(end) ? end[0] : end;
|
|
|
|
|
|
2026-03-20 09:43:50 +08:00
|
|
|
const analytics = await IndependentSiteService.getSiteAnalytics(id, {
|
2026-03-20 17:53:46 +08:00
|
|
|
start: startStr as string,
|
|
|
|
|
end: endStr as string,
|
2026-03-20 09:43:50 +08:00
|
|
|
});
|
|
|
|
|
res.json({ success: true, data: analytics });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[IndependentSiteController] Get analytics failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class CostTemplateController {
|
|
|
|
|
static async list(req: Request, res: Response) {
|
|
|
|
|
const { platform, category } = req.query;
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
2026-03-20 17:53:46 +08:00
|
|
|
const platformStr = Array.isArray(platform) ? platform[0] : platform;
|
|
|
|
|
const categoryStr = Array.isArray(category) ? category[0] : category;
|
|
|
|
|
|
2026-03-20 09:43:50 +08:00
|
|
|
const templates = await CostTemplateService.list(tenantId, {
|
2026-03-20 17:53:46 +08:00
|
|
|
platform: platformStr as string,
|
|
|
|
|
category: categoryStr as string,
|
2026-03-20 09:43:50 +08:00
|
|
|
});
|
|
|
|
|
res.json({ success: true, data: templates });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[CostTemplateController] List failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async getById(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const template = await CostTemplateService.getById(id);
|
|
|
|
|
if (!template) {
|
|
|
|
|
res.status(404).json({ success: false, error: 'Template not found' });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
res.json({ success: true, data: template });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[CostTemplateController] Get failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async create(req: Request, res: Response) {
|
|
|
|
|
const { tenantId, userId, traceId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const template = await CostTemplateService.create(tenantId, req.body);
|
|
|
|
|
await AuditService.log({
|
|
|
|
|
tenantId,
|
|
|
|
|
shopId: '',
|
|
|
|
|
taskId: '',
|
|
|
|
|
traceId,
|
|
|
|
|
userId,
|
|
|
|
|
module: 'COSTTEMPLATE',
|
|
|
|
|
action: 'CREATE',
|
|
|
|
|
resourceType: 'cost_template',
|
|
|
|
|
metadata: { templateId: template.id, name: template.name },
|
|
|
|
|
result: 'success',
|
|
|
|
|
source: 'console',
|
|
|
|
|
});
|
|
|
|
|
res.json({ success: true, data: template });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[CostTemplateController] Create failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async update(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const template = await CostTemplateService.update(id, req.body);
|
|
|
|
|
res.json({ success: true, data: template });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[CostTemplateController] Update failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async delete(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await CostTemplateService.delete(id);
|
|
|
|
|
res.json({ success: true });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[CostTemplateController] Delete failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async duplicate(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const template = await CostTemplateService.duplicate(id);
|
|
|
|
|
res.json({ success: true, data: template });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[CostTemplateController] Duplicate failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async calculateCost(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
const { basePrice, quantity } = req.body;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const result = await CostTemplateService.calculateCost(id, basePrice, quantity);
|
|
|
|
|
res.json({ success: true, data: result });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[CostTemplateController] Calculate cost failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class PlatformAccountController {
|
|
|
|
|
static async list(req: Request, res: Response) {
|
|
|
|
|
const { platform, status, shopId } = req.query;
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
2026-03-20 17:53:46 +08:00
|
|
|
const platformStr = Array.isArray(platform) ? platform[0] : platform;
|
|
|
|
|
const statusStr = Array.isArray(status) ? status[0] : status;
|
|
|
|
|
const shopIdStr = Array.isArray(shopId) ? shopId[0] : shopId;
|
|
|
|
|
|
2026-03-20 09:43:50 +08:00
|
|
|
const accounts = await PlatformAccountService.list(tenantId, {
|
2026-03-20 17:53:46 +08:00
|
|
|
platform: platformStr as string,
|
|
|
|
|
status: statusStr as string,
|
|
|
|
|
shopId: shopIdStr as string,
|
2026-03-20 09:43:50 +08:00
|
|
|
});
|
|
|
|
|
res.json({ success: true, data: accounts });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[PlatformAccountController] List failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async getById(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const account = await PlatformAccountService.getById(id);
|
|
|
|
|
if (!account) {
|
|
|
|
|
res.status(404).json({ success: false, error: 'Account not found' });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
res.json({ success: true, data: account });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[PlatformAccountController] Get failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async create(req: Request, res: Response) {
|
|
|
|
|
const { tenantId, userId, traceId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const account = await PlatformAccountService.create(tenantId, req.body);
|
|
|
|
|
await AuditService.log({
|
|
|
|
|
tenantId,
|
|
|
|
|
shopId: req.body.shopId,
|
|
|
|
|
taskId: '',
|
|
|
|
|
traceId,
|
|
|
|
|
userId,
|
|
|
|
|
module: 'PLATFORMACCOUNT',
|
|
|
|
|
action: 'CREATE',
|
|
|
|
|
resourceType: 'platform_account',
|
|
|
|
|
metadata: { accountId: account.id, platform: account.platform },
|
|
|
|
|
result: 'success',
|
|
|
|
|
source: 'console',
|
|
|
|
|
});
|
|
|
|
|
res.json({ success: true, data: account });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[PlatformAccountController] Create failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async update(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const account = await PlatformAccountService.update(id, req.body);
|
|
|
|
|
res.json({ success: true, data: account });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[PlatformAccountController] Update failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async delete(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await PlatformAccountService.delete(id);
|
|
|
|
|
res.json({ success: true });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[PlatformAccountController] Delete failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async refreshToken(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const result = await PlatformAccountService.refreshToken(id);
|
|
|
|
|
res.json({ success: true, data: result });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[PlatformAccountController] Refresh token failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async testConnection(req: Request, res: Response) {
|
2026-03-20 17:53:46 +08:00
|
|
|
const id = req.params.id as string;
|
2026-03-20 09:43:50 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const result = await PlatformAccountService.testConnection(id);
|
|
|
|
|
res.json({ success: true, data: result });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[PlatformAccountController] Test connection failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async sync(req: Request, res: Response) {
|
|
|
|
|
const { id } = req.params;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const result = await PlatformAccountService.syncAccount(id);
|
|
|
|
|
res.json({ success: true, data: result });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[PlatformAccountController] Sync failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async getStats(req: Request, res: Response) {
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const stats = await PlatformAccountService.getStats(tenantId);
|
|
|
|
|
res.json({ success: true, data: stats });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[PlatformAccountController] Get stats failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class ReturnController {
|
|
|
|
|
static async fetchSKUData(req: Request, res: Response) {
|
|
|
|
|
const { status, shopId } = req.query;
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
2026-03-20 17:53:46 +08:00
|
|
|
const statusStr = Array.isArray(status) ? status[0] : status;
|
|
|
|
|
const shopIdStr = Array.isArray(shopId) ? shopId[0] : shopId;
|
|
|
|
|
|
2026-03-20 09:43:50 +08:00
|
|
|
const data = await ReturnService.fetchSKUData(tenantId, {
|
2026-03-20 17:53:46 +08:00
|
|
|
status: statusStr as string,
|
|
|
|
|
shopId: shopIdStr as string,
|
2026-03-20 09:43:50 +08:00
|
|
|
});
|
|
|
|
|
res.json({ success: true, data });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[ReturnController] Fetch SKU data failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async updateSKUStatus(req: Request, res: Response) {
|
|
|
|
|
const { id } = req.params;
|
|
|
|
|
const { status } = req.body;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const sku = await ReturnService.updateSKUStatus(id, status);
|
|
|
|
|
res.json({ success: true, data: sku });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[ReturnController] Update SKU status failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async fetchReturns(req: Request, res: Response) {
|
|
|
|
|
const { status, shopId, skuId } = req.query;
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
2026-03-20 17:53:46 +08:00
|
|
|
const statusStr = Array.isArray(status) ? status[0] : status;
|
|
|
|
|
const shopIdStr = Array.isArray(shopId) ? shopId[0] : shopId;
|
|
|
|
|
const skuIdStr = Array.isArray(skuId) ? skuId[0] : skuId;
|
|
|
|
|
|
2026-03-20 09:43:50 +08:00
|
|
|
const data = await ReturnService.fetchReturns(tenantId, {
|
2026-03-20 17:53:46 +08:00
|
|
|
status: statusStr as string,
|
|
|
|
|
shopId: shopIdStr as string,
|
|
|
|
|
skuId: skuIdStr as string,
|
2026-03-20 09:43:50 +08:00
|
|
|
});
|
|
|
|
|
res.json({ success: true, data });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[ReturnController] Fetch returns failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async createReturn(req: Request, res: Response) {
|
|
|
|
|
const { tenantId, userId, traceId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const returnData = await ReturnService.createReturn(tenantId, req.body);
|
|
|
|
|
await AuditService.log({
|
|
|
|
|
tenantId,
|
|
|
|
|
shopId: req.body.shopId,
|
|
|
|
|
taskId: '',
|
|
|
|
|
traceId,
|
|
|
|
|
userId,
|
|
|
|
|
module: 'RETURN',
|
|
|
|
|
action: 'CREATE',
|
|
|
|
|
resourceType: 'return',
|
|
|
|
|
metadata: { returnId: returnData.id, orderId: returnData.orderId },
|
|
|
|
|
result: 'success',
|
|
|
|
|
source: 'console',
|
|
|
|
|
});
|
|
|
|
|
res.json({ success: true, data: returnData });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[ReturnController] Create return failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async updateReturnStatus(req: Request, res: Response) {
|
|
|
|
|
const { id } = req.params;
|
|
|
|
|
const { status } = req.body;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const returnData = await ReturnService.updateReturnStatus(id, status);
|
|
|
|
|
res.json({ success: true, data: returnData });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[ReturnController] Update return status failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async getTrend(req: Request, res: Response) {
|
|
|
|
|
const { shopId, startDate, endDate } = req.query;
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
2026-03-20 17:53:46 +08:00
|
|
|
const shopIdStr = Array.isArray(shopId) ? shopId[0] : shopId;
|
|
|
|
|
const startDateStr = Array.isArray(startDate) ? startDate[0] : startDate;
|
|
|
|
|
const endDateStr = Array.isArray(endDate) ? endDate[0] : endDate;
|
|
|
|
|
|
2026-03-20 09:43:50 +08:00
|
|
|
const trend = await ReturnService.getReturnTrend(tenantId, {
|
2026-03-20 17:53:46 +08:00
|
|
|
shopId: shopIdStr as string,
|
|
|
|
|
startDate: startDateStr as string,
|
|
|
|
|
endDate: endDateStr as string,
|
2026-03-20 09:43:50 +08:00
|
|
|
});
|
|
|
|
|
res.json({ success: true, data: trend });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[ReturnController] Get trend failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static async getStats(req: Request, res: Response) {
|
|
|
|
|
const { shopId } = req.query;
|
|
|
|
|
const { tenantId } = (req as any).traceContext;
|
|
|
|
|
|
|
|
|
|
try {
|
2026-03-20 17:53:46 +08:00
|
|
|
const shopIdStr = Array.isArray(shopId) ? shopId[0] : shopId;
|
|
|
|
|
|
|
|
|
|
const stats = await ReturnService.getReturnStats(tenantId, { shopId: shopIdStr as string });
|
2026-03-20 09:43:50 +08:00
|
|
|
res.json({ success: true, data: stats });
|
|
|
|
|
} catch (err: any) {
|
|
|
|
|
logger.error(`[ReturnController] Get stats failed: ${err.message}`);
|
|
|
|
|
res.status(500).json({ success: false, error: err.message });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|