diff --git a/Sanhe.Abp.Framework.sln b/Sanhe.Abp.Framework.sln index 2525b30..df0fb1f 100644 --- a/Sanhe.Abp.Framework.sln +++ b/Sanhe.Abp.Framework.sln @@ -59,9 +59,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.Hangfire.Dashboar EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.Hangfire.Storage.PostgreSql", "modules\common\Sanhe.Abp.Hangfire.Storage.PostgreSql\Sanhe.Abp.Hangfire.Storage.PostgreSql.csproj", "{278CB228-3993-4F84-946B-1517887E0183}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.PinyinConverter", "modules\common\Sanhe.Abp.PinyinConverter\Sanhe.Abp.PinyinConverter.csproj", "{60620380-86CD-46DB-B2DA-CFFBCF2423DB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.PinyinConverter", "modules\common\Sanhe.Abp.PinyinConverter\Sanhe.Abp.PinyinConverter.csproj", "{60620380-86CD-46DB-B2DA-CFFBCF2423DB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.PinyinConverter.ToolGoodWords", "modules\common\Sanhe.Abp.PinyinConverter.ToolGoodWords\Sanhe.Abp.PinyinConverter.ToolGoodWords.csproj", "{EE08FB0C-EDC1-4DEF-B0C8-699F4E7D08AF}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sanhe.Abp.PinyinConverter.ToolGoodWords", "modules\common\Sanhe.Abp.PinyinConverter.ToolGoodWords\Sanhe.Abp.PinyinConverter.ToolGoodWords.csproj", "{EE08FB0C-EDC1-4DEF-B0C8-699F4E7D08AF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.RealTime", "modules\common\Sanhe.Abp.RealTime\Sanhe.Abp.RealTime.csproj", "{C99CF772-210B-4C21-84FE-089B7D038A28}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sanhe.Abp.IdGenerator", "modules\common\Sanhe.Abp.IdGenerator\Sanhe.Abp.IdGenerator.csproj", "{86CDAB3C-D7EC-4F4B-BCB9-2069DAE1D0FC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -145,6 +149,14 @@ Global {EE08FB0C-EDC1-4DEF-B0C8-699F4E7D08AF}.Debug|Any CPU.Build.0 = Debug|Any CPU {EE08FB0C-EDC1-4DEF-B0C8-699F4E7D08AF}.Release|Any CPU.ActiveCfg = Release|Any CPU {EE08FB0C-EDC1-4DEF-B0C8-699F4E7D08AF}.Release|Any CPU.Build.0 = Release|Any CPU + {C99CF772-210B-4C21-84FE-089B7D038A28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C99CF772-210B-4C21-84FE-089B7D038A28}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C99CF772-210B-4C21-84FE-089B7D038A28}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C99CF772-210B-4C21-84FE-089B7D038A28}.Release|Any CPU.Build.0 = Release|Any CPU + {86CDAB3C-D7EC-4F4B-BCB9-2069DAE1D0FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {86CDAB3C-D7EC-4F4B-BCB9-2069DAE1D0FC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {86CDAB3C-D7EC-4F4B-BCB9-2069DAE1D0FC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {86CDAB3C-D7EC-4F4B-BCB9-2069DAE1D0FC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -174,6 +186,8 @@ Global {278CB228-3993-4F84-946B-1517887E0183} = {2A768109-31B7-4C52-928C-3023DAB9F254} {60620380-86CD-46DB-B2DA-CFFBCF2423DB} = {2A768109-31B7-4C52-928C-3023DAB9F254} {EE08FB0C-EDC1-4DEF-B0C8-699F4E7D08AF} = {2A768109-31B7-4C52-928C-3023DAB9F254} + {C99CF772-210B-4C21-84FE-089B7D038A28} = {2A768109-31B7-4C52-928C-3023DAB9F254} + {86CDAB3C-D7EC-4F4B-BCB9-2069DAE1D0FC} = {2A768109-31B7-4C52-928C-3023DAB9F254} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AB69BFDE-9DDB-4D16-8CB8-72472C0319CD} diff --git a/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/README.md b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/README.md index 9aaa515..572e62f 100644 --- a/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/README.md +++ b/modules/common/Sanhe.Abp.ExceptionHandling.Emailing/README.md @@ -34,4 +34,6 @@ }); } } -``` \ No newline at end of file +``` + +[作者colinin,Github](https://github.com/colinin/abp-next-admin) \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.ExceptionHandling/README.md b/modules/common/Sanhe.Abp.ExceptionHandling/README.md index 8698cc6..b027d69 100644 --- a/modules/common/Sanhe.Abp.ExceptionHandling/README.md +++ b/modules/common/Sanhe.Abp.ExceptionHandling/README.md @@ -23,4 +23,6 @@ }); } } -``` \ No newline at end of file +``` + +[作者colinin,Github](https://github.com/colinin/abp-next-admin) \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.Features.LimitValidation/README.md b/modules/common/Sanhe.Abp.Features.LimitValidation/README.md index 1e419d7..c383364 100644 --- a/modules/common/Sanhe.Abp.Features.LimitValidation/README.md +++ b/modules/common/Sanhe.Abp.Features.LimitValidation/README.md @@ -83,3 +83,5 @@ public class YouProjectModule : AbpModule } } ``` + +[作者colinin,Github](https://github.com/colinin/abp-next-admin) \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.Hangfire.Dashboard/README.md b/modules/common/Sanhe.Abp.Hangfire.Dashboard/README.md index 443f5dd..95baacb 100644 --- a/modules/common/Sanhe.Abp.Hangfire.Dashboard/README.md +++ b/modules/common/Sanhe.Abp.Hangfire.Dashboard/README.md @@ -25,3 +25,5 @@ public class YouProjectModule : AbpModule ``` ## 注意事项 + +[作者colinin,Github](https://github.com/colinin/abp-next-admin) \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.Hangfire.Storage.PostgreSql/README.md b/modules/common/Sanhe.Abp.Hangfire.Storage.PostgreSql/README.md new file mode 100644 index 0000000..a10ad5e --- /dev/null +++ b/modules/common/Sanhe.Abp.Hangfire.Storage.PostgreSql/README.md @@ -0,0 +1,19 @@ +# Sanhe.Abp.Hangfire.Dashboard + +Hangfire持久化PostgreSql + + +## 配置使用 +还需要配置中间件,需要在授权中间件后面。 + +```csharp +[DependsOn(typeof(AbpHangfireStoragePostgreSqlModule))] +public class YouProjectModule : AbpModule +{ + +} +``` + +## 注意事项 + +[作者wwwk] \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.IdGenerator/FodyWeavers.xml b/modules/common/Sanhe.Abp.IdGenerator/FodyWeavers.xml new file mode 100644 index 0000000..1715698 --- /dev/null +++ b/modules/common/Sanhe.Abp.IdGenerator/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.IdGenerator/Sanhe.Abp.IdGenerator.csproj b/modules/common/Sanhe.Abp.IdGenerator/Sanhe.Abp.IdGenerator.csproj new file mode 100644 index 0000000..78ba939 --- /dev/null +++ b/modules/common/Sanhe.Abp.IdGenerator/Sanhe.Abp.IdGenerator.csproj @@ -0,0 +1,12 @@ + + + + + + netstandard2.0 + + + + + + diff --git a/modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/AbpIdGeneratorModule.cs b/modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/AbpIdGeneratorModule.cs new file mode 100644 index 0000000..904e52d --- /dev/null +++ b/modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/AbpIdGeneratorModule.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Sanhe.Abp.IdGenerator.Sanhe.Abp.IdGenerator.Snowflake; +using Volo.Abp.Modularity; + +namespace Sanhe.Abp.IdGenerator.Sanhe.Abp.IdGenerator; + +public class AbpIdGeneratorModule: AbpModule +{ + public override void ConfigureServices(ServiceConfigurationContext context) + { + var snowflakeIdOptions = new SnowflakeIdOptions(); + context.Services.ExecutePreConfiguredActions(snowflakeIdOptions); + + context.Services.TryAddSingleton(SnowflakeIdGenerator.Create(snowflakeIdOptions)); + } +} diff --git a/modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/IDistributedIdGenerator.cs b/modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/IDistributedIdGenerator.cs new file mode 100644 index 0000000..15e87c0 --- /dev/null +++ b/modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/IDistributedIdGenerator.cs @@ -0,0 +1,6 @@ +namespace Sanhe.Abp.IdGenerator.Sanhe.Abp.IdGenerator; + +public interface IDistributedIdGenerator +{ + long Create(); +} diff --git a/modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/Snowflake/SnowflakeIdGenerator.cs b/modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/Snowflake/SnowflakeIdGenerator.cs new file mode 100644 index 0000000..c69591c --- /dev/null +++ b/modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/Snowflake/SnowflakeIdGenerator.cs @@ -0,0 +1,133 @@ +using System; +using Volo.Abp; + +namespace Sanhe.Abp.IdGenerator.Sanhe.Abp.IdGenerator.Snowflake; + +// reference: https://github.com/dotnetcore/CAP +// reference: https://blog.csdn.net/lq18050010830/article/details/89845790 +public class SnowflakeIdGenerator : IDistributedIdGenerator +{ + public const long Twepoch = 1288834974657L; + + private static readonly object _lock = new object(); + private long _lastTimestamp = -1L; + + protected long MaxWorkerId { get; set; } + protected long MaxDatacenterId { get; set; } + + protected int WorkerIdShift { get; } + protected int DatacenterIdShift { get; } + protected int TimestampLeftShift { get; } + protected long SequenceMask { get; } + + protected SnowflakeIdOptions Options { get; } + + private SnowflakeIdGenerator(SnowflakeIdOptions options) + { + Options = options; + WorkerIdShift = options.SequenceBits; + DatacenterIdShift = options.SequenceBits + options.WorkerIdBits; + TimestampLeftShift = options.SequenceBits + options.WorkerIdBits + options.DatacenterIdBits; + SequenceMask = -1L ^ -1L << options.SequenceBits; + } + + public static SnowflakeIdGenerator Create(SnowflakeIdOptions options) + { + var idGenerator = new SnowflakeIdGenerator(options) + { + WorkerId = options.WorkerId, + DatacenterId = options.DatacenterId, + Sequence = options.Sequence, + MaxWorkerId = -1L ^ -1L << options.WorkerIdBits, + MaxDatacenterId = -1L ^ -1L << options.DatacenterIdBits + }; + + if (idGenerator.WorkerId == 0 || (int)Math.Log10(options.WorkerId) + 1 > idGenerator.MaxWorkerId) + { + if (!int.TryParse(Environment.GetEnvironmentVariable("WORKERID", EnvironmentVariableTarget.Machine), out var workerId)) + { + workerId = RandomHelper.GetRandom((int)idGenerator.MaxWorkerId); + } + + if (workerId > idGenerator.MaxWorkerId || workerId < 0) + { + throw new ArgumentException($"worker Id can't be greater than {idGenerator.MaxWorkerId} or less than 0"); + } + + idGenerator.WorkerId = workerId; + } + + if (idGenerator.DatacenterId == 0 || (int)Math.Log10(options.DatacenterId) + 1 > idGenerator.MaxDatacenterId) + { + if (!int.TryParse(Environment.GetEnvironmentVariable("DATACENTERID", EnvironmentVariableTarget.Machine), out var datacenterId)) + { + datacenterId = RandomHelper.GetRandom((int)idGenerator.MaxDatacenterId); + } + + if (datacenterId > idGenerator.MaxDatacenterId || datacenterId < 0) + { + throw new ArgumentException($"datacenter Id can't be greater than {idGenerator.MaxDatacenterId} or less than 0"); + } + + idGenerator.DatacenterId = datacenterId; + } + + return idGenerator; + } + + public long WorkerId { get; internal set; } + public long DatacenterId { get; internal set; } + public long Sequence { get; internal set; } + + public virtual long Create() + { + lock (_lock) + { + var timestamp = TimeGen(); + + if (timestamp < _lastTimestamp) + { + // 如果启用此选项, 发生时间回退时使用上一个时间戳 + if (!Options.UsePreviousInTimeRollback) + { + throw new Exception( + $"InvalidSystemClock: Clock moved backwards, Refusing to generate id for {_lastTimestamp - timestamp} milliseconds"); + } + timestamp = _lastTimestamp; + } + + if (_lastTimestamp == timestamp) + { + Sequence = Sequence + 1 & SequenceMask; + if (Sequence == 0L) + { + timestamp = TilNextMillis(_lastTimestamp); + } + } + else + { + Sequence = 0; + } + + _lastTimestamp = timestamp; + var id = timestamp - Twepoch << TimestampLeftShift | + DatacenterId << DatacenterIdShift | + WorkerId << WorkerIdShift | + Sequence; + + return id; + } + } + + protected virtual long TilNextMillis(long lastTimestamp) + { + var timestamp = TimeGen(); + while (timestamp <= lastTimestamp) timestamp = TimeGen(); + return timestamp; + } + + protected virtual long TimeGen() + { + return DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); + } +} diff --git a/modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/Snowflake/SnowflakeIdOptions.cs b/modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/Snowflake/SnowflakeIdOptions.cs new file mode 100644 index 0000000..a6aa7e9 --- /dev/null +++ b/modules/common/Sanhe.Abp.IdGenerator/Sanhe/Abp/IdGenerator/Snowflake/SnowflakeIdOptions.cs @@ -0,0 +1,40 @@ +namespace Sanhe.Abp.IdGenerator.Sanhe.Abp.IdGenerator.Snowflake; + +public class SnowflakeIdOptions +{ + /// + /// 机器Id + /// + public int WorkerId { get; set; } + /// + /// 机器Id长度 + /// + public int WorkerIdBits { get; set; } + /// + /// 数据中心Id + /// + public int DatacenterId { get; set; } + /// + /// 数据中心Id长度 + /// + public int DatacenterIdBits { get; set; } + /// + /// 12bit 的序号 + /// + public long Sequence { get; set; } + + public int SequenceBits { get; set; } + /// + /// 发生时间回退时使用上一个ID + /// + public bool UsePreviousInTimeRollback { get; set; } + + public SnowflakeIdOptions() + { + WorkerIdBits = 5; + DatacenterIdBits = 5; + Sequence = 0L; + SequenceBits = 12; + UsePreviousInTimeRollback = true; + } +} diff --git a/modules/common/Sanhe.Abp.Localization.Dynamic/README.md b/modules/common/Sanhe.Abp.Localization.Dynamic/README.md index b6e6578..7014bb3 100644 --- a/modules/common/Sanhe.Abp.Localization.Dynamic/README.md +++ b/modules/common/Sanhe.Abp.Localization.Dynamic/README.md @@ -35,4 +35,6 @@ public class YouProjectModule : AbpModule 动态资源在启动时加载,如果通过LocalizationManagement模块查询,可能受后端存储资源体量影响整体启动时间 -详情见: [DynamicLocalizationInitializeService](./Sanhe/Abp/Localization/Dynamic/DynamicLocalizationInitializeService.cs#L25-L38) \ No newline at end of file +详情见: [DynamicLocalizationInitializeService](./Sanhe/Abp/Localization/Dynamic/DynamicLocalizationInitializeService.cs#L25-L38) + +[作者colinin,Github](https://github.com/colinin/abp-next-admin) \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.RealTime/FodyWeavers.xml b/modules/common/Sanhe.Abp.RealTime/FodyWeavers.xml new file mode 100644 index 0000000..1715698 --- /dev/null +++ b/modules/common/Sanhe.Abp.RealTime/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/modules/common/Sanhe.Abp.RealTime/Sanhe.Abp.RealTime.csproj b/modules/common/Sanhe.Abp.RealTime/Sanhe.Abp.RealTime.csproj new file mode 100644 index 0000000..b24d631 --- /dev/null +++ b/modules/common/Sanhe.Abp.RealTime/Sanhe.Abp.RealTime.csproj @@ -0,0 +1,15 @@ + + + + + + + netstandard2.0 + + + + + + + + diff --git a/modules/common/Sanhe.Abp.RealTime/Sanhe/Abp/RealTime/AbpRealTimeModule.cs b/modules/common/Sanhe.Abp.RealTime/Sanhe/Abp/RealTime/AbpRealTimeModule.cs new file mode 100644 index 0000000..c6ee508 --- /dev/null +++ b/modules/common/Sanhe.Abp.RealTime/Sanhe/Abp/RealTime/AbpRealTimeModule.cs @@ -0,0 +1,9 @@ +using Volo.Abp.EventBus.Abstractions; +using Volo.Abp.Modularity; + +namespace Sanhe.Abp.RealTime.Sanhe.Abp.RealTime; + +[DependsOn(typeof(AbpEventBusAbstractionsModule))] +public class AbpRealTimeModule : AbpModule +{ +} diff --git a/modules/common/Sanhe.Abp.RealTime/Sanhe/Abp/RealTime/Localization/LocalizableStringInfo.cs b/modules/common/Sanhe.Abp.RealTime/Sanhe/Abp/RealTime/Localization/LocalizableStringInfo.cs new file mode 100644 index 0000000..bfe2de0 --- /dev/null +++ b/modules/common/Sanhe.Abp.RealTime/Sanhe/Abp/RealTime/Localization/LocalizableStringInfo.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; + +namespace Sanhe.Abp.RealTime.Localization; + +/// +/// The notification that needs to be localized +/// +public class LocalizableStringInfo +{ + /// + /// Resource name + /// + public string ResourceName { get; } + /// + /// Properties + /// + public string Name { get; } + /// + /// Formatted data + /// + public Dictionary Values { get; } + + /// + /// Instantiate + /// + /// Resource name + /// Properties + /// Formatted data + public LocalizableStringInfo( + string resourceName, + string name, + Dictionary values = null) + { + ResourceName = resourceName; + Name = name; + Values = values; + } +} diff --git a/modules/common/Sanhe.Abp.RealTime/Sanhe/Abp/RealTime/RealTimeEto.cs b/modules/common/Sanhe.Abp.RealTime/Sanhe/Abp/RealTime/RealTimeEto.cs new file mode 100644 index 0000000..0608388 --- /dev/null +++ b/modules/common/Sanhe.Abp.RealTime/Sanhe/Abp/RealTime/RealTimeEto.cs @@ -0,0 +1,19 @@ +using System; +using Volo.Abp.Domain.Entities.Events.Distributed; +using Volo.Abp.EventBus; + +namespace Sanhe.Abp.RealTime.Sanhe.Abp.RealTime; + +[Serializable] +[GenericEventName(Prefix = "abp.realtime.")] +public class RealTimeEto : EtoBase +{ + public T Data { get; set; } + + public RealTimeEto() : base() { } + + public RealTimeEto(T data) : base() + { + Data = data; + } +}