feat: 打手支持昵称登录,物品规则标识唯一校验
- 昵称全局唯一(迁移 016 唯一索引 + 注册校验),存量空昵称自动补账号名 - 登录解析链扩展为 账号/手机号/昵称,均可加密码登录 - 登录页提示更新为「手机号 / 昵称 / 账号」,注册页提示昵称可作登录账号 - 新建物品规则时校验规则标识唯一:已存在则拒绝(409),编辑不受影响 - 前端规则表单增加规则标识重复的即时校验
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
-- 打手昵称唯一:昵称作为登录凭证之一,需全局唯一。
|
||||
-- 存量空昵称账号用账号名兜底(冲突时保持空昵称,不参与唯一约束)。
|
||||
|
||||
UPDATE worker_users
|
||||
SET display_name = username
|
||||
WHERE display_name = ''
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM worker_users other
|
||||
WHERE other.display_name = worker_users.username
|
||||
AND other.id <> worker_users.id
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_worker_users_display_name_unique
|
||||
ON worker_users (display_name)
|
||||
WHERE display_name <> '';
|
||||
@@ -126,6 +126,16 @@ export async function getWorkerUserByPhone(phone: string): Promise<WorkerUserRow
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export async function getWorkerUserByDisplayName(
|
||||
displayName: string,
|
||||
): Promise<WorkerUserRow | null> {
|
||||
const result = await query<WorkerUserRow>(
|
||||
`${WORKER_USER_SELECT} WHERE wu.display_name = $1 LIMIT 1`,
|
||||
[String(displayName || '').trim()],
|
||||
)
|
||||
return result.rows[0] || null
|
||||
}
|
||||
|
||||
export async function getWorkerUserByInviteCode(
|
||||
inviteCode: string,
|
||||
): Promise<WorkerUserRow | null> {
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
getWorkOrderById,
|
||||
getWorkOrderByOrderItemId,
|
||||
getWorkOrderShare,
|
||||
getWorkProductRuleByKey,
|
||||
grabWorkOrder,
|
||||
incrementWorkerSessionVersion,
|
||||
joinWorkOrderShare,
|
||||
@@ -260,11 +261,19 @@ export async function saveAdminWorkProductRule(payload: JsonObject = {}) {
|
||||
: 0
|
||||
: normalizeAmountFen(payload.requiredDepositAmount ?? payload.requiredDepositAmountYuan, 0)
|
||||
const fields = normalizeRequirementFieldsFromPayload(payload)
|
||||
const ruleKey = normalizeSlugKey(
|
||||
payload.ruleKey || payload.rule_key || skuCode || productName,
|
||||
`rule-${Date.now()}`,
|
||||
)
|
||||
const ruleId = normalizeOptionalId(payload.ruleId ?? payload.rule_id)
|
||||
if (!ruleId && (await getWorkProductRuleByKey(ruleKey))) {
|
||||
throw createHttpError('规则标识已存在,请更换后重试', {
|
||||
statusCode: 409,
|
||||
errorCode: 'work_product_rule_key_taken',
|
||||
})
|
||||
}
|
||||
const rule = await upsertWorkProductRule({
|
||||
ruleKey: normalizeSlugKey(
|
||||
payload.ruleKey || payload.rule_key || skuCode || productName,
|
||||
`rule-${Date.now()}`,
|
||||
),
|
||||
ruleKey,
|
||||
provider: String(payload.provider || '').trim(),
|
||||
platform: String(payload.platform || '').trim(),
|
||||
shopId: String(payload.shopId || payload.shop_id || '').trim(),
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
getWorkerUserById,
|
||||
getWorkerUserByPhone,
|
||||
getWorkerUserByUsername,
|
||||
getWorkerUserByDisplayName,
|
||||
getWorkOrderById,
|
||||
getWorkOrderByOrderItemId,
|
||||
getWorkOrderShare,
|
||||
@@ -248,6 +249,13 @@ export async function registerWorker(payload: JsonObject = {}) {
|
||||
})
|
||||
}
|
||||
|
||||
if (await getWorkerUserByDisplayName(displayName)) {
|
||||
throw createHttpError('该昵称已被使用,请更换昵称', {
|
||||
statusCode: 409,
|
||||
errorCode: 'worker_display_name_taken',
|
||||
})
|
||||
}
|
||||
|
||||
const defaults = await ensureWorkerPlatformDefaults()
|
||||
const now = nowIso()
|
||||
const created = await createWorkerUser({
|
||||
@@ -327,7 +335,10 @@ export async function loginWorker(username: unknown, password: unknown) {
|
||||
})
|
||||
}
|
||||
|
||||
const worker = await getWorkerUserByUsername(normalizedUsername)
|
||||
const worker =
|
||||
(await getWorkerUserByUsername(normalizedUsername)) ||
|
||||
(await getWorkerUserByPhone(normalizedUsername)) ||
|
||||
(await getWorkerUserByDisplayName(normalizedUsername))
|
||||
if (!worker || !verifyWorkerPassword(normalizedPassword, worker.password_hash)) {
|
||||
throw createHttpError('账号或密码错误', {
|
||||
statusCode: 401,
|
||||
|
||||
Reference in New Issue
Block a user