using Fake.DependencyInjection; using Fake.Rbac.Infrastructure; using Microsoft.EntityFrameworkCore; using Oms.Application.Dtos.DataPerm; namespace Oms.Application.DataPerm; public class DataPermRepository(FakeRbacDbContext dbContext) : IScopedDependency { private FakeRbacDbContext Db => dbContext; public async Task GetUserSnapshotAsync(Guid userId, CancellationToken cancellationToken = default) { const string sql = """ SELECT u.`Id` AS `Id`, u.`userid` AS `MapId`, u.`Account` AS `Account`, u.`Name` AS `Name`, u.`OrganizationId` AS `OrganizationId`, o.`Name` AS `OrganizationName` FROM `user` u LEFT JOIN `organization` o ON o.`Id` = u.`OrganizationId` AND o.`IsDeleted` = 0 WHERE u.`Id` = {0} AND u.`IsDeleted` = 0 LIMIT 1 """; return await Db.Database.SqlQueryRaw(sql, userId.ToString()) .FirstOrDefaultAsync(cancellationToken); } public async Task IsPureCustomerUserAsync(Guid userId, CancellationToken cancellationToken = default) { const string sql = """ SELECT COUNT(*) AS `Value` FROM `user_role` ur INNER JOIN `role` r ON r.`Id` = ur.`RoleId` AND r.`IsDeleted` = 0 WHERE ur.`UserId` = {0} AND r.`Code` <> 'Customer' AND r.`Code` NOT LIKE 'CustomerSub_%' """; var nonCustomerRoleCount = await Db.Database.SqlQueryRaw(sql, userId.ToString()) .FirstAsync(cancellationToken); if (nonCustomerRoleCount > 0) { return false; } const string hasRoleSql = """ SELECT COUNT(*) AS `Value` FROM `user_role` ur WHERE ur.`UserId` = {0} """; var roleCount = await Db.Database.SqlQueryRaw(hasRoleSql, userId.ToString()) .FirstAsync(cancellationToken); return roleCount > 0; } public async Task IsAdminUserAsync(Guid userId, CancellationToken cancellationToken = default) { const string sql = """ SELECT COUNT(*) AS `Value` FROM `user_role` ur INNER JOIN `role` r ON r.`Id` = ur.`RoleId` AND r.`IsDeleted` = 0 WHERE ur.`UserId` = {0} AND r.`Code` = 'Admin' """; var count = await Db.Database.SqlQueryRaw(sql, userId.ToString()).FirstAsync(cancellationToken); return count > 0; } public async Task> GetUserRoleIdsAsync(Guid userId, CancellationToken cancellationToken = default) { const string sql = """ SELECT ur.`RoleId` AS `Value` FROM `user_role` ur WHERE ur.`UserId` = {0} """; return await Db.Database.SqlQueryRaw(sql, userId.ToString()) .ToListAsync(cancellationToken); } public async Task> GetUserIdsByRoleAsync(Guid roleId, CancellationToken cancellationToken = default) { const string sql = """ SELECT ur.`UserId` AS `Value` FROM `user_role` ur WHERE ur.`RoleId` = {0} """; return await Db.Database.SqlQueryRaw(sql, roleId.ToString()) .ToListAsync(cancellationToken); } public async Task> GetRoleOrgPermsAsync(IEnumerable roleIds, CancellationToken cancellationToken = default) { var ids = roleIds.Distinct().ToList(); if (ids.Count == 0) { return []; } var inClause = string.Join(",", ids.Select(x => $"'{x}'")); var sql = $""" SELECT p.`OrganizationId` AS `OrganizationId`, p.`OrgScope` AS `OrgScope`, NULL AS `EffectiveTime`, NULL AS `ExpireTime` FROM `role_org_perm` p WHERE p.`IsDeleted` = 0 AND p.`RoleId` IN ({inClause}) """; return await Db.Database.SqlQueryRaw(sql).ToListAsync(cancellationToken); } public async Task> GetUserOrgPermsAsync(Guid userId, bool includeExpired, CancellationToken cancellationToken = default) { var sql = includeExpired ? """ SELECT p.`OrganizationId` AS `OrganizationId`, p.`OrgScope` AS `OrgScope`, p.`EffectiveTime` AS `EffectiveTime`, p.`ExpireTime` AS `ExpireTime` FROM `user_org_perm` p WHERE p.`IsDeleted` = 0 AND p.`UserId` = {0} """ : """ SELECT p.`OrganizationId` AS `OrganizationId`, p.`OrgScope` AS `OrgScope`, p.`EffectiveTime` AS `EffectiveTime`, p.`ExpireTime` AS `ExpireTime` FROM `user_org_perm` p WHERE p.`IsDeleted` = 0 AND p.`UserId` = {0} AND p.`EffectiveTime` <= UTC_TIMESTAMP() AND (p.`ExpireTime` IS NULL OR p.`ExpireTime` > UTC_TIMESTAMP()) """; return await Db.Database.SqlQueryRaw(sql, userId.ToString()) .ToListAsync(cancellationToken); } public async Task> GetRoleDataScopesAsync(IEnumerable roleIds, CancellationToken cancellationToken = default) { var ids = roleIds.Distinct().ToList(); if (ids.Count == 0) { return []; } var inClause = string.Join(",", ids.Select(x => $"'{x}'")); var sql = $""" SELECT p.`DataScope` AS `DataScope`, p.`DataResourceCode` AS `DataResourceCode`, NULL AS `ExpireTime` FROM `role_data_scope` p WHERE p.`IsDeleted` = 0 AND p.`RoleId` IN ({inClause}) """; return await Db.Database.SqlQueryRaw(sql).ToListAsync(cancellationToken); } public async Task> GetUserDataScopesAsync(Guid userId, bool includeExpired, CancellationToken cancellationToken = default) { var sql = includeExpired ? """ SELECT p.`DataScope` AS `DataScope`, p.`DataResourceCode` AS `DataResourceCode`, p.`ExpireTime` AS `ExpireTime` FROM `user_data_scope` p WHERE p.`IsDeleted` = 0 AND p.`UserId` = {0} """ : """ SELECT p.`DataScope` AS `DataScope`, p.`DataResourceCode` AS `DataResourceCode`, p.`ExpireTime` AS `ExpireTime` FROM `user_data_scope` p WHERE p.`IsDeleted` = 0 AND p.`UserId` = {0} AND p.`EffectiveTime` <= UTC_TIMESTAMP() AND (p.`ExpireTime` IS NULL OR p.`ExpireTime` > UTC_TIMESTAMP()) """; return await Db.Database.SqlQueryRaw(sql, userId.ToString()) .ToListAsync(cancellationToken); } public async Task> GetRoleBizPermsAsync(IEnumerable roleIds, CancellationToken cancellationToken = default) { var ids = roleIds.Distinct().ToList(); if (ids.Count == 0) { return []; } var inClause = string.Join(",", ids.Select(x => $"'{x}'")); var sql = $""" SELECT p.`BizType` AS `BizType`, p.`BizId` AS `BizId`, NULL AS `ExpireTime` FROM `role_biz_perm` p WHERE p.`IsDeleted` = 0 AND p.`RoleId` IN ({inClause}) """; return await Db.Database.SqlQueryRaw(sql).ToListAsync(cancellationToken); } public async Task> GetUserBizPermsAsync(Guid userId, bool includeExpired, CancellationToken cancellationToken = default) { var sql = includeExpired ? """ SELECT p.`BizType` AS `BizType`, p.`BizId` AS `BizId`, p.`ExpireTime` AS `ExpireTime` FROM `user_biz_perm` p WHERE p.`IsDeleted` = 0 AND p.`UserId` = {0} """ : """ SELECT p.`BizType` AS `BizType`, p.`BizId` AS `BizId`, p.`ExpireTime` AS `ExpireTime` FROM `user_biz_perm` p WHERE p.`IsDeleted` = 0 AND p.`UserId` = {0} AND p.`EffectiveTime` <= UTC_TIMESTAMP() AND (p.`ExpireTime` IS NULL OR p.`ExpireTime` > UTC_TIMESTAMP()) """; return await Db.Database.SqlQueryRaw(sql, userId.ToString()) .ToListAsync(cancellationToken); } public async Task> GetRoleSecretPermsAsync(IEnumerable roleIds, CancellationToken cancellationToken = default) { var ids = roleIds.Distinct().ToList(); if (ids.Count == 0) { return []; } var inClause = string.Join(",", ids.Select(x => $"'{x}'")); var sql = $""" SELECT p.`SecretLevel` AS `SecretLevel`, p.`DataResourceCode` AS `DataResourceCode`, NULL AS `ExpireTime` FROM `role_secret_perm` p WHERE p.`IsDeleted` = 0 AND p.`RoleId` IN ({inClause}) """; return await Db.Database.SqlQueryRaw(sql).ToListAsync(cancellationToken); } public async Task> GetUserSecretPermsAsync(Guid userId, bool includeExpired, CancellationToken cancellationToken = default) { var sql = includeExpired ? """ SELECT p.`SecretLevel` AS `SecretLevel`, p.`DataResourceCode` AS `DataResourceCode`, p.`ExpireTime` AS `ExpireTime` FROM `user_secret_perm` p WHERE p.`IsDeleted` = 0 AND p.`UserId` = {0} """ : """ SELECT p.`SecretLevel` AS `SecretLevel`, p.`DataResourceCode` AS `DataResourceCode`, p.`ExpireTime` AS `ExpireTime` FROM `user_secret_perm` p WHERE p.`IsDeleted` = 0 AND p.`UserId` = {0} AND p.`EffectiveTime` <= UTC_TIMESTAMP() AND (p.`ExpireTime` IS NULL OR p.`ExpireTime` > UTC_TIMESTAMP()) """; return await Db.Database.SqlQueryRaw(sql, userId.ToString()) .ToListAsync(cancellationToken); } public async Task> GetRoleFilterRulesAsync( IEnumerable roleIds, string dataResourceCode, CancellationToken cancellationToken = default) { var ids = roleIds.Distinct().ToList(); if (ids.Count == 0) { return []; } var inClause = string.Join(",", ids.Select(x => $"'{x}'")); var sql = $""" SELECT p.`DataResourceCode` AS `DataResourceCode`, CAST(p.`FilterContent` AS CHAR) AS `FilterContentJson`, NULL AS `ExpireTime` FROM `role_filter_rule` p WHERE p.`IsDeleted` = 0 AND p.`RoleId` IN ({inClause}) AND p.`DataResourceCode` = '{dataResourceCode.Replace("'", "''")}' """; return await Db.Database.SqlQueryRaw(sql).ToListAsync(cancellationToken); } public async Task> GetUserFilterRulesAsync( Guid userId, string dataResourceCode, bool includeExpired, CancellationToken cancellationToken = default) { var resource = dataResourceCode.Replace("'", "''"); var baseSql = """ SELECT p.`DataResourceCode` AS `DataResourceCode`, CAST(p.`FilterContent` AS CHAR) AS `FilterContentJson`, p.`ExpireTime` AS `ExpireTime` FROM `user_filter_rule` p WHERE p.`IsDeleted` = 0 AND p.`UserId` = {0} AND p.`DataResourceCode` = """ + $"'{resource}'"; var sql = includeExpired ? baseSql : baseSql + """ AND p.`EffectiveTime` <= UTC_TIMESTAMP() AND (p.`ExpireTime` IS NULL OR p.`ExpireTime` > UTC_TIMESTAMP()) """; return await Db.Database.SqlQueryRaw(sql, userId.ToString()) .ToListAsync(cancellationToken); } public async Task> ExpandOrganizationIdsAsync( IEnumerable<(Guid OrganizationId, int OrgScope)> orgEntries, CancellationToken cancellationToken = default) { var result = new HashSet(); foreach (var (organizationId, orgScope) in orgEntries) { if (organizationId == Guid.Empty) { continue; } if (orgScope == DataPermConstants.OrgScopeSelfOnly) { result.Add(organizationId); continue; } var descendants = await GetOrganizationSubtreeIdsAsync(organizationId, cancellationToken); foreach (var id in descendants) { result.Add(id); } } return result; } public async Task> GetOrganizationSubtreeIdsAsync(Guid rootOrganizationId, CancellationToken cancellationToken = default) { const string sql = """ WITH RECURSIVE org_tree AS ( SELECT o.`Id` FROM `organization` o WHERE o.`Id` = {0} AND o.`IsDeleted` = 0 UNION ALL SELECT c.`Id` FROM `organization` c INNER JOIN org_tree t ON c.`ParentId` = t.`Id` WHERE c.`IsDeleted` = 0 ) SELECT `Id` AS `Value` FROM org_tree """; return await Db.Database.SqlQueryRaw(sql, rootOrganizationId.ToString()) .ToListAsync(cancellationToken); } public async Task> GetOrganizationNamesAsync( IEnumerable organizationIds, CancellationToken cancellationToken = default) { var ids = organizationIds.Distinct().Where(x => x != Guid.Empty).ToList(); if (ids.Count == 0) { return []; } var inClause = string.Join(",", ids.Select(x => $"'{x}'")); var sql = $""" SELECT o.`Id` AS `Id`, o.`Name` AS `Name` FROM `organization` o WHERE o.`IsDeleted` = 0 AND o.`Id` IN ({inClause}) """; var rows = await Db.Database.SqlQueryRaw(sql).ToListAsync(cancellationToken); return rows.ToDictionary(x => x.Id, x => x.Name ?? x.Id.ToString()); } public async Task OrganizationExistsAndEnabledAsync(Guid organizationId, CancellationToken cancellationToken = default) { const string sql = """ SELECT COUNT(*) AS `Value` FROM `organization` o WHERE o.`Id` = {0} AND o.`IsDeleted` = 0 """; var count = await Db.Database.SqlQueryRaw(sql, organizationId.ToString()).FirstAsync(cancellationToken); return count > 0; } public async Task> ResolveOwnerMapIdsAsync( Guid organizationId, int dataScope, CancellationToken cancellationToken = default) { if (organizationId == Guid.Empty) { return []; } if (dataScope == DataPermConstants.DataScopeTeam) { const string sql = """ SELECT u.`userid` AS `Value` FROM `user` u WHERE u.`IsDeleted` = 0 AND u.`OrganizationId` = {0} """; return await Db.Database.SqlQueryRaw(sql, organizationId.ToString()) .ToListAsync(cancellationToken); } if (dataScope == DataPermConstants.DataScopeDepartment) { var orgIds = await GetOrganizationSubtreeIdsAsync(organizationId, cancellationToken); if (orgIds.Count == 0) { return []; } var inClause = string.Join(",", orgIds.Select(x => $"'{x}'")); var sql = $""" SELECT u.`userid` AS `Value` FROM `user` u WHERE u.`IsDeleted` = 0 AND u.`OrganizationId` IN ({inClause}) """; return await Db.Database.SqlQueryRaw(sql).ToListAsync(cancellationToken); } return []; } public async Task> GetDataResourcesAsync(CancellationToken cancellationToken = default) { const string sql = """ SELECT r.`Code` AS `Code`, r.`Name` AS `Name`, r.`MainTable` AS `MainTable` FROM `data_resource` r WHERE r.`IsDeleted` = 0 AND r.`IsEnabled` = 1 ORDER BY r.`SortOrder`, r.`Code` """; var rows = await Db.Database.SqlQueryRaw(sql).ToListAsync(cancellationToken); return rows.Select(row => new DataResourceMetaDto { Code = row.Code, Name = row.Name, MainTable = row.MainTable, FilterableFields = DataPermConstants.FilterableFieldsByResource.TryGetValue(row.Code, out var fields) ? fields.ToList() : [] }).ToList(); } public async Task InvalidateUserContextCacheKeysAsync(Guid userId, CancellationToken cancellationToken = default) { // Cache invalidation handled by DataPermContextCache; repository placeholder for future DB-side hooks. await Task.CompletedTask; } private sealed class OrganizationNameRow { public Guid Id { get; set; } public string? Name { get; set; } } private sealed class DataResourceRow { public string Code { get; set; } = string.Empty; public string Name { get; set; } = string.Empty; public string? MainTable { get; set; } } }