using System.Text.RegularExpressions;
using Fake.DependencyInjection;
using Fake.Rbac.Infrastructure;
using Fake.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Oms.Application.Dtos.Rbac;
namespace Oms.Application.Rbac;
public class UserProfileRepository(FakeRbacDbContext dbContext) : IScopedDependency
{
///
/// 工号规则:IDE + 至少 3 位数字,如 IDE001。
///
private static readonly Regex EmployeeNoPattern = new(@"^IDE(\d{3,})$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public async Task GetAsync(Guid userId, CancellationToken cancellationToken = default)
{
var sql = $@"
SELECT
u.`Phone` AS `Phone`,
u.`EmployeeNo` AS `EmployeeNo`,
u.`WechatId` AS `WechatId`,
u.`WechatQrCode` AS `WechatQrCode`,
u.`WecomKfUrl` AS `WecomKfUrl`
FROM `{FakeTables.User}` u
WHERE u.`Id` = {{0}} AND u.`IsDeleted` = 0
LIMIT 1
";
var row = await dbContext.Database
.SqlQueryRaw(sql, userId.ToString())
.FirstOrDefaultAsync(cancellationToken);
if (row == null)
{
return null;
}
return new UserProfileData
{
Phone = row.Phone,
EmployeeNo = row.EmployeeNo,
WechatId = row.WechatId,
WechatQrCode = row.WechatQrCode,
WecomKfUrl = row.WecomKfUrl,
};
}
public async Task> GetManyAsync(
IReadOnlyCollection userIds,
CancellationToken cancellationToken = default)
{
if (userIds.Count == 0)
{
return [];
}
var inClause = string.Join(",", userIds.Select(x => $"'{x}'"));
var sql = $@"
SELECT
u.`Id` AS `UserId`,
u.`Phone` AS `Phone`,
u.`EmployeeNo` AS `EmployeeNo`,
u.`WechatId` AS `WechatId`,
u.`WechatQrCode` AS `WechatQrCode`,
u.`WecomKfUrl` AS `WecomKfUrl`
FROM `{FakeTables.User}` u
WHERE u.`Id` IN ({inClause}) AND u.`IsDeleted` = 0
";
var rows = await dbContext.Database.SqlQueryRaw(sql).ToListAsync(cancellationToken);
return rows.ToDictionary(
x => x.UserId,
x => new UserProfileData
{
Phone = x.Phone,
EmployeeNo = x.EmployeeNo,
WechatId = x.WechatId,
WechatQrCode = x.WechatQrCode,
WecomKfUrl = x.WecomKfUrl,
});
}
public async Task SaveAsync(Guid userId, UserProfileData profile, CancellationToken cancellationToken = default)
{
var sql = $@"
UPDATE `{FakeTables.User}`
SET
`Phone` = {{1}},
`EmployeeNo` = {{2}},
`WechatId` = {{3}},
`WechatQrCode` = {{4}},
`WecomKfUrl` = {{5}}
WHERE `Id` = {{0}} AND `IsDeleted` = 0
";
await dbContext.Database.ExecuteSqlRawAsync(
sql,
[
userId.ToString(),
// EF Core 参数不接受 DBNull,空值直接传 null 即可映射为 SQL NULL
(object?)NormalizeOptional(profile.Phone),
(object?)NormalizeOptional(profile.EmployeeNo),
(object?)NormalizeOptional(profile.WechatId),
(object?)NormalizeOptional(profile.WechatQrCode),
(object?)NormalizeOptional(profile.WecomKfUrl),
],
cancellationToken);
}
public async Task UpdateContactAsync(
Guid userId,
UserProfileData profile,
CancellationToken cancellationToken = default)
{
var sql = $@"
UPDATE `{FakeTables.User}`
SET
`Phone` = {{1}},
`WechatId` = {{2}},
`WechatQrCode` = {{3}},
`WecomKfUrl` = {{4}}
WHERE `Id` = {{0}} AND `IsDeleted` = 0
";
await dbContext.Database.ExecuteSqlRawAsync(
sql,
[
userId.ToString(),
(object?)NormalizeOptional(profile.Phone),
(object?)NormalizeOptional(profile.WechatId),
(object?)NormalizeOptional(profile.WechatQrCode),
(object?)NormalizeOptional(profile.WecomKfUrl),
],
cancellationToken);
}
public async Task IsEmployeeNoTakenAsync(
string employeeNo,
Guid? excludeUserId,
CancellationToken cancellationToken = default)
{
var normalized = employeeNo.Trim().ToUpperInvariant();
var sql = excludeUserId.HasValue
? $@"
SELECT COUNT(*) AS `Value`
FROM `{FakeTables.User}` u
WHERE u.`IsDeleted` = 0
AND u.`EmployeeNo` = {{0}}
AND u.`Id` <> {{1}}
"
: $@"
SELECT COUNT(*) AS `Value`
FROM `{FakeTables.User}` u
WHERE u.`IsDeleted` = 0
AND u.`EmployeeNo` = {{0}}
";
var count = excludeUserId.HasValue
? await dbContext.Database.SqlQueryRaw(sql, normalized, excludeUserId.Value.ToString())
.FirstAsync(cancellationToken)
: await dbContext.Database.SqlQueryRaw(sql, normalized).FirstAsync(cancellationToken);
return count > 0;
}
public async Task GenerateNextEmployeeNoAsync(CancellationToken cancellationToken = default)
{
var sql = $@"
SELECT u.`EmployeeNo` AS `EmployeeNo`
FROM `{FakeTables.User}` u
WHERE u.`IsDeleted` = 0
AND u.`EmployeeNo` IS NOT NULL
AND u.`EmployeeNo` <> ''
";
var rows = await dbContext.Database.SqlQueryRaw(sql).ToListAsync(cancellationToken);
var max = 0;
foreach (var row in rows)
{
var value = row.EmployeeNo?.Trim() ?? string.Empty;
var match = EmployeeNoPattern.Match(value);
if (!match.Success)
{
continue;
}
if (int.TryParse(match.Groups[1].Value, out var number))
{
max = Math.Max(max, number);
}
}
var next = max + 1;
return $"IDE{next:D3}";
}
public static bool IsValidEmployeeNo(string employeeNo) => EmployeeNoPattern.IsMatch(employeeNo.Trim());
private static string? NormalizeOptional(string? value)
{
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
return value.Trim();
}
private sealed class UserProfileRow
{
public string? Phone { get; set; }
public string? EmployeeNo { get; set; }
public string? WechatId { get; set; }
public string? WechatQrCode { get; set; }
public string? WecomKfUrl { get; set; }
}
private sealed class UserProfileListRow
{
public Guid UserId { get; set; }
public string? Phone { get; set; }
public string? EmployeeNo { get; set; }
public string? WechatId { get; set; }
public string? WechatQrCode { get; set; }
public string? WecomKfUrl { get; set; }
}
private sealed class EmployeeNoRow
{
public string? EmployeeNo { get; set; }
}
}