added comments

master
Young 7 months ago
parent 1c9c3c3ee2
commit c6a36c055d

@ -1,5 +1,11 @@
namespace Infrastructure.Attributes; namespace Infrastructure.Attributes;
/// <summary>
/// 幂等性Attribute
/// </summary>
/// <param name="parameter">用于获取请求参数的key,比如form或者body参数的名称</param>
/// <param name="seconds">判定时长</param>
/// <param name="message">返回消息</param>
[AttributeUsage(AttributeTargets.Method, Inherited = false)] [AttributeUsage(AttributeTargets.Method, Inherited = false)]
public sealed class IdempotencyAttribute(string parameter, int seconds = 5, string message = "invalid request") public sealed class IdempotencyAttribute(string parameter, int seconds = 5, string message = "invalid request")
: Attribute : Attribute

@ -3,6 +3,9 @@ using Microsoft.AspNetCore.Mvc;
namespace Infrastructure; namespace Infrastructure;
/// <summary>
/// controller基类
/// </summary>
[ApiController] [ApiController]
[Produces("application/json")] [Produces("application/json")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
@ -38,18 +41,15 @@ public class DefaultControllerBase : ControllerBase
[NonAction] [NonAction]
[ApiExplorerSettings(IgnoreApi = true)] [ApiExplorerSettings(IgnoreApi = true)]
public static MessageData<PageData<T>> SucceedPage<T>(int page, int dataCount, int pageSize, List<T> data, public static MessageData<PageData<T>> SucceedPage<T>(
int pageCount, int page,
int totalItems,
int pageSize,
List<T> data,
int totalPages,
string message = "successful") string message = "successful")
{ {
var pageData = new PageData<T>() var pageData = new PageData<T>(page, pageSize, totalPages, totalItems, data);
{
Data = data,
TotalPages = pageCount,
PageSize = pageSize,
Page = page,
TotalItems = dataCount,
};
return new MessageData<PageData<T>>(pageData, true, message); return new MessageData<PageData<T>>(pageData, true, message);
} }

@ -1,5 +1,10 @@
namespace Infrastructure.Exceptions; namespace Infrastructure.Exceptions;
/// <summary>
/// 返回客户端的友好错误提示
/// </summary>
/// <param name="message">错误信息</param>
/// <param name="code">错误code</param>
public sealed class FriendlyException(string message, int code = 500) : Exception public sealed class FriendlyException(string message, int code = 500) : Exception
{ {
public string? Message { get; set; } = message; public string? Message { get; set; } = message;

@ -5,6 +5,12 @@ namespace Infrastructure.Extensions;
public static class AuthorizeSetup public static class AuthorizeSetup
{ {
/// <summary>
/// 添加权限认证权限Policy、Roles从配置中获取
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <returns></returns>
public static IServiceCollection AddDefaultAuthorize(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddDefaultAuthorize(this IServiceCollection services, IConfiguration configuration)
{ {
ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(services);
@ -21,10 +27,10 @@ public static class AuthorizeSetup
var securityKey = new SymmetricSecurityKey(buffer); var securityKey = new SymmetricSecurityKey(buffer);
var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256); var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
services.AddSingleton(new JwtOptions( services.AddSingleton(new JwtContext(
audienceOptions.Issuer, audienceOptions.Issuer,
audienceOptions.Audience, audienceOptions.Audience,
audienceOptions.Expiration, audienceOptions.Duration,
signingCredentials)); signingCredentials));
services.AddAuthorizationBuilder() services.AddAuthorizationBuilder()

@ -9,6 +9,11 @@ namespace Infrastructure.Extensions;
public static class ControllerSetup public static class ControllerSetup
{ {
/// <summary>
/// 配置controller包含过滤器、json序列化以及模型验证等配置
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddDefaultControllers(this IServiceCollection services) public static IServiceCollection AddDefaultControllers(this IServiceCollection services)
{ {
ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(services);

@ -2,6 +2,11 @@ namespace Infrastructure.Extensions;
public static class EncryptionSetup public static class EncryptionSetup
{ {
/// <summary>
/// 注入Aes加密对称加密服务
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddAesEncryption(this IServiceCollection services) public static IServiceCollection AddAesEncryption(this IServiceCollection services)
{ {
ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(services);

@ -1,10 +1,17 @@
using Infrastructure.Options; using Infrastructure.Options;
using Infrastructure.Repository;
using StackExchange.Redis; using StackExchange.Redis;
namespace Infrastructure.Extensions; namespace Infrastructure.Extensions;
public static class RedisSetup public static class RedisSetup
{ {
/// <summary>
/// 配置redis连接服务,以及redis数据访问仓储
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <returns></returns>
public static IServiceCollection AddDefaultRedis(this IServiceCollection services, IConfiguration configuration) public static IServiceCollection AddDefaultRedis(this IServiceCollection services, IConfiguration configuration)
{ {
ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(services);
@ -15,6 +22,7 @@ public static class RedisSetup
return services; return services;
} }
services.TryAddScoped<IRedisBasketRepository, RedisBasketRepository>();
services.TryAddSingleton<ConnectionMultiplexer>(_ => services.TryAddSingleton<ConnectionMultiplexer>(_ =>
{ {
var host = configuration["REDIS_HOST"] ?? redisOptions.Host; var host = configuration["REDIS_HOST"] ?? redisOptions.Host;

@ -4,6 +4,11 @@ namespace Infrastructure.Extensions;
public static class RepositoryContextSetup public static class RepositoryContextSetup
{ {
/// <summary>
/// 配置数据库仓储服务
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddDefaultRepositoryContext(this IServiceCollection services) public static IServiceCollection AddDefaultRepositoryContext(this IServiceCollection services)
{ {
ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(services);

@ -9,6 +9,13 @@ namespace Infrastructure.Extensions;
public static class SqlSugarSetup public static class SqlSugarSetup
{ {
/// <summary>
/// 配置sqlsugar ORM连接
/// </summary>
/// <param name="services"></param>
/// <param name="configuration"></param>
/// <param name="hostEnvironment"></param>
/// <returns></returns>
public static IServiceCollection AddDefaultSqlSugarSetup( public static IServiceCollection AddDefaultSqlSugarSetup(
this IServiceCollection services, this IServiceCollection services,
IConfiguration configuration, IConfiguration configuration,

@ -2,6 +2,11 @@ namespace Infrastructure.Extensions;
public static class TokenContextSetup public static class TokenContextSetup
{ {
/// <summary>
///
/// </summary>
/// <param name="services"></param>
/// <returns></returns>
public static IServiceCollection AddDefaultTokenContext(this IServiceCollection services) public static IServiceCollection AddDefaultTokenContext(this IServiceCollection services)
{ {
ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(services);

@ -4,6 +4,12 @@ namespace Infrastructure.Extensions;
public static class UserContextSetup public static class UserContextSetup
{ {
/// <summary>
/// 配置用户上下文服务 T为主键类型
/// </summary>
/// <param name="services"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IServiceCollection AddDefaultUserContext<T>(this IServiceCollection services) where T : IEquatable<T> public static IServiceCollection AddDefaultUserContext<T>(this IServiceCollection services) where T : IEquatable<T>
{ {
ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(services);

@ -6,6 +6,10 @@ using Microsoft.AspNetCore.Mvc.Filters;
namespace Infrastructure.Filters; namespace Infrastructure.Filters;
/// <summary>
/// controller异常过滤器用于处理已知或未知异常。
/// </summary>
/// <param name="logger"></param>
public class ExceptionsFilter(ILogger<ExceptionsFilter> logger) : IAsyncExceptionFilter public class ExceptionsFilter(ILogger<ExceptionsFilter> logger) : IAsyncExceptionFilter
{ {
public Task OnExceptionAsync(ExceptionContext context) public Task OnExceptionAsync(ExceptionContext context)
@ -14,6 +18,7 @@ public class ExceptionsFilter(ILogger<ExceptionsFilter> logger) : IAsyncExceptio
{ {
return Task.CompletedTask; return Task.CompletedTask;
} }
var message = default(MessageData); var message = default(MessageData);
if (context.Exception is FriendlyException fe) if (context.Exception is FriendlyException fe)
{ {
@ -32,6 +37,6 @@ public class ExceptionsFilter(ILogger<ExceptionsFilter> logger) : IAsyncExceptio
Content = message.Serialize() Content = message.Serialize()
}; };
context.ExceptionHandled = true; context.ExceptionHandled = true;
return Task.CompletedTask; return Task.CompletedTask;
} }
} }

@ -8,6 +8,11 @@ using Microsoft.AspNetCore.Mvc;
namespace Infrastructure.Filters; namespace Infrastructure.Filters;
/// <summary>
/// 请求幂等性过滤器配合redis统一处理需要幂等性的请求。
/// </summary>
/// <param name="logger"></param>
/// <param name="redis"></param>
public class IdempotencyFilter(ILogger<IdempotencyFilter> logger, IRedisBasketRepository redis) : IAsyncActionFilter public class IdempotencyFilter(ILogger<IdempotencyFilter> logger, IRedisBasketRepository redis) : IAsyncActionFilter
{ {
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)

@ -2,21 +2,54 @@
namespace Infrastructure.HttpUserContext; namespace Infrastructure.HttpUserContext;
/// <summary>
/// 用户上下文
/// </summary>
/// <typeparam name="TId"></typeparam>
public interface IUserContext<out TId> where TId : IEquatable<TId> public interface IUserContext<out TId> where TId : IEquatable<TId>
{ {
/// <summary>
/// 用户primary key的类型
/// </summary>
TId Id { get; } TId Id { get; }
/// <summary>
/// 用户名
/// </summary>
string Username { get; } string Username { get; }
/// <summary>
/// 名称
/// </summary>
string Name { get; } string Name { get; }
/// <summary>
/// 邮箱
/// </summary>
string Email { get; } string Email { get; }
/// <summary>
/// 角色id
/// </summary>
string[] RoleIds { get; } string[] RoleIds { get; }
/// <summary>
/// 访问Ip
/// </summary>
string RemoteIpAddress { get; } string RemoteIpAddress { get; }
JwtTokenInfo GenerateTokenInfo(JwtSecurityToken jwtSecurityToken,double? duration, string schemeName); /// <summary>
/// 生成Jwt token信息
/// </summary>
/// <param name="jwtSecurityToken"></param>
/// <param name="duration"></param>
/// <param name="schemeName"></param>
/// <returns></returns>
JwtTokenInfo GenerateTokenInfo(JwtSecurityToken jwtSecurityToken, double? duration, string schemeName);
/// <summary>
/// 从当前用户上下文获取claims
/// </summary>
/// <returns></returns>
IList<Claim> GetClaimsFromUserContext(); IList<Claim> GetClaimsFromUserContext();
} }

@ -7,9 +7,17 @@ using Microsoft.IdentityModel.Tokens;
namespace Infrastructure.HttpUserContext; namespace Infrastructure.HttpUserContext;
/// <summary>
/// <inheritdoc cref="IUserContext{TId}"/>
/// </summary>
/// <param name="httpContextAccessor"></param>
/// <param name="jwtContext"></param>
/// <param name="encryptionService"></param>
/// <param name="jwtSecurityTokenHandler"></param>
/// <typeparam name="TId"></typeparam>
public class UserContext<TId>( public class UserContext<TId>(
IHttpContextAccessor httpContextAccessor, IHttpContextAccessor httpContextAccessor,
JwtOptions jwtOptions, JwtContext jwtContext,
IEncryptionService encryptionService, IEncryptionService encryptionService,
JwtSecurityTokenHandler jwtSecurityTokenHandler) JwtSecurityTokenHandler jwtSecurityTokenHandler)
: IUserContext<TId> where TId : IEquatable<TId> : IUserContext<TId> where TId : IEquatable<TId>
@ -35,15 +43,15 @@ public class UserContext<TId>(
{ {
var claims = GetClaimsFromUserContext(); var claims = GetClaimsFromUserContext();
securityToken ??= new JwtSecurityToken( securityToken ??= new JwtSecurityToken(
issuer: jwtOptions.Issuer, issuer: jwtContext.Issuer,
audience: jwtOptions.Audience, audience: jwtContext.Audience,
claims: claims, claims: claims,
notBefore: DateTime.Now, notBefore: DateTime.Now,
expires: DateTime.Now.AddSeconds(jwtOptions.Duration), expires: DateTime.Now.AddSeconds(jwtContext.Duration),
signingCredentials: jwtOptions.SigningCredentials); signingCredentials: jwtContext.SigningCredentials);
var token = jwtSecurityTokenHandler.WriteToken(securityToken); var token = jwtSecurityTokenHandler.WriteToken(securityToken);
token = encryptionService.Encrypt(token); token = encryptionService.Encrypt(token);
return new JwtTokenInfo(token, duration ?? jwtOptions.Duration, schemeName); return new JwtTokenInfo(token, duration ?? jwtContext.Duration, schemeName);
} }
public IList<Claim> GetClaimsFromUserContext() public IList<Claim> GetClaimsFromUserContext()
@ -58,7 +66,7 @@ public class UserContext<TId>(
EpochTime.GetIntDate(DateTime.Now).ToString(CultureInfo.InvariantCulture), EpochTime.GetIntDate(DateTime.Now).ToString(CultureInfo.InvariantCulture),
ClaimValueTypes.Integer64), ClaimValueTypes.Integer64),
new(JwtRegisteredClaimNames.Exp, new(JwtRegisteredClaimNames.Exp,
TimeSpan.FromSeconds(jwtOptions.Duration).ToString()) TimeSpan.FromSeconds(jwtContext.Duration).ToString())
}; };
claims.AddRange(RoleIds.Select(rId => new Claim(ClaimTypes.Role, rId))); claims.AddRange(RoleIds.Select(rId => new Claim(ClaimTypes.Role, rId)));
return claims; return claims;

@ -1,5 +1,8 @@
namespace Infrastructure; namespace Infrastructure;
/// <summary>
/// 实体类软删除接口用于filter
/// </summary>
public interface IDeletable public interface IDeletable
{ {
bool IsDeleted { get; set; } bool IsDeleted { get; set; }

@ -1,5 +1,11 @@
namespace Infrastructure; namespace Infrastructure;
/// <summary>
/// 客户端返回统一封装
/// </summary>
/// <param name="successful"></param>
/// <param name="message"></param>
/// <param name="code"></param>
public class MessageData(bool successful, string message, int code = 500) public class MessageData(bool successful, string message, int code = 500)
{ {
public bool Successful { get; set; } = successful; public bool Successful { get; set; } = successful;

@ -3,6 +3,10 @@ using Microsoft.AspNetCore.Http;
namespace Infrastructure.Middlewares; namespace Infrastructure.Middlewares;
/// <summary>
/// 访问路由404自定义中间件
/// </summary>
/// <param name="next"></param>
public class NotFoundMiddleware(RequestDelegate next) public class NotFoundMiddleware(RequestDelegate next)
{ {
public async Task InvokeAsync(HttpContext context) public async Task InvokeAsync(HttpContext context)
@ -12,10 +16,10 @@ public class NotFoundMiddleware(RequestDelegate next)
if (context.Response.StatusCode == StatusCodes.Status404NotFound) if (context.Response.StatusCode == StatusCodes.Status404NotFound)
{ {
var path = context.Request.Path.Value; var path = context.Request.Path.Value;
var message=new MessageData(false,$"request path: {path} not found",404); var message = new MessageData(false, $"request path: {path} not found", 404);
context.Response.ContentType = "application/json"; context.Response.ContentType = "application/json";
context.Response.StatusCode = StatusCodes.Status200OK; context.Response.StatusCode = StatusCodes.Status200OK;
await context.Response.WriteAsync(message.Serialize()); await context.Response.WriteAsync(message.Serialize());
} }
} }
} }

@ -1,5 +1,8 @@
namespace Infrastructure.Options; namespace Infrastructure.Options;
/// <summary>
/// Jwt配置
/// </summary>
public sealed class AudienceOptions : OptionsBase public sealed class AudienceOptions : OptionsBase
{ {
public const string Name = "Audience"; public const string Name = "Audience";
@ -10,7 +13,7 @@ public sealed class AudienceOptions : OptionsBase
public string Secret { get; set; } public string Secret { get; set; }
public int Expiration { get; set; } public int Duration { get; set; }
public string? Policy { get; set; } public string? Policy { get; set; }

@ -1,5 +1,8 @@
namespace Infrastructure.Options; namespace Infrastructure.Options;
/// <summary>
/// rabbitmq链接配置
/// </summary>
public class RabbitMqOptions public class RabbitMqOptions
{ {
public string Host { get; set; } = string.Empty; public string Host { get; set; } = string.Empty;

@ -1,5 +1,8 @@
namespace Infrastructure.Options; namespace Infrastructure.Options;
/// <summary>
/// redis连接配置
/// </summary>
public sealed class RedisOptions : OptionsBase public sealed class RedisOptions : OptionsBase
{ {
public const string Name = "Redis"; public const string Name = "Redis";

@ -1,5 +1,8 @@
namespace Infrastructure.Options; namespace Infrastructure.Options;
/// <summary>
/// ORM连接配置
/// </summary>
public sealed class SqlSugarOptions : OptionsBase public sealed class SqlSugarOptions : OptionsBase
{ {
public const string Name = "SqlSugar"; public const string Name = "SqlSugar";

@ -1,5 +1,8 @@
namespace Infrastructure.Options; namespace Infrastructure.Options;
/// <summary>
/// api版本配置
/// </summary>
public class VersionOptions : OptionsBase public class VersionOptions : OptionsBase
{ {
public const string Name = "Version"; public const string Name = "Version";

@ -1,27 +1,23 @@
namespace Infrastructure; namespace Infrastructure;
public class PageData<T> /// <summary>
/// 分页数据
/// </summary>
/// <param name="page">当前页</param>
/// <param name="pageSize">当前页数量</param>
/// <param name="totalPages">总页数量</param>
/// <param name="totalItems">总条目数量</param>
/// <param name="data">返回数据</param>
/// <typeparam name="T">实体类型</typeparam>
public class PageData<T>(int page, int pageSize, int totalPages, int totalItems, List<T> data)
{ {
public PageData() public int Page { get; set; } = page;
{
}
public PageData(int page, int pageSize, int totalPages, int totalItems, List<T> data) public int PageSize { get; set; } = pageSize;
{
Page = page;
PageSize = pageSize;
TotalItems = totalItems;
TotalPages = totalPages;
Data = data;
}
public int Page { get; set; } public int TotalPages { get; set; } = totalPages;
public int PageSize { get; set; } public int TotalItems { get; set; } = totalItems;
public int TotalPages { get; set; } public List<T> Data { get; set; } = data;
public int TotalItems { get; set; }
public List<T> Data { get; set; }
} }

@ -1,8 +1,10 @@
using Infrastructure.Utils; using StackExchange.Redis;
using StackExchange.Redis;
namespace Infrastructure.Repository; namespace Infrastructure.Repository;
/// <summary>
/// redis访问仓储
/// </summary>
public interface IRedisBasketRepository public interface IRedisBasketRepository
{ {
Task<string> GetValue(string key); Task<string> GetValue(string key);
@ -46,172 +48,4 @@ public interface IRedisBasketRepository
Task<long> ListDelRangeAsync(string redisKey, string redisValue, long type = 0, int db = -1); Task<long> ListDelRangeAsync(string redisKey, string redisValue, long type = 0, int db = -1);
Task ListClearAsync(string redisKey, int db = -1); Task ListClearAsync(string redisKey, int db = -1);
}
public class RedisBasketRepository(ILogger<RedisBasketRepository> logger, ConnectionMultiplexer redis)
: IRedisBasketRepository
{
private readonly IDatabase _database = redis.GetDatabase();
private IServer GetServer()
{
var endpoint = redis.GetEndPoints();
return redis.GetServer(endpoint.First());
}
public async Task Clear()
{
foreach (var endPoint in redis.GetEndPoints())
{
var server = GetServer();
foreach (var key in server.Keys())
{
await _database.KeyDeleteAsync(key);
}
}
}
public async Task<bool> Exist(string key)
{
return await _database.KeyExistsAsync(key);
}
public async Task<string> GetValue(string key)
{
return await _database.StringGetAsync(key);
}
public async Task Remove(string key)
{
await _database.KeyDeleteAsync(key);
}
public async Task Set(string key, object value, TimeSpan cacheTime)
{
if (value != null)
{
if (value is string cacheValue)
{
await _database.StringSetAsync(key, cacheValue, cacheTime);
}
else
{
var jsonString = value.Serialize();
var buffer = Encoding.UTF8.GetBytes(jsonString);
await _database.StringSetAsync(key, buffer, cacheTime);
}
}
}
public async Task<bool> SetValues(Dictionary<string, object> valuePairs, TimeSpan cacheTime)
{
var transaction = _database.CreateTransaction();
foreach (var pair in valuePairs)
{
if (pair.Value is string value)
{
await _database.StringSetAsync(pair.Key, value, cacheTime);
}
else
{
var jsonString = pair.Value.Serialize();
var buffer = Encoding.UTF8.GetBytes(jsonString);
await _database.StringSetAsync(pair.Key, buffer, cacheTime);
}
}
return await transaction.ExecuteAsync();
}
public async Task<TEntity?> Get<TEntity>(string key)
{
var value = await _database.StringGetAsync(key);
if (value.HasValue)
{
var jsonString = Encoding.UTF8.GetString(value);
return jsonString.Deserialize<TEntity>();
}
else
{
return default;
}
}
public async Task<List<T>> GetValues<T>(string[] keys) where T : class
{
var redisKeys = keys.Select(k => new RedisKey(k)).ToArray();
var redisValues = await _database.StringGetAsync(redisKeys);
return redisValues.Where(v => v.HasValue).Select(r => SerializeExtension.Deserialize<T>(r)).ToList();
}
public async Task<RedisValue[]> ListRangeAsync(string redisKey)
{
return await _database.ListRangeAsync(redisKey);
}
public async Task<long> ListLeftPushAsync(string redisKey, string redisValue, int db = -1)
{
return await _database.ListLeftPushAsync(redisKey, redisValue);
}
public async Task<long> ListRightPushAsync(string redisKey, string redisValue, int db = -1)
{
return await _database.ListRightPushAsync(redisKey, redisValue);
}
public async Task<long> ListRightPushAsync(string redisKey, IEnumerable<string> redisValue, int db = -1)
{
var redislist = redisValue.Select(r => (RedisValue)r).ToArray();
return await _database.ListRightPushAsync(redisKey, redislist);
}
public async Task<T> ListLeftPopAsync<T>(string redisKey, int db = -1) where T : class
{
var value = await _database.ListLeftPopAsync(redisKey);
return SerializeExtension.Deserialize<T>(await _database.ListLeftPopAsync(redisKey));
}
public async Task<T> ListRightPopAsync<T>(string redisKey, int db = -1) where T : class
{
return SerializeExtension.Deserialize<T>(await _database.ListRightPopAsync(redisKey));
}
public async Task<string> ListLeftPopAsync(string redisKey, int db = -1)
{
return await _database.ListLeftPopAsync(redisKey);
}
public async Task<string> ListRightPopAsync(string redisKey, int db = -1)
{
return await _database.ListRightPopAsync(redisKey);
}
public async Task<long> ListLengthAsync(string redisKey, int db = -1)
{
return await _database.ListLengthAsync(redisKey);
}
public async Task<IEnumerable<string>> ListRangeAsync(string redisKey, int db = -1)
{
var result = await _database.ListRangeAsync(redisKey);
return result.Select(o => o.ToString());
}
public async Task<IEnumerable<string>> ListRangeAsync(string redisKey, int start, int stop, int db = -1)
{
var result = await _database.ListRangeAsync(redisKey, start, stop);
return result.Select(o => o.ToString());
}
public async Task<long> ListDelRangeAsync(string redisKey, string redisValue, long type = 0, int db = -1)
{
return await _database.ListRemoveAsync(redisKey, redisValue, type);
}
public async Task ListClearAsync(string redisKey, int db = -1)
{
await _database.ListTrimAsync(redisKey, 1, 0);
}
} }

@ -3,6 +3,10 @@ using SqlSugar;
namespace Infrastructure.Repository; namespace Infrastructure.Repository;
/// <summary>
/// 数据库ORM仓储
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IRepositoryBase<T> where T : class, new() public interface IRepositoryBase<T> where T : class, new()
{ {
ISqlSugarClient DbContext { get; } ISqlSugarClient DbContext { get; }

@ -3,6 +3,10 @@ using SqlSugar;
namespace Infrastructure.Repository; namespace Infrastructure.Repository;
/// <summary>
/// 数据库访问层
/// </summary>
/// <typeparam name="T"></typeparam>
public interface IServiceBase<T> where T : class, new() public interface IServiceBase<T> where T : class, new()
{ {
IRepositoryBase<T> DAL { get; } IRepositoryBase<T> DAL { get; }

@ -2,6 +2,9 @@
namespace Infrastructure.Repository; namespace Infrastructure.Repository;
/// <summary>
/// 工作单元
/// </summary>
public interface IUnitOfWork public interface IUnitOfWork
{ {
SqlSugarScope DbClient { get; } SqlSugarScope DbClient { get; }
@ -11,61 +14,4 @@ public interface IUnitOfWork
void CommitTransaction(); void CommitTransaction();
void RollbackTransaction(); void RollbackTransaction();
}
public class UnitOfWork : IUnitOfWork
{
private int _count;
private readonly SqlSugarScope? _dbClient;
public SqlSugarScope DbClient => _dbClient!;
public UnitOfWork(ISqlSugarClient sqlSugarClient)
{
if (sqlSugarClient is SqlSugarScope scope)
{
_dbClient = scope;
}
}
public void BeginTransaction()
{
lock (this)
{
_count++;
_dbClient?.BeginTran();
}
}
public void CommitTransaction()
{
lock (this)
{
_count--;
if (_count != 0)
{
return;
}
try
{
_dbClient?.CommitTran();
}
catch (Exception e)
{
Console.WriteLine(e);
_dbClient?.RollbackTran();
}
}
}
public void RollbackTransaction()
{
lock (this)
{
_count--;
_dbClient?.RollbackTran();
}
}
} }

@ -0,0 +1,177 @@
using Infrastructure.Utils;
using StackExchange.Redis;
namespace Infrastructure.Repository;
/// <summary>
/// <inheritdoc cref="IRedisBasketRepository"/>
/// </summary>
/// <param name="logger"></param>
/// <param name="redis"></param>
public class RedisBasketRepository(ILogger<RedisBasketRepository> logger, ConnectionMultiplexer redis)
: IRedisBasketRepository
{
private readonly IDatabase _database = redis.GetDatabase();
private IServer GetServer()
{
var endpoint = redis.GetEndPoints();
return redis.GetServer(endpoint.First());
}
public async Task Clear()
{
foreach (var endPoint in redis.GetEndPoints())
{
var server = GetServer();
foreach (var key in server.Keys())
{
await _database.KeyDeleteAsync(key);
}
}
}
public async Task<bool> Exist(string key)
{
return await _database.KeyExistsAsync(key);
}
public async Task<string> GetValue(string key)
{
return await _database.StringGetAsync(key);
}
public async Task Remove(string key)
{
await _database.KeyDeleteAsync(key);
}
public async Task Set(string key, object value, TimeSpan cacheTime)
{
if (value != null)
{
if (value is string cacheValue)
{
await _database.StringSetAsync(key, cacheValue, cacheTime);
}
else
{
var jsonString = value.Serialize();
var buffer = Encoding.UTF8.GetBytes(jsonString);
await _database.StringSetAsync(key, buffer, cacheTime);
}
}
}
public async Task<bool> SetValues(Dictionary<string, object> valuePairs, TimeSpan cacheTime)
{
var transaction = _database.CreateTransaction();
foreach (var pair in valuePairs)
{
if (pair.Value is string value)
{
await _database.StringSetAsync(pair.Key, value, cacheTime);
}
else
{
var jsonString = pair.Value.Serialize();
var buffer = Encoding.UTF8.GetBytes(jsonString);
await _database.StringSetAsync(pair.Key, buffer, cacheTime);
}
}
return await transaction.ExecuteAsync();
}
public async Task<TEntity?> Get<TEntity>(string key)
{
var value = await _database.StringGetAsync(key);
if (value.HasValue)
{
var jsonString = Encoding.UTF8.GetString(value);
return jsonString.Deserialize<TEntity>();
}
else
{
return default;
}
}
public async Task<List<T>> GetValues<T>(string[] keys) where T : class
{
var redisKeys = keys.Select(k => new RedisKey(k)).ToArray();
var redisValues = await _database.StringGetAsync(redisKeys);
return redisValues.Where(v => v.HasValue).Select(r => SerializeExtension.Deserialize<T>(r)).ToList();
}
public async Task<RedisValue[]> ListRangeAsync(string redisKey)
{
return await _database.ListRangeAsync(redisKey);
}
public async Task<long> ListLeftPushAsync(string redisKey, string redisValue, int db = -1)
{
return await _database.ListLeftPushAsync(redisKey, redisValue);
}
public async Task<long> ListRightPushAsync(string redisKey, string redisValue, int db = -1)
{
return await _database.ListRightPushAsync(redisKey, redisValue);
}
public async Task<long> ListRightPushAsync(string redisKey, IEnumerable<string> redisValue, int db = -1)
{
var redislist = redisValue.Select(r => (RedisValue)r).ToArray();
return await _database.ListRightPushAsync(redisKey, redislist);
}
public async Task<T> ListLeftPopAsync<T>(string redisKey, int db = -1) where T : class
{
var value = await _database.ListLeftPopAsync(redisKey);
return SerializeExtension.Deserialize<T>(await _database.ListLeftPopAsync(redisKey));
}
public async Task<T> ListRightPopAsync<T>(string redisKey, int db = -1) where T : class
{
return SerializeExtension.Deserialize<T>(await _database.ListRightPopAsync(redisKey));
}
public async Task<string> ListLeftPopAsync(string redisKey, int db = -1)
{
return await _database.ListLeftPopAsync(redisKey);
}
public async Task<string> ListRightPopAsync(string redisKey, int db = -1)
{
return await _database.ListRightPopAsync(redisKey);
}
public async Task<long> ListLengthAsync(string redisKey, int db = -1)
{
return await _database.ListLengthAsync(redisKey);
}
public async Task<IEnumerable<string>> ListRangeAsync(string redisKey, int db = -1)
{
var result = await _database.ListRangeAsync(redisKey);
return result.Select(o => o.ToString());
}
public async Task<IEnumerable<string>> ListRangeAsync(string redisKey, int start, int stop, int db = -1)
{
var result = await _database.ListRangeAsync(redisKey, start, stop);
return result.Select(o => o.ToString());
}
public async Task<long> ListDelRangeAsync(string redisKey, string redisValue, long type = 0, int db = -1)
{
return await _database.ListRemoveAsync(redisKey, redisValue, type);
}
public async Task ListClearAsync(string redisKey, int db = -1)
{
await _database.ListTrimAsync(redisKey, 1, 0);
}
}

@ -4,6 +4,11 @@ using SqlSugar.Extensions;
namespace Infrastructure.Repository; namespace Infrastructure.Repository;
/// <summary>
/// <inheritdoc cref="IRepositoryBase{T}"/>
/// </summary>
/// <param name="context"></param>
/// <typeparam name="T"></typeparam>
public class RepositoryBase<T>(ISqlSugarClient context) : IRepositoryBase<T> where T : class, new() public class RepositoryBase<T>(ISqlSugarClient context) : IRepositoryBase<T> where T : class, new()
{ {
public ISqlSugarClient DbContext => context; public ISqlSugarClient DbContext => context;

@ -3,6 +3,11 @@ using SqlSugar;
namespace Infrastructure.Repository; namespace Infrastructure.Repository;
/// <summary>
/// <inheritdoc cref="IServiceBase{T}"/>
/// </summary>
/// <param name="dbContext"></param>
/// <typeparam name="T"></typeparam>
public abstract class ServiceBase<T>(IRepositoryBase<T> dbContext) : IServiceBase<T> where T : class, new() public abstract class ServiceBase<T>(IRepositoryBase<T> dbContext) : IServiceBase<T> where T : class, new()
{ {
public IRepositoryBase<T> DAL { get; } = dbContext; public IRepositoryBase<T> DAL { get; } = dbContext;

@ -0,0 +1,60 @@
using SqlSugar;
namespace Infrastructure.Repository;
public class UnitOfWork : IUnitOfWork
{
private int _count;
private readonly SqlSugarScope? _dbClient;
public SqlSugarScope DbClient => _dbClient!;
public UnitOfWork(ISqlSugarClient sqlSugarClient)
{
if (sqlSugarClient is SqlSugarScope scope)
{
_dbClient = scope;
}
}
public void BeginTransaction()
{
lock (this)
{
_count++;
_dbClient?.BeginTran();
}
}
public void CommitTransaction()
{
lock (this)
{
_count--;
if (_count != 0)
{
return;
}
try
{
_dbClient?.CommitTran();
}
catch (Exception e)
{
Console.WriteLine(e);
_dbClient?.RollbackTran();
}
}
}
public void RollbackTransaction()
{
lock (this)
{
_count--;
_dbClient?.RollbackTran();
}
}
}

@ -5,6 +5,12 @@ using Microsoft.AspNetCore.Http;
namespace Infrastructure.Security; namespace Infrastructure.Security;
/// <summary>
/// 用于统一处理验证和授权
/// </summary>
/// <param name="options"></param>
/// <param name="logger"></param>
/// <param name="encoder"></param>
public class DefaultAuthenticationHandler( public class DefaultAuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options, IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, ILoggerFactory logger,

@ -2,6 +2,11 @@ using Microsoft.IdentityModel.Tokens;
namespace Infrastructure.Security; namespace Infrastructure.Security;
/// <summary>
/// 自定义token解密
/// </summary>
/// <param name="encryptionService"></param>
/// <param name="jwtSecurityTokenHandler"></param>
public class DefaultTokenHandler(IEncryptionService encryptionService, JwtSecurityTokenHandler jwtSecurityTokenHandler) public class DefaultTokenHandler(IEncryptionService encryptionService, JwtSecurityTokenHandler jwtSecurityTokenHandler)
: TokenHandler : TokenHandler
{ {

@ -2,6 +2,9 @@ using System.Security.Cryptography;
namespace Infrastructure.Security; namespace Infrastructure.Security;
/// <summary>
/// aes对称加解密服务
/// </summary>
public interface IEncryptionService public interface IEncryptionService
{ {
string Encrypt(string plain, string? aesKey = null); string Encrypt(string plain, string? aesKey = null);

@ -2,7 +2,14 @@ using Microsoft.IdentityModel.Tokens;
namespace Infrastructure.Security; namespace Infrastructure.Security;
public class JwtOptions( /// <summary>
/// jwt配置上下文
/// </summary>
/// <param name="issuer"></param>
/// <param name="audience"></param>
/// <param name="duration"></param>
/// <param name="credentials"></param>
public class JwtContext(
string issuer, string issuer,
string audience, string audience,
long duration, long duration,

@ -1,5 +1,11 @@
namespace Infrastructure.Security; namespace Infrastructure.Security;
/// <summary>
/// 返回客户端的jwt信息
/// </summary>
/// <param name="token"></param>
/// <param name="expiredIn"></param>
/// <param name="tokenType"></param>
public class JwtTokenInfo(string token, double expiredIn, string tokenType) public class JwtTokenInfo(string token, double expiredIn, string tokenType)
{ {
public string? Token { get; } = token; public string? Token { get; } = token;

@ -2,8 +2,16 @@ using Microsoft.AspNetCore.Http;
namespace Infrastructure.Utils; namespace Infrastructure.Utils;
/// <summary>
/// HttpContext扩展
/// </summary>
public static class HttpContextExtension public static class HttpContextExtension
{ {
/// <summary>
/// 用于获取用户访问ip
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public static string? GetRequestIp(this HttpContext context) public static string? GetRequestIp(this HttpContext context)
{ {
var ip = context.GetRequestHeaderValue<string>("X-Forwarded-For"); var ip = context.GetRequestHeaderValue<string>("X-Forwarded-For");
@ -20,7 +28,14 @@ public static class HttpContextExtension
return ip; return ip;
} }
/// <summary>
/// 获取header值
/// </summary>
/// <param name="context"></param>
/// <param name="headerName"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T? GetRequestHeaderValue<T>(this HttpContext context, string headerName) public static T? GetRequestHeaderValue<T>(this HttpContext context, string headerName)
{ {
if (!context.Request.Headers.TryGetValue(headerName, out var value)) if (!context.Request.Headers.TryGetValue(headerName, out var value))

@ -3,6 +3,9 @@ using Newtonsoft.Json.Serialization;
namespace Infrastructure.Utils; namespace Infrastructure.Utils;
/// <summary>
/// json序列化扩展
/// </summary>
public static class SerializeExtension public static class SerializeExtension
{ {
static SerializeExtension() static SerializeExtension()
@ -16,6 +19,7 @@ public static class SerializeExtension
ReferenceLoopHandling = ReferenceLoopHandling.Ignore ReferenceLoopHandling = ReferenceLoopHandling.Ignore
}; };
} }
public static string Serialize(this object obj) public static string Serialize(this object obj)
{ {
return JsonConvert.SerializeObject(obj); return JsonConvert.SerializeObject(obj);

Loading…
Cancel
Save