feat: 冻结/拒绝的打手账号禁止登录

- loginWorker 仅允许 active 状态登录,rejected(冻结)/pending_review/disabled 均拦截并返回对应错误码
- verifyWorkerSessionToken 对非 active 状态直接失效会话,冻结后已登录会话立即退出
- 补充 assertWorkerLoginAllowed 四种状态的单元测试
This commit is contained in:
yml2213
2026-08-14 15:14:26 +08:00
parent da1f2469bd
commit dffe32e84a
2 changed files with 90 additions and 7 deletions
@@ -1,8 +1,71 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import type { WorkOrderRow } from '../../repositories/worker-platform/index.js'
import { resolveCollectSubmitTargetWorkOrder } from './index.js'
import type { WorkOrderRow, WorkerUserRow } from '../../repositories/worker-platform/index.js'
import { assertWorkerLoginAllowed, resolveCollectSubmitTargetWorkOrder } from './index.js'
function buildWorkerUserRow(overrides: Partial<WorkerUserRow> = {}): WorkerUserRow {
return {
id: 1,
username: 'jd01',
password_hash: 'scrypt$salt$hash',
display_name: '测试打手',
phone: '',
level_id: 1,
status: 'active',
review_note: '',
session_version: 1,
created_at: '2026-07-28T00:00:00.000Z',
updated_at: '2026-07-28T00:00:00.000Z',
reviewed_at: null,
inviter_id: null,
invite_code: 'TEST0001',
...overrides,
}
}
test('assertWorkerLoginAllowed allows active workers', () => {
assert.doesNotThrow(() => assertWorkerLoginAllowed(buildWorkerUserRow()))
})
test('assertWorkerLoginAllowed blocks rejected (frozen) workers with worker_rejected', () => {
assert.throws(
() => assertWorkerLoginAllowed(buildWorkerUserRow({ status: 'rejected' })),
(error: unknown) =>
Boolean(
error &&
typeof error === 'object' &&
(error as { statusCode?: number }).statusCode === 403 &&
(error as { errorCode?: string }).errorCode === 'worker_rejected',
),
)
})
test('assertWorkerLoginAllowed blocks pending review workers with worker_pending_review', () => {
assert.throws(
() => assertWorkerLoginAllowed(buildWorkerUserRow({ status: 'pending_review' })),
(error: unknown) =>
Boolean(
error &&
typeof error === 'object' &&
(error as { statusCode?: number }).statusCode === 403 &&
(error as { errorCode?: string }).errorCode === 'worker_pending_review',
),
)
})
test('assertWorkerLoginAllowed blocks disabled workers with worker_disabled', () => {
assert.throws(
() => assertWorkerLoginAllowed(buildWorkerUserRow({ status: 'disabled' })),
(error: unknown) =>
Boolean(
error &&
typeof error === 'object' &&
(error as { statusCode?: number }).statusCode === 403 &&
(error as { errorCode?: string }).errorCode === 'worker_disabled',
),
)
})
function buildWorkOrderRow(
overrides: Partial<WorkOrderRow> = {},
@@ -335,14 +335,34 @@ export async function loginWorker(username: unknown, password: unknown) {
})
}
if (worker.status === 'disabled') {
throw createHttpError('接单账号已停用', {
assertWorkerLoginAllowed(worker)
return createWorkerSession(worker)
}
export function assertWorkerLoginAllowed(worker: WorkerUserRow) {
if (worker.status === 'active') {
return
}
if (worker.status === 'rejected') {
throw createHttpError('接单账号已被冻结,无法登录', {
statusCode: 403,
errorCode: 'worker_disabled',
errorCode: 'worker_rejected',
})
}
return createWorkerSession(worker)
if (worker.status === 'pending_review') {
throw createHttpError('接单账号待审核,暂无法登录', {
statusCode: 403,
errorCode: 'worker_pending_review',
})
}
throw createHttpError('接单账号已停用', {
statusCode: 403,
errorCode: 'worker_disabled',
})
}
export async function logoutWorkerSession(token: unknown) {
@@ -411,7 +431,7 @@ export async function verifyWorkerSessionToken(token: unknown): Promise<WorkerSe
const workerId = Number(payload.uid || 0)
const worker = await getWorkerUserById(workerId)
if (!worker || worker.status === 'disabled') {
if (!worker || worker.status !== 'active') {
throw createHttpError('接单账号已不可用,请重新登录', {
statusCode: 401,
errorCode: 'worker_auth_user_invalid',