Files
makemd/server/src/api/controllers/SyncController.ts
wurenzhi 2748456d8a refactor(services): 重构服务文件结构,将服务按功能分类到不同目录
- 将服务文件按功能分类到core、ai、analytics、security等目录
- 修复logger导入路径问题,统一使用相对路径
- 更新相关文件的导入路径引用
- 添加新的批量操作组件导出文件
- 修复dashboard页面中的类型错误
- 添加dotenv依赖到package.json
2026-03-25 13:46:26 +08:00

161 lines
4.7 KiB
TypeScript

import { Request, Response } from 'express';
import db from '../../config/database';
import { isValidSyncStatusTransition } from '../../core/guards/state-transition.guard';
import { AuditService } from '../../services/utils/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 });
}
}
}