接单平台新增 VIP 等级体系与邀请绑定
- 等级按累计验收通过工单数自动升级(阈值后台可配),VIP1-5 默认配置 10/30/50/100 - 存量打手统一归 VIP1,原 regular 等级停用 - 注册支持邀请码绑定邀请人,每人生成唯一邀请码 - 新增 worker_level_logs 升级流水表 - 个人中心展示邀请码与升级进度,管理端等级表单支持升级阈值配置
This commit is contained in:
@@ -26,6 +26,10 @@ export type WorkerUserRow = {
|
||||
created_at: string
|
||||
updated_at: string
|
||||
reviewed_at: string | null
|
||||
inviter_id: number | null
|
||||
invite_code: string
|
||||
inviter_username?: string
|
||||
inviter_display_name?: string
|
||||
level_key?: string
|
||||
level_name?: string
|
||||
level_permission_json?: string | Record<string, unknown>
|
||||
@@ -192,6 +196,8 @@ type CreateWorkerInput = {
|
||||
phone: string
|
||||
levelId: number | null
|
||||
status: string
|
||||
inviterId?: number | null
|
||||
inviteCode: string
|
||||
now: string
|
||||
}
|
||||
|
||||
@@ -218,12 +224,15 @@ const WORKER_USER_SELECT = `
|
||||
wl.level_key,
|
||||
wl.name AS level_name,
|
||||
wl.permission_json AS level_permission_json,
|
||||
inv.username AS inviter_username,
|
||||
inv.display_name AS inviter_display_name,
|
||||
ww.available_amount,
|
||||
ww.frozen_deposit_amount,
|
||||
ww.total_credited_amount,
|
||||
ww.total_settled_amount
|
||||
FROM worker_users wu
|
||||
LEFT JOIN worker_levels wl ON wl.id = wu.level_id
|
||||
LEFT JOIN worker_users inv ON inv.id = wu.inviter_id
|
||||
LEFT JOIN worker_wallets ww ON ww.worker_id = wu.id
|
||||
`
|
||||
|
||||
@@ -325,6 +334,16 @@ export async function getWorkerUserByUsername(username: string): Promise<WorkerU
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export async function getWorkerUserByInviteCode(
|
||||
inviteCode: string,
|
||||
): Promise<WorkerUserRow | null> {
|
||||
const result = await query<WorkerUserRow>(
|
||||
`${WORKER_USER_SELECT} WHERE wu.invite_code = $1 LIMIT 1`,
|
||||
[String(inviteCode || '').trim().toUpperCase()],
|
||||
)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export async function getWorkerUserById(workerId: number | string): Promise<WorkerUserRow | null> {
|
||||
const result = await query<WorkerUserRow>(`${WORKER_USER_SELECT} WHERE wu.id = $1 LIMIT 1`, [
|
||||
Number(workerId),
|
||||
@@ -338,8 +357,9 @@ export async function createWorkerUser(input: CreateWorkerInput): Promise<Worker
|
||||
`
|
||||
INSERT INTO worker_users (
|
||||
username, password_hash, display_name, phone, level_id, status,
|
||||
inviter_id, invite_code,
|
||||
created_at, updated_at
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
RETURNING id
|
||||
`,
|
||||
[
|
||||
@@ -349,6 +369,8 @@ export async function createWorkerUser(input: CreateWorkerInput): Promise<Worker
|
||||
input.phone,
|
||||
input.levelId,
|
||||
input.status,
|
||||
input.inviterId || null,
|
||||
input.inviteCode,
|
||||
input.now,
|
||||
input.now,
|
||||
],
|
||||
@@ -824,6 +846,64 @@ export async function countWorkerAcceptedOrders(workerId: number | string): Prom
|
||||
return Number(result.rows[0]?.total || 0)
|
||||
}
|
||||
|
||||
export async function maybeUpgradeWorkerLevelWithClient(
|
||||
client: PoolClient,
|
||||
workerId: number,
|
||||
now: string,
|
||||
): Promise<WorkerUserRow | null> {
|
||||
const acceptedResult = await client.query<{ total: number }>(
|
||||
`
|
||||
SELECT COUNT(*)::int AS total
|
||||
FROM work_orders
|
||||
WHERE assigned_worker_id = $1
|
||||
AND status = 'accepted'
|
||||
`,
|
||||
[workerId],
|
||||
)
|
||||
const acceptedCount = Number(acceptedResult.rows[0]?.total || 0)
|
||||
|
||||
const nextLevelResult = await client.query<{ id: number }>(
|
||||
`
|
||||
SELECT id
|
||||
FROM worker_levels
|
||||
WHERE status = 'active'
|
||||
AND COALESCE((permission_json->>'upgradeThreshold')::int, 0) <= $1
|
||||
ORDER BY COALESCE((permission_json->>'upgradeThreshold')::int, 0) DESC, sort_order ASC
|
||||
LIMIT 1
|
||||
`,
|
||||
[acceptedCount],
|
||||
)
|
||||
const nextLevelId = Number(nextLevelResult.rows[0]?.id || 0)
|
||||
if (!nextLevelId) return null
|
||||
|
||||
const current = await client.query<{ id: number; level_id: number | null }>(
|
||||
'SELECT id, level_id FROM worker_users WHERE id = $1 LIMIT 1',
|
||||
[workerId],
|
||||
)
|
||||
const currentRow = current.rows[0]
|
||||
if (!currentRow || Number(currentRow.level_id || 0) === nextLevelId) return null
|
||||
|
||||
await client.query(
|
||||
'UPDATE worker_users SET level_id = $1, updated_at = $2 WHERE id = $3',
|
||||
[nextLevelId, now, workerId],
|
||||
)
|
||||
await client.query(
|
||||
`
|
||||
INSERT INTO worker_level_logs (
|
||||
worker_id, from_level_id, to_level_id, reason, payload_json, created_at
|
||||
) VALUES ($1, $2, $3, 'auto', $4::jsonb, $5)
|
||||
`,
|
||||
[
|
||||
workerId,
|
||||
currentRow.level_id || null,
|
||||
nextLevelId,
|
||||
JSON.stringify({ acceptedCount }),
|
||||
now,
|
||||
],
|
||||
)
|
||||
return getWorkerUserByIdWithClient(client, workerId)
|
||||
}
|
||||
|
||||
export async function getWorkCategoryByKey(categoryKey: string): Promise<WorkCategoryRow | null> {
|
||||
const result = await query<WorkCategoryRow>(
|
||||
'SELECT * FROM work_categories WHERE category_key = $1 LIMIT 1',
|
||||
@@ -1304,6 +1384,10 @@ export async function acceptWorkOrderAndSettle(input: {
|
||||
now: input.now,
|
||||
})
|
||||
|
||||
if (workerId > 0) {
|
||||
await maybeUpgradeWorkerLevelWithClient(client, workerId, input.now)
|
||||
}
|
||||
|
||||
return getWorkOrderByIdWithClient(client, input.workOrderId)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user