using FoodLabeling.Application.Services.DbModels;
using FoodLabeling.Domain.Entities;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Guids;
using Volo.Abp.Users;
using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace FoodLabeling.Application.Helpers;
///
/// Region(fl_group.Id)与 Location(location.Id)范围解析:Region 落库对应 location.GroupName。
///
public static class LocationScopeBindingHelper
{
///
/// 读取用户 userlocation 绑定的门店 Id(Guid 归一化,避免大小写导致漏绑)。
///
public static async Task> ResolveBoundLocationIdsForUserAsync(
ISqlSugarClient db,
Guid userId)
{
var userKey = TeamMemberListScopeHelper.UserKey(userId);
var userIdStr = userId.ToString("D");
var rows = await db.Ado.SqlQueryAsync(
"""
SELECT LocationId
FROM userlocation
WHERE IsDeleted = 0
AND (
UserId = @UserIdD
OR UserId = @UserIdLower
OR LOWER(TRIM(UserId)) = @UserIdLower
)
""",
new
{
UserIdD = userIdStr,
UserIdLower = userKey
});
var locationIds = new HashSet(StringComparer.OrdinalIgnoreCase);
foreach (var locationId in rows)
{
var locKey = TeamMemberListScopeHelper.NormalizeScopeKey(locationId);
if (!string.IsNullOrEmpty(locKey))
{
locationIds.Add(locKey);
}
}
return locationIds.OrderBy(x => x, StringComparer.Ordinal).ToList();
}
///
/// App 可选门店 Id:Company Admin 展开为绑定 Company 下全部门店;其它角色为 userlocation 绑定。
///
public static Task> ResolveAppAccessibleLocationIdsForUserAsync(
ISqlSugarClient db,
Guid userId) =>
ResolveAppAccessibleLocationIdsForUserAsync(db, userId, null);
///
/// App 可选门店 Id;传入 时优先 JWT 声明识别 Company Admin,避免 ORM 查 Role 触发数据权限过滤器。
///
public static async Task> ResolveAppAccessibleLocationIdsForUserAsync(
ISqlSugarClient db,
Guid userId,
ICurrentUser? currentUser)
{
var locationIds = await ResolveBoundLocationIdsForUserAsync(db, userId);
if (locationIds.Count == 0)
{
return locationIds;
}
var isCompanyAdmin = currentUser?.Id == userId
? await TeamMemberRoleHelper.IsCompanyAdminUserAsync(db, currentUser)
: await TeamMemberRoleHelper.IsCompanyAdminUserAsync(db, userId);
if (!isCompanyAdmin)
{
return locationIds;
}
var partnerIds = await ResolvePartnerIdsFromLocationIdsAsync(db, locationIds);
if (partnerIds.Count == 0)
{
return locationIds;
}
var expanded = await ResolveLocationIdsFromPartnerIdsAsync(db, partnerIds);
if (expanded.Count == 0)
{
return locationIds;
}
var merged = new HashSet(StringComparer.OrdinalIgnoreCase);
foreach (var id in expanded)
{
var key = TeamMemberListScopeHelper.NormalizeScopeKey(id);
if (!string.IsNullOrEmpty(key))
{
merged.Add(key);
}
}
return merged.OrderBy(x => x, StringComparer.Ordinal).ToList();
}
public static async Task> ResolveDisplayRegionIdsForCurrentUserAsync(
ISqlSugarClient db,
ICurrentUser currentUser)
{
if (currentUser.Id is null)
{
return new List();
}
return await ResolveDisplayRegionIdsForUserAsync(db, currentUser, null);
}
///
/// 与 Team Member 详情/编辑回显一致:Company Admin 展示绑定 Company 下全部 Region。
///
public static async Task> ResolveDisplayRegionIdsForUserAsync(
ISqlSugarClient db,
ICurrentUser currentUser,
Guid? roleId)
{
if (currentUser.Id is null)
{
return new List();
}
var locationIds = await ResolveBoundLocationIdsForUserAsync(db, currentUser.Id.Value);
if (locationIds.Count == 0)
{
return new List();
}
var partnerIds = await ResolvePartnerIdsFromLocationIdsAsync(db, locationIds);
var regionIds = await ResolveGroupIdsFromLocationIdsAsync(db, locationIds);
if (await TeamMemberRoleHelper.IsCompanyAdminUserAsync(db, currentUser) && partnerIds.Count > 0)
{
regionIds = await ResolveGroupIdsFromPartnerIdsAsync(db, partnerIds);
}
return regionIds;
}
///
/// 按用户 Id 解析可见 Region(无 JWT 上下文时用,如 Team Member 详情)。
///
public static async Task> ResolveDisplayRegionIdsForUserAsync(
ISqlSugarClient db,
Guid userId,
Guid? roleId)
{
var locationIds = await ResolveBoundLocationIdsForUserAsync(db, userId);
if (locationIds.Count == 0)
{
return new List();
}
var partnerIds = await ResolvePartnerIdsFromLocationIdsAsync(db, locationIds);
var regionIds = await ResolveGroupIdsFromLocationIdsAsync(db, locationIds);
if (await TeamMemberRoleHelper.IsCompanyAdminUserAsync(db, userId) && partnerIds.Count > 0)
{
regionIds = await ResolveGroupIdsFromPartnerIdsAsync(db, partnerIds);
}
return regionIds;
}
///
/// 根据成员绑定的门店 Id 解析 Company / Region 范围(Team Member 详情、编辑回显)。
///
public static async Task<(List PartnerIds, List RegionIds, List LocationIds)>
ResolveMemberScopeFromLocationIdsAsync(ISqlSugarClient db, IReadOnlyList locationIds)
{
var normalized = NormalizeIds(locationIds);
if (normalized.Count == 0)
{
return (new List(), new List(), new List());
}
var partnerIds = await ResolvePartnerIdsFromLocationIdsAsync(db, normalized);
var regionIds = await ResolveGroupIdsFromLocationIdsAsync(db, normalized);
return (partnerIds, regionIds, normalized);
}
///
/// 列表筛选:门店 Id 优先,否则 Region(groupId),否则 Company(partnerId);均未传返回 null。
///
public static async Task?> ResolveFilteredLocationIdsForListAsync(
ISqlSugarClient db,
string? partnerId,
string? groupId,
string? locationId)
{
var locId = locationId?.Trim();
if (!string.IsNullOrWhiteSpace(locId))
{
if (!Guid.TryParse(locId, out var locationGuid))
{
return new List();
}
var exists = await db.Queryable()
.AnyAsync(x => !x.IsDeleted && x.Id == locationGuid);
return exists ? new List { locId } : new List();
}
var groupIds = ParseScopeIdsFromQuery(groupId);
var pid = partnerId?.Trim();
if (string.IsNullOrWhiteSpace(pid) && groupIds.Count == 0)
{
return null;
}
if (groupIds.Count > 0)
{
return await ResolveLocationIdsFromGroupIdsAsync(db, groupIds);
}
var q = db.Queryable().Where(x => !x.IsDeleted);
if (!string.IsNullOrWhiteSpace(pid))
{
var partner = await db.Queryable()
.FirstAsync(x => !x.IsDeleted && x.Id == pid);
if (partner is null)
{
return new List();
}
var pName = partner.PartnerName?.Trim() ?? string.Empty;
q = q.Where(x => x.Partner == pName);
}
return await q.Select(x => SqlFunc.ToString(x.Id)).ToListAsync();
}
///
/// 列表筛选:按门店 Id 优先,否则按 Region(groupId)解析门店主键;均未传返回 null。
///
public static async Task?> ResolveScopedLocationIdsAsync(
ISqlSugarClient db,
string? groupId,
string? locationId)
{
var locId = locationId?.Trim();
if (!string.IsNullOrWhiteSpace(locId))
{
if (!Guid.TryParse(locId, out var locationGuid))
{
return new List();
}
var exists = await db.Queryable()
.AnyAsync(x => !x.IsDeleted && x.Id == locationGuid);
return exists ? new List { locId } : new List();
}
var groupIds = ParseScopeIdsFromQuery(groupId);
if (groupIds.Count == 0)
{
return null;
}
return await ResolveLocationIdsFromGroupIdsAsync(db, groupIds);
}
///
/// 将 Company(partnerId)、Region(groupIds)与显式 locationIds 合并为去重后的门店 Id 列表。
///
public static Task> MergeToLocationIdsAsync(
ISqlSugarClient db,
string? partnerId,
IReadOnlyList? groupIds,
IReadOnlyList? locationIds) =>
MergeToLocationIdsAsync(db,
string.IsNullOrWhiteSpace(partnerId) ? null : new[] { partnerId.Trim() },
groupIds,
locationIds);
///
/// 将 Company(partnerIds)、Region(groupIds)与显式 locationIds 合并为去重后的门店 Id 列表。
///
public static async Task> MergeToLocationIdsAsync(
ISqlSugarClient db,
IReadOnlyList? partnerIds,
IReadOnlyList? groupIds,
IReadOnlyList? locationIds)
{
var merged = new HashSet(StringComparer.Ordinal);
var fromPartner = await ResolveLocationIdsFromPartnerIdsAsync(db, partnerIds);
foreach (var id in fromPartner)
{
merged.Add(id);
}
var fromGroups = await ResolveLocationIdsFromGroupIdsAsync(db, groupIds);
foreach (var id in fromGroups)
{
merged.Add(id);
}
foreach (var id in NormalizeIds(locationIds))
{
merged.Add(id);
}
return merged.ToList();
}
///
/// 多选 Id 是否含 ALL 哨兵(大小写不敏感)。与具体 Guid 同传时以 ALL 为准(全选)。
///
public static bool IsAllScopeSentinel(string? value) =>
string.Equals(value?.Trim(), AllScopeBindingHelper.ScopeAll, StringComparison.OrdinalIgnoreCase);
/// Id 列表是否含 ALL 哨兵。
public static bool ContainsAllScopeSentinel(IReadOnlyList? ids) =>
ids?.Any(IsAllScopeSentinel) == true;
/// 去掉 ALL 哨兵,仅保留具体 Id。
public static List FilterConcreteScopeIds(IReadOnlyList? ids) =>
NormalizeIds(ids).Where(x => !IsAllScopeSentinel(x)).ToList();
///
/// Team Member 门店范围落库(不做 partner+region+location 并集)。
/// 优先级:
/// 1) locationIds 含 ALL → 公司全部门店;
/// 2) 有具体 locationIds,且 regionIds 为空或为 ALL → 只绑这些门店(UI 在 Region=ALL 下再收窄门店);
/// 3) regionIds 含 ALL → 公司全部 Region 下门店;
/// 4) 具体 regionIds → 按 Region 展开(忽略同传 locationIds,保证多区域能落库);
/// 5) 仅 → 公司全部门店。
///
public static async Task> ResolveTeamMemberLocationIdsForSaveAsync(
ISqlSugarClient db,
IReadOnlyList? partnerIds,
IReadOnlyList? regionIds,
IReadOnlyList? locationIds)
{
var normalizedPartners = NormalizeIds(partnerIds);
var locationHasAll = ContainsAllScopeSentinel(locationIds);
var concreteLocations = FilterConcreteScopeIds(locationIds);
var regionHasAll = ContainsAllScopeSentinel(regionIds);
var concreteRegions = FilterConcreteScopeIds(regionIds);
// 1. locationIds 含 ALL → 按 partner 展开全部门店
if (locationHasAll)
{
if (normalizedPartners.Count == 0)
{
throw new UserFriendlyException("选择全部门店时需指定 Company(partnerId / partnerIds)");
}
return await ResolveLocationIdsFromPartnerIdsAsync(db, normalizedPartners);
}
// 2. 具体门店 +(无区域 / 区域为 ALL)→ 只绑这些门店,避免 regionIds=ALL 盖掉单店
if (concreteLocations.Count > 0 && (regionHasAll || concreteRegions.Count == 0))
{
return concreteLocations;
}
// 3. regionIds 含 ALL(且无具体门店)→ 按 partner 展开全部 Region 下门店
if (regionHasAll)
{
if (normalizedPartners.Count == 0)
{
throw new UserFriendlyException("选择全部 Region 时需指定 Company(partnerId / partnerIds)");
}
return await AllScopeBindingHelper.ResolveAllLocationIdsAsync(db, normalizedPartners, null);
}
// 4. 具体 regionIds → 按 Region 展开门店(忽略同传 locationIds)
if (concreteRegions.Count > 0)
{
return await MergeToLocationIdsAsync(
db,
(IReadOnlyList?)null,
concreteRegions,
null);
}
// 5. 仅有具体 locationIds → 只绑这些门店
if (concreteLocations.Count > 0)
{
return concreteLocations;
}
// 6. 仅有 partner → 绑该公司全部门店
if (normalizedPartners.Count > 0)
{
return await MergeToLocationIdsAsync(db, normalizedPartners, null, null);
}
return new List();
}
///
/// 标签类型/分类/多选项门店范围落库:仅按 Region/Location 合并,不因 Company(partner)展开并集。
///
public static async Task> ResolveEntityLocationIdsForSaveAsync(
ISqlSugarClient db,
bool locationSpecified,
IReadOnlyList? regionIds,
IReadOnlyList? locationIds)
{
if (!locationSpecified)
{
return new List();
}
var merged = await MergeToLocationIdsAsync(
db,
(IReadOnlyList?)null,
regionIds,
locationIds);
if (merged.Count == 0)
{
throw new UserFriendlyException("指定适用区域或门店时,至少需要匹配到一个有效门店");
}
await ValidateLocationIdsExistAsync(db, merged);
return merged;
}
///
/// 根据已绑定门店反推适用的 Company Id(fl_partner.Id)。
///
public static async Task> ResolvePartnerIdsFromLocationIdsAsync(
ISqlSugarClient db,
IReadOnlyList locationIds)
{
var ids = NormalizeIds(locationIds);
if (ids.Count == 0)
{
return new List();
}
var guidList = ids.Where(x => Guid.TryParse(x, out _)).Select(Guid.Parse).ToList();
if (guidList.Count == 0)
{
return new List();
}
var locs = await db.Queryable()
.Where(x => !x.IsDeleted && guidList.Contains(x.Id))
.ToListAsync();
if (locs.Count == 0)
{
return new List();
}
var partnerNames = locs
.Select(x => x.Partner?.Trim())
.Where(x => !string.IsNullOrEmpty(x))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
if (partnerNames.Count == 0)
{
return new List();
}
var partners = await db.Queryable()
.Where(x => !x.IsDeleted)
.ToListAsync();
var partnerIdSet = new HashSet(StringComparer.Ordinal);
foreach (var name in partnerNames)
{
var match = partners.FirstOrDefault(p =>
string.Equals(p.PartnerName?.Trim(), name, StringComparison.OrdinalIgnoreCase) ||
string.Equals(p.Id, name, StringComparison.OrdinalIgnoreCase));
if (match is not null && !string.IsNullOrWhiteSpace(match.Id))
{
partnerIdSet.Add(match.Id.Trim());
}
}
return partnerIdSet.OrderBy(x => x, StringComparer.Ordinal).ToList();
}
///
/// 解析 Company(fl_partner.Id)下全部未删除门店 Id。
///
public static async Task> ResolveLocationIdsFromPartnerIdsAsync(
ISqlSugarClient db,
IReadOnlyList? partnerIds)
{
var ids = NormalizeIds(partnerIds);
if (ids.Count == 0)
{
return new List();
}
var result = new HashSet(StringComparer.Ordinal);
foreach (var pid in ids)
{
var partner = await db.Queryable()
.FirstAsync(x => !x.IsDeleted && x.Id == pid);
if (partner is null)
{
continue;
}
var pName = partner.PartnerName?.Trim() ?? string.Empty;
if (string.IsNullOrEmpty(pName))
{
continue;
}
var locIds = await db.Queryable()
.Where(x => !x.IsDeleted && x.Partner == pName)
.Select(x => SqlFunc.ToString(x.Id))
.ToListAsync();
foreach (var lid in locIds.Where(x => !string.IsNullOrWhiteSpace(x)))
{
result.Add(lid.Trim());
}
}
return result.ToList();
}
///
/// 根据已绑定门店反推适用的 Region Id(fl_group.Id)。
///
public static async Task> ResolveGroupIdsFromLocationIdsAsync(
ISqlSugarClient db,
IReadOnlyList locationIds)
{
var ids = NormalizeIds(locationIds);
if (ids.Count == 0)
{
return new List();
}
var guidList = ids.Where(x => Guid.TryParse(x, out _)).Select(Guid.Parse).ToList();
if (guidList.Count == 0)
{
return new List();
}
var locs = await db.Queryable()
.Where(x => !x.IsDeleted && guidList.Contains(x.Id))
.ToListAsync();
if (locs.Count == 0)
{
return new List();
}
var partners = await db.Queryable()
.Where(x => !x.IsDeleted)
.ToListAsync();
var partnerNameToId = partners
.Where(x => !string.IsNullOrWhiteSpace(x.PartnerName))
.GroupBy(x => x.PartnerName!.Trim(), StringComparer.OrdinalIgnoreCase)
.ToDictionary(g => g.Key, g => g.First().Id, StringComparer.OrdinalIgnoreCase);
var groups = await db.Queryable()
.Where(x => !x.IsDeleted)
.ToListAsync();
var groupIdSet = new HashSet(StringComparer.Ordinal);
foreach (var loc in locs)
{
var gName = loc.GroupName?.Trim();
var pName = loc.Partner?.Trim();
if (string.IsNullOrEmpty(gName) || string.IsNullOrEmpty(pName))
{
continue;
}
if (!partnerNameToId.TryGetValue(pName, out var partnerId))
{
continue;
}
var match = groups.FirstOrDefault(g =>
g.PartnerId == partnerId &&
string.Equals(g.GroupName?.Trim(), gName, StringComparison.OrdinalIgnoreCase));
if (match is not null && !string.IsNullOrWhiteSpace(match.Id))
{
groupIdSet.Add(match.Id.Trim());
}
}
return groupIdSet.OrderBy(x => x, StringComparer.Ordinal).ToList();
}
///
/// 解析 Region(fl_group.Id)下全部未删除门店 Id。
///
public static async Task> ResolveLocationIdsFromGroupIdsAsync(
ISqlSugarClient db,
IReadOnlyList? groupIds)
{
var ids = NormalizeIds(groupIds);
if (ids.Count == 0)
{
return new List();
}
var result = new HashSet(StringComparer.Ordinal);
foreach (var gid in ids)
{
var g = await db.Queryable()
.FirstAsync(x => !x.IsDeleted && x.Id == gid);
if (g is null)
{
continue;
}
var partner = await db.Queryable()
.FirstAsync(x => !x.IsDeleted && x.Id == g.PartnerId);
var pName = partner?.PartnerName?.Trim() ?? string.Empty;
var gName = g.GroupName?.Trim() ?? string.Empty;
if (string.IsNullOrEmpty(pName) || string.IsNullOrEmpty(gName))
{
continue;
}
var locIds = await db.Queryable()
.Where(x => !x.IsDeleted && x.Partner == pName && x.GroupName == gName)
.Select(x => SqlFunc.ToString(x.Id))
.ToListAsync();
foreach (var lid in locIds.Where(x => !string.IsNullOrWhiteSpace(x)))
{
result.Add(lid.Trim());
}
}
return result.ToList();
}
///
/// 解析 Company(fl_partner.Id)下全部 Region Id(fl_group.Id)。
///
public static async Task> ResolveGroupIdsFromPartnerIdsAsync(
ISqlSugarClient db,
IReadOnlyList? partnerIds)
{
var ids = NormalizeIds(partnerIds);
if (ids.Count == 0)
{
return new List();
}
var result = new HashSet(StringComparer.Ordinal);
foreach (var pid in ids)
{
var groupIds = await db.Queryable()
.Where(x => !x.IsDeleted && x.PartnerId == pid)
.Select(x => x.Id)
.ToListAsync();
foreach (var gid in groupIds.Where(x => !string.IsNullOrWhiteSpace(x)))
{
result.Add(gid.Trim());
}
}
return result.OrderBy(x => x, StringComparer.Ordinal).ToList();
}
///
/// 校验合并后的门店 Id 均存在。
///
public static async Task ValidateLocationIdsExistAsync(ISqlSugarClient db, IReadOnlyList locationIds)
{
var ids = NormalizeIds(locationIds);
if (ids.Count == 0)
{
return;
}
foreach (var id in ids)
{
if (!Guid.TryParse(id, out _))
{
throw new UserFriendlyException("门店Id格式不正确");
}
}
var guidList = ids.Select(Guid.Parse).ToList();
var existCount = await db.Queryable()
.Where(x => !x.IsDeleted && guidList.Contains(x.Id))
.CountAsync();
if (existCount != ids.Count)
{
throw new UserFriendlyException("门店不存在");
}
}
public static List NormalizeIds(IReadOnlyList? raw)
{
return raw?
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(x => x.Trim())
.Distinct(StringComparer.Ordinal)
.ToList() ?? new List();
}
///
/// 解析 Query 中的 Id 参数(支持逗号分隔多值,如多个 Region GroupId)。
///
public static List ParseScopeIdsFromQuery(string? raw)
{
if (string.IsNullOrWhiteSpace(raw))
{
return new List();
}
var parts = raw.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
return NormalizeIds(parts);
}
///
/// Company Admin 在其绑定 Company 下新建 Region 后,将该 Region 下已有门店追加写入 userlocation(不删除已有绑定)。
///
public static async Task TryAutoBindCompanyAdminToNewRegionAsync(
ISqlSugarClient db,
ICurrentUser currentUser,
IGuidGenerator guidGenerator,
string partnerId,
string groupName,
string? creatorId)
{
if (currentUser.Id is null || ReportsRoleHelper.IsAdminRole(currentUser))
{
return;
}
if (!await TeamMemberRoleHelper.IsCompanyAdminUserAsync(db, currentUser))
{
return;
}
var pid = partnerId.Trim();
if (!await IsPartnerAllowedForUserAsync(db, currentUser.Id.Value, pid))
{
return;
}
var locationIds = await ResolveLocationIdsForPartnerAndGroupNameAsync(db, pid, groupName);
await AppendUserLocationBindingsAsync(db, guidGenerator, currentUser.Id.Value, locationIds, creatorId);
}
///
/// Company Admin 在其绑定 Company 下新建门店后,将该门店追加写入 userlocation(空 Region 先建后补门店场景)。
///
public static async Task TryAutoBindCompanyAdminToNewLocationAsync(
ISqlSugarClient db,
ICurrentUser currentUser,
IGuidGenerator guidGenerator,
string locationId,
string? partnerName,
string? creatorId)
{
if (currentUser.Id is null || ReportsRoleHelper.IsAdminRole(currentUser))
{
return;
}
if (!await TeamMemberRoleHelper.IsCompanyAdminUserAsync(db, currentUser))
{
return;
}
var partnerIds = await ResolvePartnerIdsFromLocationIdsAsync(
db, new List { locationId });
if (partnerIds.Count == 0)
{
partnerIds = await ResolvePartnerIdsFromPartnerNamesAsync(db, partnerName);
}
if (partnerIds.Count == 0 ||
!await IsAnyPartnerAllowedForUserAsync(db, currentUser.Id.Value, partnerIds))
{
return;
}
await AppendUserLocationBindingsAsync(
db, guidGenerator, currentUser.Id.Value, new List { locationId }, creatorId);
}
///
/// 追加 userlocation 绑定(幂等;仅插入当前用户尚未绑定的门店)。
///
public static async Task AppendUserLocationBindingsAsync(
ISqlSugarClient db,
IGuidGenerator guidGenerator,
Guid userId,
IReadOnlyList locationIds,
string? creatorId)
{
var wanted = NormalizeIds(locationIds);
if (wanted.Count == 0)
{
return 0;
}
foreach (var id in wanted)
{
if (!Guid.TryParse(id, out _))
{
return 0;
}
}
var guidList = wanted.Select(Guid.Parse).ToList();
var existCount = await db.Queryable()
.Where(x => !x.IsDeleted && guidList.Contains(x.Id))
.CountAsync();
if (existCount != wanted.Count)
{
return 0;
}
var existing = await ResolveBoundLocationIdsForUserAsync(db, userId);
var existingSet = new HashSet(existing, StringComparer.OrdinalIgnoreCase);
var toInsert = wanted.Where(x => !existingSet.Contains(x)).ToList();
if (toInsert.Count == 0)
{
return 0;
}
var now = DateTime.Now;
var userIdString = TeamMemberListScopeHelper.UserKey(userId);
var rows = toInsert.Select(locationId => new UserLocationDbEntity
{
Id = guidGenerator.Create().ToString(),
IsDeleted = false,
CreationTime = now,
CreatorId = creatorId,
UserId = userIdString,
LocationId = locationId,
ConcurrencyStamp = string.Empty
}).ToList();
await db.Insertable(rows).ExecuteCommandAsync();
return toInsert.Count;
}
private static async Task IsPartnerAllowedForUserAsync(
ISqlSugarClient db,
Guid userId,
string partnerId)
{
var boundLocationIds = await ResolveBoundLocationIdsForUserAsync(db, userId);
if (boundLocationIds.Count == 0)
{
return false;
}
var allowedPartnerIds = await ResolvePartnerIdsFromLocationIdsAsync(db, boundLocationIds);
return allowedPartnerIds.Contains(partnerId.Trim(), StringComparer.OrdinalIgnoreCase);
}
private static async Task IsAnyPartnerAllowedForUserAsync(
ISqlSugarClient db,
Guid userId,
IReadOnlyList partnerIds)
{
var boundLocationIds = await ResolveBoundLocationIdsForUserAsync(db, userId);
if (boundLocationIds.Count == 0)
{
return false;
}
var allowedPartnerIds = await ResolvePartnerIdsFromLocationIdsAsync(db, boundLocationIds);
var allowedSet = new HashSet(allowedPartnerIds, StringComparer.OrdinalIgnoreCase);
return partnerIds.Any(id => allowedSet.Contains(id));
}
private static async Task> ResolvePartnerIdsFromPartnerNamesAsync(
ISqlSugarClient db,
string? partnerName)
{
var name = partnerName?.Trim();
if (string.IsNullOrWhiteSpace(name))
{
return new List();
}
var partners = await db.Queryable()
.Where(x => !x.IsDeleted)
.ToListAsync();
var match = partners.FirstOrDefault(p =>
string.Equals(p.PartnerName?.Trim(), name, StringComparison.OrdinalIgnoreCase) ||
string.Equals(p.Id, name, StringComparison.OrdinalIgnoreCase));
return match is null || string.IsNullOrWhiteSpace(match.Id)
? new List()
: new List { match.Id.Trim() };
}
private static async Task> ResolveLocationIdsForPartnerAndGroupNameAsync(
ISqlSugarClient db,
string partnerId,
string groupName)
{
var partner = await db.Queryable()
.FirstAsync(x => !x.IsDeleted && x.Id == partnerId.Trim());
if (partner is null)
{
return new List();
}
var pName = partner.PartnerName?.Trim() ?? string.Empty;
var gName = groupName.Trim();
if (string.IsNullOrEmpty(pName) || string.IsNullOrEmpty(gName))
{
return new List();
}
return await db.Queryable()
.Where(x => !x.IsDeleted && x.Partner == pName && x.GroupName == gName)
.Select(x => SqlFunc.ToString(x.Id))
.ToListAsync();
}
}