fix(wechat): 注册失败时返回中文错误并校验 RBAC 账号
避免 user 表重复写入抛出英文 DbUpdateException;注册前检查 RBAC 登录账号/邮箱是否已占用。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
4d93a9b50d
commit
bf81f00c91
118
src/Oms.Application/Shared/OmsRbacRegistrationGuard.cs
Normal file
118
src/Oms.Application/Shared/OmsRbacRegistrationGuard.cs
Normal file
@ -0,0 +1,118 @@
|
||||
using Fake.Application;
|
||||
using Fake.Rbac.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Oms.Application.Shared;
|
||||
|
||||
/// <summary>
|
||||
/// 微信/客户注册前校验 RBAC 登录账号,避免 user 表已存在但 t_customer 未创建时 EF 抛出英文 DbUpdateException。
|
||||
/// </summary>
|
||||
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<bool> ExistsActiveUserByAccountAsync(
|
||||
FakeRbacDbContext rbacDbContext,
|
||||
string account,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var hit = await rbacDbContext.Database
|
||||
.SqlQuery<int?>(
|
||||
$"SELECT 1 AS Value FROM `user` WHERE `IsDeleted` = 0 AND `Account` = {account} LIMIT 1")
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
return hit.HasValue;
|
||||
}
|
||||
|
||||
private static async Task<bool> ExistsActiveUserByEmailAsync(
|
||||
FakeRbacDbContext rbacDbContext,
|
||||
string email,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var hit = await rbacDbContext.Database
|
||||
.SqlQuery<int?>(
|
||||
$"SELECT 1 AS Value FROM `user` WHERE `IsDeleted` = 0 AND `Email` = {email} LIMIT 1")
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
return hit.HasValue;
|
||||
}
|
||||
|
||||
private static string ExtractDeepMessage(Exception ex)
|
||||
{
|
||||
var parts = new List<string>();
|
||||
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);
|
||||
}
|
||||
@ -97,6 +97,7 @@ public class WeChatCustomerService(
|
||||
|
||||
var customerCode = await CustomerCodeGenerator.GenerateNextAsync(customerRepository, cancellationToken);
|
||||
await EnsureCustomerIdentityUniqueAsync(customerCode, phone, email, cancellationToken);
|
||||
await OmsRbacRegistrationGuard.EnsureLoginIdentityAvailableAsync(rbacDbContext, phone, email, cancellationToken);
|
||||
|
||||
var customerRoleId = await GetCustomerRoleIdOrThrowAsync(cancellationToken);
|
||||
var user = await userService.CreateAsync(new UserCreateDto
|
||||
@ -128,10 +129,10 @@ public class WeChatCustomerService(
|
||||
};
|
||||
await customerRepository.InsertAsync(entity);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
await TryRollbackRbacUserAsync(user.Id);
|
||||
throw;
|
||||
throw ex is BusinessException ? ex : OmsRbacRegistrationGuard.ToSaveBusinessException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user