import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify' import { TaskService } from '../services/task.service' import { UpdateTaskBody, ListTaskQuery, ListTaskItemQuery } from '../dto/task.dto' import { Task, TaskType } from '../entities/task.entity' export class TaskController { private taskService: TaskService constructor(app: FastifyInstance) { this.taskService = new TaskService(app) } private parseIntervalTime(input: unknown): { ok: true; value: string } | { ok: false; message: string } { if (input === undefined || input === null || input === '') { return { ok: true, value: '5-5' } } const raw = String(input).trim() // 允许: "10", "10-20", "10~20", "10,20", "10 20" const normalized = raw.replace('~', '-').replace(',', '-').replace(/\s+/g, '-') const parts = normalized.split('-').filter(Boolean) if (parts.length === 1) { const s = Number(parts[0]) if (Number.isNaN(s) || s < 0) return { ok: false, message: 'intervalTime 必须为大于等于 0 的秒数或区间(如 10-20)' } return { ok: true, value: `${s}-${s}` } } if (parts.length === 2) { const a = Number(parts[0]) const b = Number(parts[1]) if (Number.isNaN(a) || Number.isNaN(b) || a < 0 || b < 0) { return { ok: false, message: 'intervalTime 必须为大于等于 0 的秒数区间(如 10-20)' } } const min = Math.min(a, b) const max = Math.max(a, b) return { ok: true, value: `${min}-${max}` } } return { ok: false, message: 'intervalTime 格式错误,示例:10 或 10-20' } } async create(request: FastifyRequest, reply: FastifyReply) { try { const userId = request.user.id const data = await request.file() if (!data) { return reply.code(400).send({ message: '请选择要上传的文件' }) } const nameField = data.fields['name'] const typeField = data.fields['type'] const messageField = data.fields['message'] const inviteLinkField = data.fields['inviteLink'] // 新字段 const accountLimitField = data.fields['accountLimit'] const intervalTimeField = data.fields['intervalTime'] const threadsField = data.fields['threads'] const name = nameField && !Array.isArray(nameField) && 'value' in nameField ? (nameField.value as string) : undefined const type = typeField && !Array.isArray(typeField) && 'value' in typeField ? (typeField.value as string) : undefined const message = messageField && !Array.isArray(messageField) && 'value' in messageField ? (messageField.value as string) : undefined const inviteLink = inviteLinkField && !Array.isArray(inviteLinkField) && 'value' in inviteLinkField ? (inviteLinkField.value as string) : undefined const accountLimit = accountLimitField && !Array.isArray(accountLimitField) && 'value' in accountLimitField ? Number(accountLimitField.value) : undefined const intervalRaw = intervalTimeField && !Array.isArray(intervalTimeField) && 'value' in intervalTimeField ? intervalTimeField.value : undefined const intervalParsed = this.parseIntervalTime(intervalRaw) if (!intervalParsed.ok) { return reply.code(400).send({ message: intervalParsed.message }) } const threads = threadsField && !Array.isArray(threadsField) && 'value' in threadsField ? Number(threadsField.value) : undefined if (!name) { return reply.code(400).send({ message: '任务名称不能为空' }) } const taskType: TaskType = type === TaskType.INVITE_TO_GROUP || type === TaskType.SEND_MESSAGE ? (type as TaskType) : TaskType.SEND_MESSAGE if (accountLimit !== undefined && (isNaN(accountLimit) || accountLimit <= 0)) { return reply.code(400).send({ message: 'accountLimit 必须为大于 0 的数字' }) } if (threads !== undefined && (isNaN(threads) || threads <= 0)) { return reply.code(400).send({ message: 'threads 必须为大于 0 的数字' }) } if (threads !== undefined && threads > 10) { return reply.code(400).send({ message: 'threads 最大值为 10' }) } // payload 仅存任务特有配置 let payload: Record = {} if (taskType === TaskType.SEND_MESSAGE) { if (!message) { return reply.code(400).send({ message: 'SEND_MESSAGE 任务 message 不能为空' }) } payload = { message } } else if (taskType === TaskType.INVITE_TO_GROUP) { if (!inviteLink || !inviteLink.trim()) { return reply.code(400).send({ message: 'INVITE_TO_GROUP 任务 inviteLink 不能为空' }) } payload = { inviteLink: inviteLink.trim() } } const buffer = await data.toBuffer() const task = await this.taskService.create({ name, userId, buffer, type: taskType, payload, accountLimit, intervalTime: intervalParsed.value, threads }) return reply.code(201).send({ task: { id: task.id, name: task.name, type: task.type, payload: task.payload, accountLimit: task.accountLimit, intervalTime: task.intervalTime, threads: task.threads, processed: task.processed, success: task.success, total: task.total } }) } catch (error) { return reply.code(500).send(error) } } async getById(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) { try { const id = parseInt(request.params.id) if (isNaN(id)) { return reply.code(400).send({ message: '无效的任务ID' }) } const task = await this.taskService.findById(id) return reply.send(task) } catch (error) { return reply.code(500).send({ error }) } } async list(request: FastifyRequest<{ Querystring: ListTaskQuery }>, reply: FastifyReply) { try { const { page, size, userId } = request.query const result = await this.taskService.findAll(Number(page) || 0, Number(size) || 20, userId || request.user.id) return reply.send(result) } catch (error) { return reply.code(500).send({ error }) } } async update(request: FastifyRequest<{ Body: UpdateTaskBody }>, reply: FastifyReply) { try { const { id, name, payload, total, processed, success, startedAt, accountLimit, intervalTime, threads } = request.body if (!id) { return reply.code(400).send({ message: '任务ID不能为空' }) } const task = await this.taskService.findById(id) if (!task) { return reply.code(500).send({ message: '任务不存在' }) } if (accountLimit !== undefined && (isNaN(Number(accountLimit)) || Number(accountLimit) <= 0)) { return reply.code(400).send({ message: 'accountLimit 必须为大于 0 的数字' }) } if (intervalTime !== undefined) { const parsed = this.parseIntervalTime(intervalTime) if (!parsed.ok) return reply.code(400).send({ message: parsed.message }) } if (threads !== undefined && (isNaN(Number(threads)) || Number(threads) <= 0)) { return reply.code(400).send({ message: 'threads 必须为大于 0 的数字' }) } if (threads !== undefined && Number(threads) > 10) { return reply.code(400).send({ message: 'threads 最大值为 10' }) } const updateData: Partial = {} if (name !== undefined) updateData.name = name if (payload !== undefined) updateData.payload = payload as any if (total !== undefined) updateData.total = total if (processed !== undefined) updateData.processed = processed if (success !== undefined) updateData.success = success if (startedAt !== undefined) updateData.startedAt = startedAt if (accountLimit !== undefined) updateData.accountLimit = Number(accountLimit) if (intervalTime !== undefined) { const parsed = this.parseIntervalTime(intervalTime) if (parsed.ok) updateData.intervalTime = parsed.value } if (threads !== undefined) updateData.threads = Math.min(10, Number(threads)) await this.taskService.update(id, updateData) return reply.send({ message: '任务更新成功' }) } catch (error) { return reply.code(500).send({ error }) } } async delete(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) { try { const id = parseInt(request.params.id) if (isNaN(id)) { return reply.code(400).send({ message: '无效的任务ID' }) } const task = await this.taskService.findById(id) if (!task) { return reply.code(500).send({ message: '任务不存在' }) } await this.taskService.delete(id) return reply.send({ message: '任务删除成功' }) } catch (error) { return reply.code(500).send(error) } } async listTaskItems(request: FastifyRequest<{ Querystring: ListTaskItemQuery }>, reply: FastifyReply) { try { const { page, size, taskId, status } = request.query const result = await this.taskService.findTaskItems( Number(page) || 0, Number(size) || 20, taskId ? Number(taskId) : undefined, status ) return reply.send(result) } catch (error) { return reply.code(500).send({ message: '查询任务项失败', error: error instanceof Error ? error.message : '未知错误' }) } } async startTask(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) { try { const id = parseInt(request.params.id) if (isNaN(id)) { return reply.code(400).send({ message: '无效的任务ID' }) } await this.taskService.startTask(id) return reply.send({ message: '任务启动成功' }) } catch (error) { return reply.code(500).send({ message: '启动任务失败', error: error instanceof Error ? error.message : '未知错误' }) } } async pauseTask(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) { try { const id = parseInt(request.params.id) if (isNaN(id)) { return reply.code(400).send({ message: '无效的任务ID' }) } await this.taskService.pauseTask(id) return reply.send({ message: '任务已暂停' }) } catch (error) { return reply.code(500).send({ message: '暂停任务失败', error: error instanceof Error ? error.message : '未知错误' }) } } }