拆分打手注册服务
This commit is contained in:
@@ -6,6 +6,7 @@ export * from './worker-session-view.js'
|
|||||||
export * from './worker-session-auth-service.js'
|
export * from './worker-session-auth-service.js'
|
||||||
export * from './worker-credentials-service.js'
|
export * from './worker-credentials-service.js'
|
||||||
export * from './worker-sms-service.js'
|
export * from './worker-sms-service.js'
|
||||||
|
export * from './worker-registration-service.js'
|
||||||
export * from './worker-platform-defaults.js'
|
export * from './worker-platform-defaults.js'
|
||||||
export * from './work-order-timeout-policy.js'
|
export * from './work-order-timeout-policy.js'
|
||||||
export * from './after-sales-service.js'
|
export * from './after-sales-service.js'
|
||||||
|
|||||||
@@ -0,0 +1,208 @@
|
|||||||
|
import crypto from 'node:crypto'
|
||||||
|
|
||||||
|
import {
|
||||||
|
consumeWorkerSmsCode,
|
||||||
|
createWorkerUser,
|
||||||
|
getLatestWorkerSmsCode,
|
||||||
|
getWorkerUserByDisplayName,
|
||||||
|
getWorkerUserByInviteCode,
|
||||||
|
getWorkerUserByPhone,
|
||||||
|
getWorkerUserByUsername,
|
||||||
|
recordWorkerSmsCodeFailure,
|
||||||
|
revokeAllWorkerSessions,
|
||||||
|
updateWorkerPassword,
|
||||||
|
} from '../../repositories/worker-platform/index.js'
|
||||||
|
import type { JsonObject } from '../../types/json.js'
|
||||||
|
import { createHttpError } from '../../utils/http.js'
|
||||||
|
import { nowIso } from '../../utils/time.js'
|
||||||
|
import {
|
||||||
|
ensureWorkerAuthConfigured,
|
||||||
|
hashWorkerPassword,
|
||||||
|
mapWorkerUser,
|
||||||
|
normalizePassword,
|
||||||
|
normalizeSessionVersion,
|
||||||
|
validateWorkerPassword,
|
||||||
|
} from './mappers.js'
|
||||||
|
import { ensureWorkerPlatformDefaults } from './worker-platform-defaults.js'
|
||||||
|
import { hashSmsCode, normalizeWorkerPhone } from './worker-sms-service.js'
|
||||||
|
|
||||||
|
const INVITE_CODE_LENGTH = 8
|
||||||
|
const INVITE_CODE_RETRY_LIMIT = 5
|
||||||
|
const WORKER_SMS_MAX_VERIFY_ATTEMPTS = 5
|
||||||
|
|
||||||
|
export async function resetWorkerPassword(payload: JsonObject = {}) {
|
||||||
|
const phone = normalizeWorkerPhone(payload.phone)
|
||||||
|
const code = String(payload.code || payload.smsCode || '').trim()
|
||||||
|
const password = normalizePassword(payload.password || payload.newPassword)
|
||||||
|
validateWorkerPassword(password)
|
||||||
|
if (!code) {
|
||||||
|
throw createHttpError('请输入短信验证码', {
|
||||||
|
statusCode: 400,
|
||||||
|
errorCode: 'worker_sms_code_required',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const worker = await getWorkerUserByPhone(phone)
|
||||||
|
if (!worker) {
|
||||||
|
throw createHttpError('该手机号未注册', {
|
||||||
|
statusCode: 404,
|
||||||
|
errorCode: 'worker_phone_not_registered',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const latest = await getLatestWorkerSmsCode(phone, 'password_reset')
|
||||||
|
const codeValid = isWorkerSmsCodeValid(latest, code)
|
||||||
|
if (!codeValid) {
|
||||||
|
await recordInvalidWorkerSmsCode(latest)
|
||||||
|
throw createHttpError('短信验证码错误或已过期', {
|
||||||
|
statusCode: 400,
|
||||||
|
errorCode: 'worker_sms_code_invalid',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!(await consumeWorkerSmsCode(latest!.id, nowIso()))) {
|
||||||
|
throw createHttpError('验证码已失效,请重新获取', {
|
||||||
|
statusCode: 400,
|
||||||
|
errorCode: 'worker_sms_code_invalid',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const now = nowIso()
|
||||||
|
const updated = await updateWorkerPassword({
|
||||||
|
workerId: worker.id,
|
||||||
|
passwordHash: hashWorkerPassword(password),
|
||||||
|
sessionVersion: normalizeSessionVersion(worker.session_version) + 1,
|
||||||
|
now,
|
||||||
|
})
|
||||||
|
await revokeAllWorkerSessions(worker.id, now)
|
||||||
|
return { worker: mapWorkerUser(updated || worker), reloginRequired: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function registerWorker(payload: JsonObject = {}) {
|
||||||
|
ensureWorkerAuthConfigured()
|
||||||
|
const phone = normalizeWorkerPhone(payload.phone)
|
||||||
|
const code = String(payload.code || payload.smsCode || '').trim()
|
||||||
|
const password = normalizePassword(payload.password)
|
||||||
|
const displayName = String(payload.displayName || payload.nickname || '').trim()
|
||||||
|
const inviteCode = String(payload.inviteCode || '')
|
||||||
|
.trim()
|
||||||
|
.toUpperCase()
|
||||||
|
|
||||||
|
validateWorkerPassword(password)
|
||||||
|
if (!displayName) {
|
||||||
|
throw createHttpError('请输入昵称', {
|
||||||
|
statusCode: 400,
|
||||||
|
errorCode: 'worker_display_name_required',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (!code) {
|
||||||
|
throw createHttpError('请输入短信验证码', {
|
||||||
|
statusCode: 400,
|
||||||
|
errorCode: 'worker_sms_code_required',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const latest = await getLatestWorkerSmsCode(phone, 'register')
|
||||||
|
if (!isWorkerSmsCodeValid(latest, code)) {
|
||||||
|
await recordInvalidWorkerSmsCode(latest)
|
||||||
|
throw createHttpError('短信验证码错误或已过期', {
|
||||||
|
statusCode: 400,
|
||||||
|
errorCode: 'worker_sms_code_invalid',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (await getWorkerUserByUsername(phone)) {
|
||||||
|
throw createHttpError('该手机号已注册,请直接登录', {
|
||||||
|
statusCode: 409,
|
||||||
|
errorCode: 'worker_user_exists',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (await getWorkerUserByPhone(phone)) {
|
||||||
|
throw createHttpError('该手机号已注册,请直接登录', {
|
||||||
|
statusCode: 409,
|
||||||
|
errorCode: 'worker_phone_exists',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const inviter = inviteCode ? await getWorkerUserByInviteCode(inviteCode) : null
|
||||||
|
if (inviteCode && !inviter) {
|
||||||
|
throw createHttpError('邀请码无效', {
|
||||||
|
statusCode: 400,
|
||||||
|
errorCode: 'worker_invite_code_invalid',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (inviter && inviter.status !== 'active') {
|
||||||
|
throw createHttpError('邀请人账号不可用', {
|
||||||
|
statusCode: 400,
|
||||||
|
errorCode: 'worker_inviter_invalid',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (await getWorkerUserByDisplayName(displayName)) {
|
||||||
|
throw createHttpError('该昵称已被使用,请更换昵称', {
|
||||||
|
statusCode: 409,
|
||||||
|
errorCode: 'worker_display_name_taken',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaults = await ensureWorkerPlatformDefaults()
|
||||||
|
const now = nowIso()
|
||||||
|
const created = await createWorkerUser({
|
||||||
|
username: phone,
|
||||||
|
passwordHash: hashWorkerPassword(password),
|
||||||
|
displayName,
|
||||||
|
phone,
|
||||||
|
levelId: defaults.level?.id || null,
|
||||||
|
status: 'active',
|
||||||
|
inviterId: inviter ? Number(inviter.id) : null,
|
||||||
|
inviteCode: await generateInviteCode(),
|
||||||
|
smsCodeId: latest!.id,
|
||||||
|
now,
|
||||||
|
})
|
||||||
|
if (!created) {
|
||||||
|
throw createHttpError('接单账号注册失败', {
|
||||||
|
statusCode: 500,
|
||||||
|
errorCode: 'worker_register_failed',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return { worker: mapWorkerUser(created), reviewRequired: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
function isWorkerSmsCodeValid(
|
||||||
|
latest: { consumed_at: string | null; expires_at: string; code_hash: string } | null,
|
||||||
|
code: string,
|
||||||
|
) {
|
||||||
|
return Boolean(
|
||||||
|
latest &&
|
||||||
|
!latest.consumed_at &&
|
||||||
|
new Date(latest.expires_at).getTime() > Date.now() &&
|
||||||
|
latest.code_hash === hashSmsCode(code),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function recordInvalidWorkerSmsCode(
|
||||||
|
latest: { id: number; consumed_at: string | null } | null,
|
||||||
|
) {
|
||||||
|
if (!latest || latest.consumed_at) return
|
||||||
|
const failed = await recordWorkerSmsCodeFailure(
|
||||||
|
latest.id,
|
||||||
|
nowIso(),
|
||||||
|
WORKER_SMS_MAX_VERIFY_ATTEMPTS,
|
||||||
|
)
|
||||||
|
if (failed?.consumed_at) {
|
||||||
|
throw createHttpError('验证码错误次数过多,请重新获取验证码', {
|
||||||
|
statusCode: 400,
|
||||||
|
errorCode: 'worker_sms_code_locked',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateInviteCode(): Promise<string> {
|
||||||
|
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'
|
||||||
|
for (let attempt = 0; attempt < INVITE_CODE_RETRY_LIMIT; attempt += 1) {
|
||||||
|
const candidate = Array.from(
|
||||||
|
{ length: INVITE_CODE_LENGTH },
|
||||||
|
() => chars[Math.floor(crypto.randomInt(chars.length))],
|
||||||
|
).join('')
|
||||||
|
if (!(await getWorkerUserByInviteCode(candidate))) return candidate
|
||||||
|
}
|
||||||
|
throw createHttpError('邀请码生成失败,请重试', {
|
||||||
|
statusCode: 500,
|
||||||
|
errorCode: 'worker_invite_code_generation_failed',
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import crypto from 'node:crypto'
|
|
||||||
|
|
||||||
import { runtimeConfig } from '../../config/runtime.js'
|
import { runtimeConfig } from '../../config/runtime.js'
|
||||||
import { WORK_ORDER_STATUS } from '../../domain/work-order-status.js'
|
import { WORK_ORDER_STATUS } from '../../domain/work-order-status.js'
|
||||||
import {
|
import {
|
||||||
@@ -15,12 +13,10 @@ import {
|
|||||||
consumeWorkerSmsCode,
|
consumeWorkerSmsCode,
|
||||||
createWorkOrderEvent,
|
createWorkOrderEvent,
|
||||||
createWorkerFinanceRequest,
|
createWorkerFinanceRequest,
|
||||||
createWorkerUser,
|
|
||||||
createWorkerWithdrawalAccount as createWorkerWithdrawalAccountRecord,
|
createWorkerWithdrawalAccount as createWorkerWithdrawalAccountRecord,
|
||||||
getWorkerFinanceRequestSummary,
|
getWorkerFinanceRequestSummary,
|
||||||
getLatestWorkerSmsCode,
|
getLatestWorkerSmsCode,
|
||||||
recordWorkerSmsCodeFailure,
|
recordWorkerSmsCodeFailure,
|
||||||
getWorkerUserByInviteCode,
|
|
||||||
getWorkerUserById,
|
getWorkerUserById,
|
||||||
getWorkerWithdrawalAccount,
|
getWorkerWithdrawalAccount,
|
||||||
getWorkerUserByPhone,
|
getWorkerUserByPhone,
|
||||||
@@ -34,7 +30,6 @@ import {
|
|||||||
getWorkerSessionBySessionId,
|
getWorkerSessionBySessionId,
|
||||||
listWorkerSessions,
|
listWorkerSessions,
|
||||||
revokeWorkerSession,
|
revokeWorkerSession,
|
||||||
revokeAllWorkerSessions,
|
|
||||||
touchWorkerSession,
|
touchWorkerSession,
|
||||||
joinWorkOrderShare,
|
joinWorkOrderShare,
|
||||||
listPendingMaterialWorkOrdersByPlatformOrderId,
|
listPendingMaterialWorkOrdersByPlatformOrderId,
|
||||||
@@ -67,7 +62,6 @@ import {
|
|||||||
updateWorkOrderShareDraftAcceptance,
|
updateWorkOrderShareDraftAcceptance,
|
||||||
upsertWorkerWorkOrderNote,
|
upsertWorkerWorkOrderNote,
|
||||||
markWorkerWorkOrderViewed,
|
markWorkerWorkOrderViewed,
|
||||||
updateWorkerPassword,
|
|
||||||
type WorkOrderRow,
|
type WorkOrderRow,
|
||||||
type WorkOrderShareRow,
|
type WorkOrderShareRow,
|
||||||
type WorkerSessionRow,
|
type WorkerSessionRow,
|
||||||
@@ -103,8 +97,6 @@ import {
|
|||||||
} from '../realtime/realtime-event-service.js'
|
} from '../realtime/realtime-event-service.js'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
INVITE_CODE_LENGTH,
|
|
||||||
INVITE_CODE_RETRY_LIMIT,
|
|
||||||
createWorkerSession,
|
createWorkerSession,
|
||||||
ensureWorkerAuthConfigured,
|
ensureWorkerAuthConfigured,
|
||||||
hashWorkerPassword,
|
hashWorkerPassword,
|
||||||
@@ -153,6 +145,7 @@ import {
|
|||||||
normalizeWorkerDeviceType,
|
normalizeWorkerDeviceType,
|
||||||
} from './worker-auth-policy.js'
|
} from './worker-auth-policy.js'
|
||||||
import { ensureWorkerPlatformDefaults } from './worker-platform-defaults.js'
|
import { ensureWorkerPlatformDefaults } from './worker-platform-defaults.js'
|
||||||
|
import { registerWorker, resetWorkerPassword } from './worker-registration-service.js'
|
||||||
import {
|
import {
|
||||||
hashSmsCode,
|
hashSmsCode,
|
||||||
normalizeWorkerPhone,
|
normalizeWorkerPhone,
|
||||||
@@ -189,6 +182,7 @@ export {
|
|||||||
} from './work-order-timeout-policy.js'
|
} from './work-order-timeout-policy.js'
|
||||||
export type { WorkOrderTimeoutPolicy } from './work-order-timeout-policy.js'
|
export type { WorkOrderTimeoutPolicy } from './work-order-timeout-policy.js'
|
||||||
export { changeWorkerPassword } from './worker-credentials-service.js'
|
export { changeWorkerPassword } from './worker-credentials-service.js'
|
||||||
|
export { registerWorker, resetWorkerPassword } from './worker-registration-service.js'
|
||||||
export {
|
export {
|
||||||
sendWorkerLoginSmsCode,
|
sendWorkerLoginSmsCode,
|
||||||
sendWorkerPasswordResetSmsCode,
|
sendWorkerPasswordResetSmsCode,
|
||||||
@@ -204,200 +198,6 @@ export {
|
|||||||
|
|
||||||
const WORKER_MAX_DEVICES = 3
|
const WORKER_MAX_DEVICES = 3
|
||||||
|
|
||||||
export async function resetWorkerPassword(payload: JsonObject = {}) {
|
|
||||||
const phone = normalizeWorkerPhone(payload.phone)
|
|
||||||
const code = String(payload.code || payload.smsCode || '').trim()
|
|
||||||
const password = normalizePassword(payload.password || payload.newPassword)
|
|
||||||
validateWorkerPassword(password)
|
|
||||||
if (!code) {
|
|
||||||
throw createHttpError('请输入短信验证码', {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'worker_sms_code_required',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const worker = await getWorkerUserByPhone(phone)
|
|
||||||
if (!worker) {
|
|
||||||
throw createHttpError('该手机号未注册', {
|
|
||||||
statusCode: 404,
|
|
||||||
errorCode: 'worker_phone_not_registered',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const latest = await getLatestWorkerSmsCode(phone, 'password_reset')
|
|
||||||
const codeValid = Boolean(
|
|
||||||
latest &&
|
|
||||||
!latest.consumed_at &&
|
|
||||||
new Date(latest.expires_at).getTime() > Date.now() &&
|
|
||||||
latest.code_hash === hashSmsCode(code),
|
|
||||||
)
|
|
||||||
if (!codeValid) {
|
|
||||||
if (latest && !latest.consumed_at) {
|
|
||||||
const failed = await recordWorkerSmsCodeFailure(
|
|
||||||
latest.id,
|
|
||||||
nowIso(),
|
|
||||||
WORKER_SMS_MAX_VERIFY_ATTEMPTS,
|
|
||||||
)
|
|
||||||
if (failed?.consumed_at) {
|
|
||||||
throw createHttpError('验证码错误次数过多,请重新获取验证码', {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'worker_sms_code_locked',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw createHttpError('短信验证码错误或已过期', {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'worker_sms_code_invalid',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const consumed = await consumeWorkerSmsCode(latest!.id, nowIso())
|
|
||||||
if (!consumed) {
|
|
||||||
throw createHttpError('验证码已失效,请重新获取', {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'worker_sms_code_invalid',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const now = nowIso()
|
|
||||||
const updated = await updateWorkerPassword({
|
|
||||||
workerId: worker.id,
|
|
||||||
passwordHash: hashWorkerPassword(password),
|
|
||||||
sessionVersion: normalizeSessionVersion(worker.session_version) + 1,
|
|
||||||
now,
|
|
||||||
})
|
|
||||||
await revokeAllWorkerSessions(worker.id, now)
|
|
||||||
return { worker: mapWorkerUser(updated || worker), reloginRequired: true }
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function registerWorker(payload: JsonObject = {}) {
|
|
||||||
ensureWorkerAuthConfigured()
|
|
||||||
const phone = normalizeWorkerPhone(payload.phone)
|
|
||||||
const code = String(payload.code || payload.smsCode || '').trim()
|
|
||||||
const password = normalizePassword(payload.password)
|
|
||||||
const displayName = String(payload.displayName || payload.nickname || '').trim()
|
|
||||||
const inviteCode = String(payload.inviteCode || '')
|
|
||||||
.trim()
|
|
||||||
.toUpperCase()
|
|
||||||
|
|
||||||
validateWorkerPassword(password)
|
|
||||||
if (!displayName) {
|
|
||||||
throw createHttpError('请输入昵称', {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'worker_display_name_required',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (!code) {
|
|
||||||
throw createHttpError('请输入短信验证码', {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'worker_sms_code_required',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const latest = await getLatestWorkerSmsCode(phone, 'register')
|
|
||||||
const codeValid = Boolean(
|
|
||||||
latest &&
|
|
||||||
!latest.consumed_at &&
|
|
||||||
new Date(latest.expires_at).getTime() > Date.now() &&
|
|
||||||
latest.code_hash === hashSmsCode(code),
|
|
||||||
)
|
|
||||||
if (!codeValid) {
|
|
||||||
if (latest && !latest.consumed_at) {
|
|
||||||
const failed = await recordWorkerSmsCodeFailure(
|
|
||||||
latest.id,
|
|
||||||
nowIso(),
|
|
||||||
WORKER_SMS_MAX_VERIFY_ATTEMPTS,
|
|
||||||
)
|
|
||||||
if (failed && failed.consumed_at) {
|
|
||||||
throw createHttpError('验证码错误次数过多,请重新获取验证码', {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'worker_sms_code_locked',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw createHttpError('短信验证码错误或已过期', {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'worker_sms_code_invalid',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (await getWorkerUserByUsername(phone)) {
|
|
||||||
throw createHttpError('该手机号已注册,请直接登录', {
|
|
||||||
statusCode: 409,
|
|
||||||
errorCode: 'worker_user_exists',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (await getWorkerUserByPhone(phone)) {
|
|
||||||
throw createHttpError('该手机号已注册,请直接登录', {
|
|
||||||
statusCode: 409,
|
|
||||||
errorCode: 'worker_phone_exists',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const inviter = inviteCode ? await getWorkerUserByInviteCode(inviteCode) : null
|
|
||||||
if (inviteCode && !inviter) {
|
|
||||||
throw createHttpError('邀请码无效', {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'worker_invite_code_invalid',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (inviter && inviter.status !== 'active') {
|
|
||||||
throw createHttpError('邀请人账号不可用', {
|
|
||||||
statusCode: 400,
|
|
||||||
errorCode: 'worker_inviter_invalid',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (await getWorkerUserByDisplayName(displayName)) {
|
|
||||||
throw createHttpError('该昵称已被使用,请更换昵称', {
|
|
||||||
statusCode: 409,
|
|
||||||
errorCode: 'worker_display_name_taken',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaults = await ensureWorkerPlatformDefaults()
|
|
||||||
const now = nowIso()
|
|
||||||
const created = await createWorkerUser({
|
|
||||||
username: phone,
|
|
||||||
passwordHash: hashWorkerPassword(password),
|
|
||||||
displayName,
|
|
||||||
phone,
|
|
||||||
levelId: defaults.level?.id || null,
|
|
||||||
// 手机号验证码已完成注册身份校验,账号直接启用,无需人工审核。
|
|
||||||
status: 'active',
|
|
||||||
inviterId: inviter ? Number(inviter.id) : null,
|
|
||||||
inviteCode: await generateInviteCode(now),
|
|
||||||
smsCodeId: latest!.id,
|
|
||||||
now,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!created) {
|
|
||||||
throw createHttpError('接单账号注册失败', {
|
|
||||||
statusCode: 500,
|
|
||||||
errorCode: 'worker_register_failed',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
worker: mapWorkerUser(created),
|
|
||||||
// 保留字段兼容旧版客户端,但注册流程不再需要后台审核。
|
|
||||||
reviewRequired: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function generateInviteCode(_now: string): Promise<string> {
|
|
||||||
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'
|
|
||||||
for (let attempt = 0; attempt < INVITE_CODE_RETRY_LIMIT; attempt++) {
|
|
||||||
const candidate = Array.from(
|
|
||||||
{ length: INVITE_CODE_LENGTH },
|
|
||||||
() => chars[Math.floor(crypto.randomInt(chars.length))],
|
|
||||||
).join('')
|
|
||||||
if (!(await getWorkerUserByInviteCode(candidate))) {
|
|
||||||
return candidate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw createHttpError('邀请码生成失败,请重试', {
|
|
||||||
statusCode: 500,
|
|
||||||
errorCode: 'worker_invite_code_generation_failed',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function loginWorker(
|
export async function loginWorker(
|
||||||
username: unknown,
|
username: unknown,
|
||||||
password: unknown,
|
password: unknown,
|
||||||
|
|||||||
Reference in New Issue
Block a user