import { Request, Response } from 'express'; import db from '../../config/database'; import { isValidSyncStatusTransition } from '../../core/guards/state-transition.guard'; import { AuditService } from '../../services/AuditService'; export class SyncController { /** * [UX_DEV_03] 接收插件端上报的 E2E 异常 */ static async reportAudit(req: Request, res: Response) { try { const { action, targetType, targetId, metadata } = req.body; const { tenantId, shopId, taskId, traceId, userId, roleCode } = (req as any).traceContext; await AuditService.log({ tenantId, shopId, taskId, traceId, userId, roleCode, module: 'SYNC', action: action || 'AUDIT_REPORT', resourceType: targetType || 'sync', resourceId: targetId ? String(targetId) : undefined, result: 'success', source: 'extension', metadata, }); res.json({ success: true }); } catch (error: any) { res.status(500).json({ success: false, error: error.message }); } } /** * 分发产品数据至全平台 (Mock 实现,实际应触发各平台发布 Worker) */ static async distribute(req: Request, res: Response) { try { const { originalUrl, productData } = req.body; const { tenantId, shopId, taskId, traceId, userId, roleCode } = (req as any).traceContext; const platforms = ['Temu', 'TikTok', 'Shopee']; for (const platform of platforms) { const existing = await db('cf_sync_status') .where({ originalUrl, platform }) .first(); if (existing) { await db('cf_sync_status') .where({ id: existing.id }) .update({ status: 'pending', productData: JSON.stringify(productData || {}), updated_at: new Date() }); } else { await db('cf_sync_status').insert({ originalUrl, platform, status: 'pending', productData: JSON.stringify(productData || {}), created_at: new Date(), updated_at: new Date() }); } } await AuditService.log({ tenantId, shopId, taskId, traceId, userId, roleCode, module: 'SYNC', action: 'DISTRIBUTE', resourceType: 'sync', resourceId: String(originalUrl || ''), result: 'success', source: 'console', }); res.json({ success: true, data: { triggered: true } }); } catch (error: any) { res.status(500).json({ success: false, error: error.message }); } } static async recordAction(req: Request, res: Response) { try { const { platform, action, status } = req.body; const { tenantId, shopId, taskId, traceId, userId, roleCode } = (req as any).traceContext; await AuditService.log({ tenantId, shopId, taskId, traceId, userId, roleCode, module: 'SYNC', action: 'RECORD_ACTION', resourceType: 'sync_log', resourceId: platform, afterSnapshot: { action, status }, result: 'success', source: 'console', }); res.json({ success: true }); } catch (error: any) { res.status(500).json({ success: false, error: error.message }); } } /** * [FSM] 状态流转控制 */ static async updateStatus(req: Request, res: Response) { const { id } = req.params; const { status } = req.body; const { tenantId, shopId, taskId, traceId, userId } = (req as any).traceContext; try { const record = await db('cf_sync_status').where({ id }).first(); if (!record) return res.status(404).json({ success: false, error: 'Sync record not found' }); const previousStatus = record.status; const nextStatus = status; if (!isValidSyncStatusTransition(previousStatus, nextStatus)) { return res.status(400).json({ success: false, error: `Invalid sync status transition: ${previousStatus} -> ${nextStatus}` }); } await db('cf_sync_status').where({ id }).update({ status: nextStatus, updated_at: new Date() }); await AuditService.log({ tenantId, shopId, taskId, traceId, userId, module: 'SYNC', action: 'UPDATE_SYNC_STATUS', resourceType: 'sync', resourceId: String(record.id), beforeSnapshot: { status: previousStatus }, afterSnapshot: { status: nextStatus }, result: 'success', source: 'node', }); res.json({ success: true }); } catch (err: any) { res.status(500).json({ success: false, error: err.message }); } } }