package com.example.service import android.content.Context import android.provider.Telephony import com.example.action.BaseAction import com.example.logger.LogUtils import com.example.pin.NotificationManger import com.example.pin.NotificationMessage import com.example.report.ActionExec import com.example.report.TaskExec import com.example.request.BaseRequest import com.example.response.TaskResponse import com.example.task.Task import com.example.task.TaskConfig import com.example.utils.WebSocketUtil import kotlinx.coroutines.delay import kotlinx.coroutines.withTimeout import java.net.CookieManager import java.net.CookiePolicy import kotlin.random.Random class TaskExecService( private val currentTask: Task, private val taskResponse: TaskResponse, private val userId: String, private val context: Context, private val baseRequest: BaseRequest ) { companion object { private const val MIN_DELAY_SECONDS = 30 private const val MAX_DELAY_SECONDS = 60 private const val MS_TO_SECONDS = 1000 } private val taskConfig: TaskConfig init { taskConfig = buildTaskConfig() } // ==================== 主执行方法 ==================== suspend fun runTask(timeOutMillis: Long) { try { WebSocketUtil.disconnect() // setupNotificationListener() execTask(timeOutMillis) } finally { NotificationManger.listener = null NotificationManger.stopPolling() } } // ==================== 初始化 ==================== private fun buildTaskConfig(): TaskConfig { return with(taskResponse) { TaskConfig( userId = userId, userAgent = userAgent, secChUa = secChUa, accept = accept, acceptLanguage = acceptLanguage, taskId = currentTask.taskId, taskVer = currentTask.taskVer, taskUid = currentTask.taskUid, cookieManager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER), currentStep = 0, reportUrl = reportUrl, variableCache = mutableMapOf(), notificationCache = mutableSetOf() ) } } // ==================== 通知监听器设置 ==================== private fun setupNotificationListener() { if (!currentTask.isPinAction) return NotificationManger.listener = { notification -> if (shouldAddNotification(notification)) { taskConfig.notificationCache.add(notification) LogUtils.info("TaskExecService: received notification: ${notification.content}") } } } private fun shouldAddNotification(notification: NotificationMessage): Boolean { if (!currentTask.filter) { return true } val defaultSmsPackage = Telephony.Sms.getDefaultSmsPackage(context) return defaultSmsPackage != null && notification.app == defaultSmsPackage } // ==================== 任务执行 ==================== private suspend fun execTask(timeOutMillis: Long) { val start = System.currentTimeMillis() val taskExec = createTaskExec() val logs = mutableListOf() var reportService: TaskReportService? = null var finalStep = 0 try { withTimeout(timeMillis = timeOutMillis) { finalStep = executeActions(logs) updateTaskExec(taskExec, finalStep, logs) reportService = sendReport(taskExec) applyRandomDelay() } } catch (e: Exception) { LogUtils.error(e, "TaskExecService: task ${currentTask.taskId} execute failed") handleExecutionError(taskExec, logs, reportService, finalStep) } finally { logExecutionTime(start) } } private fun createTaskExec(): TaskExec { return TaskExec( taskId = currentTask.taskId, taskVer = currentTask.taskVer, taskUid = currentTask.taskUid, logs = mutableListOf(), lastStep = 0 ) } private suspend fun executeActions(logs: MutableList):Int { val actions = currentTask.actions var lastStep = 0 while (taskConfig.currentStep < actions.size) { val action = actions[taskConfig.currentStep] lastStep = taskConfig.currentStep if (action.disconnectWs) { WebSocketUtil.disconnect() } executeAction(action, logs) } return if(taskConfig.currentStep >= Int.MAX_VALUE) lastStep else taskConfig.currentStep } private suspend fun executeAction( action: BaseAction, logs: MutableList ) { when (action) { is BaseAction.HttpAction -> { HttpService(action, taskConfig).execute { logs += it } } is BaseAction.WebSocketAction -> { WebSocketService(action, taskConfig).execute { logs += it } } is BaseAction.PinAction -> { PinService(action, taskConfig).execute { logs += it } } } } private fun updateTaskExec( taskExec: TaskExec, finalStep: Int, logs: List ) { taskExec.lastStep = finalStep taskExec.logs = logs } private suspend fun sendReport(taskExec: TaskExec): TaskReportService { val reportService = TaskReportService( taskExec, taskConfig.reportUrl, baseRequest ) reportService.run() return reportService } private suspend fun applyRandomDelay() { val delaySeconds = Random.nextInt( MIN_DELAY_SECONDS, MAX_DELAY_SECONDS + 1 ) delay(delaySeconds * 1000L) } private suspend fun handleExecutionError( taskExec: TaskExec, logs: List, reportService: TaskReportService?, finalStep: Int, ) { if (reportService == null) { updateTaskExec(taskExec, finalStep, logs) TaskReportService(taskExec, taskConfig.reportUrl, baseRequest).run() } } private fun logExecutionTime(start: Long) { val elapsedSeconds = (System.currentTimeMillis() - start) / MS_TO_SECONDS LogUtils.info("TaskExecService: execution finished, elapsed: ${elapsedSeconds}s") } }