From 059b38e8fed28528f20ea57d25cb0b4ff0031afd Mon Sep 17 00:00:00 2001 From: hectorzhao Date: Sun, 26 Jul 2026 13:14:46 +0800 Subject: [PATCH] fix: support soft-delete user uniqueness in tools --- docs/testing-progress.md | 1 + tools/deploy/ensure-production-admin.mjs | 48 +++++++++++++---------- tools/smoke/real-env-smoke.mjs | 50 +++++++++++++----------- 3 files changed, 56 insertions(+), 43 deletions(-) diff --git a/docs/testing-progress.md b/docs/testing-progress.md index 49365c7..0175bad 100644 --- a/docs/testing-progress.md +++ b/docs/testing-progress.md @@ -2488,4 +2488,5 @@ git diff --check - 手机号码基础校验放宽为`^1\d{10}$`,不依赖号段更新。客户端/HTTP混合批次逐号码把非法号码、平台黑名单和企业应用黑名单记为`submit_failed/rejected`且金额0,合法号码照常冻结、入队;CMPP混合多目的提交对被拦截号码生成平台`REJECTD`失败回执,合法号码继续处理。 - 短信审核页只返回待人工审核及人工处理记录,自动放行/拒绝不再混入;号码数量恢复设计基线的查看入口,真实接口仅返回手机号、归属地、运营商和短信状态并支持服务端搜索/分页。 - 新待审核短信同步保存`reviewTaskId`,审核决定同时按短信直连和批次`riskTaskId`查找,修复审核任务已通过而短信仍`pending_review`。遵照要求不改写现存历史异常数据、不补发历史短信。 +- 用户登录标识改为“仅未删除记录唯一”后,部署管理员初始化与真实环境冒烟脚本同步从`username`唯一`upsert`改为先查询未删除用户、再按主键更新或创建,避免迁移后Prisma拒绝旧的唯一查询。 - 发布前门禁阶段结果:Prisma format/generate/validate通过;风险审核+发送链定向2 suites / 108 tests通过,随后补充应用覆盖、号码分页与逐号码拦截用例;API全量26 suites / 338 tests、API TypeScript build、前端TypeScript/Vite生产构建、Gateway `go test ./...`/`go vet ./...`、依赖安全门禁通过。API保留既有Redis不可用容错告警与`--forceExit`提示,前端保留约1.95MB单chunk/584.70KB gzip提示。 diff --git a/tools/deploy/ensure-production-admin.mjs b/tools/deploy/ensure-production-admin.mjs index 916e91b..3f6a47d 100644 --- a/tools/deploy/ensure-production-admin.mjs +++ b/tools/deploy/ensure-production-admin.mjs @@ -19,7 +19,9 @@ function hashPassword(value) { } async function main() { - const existingUser = await prisma.user.findUnique({ where: { username } }); + const existingUser = await prisma.user.findFirst({ + where: { username, deletedAt: null }, + }); const createPassword = configuredPassword || randomBytes(18).toString('base64url'); const updatePassword = configuredPassword; const role = await prisma.role.upsert({ @@ -27,26 +29,30 @@ async function main() { update: { name: '平台管理员', scope: 'platform' }, create: { code: 'platform_admin', name: '平台管理员', scope: 'platform' }, }); - const user = await prisma.user.upsert({ - where: { username }, - update: { - email, - displayName: '生产平台管理员', - ...(updatePassword ? { passwordHash: hashPassword(updatePassword) } : {}), - status: 'active', - failedLoginCount: 0, - lockedUntil: null, - deletedAt: null, - tenantId: null, - }, - create: { - username, - email, - displayName: '生产平台管理员', - passwordHash: hashPassword(createPassword), - status: 'active', - }, - }); + const userData = { + email, + displayName: '生产平台管理员', + ...(updatePassword ? { passwordHash: hashPassword(updatePassword) } : {}), + status: 'active', + failedLoginCount: 0, + lockedUntil: null, + deletedAt: null, + tenantId: null, + }; + const user = existingUser + ? await prisma.user.update({ + where: { id: existingUser.id }, + data: userData, + }) + : await prisma.user.create({ + data: { + username, + email, + displayName: '生产平台管理员', + passwordHash: hashPassword(createPassword), + status: 'active', + }, + }); await prisma.userRole.upsert({ where: { userId_roleId: { userId: user.id, roleId: role.id } }, update: {}, diff --git a/tools/smoke/real-env-smoke.mjs b/tools/smoke/real-env-smoke.mjs index 0896f02..dc4effd 100644 --- a/tools/smoke/real-env-smoke.mjs +++ b/tools/smoke/real-env-smoke.mjs @@ -25,29 +25,35 @@ async function ensureSmokeData() { create: { code: tenantCode, name: 'Smoke Test Enterprise', status: 'active' }, }); - const user = await prisma.user.upsert({ - where: { username }, - update: { - tenantId: tenant.id, - email, - phone, - displayName: 'Smoke Client Admin', - passwordHash: hashPassword(password), - status: 'active', - failedLoginCount: 0, - lockedUntil: null, - deletedAt: null, - }, - create: { - tenantId: tenant.id, - username, - email, - phone, - displayName: 'Smoke Client Admin', - passwordHash: hashPassword(password), - status: 'active', - }, + const existingUser = await prisma.user.findFirst({ + where: { username, deletedAt: null }, }); + const user = existingUser + ? await prisma.user.update({ + where: { id: existingUser.id }, + data: { + tenantId: tenant.id, + email, + phone, + displayName: 'Smoke Client Admin', + passwordHash: hashPassword(password), + status: 'active', + failedLoginCount: 0, + lockedUntil: null, + deletedAt: null, + }, + }) + : await prisma.user.create({ + data: { + tenantId: tenant.id, + username, + email, + phone, + displayName: 'Smoke Client Admin', + passwordHash: hashPassword(password), + status: 'active', + }, + }); const enterpriseAdminRole = await prisma.role.upsert({ where: { code: 'enterprise_admin' },