| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205 |
- import { Router } from 'express';
- import { body, param } from 'express-validator';
- import { PublishService } from '../services/PublishService.js';
- import { taskQueueService } from '../services/TaskQueueService.js';
- import { authenticate } from '../middleware/auth.js';
- import { asyncHandler } from '../middleware/error.js';
- import { validateRequest } from '../middleware/validate.js';
- const router = Router();
- const publishService = new PublishService();
- router.use(authenticate);
- // 获取发布任务列表
- router.get(
- '/',
- asyncHandler(async (req, res) => {
- const { page = 1, pageSize = 20, status } = req.query;
- const result = await publishService.getTasks(req.user!.userId, {
- page: Number(page),
- pageSize: Number(pageSize),
- status: status as string,
- });
- res.json({ success: true, data: result });
- })
- );
- // 获取任务详情
- router.get(
- '/:id',
- [
- param('id').isInt().withMessage('任务ID无效'),
- validateRequest,
- ],
- asyncHandler(async (req, res) => {
- const task = await publishService.getTaskById(
- req.user!.userId,
- Number(req.params.id)
- );
- res.json({ success: true, data: task });
- })
- );
- // 创建发布任务
- router.post(
- '/',
- [
- // 修复 #6069:videoPath 和 title 改为可选,具体校验由前端按平台要求完成
- body('videoPath').optional({ nullable: true }).isString().withMessage('视频路径格式无效'),
- body('title').optional({ nullable: true }).isString().withMessage('标题格式无效'),
- body('targetAccounts').isArray({ min: 1 }).withMessage('至少选择一个目标账号'),
- body('publishProxy').optional({ nullable: true }).isObject().withMessage('发布代理配置无效'),
- validateRequest,
- ],
- asyncHandler(async (req, res) => {
- const userId = req.user!.userId;
- // 1. 创建数据库记录
- const task = await publishService.createTask(userId, req.body);
- // 2. 如果不是定时任务,加入任务队列
- if (!req.body.scheduledAt) {
- await taskQueueService.createTask(userId, {
- type: 'publish_video',
- title: `发布视频: ${req.body.title}`,
- description: `发布到 ${req.body.targetAccounts.length} 个账号`,
- data: {
- publishTaskId: task.id,
- },
- });
- }
- res.status(201).json({ success: true, data: task });
- })
- );
- // 修改并重新发布(更新现有任务)
- router.put(
- '/:id',
- [
- param('id').isInt().withMessage('任务ID无效'),
- body('targetAccounts').isArray({ min: 1 }).withMessage('至少选择一个目标账号'),
- body('publishProxy').optional({ nullable: true }).isObject().withMessage('发布代理配置无效'),
- validateRequest,
- ],
- asyncHandler(async (req, res) => {
- const userId = req.user!.userId;
- const taskId = Number(req.params.id);
- // 修复 Bug #6145:更新现有任务而非创建新任务
- const task = await publishService.updateTask(userId, taskId, req.body);
- // 如果不是定时任务且状态允许,加入任务队列重新执行
- if (!req.body.scheduledAt && task.status !== 'cancelled') {
- await taskQueueService.createTask(userId, {
- type: 'publish_video',
- title: `重新发布: ${task.title}`,
- description: `发布到 ${task.targetAccounts.length} 个账号`,
- data: {
- publishTaskId: task.id,
- },
- });
- }
- res.json({ success: true, data: task });
- })
- );
- // 取消任务
- router.post(
- '/:id/cancel',
- [
- param('id').isInt().withMessage('任务ID无效'),
- validateRequest,
- ],
- asyncHandler(async (req, res) => {
- await publishService.cancelTask(req.user!.userId, Number(req.params.id));
- res.json({ success: true, message: '任务已取消' });
- })
- );
- // 重试任务
- router.post(
- '/:id/retry',
- [
- param('id').isInt().withMessage('任务ID无效'),
- validateRequest,
- ],
- asyncHandler(async (req, res) => {
- const userId = req.user!.userId;
- const taskId = Number(req.params.id);
- // 1. 更新数据库状态
- const task = await publishService.retryTask(userId, taskId);
- // 2. 加入任务队列重新执行
- await taskQueueService.createTask(userId, {
- type: 'publish_video',
- title: `重试发布: ${task.title}`,
- description: `重新发布到 ${task.targetAccounts.length} 个账号`,
- data: {
- publishTaskId: task.id,
- },
- });
- res.json({ success: true, data: task });
- })
- );
- // 删除任务
- router.delete(
- '/:id',
- [
- param('id').isInt().withMessage('任务ID无效'),
- validateRequest,
- ],
- asyncHandler(async (req, res) => {
- await publishService.deleteTask(req.user!.userId, Number(req.params.id));
- res.json({ success: true, message: '任务已删除' });
- })
- );
- // 手动确认发布结果(如小红书"发布结果待确认"时用户确认已发布成功)
- router.post(
- '/:taskId/results/:resultId/confirm',
- [
- param('taskId').isInt().withMessage('任务ID无效'),
- param('resultId').isInt().withMessage('结果ID无效'),
- validateRequest,
- ],
- asyncHandler(async (req, res) => {
- await publishService.confirmPublishResult(
- req.user!.userId,
- Number(req.params.taskId),
- Number(req.params.resultId)
- );
- res.json({ success: true, message: '已确认发布成功' });
- })
- );
- // 单账号有头浏览器重试发布(用于验证码场景)
- router.post(
- '/:taskId/retry-headful/:accountId',
- [
- param('taskId').isInt().withMessage('任务ID无效'),
- param('accountId').isInt().withMessage('账号ID无效'),
- validateRequest,
- ],
- asyncHandler(async (req, res) => {
- const userId = req.user!.userId;
- const taskId = Number(req.params.taskId);
- const accountId = Number(req.params.accountId);
- // 调用服务层执行有头浏览器发布
- const result = await publishService.retryAccountWithHeadfulBrowser(
- userId,
- taskId,
- accountId
- );
- res.json({ success: true, data: result });
- })
- );
- export default router;
|