178 lines
7.0 KiB
C#
178 lines
7.0 KiB
C#
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("账号信息已存在,请直接绑定或更换手机号/邮箱");
|
|
}
|
|
|
|
if (message.Contains("Data too long", StringComparison.OrdinalIgnoreCase)
|
|
|| message.Contains("1406", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (message.Contains("Avatar", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return new BusinessException("微信头像地址过长,请稍后重试或联系客服");
|
|
}
|
|
|
|
if (message.Contains("Nickname", StringComparison.OrdinalIgnoreCase)
|
|
|| message.Contains("CustomerName", StringComparison.OrdinalIgnoreCase)
|
|
|| message.Contains("Name", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return new BusinessException("客户名称或微信昵称过长,请修改后重试");
|
|
}
|
|
|
|
return new BusinessException("注册信息字段过长,请缩短客户名称后重试");
|
|
}
|
|
|
|
if (message.Contains("foreign key constraint fails", StringComparison.OrdinalIgnoreCase)
|
|
|| message.Contains("1452", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return new BusinessException("客户角色未正确初始化,请联系管理员");
|
|
}
|
|
|
|
if (message.Contains("Cannot insert duplicate key", StringComparison.OrdinalIgnoreCase)
|
|
|| message.Contains("2627", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return new BusinessException("账号信息已存在,请直接绑定或更换手机号/邮箱");
|
|
}
|
|
|
|
var reason = ExtractActionableReason(ex);
|
|
return new BusinessException($"注册失败:{reason}");
|
|
}
|
|
|
|
private static string ExtractActionableReason(Exception ex)
|
|
{
|
|
var parts = new List<string>();
|
|
for (var current = ex; current != null; current = current.InnerException)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(current.Message))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (current.Message.Contains("saving the entity changes", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
parts.Add(current.Message.Trim());
|
|
}
|
|
|
|
var reason = parts.LastOrDefault();
|
|
if (string.IsNullOrWhiteSpace(reason))
|
|
{
|
|
return "请稍后重试或联系客服";
|
|
}
|
|
|
|
return reason.Length <= 160 ? reason : reason[..160];
|
|
}
|
|
|
|
private static async Task<bool> ExistsActiveUserByAccountAsync(
|
|
FakeRbacDbContext rbacDbContext,
|
|
string account,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
// 唯一索引不按 IsDeleted 区分,软删除账号仍会触发 Duplicate entry。
|
|
var hit = await rbacDbContext.Database
|
|
.SqlQuery<int?>(
|
|
$"SELECT 1 AS Value FROM `user` WHERE `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 `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);
|
|
}
|