添加项目文件。
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
using Atomx.Common.Utils;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using System.Net.Http.Json;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace Atomx.Admin.Client.Services
|
||||
{
|
||||
public interface IPermissionService
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否拥有权限点
|
||||
/// </summary>
|
||||
/// <param name="permission"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> HasPermissionAsync(string permission);
|
||||
|
||||
/// <summary>
|
||||
/// 是否拥有指定权限中的一个
|
||||
/// </summary>
|
||||
/// <param name="permissions"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> HasAnyPermissionAsync(params string[] permissions);
|
||||
|
||||
/// <summary>
|
||||
/// 是否拥有指定权限中的所有权限
|
||||
/// </summary>
|
||||
/// <param name="permissions"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> HasAllPermissionsAsync(params string[] permissions);
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户的所有权限
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<List<string>> GetUserPermissionsAsync();
|
||||
//Task<List<string>> GetUserRolesAsync();
|
||||
}
|
||||
|
||||
public class ClientPermissionService : IPermissionService
|
||||
{
|
||||
private readonly AuthenticationStateProvider _authenticationStateProvider;
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public ClientPermissionService(
|
||||
AuthenticationStateProvider authenticationStateProvider,
|
||||
HttpClient httpClient)
|
||||
{
|
||||
_authenticationStateProvider = authenticationStateProvider;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public async Task<bool> HasPermissionAsync(string permission)
|
||||
{
|
||||
// 客户端检查(基于声明)
|
||||
var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
|
||||
var user = authState.User;
|
||||
|
||||
if (!user.Identity?.IsAuthenticated ?? true)
|
||||
return false;
|
||||
|
||||
// 检查声明中的权限
|
||||
var hasPermission = user.Claims.Any(c =>
|
||||
c.Type == ClaimKeys.Permission && c.Value == permission);
|
||||
|
||||
if (hasPermission)
|
||||
return true;
|
||||
|
||||
// 如果声明中没有,调用API验证
|
||||
//try
|
||||
//{
|
||||
// return await _httpClient.GetFromJsonAsync<bool>($"/api/auth/haspermission?permission={permission}");
|
||||
//}
|
||||
//catch
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> HasAnyPermissionAsync(params string[] permissions)
|
||||
{
|
||||
foreach (var permission in permissions)
|
||||
{
|
||||
if (await HasPermissionAsync(permission))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public async Task<bool> HasAllPermissionsAsync(params string[] permissions)
|
||||
{
|
||||
foreach (var permission in permissions)
|
||||
{
|
||||
if (!await HasPermissionAsync(permission))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetUserPermissionsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _httpClient.GetFromJsonAsync<List<string>>("/api/auth/permissions")
|
||||
?? new List<string>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new List<string>();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetUserRolesAsync()
|
||||
{
|
||||
var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
|
||||
var user = authState.User;
|
||||
|
||||
return user.Claims
|
||||
.Where(c => c.Type == ClaimTypes.Role)
|
||||
.Select(c => c.Value)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
67
Atomx.Admin/Atomx.Admin.Client/Services/HttpService.cs
Normal file
67
Atomx.Admin/Atomx.Admin.Client/Services/HttpService.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using Atomx.Common.Models;
|
||||
using Atomx.Utils.Json;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
|
||||
namespace Atomx.Admin.Client.Services
|
||||
{
|
||||
public class HttpService(HttpClient httpClient)
|
||||
{
|
||||
public async Task<T> Get<T>(string url)
|
||||
{
|
||||
var response = await httpClient.GetAsync(url);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
return content.FromJson<T>();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Error: {response.StatusCode}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<T> Post<T>(string url, object data)
|
||||
{
|
||||
var json = data.ToJson();
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
var response = await httpClient.PostAsync(url, content);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
return responseContent.FromJson<T>();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Error: {response.StatusCode}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ApiResult<PagingList<T>>> GetPagingList<T>(string url, object data, int page, int size = 20)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (page < 1)
|
||||
{
|
||||
page = 1;
|
||||
}
|
||||
url = $"{url}?page={page}&size={size}";
|
||||
var response = await httpClient.PostAsJsonAsync(url, data);
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
return content.FromJson<ApiResult<PagingList<T>>>();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Error: {response.StatusCode}");
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
throw new Exception($"api {url} service failure");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,325 @@
|
||||
using Atomx.Common.Models;
|
||||
using Microsoft.JSInterop;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Atomx.Admin.Client.Services
|
||||
{
|
||||
public interface ILocalizationService
|
||||
{
|
||||
/// <summary>
|
||||
/// 根据name获取制定文化语言的译文
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns></returns>
|
||||
Task<string?> GetStringAsync(string name, string? culture = null);
|
||||
|
||||
/// <summary>
|
||||
/// 把本地化文化语言加载到内存中
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> LoadResourcesAsync(string culture);
|
||||
|
||||
|
||||
event EventHandler<string>? ResourcesUpdated;
|
||||
}
|
||||
|
||||
public class LocalizationClientService : ILocalizationService, IAsyncDisposable
|
||||
{
|
||||
private readonly HttpService _httpService;
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly IJSRuntime _jsRuntime;
|
||||
private readonly ILogger<LocalizationClientService> _logger;
|
||||
|
||||
private readonly Dictionary<string, Dictionary<string, string>> _resources = new();
|
||||
private readonly Dictionary<string, string> _versions = new();
|
||||
private readonly SemaphoreSlim _semaphore = new(1, 1);
|
||||
|
||||
public event EventHandler<string>? ResourcesUpdated;
|
||||
|
||||
public LocalizationClientService(
|
||||
HttpService httpService,
|
||||
HttpClient httpClient,
|
||||
IJSRuntime jsRuntime,
|
||||
ILogger<LocalizationClientService> logger)
|
||||
{
|
||||
_httpService = httpService;
|
||||
_httpClient = httpClient;
|
||||
_jsRuntime = jsRuntime;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据name获取制定文化语言的译文
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<string?> GetStringAsync(string name, string? culture = null)
|
||||
{
|
||||
culture ??= await GetCurrentCultureAsync();
|
||||
|
||||
if (_resources.TryGetValue(culture, out var cultureResources))
|
||||
{
|
||||
if (cultureResources.TryGetValue(name, out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
// 键不存在,触发资源更新检查
|
||||
_ = Task.Run(async () => await CheckAndUpdateResourcesAsync(culture));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 资源未加载,立即加载
|
||||
await LoadResourcesAsync(culture);
|
||||
|
||||
// 重试获取
|
||||
if (_resources.TryGetValue(culture, out cultureResources) &&
|
||||
cultureResources.TryGetValue(name, out var value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogWarning("Localization key not found: {Key} for culture: {Culture}", name, culture);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 把本地化文化语言加载到内存中
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> LoadResourcesAsync(string culture)
|
||||
{
|
||||
await _semaphore.WaitAsync();
|
||||
try
|
||||
{
|
||||
// 先尝试从localStorage加载
|
||||
if (await TryLoadFromLocalStorage(culture))
|
||||
{
|
||||
// 检查服务器版本,如果需要更新则从服务器加载
|
||||
if (await CheckAndUpdateFromServer(culture))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return true; // 本地版本仍然有效
|
||||
}
|
||||
|
||||
// 从服务器加载
|
||||
return await LoadFromServer(culture);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_semaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从单例对象中获取版本记录
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<string?> GetResourceVersionAsync(string culture)
|
||||
{
|
||||
if (_versions.TryGetValue(culture, out var version))
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
var storedVersion = await GetStoredVersion(culture);
|
||||
return storedVersion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查更新资源
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<bool> CheckAndUpdateResourcesAsync(string culture)
|
||||
{
|
||||
return await CheckAndUpdateFromServer(culture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从本地加载文化语言数据
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<bool> TryLoadFromLocalStorage(string culture)
|
||||
{
|
||||
try
|
||||
{
|
||||
var resourcesJson = await _jsRuntime.InvokeAsync<string?>("localStorage.getItem", $"locales_{culture}");
|
||||
var version = await _jsRuntime.InvokeAsync<string?>("localStorage.getItem", $"locales_version_{culture}");
|
||||
|
||||
if (string.IsNullOrEmpty(resourcesJson) || string.IsNullOrEmpty(version))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var resources = JsonSerializer.Deserialize<Dictionary<string, string>>(resourcesJson);
|
||||
|
||||
if (resources != null && version != null)
|
||||
{
|
||||
_resources[culture] = resources;
|
||||
_versions[culture] = version;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to load resources from localStorage for culture: {Culture}", culture);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从服务器更新获取文化语言数据
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<bool> LoadFromServer(string culture)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await _httpService.Get<ApiResult<LocalizationFile>>($"/api/localeresource/resources/{culture}");
|
||||
if (response.Success)
|
||||
{
|
||||
_resources[culture] = response.Data.Translations;
|
||||
_versions[culture] = response.Data.ResourceVersion;
|
||||
// 保存到localStorage
|
||||
await SaveToLocalStorage(culture, response.Data.Translations, response.Data.ResourceVersion);
|
||||
ResourcesUpdated?.Invoke(this, culture);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to load resources from server for culture: {Culture}", culture);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据版本信息上从服务器数据对比版本更新数据
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<bool> CheckAndUpdateFromServer(string culture)
|
||||
{
|
||||
try
|
||||
{
|
||||
var currentVersion = await GetResourceVersionAsync(culture);
|
||||
var serverVersion = await GetServerVersion(culture);
|
||||
|
||||
if (serverVersion != null && (currentVersion == null || serverVersion != currentVersion))
|
||||
{
|
||||
_logger.LogInformation("Updating resources for culture: {Culture}", culture);
|
||||
return await LoadFromServer(culture);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to check and update resources for culture: {Culture}", culture);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从服务器获取当前多语言文化数据版本信息
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<string?> GetServerVersion(string culture)
|
||||
{
|
||||
try
|
||||
{
|
||||
var api = $"/api/localeresource/version/{culture}";
|
||||
var result = await _httpService.Get<ApiResult<string>>(api);
|
||||
if (result.Success)
|
||||
{
|
||||
return result.Data;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to get server version for culture: {Culture}", culture);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取存储在本地的文化语言版本信息
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<string?> GetStoredVersion(string culture)
|
||||
{
|
||||
try
|
||||
{
|
||||
var version = await _jsRuntime.InvokeAsync<string?>("localStorage.getItem", $"locales_version_{culture}");
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
{
|
||||
return version;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to get stored version for culture: {Culture}", culture);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 存储版本和文化语言信息
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <param name="resources"></param>
|
||||
/// <param name="version"></param>
|
||||
/// <returns></returns>
|
||||
private async Task SaveToLocalStorage(string culture, Dictionary<string, string> resources, string version)
|
||||
{
|
||||
try
|
||||
{
|
||||
var resourcesJson = JsonSerializer.Serialize(resources);
|
||||
|
||||
await _jsRuntime.InvokeVoidAsync("localStorage.setItem", $"locales_{culture}", resourcesJson);
|
||||
await _jsRuntime.InvokeVoidAsync("localStorage.setItem", $"locales_version_{culture}", version);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Failed to save resources to localStorage for culture: {Culture}", culture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取浏览器上的文化语言信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task<string> GetCurrentCultureAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _jsRuntime.InvokeAsync<string>("blazorCulture.get");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "en-US"; // 默认文化
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
_semaphore?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user