添加项目文件。

This commit is contained in:
2026-10-12 16:22:04 +08:00
parent 7c68a0ca0a
commit aad11be43d
1000 changed files with 120807 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
public class AppInstanceId
{
public string Id { get; set; }
}
}
+45
View File
@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
public class BaseResponse<T> : BaseCommonResponse
{
public T Data { get; set; }
}
public class ListResponse<T> : BaseCommonResponse
{
public IEnumerable<T> Data { get; set; }
}
public class PageResponse<T> : BaseCommonResponse
{
public IEnumerable<T> Data { get; set; }
public int Count { get; set; }
}
public class PageDataResponse<T> : BaseCommonResponse
{
public int Count { get; set; }
public IEnumerable<T> Data { get; set; }
}
public class BaseCommonResponse
{
public CommonResponseCodeEnum Code { get; set; }
public string Info { get; set; }
public BaseCommonResponse(CommonResponseCodeEnum code = CommonResponseCodeEnum.Success, string info = null)
{
Code = code;
Info = info;
}
}
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ToolGood.Words;
namespace Common.BlockWord
{
public static class BlockWordHelper
{
public static bool IsHaveBlockWord(string text)
{
if (string.IsNullOrWhiteSpace(text))
return false;
StringSearch iwords = new StringSearch();
iwords.SetKeywords(BlockWord.BlockWordValue.Split(','));
return iwords.ContainsAny(text);
}
public static string ReplaceBlockWord(string text, char replaceChar = '*')
{
StringSearch iwords = new StringSearch();
iwords.SetKeywords(BlockWord.BlockWordValue.Split(','));
return iwords.Replace(text, replaceChar);
}
public static List<string> GetBlockWord(string text)
{
if (string.IsNullOrWhiteSpace(text))
return null;
StringSearch iwords = new StringSearch();
iwords.SetKeywords(BlockWord.BlockWordValue.Split(','));
return iwords.FindAll(text);
}
}
}
@@ -0,0 +1,18 @@
using System;
namespace Common
{
public class BusinessException : Exception
{
public int ErrorCode { get; }
public BusinessException() : base("业务处理失败") { }
public BusinessException(string message) : base(message) { }
public BusinessException(string message, int errorCode) : base(message)
{
ErrorCode = errorCode;
}
}
}
+23
View File
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Cors" Version="2.3.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.7" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="NPinyin" Version="0.2.6321.26573" />
<PackageReference Include="RestSharp" Version="112.1.0" />
<PackageReference Include="ToolGood.Words" Version="3.1.0.3" />
<PackageReference Include="XAct.Core.PCL" Version="0.0.5014" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RedisHelper\RedisHelper.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.CommonModel
{
public class BaseIdName
{
/// <summary>
/// Id
/// </summary>
public string Id { get; set; }
public string Name { get; set; }
}
}
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.CommonModel
{
public class BaseListPageParam
{
private string _keyword = "";
private int _page = 1;
private int _pageSize = 10;
public string KeyWord
{
get { return string.IsNullOrWhiteSpace(_keyword) ? "" : _keyword.Trim(); }
set { _keyword = string.IsNullOrWhiteSpace(value) ? "" : value.Trim(); }
}
public int? PageIndex
{
get { return _page <= 1 ? 1 : _page; }
set { _page = value ?? 0; }
}
public int? PageSize
{
get { return _pageSize <= 1 ? 10 : _pageSize; }
set { _pageSize = value ?? 0; }
}
}
}
@@ -0,0 +1,41 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.Cors
{
public static class CorsExtension
{
public static IServiceCollection AddCustomCors(this IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("Cors", policy =>
{
policy
.SetIsOriginAllowed(_ => true)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
;
});
});
return services;
}
public static void UseCustomCors(this IApplicationBuilder app)
{
app.UseCors("Cors");
}
}
}
+103
View File
@@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
/// <summary>
/// Enables the efficient, dynamic composition of query predicates.
/// </summary>
public static class LinqExtensions
{
public static Expression Property(this Expression expression, string propertyName)
{
return Expression.Property(expression, propertyName);
}
public static Expression AndAlso(this Expression left, Expression right)
{
return Expression.AndAlso(left, right);
}
public static Expression Call(this Expression instance, string methodName, params Expression[] arguments)
{
return Expression.Call(instance, instance.Type.GetMethod(methodName), arguments);
}
public static Expression GreaterThan(this Expression left, Expression right)
{
return Expression.GreaterThan(left, right);
}
public static Expression<T> ToLambda<T>(this Expression body, params ParameterExpression[] parameters)
{
return Expression.Lambda<T>(body, parameters);
}
public static Expression<Func<T, bool>> True<T>() { return param => true; }
public static Expression<Func<T, bool>> False<T>() { return param => false; }
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.AndAlso);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.OrElse);
}
/// <summary>
/// Combines the first expression with the second using the specified merge function.
/// </summary>
static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
{
var map = first.Parameters
.Select((f, i) => new { f, s = second.Parameters[i] })
.ToDictionary(p => p.s, p => p.f);
var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
}
/// <summary>
/// ParameterRebinder
/// </summary>
private class ParameterRebinder : ExpressionVisitor
{
/// <summary>
/// The ParameterExpression map
/// </summary>
readonly Dictionary<ParameterExpression, ParameterExpression> map;
/// <summary>
/// Initializes a new instance of the <see cref="ParameterRebinder"/> class.
/// </summary>
/// <param name="map">The map.</param>
ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
{
this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
}
/// <summary>
/// Replaces the parameters.
/// </summary>
/// <param name="map">The map.</param>
/// <param name="exp">The exp.</param>
/// <returns>Expression</returns>
public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
{
return new ParameterRebinder(map).Visit(exp);
}
/// <summary>
/// Visits the parameter.
/// </summary>
/// <param name="p">The p.</param>
/// <returns>Expression</returns>
protected override Expression VisitParameter(ParameterExpression p)
{
ParameterExpression replacement;
if (map.TryGetValue(p, out replacement))
{
p = replacement;
}
return base.VisitParameter(p);
}
}
}
}
+55
View File
@@ -0,0 +1,55 @@
using System;
namespace Common.Redis
{
public class RedisKey
{
public static string MatchingRoom => RedisKeyGenerator.GameKey("matching:room:level:{0}");
public static string UserCIDGID => RedisKeyGenerator.UserKey("uid:{0}");
public static string LoginCIDUser => RedisKeyGenerator.UserKey("cid:{0}");
public static string CIDUser => RedisKeyGenerator.UserKey("cid:{0}");
public static string VerificationCode => RedisKeyGenerator.UserKey("verification_code:{0}");
public static string ProductRedis => RedisKeyGenerator.UserKey("product:{0}");
public static string WeiXin_AccessToken => RedisKeyGenerator.CommonKey("wx_access_token");
public static string WeiXinUser_AccessToken => RedisKeyGenerator.CommonKey("wx_user_access_token:{0}");
public static string GameConfig => RedisKeyGenerator.GameKey("config");
public static string ActivityConfig => RedisKeyGenerator.GameKey("activity:config");
public static string TodayVisitsKey => RedisKeyGenerator.AdminKey("stats:visits:today");
public static string MonthVisitsKey => RedisKeyGenerator.AdminKey("stats:visits:month");
public static string TotalVisitsKey => RedisKeyGenerator.AdminKey("stats:visits:total");
public static string TotalTrafficKey => RedisKeyGenerator.AdminKey("stats:traffic:total");
public static string TodayTrafficKey => RedisKeyGenerator.AdminKey("stats:traffic:today");
public static string MonthTrafficKey => RedisKeyGenerator.AdminKey("stats:traffic:month");
public static string GameRoomIdKey => RedisKeyGenerator.GameKey("room:model:{0}_{1}");
public static string DistributedLockPrefix => RedisKeyGenerator.CommonKey("lock:");
public static string ChessRoomList => RedisKeyGenerator.GameKey("chess:room:list");
public static string ChessRoomKey => RedisKeyGenerator.GameKey("chess:room:{0}");
public static string SystemGoldDayKey => RedisKeyGenerator.GameKey("fish:gamestatic");
public static string CUSTOMER_ONLINE_KEY => RedisKeyGenerator.CustomerKey("online");
public static string FishingKillConfig => RedisKeyGenerator.GameKey("fishing:killconfig");
public static string FishSpawnConfig => RedisKeyGenerator.GameKey("fish:spawn:config:v1");
}
}
@@ -0,0 +1,92 @@
namespace Common.Redis
{
public static class RedisKeyGenerator
{
private static readonly Dictionary<string, string> ServicePrefixes = new()
{
{ "user", "user-service" },
{ "admin", "admin-service" },
{ "game", "game-service" },
{ "fish", "game-service" },
{ "chess", "game-service" },
{ "customer", "customer-service" },
{ "common", "app-shared" }
};
public static string GetKey(string serviceName, string key, params object[] args)
{
var prefix = GetPrefix(serviceName);
var formattedKey = args.Length > 0 ? string.Format(key, args) : key;
return $"{prefix}:{formattedKey}";
}
private static string GetPrefix(string serviceName)
{
return ServicePrefixes.TryGetValue(serviceName.ToLower(), out var prefix)
? prefix
: "app-shared";
}
public static string UserKey(string key, params object[] args) =>
GetKey("user", key, args);
public static string AdminKey(string key, params object[] args) =>
GetKey("admin", key, args);
public static string GameKey(string key, params object[] args) =>
GetKey("game", key, args);
public static string CustomerKey(string key, params object[] args) =>
GetKey("customer", key, args);
public static string CommonKey(string key, params object[] args) =>
GetKey("common", key, args);
}
public static class RedisKeyConstants
{
private const string ServicePrefix = "redis0524";
public static string MatchingRoom => $"{ServicePrefix}:matching:room:level_{{0}}";
public static string UserCIDGID => $"{ServicePrefix}:user:uid_{{0}}";
public static string LoginCIDUser => $"{ServicePrefix}:user:cid_{{0}}";
public static string CIDUser => $"{ServicePrefix}:user:cid_{{0}}";
public static string VerificationCode => $"{ServicePrefix}:verification_code:{{0}}";
public static string ProductRedis => $"{ServicePrefix}:product:{{0}}";
public static string WeiXin_AccessToken => $"{ServicePrefix}:wx_access_token";
public static string WeiXinUser_AccessToken => $"{ServicePrefix}:wx_user_access_token:{{0}}";
public static string GameConfig => $"{ServicePrefix}:game_config";
public static string TodayVisitsKey => $"{ServicePrefix}:TodayVisits";
public static string MonthVisitsKey => $"{ServicePrefix}:MonthVisits";
public static string TotalVisitsKey => $"{ServicePrefix}:TotalVisits";
public static string TotalTrafficKey => $"{ServicePrefix}:TotalTraffic";
public static string TodayTrafficKey => $"{ServicePrefix}:TodayTraffic";
public static string MonthTrafficKey => $"{ServicePrefix}:MonthTraffic";
public static string GameRoomIdKey => $"{ServicePrefix}:game:room:model:{{0}}_{{1}}";
public static string DistributedLockPrefix => $"{ServicePrefix}:lock:";
public static string ChessRoomList => $"{ServicePrefix}:chess:room:list";
public static string ChessRoomKey => $"{ServicePrefix}:chess:room:{{0}}";
public static string SystemGoldDayKey => $"{ServicePrefix}:fish:gamestatic";
public static string CustomerOnlineKey => "game0524:customer:online";
}
}
@@ -0,0 +1,21 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Common.Request
{
public interface IRequestApi
{
Task<string> GetAsync(string host, string resource, Dictionary<string, object> param,
Dictionary<string, string> headerParms = null, int timeOut = 5);
Task<string> PostAsync(string host, string resource, object param, Dictionary<string, string> queryParam = null,
Dictionary<string, string> headerParms = null, int timeOut = 5);
Task<string> PostFormDataAsync(string host, string resource, Dictionary<string, object> param);
Task<bool> DownloadFile(string fileName, string host, string resource,
Dictionary<string, object> param = null, Dictionary<string, string> headers = null);
}
}
+202
View File
@@ -0,0 +1,202 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Common.Utils;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RestSharp;
namespace Common.Request
{
public class RequestApi : IRequestApi
{
private readonly IHttpContextAccessor _httpContextAccessor;
private ILogger<RequestApi> _logger;
private Dictionary<string, string> defaultHeader = null;
public RequestApi(IHttpContextAccessor httpContextAccessor, ILogger<RequestApi> logger)
{
_logger = logger;
_httpContextAccessor = httpContextAccessor;
defaultHeader = new Dictionary<string, string>();
if (_httpContextAccessor.HttpContext != null && _httpContextAccessor.HttpContext.Request != null &&
_httpContextAccessor.HttpContext.Request.Headers != null)
{
defaultHeader.Add("token", _httpContextAccessor.HttpContext.Request.Headers["token"].ToString() ?? "");
}
}
public async Task<string> GetAsync(string host, string resource, Dictionary<string, object> param = null,
Dictionary<string, string> headers = null, int timeOut = 5)
{
var options = new RestClientOptions(host)
{
ThrowOnAnyError = true
};
var restClient = new RestClient(options);
var request = new RestRequest(resource)
{
Timeout = TimeSpan.FromSeconds(timeOut)
};
if (param != null && param.Count > 0)
{
foreach (var item in param.Where(e => e.Value != null))
{
request.AddQueryParameter(item.Key, item.Value.ToString());
}
}
if (headers != null)
{
defaultHeader = headers;
}
request.AddHeaders(defaultHeader);
var restResponse = await restClient.ExecuteGetAsync(request);
return HandleResponse(restResponse, resource, param);
}
public async Task<string> PostAsync(string host, string resource, object param,
Dictionary<string, string> queryParams = null, Dictionary<string, string> headers = null, int timeOut = 5)
{
var options = new RestClientOptions(host)
{
ThrowOnAnyError = true
};
var restClient = new RestClient(options);
var request = new RestRequest(resource)
{
Timeout = TimeSpan.FromSeconds(timeOut)
};
if (queryParams != null && queryParams.Count > 0)
{
foreach (var item in queryParams)
{
request.AddQueryParameter(item.Key, item.Value);
}
}
if (param != null)
{
request.AddJsonBody(param);
}
if (headers != null)
{
defaultHeader = headers;
}
try
{
request.AddHeaders(defaultHeader);
var restResponse = await restClient.ExecutePostAsync(request);
return HandleResponse(restResponse, resource, param);
}
catch (Exception ex)
{
return "";
}
}
public async Task<string> PostFormDataAsync(string host, string resource, Dictionary<string, object> param)
{
var options = new RestClientOptions(host)
{
ThrowOnAnyError = true
};
var restClient = new RestClient(options);
var request = new RestRequest(resource);
if (param != null && param.Count > 0)
{
foreach (var item in param)
{
request.AddParameter(item.Key, item.Value.ToJson(), ParameterType.RequestBody);
}
}
try
{
request.AddHeaders(defaultHeader);
var restResponse = await restClient.ExecutePostAsync(request);
return HandleResponse(restResponse, resource, param);
}
catch (Exception ex)
{
return "";
}
}
private string HandleResponse(RestResponse restResponse, string resource, object param)
{
if (!restResponse.IsSuccessful)
{
return JsonConvert.SerializeObject(new {info = restResponse.ErrorMessage, code = -1});
}
try
{
JObject jt = Newtonsoft.Json.JsonConvert.DeserializeObject<JObject>(restResponse.Content);
int code = jt["code"].ObjToInt();
if (code != 0 && code != 401)
{
}
}
catch (Exception ex)
{
}
return restResponse.Content;
}
public async Task<bool> DownloadFile(string fileName, string host, string resource,
Dictionary<string, object> param = null, Dictionary<string, string> headers = null)
{
var options = new RestClientOptions(host)
{
ThrowOnAnyError = true,
};
var restClient = new RestClient(options);
var request = new RestRequest(resource);
if (param != null && param.Count > 0)
{
foreach (var item in param.Where(e => e.Value != null))
{
request.AddQueryParameter(item.Key, item.Value.ToString());
}
}
if (headers != null)
{
defaultHeader = headers;
}
request.AddHeaders(defaultHeader);
var restResponse = await restClient.DownloadDataAsync(request);
if (System.IO.File.Exists(fileName))
{
System.IO.File.Delete(fileName);
}
using (FileStream pFileStream = new FileStream(fileName, FileMode.OpenOrCreate))
{
pFileStream.Write(restResponse, 0, restResponse.Length);
}
return System.IO.File.Exists(fileName);
}
}
}
+27
View File
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common
{
public enum CommonResponseCodeEnum
{
Error = -1,
Exception = 500,
NoAuth = 401,
NoRole = 403,
TokenExpire = 1000,
Success = 0,
NoLogin = 401,
TokenInvalid = 12,
ParamError = 13,
SignError = 14,
ResultNull = 15,
LowVersion = 16,
ResultFail = -1
}
}
@@ -0,0 +1,14 @@
using System.Collections.Generic;
namespace Common.Utils
{
public static class ConstValues
{
public const int LockSecond = 5;
public const string LockNotice = "业务正在处理中,请稍后";
public const string DevModel = "dev";
}
}
@@ -0,0 +1,611 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using Newtonsoft.Json;
using NPinyin;
namespace Common.Utils
{
public static class ConvertExtension
{
/// <summary>
///
/// </summary>
/// <param name="thisValue"></param>
/// <returns></returns>
public static int ObjToInt(this object thisValue)
{
int reval = 0;
if (thisValue == null) return 0;
if (thisValue != null && thisValue != DBNull.Value && int.TryParse(thisValue.ToString(), out reval))
{
return reval;
}
return reval;
}
public static bool ContainsAll<T>(this IEnumerable<T> source, IEnumerable<T> items)
{
if (source == null || items == null)
return false;
var sourceCounts = source.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
var itemsCounts = items.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
foreach (var item in itemsCounts)
{
if (!sourceCounts.TryGetValue(item.Key, out int count) || count < item.Value)
{
return false;
}
}
return true;
}
public static IEnumerable<T> RemoveRange<T>(this IEnumerable<T> source, IEnumerable<T> items)
{
if (source == null)
return Enumerable.Empty<T>();
if (items == null || !items.Any())
return source;
var list = new List<T>(source);
foreach (var item in items)
{
int index = list.IndexOf(item);
if (index >= 0)
{
list.RemoveAt(index);
}
}
return list;
}
/// <summary>
///
/// </summary>
/// <param name="thisValue"></param>
/// <param name="errorValue"></param>
/// <returns></returns>
public static int ObjToInt(this object thisValue, int errorValue)
{
int reval = 0;
if (thisValue != null && thisValue != DBNull.Value && int.TryParse(thisValue.ToString(), out reval))
{
return reval;
}
return errorValue;
}
/// <summary>
///
/// </summary>
/// <param name="thisValue"></param>
/// <returns></returns>
public static long ObjToLong(this object thisValue)
{
long reval = 0;
if (thisValue == null) return 0;
if (thisValue != null && thisValue != DBNull.Value && long.TryParse(thisValue.ToString(), out reval))
{
return reval;
}
return reval;
}
/// <summary>
///
/// </summary>
/// <param name="thisValue"></param>
/// <returns></returns>
public static double ObjToMoney(this object thisValue)
{
double reval = 0;
if (thisValue != null && thisValue != DBNull.Value && double.TryParse(thisValue.ToString(), out reval))
{
return reval;
}
return 0;
}
public static T GetRandomItem<T>(this IEnumerable<T> thisValue)
{
if (thisValue != null && thisValue.Count() > 0)
{
int index = Random.Shared.Next(thisValue.Count());
return thisValue.ToList()[index];
}
else if (thisValue.Count() == 1)
{
return thisValue.ToList()[0];
}
else
return default(T);
}
/// <summary>
///
/// </summary>
/// <param name="thisValue"></param>
/// <param name="errorValue"></param>
/// <returns></returns>
public static double ObjToMoney(this object thisValue, double errorValue)
{
double reval = 0;
if (thisValue != null && thisValue != DBNull.Value && double.TryParse(thisValue.ToString(), out reval))
{
return reval;
}
return errorValue;
}
/// <summary>
///
/// </summary>
/// <param name="thisValue"></param>
/// <returns></returns>
public static string ObjToString(this object thisValue)
{
if (thisValue != null) return thisValue.ToString().Trim();
return "";
}
/// <summary>
///
/// </summary>
/// <param name="thisValue"></param>
/// <returns></returns>
public static bool IsNotEmptyOrNull(this IEnumerable<object> thisValue)
{
return thisValue != null && thisValue.Count() > 0; ;
}
/// <summary>
///
/// </summary>
/// <param name="thisValue"></param>
/// <param name="errorValue"></param>
/// <returns></returns>
public static string ObjToString(this object thisValue, string errorValue)
{
if (thisValue != null) return thisValue.ToString().Trim();
return errorValue;
}
/// <summary>
///
/// </summary>
/// <param name="thisValue"></param>
/// <returns></returns>
public static Decimal ObjToDecimal(this object thisValue)
{
Decimal reval = 0;
if (thisValue != null && thisValue != DBNull.Value && decimal.TryParse(thisValue.ToString(), out reval))
{
return reval;
}
return 0;
}
/// <summary>
///
/// </summary>
/// <param name="thisValue"></param>
/// <returns></returns>
public static double ObjToDouble(this object thisValue)
{
double reval = 0;
if (thisValue != null && thisValue != DBNull.Value && double.TryParse(thisValue.ToString(), out reval))
{
return reval;
}
return 0;
}
/// <summary>
///
/// </summary>
/// <param name="thisValue"></param>
/// <param name="errorValue"></param>
/// <returns></returns>
public static Decimal ObjToDecimal(this object thisValue, decimal errorValue)
{
Decimal reval = 0;
if (thisValue != null && thisValue != DBNull.Value && decimal.TryParse(thisValue.ToString(), out reval))
{
return reval;
}
return errorValue;
}
/// <summary>
///
/// </summary>
/// <param name="thisValue"></param>
/// <returns></returns>
public static DateTime ObjToDate(this object thisValue)
{
DateTime reval = DateTime.MinValue;
if (thisValue != null && thisValue != DBNull.Value && DateTime.TryParse(thisValue.ToString(), out reval))
{
reval = Convert.ToDateTime(thisValue);
}
return reval;
}
/// <summary>
///
/// </summary>
/// <param name="thisValue"></param>
/// <param name="errorValue"></param>
/// <returns></returns>
public static DateTime ObjToDate(this object thisValue, DateTime errorValue)
{
DateTime reval = DateTime.MinValue;
if (thisValue != null && thisValue != DBNull.Value && DateTime.TryParse(thisValue.ToString(), out reval))
{
return reval;
}
return errorValue;
}
/// <summary>
///
/// </summary>
/// <param name="thisValue"></param>
/// <returns></returns>
public static bool ObjToBool(this object thisValue)
{
bool reval = false;
if (thisValue != null && thisValue != DBNull.Value && bool.TryParse(thisValue.ToString(), out reval))
{
return reval;
}
return reval;
}
public static DateTime ConvertToDateTime(this long timeStamp)
{
var dto = DateTimeOffset.FromUnixTimeSeconds(timeStamp);
return dto.ToLocalTime().DateTime;
}
public static DateTime ConvertToDateTime(this string timeStamp)
{
try
{
var dto = DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt64(timeStamp));
return dto.ToLocalTime().DateTime;
}
catch (Exception e)
{
throw new FormatException($"无效的Unix时间戳: {timeStamp}", e);
}
}
public static string DateToTimeStamp(this DateTime thisValue)
{
DateTimeOffset dto = new DateTimeOffset(thisValue);
return dto.ToUnixTimeSeconds().ToString();
}
public static decimal ToCeilingFixed2(this decimal value)
{
try
{
return Convert.ToInt32(Math.Ceiling(Math.Round(value * 100, 2))) / (decimal)100.0;
}
catch
{
return (decimal)0.0;
}
}
public static decimal ToFloorFixed2(this decimal value)
{
try
{
return Convert.ToInt32(Math.Floor(Math.Round(value * 100, 2))) / (decimal)100.0;
}
catch
{
return (decimal)0.0;
}
}
public static double ToCeilingFixed2(this double value)
{
try
{
return Convert.ToInt32(Math.Ceiling(Math.Round(value * 100, 2))) / 100.0;
}
catch
{
return 0.0;
}
}
public static double ToFloorFixed2(this double value)
{
try
{
return Convert.ToInt32(Math.Floor(Math.Round(value * 100, 2))) / 100.0;
}
catch
{
return 0.0;
}
}
public static string ConvertToPinyin(this string chinese)
{
if (string.IsNullOrWhiteSpace(chinese))
return "*";
return Pinyin.GetPinyin(chinese).Trim().Replace(" ", "").ToUpper();
}
public static int ToCeilingInt(this double value)
{
try
{
return Convert.ToInt32(Math.Ceiling(Math.Round(value, 1)));
}
catch
{
return Convert.ToInt32(value);
}
}
public static int ToCeilingInt(this decimal value)
{
try
{
return Convert.ToInt32(Math.Ceiling(Math.Round(value, 1)));
}
catch
{
return Convert.ToInt32(value);
}
}
public static string[] Remove(this string[] value, string str)
{
List<string> valueList = value.ToList();
if (!valueList.Contains(str))
return value;
valueList.Remove(str);
return valueList.ToArray();
}
public static int[] Remove(this int[] value, int str)
{
List<int> valueList = value.ToList();
if (!valueList.Contains(str))
return value;
valueList.Remove(str);
return valueList.ToArray();
}
//
//
// time:
public static long ToTimeStamp(this DateTime time)
{
return new DateTimeOffset(time).ToUnixTimeSeconds();
}
//
//
// timeStamp:
public static DateTime ToDateTime(this long timeStamp)
{
return DateTimeOffset.FromUnixTimeSeconds(timeStamp).ToLocalTime().DateTime;
}
//
//
// obj:
public static string ToJson(this object obj)
{
return JsonConvert.SerializeObject(obj);
}
//
//
// str:
public static T ToObject<T>(this string str)
{
return JsonConvert.DeserializeObject<T>(str);
}
//
//
// str:
//
// IgnoreCapitalized:
public static string ToUrlEncode(this string str, bool IgnoreCapitalized = true)
{
return WebUtility.UrlEncode(IgnoreCapitalized ? str.ToLowerInvariant() : str);
}
//
//
// source:
//
// T:
//
// T:System.ArgumentException:
public static T Clone<T>(T source)
{
return JsonConvert.DeserializeObject<T>(source.ToJson());
}
//
//
// str:
public static string ToUrlDecode(this string str)
{
return WebUtility.UrlDecode(str);
}
//
//
// str:
public static string ToBase64String(this string str)
{
return Convert.ToBase64String(Encoding.Default.GetBytes(str));
}
//
//
// str:
public static string ToParseBase64(this string str)
{
try
{
byte[] bytes = Convert.FromBase64String(str);
return Encoding.Default.GetString(bytes);
}
catch
{
return "";
}
}
//
//
// value:
public static double ToDoubleFixed2(this double value)
{
try
{
return (double)Convert.ToInt32(Math.Ceiling(Math.Round(value * 100.0, 2))) / 100.0;
}
catch
{
return 0.0;
}
}
//
//
// value:
public static decimal ToDecimalFixed2(this decimal value)
{
try
{
return (decimal)Convert.ToInt32(Math.Ceiling(Math.Round(value * 100m, 2))) / 100m;
}
catch
{
return 0m;
}
}
//
//
// value:
public static double ToDoubleFloor2(this double value)
{
try
{
return (double)Convert.ToInt32(Math.Floor(Math.Round(value * 100.0, 2))) / 100.0;
}
catch
{
return 0.0;
}
}
//
//
// str:
//
public static string ToReverse(this string str)
{
return new string(str.ToCharArray().AsEnumerable().Reverse().ToArray());
}
//
//
// value:
public static decimal ToDecimalFloor2(this decimal value)
{
try
{
return (decimal)Convert.ToInt32(Math.Floor(Math.Round(value * 100m, 2))) / 100m;
}
catch
{
return 0m;
}
}
//
//
// content:
public static string ToDecodeBase64(this string content)
{
byte[] bytes = Convert.FromBase64String(content);
return Encoding.UTF8.GetString(bytes);
}
//
//
// mobile:
public static string ToHidMobile(this string mobile)
{
if (string.IsNullOrWhiteSpace(mobile))
{
return "";
}
if (IsHandset(mobile))
{
return mobile.Substring(0, 3) + "****" + mobile.Substring(mobile.Length - 4, 4);
}
return mobile;
}
//
//
// str_handset:
public static bool IsHandset(string str_handset)
{
return Regex.IsMatch(str_handset, "^1[3456789]\\d{9}$");
}
//
//
// list:
//
// T:
public static bool IsNull<T>(this List<T> list)
{
if (list != null)
{
return !list.Any();
}
return true;
}
//
//
// source:
//
// isReplace:
//
// isToLower:
public static string ToSHA1Encrypt(this string source, bool isReplace = true, bool isToLower = false)
{
string text = BitConverter.ToString(SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(source)));
if (isReplace)
{
text = text.Replace("-", "");
}
if (isToLower)
{
text = text.ToLower();
}
return text;
}
}
}
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
namespace Common.Utils
{
public class DateTimeConverterUtf : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.Parse(reader.GetString()!);
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
var cstTime = TimeZoneInfo.ConvertTimeFromUtc(value,
TimeZoneInfo.FindSystemTimeZoneById("Asia/Shanghai"));
writer.WriteStringValue(cstTime.ToString("yyyy-MM-dd HH:mm:ss"));
}
}
}
@@ -0,0 +1,42 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Common.Utils
{
public static class DateTimeTools
{
public static DateTime GetDateTimeNow()
{
return DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc);
}
public static int GetTimeDiffDays(DateTime start, DateTime end)
{
string result = string.Empty;
TimeSpan ts1 = new TimeSpan(start.Ticks);
TimeSpan ts2 = new TimeSpan(end.Ticks);
TimeSpan ts = ts1.Subtract(ts2).Duration();
return ts.Days;
}
public class DatetimeJsonConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String)
{
if (DateTime.TryParse(reader.GetString(), out DateTime date))
return date;
}
return reader.GetDateTime();
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss"));
}
}
}
}
+28
View File
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace Common.Utils
{
public static class Md5Helper
{
public static string GetMD5(string str)
{
byte[] sor = Encoding.UTF8.GetBytes(str);
MD5 md5 = MD5.Create();
byte[] result = md5.ComputeHash(sor);
StringBuilder strbul = new StringBuilder(40);
for (int i = 0; i < result.Length; i++)
{
strbul.Append(result[i].ToString("x2"));
}
return strbul.ToString();
}
}
}
+132
View File
@@ -0,0 +1,132 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.Utils
{
public static class RandomHelper
{
//
//
// length:
public static string RandomNumbers(int length = 4)
{
if (length <= 4)
{
length = 4;
}
if (length >= 8)
{
length = 8;
}
string value = 1.ToString().PadRight(length, '0');
string value2 = 1.ToString().PadRight(length + 1, '0');
return Random.Shared.Next(Convert.ToInt32(value), Convert.ToInt32(value2)).ToString();
}
//
//
// length:
public static string RandomChars(int length = 4)
{
if (length <= 4)
{
length = 4;
}
if (length >= 8)
{
length = 8;
}
char[] array = new char[62]
{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
'y', 'z'
};
string text = "";
for (int i = 0; i < length; i++)
{
int num = Random.Shared.Next(0, 61);
text += array[num];
}
return text;
}
//
//
// length:
public static string RandomNoRepeatNumber(int length)
{
int num = 0;
string text = string.Empty;
long num2 = DateTime.Now.Ticks + num;
num++;
Random random = new Random((int)(num2 & 0xFFFFFFFFu) | (int)(num2 >> num));
for (int i = 0; i < length; i++)
{
int num3 = random.Next();
text += (char)(48 + (ushort)(num3 % 10));
}
return text;
}
//
//
// length:
public static string RandomNoRepeatChars(int length)
{
int num = 0;
string text = string.Empty;
long num2 = DateTime.Now.Ticks + num;
num++;
Random random = new Random((int)(num2 & 0xFFFFFFFFu) | (int)(num2 >> num));
for (int i = 0; i < length; i++)
{
int num3 = random.Next();
text += ((num3 % 2 != 0) ? ((char)(65 + (ushort)(num3 % 26))) : ((char)(48 + (ushort)(num3 % 10))));
}
return text;
}
//
//
// allChar:
//
// length:
public static string GetRandomCode(string allChar, int length)
{
string[] array = allChar.Split(',');
string text = "";
int num = -1;
for (int i = 0; i < length; i++)
{
int num2 = Random.Shared.Next(array.Length - 1);
while (num == num2)
{
num2 = Random.Shared.Next(array.Length - 1);
}
num = num2;
text += array[num2];
}
return text;
}
}
}
+186
View File
@@ -0,0 +1,186 @@
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using XSystem.Security.Cryptography;
namespace Common
{
public static class Tools
{
private static string GetTimeString()
{
return DateTime.Now.ToString("yyyyMMddHHmmssfff");
}
public static string GetTimeStringRandom(string str = "")
{
return GetTimeString() + str;
}
public static string LockKey(object param)
{
var source = JsonConvert.SerializeObject(param);
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
byte[] bytes = Encoding.UTF8.GetBytes(source);
string result = BitConverter.ToString(md5.ComputeHash(bytes));
return "HSCW:RedisLock:" + result.Replace("-", "").ToLower();
}
public static string GetSerialNumber()
{
return DateTime.Now.ToString("yyyyMMddHHmmssfff") + Random.Shared.Next(100000, 999999);
}
public static string ReplaceWithSpecialChar(string value, int startLen = 1, int endLen = 1, char specialChar = '*')
{
try
{
if (string.IsNullOrEmpty(value))
{
return value;
}
if (value.Length < startLen + endLen)
{
return value;
}
string startStr = value.Substring(0, startLen);
var len = value.Length - startLen - endLen;
for (int i = 0; i < len; i++)
{
startStr += specialChar;
}
startStr += value.Substring(startLen + len, endLen);
return startStr;
}
catch (Exception)
{
return value;
}
}
public static string SubStringText(string content, int length)
{
if (string.IsNullOrEmpty(content))
{
return "";
}
if (content.Length <= length)
{
return content;
}
return content.Substring(0, length);
}
public static string SubStringTextLast(string content, int length)
{
if (string.IsNullOrEmpty(content))
{
return "";
}
if (content.Length <= length)
{
return content;
}
return content.Substring(content.Length - length);
}
public static List<string> PickupImgUrl(string html)
{
if (string.IsNullOrWhiteSpace(html))
return null;
Regex regImg = new Regex(@"<img\b[^<>]*?\bsrc[\s\t\r\n]*=[\s\t\r\n]*[""']?[\s\t\r\n]*(?<imgUrl>[^\s\t\r\n""'<>]*)[^<>]*?/?[\s\t\r\n]*>", RegexOptions.IgnoreCase);
MatchCollection matches = regImg.Matches(html);
List<string> lstImg = new List<string>();
foreach (Match match in matches)
{
lstImg.Add(match.Groups["imgUrl"].Value);
}
return lstImg;
}
public static string NoHTML(string html)
{
string str = "";
for (int i = 0; i < html.Length; i++)
{
if (Convert.ToInt32(Convert.ToChar(html.Substring(i, 1))) > Convert.ToInt32(Convert.ToChar(128)))
{
str += html.Substring(i, 1).ToString();
}
}
return str;
}
public static string RegexSubstring(string content, string startStr, string endStr)
{
Regex rex = new Regex("(?<=(" + startStr + "))[.\\s\\S]*?(?=(" + endStr + "))", RegexOptions.Multiline | RegexOptions.Singleline);
return rex.Match(content).Value;
}
public static bool CheckIsUrl(string strValue)
{
return CheckIsFormat(@"^(http|https|ftp)\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\-\._\?\,\'/\\\+&$%\$#\=~])*$", strValue);
}
private static bool CheckIsFormat(string strRegex, string strValue)
{
if (strValue != null && strValue.Trim() != "")
{
Regex re = new Regex(strRegex);
if (re.IsMatch(strValue))
{
return true;
}
return false;
}
return false;
}
public static bool IsIdCard(string idCardNumber)
{
if (string.IsNullOrEmpty(idCardNumber))
return false;
idCardNumber = idCardNumber.ToUpper();
if (!Regex.IsMatch(idCardNumber, @"^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$"))
return false;
string birthDateString = idCardNumber.Substring(6, 8);
char genderCheckDigitChar = idCardNumber[16];
char checkDigit = idCardNumber[17];
if (!DateTime.TryParseExact(birthDateString, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime birthDate))
{
return false;
}
DateTime today = DateTime.Now.AddYears(18);
if (birthDate > today)
{
return false;
}
if (today.Year - birthDate.Year > 80)
{
return false;
}
if (!char.IsDigit(genderCheckDigitChar))
{
return false;
}
return true;
}
}
}
@@ -0,0 +1,84 @@
using System.Text.RegularExpressions;
namespace Common.Utils
{
public static class ValidationHelper
{
private static readonly Regex MobileRegex = new(@"^1[3-9]\d{9}$", RegexOptions.Compiled);
private static readonly Regex IdCardRegex = new(@"^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$", RegexOptions.Compiled);
private static readonly Regex UserNameRegex = new(@"^[\u4e00-\u9fa5a-zA-Z0-9_]{2,20}$", RegexOptions.Compiled);
public static bool IsValidMobile(string mobile)
{
if (string.IsNullOrWhiteSpace(mobile))
return false;
return MobileRegex.IsMatch(mobile);
}
public static bool IsValidIdCard(string idCard)
{
if (string.IsNullOrWhiteSpace(idCard) || idCard.Length != 18)
return false;
if (!IdCardRegex.IsMatch(idCard))
return false;
return ValidateIdCardCheckSum(idCard);
}
public static bool IsValidUserName(string userName)
{
if (string.IsNullOrWhiteSpace(userName))
return false;
return UserNameRegex.IsMatch(userName);
}
public static bool IsPositiveAmount(long amount)
{
return amount > 0;
}
public static bool IsPositiveAmount(int amount)
{
return amount > 0;
}
public static bool IsInRange(int count, int min, int max)
{
return count >= min && count <= max;
}
public static bool IsValidLength(string value, int minLength, int maxLength)
{
if (string.IsNullOrWhiteSpace(value))
return false;
return value.Length >= minLength && value.Length <= maxLength;
}
public static bool IsValidPassword(string password)
{
if (string.IsNullOrWhiteSpace(password) || password.Length < 6 || password.Length > 20)
return false;
return password.Any(char.IsLetter) && password.Any(char.IsDigit);
}
private static bool ValidateIdCardCheckSum(string idCard)
{
int[] weight = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
char[] checkCodes = { '1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2' };
int sum = 0;
for (int i = 0; i < 17; i++)
{
sum += (idCard[i] - '0') * weight[i];
}
char expectedCheckChar = checkCodes[sum % 11];
char actualCheckChar = char.ToUpper(idCard[17]);
return expectedCheckChar == actualCheckChar;
}
}
}
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.WX.Response
{
public class AccessTokenResponse : WXBaseResponse
{
/// <summary>
/// access_token
/// </summary>
public string access_token { get; set; }
public int expires_in { get; set; }
public string refresh_token { get; set; }
public string openid { get; set; }
public string scope { get; set; }
/// <summary>
/// is_snapshotuser
/// </summary>
public int is_snapshotuser { get; set; }
public string unionid { get; set; }
}
}
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.WX.Response
{
public class GZHUserInfoResponse : WXBaseResponse
{
public string openid { get; set; }
public string nickname { get; set; }
public int sex { get; set; }
public string province { get; set; }
public string city { get; set; }
public string country { get; set; }
public string headimgurl { get; set; }
public string[] privilege { get; set; }
public string unionid { get; set; }
}
}
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.WX.Response
{
public class TicketResponse: WXBaseResponse
{
/// <summary>
/// ticket
/// </summary>
public string ticket { get; set; }
public int expires_in { get; set; }
}
}
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Common.WX.Response
{
public class WXBaseResponse
{
public int errcode { get; set; } = 0;
public string errmsg { get; set; }
}
}
+91
View File
@@ -0,0 +1,91 @@
using Common.Redis;
using Common.Request;
using Common.Utils;
using Common.WX.Response;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using RedisHelper;
namespace Common.WX
{
public class WXBaseHelper
{
private readonly IRequestApi _requestApi;
private readonly ICache _cache;
private readonly IConfiguration _configuration;
private ILogger<WXBaseHelper> _logger;
private readonly string appid;
private readonly string secret;
private readonly string wx_url;
public WXBaseHelper(IRequestApi requestApi, ICache cache, IConfiguration configuration, ILogger<WXBaseHelper> logger)
{
_requestApi = requestApi;
_cache = cache;
_configuration = configuration;
_logger = logger;
appid = _configuration["WXConfig:AppId"];
secret = _configuration["WXConfig:Secret"];
wx_url = _configuration["WXConfig:Url"];
}
public async Task<(bool isok,string msg,string accessToken)> GetAccessToken()
{
var accessToken = _cache.Get<string>(RedisKey.WeiXin_AccessToken);
if (accessToken != null && !string.IsNullOrWhiteSpace(accessToken))
{
return (true,"", accessToken);
}
var param = new Dictionary<string, object>
{
{ "appid", appid },
{ "secret", secret },
{ "grant_type", "client_credential" }
};
var result = await _requestApi.GetAsync(wx_url, "/cgi-bin/token", param);
var res = result.ToObject<AccessTokenResponse>();
if (res.errcode == 0)
{
_cache.Insert(RedisKey.WeiXin_AccessToken, res.access_token, 3600);
return (true, "", res.access_token);
}
else
return (false, res.errcode+"-"+ res.errmsg,null);
}
public async Task<(bool isok, string msg, AccessTokenResponse response)> GetAccessTokenByCode(string js_code)
{
var codeRedis = _cache.Get<AccessTokenResponse>(string.Format(RedisKey.WeiXinUser_AccessToken, js_code));
if (codeRedis != null)
{
return (true, "", codeRedis);
}
var param = new Dictionary<string, object>
{
{ "appid", appid },
{ "secret", secret },
{ "js_code", js_code },
{ "grant_type", "authorization_code" }
};
var result = await _requestApi.GetAsync(wx_url, "/sns/jscode2session", param);
var res = result.ToObject<AccessTokenResponse>();
if (res.errcode == 0)
{
_cache.Insert(string.Format(RedisKey.WeiXinUser_AccessToken, js_code), res, 6000);
return (true, res.errmsg, res);
}
else
return (false, res.errmsg, null);
}
}
}
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Autofac" Version="8.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\RedisService\RedisService.csproj" />
<ProjectReference Include="..\Common\Common.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,39 @@
using Common;
using CommonInfrastructure.Response;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace CommonInfrastructure
{
public class BaseController : Controller
{
public LoginUser loginUser { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var context = filterContext.HttpContext;
loginUser = new LoginUser();
var request = context.Request;
try
{
loginUser.UserId = context.User.Claims.FirstOrDefault(e => e.Type == "uid")?.Value;
loginUser.UserName = context.User.Claims.FirstOrDefault(e => e.Type == "uname")?.Value;
loginUser.DisplayName = context.User.Claims.FirstOrDefault(e => e.Type == "dname")?.Value;
loginUser.Icon = context.User.Claims.FirstOrDefault(e => e.Type == "avatar")?.Value;
loginUser.Number = context.User.Claims.FirstOrDefault(e => e.Type == "number")?.Value;
loginUser.Ip = context.Request.Headers["X-Forwarded-For"].FirstOrDefault()?.Split(',').FirstOrDefault()?.Trim();
if (string.IsNullOrEmpty(loginUser.Ip))
{
loginUser.Ip = context.Connection.RemoteIpAddress?.ToString() ?? "";
}
}
catch (Exception ex)
{
filterContext.Result = new JsonResult(new BaseCommonResponse() { Code = CommonResponseCodeEnum.ParamError, Info = "请求参数异常"+ex.Message });
}
}
}
}
@@ -0,0 +1,58 @@
using Common;
using Microsoft.AspNetCore.Mvc;
namespace CommonInfrastructure
{
public static class ControllerExtension
{
public static BaseResponse<T> ToFailResult<T>(
this Controller controller,
string info) => new BaseResponse<T>() { Info = info, Code = CommonResponseCodeEnum.ResultFail };
public static ListResponse<T> ToListFailResult<T>(
this Controller controller,
string info) => new ListResponse<T>()
{ Info = info, Code = CommonResponseCodeEnum.ResultFail };
public static PageResponse<T> ToPageFailResult<T>(
this Controller controller,
string info) => new PageResponse<T>()
{
Code = CommonResponseCodeEnum.ResultFail,
Info = info
};
public static BaseResponse<T> ToResult<T>(
this Controller controller,
T obj, string info = null) => new BaseResponse<T>() { Data = obj, Info = info, Code = CommonResponseCodeEnum.Success };
public static ListResponse<T> ToListResult<T>(
this Controller controller,
IEnumerable<T> list, string info = null) => new ListResponse<T>()
{
Data = list,
Code = CommonResponseCodeEnum.Success,
Info = info
};
public static PageResponse<T> ToPageResult<T>(
this Controller controller,
IEnumerable<T> list, int count = 0, string info = null) => new PageResponse<T>()
{
Code = CommonResponseCodeEnum.Success,
Info = info,
Count = count,
Data = list
};
public static BaseResponse<T> ToNoLoginResult<T>(
this Controller controller) => new BaseResponse<T>() { Info = "未登录", Code = CommonResponseCodeEnum.NoLogin };
public static BaseResponse<T> ToTokenInvalidResult<T>(
this Controller controller) => new BaseResponse<T>() { Info = "token失效", Code = CommonResponseCodeEnum.TokenInvalid };
public static BaseResponse<T> ToParamErrorResult<T>(
this Controller controller) => new BaseResponse<T>() { Info = "请求参数错误", Code = CommonResponseCodeEnum.ParamError };
}
}
@@ -0,0 +1,27 @@
using Autofac;
using MongoRepository;
namespace CommonInfrastructure
{
public class AutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterGeneric(typeof(MongoBaseRepository<>))
.As(typeof(IMongoBaseRepository<>))
.InstancePerLifetimeScope();
builder.RegisterType<RedisService.RedisService>()
.As<RedisService.IRedisService>()
.InstancePerLifetimeScope();
}
public static void RegisterServices(ContainerBuilder builder, Type serviceAssemblyType)
{
builder.RegisterAssemblyTypes(serviceAssemblyType.Assembly)
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
}
}
}
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CommonInfrastructure.Response
{
public class LoginUser
{
public string? UserId{ get; set; }
public string? UserName { get; set; }
public string Number { get; set; }
public string? DisplayName { get; set; }
public string? Icon { get; set; }
public string Source { get; set; }
public string Ip { get; set; }
/// <summary>
/// token
/// </summary>
public string Token { get; set; }
}
}
+27
View File
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace JWTHelper
{
public class JWTConfig
{
public string TokenKey { get; set; }
public bool ValidateIssuer { get; set; }
public string Issuer { get; set; }
public bool ValidateAudience { get; set; }
public string Audience { get; set; }
public bool ValidateIssuerSigningKey { get; set; }
public string SecretKey { get; set; }
public bool ValidateLifetime { get; set; }
}
}
+180
View File
@@ -0,0 +1,180 @@
using System;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JWTHelper
{
public static class JWTExtension
{
public static IServiceCollection AddCustomJwt(this IServiceCollection services,
IConfiguration configuration)
{
services.AddScoped<JwtTools>();
services.Configure<JWTConfig>(options => { configuration.Bind("JwtConfig", options); });
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
var jwtSettings = new JWTConfig();
configuration.Bind("JwtConfig", jwtSettings);
options.Events = new JwtBearerEvents()
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/hubs/") || path.StartsWithSegments("/fishinghub") || path.StartsWithSegments("/fishingroomhub")))
{
context.Token = accessToken;
}
else
{
try
{
context.Token = context.HttpContext.Request.Headers[jwtSettings.TokenKey].ToString();
}
catch (Exception e)
{
context.Token = "";
}
}
if (!string.IsNullOrEmpty(context.Token))
{
// var key = JwtTools.GetTokenContent(context.Token, "key");
var userId =JwtTools.GetTokenReverseContent(context.Token, "uid");
if (string.IsNullOrEmpty(userId))
{
context.Token = "";
}
//if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(userId))
//{
// context.Token = "";
//}
//else
//{
// if (Md5Helper.Encode(userId)[..8] != key)
// {
// context.Token = "";
// }
//}
}
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
string s = context.Exception.Message;
return Task.CompletedTask;
},
OnChallenge = context =>
{
context.HandleResponse();
var payload = JsonConvert.SerializeObject(new { code = 401, msg = "fail",info= "很抱歉,您无权访问该接口" });
context.Response.ContentType = "application/json";
context.Response.StatusCode = StatusCodes.Status200OK;
context.Response.WriteAsync(payload);
return Task.FromResult(0);
}
};
var tokenValidationParameters = new TokenValidationParameters();
tokenValidationParameters.ValidateAudience = jwtSettings.ValidateAudience;
tokenValidationParameters.ValidAudience = jwtSettings.Audience;
tokenValidationParameters.ValidateIssuer = jwtSettings.ValidateIssuer;
tokenValidationParameters.ValidIssuer = jwtSettings.Issuer;
tokenValidationParameters.ValidateIssuerSigningKey = jwtSettings.ValidateIssuerSigningKey;
tokenValidationParameters.IssuerSigningKey =
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey));
tokenValidationParameters.ValidateLifetime = jwtSettings.ValidateLifetime;
options.TokenValidationParameters = tokenValidationParameters;
});
return services;
}
public static IServiceCollection AddJwt(this IServiceCollection services,
IConfiguration configuration)
{
services.AddScoped<JwtTools>();
services.Configure<JWTConfig>(options => { configuration.Bind("JwtConfig", options); });
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
var jwtSettings = new JWTConfig();
configuration.Bind("JwtConfig", jwtSettings);
options.Events = new JwtBearerEvents()
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && (path.StartsWithSegments("/hubs/") || path.StartsWithSegments("/fishinghub") || path.StartsWithSegments("/fishingroomhub")))
{
context.Token = accessToken;
}
else
{
context.Token = context.HttpContext.Request.Headers[jwtSettings.TokenKey].ToString();
}
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
string s = context.Exception.Message;
return Task.CompletedTask;
},
OnChallenge = context =>
{
context.HandleResponse();
var payload = JsonConvert.SerializeObject(new { code = 401, msg = "fail",info= "很抱歉,您无权访问该接口" });
context.Response.ContentType = "application/json";
context.Response.StatusCode = StatusCodes.Status200OK;
context.Response.WriteAsync(payload);
return Task.FromResult(0);
}
};
var tokenValidationParameters = new TokenValidationParameters();
tokenValidationParameters.ValidateAudience = jwtSettings.ValidateAudience;
tokenValidationParameters.ValidAudience = jwtSettings.Audience;
tokenValidationParameters.ValidateIssuer = jwtSettings.ValidateIssuer;
tokenValidationParameters.ValidIssuer = jwtSettings.Issuer;
tokenValidationParameters.ValidateIssuerSigningKey = jwtSettings.ValidateIssuerSigningKey;
tokenValidationParameters.IssuerSigningKey =
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey));
tokenValidationParameters.ValidateLifetime = jwtSettings.ValidateLifetime;
options.TokenValidationParameters = tokenValidationParameters;
});
return services;
}
}
}
+15
View File
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.15" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.7" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>
+125
View File
@@ -0,0 +1,125 @@

using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
namespace JWTHelper
{
public class JwtTools
{
private JWTConfig config;
public JwtTools(IOptions<JWTConfig> opts)
{
config = opts.Value;
}
public bool ValidateToken(string token)
{
try
{
SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config.SecretKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var tokenValidationParameters = new TokenValidationParameters();
tokenValidationParameters.ValidateAudience = config.ValidateAudience;
tokenValidationParameters.ValidAudience = config.Audience;
tokenValidationParameters.ValidateIssuer = config.ValidateIssuer;
tokenValidationParameters.ValidIssuer = config.Issuer;
tokenValidationParameters.ValidateIssuerSigningKey = config.ValidateIssuerSigningKey;
tokenValidationParameters.IssuerSigningKey =
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config.SecretKey));
tokenValidationParameters.ValidateLifetime = config.ValidateLifetime;
SecurityToken securityToken = null;
var identiy = new JwtSecurityTokenHandler().ValidateToken(token, tokenValidationParameters, out securityToken);
return identiy.Claims.Count(n => n.Type == "uid" || n.Type == "uname") >= 2;
}
catch
{
return false;
}
}
public string CreateToken(Claim[] claims)
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config.SecretKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: config.Issuer,
// audience: jwtconfig.Audience,
claims: claims,
expires: DateTime.Now.AddMinutes(60),
signingCredentials: creds
);
return new JwtSecurityTokenHandler().WriteToken(token);
}
public static string GetTokenContent(string token, string key)
{
if (!string.IsNullOrEmpty(token))
{
try
{
var securityTokenHandler = new JwtSecurityTokenHandler();
var claims = securityTokenHandler.ReadJwtToken(token).Payload;
return claims.Claims.FirstOrDefault(n => n.Type == key)?.Value ?? "";
}
catch
{
return "";
}
}
else
{
return "";
}
}
public static string GetTokenReverseContent(string token, string key)
{
if (!string.IsNullOrEmpty(token))
{
try
{
var securityTokenHandler = new JwtSecurityTokenHandler();
var claims = securityTokenHandler.ReadJwtToken(token).Payload;
return new string((claims.Claims.FirstOrDefault(n => n.Type == key)?.Value ?? "").ToCharArray()
.AsEnumerable().Reverse().ToArray());
}
catch
{
return "";
}
}
else
{
return "";
}
}
public static IEnumerable<Claim> GetTokenClaim(string token)
{
var securityTokenHandler = new JwtSecurityTokenHandler();
var claims = securityTokenHandler.ReadJwtToken(token).Payload;
return claims.Claims;
}
}
}
+22
View File
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace JWTHelper
{
sealed class Md5Helper
{
public static string Encode(string ConvertString)
{
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
string t2 = BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(ConvertString)), 4, 8);
t2 = t2.Replace("-", "");
return t2;
}
}
}
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MongoRepository.Attributes
{
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class BsonCollectionAttribute : Attribute
{
public string CollectionName { get; }
public string DataBaseName { get; }
public BsonCollectionAttribute(string collectionName, string dataBaseName)
{
CollectionName = collectionName;
DataBaseName = dataBaseName;
}
}
}
@@ -0,0 +1,45 @@
using System;
namespace MongoRepository.Config
{
public interface IMongoDbSettings
{
/// <summary>
/// ServerUrl
/// </summary>
public string ServerUrl { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Database { get; set; }
}
public class MongoDbSettings : IMongoDbSettings
{
/// <summary>
/// ServerUrl
/// </summary>
public string ServerUrl { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Database { get; set; } = "data_0524";
}
public static class ServiceDatabases
{
public const string User = "data_0524_user";
public const string Admin = "data_0524_admin";
public const string Fish = "data_0524_fish";
public const string Chess = "data_0524_chess";
public const string Customer = "data_0524_customer";
}
}
@@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
namespace MongoRepository
{
public interface IMongoBaseRepository<TDocument> where TDocument : MongoBaseEntity
{
IQueryable<TDocument> AsQueryable();
IEnumerable<TDocument> FilterBy(Expression<Func<TDocument, bool>> filterExpression);
Task<List<TDocument>> FilterByAsync(
Expression<Func<TDocument, bool>> filterExpression);
Task<List<TDocument>> FilterByAsync(
FilterDefinition<TDocument> filterExpression);
IEnumerable<TDocument> FilterBy(FilterDefinition<TDocument> filterExpression);
Task<long> CountAsync(
Expression<Func<TDocument, bool>> filterExpression);
long Count(
Expression<Func<TDocument, bool>> filterExpression);
Task<long> CountAsync(
FilterDefinition<TDocument> filterExpression);
long Count(FilterDefinition<TDocument> filterExpression);
IEnumerable<TProjected> FilterBy<TProjected>(
Expression<Func<TDocument, bool>> filterExpression,
Expression<Func<TDocument, TProjected>> projectionExpression);
TDocument FindOne(Expression<Func<TDocument, bool>> filterExpression);
Task<TDocument> FindOneAsync(Expression<Func<TDocument, bool>> filterExpression);
TDocument FindOne(FilterDefinition<TDocument> filterExpression);
Task<TDocument> FindOneAsync(FilterDefinition<TDocument> filterExpression);
TDocument FindOne(FilterDefinition<TDocument> filterExpression, Expression<Func<TDocument, object>> orderExpression);
Task<TDocument> FindOneAsync(FilterDefinition<TDocument> filterExpression, Expression<Func<TDocument, object>> orderExpression);
TDocument FindOne(Expression<Func<TDocument, bool>> filterExpression, Expression<Func<TDocument, object>> orderExpression);
Task<TDocument> FindOneAsync(Expression<Func<TDocument, bool>> filterExpression, Expression<Func<TDocument, object>> orderExpression);
TDocument FindById(string id);
Task<TDocument> FindByIdAsync(string id);
(long count, List<TDocument> list) QueryPage(Expression<Func<TDocument, bool>> filterExpression, Expression<Func<TDocument, object>> orderExpression, bool desc, int pageSize, int pageIndex);
Task<(long count, List<TDocument> list)> QueryPageAsync(Expression<Func<TDocument, bool>> filterExpression, int pageSize, int pageIndex);
Task<(long count, List<TDocument> list)> QueryPageAsync(Expression<Func<TDocument, bool>> filterExpression, Expression<Func<TDocument, object>> orderExpression, bool desc, int pageSize, int pageIndex);
Task<(long count, List<TDocument> list)> QueryPageAsync(Expression<Func<TDocument, bool>> filterExpression, Expression<Func<TDocument, object>> orderExpression, bool desc, int pageSize, int pageIndex, int skipIndex);
Task<(long count, List<TDocument> list)> QueryPageAsync(FilterDefinition<TDocument> filterExpression, SortDefinition<TDocument> orderExpression, bool desc, int pageSize, int pageIndex);
Task<(long count, List<TDocument> list)> QueryPageAsync(FilterDefinition<TDocument> filterExpression, SortDefinition<TDocument> orderExpression, bool desc, int pageSize, int pageIndex, int skipIndex);
(bool ok, string message) InsertOne(TDocument document);
Task<(bool ok, string message)> InsertOneAsync(TDocument document);
(bool ok, string message) InsertMany(ICollection<TDocument> documents);
Task<(bool ok, string message)> InsertManyAsync(ICollection<TDocument> documents);
(bool ok, string message) UpdateById(string id, UpdateDefinition<TDocument> updateDefinition);
Task<(bool ok, string message)> UpdateByIdAsync(string id, UpdateDefinition<TDocument> updateDefinition);
(bool ok, string message) UpdateMany(Expression<Func<TDocument, bool>> filterExpression, UpdateDefinition<TDocument> updateDefinition);
Task<(bool ok, string message)> UpdateManyAsync(Expression<Func<TDocument, bool>> filterExpression, UpdateDefinition<TDocument> updateDefinition);
(bool ok, string message) UpdateMany(FilterDefinition<TDocument> filterExpression, UpdateDefinition<TDocument> updateDefinition);
Task<(bool ok, string message)> UpdateManyAsync(FilterDefinition<TDocument> filterExpression, UpdateDefinition<TDocument> updateDefinition);
(bool ok, string message) ReplaceOne(TDocument document);
Task<(bool ok, string message)> ReplaceOneAsync(TDocument document);
(bool ok, string message) DeleteOne(Expression<Func<TDocument, bool>> filterExpression);
Task<(bool ok, string message)> DeleteOneAsync(Expression<Func<TDocument, bool>> filterExpression);
(bool ok, string message) DeleteById(string id);
Task<(bool ok, string message)> DeleteByIdAsync(string id);
(bool ok, string message) DeleteMany(Expression<Func<TDocument, bool>> filterExpression);
Task<(bool ok, string message)> DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression);
Task<List<BsonDocument>> Aggregate(FilterDefinition<TDocument> filterExpression, ProjectionDefinition<TDocument, BsonDocument> group);
Task<List<TDocument>> RandomData(Expression<Func<TDocument, bool>> filterExpression, int count);
}
}
@@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Common.Utils;
using MongoDB.Bson.Serialization.Attributes;
namespace MongoRepository
{
public class MongoBaseEntity
{
[BsonElement("_id")]
public string Id { get; set; } = Guid.NewGuid().ToString("N");
[BsonElement("isdelete")]
public bool IsDelete { get; set; } = false;
[Column(TypeName = "jsonb")]
public OptionUserInfo? CreateInfo { get; set; }
}
[BsonIgnoreExtraElements]
public class OptionUserInfo
{
public OptionUserInfo(string userId, string userName)
{
UserId = userId;
UserName = userName;
OptionTime = DateTime.Now.ToTimeStamp();
}
public OptionUserInfo()
{
}
public string UserId { get; set; }
public string UserName { get; set; }
public long OptionTime { get; set; }
}
}
@@ -0,0 +1,702 @@
using Common;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using MongoRepository.Attributes;
using MongoRepository.Config;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace MongoRepository
{
public class MongoBaseRepository<TDocument> : IMongoBaseRepository<TDocument> where TDocument : MongoBaseEntity
{
public readonly IMongoCollection<TDocument> _collection;
private readonly ILogger<MongoBaseRepository<TDocument>> _logger;
private static readonly HashSet<string> _indexedCollections = new();
public MongoBaseRepository(IMongoDbSettings settings, ILogger<MongoBaseRepository<TDocument>> logger)
{
_logger = logger;
var mongoUrl = new MongoUrlBuilder($"{settings.ServerUrl.TrimEnd('/')}")
{
Username = settings.Username,
Password = settings.Password,
DatabaseName = GetDataBaseName(typeof(TDocument))
}.ToMongoUrl();
var client = new MongoClient(mongoUrl);
EnsureDatabaseAndUser(client, settings, GetDataBaseName(typeof(TDocument)));
var database = client.GetDatabase(GetDataBaseName(typeof(TDocument)));
_collection = database.GetCollection<TDocument>(GetCollectionName(typeof(TDocument)));
EnsureIndexes(database);
}
private void EnsureIndexes(IMongoDatabase database)
{
var collectionName = GetCollectionName(typeof(TDocument));
var key = $"{database.DatabaseNamespace.DatabaseName}:{collectionName}";
if (!_indexedCollections.Add(key))
return;
try
{
if (collectionName == "user")
{
var bsonCollection = database.GetCollection<BsonDocument>(collectionName);
var mobileIndex = new CreateIndexModel<BsonDocument>(
Builders<BsonDocument>.IndexKeys.Ascending("BaseInfo.Mobile"),
new CreateIndexOptions { Unique = true, Sparse = true, Name = "idx_baseinfo_mobile_unique" }
);
var idCardIndex = new CreateIndexModel<BsonDocument>(
Builders<BsonDocument>.IndexKeys.Ascending("RealName.IDCard"),
new CreateIndexOptions { Unique = true, Sparse = true, Name = "idx_realname_idcard_unique" }
);
bsonCollection.Indexes.CreateMany(new[] { mobileIndex, idCardIndex });
}
}
catch (Exception ex)
{
_logger.LogWarning(ex, "MongoDB 索引创建异常(可能已存在): collection={Collection}", collectionName);
}
}
private void EnsureDatabaseAndUser(MongoClient client, IMongoDbSettings settings, string databaseName)
{
try
{
var databaseNames = client.ListDatabaseNames().ToList();
if (!databaseNames.Contains(databaseName))
{
var tempDatabase = client.GetDatabase(databaseName);
var tempCollection = tempDatabase.GetCollection<BsonDocument>("temp_collection");
var tempDoc = new BsonDocument("temp", "init");
tempCollection.InsertOne(tempDoc);
tempCollection.DeleteOne(new BsonDocument("temp", "init"));
}
}
catch (Exception ex) when (ex.GetType().Name == "MongoAuthenticationException")
{
}
catch (Exception ex)
{
_logger.LogError(ex, "检查MongoDB数据库失败 database={DatabaseName}", databaseName);
}
}
private protected string GetCollectionName(Type documentType)
{
var collectionName = ((BsonCollectionAttribute)documentType.GetCustomAttributes(
typeof(BsonCollectionAttribute),
true)
.FirstOrDefault())?.CollectionName;
var formatStr = Tools.RegexSubstring(collectionName, "{", "}");
if (!string.IsNullOrEmpty(collectionName) && !string.IsNullOrEmpty(formatStr))
{
collectionName = collectionName.Replace("{" + formatStr + "}", DateTime.Now.ToString(formatStr));
}
return collectionName;
}
private protected string GetDataBaseName(Type documentType)
{
return ((BsonCollectionAttribute)documentType.GetCustomAttributes(
typeof(BsonCollectionAttribute),
true)
.FirstOrDefault())?.DataBaseName;
}
public virtual IQueryable<TDocument> AsQueryable()
{
return _collection.AsQueryable();
}
public virtual IMongoCollection<TDocument> GetCollection()
{
return _collection;
}
public virtual IEnumerable<TDocument> FilterBy(
Expression<Func<TDocument, bool>> filterExpression)
{
return _collection.Find(filterExpression).ToEnumerable();
}
public virtual async Task<List<TDocument>> FilterByAsync(
Expression<Func<TDocument, bool>> filterExpression)
{
return await _collection.Find(filterExpression).ToListAsync();
}
public virtual async Task<List<TDocument>> FilterByAsync(
FilterDefinition<TDocument> filterExpression)
{
return await _collection.Find(filterExpression).ToListAsync();
}
public virtual IEnumerable<TDocument> FilterBy(
FilterDefinition<TDocument> filterExpression)
{
return _collection.Find(filterExpression).ToEnumerable();
}
public virtual IEnumerable<TProjected> FilterBy<TProjected>(
Expression<Func<TDocument, bool>> filterExpression,
Expression<Func<TDocument, TProjected>> projectionExpression)
{
return _collection.Find(filterExpression).Project(projectionExpression).ToEnumerable();
}
public virtual TDocument FindOne(FilterDefinition<TDocument> filterExpression)
{
return _collection.Find(filterExpression).FirstOrDefault();
}
public virtual Task<TDocument> FindOneAsync(FilterDefinition<TDocument> filterExpression)
{
return Task.Run(() => _collection.Find(filterExpression).FirstOrDefaultAsync());
}
public virtual TDocument FindOne(Expression<Func<TDocument, bool>> filterExpression)
{
return _collection.Find(filterExpression).FirstOrDefault();
}
public virtual Task<TDocument> FindOneAsync(Expression<Func<TDocument, bool>> filterExpression)
{
return Task.Run(() => _collection.Find(filterExpression).FirstOrDefaultAsync());
}
public virtual TDocument FindOne(FilterDefinition<TDocument> filterExpression, Expression<Func<TDocument, object>> orderExpression)
{
return _collection.Find(filterExpression).SortByDescending(orderExpression).FirstOrDefault();
}
public virtual Task<TDocument> FindOneAsync(FilterDefinition<TDocument> filterExpression, Expression<Func<TDocument, object>> orderExpression)
{
return Task.Run(() => _collection.Find(filterExpression).SortByDescending(orderExpression).FirstOrDefaultAsync());
}
public virtual TDocument FindOne(Expression<Func<TDocument, bool>> filterExpression, Expression<Func<TDocument, object>> orderExpression)
{
return _collection.Find(filterExpression).SortByDescending(orderExpression).FirstOrDefault();
}
public virtual Task<TDocument> FindOneAsync(Expression<Func<TDocument, bool>> filterExpression, Expression<Func<TDocument, object>> orderExpression)
{
return Task.Run(() => _collection.Find(filterExpression).SortByDescending(orderExpression).FirstOrDefaultAsync());
}
public virtual TDocument FindById(string id)
{
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, id);
return _collection.Find(filter).SingleOrDefault();
}
public virtual Task<TDocument> FindByIdAsync(string id)
{
return Task.Run(() =>
{
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, id);
return _collection.Find(filter).SingleOrDefaultAsync();
});
}
public virtual (long count, List<TDocument> list) QueryPage(Expression<Func<TDocument, bool>> filterExpression, Expression<Func<TDocument, object>> orderExpression, bool desc, int pageSize, int pageIndex)
{
pageSize = pageSize <= 0 ? 10 : pageSize;
pageIndex = pageIndex <= 0 ? 1 : pageIndex;
var count = _collection.Find(filterExpression).CountDocuments();
if (count == 0)
{
return (0, new List<TDocument>());
}
var query = _collection.Find(filterExpression);
if (desc)
{
query = query.SortByDescending(orderExpression);
}
else
{
query = query.SortBy(orderExpression);
}
var list = query.Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToList();
return (count, list);
}
public virtual async Task<(long count, List<TDocument> list)> QueryPageAsync(Expression<Func<TDocument, bool>> filterExpression, int pageSize, int pageIndex)
{
try
{
pageSize = pageSize <= 0 ? 10 : pageSize;
pageIndex = pageIndex <= 0 ? 1 : pageIndex;
var count = await _collection.Find(filterExpression).CountDocumentsAsync();
if (count == 0)
{
return (0, new List<TDocument>());
}
var query = _collection.Find(filterExpression);
var list = await query.Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync();
return (count, list);
}
catch (Exception ex)
{
throw ex;
}
}
public virtual async Task<(long count, List<TDocument> list)> QueryPageAsync(Expression<Func<TDocument, bool>> filterExpression,
Expression<Func<TDocument, object>> orderExpression, bool desc, int pageSize, int pageIndex)
{
try
{
pageSize = pageSize <= 0 ? 10 : pageSize;
pageIndex = pageIndex <= 0 ? 1 : pageIndex;
var count = await _collection.Find(filterExpression).CountDocumentsAsync();
if (count == 0)
{
return (0, new List<TDocument>());
}
var query = _collection.Find(filterExpression);
if (desc)
{
query = query.SortByDescending(orderExpression);
}
else
{
query = query.SortBy(orderExpression);
}
var list = await query.Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync();
return (count, list);
}
catch (Exception ex)
{
throw ex;
}
}
public virtual async Task<(long count, List<TDocument> list)> QueryPageAsync(Expression<Func<TDocument, bool>> filterExpression,
Expression<Func<TDocument, object>> orderExpression, bool desc, int pageSize, int pageIndex, int skipCount)
{
try
{
pageSize = pageSize <= 0 ? 10 : pageSize;
pageIndex = pageIndex <= 0 ? 1 : pageIndex;
var count = await _collection.Find(filterExpression).CountDocumentsAsync();
if (count == 0)
{
return (0, new List<TDocument>());
}
var query = _collection.Find(filterExpression);
if (desc)
{
query = query.SortByDescending(orderExpression);
}
else
{
query = query.SortBy(orderExpression);
}
var list = await query.Skip((pageIndex - 1) * pageSize + skipCount).Limit(pageSize).ToListAsync();
return (count - skipCount, list);
}
catch (Exception ex)
{
throw ex;
}
}
public virtual async Task<(long count, List<TDocument> list)> QueryPageTwoOrderAsync(Expression<Func<TDocument, bool>> filterExpression,
Expression<Func<TDocument, object>> orderExpression, Expression<Func<TDocument, object>> thenorderExpression, bool desc, int pageSize, int pageIndex)
{
try
{
pageSize = pageSize <= 0 ? 10 : pageSize;
pageIndex = pageIndex <= 0 ? 1 : pageIndex;
var count = await _collection.Find(filterExpression).CountDocumentsAsync();
if (count == 0)
{
return (0, new List<TDocument>());
}
var query = _collection.Find(filterExpression);
if (desc)
{
query = query.SortByDescending(orderExpression).ThenByDescending(thenorderExpression);
}
else
{
query = query.SortBy(orderExpression).ThenByDescending(thenorderExpression);
}
var list = await query.Skip((pageIndex - 1) * pageSize ).Limit(pageSize).ToListAsync();
return (count , list);
}
catch (Exception ex)
{
throw ex;
}
}
public virtual async Task<(long count, List<TDocument> list)> QueryPageAsync(
FilterDefinition<TDocument> filterExpression, SortDefinition<TDocument> orderExpression, bool desc,
int pageSize, int pageIndex)
{
try
{
pageSize = pageSize <= 0 ? 10 : pageSize;
pageIndex = pageIndex <= 0 ? 1 : pageIndex;
var count = await _collection.Find(filterExpression).CountDocumentsAsync();
if (count == 0)
{
return (0, new List<TDocument>());
}
var query = _collection.Find(filterExpression).Sort(orderExpression);
var list = await query.Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync();
return (count, list);
}
catch (Exception ex)
{
throw ex;
}
}
public virtual async Task<(long count, List<TDocument> list)> QueryPageAsync(
FilterDefinition<TDocument> filterExpression, SortDefinition<TDocument> orderExpression, bool desc,
int pageSize, int pageIndex, int skipIndex)
{
try
{
pageSize = pageSize <= 0 ? 10 : pageSize;
pageIndex = pageIndex <= 0 ? 1 : pageIndex;
var count = await _collection.Find(filterExpression).CountDocumentsAsync();
if (count == 0)
{
return (0, new List<TDocument>());
}
var query = _collection.Find(filterExpression).Sort(orderExpression);
var list = await query.Skip((pageIndex - 1) * pageSize + skipIndex).Limit(pageSize).ToListAsync();
return (count - skipIndex, list);
}
catch (Exception ex)
{
throw ex;
}
}
public virtual (bool ok, string message) InsertOne(TDocument document)
{
try
{
_collection.InsertOne(document);
return (true, null);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
public virtual async Task<(bool ok, string message)> InsertOneAsync(TDocument document)
{
try
{
await Task.Run(() => _collection.InsertOneAsync(document));
return (true, null);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
public (bool ok, string message) InsertMany(ICollection<TDocument> documents)
{
try
{
_collection.InsertMany(documents);
return (true, null);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
public virtual async Task<(bool ok, string message)> InsertManyAsync(ICollection<TDocument> documents)
{
try
{
await Task.Run(() => _collection.InsertManyAsync(documents));
return (true, null);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
public (bool ok, string message) ReplaceOne(TDocument document)
{
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, document.Id);
try
{
_collection.FindOneAndReplace(filter, document);
return (true, null);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
public virtual (bool ok, string message) UpdateById(string id, UpdateDefinition<TDocument> updateDefinition)
{
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, id);
var result = _collection.UpdateOne(filter, updateDefinition);
return (result.IsAcknowledged, null);
}
public virtual async Task<(bool ok, string message)> UpdateByIdAsync(string id, UpdateDefinition<TDocument> updateDefinition)
{
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, id);
try
{
var result = await _collection.UpdateOneAsync(filter, updateDefinition);
return (result.IsAcknowledged, null);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
public virtual async Task<long> CountAsync(
Expression<Func<TDocument, bool>> filterExpression)
{
return await _collection.Find(filterExpression).CountDocumentsAsync();
}
public virtual long Count(
Expression<Func<TDocument, bool>> filterExpression)
{
return _collection.Find(filterExpression).CountDocuments();
}
public virtual async Task<long> CountAsync(
FilterDefinition<TDocument> filterExpression)
{
return await _collection.Find(filterExpression).CountDocumentsAsync();
}
public virtual long Count(
FilterDefinition<TDocument> filterExpression)
{
return _collection.Find(filterExpression).CountDocuments();
}
public virtual (bool ok, string message) UpdateMany(Expression<Func<TDocument, bool>> filterExpression, UpdateDefinition<TDocument> updateDefinition)
{
var result = _collection.UpdateMany(filterExpression, updateDefinition);
return (result.IsAcknowledged, null);
}
public virtual async Task<(bool ok, string message)> UpdateManyAsync(Expression<Func<TDocument, bool>> filterExpression, UpdateDefinition<TDocument> updateDefinition)
{
try
{
var result = await _collection.UpdateManyAsync(filterExpression, updateDefinition);
return (result.IsAcknowledged, null);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
public virtual (bool ok, string message) UpdateMany(FilterDefinition<TDocument> filterExpression, UpdateDefinition<TDocument> updateDefinition)
{
var result = _collection.UpdateMany(filterExpression, updateDefinition);
return (result.IsAcknowledged, null);
}
public virtual async Task<(bool ok, string message)> UpdateManyAsync(FilterDefinition<TDocument> filterExpression, UpdateDefinition<TDocument> updateDefinition)
{
try
{
var result = await _collection.UpdateManyAsync(filterExpression, updateDefinition);
return (result.IsAcknowledged, null);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
public virtual async Task<(bool ok, string message)> ReplaceOneAsync(TDocument document)
{
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, document.Id);
try
{
await _collection.FindOneAndReplaceAsync(filter, document);
return (true, null);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
public (bool ok, string message) DeleteOne(Expression<Func<TDocument, bool>> filterExpression)
{
try
{
_collection.FindOneAndDelete(filterExpression);
return (true, null);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
public async Task<(bool ok, string message)> DeleteOneAsync(Expression<Func<TDocument, bool>> filterExpression)
{
try
{
await Task.Run(() => _collection.FindOneAndDeleteAsync(filterExpression));
return (true, null);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
public (bool ok, string message) DeleteById(string id)
{
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, id);
try
{
_collection.FindOneAndDelete(filter);
return (true, null);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
public async Task<(bool ok, string message)> DeleteByIdAsync(string id)
{
var filter = Builders<TDocument>.Filter.Eq(doc => doc.Id, id);
try
{
var deleted = await _collection.FindOneAndDeleteAsync(filter);
if (deleted == null)
return (false, "记录不存在或已删除");
return (true, null);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
public (bool ok, string message) DeleteMany(Expression<Func<TDocument, bool>> filterExpression)
{
try
{
_collection.DeleteMany(filterExpression);
return (true, null);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
public async Task<(bool ok, string message)> DeleteManyAsync(Expression<Func<TDocument, bool>> filterExpression)
{
try
{
await _collection.DeleteManyAsync(filterExpression);
return (true, null);
}
catch (Exception ex)
{
return (false, ex.Message);
}
}
public async Task<List<BsonDocument>> Aggregate(FilterDefinition<TDocument> filterExpression, ProjectionDefinition<TDocument, BsonDocument> group)
{
try
{
var res = await Task.Run(() => _collection.Aggregate().Match(filterExpression).Group(group).ToList());
return res;
}
catch (Exception ex)
{
return null;
}
}
public async Task<List<TDocument>> RandomData(Expression<Func<TDocument, bool>> filterExpression, int count)
{
try
{
var res = await Task.Run(() =>
_collection.AsQueryable().Where(filterExpression).Sample(count).ToList());
return res;
}
catch (Exception ex)
{
return null;
}
}
}
}
@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MongoDB.Bson" Version="3.6.0" />
<PackageReference Include="MongoDB.Driver" Version="3.6.0" />
<PackageReference Include="MongoDB.Driver.Core" Version="2.30.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Common\Common.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,14 @@
using System;
namespace RabbitMQHelper.Attributes
{
public class RabbitMQAttribute : Attribute
{
public string Topic { get; set; }
public RabbitMQAttribute(string topic)
{
this.Topic = topic;
}
}
}
@@ -0,0 +1,15 @@
using RabbitMQHelper.Attributes;
using System.Reflection;
namespace RabbitMQHelper
{
public class ConsumerExecutorDescriptor
{
public MethodInfo MethodInfo { get; set; }
public TypeInfo ImplTypeInfo { get; set; }
public RabbitMQAttribute Attribute { get; set; }
public string ClassName { get; set; }
}
}
@@ -0,0 +1,6 @@
namespace RabbitMQHelper.Attributes
{
public interface IRabbitSubscribe
{
}
}
@@ -0,0 +1,9 @@
using System.Threading.Tasks;
namespace RabbitMQHelper
{
public interface ISendMQMessage
{
Task<bool> Send(string topic, object value);
}
}
@@ -0,0 +1,85 @@
using System;
using EasyNetQ;
using EasyNetQ.Topology;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
namespace RabbitMQHelper
{
public static class InitRabbitMQSetup
{
public static void AddInitMQ(this IServiceCollection services, Action<RabbitConfig> setupAction, ILogger logger = null)
{
//#if NET5_0
services.AddHttpContextAccessor();//<IHttpContextAccessor, HttpContextAccessor>();
//#endif
var options = new RabbitConfig();
setupAction(options);
try
{
var bus = RabbitHutch.CreateBus($"host={options.Host};virtualHost={options.VirtualHost};username={options.UserName};password={options.Password}");
bus.Advanced.Connected += delegate (object sender1, ConnectedEventArgs e1) { c_Conneted(sender1, e1, logger); };
bus.Advanced.Disconnected += delegate (object sender1, DisconnectedEventArgs e1) { c_DisConnect(sender1, e1, logger); };
services.AddSingleton<IBus>(bus);
}
catch (Exception ex)
{
throw new Exception("MQ初始化异常:" + ex.Message);
}
services.AddSingleton<IRabbitConfig>(sp => options);
services.AddScoped<ISendMQMessage, SendMQMessage>();
services.AddHostedService<MQHostedService>();
if (options.ListSubscribe != null)
{
foreach (var item in options.ListSubscribe)
{
if (options.DefaultRegisterWay.ToLower() == "singleton")
{
services.TryAddSingleton(item);
}
else if (options.DefaultRegisterWay.ToLower() == "scoped")
{
services.TryAddScoped(item);
}
else if (options.DefaultRegisterWay.ToLower() == "transient")
{
services.TryAddTransient(item);
}
else
{
services.TryAddSingleton(item);
}
}
}
}
static void c_Conneted(object sender, EventArgs e, ILogger logger)
{
if (logger == null)
{
Console.WriteLine("RabbitMQ已链接");
}
else
{
}
}
static void c_DisConnect(object sender, EventArgs e, ILogger logger)
{
if (logger == null)
{
Console.WriteLine("RabbitMQ已断开");
}
else
{
}
}
}
}
@@ -0,0 +1,123 @@
using EasyNetQ;
using EasyNetQ.Topology;
using RabbitMQHelper.Attributes;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using RabbitMQHelper.Models;
using MongoDB.Driver;
namespace RabbitMQHelper
{
public class MQHostedService : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
private readonly IBus _bus;
private readonly IRabbitConfig _mongoDBConfig;
private readonly IMongoCollection<ReceiveRecord> _collection;
private static readonly object lockHelper = new object();
public MQHostedService(IServiceProvider serviceProvider, IBus bus, IRabbitConfig mongoDBConfig)
{
_serviceProvider = serviceProvider;
_bus = bus;
_mongoDBConfig = mongoDBConfig;
var database = new MongoClient(_mongoDBConfig.MongoConnectionString).GetDatabase(_mongoDBConfig.MongoDatabaseName);
_collection = database.GetCollection<ReceiveRecord>("mq_receive_record");
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var executorDescriptorList = new List<ConsumerExecutorDescriptor>();
foreach (Type ty in _mongoDBConfig.ListSubscribe)
{
executorDescriptorList.AddRange(GetTopicAttributesDescription(ty.GetTypeInfo()));
}
var exchange = await _bus.Advanced.ExchangeDeclareAsync(_mongoDBConfig.DefaultExchange, ExchangeType.Topic);
foreach (var item in executorDescriptorList)
{
using (var scope = _serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var topic = item.Attribute.Topic;
var provider = scope.ServiceProvider;
var obj = ActivatorUtilities.GetServiceOrCreateInstance(provider, item.ImplTypeInfo.GetTypeInfo());
var subscriptId = $"{ item.ClassName.ToLower() }.{item.MethodInfo.Name.ToLower()}";
var queue = await _bus.Advanced.QueueDeclareAsync(subscriptId);
await _bus.Advanced.BindAsync(exchange, queue, topic, null);
_bus.Advanced.Consume<RabbitMessage>(queue, (msg, info) => Task.Factory.StartNew(async () =>
{
await ReceiveMessage(msg.Body, info.Queue, info.RoutingKey);
lock (lockHelper)
{
object[] parameters = new object[] { msg.Body.Data, msg.Body.Headers };
item.MethodInfo.Invoke(obj, parameters);
}
}));
//await _bus.PubSub.SubscribeAsync<RabbitMessage>(subscriptId, async msg =>
//{
// await ReceiveMessage(msg, subscriptId, topic);
// object[] parameters = new object[] { msg.Data, msg.Headers };
// item.MethodInfo.Invoke(obj, parameters);
//}, x => x.WithTopic(topic).w);
}
}
await Task.Delay(Timeout.Infinite, stoppingToken);
}
public async Task ReceiveMessage(RabbitMessage msg,string subscriptId,string topic)
{
try {
//var database = new MongoClient(_mongoDBConfig.MongoConnectionString).GetDatabase(_mongoDBConfig.MongoDatabaseName);
//var collection = database.GetCollection<ReceiveRecord>("mq_receive_record");
await _collection.InsertOneAsync(new ReceiveRecord(msg.RecordId, subscriptId, topic));
}
catch (Exception ex)
{
}
}
private IEnumerable<ConsumerExecutorDescriptor> GetTopicAttributesDescription(TypeInfo typeInfo)
{
foreach (var method in typeInfo.DeclaredMethods)
{
var topicAttr = method.GetCustomAttributes<RabbitMQAttribute>(true);
var topicAttributes = topicAttr as IList<RabbitMQAttribute> ?? topicAttr.ToList();
if (!topicAttributes.Any())
{
continue;
}
foreach (var attr in topicAttributes)
{
yield return InitDescriptor(attr, method, typeInfo, typeInfo.Name);
}
}
}
private ConsumerExecutorDescriptor InitDescriptor(RabbitMQAttribute attr, MethodInfo methodInfo, TypeInfo implType, string name)
{
var descriptor = new ConsumerExecutorDescriptor
{
Attribute = attr,
MethodInfo = methodInfo,
ImplTypeInfo = implType,
ClassName = name
};
return descriptor;
}
}
}
@@ -0,0 +1,40 @@
using System;
namespace RabbitMQHelper.Models
{
public class PublishRecord
{
public PublishRecord(string publishId, string topic,object message,bool success)
{
this.PublishId = publishId;
this.Topic = topic;
this.MessageContent = message;
this.Success = success;
}
public string id { get; set; } = Guid.NewGuid().ToString("N");
public string PublishId { get; set; }
public string Topic { get; set; }
public object MessageContent { get; set; }
public bool Success { get; set; }
public DateTime PublishTime { get; set; } = DateTime.Now;
}
public class ReceiveRecord
{
public ReceiveRecord(string publishId, string subscriptId, string topic)
{
this.PublishId = publishId;
this.SubscriptId = subscriptId;
this.Topic = topic;
}
public string id { get; set; } = Guid.NewGuid().ToString("N");
public string PublishId { get; set; }
public string SubscriptId { get; set; }
public string Topic { get; set; }
public DateTime ReceiveTime { get; set; } = DateTime.Now;
}
}
@@ -0,0 +1,27 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:60480/",
"sslPort": 44373
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"RabbitMQHelper": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:5001;http://localhost:5000"
}
}
}
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
namespace RabbitMQHelper
{
public interface IRabbitConfig
{
string Host { get; set; }
string VirtualHost { get; set; }
string UserName { get; set; }
string Password { get; set; }
List<Type> ListSubscribe { get; set; }
string MongoDatabaseName { get; set; }
string MongoConnectionString { get; set; }
string DefaultExchange { get; set; }
string DefaultRegisterWay { get; set; }
}
public class RabbitConfig: IRabbitConfig
{
public string Host { get; set; } = "49.233.122.91";
public string VirtualHost { get; set; } = "/";
public string UserName { get; set; } = "guest";
public string Password { get; set; } = "guest";
public List<Type> ListSubscribe { get; set; }
public string MongoDatabaseName { get; set; }
public string MongoConnectionString { get; set; }
public string DefaultExchange { get; set; } = "hscw.default.topic";
public string DefaultRegisterWay { get; set; } = "singleton";
}
}
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DotNetCore.CAP" Version="8.3.4" />
<PackageReference Include="EasyNetQ" Version="7.8.0" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.3.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.4" />
<PackageReference Include="MongoDB.Driver" Version="3.6.0" />
</ItemGroup>
</Project>
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
namespace RabbitMQHelper
{
public class RabbitMessage
{
public RabbitMessage(object data, Dictionary<string, string> headers = null)
{
this.Data = data;
this.Headers = headers;
}
public string RecordId { get; set; } = Guid.NewGuid().ToString("N");
public object Data { get; set; }
public Dictionary<string,string> Headers { get; set; }
}
}
@@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EasyNetQ;
using EasyNetQ.Topology;
using RabbitMQHelper.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using MongoDB.Driver;
namespace RabbitMQHelper
{
public class SendMQMessage:ISendMQMessage
{
private readonly IBus _bus;
private readonly ILogger<SendMQMessage> _logger;
private readonly IRabbitConfig _mongoDBConfig;
private readonly IMongoCollection<PublishRecord> _collection;
private readonly IHttpContextAccessor _httpContextAccessor;
public SendMQMessage(IBus bus, ILogger<SendMQMessage> logger, IRabbitConfig mongoDBConfig, IHttpContextAccessor httpContextAccessor)
{
_bus = bus;
_logger = logger;
_mongoDBConfig = mongoDBConfig;
_httpContextAccessor = httpContextAccessor;
var database = new MongoClient(_mongoDBConfig.MongoConnectionString).GetDatabase(_mongoDBConfig.MongoDatabaseName);
_collection = database.GetCollection<PublishRecord>("mq_publish_record");
}
public async Task<bool> Send(string topic, object value)
{
if (!_bus.Advanced.IsConnected)
{
return false;
}
Dictionary<string, string> headers = new Dictionary<string, string>();
if (_httpContextAccessor.HttpContext != null && _httpContextAccessor.HttpContext.Request != null && _httpContextAccessor.HttpContext.Request.Headers != null)
{
if (_httpContextAccessor.HttpContext.Request.Headers.ContainsKey("token"))
{
headers.Add("token", _httpContextAccessor.HttpContext.Request.Headers["token"].ToString() ?? "");
}
;
}
bool success = false;
var model = new RabbitMessage(value, headers);
var exchange = await _bus.Advanced.ExchangeDeclareAsync(_mongoDBConfig.DefaultExchange, ExchangeType.Topic);
var message = new Message<RabbitMessage>(model);
await _bus.Advanced.PublishAsync<RabbitMessage>(exchange, topic, false, message).ContinueWith(m =>
{
if (m.IsCompleted && m.IsCompletedSuccessfully)
{
success = true;
}
else
{
_logger.LogError(m.Exception, "RabbitMQ消息推送失败 topic={Topic}", topic);
}
});
try
{
await _collection.InsertOneAsync(new PublishRecord(model.RecordId, topic, value, success));
}
catch (Exception ex)
{
_logger.LogError(ex, "RabbitMQ推送记录写入异常 topic={Topic}", topic);
}
return success;
}
}
}
@@ -0,0 +1,175 @@
using Microsoft.Extensions.Logging;
namespace RedisHelper
{
public class DistributedLock : IDistributedLock
{
private readonly ICache _cache;
private readonly ILogger<DistributedLock> _logger;
public DistributedLock(ICache cache, ILogger<DistributedLock> logger)
{
_cache = cache;
_logger = logger;
}
public async Task<T> TryExecuteAsync<T>(string key, Func<Task<T>> action, int expiry = 30, bool retry = false, int retryDelay = 100)
{
string lockKey = GetLockKey(key);
string lockValue = Guid.NewGuid().ToString();
while (true)
{
if (TryAcquireLock(lockKey, lockValue, expiry))
{
try
{
return await action();
}
finally
{
ReleaseLock(lockKey, lockValue);
}
}
if (!retry)
{
return default(T);
}
await Task.Delay(retryDelay);
}
}
public async Task<bool> TryExecuteAsync(string key, Func<Task> action, int expiry = 30, bool retry = false, int retryDelay = 100)
{
string lockKey = GetLockKey(key);
string lockValue = Guid.NewGuid().ToString();
while (true)
{
if (TryAcquireLock(lockKey, lockValue, expiry))
{
try
{
await action();
return true;
}
finally
{
ReleaseLock(lockKey, lockValue);
}
}
if (!retry)
{
return false;
}
await Task.Delay(retryDelay);
}
}
public T TryExecute<T>(string key, Func<T> action, int expiry = 30, bool retry = false, int retryDelay = 100)
{
string lockKey = GetLockKey(key);
string lockValue = Guid.NewGuid().ToString();
while (true)
{
if (TryAcquireLock(lockKey, lockValue, expiry))
{
try
{
return action();
}
finally
{
ReleaseLock(lockKey, lockValue);
}
}
if (!retry)
{
return default(T);
}
Thread.Sleep(retryDelay);
}
}
public bool TryExecute(string key, Action action, int expiry = 30, bool retry = false, int retryDelay = 100)
{
string lockKey = GetLockKey(key);
string lockValue = Guid.NewGuid().ToString();
while (true)
{
if (TryAcquireLock(lockKey, lockValue, expiry))
{
try
{
action();
return true;
}
finally
{
ReleaseLock(lockKey, lockValue);
}
}
if (!retry)
{
return false;
}
Thread.Sleep(retryDelay);
}
}
private bool TryAcquireLock(string lockKey, string lockValue, int expiry)
{
try
{
var redis = _cache.GetFullRedis();
bool acquired = redis.Add(lockKey, lockValue, expiry);
if (acquired)
{
}
return acquired;
}
catch (Exception ex)
{
_logger.LogError(ex, $"获取分布式锁时发生错�? {lockKey}");
return false;
}
}
private void ReleaseLock(string lockKey, string lockValue)
{
try
{
var redis = _cache.GetFullRedis();
var currentValue = redis.Get<string>(lockKey);
if (currentValue == lockValue)
{
redis.Remove(lockKey);
}
}
catch (Exception ex)
{
_logger.LogError(ex, $"释放分布式锁时发生错�? {lockKey}");
}
}
private string GetLockKey(string key)
{
return $"{key}";
}
}
}
+124
View File
@@ -0,0 +1,124 @@

using NewLife.Caching;
using System;
using System.Collections.Generic;
using System.Text;
namespace RedisHelper
{
public interface ICache
{
FullRedis GetFullRedis();
void StartPipeline();
T Get<T>(string key);
IDictionary<string, T> GetAll<T>(List<string> keys);
int Remove(string key);
void Clear();
void Clear(string pattern, int count = 1000);
bool Insert<T>(string key, T data);
bool Insert<T>(string key, T data, int expireSeconds);
bool Insert<T>(string key, T data, TimeSpan expireSeconds);
long IncrBy(string key, long value);
bool SetAdd<T>(string key, T value);
bool SetContains<T>(string key, T value);
bool SetRemove<T>(string key, T value);
long SetCount(string key);
List<T> SetMembers<T>(string key);
bool InitDataToList<T>(string key, List<T> t);
IEnumerable<string> GetPatternKey(string pattern, int count = 1000);
IEnumerable<T> GetPatternList<T>(string pattern, int count = 1000);
bool UpdateDataToList<T>(string key, T t, Func<T, bool> predicate);
bool Exists(string key);
bool AddToList<T>(string key, T t);
bool RemoveFromList<T>(string key, Func<T, bool> predicate);
IList<T> GetList<T>(string key);
bool InitDataToDictionary<T>(string key, IDictionary<string, T> t);
bool UpdateDataToDictionary<T>(string key, T t, string dataKey);
bool RemoveFromDictionary<T>(string key, string dataKey);
Tuple<IEnumerable<T>,int> GetPageDescendingListFromDictionary<T, Tkey>(string key, int pageIndex, int pageSize,
Func<T, bool> filter,
Func<T, Tkey> sort);
Tuple<IEnumerable<T>, int> GetPageDescendingListFromDictionary<T, Tkey>(string key, int pageIndex,
int pageSize, Func<T, bool> filter,
Func<T, Tkey> sort, int skipCount);
Tuple<IEnumerable<T>,int> GetPageListFromDictionary<T, Tkey>(string key, int pageIndex, int pageSize,
Func<T, bool> filter,
Func<T, Tkey> sort);
Tuple<IEnumerable<T>,int> GetPageListFromDictionary<T, Tkey>(string key, int pageIndex, int pageSize,
Func<T, bool> filter,
Func<T, Tkey> sort, int skipCount);
IEnumerable<T> GetListFromDictionary<T, Tkey>(string key, int pageIndex, int pageSize,
Func<T, bool> filter,
Func<T, Tkey> sort);
IEnumerable<T> GetDescendingListFromDictionary<T, Tkey>(string key, int pageIndex, int pageSize,
Func<T, bool> filter,
Func<T, Tkey> sort);
T GetFromDictionary<T>(string key, string dataKey);
ICollection<T> GetListFromDictionary<T>(string key);
IEnumerable<T> GetListFromDictionaryByFilter<T>(string key, Func<T, bool> filter);
int GetListCountFromDictionaryByFilter<T>(string key, Func<T, bool> filter);
bool AddTempContent<T>(string tempId, T data, TimeSpan timeSpan);
T GetTempContent<T>(string tempId);
}
}
@@ -0,0 +1,16 @@
using System;
using System.Threading.Tasks;
namespace RedisHelper
{
public interface IDistributedLock
{
Task<T> TryExecuteAsync<T>(string key, Func<Task<T>> action, int expiry = 30, bool retry = false, int retryDelay = 100);
Task<bool> TryExecuteAsync(string key, Func<Task> action, int expiry = 30, bool retry = false, int retryDelay = 100);
T TryExecute<T>(string key, Func<T> action, int expiry = 30, bool retry = false, int retryDelay = 100);
bool TryExecute(string key, Action action, int expiry = 30, bool retry = false, int retryDelay = 100);
}
}
+440
View File
@@ -0,0 +1,440 @@
using NewLife.Caching;
using Microsoft.Extensions.Logging;
namespace RedisHelper
{
public class RedisCache : ICache
{
public FullRedis fullRedis = null;
private readonly ILogger<RedisCache> _logger;
public RedisCache(RedisUrl config, ILogger<RedisCache> logger)
{
_logger = logger;
fullRedis = FullRedis.Create(config.url);
fullRedis.MaxMessageSize = 16 * 1024 * 1024;
}
public FullRedis GetFullRedis()
{
return fullRedis;
}
~RedisCache()
{
fullRedis.Dispose();
}
public void StartPipeline()
{
fullRedis.StartPipeline();
}
public long IncrBy(string key, long value)
{
return fullRedis.Increment(key, value);
}
public bool SetAdd<T>(string key, T value)
{
var result = fullRedis.SADD(key, value);
return result > 0;
}
public bool SetContains<T>(string key, T value)
{
return fullRedis.SISMEMBER(key, value) > 0;
}
public bool SetRemove<T>(string key, T value)
{
return fullRedis.SREM(key, value) > 0;
}
public long SetCount(string key)
{
return fullRedis.SCARD(key);
}
public List<T> SetMembers<T>(string key)
{
return fullRedis.SMEMBERS<T>(key).ToList();
}
private static readonly System.Text.Json.JsonSerializerOptions _jsonOptions = new()
{
PropertyNameCaseInsensitive = true,
WriteIndented = false
};
private static bool IsSimpleType(Type t) =>
t == typeof(string) || t == typeof(long) || t == typeof(int) || t == typeof(bool) || t == typeof(double);
public T Get<T>(string key)
{
try
{
if (IsSimpleType(typeof(T)))
return fullRedis.Get<T>(key);
var json = fullRedis.Get<string>(key);
if (json == null) return default;
return System.Text.Json.JsonSerializer.Deserialize<T>(json, _jsonOptions);
}
catch
{
return default;
}
}
public IDictionary<string,T> GetAll<T>(List<string> keys)
{
try
{
if (IsSimpleType(typeof(T)))
return fullRedis.GetAll<T>(keys);
var strDict = fullRedis.GetAll<string>(keys);
if (strDict == null) return null;
var result = new Dictionary<string, T>();
foreach (var kv in strDict)
{
if (kv.Value != null)
result[kv.Key] = System.Text.Json.JsonSerializer.Deserialize<T>(kv.Value, _jsonOptions);
}
return result;
}
catch
{
return null;
}
}
public bool Insert<T>(string key, T data)
{
if (IsSimpleType(typeof(T)))
return fullRedis.Set<T>(key, data);
var json = System.Text.Json.JsonSerializer.Serialize(data, _jsonOptions);
return fullRedis.Set<string>(key, json);
}
public bool Insert<T>(string key, T data, int expireSeconds)
{
if (IsSimpleType(typeof(T)))
return fullRedis.Set<T>(key, data, expireSeconds);
var json = System.Text.Json.JsonSerializer.Serialize(data, _jsonOptions);
return fullRedis.Set<string>(key, json, expireSeconds);
}
public bool Insert<T>(string key, T data, TimeSpan expireSeconds)
{
if (IsSimpleType(typeof(T)))
return fullRedis.Set<T>(key, data, expireSeconds);
var json = System.Text.Json.JsonSerializer.Serialize(data, _jsonOptions);
return fullRedis.Set<string>(key, json, expireSeconds);
}
public int Remove(string key)
{
return fullRedis.Remove(key);
}
public void Clear(string pattern, int count = 1000)
{
try
{
var keys = fullRedis.Search(pattern, count).ToArray();
if (keys.Any())
{
fullRedis.Remove(keys);
}
}
catch (Exception e)
{
_logger.LogError(e, "按表达式清理Redis键失败 pattern={Pattern} count={Count}", pattern, count);
}
}
public IEnumerable<string> GetPatternKey(string pattern, int count = 1000)
{
return fullRedis.Search(pattern, count).ToArray();
}
public IEnumerable<T> GetPatternList<T>(string pattern, int count = 1000)
{
var lstKey = fullRedis.Search(pattern, count).ToArray();
return lstKey.Select(item => fullRedis.Get<T>(item)).ToList();
}
public void Clear()
{
fullRedis.Clear();
}
public bool Exists(string key)
{
return fullRedis.ContainsKey(key);
}
public IList<T> GetList<T>(string key)
{
return fullRedis.GetList<T>(key);
}
public bool AddToList<T>(string key, T t)
{
try
{
var lst = fullRedis.GetList<T>(key);
lst.Add(t);
return true;
}
catch (Exception ex)
{
return false;
}
}
public bool InitDataToList<T>(string key, List<T> t)
{
try
{
fullRedis.Remove(key);
var lst = fullRedis.GetList<T>(key);
lst.Clear();
foreach (var item in t)
{
lst.Add(item);
}
return true;
}
catch (Exception ex)
{
return false;
}
}
public bool UpdateDataToList<T>(string key, T t, Func<T, bool> predicate)
{
try
{
var lst = fullRedis.GetList<T>(key);
var data = lst.Where(predicate);
foreach (var item in data)
{
lst.Remove(item);
}
lst.Add(t);
return true;
}
catch (Exception ex)
{
return false;
}
}
public bool RemoveFromList<T>(string key, Func<T, bool> predicate)
{
var result = false;
var lst = fullRedis.GetList<T>(key);
var data = lst.Where(predicate);
if (data != null)
{
foreach (var item in data)
{
result = lst.Remove(item);
}
}
return result;
}
public bool InitDataToDictionary<T>(string key, IDictionary<string, T> t)
{
try
{
fullRedis.Remove(key);
var lst = fullRedis.GetDictionary<T>(key);
// lst.Clear();
foreach (var item in t)
{
lst.Add(item.Key, item.Value);
}
return true;
}
catch (Exception ex)
{
return false;
}
}
public bool UpdateDataToDictionary<T>(string key, T t, string dataKey)
{
try
{
var lst = fullRedis.GetDictionary<T>(key);
lst.Remove(dataKey);
lst.Add(dataKey, t);
return true;
}
catch (Exception ex)
{
return false;
}
}
public bool RemoveFromDictionary<T>(string key, string dataKey)
{
var lst = fullRedis.GetDictionary<T>(key);
return lst.Remove(dataKey);
}
public T GetFromDictionary<T>(string key, string dataKey)
{
var lst = fullRedis.GetDictionary<T>(key);
if (lst.ContainsKey(dataKey))
{
return lst[dataKey];
}
else
{
return default(T);
}
}
public ICollection<T> GetListFromDictionary<T>(string key)
{
var dict = fullRedis.GetDictionary<T>(key);
return dict.Values;
}
public IEnumerable<T> GetListFromDictionaryByFilter<T>(string key, Func<T, bool> filter)
{
var dict = fullRedis.GetDictionary<T>(key);
return dict.Values.Where(filter);
}
public int GetListCountFromDictionaryByFilter<T>(string key, Func<T, bool> filter)
{
var dict = fullRedis.GetDictionary<T>(key);
return dict.Values.Where(filter).Count();
}
public Tuple<IEnumerable<T>,int> GetPageDescendingListFromDictionary<T,Tkey>(string key, int pageIndex, int pageSize, Func<T, bool> filter,
Func<T,Tkey> sort)
{
var dict = fullRedis.GetDictionary<T>(key);
var count = dict.Values.Where(filter).Count();
var data= dict.Values.Where(filter)
.OrderByDescending(sort)
.Skip((pageIndex - 1) * pageSize).Take(pageSize);
return new Tuple<IEnumerable<T>, int>(data, count);
}
public Tuple<IEnumerable<T>,int> GetPageDescendingListFromDictionary<T,Tkey>(string key, int pageIndex, int pageSize, Func<T, bool> filter,
Func<T,Tkey> sort,int skipCount)
{
var dict = fullRedis.GetDictionary<T>(key);
var count = dict.Values.Where(filter).Count();
var data= dict.Values.Where(filter)
.OrderByDescending(sort)
.Skip((pageIndex - 1) * pageSize+skipCount).Take(pageSize);
return new Tuple<IEnumerable<T>, int>(data, count);
}
public Tuple<IEnumerable<T>, int> GetPageListFromDictionary<T, Tkey>(string key, int pageIndex, int pageSize,
Func<T, bool> filter,
Func<T, Tkey> sort)
{
var dict = fullRedis.GetDictionary<T>(key);
var count = dict.Values.Where(filter).Count();
var data = dict.Values.Where(filter)
.OrderBy(sort)
.Skip((pageIndex - 1) * pageSize).Take(pageSize);
return new Tuple<IEnumerable<T>, int>(data, count);
}
public Tuple<IEnumerable<T>, int> GetPageListFromDictionary<T, Tkey>(string key, int pageIndex, int pageSize,
Func<T, bool> filter,
Func<T, Tkey> sort,int skipCount)
{
var dict = fullRedis.GetDictionary<T>(key);
var count = dict.Values.Where(filter).Count();
var data = dict.Values.Where(filter)
.OrderBy(sort)
.Skip((pageIndex - 1) * pageSize+skipCount).Take(pageSize);
return new Tuple<IEnumerable<T>, int>(data, count);
}
public IEnumerable<T> GetListFromDictionary<T, Tkey>(string key, int pageIndex, int pageSize,
Func<T, bool> filter,
Func<T, Tkey> sort)
{
var dict = fullRedis.GetDictionary<T>(key);
var count = dict.Values.Where(filter).Count();
var data = dict.Values.Where(filter)
.OrderBy(sort)
.Skip((pageIndex - 1) * pageSize).Take(pageSize);
return data;
}
public IEnumerable<T> GetDescendingListFromDictionary<T,Tkey>(string key, int pageIndex, int pageSize, Func<T, bool> filter,
Func<T,Tkey> sort)
{
var dict = fullRedis.GetDictionary<T>(key);
var data= dict.Values.Where(filter)
.OrderByDescending(sort)
.Skip((pageIndex - 1) * pageSize).Take(pageSize);
return data;
}
private string Temp="HS:Temp:{0}";
public bool AddTempContent<T>(string tempId, T data, TimeSpan timeSpan)
{
string key = string.Format(Temp, tempId);
return Insert(key, data, timeSpan);
}
public T GetTempContent<T>(string tempId)
{
string key = string.Format(Temp, tempId);
return Get<T>(key);
}
}
}
+17
View File
@@ -0,0 +1,17 @@
namespace RedisHelper
{
public class RedisConfig
{
public string Host { get; set; }
public int Port { get; set; }
public string Password { get; set; }
public string UserName { get; set; } = "";
public int Database { get; set; }
public override string ToString()
{
return $"Server={Host}:{Port};Password={Password};UserName={UserName};Db={Database}";
}
}
}
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.7" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.7" />
<PackageReference Include="NewLife.Redis" Version="6.2.2025.503" />
</ItemGroup>
</Project>
@@ -0,0 +1,49 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RedisHelper
{
public static class RedisServiceExtension
{
public static IServiceCollection AddRedisCache(this IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton<RedisUrl>(opts =>
{
var setting = new RedisConfig();
configuration.Bind("RedisConfig", setting);
return new RedisUrl { url = setting.ToString() };
});
services.AddScoped<ICache, RedisCache>();
services.AddScoped<IDistributedLock, DistributedLock>();
return services;
}
public static IServiceCollection AddRedisCacheSingleton(this IServiceCollection services, IConfiguration configuration)
{
services.AddSingleton<RedisUrl>(opts =>
{
var setting = new RedisConfig();
configuration.Bind("RedisConfig", setting);
return new RedisUrl { url = setting.ToString() };
});
services.AddSingleton<ICache, RedisCache>();
services.AddSingleton<IDistributedLock, DistributedLock>();
return services;
}
public static IServiceCollection AddRedisCache(this IServiceCollection services, string url)
{
services.AddSingleton<RedisUrl>(options => new RedisUrl { url = url });
services.AddSingleton<ICache, RedisCache>();
services.AddSingleton<IDistributedLock, DistributedLock>();
return services;
}
}
}
+16
View File
@@ -0,0 +1,16 @@
namespace RedisHelper
{
public class RedisUrl
{
public RedisUrl()
{
}
//public RedisUrl(string url)
//{
// _url = url;
//}
public string url { get; set; }
}
}
@@ -0,0 +1,85 @@
using Cronos;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
namespace ScheduledService;
public class CronJobService : IHostedService, IDisposable
{
private System.Timers.Timer? _timer;
private readonly CronExpression _expression;
private readonly TimeZoneInfo _timeZoneInfo;
private readonly ILogger<CronJobService> _logger;
public CronJobService(string cronExpression, TimeZoneInfo timeZoneInfo, ILogger<CronJobService>? logger = null)
{
_expression = CronExpression.Parse(cronExpression, CronFormat.IncludeSeconds);
_timeZoneInfo = timeZoneInfo;
_logger = logger ?? NullLogger<CronJobService>.Instance;
}
public virtual async Task StartAsync(CancellationToken cancellationToken)
{
await ScheduleJob(cancellationToken);
}
protected virtual async Task ScheduleJob(CancellationToken cancellationToken)
{
var next = _expression.GetNextOccurrence(DateTimeOffset.Now, _timeZoneInfo);
if (next.HasValue)
{
var delay = next.Value - DateTimeOffset.Now;
if (delay.TotalMilliseconds <= 0)
{
await ScheduleJob(cancellationToken);
return;
}
_timer = new System.Timers.Timer(delay.TotalMilliseconds);
_timer.Elapsed += async (sender, args) =>
{
_timer?.Dispose();
_timer = null;
try
{
if (!cancellationToken.IsCancellationRequested)
await DoWork(cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "定时任务 DoWork 执行异常");
}
try
{
if (!cancellationToken.IsCancellationRequested)
await ScheduleJob(cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "定时任务 ScheduleJob 调度异常");
}
};
_timer.Start();
}
await Task.CompletedTask;
}
public virtual async Task DoWork(CancellationToken cancellationToken)
{
await Task.CompletedTask;
}
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
_timer?.Stop();
await Task.CompletedTask;
}
public virtual void Dispose()
{
_timer?.Dispose();
}
}
@@ -0,0 +1,13 @@
namespace ScheduledService;
public interface IScheduleConfig<T>
{
string CronExpression { get; set; }
TimeZoneInfo TimeZoneInfo { get; set; }
}
public class ScheduleConfig<T> : IScheduleConfig<T>
{
public string CronExpression { get; set; } = "* * * * *";
public TimeZoneInfo TimeZoneInfo { get; set; } = TimeZoneInfo.Local;
}
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Cronos" Version="0.8.4" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.7" />
</ItemGroup>
</Project>
@@ -0,0 +1,28 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace ScheduledService;
public static class ScheduledServiceExtensions
{
public static IServiceCollection AddCronJob<T>(this IServiceCollection services, Action<IScheduleConfig<T>> options) where T : CronJobService
{
if (options == null)
{
throw new ArgumentNullException(nameof(options), "Options cannot be null");
}
var config = new ScheduleConfig<T>();
options.Invoke(config);
if (string.IsNullOrWhiteSpace(config.CronExpression))
{
throw new ArgumentNullException(nameof(ScheduleConfig<T>.CronExpression), "Cron expression cannot be null or empty");
}
services.AddSingleton<IScheduleConfig<T>>(config);
services.AddHostedService<T>();
return services;
}
}
@@ -0,0 +1,55 @@
using System.Reflection;
using Autofac;
using CommonService;
using MongoRepository;
using RedisService;
namespace SharpAbp.Infrastructure.Api.Autofac
{
public abstract class ApplicationModule : global::Autofac.Module
{
protected abstract Assembly CurrentAssembly { get; }
protected virtual Assembly[] GetServiceAssemblies()
{
return new[] { CurrentAssembly };
}
protected virtual bool RegisterMongoBaseService => true;
protected override void Load(ContainerBuilder builder)
{
builder.RegisterGeneric(typeof(MongoBaseRepository<>))
.As(typeof(IMongoBaseRepository<>))
.InstancePerLifetimeScope();
if (RegisterMongoBaseService)
{
builder.RegisterGeneric(typeof(MongoBaseService<>))
.As(typeof(IMongoBaseService<>))
.InstancePerLifetimeScope();
}
builder.RegisterType<RedisService.RedisService>()
.As<RedisService.IRedisService>()
.InstancePerLifetimeScope();
var serviceAssemblies = GetServiceAssemblies();
foreach (var assembly in serviceAssemblies)
{
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.Name.EndsWith("Service") && !t.IsAbstract)
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
}
foreach (var assembly in serviceAssemblies)
{
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.Name.EndsWith("Repository") && !t.IsAbstract)
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
}
}
}
}
@@ -0,0 +1,56 @@
namespace SharpAbp.Infrastructure.Api.Configuration
{
public class JwtConfig
{
public string TokenKey { get; set; } = "token";
public string Issuer { get; set; } = "admin";
public string Audience { get; set; } = "";
public string SecretKey { get; set; } = "";
public bool ValidateIssuer { get; set; } = false;
public bool ValidateAudience { get; set; } = false;
public bool ValidateIssuerSigningKey { get; set; } = false;
public bool ValidateLifetime { get; set; } = false;
}
public class CapConfig
{
public string Host { get; set; } = "localhost";
public string UserName { get; set; } = "admin";
public string Password { get; set; } = "";
public string VirtualHost { get; set; } = "/";
public string PGLogHost { get; set; } = "";
}
public class RedisConfig
{
public string Host { get; set; } = "localhost";
public int Port { get; set; } = 6379;
public string UserName { get; set; } = "";
public string Password { get; set; } = "";
public int Database { get; set; } = 0;
}
public class WXConfig
{
public string AppId { get; set; } = "";
public string Secret { get; set; } = "";
public string Url { get; set; } = "";
}
public class AppSettings
{
public JwtConfig JwtConfig { get; set; } = new();
public string PgSqlConnection { get; set; } = "";
public CapConfig CapConfig { get; set; } = new();
public RedisConfig RedisConfig { get; set; } = new();
public MongoDbSettings MongoDbSettings { get; set; } = new();
public WXConfig WXConfig { get; set; } = new();
}
public class MongoDbSettings
{
public string Username { get; set; } = "";
public string Password { get; set; } = "";
public string ServerUrl { get; set; } = "";
}
}
@@ -0,0 +1,100 @@
namespace SharpAbp.Infrastructure.Api.Configuration
{
public static class ConfigurationHelper
{
public static string GetString(string key, string defaultValue = null)
{
var envValue = Environment.GetEnvironmentVariable(key);
if (!string.IsNullOrEmpty(envValue))
{
return envValue;
}
return defaultValue;
}
public static int GetInt(string key, int defaultValue = 0)
{
var envValue = Environment.GetEnvironmentVariable(key);
if (int.TryParse(envValue, out var result))
{
return result;
}
return defaultValue;
}
public static bool GetBool(string key, bool defaultValue = false)
{
var envValue = Environment.GetEnvironmentVariable(key);
if (bool.TryParse(envValue, out var result))
{
return result;
}
return defaultValue;
}
public static string GetConnectionString(string type)
{
return type.ToLower() switch
{
"mongodb" => GetMongoDBConnectionString(),
"redis" => GetRedisConnectionString(),
"rabbitmq" => GetRabbitMQConnectionString(),
"mysql" => GetMySQLConnectionString(),
_ => throw new ArgumentException($"Unknown connection type: {type}")
};
}
private static string GetMongoDBConnectionString()
{
var host = GetString("MONGODB_HOST", "localhost");
var port = GetString("MONGODB_PORT", "27017");
var user = GetString("MONGODB_USER", "");
var password = GetString("MONGODB_PASSWORD", "");
var database = GetString("MONGODB_DATABASE", "data_0524");
if (!string.IsNullOrEmpty(user) && !string.IsNullOrEmpty(password))
{
return $"mongodb://{user}:{password}@{host}:{port}/{database}";
}
return $"mongodb://{host}:{port}/{database}";
}
private static string GetRedisConnectionString()
{
var host = GetString("REDIS_HOST", "localhost");
var port = GetString("REDIS_PORT", "6379");
var password = GetString("REDIS_PASSWORD", "");
var database = GetString("REDIS_DB", "0");
var connectionString = $"{host}:{port}";
if (!string.IsNullOrEmpty(password))
{
connectionString += $",password={password}";
}
connectionString += $",defaultDatabase={database}";
return connectionString;
}
private static string GetRabbitMQConnectionString()
{
var host = GetString("RABBITMQ_HOST", "localhost");
var port = GetString("RABBITMQ_PORT", "5672");
var user = GetString("RABBITMQ_USER", "guest");
var password = GetString("RABBITMQ_PASSWORD", "guest");
return $"amqp://{user}:{password}@{host}:{port}";
}
private static string GetMySQLConnectionString()
{
var host = GetString("MYSQL_HOST", "localhost");
var port = GetString("MYSQL_PORT", "3306");
var user = GetString("MYSQL_USER", "root");
var password = GetString("MYSQL_PASSWORD", "");
var database = GetString("MYSQL_DATABASE", "data_0524");
return $"Server={host};Port={port};Database={database};User={user};Password={password};";
}
}
}
@@ -0,0 +1,57 @@
using System.Linq;
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc;
namespace SharpAbp.Infrastructure.Api.Controllers
{
[ApiController]
public abstract class BaseController : Controller
{
public LoginUser loginUser
{
get
{
return GetLoginUser();
}
}
protected virtual LoginUser GetLoginUser()
{
var claims = User?.Claims?.ToList();
if (claims == null || claims.Count == 0)
{
return null;
}
var userId = claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value;
var userName = claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
var role = claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value;
return new LoginUser
{
userId = userId,
userName = userName,
role = role
};
}
protected virtual string GetClientIp()
{
var ip = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
if (string.IsNullOrEmpty(ip))
{
ip = HttpContext.Connection.RemoteIpAddress?.ToString();
}
return ip ?? "unknown";
}
}
public class LoginUser
{
public string userId { get; set; }
public string userName { get; set; }
public string role { get; set; }
}
}
@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using Common;
using Microsoft.AspNetCore.Mvc;
namespace SharpAbp.Infrastructure.Api.Extensions
{
public static class ControllerExtensions
{
public static BaseResponse<T> ToFailResult<T>(
this Controller controller,
string info) => new BaseResponse<T>() { Info = info, Code = CommonResponseCodeEnum.ResultFail };
public static ListResponse<T> ToListFailResult<T>(
this Controller controller,
string info) => new ListResponse<T>()
{ Info = info, Code = CommonResponseCodeEnum.ResultFail };
public static PageResponse<T> ToPageFailResult<T>(
this Controller controller,
string info) => new PageResponse<T>()
{
Code = CommonResponseCodeEnum.ResultFail,
Info = info
};
public static BaseResponse<T> ToResult<T>(
this Controller controller,
T obj, string info = null) => new BaseResponse<T>()
{
Data = obj,
Info = info,
Code = CommonResponseCodeEnum.Success
};
public static ListResponse<T> ToListResult<T>(
this Controller controller,
IEnumerable<T> list, string info = null) => new ListResponse<T>()
{
Data = list,
Code = CommonResponseCodeEnum.Success,
Info = info
};
public static PageResponse<T> ToPageResult<T>(
this Controller controller,
IEnumerable<T> list, int count = 0, string info = null) => new PageResponse<T>()
{
Code = CommonResponseCodeEnum.Success,
Info = info,
Count = count,
Data = list
};
public static BaseResponse<T> ToNoLoginResult<T>(
this Controller controller) => new BaseResponse<T>()
{
Info = "未登录",
Code = CommonResponseCodeEnum.NoLogin
};
public static BaseResponse<T> ToTokenInvalidResult<T>(
this Controller controller) => new BaseResponse<T>()
{
Info = "token失效",
Code = CommonResponseCodeEnum.TokenInvalid
};
public static BaseResponse<T> ToParamErrorResult<T>(
this Controller controller,
string info = "请求参数错误") => new BaseResponse<T>()
{
Info = info,
Code = CommonResponseCodeEnum.ParamError
};
}
}
@@ -0,0 +1,82 @@
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace SharpAbp.Infrastructure.Api.Middleware
{
public class GlobalExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<GlobalExceptionMiddleware> _logger;
public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private Task HandleExceptionAsync(HttpContext context, Exception ex)
{
if (ex is Common.BusinessException bizEx)
{
return WriteJsonResponse(context, Common.CommonResponseCodeEnum.Error, bizEx.Message);
}
if (ex is UnauthorizedAccessException)
{
return WriteJsonResponse(context, Common.CommonResponseCodeEnum.NoAuth, "未授权访问");
}
if (ex is ArgumentException argEx)
{
return WriteJsonResponse(context, Common.CommonResponseCodeEnum.ParamError, argEx.Message);
}
if (ex is TimeoutException || ex is OperationCanceledException)
{
return WriteJsonResponse(context, Common.CommonResponseCodeEnum.Error, "操作超时,请稍后重试: " + ex.Message);
}
_logger.LogError(ex, "系统异常: {Message}", ex.Message);
return WriteJsonResponse(context, Common.CommonResponseCodeEnum.Exception, ex.Message);
}
private static Task WriteJsonResponse(HttpContext context, Common.CommonResponseCodeEnum code, string message)
{
context.Response.StatusCode = 200;
context.Response.ContentType = "application/json";
return context.Response.WriteAsync(JsonConvert.SerializeObject(new
{
code = code,
info = message,
data = (object?)null
}));
}
}
public static class GlobalExceptionMiddlewareExtensions
{
public static IApplicationBuilder UseGlobalExceptionHandler(this IApplicationBuilder app)
{
return app.UseMiddleware<GlobalExceptionMiddleware>();
}
}
}
@@ -0,0 +1,191 @@
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace SharpAbp.Infrastructure.Api.Resilience
{
public enum CircuitState
{
Closed,
Open,
HalfOpen
}
public class CircuitBreakerOptions
{
public int FailureThreshold { get; set; } = 5;
public int BreakDurationSeconds { get; set; } = 30;
public int HalfOpenAttempts { get; set; } = 3;
public int SuccessThreshold { get; set; } = 2;
}
public class CircuitBreaker
{
private readonly CircuitBreakerOptions _options;
private readonly ILogger _logger;
private int _failureCount;
private int _successCount;
private int _halfOpenAttempts;
private CircuitState _state = CircuitState.Closed;
private DateTime _lastFailureTime;
private readonly object _lock = new();
public CircuitBreaker(string name, CircuitBreakerOptions options, ILogger logger)
{
Name = name;
_options = options;
_logger = logger;
}
public string Name { get; }
public CircuitState State => _state;
public async Task<T> ExecuteAsync<T>(Func<Task<T>> action)
{
if (!CanExecute())
{
throw new CircuitBreakerOpenException($"Circuit breaker '{Name}' is open");
}
try
{
var result = await action();
OnSuccess();
return result;
}
catch (Exception ex)
{
OnFailure(ex);
throw;
}
}
public async Task ExecuteAsync(Func<Task> action)
{
if (!CanExecute())
{
throw new CircuitBreakerOpenException($"Circuit breaker '{Name}' is open");
}
try
{
await action();
OnSuccess();
}
catch (Exception ex)
{
OnFailure(ex);
throw;
}
}
private bool CanExecute()
{
lock (_lock)
{
switch (_state)
{
case CircuitState.Closed:
return true;
case CircuitState.Open:
var elapsed = (DateTime.UtcNow - _lastFailureTime).TotalSeconds;
if (elapsed >= _options.BreakDurationSeconds)
{
_state = CircuitState.HalfOpen;
_halfOpenAttempts = 0;
return true;
}
return false;
case CircuitState.HalfOpen:
return _halfOpenAttempts < _options.HalfOpenAttempts;
default:
return true;
}
}
}
private void OnSuccess()
{
lock (_lock)
{
switch (_state)
{
case CircuitState.Closed:
_failureCount = 0;
break;
case CircuitState.HalfOpen:
_successCount++;
_halfOpenAttempts++;
if (_successCount >= _options.SuccessThreshold)
{
_state = CircuitState.Closed;
_failureCount = 0;
_successCount = 0;
}
break;
}
}
}
private void OnFailure(Exception ex)
{
lock (_lock)
{
_failureCount++;
_lastFailureTime = DateTime.UtcNow;
switch (_state)
{
case CircuitState.Closed:
if (_failureCount >= _options.FailureThreshold)
{
_state = CircuitState.Open;
}
break;
case CircuitState.HalfOpen:
_state = CircuitState.Open;
_halfOpenAttempts = _options.HalfOpenAttempts;
break;
}
}
}
}
public class CircuitBreakerOpenException : Exception
{
public CircuitBreakerOpenException(string message) : base(message) { }
}
public class CircuitBreakerFactory
{
private readonly ConcurrentDictionary<string, CircuitBreaker> _breakers = new();
private readonly ILoggerFactory _loggerFactory;
public CircuitBreakerFactory(ILoggerFactory loggerFactory)
{
_loggerFactory = loggerFactory;
}
public CircuitBreaker GetOrCreate(string name, CircuitBreakerOptions options = null)
{
return _breakers.GetOrAdd(name, n =>
{
var opts = options ?? new CircuitBreakerOptions();
var logger = _loggerFactory.CreateLogger<CircuitBreaker>();
return new CircuitBreaker(n, opts, logger);
});
}
}
}
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>SharpAbp.Infrastructure.Api</RootNamespace>
<AssemblyName>SharpAbp.Infrastructure.Api</AssemblyName>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Autofac" Version="8.3.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\CommonService\CommonService.csproj" />
<ProjectReference Include="..\..\RedisService\RedisService.csproj" />
<ProjectReference Include="..\Common\Common.csproj" />
</ItemGroup>
</Project>