admin/src/Oms.Application/Rbac/UserProfileRepository.cs
xiaolipro ff9c589956
All checks were successful
Build and Deploy / deploy (push) Successful in 1m2s
fix
2026-10-13 17:20:12 +08:00

244 lines
7.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
{
/// <summary>
/// 工号规则:IDE + 至少 3 位数字,如 IDE001。
/// </summary>
private static readonly Regex EmployeeNoPattern = new(@"^IDE(\d{3,})$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
public async Task<UserProfileData?> 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<UserProfileRow>(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<Dictionary<Guid, UserProfileData>> GetManyAsync(
IReadOnlyCollection<Guid> 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<UserProfileListRow>(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<bool> 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<long>(sql, normalized, excludeUserId.Value.ToString())
.FirstAsync(cancellationToken)
: await dbContext.Database.SqlQueryRaw<long>(sql, normalized).FirstAsync(cancellationToken);
return count > 0;
}
public async Task<string> 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<EmployeeNoRow>(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; }
}
}