diff --git a/apps/backend/src/routes/admin/tasks.ts b/apps/backend/src/routes/admin/tasks.ts index fb7792a5..044a4e0c 100644 --- a/apps/backend/src/routes/admin/tasks.ts +++ b/apps/backend/src/routes/admin/tasks.ts @@ -88,7 +88,7 @@ router.post( router.post( '/tasks/:taskId/kuaishou-cloud/rebind-role', - requireAdminRoles(['admin', 'operator']), + requireAdminRoles(['admin', 'operator', 'support']), createJsonHandler( (req) => rebindAdminTaskKuaishouCloudRole(getTaskId(req), req.adminSession || null), { diff --git a/apps/backend/src/services/admin/admin-read-service.ts b/apps/backend/src/services/admin/admin-read-service.ts index 8c2fbae3..2e8adbf6 100644 --- a/apps/backend/src/services/admin/admin-read-service.ts +++ b/apps/backend/src/services/admin/admin-read-service.ts @@ -354,7 +354,7 @@ export async function getAdminTaskDetail( String(task.executor_key || '').trim() === 'kuaishou_ct_assisted' && Number(kuaishouCloudFulfillment?.binding.vnId || 0) > 0, canRebindKuaishouCloudRole: - viewerContext.canManageTaskLifecycle && + viewerContext.canOperateAssistedTask && String(task.executor_key || '').trim() === 'kuaishou_ct_assisted' && canRebindKuaishouCloudRoleStatus(task.task_status) && String(kuaishouCloudFulfillment?.dispatch.status || 'pending').trim() !== 'success' && diff --git a/apps/backend/src/services/admin/admin-read-shared-helpers.test.ts b/apps/backend/src/services/admin/admin-read-shared-helpers.test.ts index 9c6f9b30..132e8dcc 100644 --- a/apps/backend/src/services/admin/admin-read-shared-helpers.test.ts +++ b/apps/backend/src/services/admin/admin-read-shared-helpers.test.ts @@ -43,9 +43,23 @@ test('createAdminViewerContext lets support operate vouchers without lifecycle p const viewerContext = createAdminViewerContext({ role: 'support' }) assert.equal(viewerContext.canOperateKuaishouIndustryVoucher, true) + assert.equal(viewerContext.canOperateAssistedTask, true) assert.equal(viewerContext.canManageTaskLifecycle, false) }) +test('createAdminViewerContext lets support rebind-assisted actions without full lifecycle', () => { + const support = createAdminViewerContext({ role: 'support' }) + const operator = createAdminViewerContext({ role: 'operator' }) + + // 客服:可协助换绑/刷新角色,不可管理任务生命周期(重试/发货/退号等) + assert.equal(support.canOperateAssistedTask, true) + assert.equal(support.canManageTaskLifecycle, false) + + // 运营:两者皆可 + assert.equal(operator.canOperateAssistedTask, true) + assert.equal(operator.canManageTaskLifecycle, true) +}) + test('mapKuaishouCloudFulfillmentContext exposes cloud account display labels', () => { const flow = mapKuaishouCloudFulfillmentContext( { diff --git a/apps/backend/src/services/admin/write/kuaishou-cloud-actions.ts b/apps/backend/src/services/admin/write/kuaishou-cloud-actions.ts index 042a6c80..35ac0aab 100644 --- a/apps/backend/src/services/admin/write/kuaishou-cloud-actions.ts +++ b/apps/backend/src/services/admin/write/kuaishou-cloud-actions.ts @@ -210,7 +210,8 @@ export async function rebindAdminTaskKuaishouCloudRole( const task = await getRequiredTask(taskId) const viewerContext = createAdminViewerContext(session) - if (!viewerContext.canManageTaskLifecycle) { + // 与刷新角色一致:客服可换绑协助处理 UID 绑错,不开放完整任务生命周期 + if (!viewerContext.canOperateAssistedTask) { throw createHttpError('当前账号没有权限换绑角色', { statusCode: 403, errorCode: 'admin_task_rebind_kuaishou_cloud_forbidden',