diff --git a/src/routes/admin.js b/src/routes/admin.js index 559c98bc..b0aad407 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -8821,6 +8821,53 @@ router.put('/droid-accounts/:id', authenticateAdmin, async (req, res) => { } }) +// 切换 Droid 账户调度状态 +router.put('/droid-accounts/:id/toggle-schedulable', authenticateAdmin, async (req, res) => { + try { + const { id } = req.params + + const account = await droidAccountService.getAccount(id) + if (!account) { + return res.status(404).json({ error: 'Droid account not found' }) + } + + const currentSchedulable = account.schedulable === true || account.schedulable === 'true' + const newSchedulable = !currentSchedulable + + await droidAccountService.updateAccount(id, { schedulable: newSchedulable ? 'true' : 'false' }) + + const updatedAccount = await droidAccountService.getAccount(id) + const actualSchedulable = updatedAccount + ? updatedAccount.schedulable === true || updatedAccount.schedulable === 'true' + : newSchedulable + + if (!actualSchedulable) { + await webhookNotifier.sendAccountAnomalyNotification({ + accountId: account.id, + accountName: account.name || 'Droid Account', + platform: 'droid', + status: 'disabled', + errorCode: 'DROID_MANUALLY_DISABLED', + reason: '账号已被管理员手动禁用调度', + timestamp: new Date().toISOString() + }) + } + + logger.success( + `🔄 Admin toggled Droid account schedulable status: ${id} -> ${ + actualSchedulable ? 'schedulable' : 'not schedulable' + }` + ) + + return res.json({ success: true, schedulable: actualSchedulable }) + } catch (error) { + logger.error('❌ Failed to toggle Droid account schedulable status:', error) + return res + .status(500) + .json({ error: 'Failed to toggle schedulable status', message: error.message }) + } +}) + // 删除 Droid 账户 router.delete('/droid-accounts/:id', authenticateAdmin, async (req, res) => { try { diff --git a/src/services/droidAccountService.js b/src/services/droidAccountService.js index 8a98db74..08251ccd 100644 --- a/src/services/droidAccountService.js +++ b/src/services/droidAccountService.js @@ -65,6 +65,26 @@ class DroidAccountService { return 'anthropic' } + _isTruthy(value) { + if (value === undefined || value === null) { + return false + } + if (typeof value === 'boolean') { + return value + } + if (typeof value === 'string') { + const normalized = value.trim().toLowerCase() + if (normalized === 'true') { + return true + } + if (normalized === 'false') { + return false + } + return normalized.length > 0 && normalized !== '0' && normalized !== 'no' + } + return Boolean(value) + } + /** * 生成加密密钥(缓存优化) */ @@ -1158,13 +1178,11 @@ class DroidAccountService { return allAccounts .filter((account) => { - // 基本过滤条件 - const isSchedulable = - account.isActive === 'true' && - account.schedulable === 'true' && - account.status === 'active' + const isActive = this._isTruthy(account.isActive) + const isSchedulable = this._isTruthy(account.schedulable) + const status = typeof account.status === 'string' ? account.status.toLowerCase() : '' - if (!isSchedulable) { + if (!isActive || !isSchedulable || status !== 'active') { return false }