| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- 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<string, any> = {}
- 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<Task> = {}
- 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 : '未知错误'
- })
- }
- }
- }
|