feat(worker-platform): 支持后台人工调级锁定等级与解除自动升级限制

- 做了什么:
  1. 新增数据库迁移 098_worker_level_lock.sql,在 worker_users 表增加 is_level_locked、level_locked_at、level_locked_reason、level_locked_by 字段;
  2. 后端自动升级函数 maybeUpgradeWorkerLevelWithClient 和判定函数 shouldApplyAutomaticWorkerLevel 增加 is_level_locked 守卫,被锁定的打手跳过自动升级;
  3. 后台打手审核调级服务 reviewAdminWorkerUser 支持等级锁定参数与变更记录,人工调级时自动写入 worker_level_logs 流水,并提供 updateAdminWorkerLevelLock 专用接口;
  4. 前端打手列表等级列展示「已锁定」状态及 Tooltip 原因,调级弹窗支持「锁定自动升级」开关与原因输入,操作列提供「解除限制」快捷操作;
  5. 打手详情抽屉补充展示等级锁定说明。
- 为什么:
  解决打手被人工调整等级(如 VIP5 调至 VIP4)后,因历史累计完成单数满足门槛导致后续结算再次自动升级回高等级的问题,并支持后续随时解除限制恢复自动升级。
- 验证方式:
  1. 后端新增 shouldApplyAutomaticWorkerLevel 与 mapWorkerUser 单元测试,运行 npm run check(670 测试全过,类型与 SQL guard 验证通过);
  2. 前端新增 worker-level-lock.test.ts 单元测试,运行 npm run check(130 测试全过,typecheck 与 build 验证通过)。
This commit is contained in:
yml2213
2026-10-10 13:44:57 +08:00
parent 687bb40db1
commit 85fc2eaa7c
15 changed files with 395 additions and 11 deletions
@@ -9,6 +9,7 @@ import {
listAdminWorkerWithdrawalAccounts,
resetAdminWorkerPassword,
reviewAdminWorkerUser,
updateAdminWorkerLevelLock,
saveAdminWorkerWithdrawalAccount,
setAdminWorkerPersonalDepositFreeAmount,
updateAdminWorkerWithdrawalBlacklist,
@@ -68,7 +69,12 @@ router.post(
'/worker-platform/workers/:workerId/review',
requireAdminRoles(['admin', 'operator']),
createJsonHandler(
(req) => reviewAdminWorkerUser(String(req.params.workerId || ''), req.body || {}),
(req) =>
reviewAdminWorkerUser(
String(req.params.workerId || ''),
req.body || {},
req.adminSession?.username || '',
),
{
successMessage: '打手账号状态已更新',
errorMessage: '更新打手账号状态失败',
@@ -83,6 +89,30 @@ router.post(
),
)
router.post(
'/worker-platform/workers/:workerId/level-lock',
requireAdminRoles(['admin', 'operator']),
createJsonHandler(
(req) =>
updateAdminWorkerLevelLock(
String(req.params.workerId || ''),
req.body || {},
req.adminSession?.username || '',
),
{
successMessage: '打手等级锁定状态已更新',
errorMessage: '更新打手等级锁定状态失败',
scope: '[admin/worker-platform/workers/:workerId/level-lock]',
audit: (req, data) => ({
action: 'worker_level_lock_updated',
targetType: 'worker',
targetId: String(req.params.workerId || ''),
data: data && typeof data === 'object' ? (data as Record<string, unknown>) : {},
}),
},
),
)
router.post(
'/worker-platform/workers/:workerId/reset-password',
requireAdminRoles(['admin', 'operator']),