using Fake.Application; using Fake.Rbac.Infrastructure; using Microsoft.EntityFrameworkCore; namespace Oms.Application.Shared; /// /// 微信/客户注册前校验 RBAC 登录账号,避免 user 表已存在但 t_customer 未创建时 EF 抛出英文 DbUpdateException。 /// public static class OmsRbacRegistrationGuard { public static async Task EnsureLoginIdentityAvailableAsync( FakeRbacDbContext rbacDbContext, string account, string? email, CancellationToken cancellationToken = default) { var normalizedAccount = account.Trim(); var normalizedEmail = string.IsNullOrWhiteSpace(email) ? null : email.Trim(); if (await ExistsActiveUserByAccountAsync(rbacDbContext, normalizedAccount, cancellationToken)) { throw new BusinessException("登录账号已存在,请直接绑定或使用其他手机号"); } if (normalizedEmail != null && await ExistsActiveUserByEmailAsync(rbacDbContext, normalizedEmail, cancellationToken)) { throw new BusinessException("邮箱已被占用,请更换邮箱或直接绑定已有账号"); } } public static BusinessException ToSaveBusinessException(Exception ex) { var message = ExtractDeepMessage(ex); if (ContainsDuplicateKey(message)) { if (message.Contains("Account", StringComparison.OrdinalIgnoreCase) || message.Contains("uk_user_account", StringComparison.OrdinalIgnoreCase) || message.Contains("IX_user_Account", StringComparison.OrdinalIgnoreCase)) { return new BusinessException("登录账号已存在,请直接绑定或使用其他手机号"); } if (message.Contains("Email", StringComparison.OrdinalIgnoreCase) || message.Contains("uk_user_email", StringComparison.OrdinalIgnoreCase)) { return new BusinessException("邮箱已被占用,请更换邮箱或直接绑定已有账号"); } if (message.Contains("Phone", StringComparison.OrdinalIgnoreCase) || message.Contains("uk_cus_phone", StringComparison.OrdinalIgnoreCase)) { return new BusinessException("手机号已存在"); } if (message.Contains("uk_cus_email", StringComparison.OrdinalIgnoreCase)) { return new BusinessException("邮箱已存在"); } if (message.Contains("WechatOpenid", StringComparison.OrdinalIgnoreCase) || message.Contains("uk_cus_openid", StringComparison.OrdinalIgnoreCase) || message.Contains("uk_customer_wechat_openid", StringComparison.OrdinalIgnoreCase)) { return new BusinessException("当前微信已绑定账号"); } return new BusinessException("账号信息已存在,请直接绑定或更换手机号/邮箱"); } return new BusinessException("注册失败,请稍后重试或联系客服"); } private static async Task ExistsActiveUserByAccountAsync( FakeRbacDbContext rbacDbContext, string account, CancellationToken cancellationToken) { // 唯一索引不按 IsDeleted 区分,软删除账号仍会触发 Duplicate entry。 var hit = await rbacDbContext.Database .SqlQuery( $"SELECT 1 AS Value FROM `user` WHERE `Account` = {account} LIMIT 1") .FirstOrDefaultAsync(cancellationToken); return hit.HasValue; } private static async Task ExistsActiveUserByEmailAsync( FakeRbacDbContext rbacDbContext, string email, CancellationToken cancellationToken) { var hit = await rbacDbContext.Database .SqlQuery( $"SELECT 1 AS Value FROM `user` WHERE `Email` = {email} LIMIT 1") .FirstOrDefaultAsync(cancellationToken); return hit.HasValue; } private static string ExtractDeepMessage(Exception ex) { var parts = new List(); for (var current = ex; current != null; current = current.InnerException) { if (!string.IsNullOrWhiteSpace(current.Message)) { parts.Add(current.Message); } } return string.Join(" | ", parts); } private static bool ContainsDuplicateKey(string message) => message.Contains("Duplicate entry", StringComparison.OrdinalIgnoreCase) || message.Contains("1062", StringComparison.OrdinalIgnoreCase) || message.Contains("unique constraint", StringComparison.OrdinalIgnoreCase) || message.Contains("UNIQUE constraint failed", StringComparison.OrdinalIgnoreCase); }