feat: define account binding match priority
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
namespace WxAgent.Core;
|
||||
|
||||
public sealed record UiAccountIdentity(string? WechatId, string? Nickname);
|
||||
public sealed record DatabaseAccountIdentity(string AccountId, string? WechatId, string? Nickname);
|
||||
public sealed record AccountMatch(string? AccountId, string Strategy, bool IsUnique)
|
||||
{
|
||||
public bool IsMatched => AccountId is not null && IsUnique;
|
||||
}
|
||||
|
||||
public static class AccountBindingMatcher
|
||||
{
|
||||
public static AccountMatch Match(UiAccountIdentity ui, IReadOnlyList<DatabaseAccountIdentity> accounts)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(ui);
|
||||
ArgumentNullException.ThrowIfNull(accounts);
|
||||
var wechatId = Normalize(ui.WechatId);
|
||||
if (wechatId is not null)
|
||||
{
|
||||
var matches = accounts.Where(a => string.Equals(Normalize(a.WechatId), wechatId, StringComparison.OrdinalIgnoreCase)).ToArray();
|
||||
if (matches.Length == 1) return new AccountMatch(matches[0].AccountId, "wechatId", true);
|
||||
if (matches.Length > 1) return new AccountMatch(null, "wechatId", false);
|
||||
}
|
||||
|
||||
var nickname = Normalize(ui.Nickname);
|
||||
if (nickname is null) return new AccountMatch(null, "none", false);
|
||||
var nicknameMatches = accounts.Where(a => string.Equals(Normalize(a.Nickname), nickname, StringComparison.Ordinal)).ToArray();
|
||||
return nicknameMatches.Length == 1
|
||||
? new AccountMatch(nicknameMatches[0].AccountId, "nickname", true)
|
||||
: new AccountMatch(null, nicknameMatches.Length == 0 ? "none" : "nickname", false);
|
||||
}
|
||||
|
||||
private static string? Normalize(string? value) => string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using WxAgent.Core;
|
||||
using Xunit;
|
||||
|
||||
namespace WxAgent.Core.Tests;
|
||||
|
||||
public sealed class AccountBindingMatcherTests
|
||||
{
|
||||
[Fact]
|
||||
public void PrefersExactWechatIdOverNickname()
|
||||
{
|
||||
var result = AccountBindingMatcher.Match(new UiAccountIdentity("wxid-a", "Same"),
|
||||
[new("db-1", "wxid-a", "Other"), new("db-2", "wxid-b", "Same")]);
|
||||
Assert.True(result.IsMatched); Assert.Equal("db-1", result.AccountId); Assert.Equal("wechatId", result.Strategy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UsesUniqueNicknameOnlyWhenWechatIdIsUnavailable()
|
||||
{
|
||||
var result = AccountBindingMatcher.Match(new UiAccountIdentity(null, " Alice "),
|
||||
[new("db-1", null, "Alice"), new("db-2", null, "Bob")]);
|
||||
Assert.True(result.IsMatched); Assert.Equal("db-1", result.AccountId); Assert.Equal("nickname", result.Strategy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RejectsDuplicateNicknameAndConflictingDuplicateWechatId()
|
||||
{
|
||||
var duplicateName = AccountBindingMatcher.Match(new UiAccountIdentity(null, "Alice"),
|
||||
[new("db-1", null, "Alice"), new("db-2", null, "Alice")]);
|
||||
var duplicateId = AccountBindingMatcher.Match(new UiAccountIdentity("wxid-a", "Alice"),
|
||||
[new("db-1", "wxid-a", "One"), new("db-2", "wxid-a", "Two")]);
|
||||
Assert.False(duplicateName.IsMatched); Assert.False(duplicateId.IsMatched);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user