feat(worker-platform): 增加寄卖白名单总开关,关闭后全员可发布
资金配置与打手列表可一键切换白名单模式;发布校验、打手端入口按总开关放行。同时收紧未开通判定、关权限确认和开通弹窗文案。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
|
||||
import { canWorkerPublishConsignment } from './consignment-whitelist.js'
|
||||
|
||||
test('白名单关闭时所有打手都能发布寄卖', () => {
|
||||
assert.equal(
|
||||
canWorkerPublishConsignment({ whitelistEnabled: false, workerCanConsign: false }),
|
||||
true,
|
||||
)
|
||||
assert.equal(
|
||||
canWorkerPublishConsignment({ whitelistEnabled: false, workerCanConsign: true }),
|
||||
true,
|
||||
)
|
||||
})
|
||||
|
||||
test('白名单开启时仅开通权限的打手能发布', () => {
|
||||
assert.equal(
|
||||
canWorkerPublishConsignment({ whitelistEnabled: true, workerCanConsign: false }),
|
||||
false,
|
||||
)
|
||||
assert.equal(
|
||||
canWorkerPublishConsignment({ whitelistEnabled: true, workerCanConsign: true }),
|
||||
true,
|
||||
)
|
||||
})
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* 寄卖白名单总开关。
|
||||
*
|
||||
* 开启:只有 worker.can_consign 的打手能发布新寄卖。
|
||||
* 关闭:所有打手都能发布;打手身上的白名单标记仍保留,方便再次开启。
|
||||
*/
|
||||
export function canWorkerPublishConsignment(input: {
|
||||
whitelistEnabled: boolean
|
||||
workerCanConsign: boolean
|
||||
}): boolean {
|
||||
return !input.whitelistEnabled || input.workerCanConsign
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { readFile } from 'node:fs/promises'
|
||||
import test from 'node:test'
|
||||
|
||||
import { mapWorkerUser } from './worker-finance-mappers.js'
|
||||
import { parseAdminWorkerConsignmentPermission } from './admin-worker-user-service.js'
|
||||
import type { WorkerUserRow } from '../../repositories/worker-platform/types.js'
|
||||
|
||||
test('mapWorkerUser 准确回显打手寄卖白名单权限与激活时间', () => {
|
||||
@@ -48,3 +49,12 @@ test('管理员打手管理路由注册了寄卖白名单权限更新接口', as
|
||||
'路由必须调用 updateAdminWorkerConsignmentPermission',
|
||||
)
|
||||
})
|
||||
|
||||
test('更新寄卖权限必须显式传开关,空 body 不得默认关闭', () => {
|
||||
assert.throws(
|
||||
() => parseAdminWorkerConsignmentPermission({}),
|
||||
(error: unknown) => (error as { errorCode?: string }).errorCode === 'can_consign_required',
|
||||
)
|
||||
assert.equal(parseAdminWorkerConsignmentPermission({ canConsign: true }), true)
|
||||
assert.equal(parseAdminWorkerConsignmentPermission({ enabled: false }), false)
|
||||
})
|
||||
|
||||
@@ -180,12 +180,23 @@ export async function updateAdminWorkerWithdrawalBlacklist(
|
||||
return { worker: mapWorkerUser(updated || worker) }
|
||||
}
|
||||
|
||||
export function parseAdminWorkerConsignmentPermission(payload: JsonObject = {}): boolean {
|
||||
const raw = payload.canConsign ?? payload.enabled
|
||||
if (raw === undefined || raw === null || raw === '') {
|
||||
throw createHttpError('请明确指定寄卖权限开关', {
|
||||
statusCode: 400,
|
||||
errorCode: 'can_consign_required',
|
||||
})
|
||||
}
|
||||
return normalizeBoolean(raw, false)
|
||||
}
|
||||
|
||||
export async function updateAdminWorkerConsignmentPermission(
|
||||
workerId: number | string,
|
||||
payload: JsonObject = {},
|
||||
) {
|
||||
const worker = await getRequiredWorker(workerId)
|
||||
const canConsign = normalizeBoolean(payload.canConsign ?? payload.enabled, false)
|
||||
const canConsign = parseAdminWorkerConsignmentPermission(payload)
|
||||
const now = nowIso()
|
||||
const updated = await updateWorkerConsignmentPermission({
|
||||
workerId: worker.id,
|
||||
|
||||
@@ -87,8 +87,8 @@ test('寄卖提报硬校验:璀璨数不能超过钻石等级(1钻最多1璀
|
||||
test('未开通寄卖白名单权限的打手提报寄卖时被强校验拦截', async () => {
|
||||
const serviceSource = await readServiceSource()
|
||||
assert.ok(
|
||||
serviceSource.includes('!worker.can_consign'),
|
||||
'服务端必须拦截未开通寄卖权限的打手',
|
||||
serviceSource.includes('canWorkerPublishConsignment('),
|
||||
'服务端必须按白名单总开关决定是否拦截未开通打手',
|
||||
)
|
||||
assert.ok(
|
||||
serviceSource.includes("errorCode: 'consignment_permission_required'"),
|
||||
|
||||
@@ -12,6 +12,7 @@ import { listEnabledWorkProductRulesForSuggestion } from '../../repositories/wor
|
||||
import { listWorkOrdersByIds } from '../../repositories/worker-platform/work-order-query-repo.js'
|
||||
import type { WorkOrderRow } from '../../repositories/worker-platform/types.js'
|
||||
import { deriveConsignmentItemStage } from '../../domain/consignment-item-stage.js'
|
||||
import { canWorkerPublishConsignment } from '../../domain/consignment-whitelist.js'
|
||||
import { suggestConsignmentPriceFromRules } from './consignment-matching-service.js'
|
||||
import { hasInternalConsignmentFulfillment } from '../../repositories/worker-platform/work-order-gift-repo.js'
|
||||
import type { JsonObject } from '../../types/json.js'
|
||||
@@ -20,7 +21,10 @@ import { generateConsignmentSubmissionNo } from '../../utils/random.js'
|
||||
import { nowIso } from '../../utils/time.js'
|
||||
import { refreshStoredFileUrl } from '../file-storage/file-storage-service.js'
|
||||
import { publishWorkerWalletRealtimeChange } from '../realtime/realtime-event-service.js'
|
||||
import { getConsignmentPublishDepositFen as getConfiguredConsignmentPublishDepositFen } from './worker-finance-config-service.js'
|
||||
import {
|
||||
getConsignmentPublishDepositFen as getConfiguredConsignmentPublishDepositFen,
|
||||
getWorkerFinanceConfig,
|
||||
} from './worker-finance-config-service.js'
|
||||
import { confirmAdminWorkOrderFriendAdded } from './admin-work-order-gift-service.js'
|
||||
import { submitWorkerOrderAcceptance } from './worker-acceptance-service.js'
|
||||
import type { WorkerSession } from './worker-session-context-service.js'
|
||||
@@ -144,8 +148,13 @@ export async function publishWorkerConsignment(workerId: number, payload: JsonOb
|
||||
if (!worker) {
|
||||
throw createHttpError('打手账户不存在', { statusCode: 404, errorCode: 'worker_not_found' })
|
||||
}
|
||||
if (!worker.can_consign) {
|
||||
throw createHttpError('您暂未开通寄卖权限,请联系客服开通', {
|
||||
if (
|
||||
!canWorkerPublishConsignment({
|
||||
whitelistEnabled: getWorkerFinanceConfig().consignmentWhitelistEnabled,
|
||||
workerCanConsign: Boolean(worker.can_consign),
|
||||
})
|
||||
) {
|
||||
throw createHttpError('您暂未开通寄卖权限,请联系客服申请开通', {
|
||||
statusCode: 403,
|
||||
errorCode: 'consignment_permission_required',
|
||||
})
|
||||
|
||||
@@ -12,6 +12,11 @@ export type WorkerFinanceConfig = {
|
||||
rewardUnfreezeEnabled: boolean
|
||||
/** 苏尔南寄卖发布所需履约押金(分),默认 2000 = ¥20 */
|
||||
consignmentPublishDepositFen: number
|
||||
/**
|
||||
* 寄卖白名单总开关。开启后仅 can_consign 打手可发布;关闭后所有打手可发布。
|
||||
* 缺省视为开启,保持当前白名单行为。
|
||||
*/
|
||||
consignmentWhitelistEnabled: boolean
|
||||
recharge: {
|
||||
enabled: boolean
|
||||
channelName: string
|
||||
@@ -71,6 +76,9 @@ export function normalizeWorkerFinanceConfig(rawValue: unknown): WorkerFinanceCo
|
||||
0,
|
||||
1_000_000,
|
||||
),
|
||||
consignmentWhitelistEnabled:
|
||||
source.consignmentWhitelistEnabled !== false &&
|
||||
source.consignment_whitelist_enabled !== false,
|
||||
recharge: {
|
||||
enabled: typeof recharge.enabled === 'boolean' ? recharge.enabled : true,
|
||||
channelName: String(recharge.channelName || recharge.channel_name || '').trim(),
|
||||
@@ -97,6 +105,7 @@ export function createDefaultWorkerFinanceConfig(): WorkerFinanceConfig {
|
||||
depositUnfreezeDays: 3,
|
||||
rewardUnfreezeEnabled: true,
|
||||
consignmentPublishDepositFen: 2000,
|
||||
consignmentWhitelistEnabled: true,
|
||||
recharge: {
|
||||
enabled: true,
|
||||
channelName: '',
|
||||
@@ -120,6 +129,7 @@ function refreshWorkerFinanceConfig(config: WorkerFinanceConfig): WorkerFinanceC
|
||||
depositUnfreezeDays: config.depositUnfreezeDays,
|
||||
rewardUnfreezeEnabled: config.rewardUnfreezeEnabled,
|
||||
consignmentPublishDepositFen: config.consignmentPublishDepositFen,
|
||||
consignmentWhitelistEnabled: config.consignmentWhitelistEnabled,
|
||||
recharge: {
|
||||
...config.recharge,
|
||||
qrCodeImage: config.recharge.qrCodeImage
|
||||
|
||||
@@ -402,6 +402,19 @@ test('寄卖发布押金配置保留显式零值并回退非法值', () => {
|
||||
)
|
||||
})
|
||||
|
||||
test('寄卖白名单总开关缺省开启,显式 false 才全员开放', () => {
|
||||
assert.equal(normalizeWorkerFinanceConfig({}).consignmentWhitelistEnabled, true)
|
||||
assert.equal(
|
||||
normalizeWorkerFinanceConfig({ consignmentWhitelistEnabled: false })
|
||||
.consignmentWhitelistEnabled,
|
||||
false,
|
||||
)
|
||||
assert.equal(
|
||||
normalizeWorkerFinanceConfig({ consignmentWhitelistEnabled: true }).consignmentWhitelistEnabled,
|
||||
true,
|
||||
)
|
||||
})
|
||||
|
||||
test('寄卖押金释放先锁钱包再读流水,冻结额不足时拒绝部分退款', async () => {
|
||||
const statements: string[] = []
|
||||
const client = {
|
||||
|
||||
Reference in New Issue
Block a user