CCITU.Middleware 2.2.6

dotnet add package CCITU.Middleware --version 2.2.6                
NuGet\Install-Package CCITU.Middleware -Version 2.2.6                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CCITU.Middleware" Version="2.2.6" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add CCITU.Middleware --version 2.2.6                
#r "nuget: CCITU.Middleware, 2.2.6"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install CCITU.Middleware as a Cake Addin
#addin nuget:?package=CCITU.Middleware&version=2.2.6

// Install CCITU.Middleware as a Cake Tool
#tool nuget:?package=CCITU.Middleware&version=2.2.6                

一、ABP.Authentication身份认证

使用场景:

用于登录授权,接口鉴权。

依赖包:已安装

Install-Package JWT -Version 10.0.2

配置:放入appsettings.json文件里

{
    "Authentication": {
        "SecretKey": "用户授权、鉴权的密钥",
        "TokenEffectiveMinutes": Token有效分钟数,
        "RefreshTokenMinutes": Token在过期多少分钟前刷新Token
    }
}

初始化:

1. 注册ABP依赖模块:AbpAuthenticationModule
[DependsOn(typeof(AbpAuthenticationModule))]
public class AbpTestModule:AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        var services = context.Services;
        //如果已经在appsettings.json文件里填了配置信息,这里可以不用调用,两种初始化方式用一种就好
        services.AddAbpAuthentication(options =>
        {
            options.SecretKey = "";//用户授权、鉴权的密钥
            options.TokenEffectiveMinutes = 30;//Token有效分钟数
            options.RefreshTokenMinutes = 5;//Token在过期多少分钟前刷新Token
        });
    }
}

过滤器:

AuthenticationAttribute,用于对请求头部的Token进行校验
1. 鉴权
2. 有效期验证
3. 当Token即将过期刷新Token,刷新的Token会放到响应头部中,Key=NewToken
4. 将授权数据写入HttpContext.Items.Add("Auth_Data", "授权数据");

扩展方法:

1. 获取授权数据:string GetAuthenticationData(this HttpContext context)

使用说明:

1. 引入依赖注入实例IAuthenticationService
public interface IAuthenticationService
{
    /// <summary>
    /// 授权
    /// </summary>
    /// <param name="data">授权数据:用于存储跟授权用户相关的数据</param>
    /// <returns>返回Token</returns>
    string Authorization(Dictionary<string, object> data);
    /// <summary>
    /// 鉴权
    /// </summary>
    /// <param name="token">访问令牌</param>
    /// <returns>授权用户相关的数据</returns>
    string Authentication(string token);
    /// <summary>
    /// 是否刷新Token
    /// </summary>
    /// <param name="expTime">token过期时间</param>
    /// <returns></returns>
    bool IsRefreshToke(DateTime expTime);
}

二、ABP.DataEncryption数据加密

使用场景:

用于调用接口时,对请求参数加密处理

依赖包:已安装

Install-Package Portable.BouncyCastle -Version 1.9.0

初始化:

1. 注册ABP依赖模块:AbpDataEncryptionModule
2. 在配置服务容器中添加以下代码
public override void ConfigureServices(ServiceConfigurationContext context)
{
    var services = context.Services;
    services.AddDataEncryption(options =>
    {
        options.RSAPublic = "RSA加密公钥";
        options.RSAPrivate = "RSA加密私钥";
    });
}

前端:

1. RsaPublicKey=获取公钥
2. AesKey=随机生成一个字符串作为对称加密的密钥,16个字符
3. 用AesKey对请求数据进行对称加密,加密后的密文放到请求body里
4. 用公钥RsaPublicKey对密钥AesKey进行非对称加密,加密后的密文放到请求头部里,key=AesKey
5. 请求头部的数据类型=Aes,Content-Type=Aes

后端:

1. 在需要加密的实体类上面加上注解[ModelBinder(BinderType = typeof(DataEncryption.DataEncryptionModelBinder))]
2. 在接口参数左边加上参数注解[FromBody]

三、ABP.DataValidation数据验证

初始化:

1. 注册ABP依赖模块:AbpModelStateValidatorModule

基于数据注解验证:

Required:指定属性不能为空
Range:指定数值属性的范围
RegularExpression:指定属性的正则表达式
MaxLength:指定字符串属性的最大长度
MinLength:指定字符串属性的最小长度
DataType:指定属性的数据类型,例如日期、电子邮件或 URL 等
Compare:指定要比较的属性名称,例如两次输入密码是否一致等

自定义数据注解验证:

NotContainChinese:不包含中文
NotContainSpecialCharacter:不包含特殊字符
NotContainNumber:不包含数字

四、ABP.Hangfire任务调度

使用场景:

用于定时任务,在指定的周期时间内,调用指定的接口

依赖包:已安装

Install-Package Hangfire -Version 1.8.1
Install-Package Hangfire.Dashboard.BasicAuthorization -Version 1.0.2
Install-Package Hangfire.HttpJob -Version 3.7.6
Install-Package Hangfire.HttpJob.Client -Version 1.2.9
Install-Package Hangfire.Redis.StackExchange -Version 1.8.7

配置:放入appsettings.json文件里

{
    "Hangfire": {
        "ConnectionString": "数据库连接字符串",
        "DbType": 2, //数据库类型,1=SqlServer|2=Redis,默认为SqlServer
        "DefaultDatabase": 10, //如果使用的是Redis,可以指定默认使用的数据库,不指定为0
        "UserName": "后台管理授权账号",
        "Password": "后台管理授权密码"
    }
}

初始化:

1. 注册ABP依赖模块:AbpHangfireModule
2. 在配置服务容器中添加以下代码
public override void ConfigureServices(ServiceConfigurationContext context)
{
    var services = context.Services;
    //如果已经在appsettings.json文件里填了配置信息,这里可以不用调用,两种初始化方式用一种就好
    services.AddAbpHangfire(options =>
    {
        options.ConnectionString = "数据库连接字符串";
        options.DbType = "数据库类型,默认为SqlServer";
        options.DefaultDatabase = "如果使用的是Redis,可以指定使用的数据库,默认为0";
        options.UserName = "登录账号";
        options.Password = "登录密码";
    });
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
    var app = context.GetApplicationBuilder();
    //如果已经在appsettings.json文件里填了配置信息,这里可以不用调用,两种初始化方式用一种就好
    app.UseAbpHangfireDashboard();
}

后台管理:路由地址/hangfire

1. 添加定时任务
2. 查看定时任务执行结果

五、ABP.LogDashboard日志面板

使用场景:

用于记录日志、查看日志

依赖包:已安装

Install-Package LogDashboard -Version 1.4.8
Install-Package Serilog.AspNetCore -Version 6.1.0

配置:放入appsettings.json文件里

{
    "LogDashboard": {
        "UserName": "后台管理授权账号",
        "Password": "后台管理授权密码"
    }
}

初始化:

1. 注册ABP依赖模块:AbpLogDashboardModule

过滤器:

ApiLogFilterAttribute 记录接口请求响应日志
ErrorLogFilterAttribute 记录系统异常日志

自定义属性:

NotRecordApiLog 不记录日志
NotRecordApiRequestLog 不记录接口请求日志
NotRecordApiResponseLog 不记录接口响应日志

后台管理:路由地址/logdashboard

1. 查看日志

使用说明:

1. 引入依赖注入实例ILogger
记录日志使用方式跟netcore框架自带的ILogger一样

六、ABP.RabbitMQ消息队列

依赖包:已安装

Install-Package RabbitMQ.Client -Version 6.5.0

配置:放入appsettings.json文件里

{
    "RabbitMQ": {
        "Host": "主机地址",
        "Port": 端口号,
        "UserName": "账号",
        "PassWord": "密码",
        "VirtualHost": "/",
        "ChannelOptions":{
            "PrefetchCount":每个消费者一次能预取的消息数量,数值越大内存占用越高
        }
    }
}

初始化:

1. 注册ABP依赖模块:AbpRabbitMQModule

使用说明:

1. 引入依赖注入实例IRabbitMQService
/// <summary>
/// MQ消息队列服务
/// </summary>
public interface IRabbitMQService
{
    /// <summary>
    /// 发送消息
    /// </summary>
    /// <param name="queueName">队列名称</param>
    /// <param name="msg">消息</param>
    void Publish(string queueName, string msg);
    /// <summary>
    /// 发送消息
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="queueName"></param>
    /// <param name="msg"></param>
    void Publish<T>(string queueName, T msg);
    /// <summary>
    /// 消费消息
    /// </summary>
    /// <param name="queueName">队列名称</param>
    /// <param name="consumeFunc">消息回调函数:返回true消费成功,返回false消费失败</param>
    /// <param name="maxRetryCount">消费失败最大重试次数</param>
    /// <returns></returns>
    string Consume(string queueName, Func<string, bool> consumeFunc, int maxRetryCount = 3);
    /// <summary>
    /// 消费消息
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="queueName"></param>
    /// <param name="consumeFunc"></param>
    /// <param name="maxRetryCount"></param>
    /// <returns></returns>
    string Consume<T>(string queueName, Func<T, bool> consumeFunc, int maxRetryCount = 3);
}

七、ABP.Redis缓存

依赖包:已安装

Install-Package StackExchange.Redis -Version 2.6.104

配置:放入appsettings.json文件里

{
    "Redis": {
        "ConnectionString": "Redis数据库连接字符串",
        "DefaultDatabase": 0,//默认使用的数据库,不指定为0
        "Enable": true,//是否启用,如果不启用会用内存缓存
        "LockType": 1,//锁类型:1=线程锁,2=Redis分布式锁。默认值=1
    }
}

初始化:

1. 注册ABP依赖模块:AbpRedisModule

缓存使用说明:

1. 引入依赖注入实例ICacheService
/// <summary>
/// 缓存服务
/// </summary>
public interface ICacheService
{
    /// <summary>
    /// 添加对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <param name="expiry">过期时间</param>
    /// <param name="dbIndex">数据库索引,如果为null则使用配置文件里的DefaultDatabase</param>
    /// <returns></returns>
    bool Add<T>(string key, T value, TimeSpan? expiry = null, int? dbIndex = null);
    /// <summary>
    /// 添加字符串
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <param name="expiry"></param>
    /// <param name="dbIndex"></param>
    /// <returns></returns>
    bool AddString(string key, string value, TimeSpan? expiry = null, int? dbIndex = null);
    /// <summary>
    /// 获取对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <param name="dbIndex"></param>
    /// <returns></returns>
    T Get<T>(string key, int? dbIndex = null);
    /// <summary>
    /// 获取字符串
    /// </summary>
    /// <param name="key"></param>
    /// <param name="dbIndex"></param>
    /// <returns></returns>
    string GetString(string key, int? dbIndex = null);
    /// <summary>
    /// 移除缓存
    /// </summary>
    /// <param name="key"></param>
    /// <param name="dbIndex"></param>
    /// <returns></returns>
    bool Remove(string key, int? dbIndex = null);
    /// <summary>
    /// 搜索key
    /// </summary>
    /// <param name="pattern">搜索关键字</param>
    /// <param name="dbIndex"></param>
    /// <returns></returns>
    IEnumerable<string> GetAllKey(string pattern, int? dbIndex = null);
    /// <summary>
    /// 缓存是否存在
    /// </summary>
    /// <param name="key"></param>
    /// <param name="dbIndex"></param>
    /// <returns></returns>
    bool Exists(string key, int? dbIndex = null);
    /// <summary>
    /// 设置缓存过期时间
    /// </summary>
    /// <param name="key"></param>
    /// <param name="expiry"></param>
    /// <param name="dbIndex"></param>
    /// <returns></returns>
    bool KeyExpire(string key, TimeSpan? expiry, int? dbIndex = null);
}

锁使用说明:

1. 引入依赖注入实例ILockService
/// <summary>
/// 锁
/// </summary>
public interface ILockService
{
    /// <summary>
    /// 加锁:回调函数中的代码会串行
    /// </summary>
    /// <param name="lockKey">锁的名称:相同的名称视为同一个锁</param>
    /// <param name="action">回调函数</param>
    /// <param name="secondsTimeout">超时时间:默认5分钟</param>

    T ActionLock<T>(string lockKey, Func<T> action, int secondsTimeout = 300);
    Task<T> ActionLockAsync<T>(string lockKey, Func<Task<T>> action, int secondsTimeout = 300);
}
2. 使用案例
var result = lockService.ActionLock("锁的名称:相同的名称视为同一个锁", () =>
{
    //业务代码执行中
    Thread.Sleep(5000);
    return true;
});

自增使用说明:

1. 引入依赖注入实例IAutoIncrementService
/// <summary>
/// 自增
/// </summary>
public interface IAutoIncrementService
{
    /// <summary>
    /// 生成自增ID
    /// </summary>
    /// <param name="key"></param>
    /// <param name="dbIndex"></param>
    /// <param name="secondsTimeout">超时时间:默认3秒</param>
    /// <returns></returns>
    Task<long> GenerateIdAsync(string key, int? dbIndex = null, int secondsTimeout = 3);
}

八、ABP.SqlSugar数据库访问

依赖包:已安装

Install-Package SqlSugarCore -Version 5.1.4.69

配置:放入appsettings.json文件里

{
    "SqlSugar": {
        "ConnConfigList": [
            {
                "Tenant": "租户名称",
                "ConnStr": "数据库连接字符串",
                "DbType": "数据库类型:0=MySql,1=SqlServer,2=Sqlite,默认值为1",
                "IsAutoCloseConnection": "是否自动关闭连接:默认是",
                "IsEnableReadWriteSeparation": "是否启用读写分离:默认不启用",
                "SlaveConnConfigs":[
                    {
                        "HitRate": 权重:数值越高,读到的概率越高,
                        "ConnStr": "数据库连接字符串"
                    }
                ]//从库连接配置集合,用于读写分离
            }
        ],//数据库连接配置集合
        "CommandTimeOut": "执行命令超时时间:默认30秒",
        "IsWithNoLockQuery": "是否脏读:默认值true",
        "IsUtcNow": "是否是Utc时间:默认值false",
        "WorkId":"用于生成雪花ID的:不同的机器不要配置一样的,默认值1"
    }
}

初始化:

1. 注册ABP依赖模块:AbpSqlSugarModule,无需二级缓存的注册AbpSqlSugarNoCacheModule

使用说明:

1.引入依赖注入实例:ISqlSugarService
/// <summary>
/// 数据库访问服务
/// </summary>
public interface ISqlSugarService
{
    /// <summary>
    /// 数据库配置参数
    /// </summary>
    SqlSugarOptions Options { get; }
    /// <summary>
    /// 数据库访问客户端
    /// </summary>
    SqlSugarClient Client { get; }
    /// <summary>
    /// 获取雪花ID
    /// </summary>
    /// <returns></returns>
    long GetSnowFlakeSingle();
}

2.多库实现:
2.1 需要在Do上面加上属性标注:[Tenant("租户名称,对应配置文件里的Tenant,根据值去匹配数据库连接字符串")]
2.2 增删改查需要使用带WithAttr后缀的方法:例如:QueryableWithAttr、InsertableWithAttr

3.读写分离:主库负责增删改,从库负责读
3.1 启用读写分离的三种方式:默认是不启用的
3.1.1 修改配置文件IsEnableReadWriteSeparation=true(不推荐使用,会影响到所有的代码)
3.1.2 在需要的接口上面添加属性标记[ReadWriteSeparation](推荐使用,影响范围只有这个接口)
3.1.3 用SlaveQueryableWithAttr方法查询数据(合理使用,对于时效性要求不高的查询可以用)
3.2 事务里会强制读主库,如需在事务外读主库用MasterQueryableWithAttr方法查询数据。

仓储使用说明:

应用服务层代码:
public class CfgAppImpl : RepositoryService<CfgAppDo>, IScopedDependency
{
    public CfgAppImpl(ISqlSugarService db, IObjectMapper mapper) : base(db, mapper)
    {
    }
}
展现层调用代码:
public class CfgAppController : ControllerBase
{
    private readonly CfgAppImpl cfgAppImpl;
    public CfgAppController(CfgAppImpl cfgAppImpl)
    {
        this.cfgAppImpl = cfgAppImpl;
    }
    public async Task Add()
    {
        AddOrEditCfgAppRequestDto dto = new AddOrEditCfgAppRequestDto()
        {
            CfgId = 10003,
            Name = "Name3",
            Value = "Value3"
        };
        var result = await cfgAppImpl.InsertAsync(dto);
    }
    public async Task Edit()
    {
        AddOrEditCfgAppRequestDto dto = new AddOrEditCfgAppRequestDto()
        {
            CfgId = 10001,
            Name = "Name11",
            Value = "Value11"
        };
        var result = await cfgAppImpl.UpdateAsync(dto);
    }
    public async Task Del()
    {
        var result = await cfgAppImpl.DeleteAsync(new CfgAppDo() { CfgId = 10001 });
    }
    public async Task ToPageList()
    {
        var result = await cfgAppImpl.ToPageListAsync(new QueryParameters(), 1, 2);
    }
}

分表使用说明:

[SplitTable(SplitType.Month)]
[SugarTable("SplitTestTable_{year}{month}{day}"), Tenant("EDM")]
public class SplitTestTableDo
{
    [SugarColumn(IsPrimaryKey = true)]
    public long Id { get; set; }

    public string Name { get; set; }

    public int Age { get; set; }

    [SplitField] //分表字段 在插入的时候会根据这个字段插入哪个表,在更新删除的时候用这个字段找出相关表
    public DateTime CreateTime { get; set; }
}
增:
//已CreateTime字段按月分表
List<SplitTestTableDo> list = new List<SplitTestTableDo>()
{
    new SplitTestTableDo(){ Id = 1, Name = "Name1", Age = 100, CreateTime = DateTime.Parse("2024-1-3 10:10:10")},
    new SplitTestTableDo(){ Id = 2, Name = "Name2", Age = 200, CreateTime = DateTime.Parse("2024-2-15 11:11:11")},
    new SplitTestTableDo(){ Id = 3, Name = "Name3", Age = 300, CreateTime = DateTime.Parse("2024-2-16 12:12:12")},
    new SplitTestTableDo(){ Id = 4, Name = "Name4", Age = 400, CreateTime = DateTime.Parse("2024-3-15")}
};
var result = db.Client.InsertableWithAttr<SplitTestTableDo>(list).SplitTable().ExecuteCommand();
删:
long id = 3;
DateTime createTime = DateTime.Parse("2024-2-1");
var result = db.Client.DeleteableWithAttr<SplitTestTableDo>()
    .Where(o => o.Id == id)
    //.SplitTable(tas => tas)//不知道该条数据的创建时间是哪个月(分表字段),此写法会把1-3月的表都删除一遍
    .SplitTable(tas =>
    {
        return tas.Where(o => o.Date == createTime);
    })//知道该条数据的创建时间是哪个月会只删除2月的表(SplitTestTable_20240201)
    .ExecuteCommand();
改:
long id = 2;
DateTime createTime = DateTime.Parse("2024-2-1");
var result = db.Client.UpdateableWithAttr<SplitTestTableDo>()
    .SetColumns(o => o.Age == 201)
    .Where(o => o.Id == id)
    //.SplitTable(tas => tas)//不知道该条数据的创建时间是哪个月(分表字段),此写法会把1-3月的表都修改一遍
    .SplitTable(tas =>
    {
        return tas.Where(o => o.Date == createTime);
    })//知道该条数据的创建时间是哪个月会只修改2月的表(SplitTestTable_20240201)
    .ExecuteCommand();
查:
DateTime createTime = DateTime.Parse("2024-1-1");
DateTime startTime = DateTime.Parse("2024-2-3");
DateTime endTime = DateTime.Parse("2024-3-25");
var result = db.Client.QueryableWithAttr<SplitTestTableDo>()
    .Where(o => o.Age >= 200 && o.Age <= 500)
    //.SplitTable()//此写法会把1-3月的表并起来查
    //.SplitTable(startTime, endTime)//此写法会把2-3月的表并起来查,查询2月3号-3月25号的数据
    .SplitTable(tas =>
    {
        return tas.Where(o => o.Date == createTime);
    })//此写法只查1月份的数据
    .ToList();

九、ABP.Swagger接口文档

依赖包:已安装

Install-Package Swashbuckle.AspNetCore -Version 6.5.0
Install-Package Swashbuckle.AspNetCore.Annotations -Version 6.5.0

初始化:

1. 注册ABP依赖模块:AbpSwaggerModule

使用说明:

1. 在浏览器中输入路由地址:/swagger即可查看接口文档

十、ABP.Kafka消息队列

依赖包:已安装

Install-Package Confluent.Kafka -Version 1.9.0
Install-Package protobuf-net -Version 3.1.17

配置:放入appsettings.json文件里

{
    "Kafka": {
        "ProduceBootstrapServers": "生产者 kafka 代理主机:端口",
        "ConsumeBootstrapServers": "消费者 kakfa 代理主机:端口",
        "UserName":"认证账号",
        "Password":"认证密码"
    }
}

初始化:

1. 注册ABP依赖模块:AbpKafkaModule

使用说明:

1. 拥入依赖注入实例:ICommonConfulentKafka
public interface ICommonConfulentKafka
{
    /// <summary>
    /// 获取主题分区数
    /// </summary>
    /// <param name="topicName">主题</param>
    /// <returns></returns>
    int? GetPartitionsByTopic(string topicName);

    /// <summary>
    /// 删除标题
    /// </summary>
    /// <param name="topicName">标题名称</param>
    /// <returns></returns>
    Task DeleteTopicAsync(string topicName);
    /// <summary>
    /// 创建标题
    /// </summary>
    /// <param name="topicName">标题名称</param>
    /// <returns></returns>
    Task CreateTopicAsync(string topicName);

    /// <summary>
    /// 生产者 发送消息
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    /// <param name="topicName">标题</param>
    /// <param name="msg">消息内容</param>
    /// <returns></returns>
    Task  ProduceMsgAsync<T>(string topicName, T msg);

    /// <summary>
    /// 消费者  接收消息
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    /// <param name="topics">标题类型</param>
    /// <param name="group">组名</param>
    /// <returns></returns>
    IConsumer<Ignore, T> ConsumeMsg<T>(string group);


    /// <summary>
    /// 生产者 发送Proto消息
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    /// <param name="topicName">标题</param>
    /// <param name="msg">消息内容</param>
    /// <returns></returns>
    Task ProduceProtoBufMsgAsync<T>(string topicName, T msg);

    /// <summary>
    /// 消费者  接收Proto消息
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    /// <param name="topics">标题类型</param>
    /// <param name="group">组名</param>
    /// <returns></returns>
    IConsumer<Ignore, T> ConsumeProtoBufMsg<T>(string group);
}

十一、ABP.Mail邮件服务

配置:放入appsettings.json文件里

{
    "Mail": {
        "Host": "邮件服务商主机地址",
        "Port": "邮件服务商主机端口号",
        "SenderEmail": "发件人邮箱",
        "SenderPwd": "发件人密码",
        "SenderName": "发件人名称",
        "WriteBackEmail": "回信邮箱"
    }
}

初始化:

1. 注册ABP依赖模块:AbpMailModule

使用说明:

1.引入依赖注入实例:IMailService
/// <summary>
/// 邮件服务
/// </summary>
public interface IMailService
{
    /// <summary>
    /// 发送邮件
    /// </summary>
    /// <param name="sendMailOptions"></param>
    void Send(SendMailOptions sendMailOptions);
}
发送邮件参数说明SendMailOptions:
AddresseeEmail:收件人邮箱。
Subject:主题。
Contents:内容集合
    ContentType:内容类型text/html|text/plain|text/richtext|text/xml
    更多类型查看MediaTypeNames常量。
    Content:内容文本。
    ContentEncoding:内容编码。
Attachments:附件集合。

十二、ABP.SMS短信服务

依赖包:已安装

阿里云短信服务开发包
Install-Package AlibabaCloud.SDK.Dysmsapi20170525 -Version 2.0.23

配置:放入appsettings.json文件里

{
    "SmsOptions": {
        "AccessKeyId": "账号ID",
        "AccessKeySecret": "api访问密钥"
    }//短信平台参数
}

初始化:

1. 注册ABP依赖模块:AbpSmsModule

使用说明:

1.引入依赖注入实例:ISmsService
/// <summary>
/// 短信服务
/// </summary>
public interface ISmsService
{
    /// <summary>
    /// 发送短信
    /// </summary>
    /// <param name="sendSmsOptions"></param>
    /// <returns></returns>
    bool Send(SendSmsOptions sendSmsOptions);
}
发送短信参数说明SendSmsOptions:
PhoneNumbers:接收人手机号码。
SignName:签名,显示在短信前面,需要去短信平台申请。
TemplateCode:模板CODE,需要去短信平台申请。
TemplateParam:模板参数:json对象字符串。

十三、ABP.FileCore文件服务

使用场景:

保存文件、excel导入导出

依赖包:已安装

Install-Package NPOI -Version 2.6.0

初始化:

1. 注册ABP依赖模块:AbpFileCoreModule
2. 在配置服务容器中添加以下代码
public override void ConfigureServices(ServiceConfigurationContext context)
{
    var services = context.Services;
    services.AddFileCore(options =>
    {
        options.AllowFileExts = "允许的文件扩展名集合";
        options.LimitMaxSize = "文件最大长度:单位字节";
        options.StorageRootDir = "存储文件根目录";
        options.MappingUrlPath = "访问文件url地址根路径";
    });
}

属性说明:

导入Excel:
导入列*:[Display(Name = "列名")]
忽略导入列:[IgnoreImportColumn]
必填列:[Required(ErrorMessage = "{0}不能为空")],更多属性参考netcore数据注解模型验证

导出Excel:
导出列*:[Display(Name = "列名", Order = 排序:非必填,不填按属性从上到下排)]
忽略导出列:[IgnoreColumn]
行高:[RowHeight(表头行高, 数据列行高)]
列宽:[HeaderStyle(ColumnSize = 单位:字符个数)]
表头字体颜色:[HeaderFont(Color = HSSFColor.Pink.Index)]
数据列字体颜色:[DataFont(Color = HSSFColor.Red.Index)]
数据列样式:[DataStyle(DataFormat = "yyyy-MM-dd HH:mm")]
数据列求和:[ColumnStats((int)FunctionEnum.Sum)]
数据列求平均值:[ColumnStats((int)FunctionEnum.Avg, OffsetRow = 3)]

使用说明:

1.引入依赖注入实例:IFileService
/// <summary>
/// 文件服务
/// </summary>
public interface IFileService
{
    /// <summary>
    /// 保存文件
    /// </summary>
    /// <param name="fileData">文件数据</param>
    /// <param name="fileName">文件名称</param>
    /// <param name="fileRelativeDir">文件的相对目录,会跟存储文件的根目录拼接起来</param>
    /// <returns></returns>
    SaveFileResultDto SaveFile(byte[] fileData, string fileName, string fileRelativeDir = "");
}
保存文件返回值说明SaveFileResultDto:
FileDir:文件完整存储目录
Url:访问文件的url地址
2.引入依赖注入实例:IExcelService
/// <summary>
/// Excel导入导出
/// </summary>
public interface IExcelService
{
    /// <summary>
    /// 导出
    /// </summary>
    /// <typeparam name="TExportDto"></typeparam>
    /// <param name="data">数据集合</param>
    /// <param name="fileName">文件名称</param>
    /// <param name="fileRelativeDir">文件的相对目录,如果传了会跟存储文件的根目录拼接起来</param>
    /// <returns></returns>
    Task<ExportFileResultDto> ExportAsync<TExportDto>(List<TExportDto> data, string fileName, string fileRelativeDir = "") where TExportDto : class, new();
    /// <summary>
    /// 导出:支持复杂表头合并、动态表头
    /// </summary>
    /// <param name="data">导出信息</param>
    /// <param name="fileName">文件名称</param>
    /// <param name="fileRelativeDir">文件的相对目录,如果传了会跟存储文件的根目录拼接起来</param>
    /// <param name="isUnique"></param>
    /// <returns></returns>
    Task<ExportFileResultDto> ExportAsync(ExportInfoDto data, string fileName, string fileRelativeDir = "Export");
    /// <summary>
    /// 导入
    /// </summary>
    /// <typeparam name="TImportDto"></typeparam>
    /// <param name="filePath">导入的文件路径</param>
    /// <param name="importFunc">导入成功回调函数</param>
    /// <param name="fileName">文件名称</param>
    /// <param name="fileRelativeDir">文件的相对目录,会跟存储文件的根目录拼接起来</param>
    /// <returns></returns>
    Task<ImportFileResultDto> ImportAsync<TImportDto>(string filePath, Func<IEnumerable<TImportDto>, IEnumerable<TImportDto>> importFunc, string fileName, string fileRelativeDir = "") where TImportDto : ImportItemDto, new();
}

导出返回值说明:ExportFileResultDto:
FileDir:文件完整存储目录
FileData:文件数据:字节数组
Url:访问文件的url地址

导入返回值说明:ImportFileResultDto:
FileDir:导入结果文件完整存储目录
FileData:导入结果文件数据:字节数组
Url:导入结果文件的url地址
Total:导入总记录数
SuccessCount:导入成功记录数
FailCount:导入失败记录数

导入输入参数说明:ExportInfoDto
SheetName:表名
HeaderList:表头集合
DataList:表体集合
AutoMerge:自动合并表头

使用案例:导出复杂表头合并、动态表头

合并前: <table border="1"> <tr> <td>序号1</td> <td>单位名称</td> <td>合计</td> <td>合计</td> <td>合计</td> <td>动态列1</td> <td>动态列2</td> </tr> <tr> <td>序号2</td> <td>单位名称</td> <td>合计A</td> <td>合计B</td> <td>合计C</td> <td>动态列1</td> <td>动态列2</td> </tr> <tr> <td>序号3</td> <td>单位信息</td> <td>合计A</td> <td>合计B</td> <td>合计C</td> <td>动态列1</td> <td>动态列2</td> </tr> </table> 合并后: <table border="1"> <tr> <td>序号1</td> <td rowspan="2">单位名称</td> <td colspan="3" align="center">合计</td> <td rowspan="3">动态列1</td> <td rowspan="3">动态列2</td> </tr> <tr> <td>序号2</td> <td rowspan="2">合计A</td> <td rowspan="2">合计B</td> <td rowspan="2">合计C</td> </tr> <tr> <td>序号3</td> <td>单位信息</td> </tr> </table>

1.导出:支持复杂表头合并、动态表头
//表头集合
var headerList = new List<List<CellInfo>>
{
    new List<CellInfo> 
    {
        new CellInfo()
        {
            Name = "序号1",//列名
            HorizontalAlignment = HorizontalAlignment.Center,//水平对齐
            VerticalAlignment = VerticalAlignment.Center,//垂直对齐
            FontColor = IndexedColors.Black.Index,//字体颜色
            FontHeightInPoints = 11,//字体大小
            FillForegroundColor = IndexedColors.White.Index,//填充背景颜色
            FillPattern = FillPattern.NoFill//填充模式
        },
        new CellInfo(){Name = "单位名称"},
        new CellInfo(){Name = "合计"},
        new CellInfo(){Name = "合计"},
        new CellInfo(){Name = "合计"},
        new CellInfo(){Name = "动态列1"},
        new CellInfo(){Name = "动态列2"}
    },
    new List<CellInfo>
    {
        new CellInfo(){Name = "序号2"},
        new CellInfo(){Name = "单位名称"},
        new CellInfo(){Name = "合计A"},
        new CellInfo(){Name = "合计B"},
        new CellInfo(){Name = "合计C"},
        new CellInfo(){Name = "动态列1"},
        new CellInfo(){Name = "动态列2"}
    },
    new List<CellInfo>
    {
        new CellInfo(){Name = "序号3"},
        new CellInfo(){Name = "单位信息"},
        new CellInfo(){Name = "合计A"},
        new CellInfo(){Name = "合计B"},
        new CellInfo(){Name = "合计C"},
        new CellInfo(){Name = "动态列1"},
        new CellInfo(){Name = "动态列2"}
    }
};
//表体集合
var dataList = new DataTable();
dataList.Columns.Add("column1", typeof(int));//列名可以不跟上面列名保持一致,但是顺序必须保持一致
dataList.Columns.Add("单位名称", typeof(string));
dataList.Columns.Add("合计A", typeof(int));
dataList.Columns.Add("合计B", typeof(int));
dataList.Columns.Add("合计C", typeof(int));
dataList.Columns.Add("动态列1", typeof(int));
dataList.Columns.Add("动态列2", typeof(int));
//添加测试数据
dataList.Rows.Add(1, "单位111", 86, 13, 45, 11, 12);
dataList.Rows.Add(2, "单位222", 23, 453, 234, 21, 22);
//导出
var exportInfo = new ExportInfoDto()
{
    SheetName = "复杂表头合并、动态表头",
    HeaderList = headerList,
    DataList = dataList,
    AutoMerge = true//自动合并表头
};
var result = await excelService.ExportAsync(exportInfo, "导出的Excel.xlsx");

十四、ABP.PaymentCore在线支付

使用场景:

用于空中云汇支付、支付宝支付

依赖包:已安装

Install-Package RestSharp -Version 106.13.0

初始化:

1. 注册ABP依赖模块:AbpPaymentCoreModule
2. 在配置服务容器中添加以下代码
public override void ConfigureServices(ServiceConfigurationContext context)
{
    var services = context.Services;
    //添加空中云汇支付
    services.AddAirwallexPayment(options =>
    {
        options.ClientId = "客户ID:第三方支付平台提供";
        options.ApiKey = "调用Api密钥:第三方支付平台提供";
        options.GetTokenUrl = "获取Token接口地址";
        options.PaymentUrl = "支付下单接口地址";
        options.IsRecordRequestMessage = "是否记录请求报文:默认值为true";
        options.CacheKeyPrefix = "缓存key前缀:默认值为Airwallex_";
    });
}

使用说明:

1.引入依赖注入实例:IPaymentService
/// <summary>
/// 支付服务
/// </summary>
public interface IPaymentService
{
    /// <summary>
    /// 开始支付:向第三方支付平台下单
    /// </summary>
    /// <param name="request"></param>
    /// <param name="paymentType"></param>
    /// <returns></returns>
    DirectPayResponse BeginPay(DirectPayRequest request, PaymentTypeEnum paymentType);
    /// <summary>
    /// 支付完成:解析第三方平台支付成功信息
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    Task<DirectPayResult> PayComplete(HttpContext context);
    /// <summary>
    /// 支付完成
    /// </summary>
    /// <param name="packet"></param>
    /// <returns></returns>
    DirectPayResult PayComplete(HttpPacket packet);
}
1.支付下单参数说明DirectPayRequest:
TradeId:唯一交易ID
TradeName:订单编号
Amount:支付金额
Currency:支付币种

2.支付下单返回值说明DirectPayResponse:
Type:数据类型:由于各平台的支付方式不一样,返回的数据类型也不一样,有的是返回支付链接、有的是返回支付密钥,还有返回支付二维码的
UrlData:支付链接
JsonData:支付密钥
ImageData:二维码数据:字节数组

3.支付成功返回值说明:DirectPayResult
Success:是否支付成功
TradeId:唯一交易ID:我们支付下单的时候提供的
TradeName:订单编号:我们支付下单的时候提供的
PaymentType:支付类型
ThirdPartyTradeId:第三方交易ID
PracticalCurrency:实付币种
PracticalAmount:实付金额

十五、ABP.AutoInject自动依赖注入

使用场景:

1.用于属性依赖注入懒加载,首次访问属性才去容器中创建实例
2.用于依赖注入检查:避免忘记做依赖注入了,导致运行中才报空引用错误!

依赖包:已安装

Install-Package Volo.Abp.Autofac -Version 4.4.4
Install-Package Volo.Abp.AspNetCore -Version 4.4.4

初始化:

1.在Startup启动类加上下面方法
public void ConfigureContainer(ContainerBuilder builder)
{
    //添加自动注入:给依赖注入实例做动态代理,动态代理会通过拦截器来拦截属性,从而实现属性自动懒加载,只在访问属性的时候才去获取依赖注入实例
    builder.AddAutoInject<AppModule>();
}
2. 在配置服务容器中添加以下代码
public override void ConfigureServices(ServiceConfigurationContext context)
{
    var services = context.Services;
    //用于将控制器注入到容器中,以便对控制器做动态代理
    services.AddControllers().AddControllersAsServices();
}
public override void PostConfigureServices(ServiceConfigurationContext context)
{
    var services = context.Services;
    //依赖注入检查:避免忘记做依赖注入了,导致运行中才报空引用错误!
    services.DependencyInjectCheck<AppModule>();
}

使用说明:

1.在Applidation应用服务层使用案例
public class TestC : IScopedDependency, IAutoLazyInject
{
    /// <summary>
    /// 属性懒加载:自动获取依赖注入实例
    /// 要求:
    /// 1.类必须继承IAutoLazyInject接口
    /// 2.属性必须是虚属性
    /// 3.属性必须是public的
    /// 4.属性必须加[AutoLazyInject]标记
    /// 建议:
    /// 1.建议用下划线开头,表示私有,不提倡在类的外部去访问
    /// </summary>
    [AutoLazyInject]
    public virtual TestB _testB { get; }

    public void Test()
    {
        _testB.Test();
    }
}
2.在Api展现层使用案例
跟应用服务层一样,控制器继承IAutoLazyInject接口

十六、ABP.RateLimit接口IP白名单、IP限流

使用场景:

用于接口限流、白名单访问
接口限流github:https://github.com/stefanprodan/AspNetCoreRateLimit

依赖包:已安装

Install-Package AspNetCoreRateLimit -Version 4.0.2

配置:放入appsettings.json文件里

{
    "RateLimit": {
        "IpAccess": {
          //是否启用IP白名单访问
          "EnableIpWhitelistAccess": true,
          //允许访问的IP白名单
          "AccessIpWhitelist": [ "::1" ],
          //是否启用代理服务
          "EnableProxyServer": false,
          //允许访问的代理服务器IP白名单
          "ProxyServerIpWhitelist": [],
          //如果使用代理服务器需要填写,将从该请求头部获取客户端IP地址
          "RealIpHeader": null
        },
        "IpRateLimiting": {
          //false:则全局将应用限制,并且仅应用具有作为端点的规则*。例如,如果您设置每秒5次调用的限制,则对任何端点的任何HTTP调用都将计入该限制
          //true:则限制将应用于每个端点,如{HTTP_Verb}{PATH}。例如,如果您为*:/api/values客户端设置每秒5个呼叫的限制,
          "EnableEndpointRateLimiting": false,
          //false:拒绝的API调用不会添加到调用次数计数器上;如 客户端每秒发出3个请求并且您设置了每秒一个调用的限制,则每分钟或每天计数器等其他限制将仅记录第一个调用,即成功的API调用。
          //true:如果您希望被拒绝的API调用计入其他时间的显示(分钟,小时等),则必须设置StackBlockedRequests为true。
          "StackBlockedRequests": false,
          //应用服务器背后是一个反向代理,如果你的代理服务器使用不同的页眉然后提取客户端IP X-Real-IP使用此选项来设置
          "RealIpHeader": "X-Real-IP",
          //限制状态码
          "QuotaExceededResponse": {
            "Content": "{{\"Success\":false,\"ResultCode\":400,\"Message\":\"Quota exceeded. Maximum allowed: {0} per {1}. Please try again in {2} second(s).\"}}",
            "ContentType": "application/json",
            "StatusCode": 429
          },
          //IP白名单:支持Ip v4和v6 
          //"IpWhitelist": [ "127.0.0.1", "::1/10", "192.168.0.0/24" ],
          //端点白名单
          //"EndpointWhitelist": [ "get:/api/license", "*:/api/status" ],
          //通用规则
          "GeneralRules": [
            {
              //端点路径
              "Endpoint": "*",
              //时间段,格式:{数字}{单位};可使用单位秒分时天:s, m, h, d
              "Period": "5s",
              //限制
              "Limit": 2
            }
          ]
        },
        "IpRateLimitPolicies": {
          //ip规则
          "IpRules": [
            {
              "Ip": "::1",
              "Rules": [
                {
                  "Endpoint": "*",
                  "Period": "5s",
                  "Limit": 3
                }
              ]
            }
          ]
        }
    }
}

初始化:

1. 注册ABP依赖模块:AbpIpWhitelistModule、AbpIpRateLimitModule
[DependsOn(typeof(AbpIpWhitelistModule), typeof(AbpIpRateLimitModule))]
public class AbpTestModule:AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        var services = context.Services;
    }
}

2. 如需单独对某个ip设置限流,需要加上以下代码
public static async Task Main(string[] args)
{
    //CreateHostBuilder(args).Build().Run();

    var webHost = CreateHostBuilder(args).Build();

    using (var scope = webHost.Services.CreateScope())
    {
        // get the IpPolicyStore instance
        var ipPolicyStore = scope.ServiceProvider.GetRequiredService<IIpPolicyStore>();
        // seed IP data from appsettings
        await ipPolicyStore.SeedAsync();
    }

    await webHost.RunAsync();
}
Product Compatible and additional computed target framework versions.
.NET net5.0 is compatible.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2.2.6 124 11/8/2024
2.2.5 138 10/23/2024
2.2.4 132 10/14/2024
2.2.3 106 9/20/2024
2.2.2 131 9/19/2024
2.2.1 86 9/19/2024
2.2.0 122 9/19/2024
2.1.34 122 9/19/2024
2.1.33 153 6/27/2024
2.1.32 101 6/3/2024
2.1.31 147 5/29/2024
2.1.30 117 5/29/2024
2.1.29 114 5/29/2024
2.1.28 111 5/28/2024
2.1.27.2 122 5/16/2024
2.1.27.1 94 5/13/2024
2.1.27 74 5/13/2024
2.1.26 251 2/23/2024
2.1.25 132 2/5/2024
2.1.25-beta 117 2/5/2024
2.1.24 236 2/5/2024
2.1.23 126 1/24/2024
2.1.22 219 1/9/2024
2.1.21 136 1/8/2024
2.1.20 160 12/29/2023
2.1.19 184 12/1/2023
2.1.18 219 11/30/2023
2.1.17 156 11/23/2023
2.1.16 237 11/23/2023
2.1.15 175 11/21/2023
2.1.14 143 11/17/2023
2.1.13 155 11/16/2023
2.1.12 183 11/13/2023
2.1.11 156 11/2/2023
2.1.10 166 10/18/2023
2.1.9 146 9/20/2023
2.1.8 139 9/15/2023
2.1.7 173 9/11/2023
2.1.6 204 9/5/2023
2.1.5 181 8/26/2023
2.1.4 167 8/22/2023
2.1.3 151 8/16/2023
2.1.2 182 8/15/2023
2.1.1 211 8/7/2023
2.1.0 190 8/4/2023
2.0.20 186 8/3/2023
2.0.19 190 8/3/2023
2.0.18 185 7/29/2023
2.0.17 166 7/29/2023
2.0.16 183 7/13/2023
2.0.15 185 7/6/2023
2.0.14 171 7/5/2023
2.0.13 157 6/29/2023
2.0.12 157 6/28/2023
2.0.11 199 6/27/2023
2.0.10 170 6/25/2023
2.0.9 212 6/20/2023
2.0.8 180 6/19/2023
2.0.7 214 6/19/2023
2.0.6 189 6/13/2023
2.0.5 168 6/13/2023
2.0.4 195 6/12/2023
2.0.3 165 6/12/2023
2.0.2 168 6/9/2023
2.0.1 189 6/9/2023
2.0.0 181 6/9/2023
1.0.2 201 6/7/2023
1.0.1 150 6/7/2023
1.0.0 188 6/7/2023