67 lines
2.2 KiB
C#
67 lines
2.2 KiB
C#
namespace YY.Admin.Core.Helper;
|
|
|
|
/// <summary>MES 只读数据:远端列表与本地缓存按 Id 对比合并</summary>
|
|
public static class MesReadOnlyCacheMergeHelper
|
|
{
|
|
public sealed record MergeResult(int Added, int Updated, int Removed)
|
|
{
|
|
public bool HasChanges => Added > 0 || Updated > 0 || Removed > 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 对比远端与本地,返回合并后的列表及变更统计。
|
|
/// 内容相同则保留本地副本;<paramref name="mergeUpdated"/> 可定制变更行的合并策略(如保留本地子表)。
|
|
/// </summary>
|
|
public static (List<T> Merged, MergeResult Stats) Merge<T>(
|
|
IReadOnlyList<T> local,
|
|
IReadOnlyList<T> remote,
|
|
Func<T, string?> getId,
|
|
Func<T, T, bool> isContentEqual,
|
|
Func<T, T> clone,
|
|
Func<T, T, T>? mergeUpdated = null)
|
|
{
|
|
var localById = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (var item in local)
|
|
{
|
|
var id = getId(item);
|
|
if (!string.IsNullOrWhiteSpace(id))
|
|
localById[id] = item;
|
|
}
|
|
|
|
var remoteIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
var merged = new List<T>(remote.Count);
|
|
int added = 0, updated = 0;
|
|
|
|
foreach (var remoteItem in remote)
|
|
{
|
|
var id = getId(remoteItem);
|
|
if (string.IsNullOrWhiteSpace(id))
|
|
{
|
|
merged.Add(clone(remoteItem));
|
|
added++;
|
|
continue;
|
|
}
|
|
|
|
remoteIds.Add(id);
|
|
|
|
if (!localById.TryGetValue(id, out var localItem))
|
|
{
|
|
merged.Add(clone(remoteItem));
|
|
added++;
|
|
}
|
|
else if (!isContentEqual(localItem, remoteItem))
|
|
{
|
|
merged.Add(mergeUpdated != null ? mergeUpdated(localItem, remoteItem) : clone(remoteItem));
|
|
updated++;
|
|
}
|
|
else
|
|
{
|
|
merged.Add(clone(localItem));
|
|
}
|
|
}
|
|
|
|
int removed = localById.Keys.Count(id => !remoteIds.Contains(id));
|
|
return (merged, new MergeResult(added, updated, removed));
|
|
}
|
|
}
|