diff --git a/app/src/main/java/com/xiaoqu/watch/service/manager/NfcTaskManager.kt b/app/src/main/java/com/xiaoqu/watch/service/manager/NfcTaskManager.kt index 83bf579..ccbbc55 100644 --- a/app/src/main/java/com/xiaoqu/watch/service/manager/NfcTaskManager.kt +++ b/app/src/main/java/com/xiaoqu/watch/service/manager/NfcTaskManager.kt @@ -5,7 +5,12 @@ import com.xiaoqu.watch.device.sensor.VibrationController import com.xiaoqu.watch.network.ApiResult import com.xiaoqu.watch.network.api.TaskApi import com.xiaoqu.watch.network.safeApiCall -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject import javax.inject.Singleton @@ -15,11 +20,11 @@ import javax.inject.Singleton * * 统一处理三种 NFC 打卡场景: * 1. 任务单个打卡(任务详情页"开启打卡"按钮) - * 2. 主动批量打卡(返回键触发,checkNfcType → nfcBatchBeginTask) - * 3. 硬件开锁(返回键触发,checkNfcType → 根据 status 播放语音) + * 2. 主动批量打卡(返回键触发,checkNfcType -> nfcBatchBeginTask) + * 3. 硬件开锁(返回键触发,checkNfcType -> 根据 status 播放语音) * * 与 PunchViewModel(考勤打卡)独立,互不干扰。 - * 通过 isScanning 防止两者同时操作 NFC。 + * 通过 isScanning + nfcController.isOpen() 防止两者同时操作 NFC。 */ @Singleton class NfcTaskManager @Inject constructor( @@ -28,9 +33,7 @@ class NfcTaskManager @Inject constructor( private val taskApi: TaskApi ) { companion object { - /** 默认 NFC 超时(毫秒),可被 MQTT type=4 的 nfcOpenTime 覆盖 */ private const val DEFAULT_NFC_TIMEOUT_MS = 20_000L - // 震动方案 planId private const val PLAN_PUNCH_SUCCESS = 4 private const val PLAN_PUNCH_FAIL = 7 private const val PLAN_NFC_OPEN = 8 @@ -49,18 +52,11 @@ class NfcTaskManager @Inject constructor( /** NFC 超时时间(毫秒),通过 MQTT type=4 更新 */ var nfcTimeoutMs: Long = DEFAULT_NFC_TIMEOUT_MS - /** 协程作用域 */ private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob()) - - /** 超时 Job */ private var timeoutJob: Job? = null /** * 场景 1:任务单个打卡 - * 开启 NFC → 读卡 → POST nfcToBeginTask → 回调结果 - * - * @param taskId 任务 ID - * @param onResult 结果回调(主线程),success + message */ fun startTaskPunch(taskId: Long, onResult: (success: Boolean, message: String) -> Unit) { if (isScanning || nfcController.isOpen()) { @@ -68,20 +64,17 @@ class NfcTaskManager @Inject constructor( return } isScanning = true - Timber.d("NFC任��打卡: 开启单个打卡, taskId=%d", taskId) + Timber.d("NFC任务打卡: 开启单个打卡, taskId=%d", taskId) - // 开启 NFC + 音效 vibrationController.executeByPlanId(PLAN_NFC_OPEN) nfcController.open() - // 开始扫描 nfcController.startScan { nfcId -> Timber.d("NFC任务打卡: 读到卡号 %s", nfcId) cancelTimeout() nfcController.stopScan() nfcController.close() - // 调用打卡 API scope.launch { val params = hashMapOf("id" to taskId, "nfcId" to nfcId) val result = safeApiCall { taskApi.nfcToBeginTask(params) } @@ -94,7 +87,7 @@ class NfcTaskManager @Inject constructor( is ApiResult.Error -> { Timber.w("NFC任务打卡: 打卡失败 - %s", result.message) vibrationController.executeByPlanId(PLAN_PUNCH_FAIL) - onResult(false, result.message ?: "��卡失败") + onResult(false, result.message ?: "打卡失败") } is ApiResult.NetworkError -> { Timber.w(result.exception, "NFC任务打卡: 网络异常") @@ -106,49 +99,42 @@ class NfcTaskManager @Inject constructor( } } - // 启动超时 startTimeout { Timber.d("NFC任务打卡: 超时自动关闭") closeNfc() - onResult(false, "超���") + vibrationController.executeByPlanId(PLAN_NFC_CLOSE) + onResult(false, "超时") isScanning = false } } /** - * 场景 2+3:主动打卡(���回键触发) - * 开启 NFC → 读卡 → checkNfcType → 批量打卡 or 硬件开锁 - * - * @param onResult 结果回调(主线程),success + message + * 场景 2+3:主动打卡(返回键触发) */ fun startActivePunch(onResult: (success: Boolean, message: String) -> Unit) { - if (isScanning) { if (isScanning || nfcController.isOpen()) { Timber.d("NFC主动打卡: NFC 正在使用中,忽略") + return } isScanning = true - Timber.d("NFC主动��卡: 开启(返回键触发)") + Timber.d("NFC主动打卡: 开启(返回键触发)") - // 开启 NFC + ��效 vibrationController.executeByPlanId(PLAN_NFC_OPEN) nfcController.open() - // 开始扫描 nfcController.startScan { nfcId -> - Timber.d("NFC主动打卡: 读到���号 %s", nfcId) + Timber.d("NFC主动打卡: 读到卡号 %s", nfcId) cancelTimeout() nfcController.stopScan() nfcController.close() vibrationController.executeByPlanId(PLAN_NFC_CLOSE) - // 判断类型:批量打卡 or 硬件开锁 scope.launch { handleNfcType(nfcId, onResult) isScanning = false } } - // 启动超时 startTimeout { Timber.d("NFC主动打卡: 超时自动关闭") closeNfc() @@ -168,11 +154,7 @@ class NfcTaskManager @Inject constructor( isScanning = false } - /** - * 判断 NFC 类型并处理 - * hardwareNfcFlag=true → 硬件开锁(播���语音) - * hardwareNfcFlag=false → 批量任务打卡(调 API) - */ + /** 判断 NFC 类型并处理 */ private suspend fun handleNfcType(nfcId: String, onResult: (Boolean, String) -> Unit) { val result = safeApiCall { taskApi.checkNfcType(nfcId) } when (result) { @@ -182,12 +164,9 @@ class NfcTaskManager @Inject constructor( onResult(false, "数据异常") return } - if (data.hardwareNfcFlag) { - // 硬件开锁 handleHardwareUnlock(data.status, onResult) } else { - // 批量任务打卡 handleBatchPunch(nfcId, onResult) } } @@ -206,7 +185,7 @@ class NfcTaskManager @Inject constructor( /** 硬件开锁:根据 status 播放对应语音 */ private fun handleHardwareUnlock(status: Int, onResult: (Boolean, String) -> Unit) { - Timber.d("NFC��动打卡: 硬件开锁, status=%d", status) + Timber.d("NFC主动打卡: 硬件开锁, status=%d", status) when (status) { 0 -> { vibrationController.executeByPlanId(PLAN_OPENING) @@ -224,9 +203,7 @@ class NfcTaskManager @Inject constructor( vibrationController.executeByPlanId(PLAN_NO_AUTH) onResult(false, "无开锁权限") } - else -> { - onResult(false, "��知状态") - } + else -> onResult(false, "未知状态") } } @@ -247,20 +224,18 @@ class NfcTaskManager @Inject constructor( onResult(false, result.message ?: "打卡失败") } is ApiResult.NetworkError -> { - Timber.w(result.exception, "NFC主动打卡: 网络��常") + Timber.w(result.exception, "NFC主动打卡: 网络异常") vibrationController.executeByPlanId(PLAN_PUNCH_FAIL) onResult(false, "网络异常") } } } - /** 关闭 NFC 硬件 */ private fun closeNfc() { nfcController.stopScan() nfcController.close() } - /** 启动超时协程 */ private fun startTimeout(onTimeout: () -> Unit) { timeoutJob = scope.launch { delay(nfcTimeoutMs) @@ -268,7 +243,6 @@ class NfcTaskManager @Inject constructor( } } - /** 取消超时协程 */ private fun cancelTimeout() { timeoutJob?.cancel() timeoutJob = null