添加项目文件。

This commit is contained in:
2025-12-02 13:10:10 +08:00
parent 93a2382a16
commit 289aa4cbe7
400 changed files with 91177 additions and 0 deletions

View File

@@ -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();
}
}
}

View 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");
}
}
}
}

View File

@@ -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();
}
}
}