mirror of
https://gitee.com/ccnetcore/Yi
synced 2026-03-19 16:06:36 +08:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
442ae94ad5 | ||
|
|
43820f71a3 | ||
|
|
63920e034a | ||
|
|
cddc47305f | ||
|
|
583bebd10c | ||
|
|
7fe9a6c900 | ||
|
|
670e457dc6 | ||
|
|
300d8224ec | ||
|
|
924f5320bb | ||
|
|
dd09fb2283 | ||
|
|
3e18af626b | ||
|
|
89dd5b79d6 | ||
|
|
356f71f13e | ||
|
|
2f5d71a299 | ||
|
|
b581c12edb | ||
|
|
d4d6aeb0b4 | ||
|
|
77114e6cfc | ||
|
|
e14a078440 | ||
|
|
3b38a0d628 | ||
|
|
927d8cb6c9 |
11
README.md
11
README.md
@@ -23,7 +23,8 @@ Yi框架-一套与SqlSugar一样爽的.Net6低代码开源框架。
|
|||||||
集大成者,终究轮子
|
集大成者,终究轮子
|
||||||
|
|
||||||
|
|
||||||
Yi框架最新版本标签:`v1.1.7`,具体版本可以查看标签迭代
|
Yi框架最新版本标签:`v1.2.0`,具体版本可以查看标签迭代
|
||||||
|
|
||||||
|
|
||||||
(项目与Sqlsugar同步更新,但这作者老杰哥代码天天爆肝到凌晨两点,我们也尽量会跟上他的脚步。更新频繁,所以可watching持续关注。)
|
(项目与Sqlsugar同步更新,但这作者老杰哥代码天天爆肝到凌晨两点,我们也尽量会跟上他的脚步。更新频繁,所以可watching持续关注。)
|
||||||
|
|
||||||
@@ -75,7 +76,7 @@ WebFirst开发:所有代码生成器已经配置完成,无需任何操作数
|
|||||||
|
|
||||||
**身份验证**:JWT、IdentityServer4
|
**身份验证**:JWT、IdentityServer4
|
||||||
|
|
||||||
**组件**:~~EFcore~~SqlSugar、Autofac、Castle、Swagger、Log4Net、Redis、RabbitMq、ES、Quartz.net、~~T4~~
|
**组件**:SqlSugar、Autofac、Castle、Swagger、Log4Net、Redis、RabbitMq、ES、Quartz.net、~~T4~~
|
||||||
|
|
||||||
**分布式**:CAP、Lock
|
**分布式**:CAP、Lock
|
||||||
|
|
||||||
@@ -97,9 +98,8 @@ WebFirst开发:所有代码生成器已经配置完成,无需任何操作数
|
|||||||
- [x] 支持采用`异步`开发awit/async
|
- [x] 支持采用`异步`开发awit/async
|
||||||
- [x] 支持数据库主从`读写分离`
|
- [x] 支持数据库主从`读写分离`
|
||||||
- [x] 支持功能替换,无需改动代码,只需配置`json文件`进行装配即可
|
- [x] 支持功能替换,无需改动代码,只需配置`json文件`进行装配即可
|
||||||
- [x] ~~-支持采用DbFirst开发方式,使用`T4模板代码生成器`,自动映射模型一键生成Service及IService所有代码~~
|
|
||||||
- [x] 支持WebFirst,无需改动代码,自动生成全套代码与数据库,只需点点点
|
- [x] 支持WebFirst,无需改动代码,自动生成全套代码与数据库,只需点点点
|
||||||
- [x] ~~-支持`用户-角色-菜单-接口`以及vue2.0前端全部逻辑代码,下载无需修改直接使用~~
|
- [x] 支持`用户-角色-菜单-接口`以及vue2.0前端全部逻辑代码,下载无需修改直接使用
|
||||||
- [x] 支持`Aop封装`,FilterAop、IocAop、LogAop、SqlAop
|
- [x] 支持`Aop封装`,FilterAop、IocAop、LogAop、SqlAop
|
||||||
- [x] 支持`Log4Net日志`记录,自动生成至bin目录下的logs文件夹
|
- [x] 支持`Log4Net日志`记录,自动生成至bin目录下的logs文件夹
|
||||||
- [x] 支持`DbSeed数据库种子数据`接入
|
- [x] 支持`DbSeed数据库种子数据`接入
|
||||||
@@ -115,7 +115,6 @@ WebFirst开发:所有代码生成器已经配置完成,无需任何操作数
|
|||||||
- [x] 支持`Ocelot`网关,路由、服务聚合、服务发现、认证、鉴权、限流、熔断、缓存、Header头传递
|
- [x] 支持`Ocelot`网关,路由、服务聚合、服务发现、认证、鉴权、限流、熔断、缓存、Header头传递
|
||||||
- [x] 支持`Apollo`全局配置中心;
|
- [x] 支持`Apollo`全局配置中心;
|
||||||
- [x] 支持`docker`镜像制作
|
- [x] 支持`docker`镜像制作
|
||||||
- [x] ~~-支持页面`静态化处理`,将动态页面生成静态页面~~
|
|
||||||
- [x] 支持`Quartz.net`任务调度,实现任意接口被调度
|
- [x] 支持`Quartz.net`任务调度,实现任意接口被调度
|
||||||
- [x] 支持`ELK`,log4net+kafka+es+logstach+kibana
|
- [x] 支持`ELK`,log4net+kafka+es+logstach+kibana
|
||||||
- [x] 支持`IdentityService4`授权中心
|
- [x] 支持`IdentityService4`授权中心
|
||||||
@@ -125,7 +124,7 @@ WebFirst开发:所有代码生成器已经配置完成,无需任何操作数
|
|||||||
- [x] 支持`Docker+k8s`部署
|
- [x] 支持`Docker+k8s`部署
|
||||||
- [x] 支持`Jenkins+CI/CD`
|
- [x] 支持`Jenkins+CI/CD`
|
||||||
- [x] 支持`AutoMapper`模块映射
|
- [x] 支持`AutoMapper`模块映射
|
||||||
- [ ] 支持`微信支付`(没账号)
|
- [x] 支持`微信支付`
|
||||||
- [x] 支持`单表多租户`常用功能
|
- [x] 支持`单表多租户`常用功能
|
||||||
- [x] 支持`逻辑删除`常用功能
|
- [x] 支持`逻辑删除`常用功能
|
||||||
- [x] 支持`操作日志`常用功能
|
- [x] 支持`操作日志`常用功能
|
||||||
|
|||||||
@@ -23,6 +23,12 @@
|
|||||||
<param name="registerDto"></param>
|
<param name="registerDto"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:Yi.Framework.ApiMicroservice.Controllers.AccountController.Logout">
|
||||||
|
<summary>
|
||||||
|
没啥说,登出
|
||||||
|
</summary>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
<member name="M:Yi.Framework.ApiMicroservice.Controllers.AccountController.GetUserAllInfo">
|
<member name="M:Yi.Framework.ApiMicroservice.Controllers.AccountController.GetUserAllInfo">
|
||||||
<summary>
|
<summary>
|
||||||
通过已登录的用户获取用户信息及菜单
|
通过已登录的用户获取用户信息及菜单
|
||||||
|
|||||||
@@ -70,6 +70,10 @@ namespace Yi.Framework.ApiMicroservice.Controllers
|
|||||||
return Result.SuccessError("注册失败!用户名已存在!");
|
return Result.SuccessError("注册失败!用户名已存在!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 没啥说,登出
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public Result Logout()
|
public Result Logout()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ namespace Yi.Framework.ApiMicroservice.Controllers
|
|||||||
{JobConst.method,"get" },
|
{JobConst.method,"get" },
|
||||||
{JobConst.url,"https://www.baidu.com" }
|
{JobConst.url,"https://www.baidu.com" }
|
||||||
};
|
};
|
||||||
await _quartzInvoker.start("*/5 * * * * ?", new Quartz.JobKey("test", "my"), "Yi.Framework.Job", "HttpJob", data: data);
|
await _quartzInvoker.StartAsync("*/5 * * * * ?", "HttpJob",jobName:"test",jobGroup:"my", data: data);
|
||||||
return Result.Success();
|
return Result.Success();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ namespace Yi.Framework.ApiMicroservice.Controllers
|
|||||||
[HttpPut]
|
[HttpPut]
|
||||||
public async Task<Result> stopJob()
|
public async Task<Result> stopJob()
|
||||||
{
|
{
|
||||||
await _quartzInvoker.Stop(new Quartz.JobKey("test", "my"));
|
await _quartzInvoker.StopAsync(new Quartz.JobKey("test", "my"));
|
||||||
return Result.Success();
|
return Result.Success();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,10 +31,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Yi.Framework.DTOModel\Yi.Framework.DTOModel.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.Interface\Yi.Framework.Interface.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.Model\Yi.Framework.Model.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.Service\Yi.Framework.Service.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.WebCore\Yi.Framework.WebCore.csproj" />
|
<ProjectReference Include="..\Yi.Framework.WebCore\Yi.Framework.WebCore.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
430
Yi.Framework.Net6/Yi.Framework.Common/Base/NullValue.cs
Normal file
430
Yi.Framework.Net6/Yi.Framework.Common/Base/NullValue.cs
Normal file
@@ -0,0 +1,430 @@
|
|||||||
|
namespace System
|
||||||
|
{
|
||||||
|
#region 无值定义
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 无值定义
|
||||||
|
/// </summary>
|
||||||
|
public class NullValue
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 唯一识别型的默认无值
|
||||||
|
/// </summary>
|
||||||
|
public static readonly Guid Guid = Guid.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 日期时间的默认无值
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>解决C#DateTime最小值是SQL 2005不允许的范围内</remarks>
|
||||||
|
public static readonly DateTime DateTime = DateTime.Parse("1753-1-1 12:00:01");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// JavaScript日期时间的默认无值
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks></remarks>
|
||||||
|
public static readonly DateTime JavaScriptDateTime = DateTime.Parse("1970-1-1 00:00:00");
|
||||||
|
|
||||||
|
#region 数字类型
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 短整数的默认无值
|
||||||
|
/// </summary>
|
||||||
|
public const short ShortInterger = -1;//short.MinValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 整数的默认无值
|
||||||
|
/// </summary>
|
||||||
|
public const int Interger = -1;//int.MinValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 长整数的默认无值
|
||||||
|
/// </summary>
|
||||||
|
public const long LongInterger = -1;//long.MinValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decimal数的默认无值
|
||||||
|
/// </summary>
|
||||||
|
public const decimal Decimal = -1;//decimal.MinValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Float数的默认无值
|
||||||
|
/// </summary>
|
||||||
|
public const float Float = -1;//float.MinValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Double数的默认无值
|
||||||
|
/// </summary>
|
||||||
|
public const double Double = -1;//double.MinValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Byte数的默认无值
|
||||||
|
/// </summary>
|
||||||
|
public const byte Byte = byte.MinValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SByte数的默认无值
|
||||||
|
/// </summary>
|
||||||
|
public const sbyte SByte = sbyte.MinValue;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 字串的默认无值
|
||||||
|
/// </summary>
|
||||||
|
public static readonly string String = String.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 一般对象的判断
|
||||||
|
/// </summary>
|
||||||
|
public const Object Object = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 无值的扩展方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 无值的扩展方法
|
||||||
|
/// </summary>
|
||||||
|
public static class NullValueExtensions
|
||||||
|
{
|
||||||
|
#region 一般类型
|
||||||
|
|
||||||
|
public static Guid TryToGuid(this string guid)
|
||||||
|
{
|
||||||
|
if (Guid.TryParse(guid, out var guid1))
|
||||||
|
{
|
||||||
|
return guid1;
|
||||||
|
}
|
||||||
|
return Guid.Empty;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string TryStringNull(this string value)
|
||||||
|
{
|
||||||
|
return value == null ? "" : value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Object类型无值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNull(this object value)
|
||||||
|
{
|
||||||
|
if (value == null || value == NullValue.Object)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Object类型有值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否不为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNotNull(this object value)
|
||||||
|
{
|
||||||
|
return !value.IsNull();
|
||||||
|
}
|
||||||
|
public static bool IsGuidNotNull(this Guid? value)
|
||||||
|
{
|
||||||
|
return !value.IsGuidNull();
|
||||||
|
}
|
||||||
|
public static bool IsGuidNull(this Guid? value)
|
||||||
|
{
|
||||||
|
if (value == null || value == Guid.Empty)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// String类型无值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNull(this String value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// String类型有值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否不为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNotNull(this String value)
|
||||||
|
{
|
||||||
|
return !value.IsNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Guid类型无值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNull(this Guid value)
|
||||||
|
{
|
||||||
|
if (value == NullValue.Guid)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Guid类型有值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否不为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNotNull(this Guid value)
|
||||||
|
{
|
||||||
|
return !value.IsNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region 数字类型
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ShortInterger类型无值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNull(this short value)
|
||||||
|
{
|
||||||
|
if (value == NullValue.ShortInterger)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ShortInterger类型有值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否不为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNotNull(this short value)
|
||||||
|
{
|
||||||
|
return !value.IsNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interger类型无值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNull(this int value)
|
||||||
|
{
|
||||||
|
if (value == NullValue.Interger)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interger类型有值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否不为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNotNull(this int value)
|
||||||
|
{
|
||||||
|
return !value.IsNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// LongInterger类型无值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNull(this long value)
|
||||||
|
{
|
||||||
|
if (value == NullValue.LongInterger)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// LongInterger类型有值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否不为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNotNull(this long value)
|
||||||
|
{
|
||||||
|
return !value.IsNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decimal类型无值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNull(this decimal value)
|
||||||
|
{
|
||||||
|
if (value == NullValue.Decimal)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decimal类型有值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否不为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNotNull(this decimal value)
|
||||||
|
{
|
||||||
|
return !value.IsNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Float类型无值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNull(this float value)
|
||||||
|
{
|
||||||
|
if (value == NullValue.Float)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Float类型有值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否不为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNotNull(this float value)
|
||||||
|
{
|
||||||
|
return !value.IsNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Double类型无值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNull(this double value)
|
||||||
|
{
|
||||||
|
if (value == NullValue.Double)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Double类型有值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否不为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNotNull(this double value)
|
||||||
|
{
|
||||||
|
return !value.IsNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Byte类型无值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNull(this byte value)
|
||||||
|
{
|
||||||
|
if (value == NullValue.Byte)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Byte类型有值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否不为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNotNull(this byte value)
|
||||||
|
{
|
||||||
|
return !value.IsNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SByte类型无值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNull(this sbyte value)
|
||||||
|
{
|
||||||
|
if (value == NullValue.SByte)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SByte类型有值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否不为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNotNull(this sbyte value)
|
||||||
|
{
|
||||||
|
return !value.IsNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DateTime类型无值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNull(this DateTime value)
|
||||||
|
{
|
||||||
|
if (value == DateTime.MinValue || value <= NullValue.DateTime || value == NullValue.JavaScriptDateTime)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DateTime类型有值判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">待判断对象</param>
|
||||||
|
/// <returns>是否不为空(true--真,false--假)</returns>
|
||||||
|
public static bool IsNotNull(this DateTime value)
|
||||||
|
{
|
||||||
|
return !value.IsNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Yi.Framework.Common.Helper
|
||||||
|
{
|
||||||
|
public class Compare<T, C> : IEqualityComparer<T>
|
||||||
|
{
|
||||||
|
private Func<T, C> _getField;
|
||||||
|
public Compare(Func<T, C> getfield)
|
||||||
|
{
|
||||||
|
this._getField = getfield;
|
||||||
|
}
|
||||||
|
public bool Equals(T x, T y)
|
||||||
|
{
|
||||||
|
return EqualityComparer<C>.Default.Equals(_getField(x), _getField(y));
|
||||||
|
}
|
||||||
|
public int GetHashCode(T obj)
|
||||||
|
{
|
||||||
|
return EqualityComparer<C>.Default.GetHashCode(this._getField(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static class DistinctHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义Distinct扩展方法
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">要去重的对象类</typeparam>
|
||||||
|
/// <typeparam name="C">自定义去重的字段类型</typeparam>
|
||||||
|
/// <param name="source">要去重的对象</param>
|
||||||
|
/// <param name="getfield">获取自定义去重字段的委托</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static IEnumerable<T> DistinctNew<T, C>(this IEnumerable<T> source, Func<T, C> getfield)
|
||||||
|
{
|
||||||
|
return source.Distinct(new Compare<T, C>(getfield));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,60 +1,222 @@
|
|||||||
using System;
|
using OfficeOpenXml;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using OfficeOpenXml;
|
|
||||||
|
|
||||||
namespace Yi.Framework.Common.Helper
|
namespace Yi.Framework.Common.Helper
|
||||||
{
|
{
|
||||||
public class ExcelHelper
|
public class ExcelHelper
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// 导出Excel
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
/// <param name="dataList">数据</param>
|
/// <param name="list"></param>
|
||||||
/// <param name="headers">表头</param>
|
/// <param name="sheetName"></param>
|
||||||
/// <returns></returns>
|
/// <param name="fileName"></param>
|
||||||
public static string CreateExcelFromList<T>(List<T> dataList, List<string> headers,string evn)
|
public static string ExportExcel<T>(List<T> list, string sheetName, string fileName,string path)
|
||||||
{
|
{
|
||||||
|
string sFileName = $"{fileName}.xlsx";
|
||||||
|
string newFileName = Path.Combine(path, sFileName);
|
||||||
|
//调试模式需要加上
|
||||||
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
||||||
string sWebRootFolder = Path.Combine($"{evn}", "wwwroot/Excel");//如果用浏览器url下载的方式 存放excel的文件夹一定要建在网站首页的同级目录下!!!
|
Directory.CreateDirectory(Path.GetDirectoryName(newFileName)!);
|
||||||
if (!Directory.Exists(sWebRootFolder))
|
using (ExcelPackage package = new(new FileInfo(newFileName)))
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(sWebRootFolder);
|
// 添加worksheet
|
||||||
}
|
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add(sheetName);
|
||||||
string sFileName = $@"Excel_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx";
|
//单元格自动适应大小
|
||||||
var path = Path.Combine(sWebRootFolder, sFileName);
|
worksheet.Cells.Style.ShrinkToFit = true;
|
||||||
FileInfo file = new FileInfo(path);
|
//全部字段导出
|
||||||
if (file.Exists)
|
worksheet.Cells.LoadFromCollection(list, true, OfficeOpenXml.Table.TableStyles.Light13);
|
||||||
{
|
|
||||||
file.Delete();
|
|
||||||
file = new FileInfo(path);
|
|
||||||
}
|
|
||||||
using (ExcelPackage package = new(file))
|
|
||||||
{
|
|
||||||
//创建sheet
|
|
||||||
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add("sheet1");
|
|
||||||
worksheet.Cells.LoadFromCollection(dataList, true);
|
|
||||||
//表头字段
|
|
||||||
for (int i = 0; i < headers.Count; i++)
|
|
||||||
{
|
|
||||||
worksheet.Cells[1, i + 1].Value = headers[i];
|
|
||||||
}
|
|
||||||
for (int i = 0; i < headers.Count + 1; i++)
|
|
||||||
{//删除不需要的列
|
|
||||||
string aa = worksheet.Cells[1, i + 1].Value.ToString();
|
|
||||||
if (aa == "总行数")
|
|
||||||
{
|
|
||||||
worksheet.DeleteColumn(i + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
package.Save();
|
package.Save();
|
||||||
}
|
}
|
||||||
//return path;//这是返回文件的方式
|
|
||||||
return sFileName; //如果用浏览器url下载的方式 这里直接返回生成的文件名就可以了
|
return newFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下载导入模板
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="list"></param>
|
||||||
|
/// <param name="stream"></param>
|
||||||
|
/// <param name="fileName">下载文件名</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected string DownloadImportTemplate<T>(List<T> list, Stream stream, string fileName,string path)
|
||||||
|
{
|
||||||
|
string sFileName = $"{fileName}.xlsx";
|
||||||
|
string newFileName = Path.Combine(path, sFileName);
|
||||||
|
//调试模式需要加上
|
||||||
|
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
||||||
|
if (!Directory.Exists(newFileName))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(newFileName)!);
|
||||||
|
}
|
||||||
|
using (ExcelPackage package = new(new FileInfo(newFileName)))
|
||||||
|
{
|
||||||
|
// 添加worksheet
|
||||||
|
ExcelWorksheet worksheet = package.Workbook.Worksheets.Add(fileName);
|
||||||
|
//单元格自动适应大小
|
||||||
|
worksheet.Cells.Style.ShrinkToFit = true;
|
||||||
|
//全部字段导出
|
||||||
|
worksheet.Cells.LoadFromCollection(list, true, OfficeOpenXml.Table.TableStyles.Light13);
|
||||||
|
package.SaveAs(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 导入数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static List<T> ImportData<T>(Stream stream) where T : new()
|
||||||
|
{
|
||||||
|
using ExcelPackage package = new(stream);
|
||||||
|
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
||||||
|
ExcelWorksheet worksheet = package.Workbook.Worksheets[0];//读取第1个sheet
|
||||||
|
//获取表格的列数和行数
|
||||||
|
|
||||||
|
int colStart = worksheet.Dimension.Start.Column;
|
||||||
|
int colEnd = worksheet.Dimension.End.Column;
|
||||||
|
int rowStart = worksheet.Dimension.Start.Row;
|
||||||
|
int rowEnd = worksheet.Dimension.End.Row;
|
||||||
|
//int rowCount = worksheet.Dimension.Rows;
|
||||||
|
//int ColCount = worksheet.Dimension.Columns;
|
||||||
|
|
||||||
|
List<T> resultList = new();
|
||||||
|
List<PropertyInfo> propertyInfos = new();// new(typeof(T).GetProperties());
|
||||||
|
Dictionary<string, int> dictHeader = new();
|
||||||
|
for (int i = colStart; i < colEnd; i++)
|
||||||
|
{
|
||||||
|
var name = worksheet.Cells[rowStart, i].Value.ToString();
|
||||||
|
dictHeader[name!] = i;
|
||||||
|
|
||||||
|
PropertyInfo propertyInfo = MapPropertyInfo<T>(name!);
|
||||||
|
if (propertyInfo != null)
|
||||||
|
{
|
||||||
|
propertyInfos.Add(propertyInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int row = rowStart + 1; row <= rowEnd; row++)
|
||||||
|
{
|
||||||
|
T result = new();
|
||||||
|
|
||||||
|
foreach (PropertyInfo p in propertyInfos)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ExcelRange cell = worksheet.Cells[row, dictHeader[p.Name]];
|
||||||
|
if (cell.Value == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (p.PropertyType.Name.ToLower())
|
||||||
|
{
|
||||||
|
case "string":
|
||||||
|
p.SetValue(result, cell.GetValue<string>());
|
||||||
|
break;
|
||||||
|
case "int16":
|
||||||
|
p.SetValue(result, cell.GetValue<short>()); break;
|
||||||
|
case "int32":
|
||||||
|
p.SetValue(result, cell.GetValue<int>()); break;
|
||||||
|
case "int64":
|
||||||
|
p.SetValue(result, cell.GetValue<long>()); break;
|
||||||
|
case "decimal":
|
||||||
|
p.SetValue(result, cell.GetValue<decimal>());
|
||||||
|
break;
|
||||||
|
case "double":
|
||||||
|
p.SetValue(result, cell.GetValue<double>()); break;
|
||||||
|
case "datetime":
|
||||||
|
p.SetValue(result, cell.GetValue<DateTime>()); break;
|
||||||
|
case "boolean":
|
||||||
|
p.SetValue(result, cell.GetValue<bool>()); break;
|
||||||
|
case "char":
|
||||||
|
p.SetValue(result, cell.GetValue<string>()); break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (KeyNotFoundException ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("未找到该列将继续循环," + ex.Message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resultList.Add(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultList.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查找Excel列名对应的实体属性
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="columnName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static PropertyInfo MapPropertyInfo<T>(string columnName)
|
||||||
|
{
|
||||||
|
PropertyInfo[] propertyList = GetProperties(typeof(T));
|
||||||
|
PropertyInfo propertyInfo = propertyList.Where(p => p.Name == columnName).FirstOrDefault()!;
|
||||||
|
if (propertyInfo != null)
|
||||||
|
{
|
||||||
|
return propertyInfo;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (PropertyInfo tempPropertyInfo in propertyList)
|
||||||
|
{
|
||||||
|
System.ComponentModel.DescriptionAttribute[] attributes = (System.ComponentModel.DescriptionAttribute[])tempPropertyInfo.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false);
|
||||||
|
if (attributes.Length > 0)
|
||||||
|
{
|
||||||
|
if (attributes[0].Description == columnName)
|
||||||
|
{
|
||||||
|
return tempPropertyInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 得到类里面的属性集合
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <param name="columns"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private static PropertyInfo[] GetProperties(Type type, string[] columns = null!)
|
||||||
|
{
|
||||||
|
PropertyInfo[] properties = null!;
|
||||||
|
properties = type.GetProperties();
|
||||||
|
|
||||||
|
if (columns != null && columns.Length > 0)
|
||||||
|
{
|
||||||
|
// 按columns顺序返回属性
|
||||||
|
var columnPropertyList = new List<PropertyInfo>();
|
||||||
|
foreach (var column in columns)
|
||||||
|
{
|
||||||
|
var columnProperty = properties.Where(p => p.Name == column).FirstOrDefault();
|
||||||
|
if (columnProperty != null)
|
||||||
|
{
|
||||||
|
columnPropertyList.Add(columnProperty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return columnPropertyList.ToArray();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
397
Yi.Framework.Net6/Yi.Framework.Common/Helper/ZipHelper.cs
Normal file
397
Yi.Framework.Net6/Yi.Framework.Common/Helper/ZipHelper.cs
Normal file
@@ -0,0 +1,397 @@
|
|||||||
|
/***
|
||||||
|
* Title:"基础工具" 项目
|
||||||
|
* Title:"基础工具" 项目
|
||||||
|
* 主题:压缩包帮助类
|
||||||
|
* Description:
|
||||||
|
* 功能:
|
||||||
|
* 1、压缩单个文件
|
||||||
|
* 2、压缩多个文件
|
||||||
|
* 3、压缩多层目录
|
||||||
|
* 4、递归遍历目录
|
||||||
|
* 5、解压缩一个 zip 文件
|
||||||
|
* 6、获取压缩文件中指定类型的文件
|
||||||
|
* 7、获取压缩文件中的所有文件
|
||||||
|
* Date:2021
|
||||||
|
* Version:0.1版本
|
||||||
|
* Author:Coffee
|
||||||
|
* Modify Recoder:
|
||||||
|
*/
|
||||||
|
|
||||||
|
using ICSharpCode.SharpZipLib.Zip;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Yi.Framework.Common.Helper
|
||||||
|
{
|
||||||
|
public class ZipHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 压缩单个文件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileToZip">要压缩的文件</param>
|
||||||
|
/// <param name="zipedFile">压缩后的文件</param>
|
||||||
|
/// <param name="compressionLevel">压缩等级</param>
|
||||||
|
/// <param name="blockSize">每次写入大小</param>
|
||||||
|
public static void ZipFile(string fileToZip, string zipedFile, int compressionLevel, int blockSize)
|
||||||
|
{
|
||||||
|
//如果文件没有找到,则报错
|
||||||
|
if (!File.Exists(fileToZip))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException("指定要压缩的文件: " + fileToZip + " 不存在!");
|
||||||
|
}
|
||||||
|
|
||||||
|
using (FileStream ZipFile = File.Create(zipedFile))
|
||||||
|
{
|
||||||
|
using (ZipOutputStream ZipStream = new ZipOutputStream(ZipFile))
|
||||||
|
{
|
||||||
|
using (FileStream StreamToZip = new FileStream(fileToZip, FileMode.Open, FileAccess.Read))
|
||||||
|
{
|
||||||
|
string fileName = fileToZip.Substring(fileToZip.LastIndexOf("\\") + 1);
|
||||||
|
|
||||||
|
ZipEntry ZipEntry = new ZipEntry(fileName);
|
||||||
|
|
||||||
|
ZipStream.PutNextEntry(ZipEntry);
|
||||||
|
|
||||||
|
ZipStream.SetLevel(compressionLevel);
|
||||||
|
|
||||||
|
byte[] buffer = new byte[blockSize];
|
||||||
|
|
||||||
|
int sizeRead = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
sizeRead = StreamToZip.Read(buffer, 0, buffer.Length);
|
||||||
|
ZipStream.Write(buffer, 0, sizeRead);
|
||||||
|
}
|
||||||
|
while (sizeRead > 0);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamToZip.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
ZipStream.Finish();
|
||||||
|
ZipStream.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
ZipFile.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 压缩单个文件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileToZip">要进行压缩的文件名</param>
|
||||||
|
/// <param name="zipedFile">压缩后生成的压缩文件名</param>
|
||||||
|
public static void ZipFile(string fileToZip, string zipedFile)
|
||||||
|
{
|
||||||
|
//如果文件没有找到,则报错
|
||||||
|
if (!File.Exists(fileToZip))
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException("指定要压缩的文件: " + fileToZip + " 不存在!");
|
||||||
|
}
|
||||||
|
|
||||||
|
using (FileStream fs = File.OpenRead(fileToZip))
|
||||||
|
{
|
||||||
|
byte[] buffer = new byte[fs.Length];
|
||||||
|
fs.Read(buffer, 0, buffer.Length);
|
||||||
|
fs.Close();
|
||||||
|
|
||||||
|
using (FileStream ZipFile = File.Create(zipedFile))
|
||||||
|
{
|
||||||
|
using (ZipOutputStream ZipStream = new ZipOutputStream(ZipFile))
|
||||||
|
{
|
||||||
|
string fileName = fileToZip.Substring(fileToZip.LastIndexOf("\\") + 1);
|
||||||
|
ZipEntry ZipEntry = new ZipEntry(fileName);
|
||||||
|
ZipStream.PutNextEntry(ZipEntry);
|
||||||
|
ZipStream.SetLevel(5);
|
||||||
|
|
||||||
|
ZipStream.Write(buffer, 0, buffer.Length);
|
||||||
|
ZipStream.Finish();
|
||||||
|
ZipStream.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 压缩多个文件到指定路径
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sourceFileNames">压缩到哪个路径</param>
|
||||||
|
/// <param name="zipFileName">压缩文件名称</param>
|
||||||
|
public static void ZipFile(List<string> sourceFileNames, string zipFileName)
|
||||||
|
{
|
||||||
|
//压缩文件打包
|
||||||
|
using (ZipOutputStream s = new ZipOutputStream(File.Create(zipFileName)))
|
||||||
|
{
|
||||||
|
s.SetLevel(9);
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
foreach (string file in sourceFileNames)
|
||||||
|
{
|
||||||
|
if (Directory.Exists(file))// 先当作目录处理如果存在这个目录就递归Copy该目录下面的文件
|
||||||
|
{
|
||||||
|
string pPath = "";
|
||||||
|
pPath += Path.GetFileName(file);
|
||||||
|
pPath += "\\";
|
||||||
|
ZipSetp(file, s, pPath, sourceFileNames);
|
||||||
|
}
|
||||||
|
else // 否则直接压缩文件
|
||||||
|
{
|
||||||
|
|
||||||
|
ZipEntry entry = new ZipEntry(Path.GetFileName(file));
|
||||||
|
entry.DateTime = DateTime.Now;
|
||||||
|
s.PutNextEntry(entry);
|
||||||
|
using (FileStream fs = File.OpenRead(file))
|
||||||
|
{
|
||||||
|
int sourceBytes;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
sourceBytes = fs.Read(buffer, 0, buffer.Length);
|
||||||
|
s.Write(buffer, 0, sourceBytes);
|
||||||
|
} while (sourceBytes > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.Finish();
|
||||||
|
s.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 压缩多层目录
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strDirectory">待压缩目录</param>
|
||||||
|
/// <param name="zipedFile">压缩后生成的压缩文件名,绝对路径</param>
|
||||||
|
public static void ZipFileDirectory(string strDirectory, string zipedFile)
|
||||||
|
{
|
||||||
|
using (FileStream ZipFile = File.Create(zipedFile))
|
||||||
|
{
|
||||||
|
using (ZipOutputStream s = new ZipOutputStream(ZipFile))
|
||||||
|
{
|
||||||
|
s.SetLevel(9);
|
||||||
|
ZipSetp(strDirectory, s, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 压缩多层目录
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strDirectory">待压缩目录</param>
|
||||||
|
/// <param name="zipedFile">压缩后生成的压缩文件名,绝对路径</param>
|
||||||
|
/// <param name="files">指定要压缩的文件列表(完全路径)</param>
|
||||||
|
public static void ZipFileDirectory(string strDirectory, string zipedFile, List<string> files)
|
||||||
|
{
|
||||||
|
using (FileStream ZipFile = File.Create(zipedFile))
|
||||||
|
{
|
||||||
|
using (ZipOutputStream s = new ZipOutputStream(ZipFile))
|
||||||
|
{
|
||||||
|
s.SetLevel(9);
|
||||||
|
ZipSetp(strDirectory, s, "", files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 递归遍历目录
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="strDirectory">需遍历的目录</param>
|
||||||
|
/// <param name="s">压缩输出流对象</param>
|
||||||
|
/// <param name="parentPath">The parent path.</param>
|
||||||
|
/// <param name="files">需要压缩的文件</param>
|
||||||
|
private static void ZipSetp(string strDirectory, ZipOutputStream s, string parentPath, List<string> files = null!)
|
||||||
|
{
|
||||||
|
if (strDirectory[strDirectory.Length - 1] != Path.DirectorySeparatorChar)
|
||||||
|
{
|
||||||
|
strDirectory += Path.DirectorySeparatorChar;
|
||||||
|
}
|
||||||
|
|
||||||
|
string[] filenames = Directory.GetFileSystemEntries(strDirectory);
|
||||||
|
|
||||||
|
byte[] buffer = new byte[4096];
|
||||||
|
foreach (string file in filenames)// 遍历所有的文件和目录
|
||||||
|
{
|
||||||
|
if (files != null && !files.Contains(file))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Directory.Exists(file))// 先当作目录处理如果存在这个目录就递归Copy该目录下面的文件
|
||||||
|
{
|
||||||
|
string pPath = parentPath;
|
||||||
|
pPath += Path.GetFileName(file);
|
||||||
|
pPath += "\\";
|
||||||
|
ZipSetp(file, s, pPath, files!);
|
||||||
|
}
|
||||||
|
else // 否则直接压缩文件
|
||||||
|
{
|
||||||
|
//打开压缩文件
|
||||||
|
string fileName = parentPath + Path.GetFileName(file);
|
||||||
|
ZipEntry entry = new ZipEntry(fileName);
|
||||||
|
|
||||||
|
entry.DateTime = DateTime.Now;
|
||||||
|
|
||||||
|
s.PutNextEntry(entry);
|
||||||
|
using (FileStream fs = File.OpenRead(file))
|
||||||
|
{
|
||||||
|
int sourceBytes;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
sourceBytes = fs.Read(buffer, 0, buffer.Length);
|
||||||
|
s.Write(buffer, 0, sourceBytes);
|
||||||
|
} while (sourceBytes > 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解压缩一个 zip 文件。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="zipedFile">压缩文件</param>
|
||||||
|
/// <param name="strDirectory">解压目录</param>
|
||||||
|
/// <param name="password">zip 文件的密码。</param>
|
||||||
|
/// <param name="overWrite">是否覆盖已存在的文件。</param>
|
||||||
|
public static void UnZip(string zipedFile, string strDirectory, bool overWrite, string password)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (strDirectory == "")
|
||||||
|
strDirectory = Directory.GetCurrentDirectory();
|
||||||
|
if (!strDirectory.EndsWith("\\"))
|
||||||
|
strDirectory = strDirectory + "\\";
|
||||||
|
|
||||||
|
using (ZipInputStream s = new ZipInputStream(File.OpenRead(zipedFile)))
|
||||||
|
{
|
||||||
|
if (password != null)
|
||||||
|
{
|
||||||
|
s.Password = password;
|
||||||
|
}
|
||||||
|
ZipEntry theEntry;
|
||||||
|
|
||||||
|
while ((theEntry = s.GetNextEntry()) != null)
|
||||||
|
{
|
||||||
|
string directoryName = "";
|
||||||
|
string pathToZip = "";
|
||||||
|
pathToZip = theEntry.Name;
|
||||||
|
|
||||||
|
if (pathToZip != "")
|
||||||
|
directoryName = Path.GetDirectoryName(pathToZip) + "\\";
|
||||||
|
|
||||||
|
string fileName = Path.GetFileName(pathToZip);
|
||||||
|
|
||||||
|
Directory.CreateDirectory(strDirectory + directoryName);
|
||||||
|
|
||||||
|
if (fileName != "")
|
||||||
|
{
|
||||||
|
if (File.Exists(strDirectory + directoryName + fileName) && overWrite || !File.Exists(strDirectory + directoryName + fileName))
|
||||||
|
{
|
||||||
|
using (FileStream streamWriter = File.Create(strDirectory + directoryName + fileName))
|
||||||
|
{
|
||||||
|
int size = 2048;
|
||||||
|
byte[] data = new byte[2048];
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
size = s.Read(data, 0, data.Length);
|
||||||
|
|
||||||
|
if (size > 0)
|
||||||
|
streamWriter.Write(data, 0, size);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
streamWriter.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解压缩一个 zip 文件。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="zipedFile">压缩文件</param>
|
||||||
|
/// <param name="strDirectory">解压目录</param>
|
||||||
|
/// <param name="overWrite">是否覆盖已存在的文件。</param>
|
||||||
|
public static void UnZip(string zipedFile, string strDirectory, bool overWrite)
|
||||||
|
{
|
||||||
|
UnZip(zipedFile, strDirectory, overWrite, null!);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解压缩一个 zip 文件。
|
||||||
|
/// 覆盖已存在的文件。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="zipedFile">压缩文件</param>
|
||||||
|
/// <param name="strDirectory">解压目录</param>
|
||||||
|
public static void UnZip(string zipedFile, string strDirectory)
|
||||||
|
{
|
||||||
|
UnZip(zipedFile, strDirectory, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取压缩文件中指定类型的文件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="zipedFile">压缩文件</param>
|
||||||
|
/// <param name="fileExtension">文件类型(.txt|.exe)</param>
|
||||||
|
/// <returns>文件名称列表(包含子目录)</returns>
|
||||||
|
public static List<string> GetFiles(string zipedFile, List<string> fileExtension)
|
||||||
|
{
|
||||||
|
List<string> files = new List<string>();
|
||||||
|
if (!File.Exists(zipedFile))
|
||||||
|
{
|
||||||
|
//return files;
|
||||||
|
throw new FileNotFoundException(zipedFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (ZipInputStream s = new ZipInputStream(File.OpenRead(zipedFile)))
|
||||||
|
{
|
||||||
|
ZipEntry theEntry;
|
||||||
|
while ((theEntry = s.GetNextEntry()) != null)
|
||||||
|
{
|
||||||
|
if (theEntry.IsFile)
|
||||||
|
{
|
||||||
|
//Console.WriteLine("Name : {0}", theEntry.Name);
|
||||||
|
if (fileExtension != null)
|
||||||
|
{
|
||||||
|
if (fileExtension.Contains(Path.GetExtension(theEntry.Name)))
|
||||||
|
{
|
||||||
|
files.Add(theEntry.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
files.Add(theEntry.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取压缩文件中的所有文件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="zipedFile">压缩文件</param>
|
||||||
|
/// <returns>文件名称列表(包含子目录)</returns>
|
||||||
|
public static List<string> GetFiles(string zipedFile)
|
||||||
|
{
|
||||||
|
return GetFiles(zipedFile, null!);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}//Class_end
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
<PackageReference Include="EPPlus" Version="5.8.4" />
|
<PackageReference Include="EPPlus" Version="5.8.4" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="6.0.3" />
|
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="6.0.3" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
|
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -18,28 +18,33 @@ namespace Yi.Framework.Core
|
|||||||
public delegate T MyAction<T>(CSRedisClient client);
|
public delegate T MyAction<T>(CSRedisClient client);
|
||||||
|
|
||||||
private readonly RedisConnOptions _RedisOptions;
|
private readonly RedisConnOptions _RedisOptions;
|
||||||
|
|
||||||
|
private CSRedisClient Client { get; set; }
|
||||||
|
|
||||||
|
public CSRedisClient _Db { get { return Client; } set { } }
|
||||||
public CacheClientDB(IOptionsMonitor<RedisConnOptions> redisConnOptions)
|
public CacheClientDB(IOptionsMonitor<RedisConnOptions> redisConnOptions)
|
||||||
{
|
{
|
||||||
this._RedisOptions = redisConnOptions.CurrentValue;
|
this._RedisOptions = redisConnOptions.CurrentValue;
|
||||||
|
Client = new CSRedisClient($"{_RedisOptions.Host}:{_RedisOptions.Prot},password={_RedisOptions.Password},defaultDatabase ={ _RedisOptions.DB }");
|
||||||
}
|
}
|
||||||
|
|
||||||
private T TryCatch<T>(MyAction<T> action)
|
private T TryCatch<T>(MyAction<T> action)
|
||||||
{
|
{
|
||||||
var client2 = new CSRedisClient($"{_RedisOptions.Host}:{_RedisOptions.Prot},password={_RedisOptions.Password},defaultDatabase ={ _RedisOptions.DB }");
|
|
||||||
T result;
|
|
||||||
|
T result = default(T);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
result = action(client2);
|
result = action(Client);
|
||||||
}
|
}
|
||||||
catch (Exception exinfo)
|
catch (Exception exinfo)
|
||||||
{
|
{
|
||||||
object p = null;
|
|
||||||
result = (T)p;
|
|
||||||
Console.WriteLine(exinfo);
|
Console.WriteLine(exinfo);
|
||||||
}
|
}
|
||||||
finally
|
//finally
|
||||||
{
|
//{
|
||||||
client2.Dispose();
|
// Client.Dispose();
|
||||||
}
|
//}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -55,6 +60,11 @@ namespace Yi.Framework.Core
|
|||||||
return this.TryCatch((u) => u.Del(key));
|
return this.TryCatch((u) => u.Del(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long HRemove(string key, params string[] par)
|
||||||
|
{
|
||||||
|
return this.TryCatch((u) => u.HDel(key, par));
|
||||||
|
}
|
||||||
|
|
||||||
public T Get<T>(string key)
|
public T Get<T>(string key)
|
||||||
{
|
{
|
||||||
return this.TryCatch<T>((u) => u.Get<T>(key));
|
return this.TryCatch<T>((u) => u.Get<T>(key));
|
||||||
@@ -68,9 +78,36 @@ namespace Yi.Framework.Core
|
|||||||
{
|
{
|
||||||
return this.TryCatch<bool>((u) => u.Set(key, data));
|
return this.TryCatch<bool>((u) => u.Set(key, data));
|
||||||
}
|
}
|
||||||
public bool AddHash<T>(string key,string field, T data)
|
public T QueuePop<T>(string key)
|
||||||
{
|
{
|
||||||
return this.TryCatch<bool>((u)=>u.HSet(key ,field,data));
|
return this.TryCatch<T>((u) => u.RPop<T>(key));
|
||||||
|
}
|
||||||
|
public long QueuePush<T>(string key, T data)
|
||||||
|
{
|
||||||
|
return this.TryCatch<long>((u) => u.LPush<T>(key, data));
|
||||||
|
}
|
||||||
|
public long QueueLen(string key)
|
||||||
|
{
|
||||||
|
return TryCatch((u) => u.LLen(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool HSet<T>(string key, string fieId, T data)
|
||||||
|
{
|
||||||
|
return this.TryCatch<bool>((u) => u.HSet(key, fieId, data));
|
||||||
|
}
|
||||||
|
public bool HSet<T>(string key, string fieId, T data, TimeSpan time)
|
||||||
|
{
|
||||||
|
return this.TryCatch<bool>((u) =>
|
||||||
|
{
|
||||||
|
var res = u.HSet(key, fieId, data);
|
||||||
|
u.Expire(key, time);
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public CSRedisClient Db()
|
||||||
|
{
|
||||||
|
return new CSRedisClient($"{_RedisOptions.Host}:{_RedisOptions.Prot},password={_RedisOptions.Password},defaultDatabase ={ _RedisOptions.DB }");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ namespace Yi.Framework.Core
|
|||||||
private IScheduler _scheduler;
|
private IScheduler _scheduler;
|
||||||
private ILogger<QuartzInvoker> _logger;
|
private ILogger<QuartzInvoker> _logger;
|
||||||
private IJobFactory _jobFactory;
|
private IJobFactory _jobFactory;
|
||||||
|
|
||||||
|
private const string JobDllName = "Yi.Framework.Job";
|
||||||
public QuartzInvoker(ISchedulerFactory schedulerFactory, ILogger<QuartzInvoker> logger, IJobFactory jobFactory)
|
public QuartzInvoker(ISchedulerFactory schedulerFactory, ILogger<QuartzInvoker> logger, IJobFactory jobFactory)
|
||||||
{
|
{
|
||||||
_schedulerFactory = schedulerFactory;
|
_schedulerFactory = schedulerFactory;
|
||||||
@@ -29,27 +31,28 @@ namespace Yi.Framework.Core
|
|||||||
/// 开始任务
|
/// 开始任务
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cron"></param>
|
/// <param name="cron"></param>
|
||||||
/// <param name="jobKey"></param>
|
|
||||||
/// <param name="jobClass"></param>
|
/// <param name="jobClass"></param>
|
||||||
|
/// <param name="jobName"></param>
|
||||||
|
/// <param name="jobGroup"></param>
|
||||||
/// <param name="second"></param>
|
/// <param name="second"></param>
|
||||||
/// <param name="data"></param>
|
/// <param name="data"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task start(string cron, JobKey jobKey, string dllName,string jobClass, long second = 0, IDictionary<string, object> data = null)
|
public async Task StartAsync(string cron, string jobClass, string jobName = "", string jobGroup = "default", long startAtSecondTime = 0, IDictionary<string, object> data = null)
|
||||||
{
|
{
|
||||||
|
jobName = jobName == "" ? jobClass : jobName;
|
||||||
if (data == null)
|
if (data == null)
|
||||||
{
|
{
|
||||||
data = new Dictionary<string, object>();
|
data = new Dictionary<string, object>();
|
||||||
}
|
}
|
||||||
|
JobKey jobKey = new JobKey(jobName, jobGroup);
|
||||||
var myClass = AssemblyHelper.GetClass(dllName, jobClass).FirstOrDefault();
|
var myClass = AssemblyHelper.GetClass(JobDllName, jobClass).FirstOrDefault();
|
||||||
|
|
||||||
_scheduler = await _schedulerFactory.GetScheduler();
|
_scheduler = await _schedulerFactory.GetScheduler();
|
||||||
_scheduler.JobFactory = _jobFactory;
|
_scheduler.JobFactory = _jobFactory;
|
||||||
//开启调度器
|
|
||||||
await _scheduler.Start();
|
|
||||||
//创建一个触发器
|
//创建一个触发器
|
||||||
var trigger = TriggerBuilder.Create()
|
var trigger = TriggerBuilder.Create()
|
||||||
.StartAt(DateTimeOffset.Now.AddSeconds(second))
|
.StartAt(DateTimeOffset.Now.AddSeconds(startAtSecondTime))
|
||||||
.WithCronSchedule(cron)
|
.WithCronSchedule(cron)
|
||||||
.Build();
|
.Build();
|
||||||
//创建任务
|
//创建任务
|
||||||
@@ -57,18 +60,78 @@ namespace Yi.Framework.Core
|
|||||||
.UsingJobData(new JobDataMap(data))
|
.UsingJobData(new JobDataMap(data))
|
||||||
.WithIdentity(jobKey.Name, jobKey.Group)
|
.WithIdentity(jobKey.Name, jobKey.Group)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
//await _scheduler.AddJob(jobDetail,false);
|
||||||
|
|
||||||
|
//await _scheduler.ScheduleJob(trigger);
|
||||||
//将触发器和任务器绑定到调度器中
|
//将触发器和任务器绑定到调度器中
|
||||||
await _scheduler.ScheduleJob(jobDetail, trigger);
|
await _scheduler.ScheduleJob(jobDetail, trigger);
|
||||||
|
|
||||||
|
//开启调度器
|
||||||
|
await _scheduler.Start();
|
||||||
|
|
||||||
_logger.LogWarning($"开始任务:{jobKey.Name},组别:{jobKey.Group}");
|
_logger.LogWarning($"开始任务:{jobKey.Name},组别:{jobKey.Group}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开始任务
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cron"></param>
|
||||||
|
/// <param name="jobClass"></param>
|
||||||
|
/// <param name="jobName"></param>
|
||||||
|
/// <param name="jobGroup"></param>
|
||||||
|
/// <param name="second"></param>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task StartAsync(int milliSecondTime, string jobClass, string jobName = "", string jobGroup = "default", long startAtSecondTime = 0, IDictionary<string, object> data = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
jobName = jobName == "" ? jobClass : jobName;
|
||||||
|
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
data = new Dictionary<string, object>();
|
||||||
|
}
|
||||||
|
JobKey jobKey = new JobKey(jobName, jobGroup);
|
||||||
|
var myClass = AssemblyHelper.GetClass(JobDllName, jobClass).FirstOrDefault();
|
||||||
|
|
||||||
|
_scheduler = await _schedulerFactory.GetScheduler();
|
||||||
|
_scheduler.JobFactory = _jobFactory;
|
||||||
|
|
||||||
|
//创建一个触发器
|
||||||
|
var trigger = TriggerBuilder.Create()
|
||||||
|
.StartAt(DateTimeOffset.Now.AddSeconds(startAtSecondTime))
|
||||||
|
.WithSimpleSchedule(option =>
|
||||||
|
{
|
||||||
|
option.WithInterval(TimeSpan.FromMilliseconds(milliSecondTime)).RepeatForever();
|
||||||
|
})
|
||||||
|
|
||||||
|
.Build();
|
||||||
|
//创建任务
|
||||||
|
var jobDetail = JobBuilder.Create(myClass)
|
||||||
|
.UsingJobData(new JobDataMap(data))
|
||||||
|
.WithIdentity(jobKey.Name, jobKey.Group)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
//await _scheduler.AddJob(jobDetail,false);
|
||||||
|
|
||||||
|
//await _scheduler.ScheduleJob(trigger);
|
||||||
|
//将触发器和任务器绑定到调度器中
|
||||||
|
await _scheduler.ScheduleJob(jobDetail, trigger);
|
||||||
|
|
||||||
|
//开启调度器
|
||||||
|
await _scheduler.Start();
|
||||||
|
|
||||||
|
_logger.LogWarning($"开始任务:{jobKey.Name},组别:{jobKey.Group}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 暂停任务
|
/// 暂停任务
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="jobKey"></param>
|
/// <param name="jobKey"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task Stop(JobKey jobKey)
|
public async Task StopAsync(JobKey jobKey)
|
||||||
{
|
{
|
||||||
var _scheduler = await _schedulerFactory.GetScheduler();
|
var _scheduler = await _schedulerFactory.GetScheduler();
|
||||||
//LogUtil.Debug($"暂停任务{jobKey.Group},{jobKey.Name}");
|
//LogUtil.Debug($"暂停任务{jobKey.Group},{jobKey.Name}");
|
||||||
@@ -77,7 +140,7 @@ namespace Yi.Framework.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task Delete(JobKey jobKey)
|
public async Task DeleteAsync(JobKey jobKey)
|
||||||
{
|
{
|
||||||
var _scheduler = await _schedulerFactory.GetScheduler();
|
var _scheduler = await _schedulerFactory.GetScheduler();
|
||||||
//LogUtil.Debug($"暂停任务{jobKey.Group},{jobKey.Name}");
|
//LogUtil.Debug($"暂停任务{jobKey.Group},{jobKey.Name}");
|
||||||
@@ -85,7 +148,7 @@ namespace Yi.Framework.Core
|
|||||||
_logger.LogWarning($"删除任务:{jobKey.Name},组别:{jobKey.Group}");
|
_logger.LogWarning($"删除任务:{jobKey.Name},组别:{jobKey.Group}");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Resume(JobKey jobKey)
|
public async Task ResumeAsync(JobKey jobKey)
|
||||||
{
|
{
|
||||||
var _scheduler = await _schedulerFactory.GetScheduler();
|
var _scheduler = await _schedulerFactory.GetScheduler();
|
||||||
//LogUtil.Debug($"恢复任务{jobKey.Group},{jobKey.Name}");
|
//LogUtil.Debug($"恢复任务{jobKey.Group},{jobKey.Name}");
|
||||||
@@ -98,9 +161,9 @@ namespace Yi.Framework.Core
|
|||||||
/// 得到可运行的job列表
|
/// 得到可运行的job列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public List<string> getJobClassList()
|
public List<string> GetJobClassList()
|
||||||
{
|
{
|
||||||
var myClassList = AssemblyHelper.GetClass("Yi.Framework.Job");
|
var myClassList = AssemblyHelper.GetClass("ETX.Job");
|
||||||
List<string> data = new List<string>();
|
List<string> data = new List<string>();
|
||||||
myClassList.ForEach(k => data.Add(k.Name));
|
myClassList.ForEach(k => data.Add(k.Name));
|
||||||
return data;
|
return data;
|
||||||
@@ -126,9 +189,9 @@ namespace Yi.Framework.Core
|
|||||||
foreach (ITrigger trigger in triggers)
|
foreach (ITrigger trigger in triggers)
|
||||||
{
|
{
|
||||||
///下一次的执行时间
|
///下一次的执行时间
|
||||||
var utcTime =trigger.GetNextFireTimeUtc();
|
var utcTime = trigger.GetNextFireTimeUtc();
|
||||||
string str = utcTime.ToString();
|
string str = utcTime.ToString();
|
||||||
//TimeZone.CurrentTimeZone.ToLocalTime(Convert.ToDateTime(str));
|
//TimeZone.CurrentTimeZone.ToLocalTime(Convert.ToDateTime(str));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -142,7 +205,7 @@ namespace Yi.Framework.Core
|
|||||||
|
|
||||||
|
|
||||||
public class JobKeyModel
|
public class JobKeyModel
|
||||||
{
|
{
|
||||||
public JobKey jobKey { get; set; }
|
public JobKey jobKey { get; set; }
|
||||||
public DateTime? nextTime { get; set; }
|
public DateTime? nextTime { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ namespace Yi.Framework.Core
|
|||||||
autoAck: false,//不ACK
|
autoAck: false,//不ACK
|
||||||
consumer: consumer);
|
consumer: consumer);
|
||||||
Console.WriteLine($" Register Consumer To {rabbitMQConsumerMode.ExchangeName}-{rabbitMQConsumerMode.QueueName}");
|
Console.WriteLine($" Register Consumer To {rabbitMQConsumerMode.ExchangeName}-{rabbitMQConsumerMode.QueueName}");
|
||||||
Console.ReadLine();
|
//Console.ReadLine();
|
||||||
Console.WriteLine($" After Register Consumer To {rabbitMQConsumerMode.ExchangeName}-{rabbitMQConsumerMode.QueueName}");
|
Console.WriteLine($" After Register Consumer To {rabbitMQConsumerMode.ExchangeName}-{rabbitMQConsumerMode.QueueName}");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,8 +18,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Yi.Framework.Common\Yi.Framework.Common.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.Model\Yi.Framework.Model.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.Task\Yi.Framework.Job.csproj" />
|
<ProjectReference Include="..\Yi.Framework.Task\Yi.Framework.Job.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Yi.Framework.DTOModel\Yi.Framework.DTOModel.csproj" />
|
<ProjectReference Include="..\Yi.Framework.DTOModel\Yi.Framework.DTOModel.csproj" />
|
||||||
<ProjectReference Include="..\Yi.Framework.Model\Yi.Framework.Model.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.Repository\Yi.Framework.Repository.csproj" />
|
<ProjectReference Include="..\Yi.Framework.Repository\Yi.Framework.Repository.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ namespace Yi.Framework.Model.Query
|
|||||||
public List<QueryParameter> Parameters { get; set; } = new List<QueryParameter>();
|
public List<QueryParameter> Parameters { get; set; } = new List<QueryParameter>();
|
||||||
public List<string> OrderBys { get; set; } = new List<string>();
|
public List<string> OrderBys { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
public bool IsAsc { get; set; } = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class QueryCondition
|
public class QueryCondition
|
||||||
@@ -21,5 +22,7 @@ namespace Yi.Framework.Model.Query
|
|||||||
public List<QueryParameter> Parameters { get; set; } = new List<QueryParameter>();
|
public List<QueryParameter> Parameters { get; set; } = new List<QueryParameter>();
|
||||||
public List<string> OrderBys { get; set; } = new List<string>();
|
public List<string> OrderBys { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
public bool IsAsc { get; set; } = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Yi.Framework.Model.Query
|
namespace Yi.Framework.Model.Query
|
||||||
|
|||||||
@@ -24,5 +24,8 @@ namespace Yi.Framework.Repository
|
|||||||
public Task<List<S>> UseSqlAsync<S>(string sql, object parameters = null);
|
public Task<List<S>> UseSqlAsync<S>(string sql, object parameters = null);
|
||||||
public Task<bool> UseSqlAsync(string sql, object parameters = null);
|
public Task<bool> UseSqlAsync(string sql, object parameters = null);
|
||||||
ISugarQueryable<T> QueryConditionHandler(QueryCondition pars);
|
ISugarQueryable<T> QueryConditionHandler(QueryCondition pars);
|
||||||
|
Task<bool> UpdateSuperSaveAsync(T data, Expression<Func<T, object>> columns);
|
||||||
|
Task<List<T>> GetListAsync(Expression<Func<T, bool>> where, Expression<Func<T, object>> order, OrderByType orderByType = OrderByType.Desc);
|
||||||
|
Task<T> GetFirstAsync(Expression<Func<T, bool>> where, Expression<Func<T, object>> order, OrderByType orderByType = OrderByType.Desc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,12 +144,68 @@ namespace Yi.Framework.Repository
|
|||||||
{
|
{
|
||||||
foreach (var item in pars.OrderBys)
|
foreach (var item in pars.OrderBys)
|
||||||
{
|
{
|
||||||
query.OrderBy(item.ToSqlFilter());
|
if (pars.IsAsc)
|
||||||
|
{
|
||||||
|
query.OrderBy(item.ToSqlFilter() + " asc");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
query.OrderBy(item.ToSqlFilter() + " desc");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return query.Where(sugarParamters);
|
return query.Where(sugarParamters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新高级保存,验证重复
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="list"></param>
|
||||||
|
/// <param name="columns"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> UpdateSuperSaveAsync(T data, Expression<Func<T, object>> columns)
|
||||||
|
{
|
||||||
|
var x = _Db.Storageable(data)
|
||||||
|
.SplitError(it => it.Any())
|
||||||
|
.SplitUpdate(it => true)
|
||||||
|
.WhereColumns(columns)//这里用name作为数据库查找条件
|
||||||
|
.ToStorage();
|
||||||
|
return await x.AsInsertable.ExecuteCommandAsync() > 0;//插入可插入部分
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加高级保存,验证重复
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="list"></param>
|
||||||
|
/// <param name="columns"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> InsertSuperSaveAsync(T data, Expression<Func<T, object>> columns)
|
||||||
|
{
|
||||||
|
var x = _Db.Storageable(data)
|
||||||
|
.SplitError(it => it.Any())
|
||||||
|
.SplitInsert(it => true)
|
||||||
|
.WhereColumns(columns)//这里用name作为数据库查找条件
|
||||||
|
.ToStorage();
|
||||||
|
return await x.AsInsertable.ExecuteCommandAsync() > 0;//插入可插入部分
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 方法重载,多条件获取第一个值
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<T> GetFirstAsync(Expression<Func<T, bool>> where, Expression<Func<T, object>> order, OrderByType orderByType = OrderByType.Desc)
|
||||||
|
{
|
||||||
|
return await _Db.Queryable<T>().Where(where).OrderBy(order, orderByType).FirstAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 方法重载,多条件获取范围
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<List<T>> GetListAsync(Expression<Func<T, bool>> where, Expression<Func<T, object>> order, OrderByType orderByType = OrderByType.Desc)
|
||||||
|
{
|
||||||
|
return await _Db.Queryable<T>().Where(where).OrderBy(order, orderByType).ToListAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Yi.Framework.Common\Yi.Framework.Common.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.Model\Yi.Framework.Model.csproj" />
|
<ProjectReference Include="..\Yi.Framework.Model\Yi.Framework.Model.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Yi.Framework.Core\Yi.Framework.Core.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.DTOModel\Yi.Framework.DTOModel.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.Interface\Yi.Framework.Interface.csproj" />
|
<ProjectReference Include="..\Yi.Framework.Interface\Yi.Framework.Interface.csproj" />
|
||||||
<ProjectReference Include="..\Yi.Framework.Model\Yi.Framework.Model.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.Repository\Yi.Framework.Repository.csproj" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Yi.Framework.Common\Yi.Framework.Common.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.Model\Yi.Framework.Model.csproj" />
|
<ProjectReference Include="..\Yi.Framework.Model\Yi.Framework.Model.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Yi.Framework.Model.Models;
|
using Yi.Framework.Model.Models;
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace Yi.Framework.WebCore
|
namespace Yi.Framework.WebCore
|
||||||
{
|
{
|
||||||
@@ -51,5 +52,42 @@ namespace Yi.Framework.WebCore
|
|||||||
//Name = claimlist.FirstOrDefault(u => u.Type == JwtRegisteredClaimNames.Name).Value
|
//Name = claimlist.FirstOrDefault(u => u.Type == JwtRegisteredClaimNames.Name).Value
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void FileInlineHandle(this HttpContext httpContext, string fileName)
|
||||||
|
{
|
||||||
|
string encodeFilename = System.Web.HttpUtility.UrlEncode(fileName, System.Text.Encoding.GetEncoding("UTF-8"));
|
||||||
|
httpContext.Response.Headers.Add("Content-Disposition", "inline;filename=" + encodeFilename);
|
||||||
|
|
||||||
|
}
|
||||||
|
public static void FileAttachmentHandle(this HttpContext httpContext, string fileName)
|
||||||
|
{
|
||||||
|
string encodeFilename = System.Web.HttpUtility.UrlEncode(fileName, System.Text.Encoding.GetEncoding("UTF-8"));
|
||||||
|
httpContext.Response.Headers.Add("Content-Disposition", "attachment;filename=" + encodeFilename);
|
||||||
|
|
||||||
|
}
|
||||||
|
public static string GetLanguage(this HttpContext httpContext)
|
||||||
|
{
|
||||||
|
string res = "zh-CN";
|
||||||
|
var str = httpContext.Request.Headers["Accept-Language"].FirstOrDefault();
|
||||||
|
if (str.IsNotNull())
|
||||||
|
{
|
||||||
|
res = str.Split(",")[0];
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetBody(this HttpContext httpContext)
|
||||||
|
{
|
||||||
|
if (httpContext.Request.Body != null)
|
||||||
|
{
|
||||||
|
httpContext.Request.EnableBuffering();
|
||||||
|
httpContext.Request.Body.Position = 0;
|
||||||
|
StreamReader stream = new StreamReader(httpContext.Request.Body);
|
||||||
|
return stream.ReadToEndAsync().GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace Yi.Framework.WebCore.MiddlewareExtend
|
|||||||
if (Appsettings.appBool("Redis_Enabled"))
|
if (Appsettings.appBool("Redis_Enabled"))
|
||||||
{
|
{
|
||||||
services.Configure<RedisConnOptions>(Appsettings.appConfiguration("RedisConnOptions"));
|
services.Configure<RedisConnOptions>(Appsettings.appConfiguration("RedisConnOptions"));
|
||||||
services.AddTransient<CacheClientDB>();
|
services.AddSingleton<CacheClientDB>();
|
||||||
}
|
}
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Yi.Framework.WebCore.MiddlewareExtend
|
|||||||
{
|
{
|
||||||
public static class SqlsugarExtension
|
public static class SqlsugarExtension
|
||||||
{
|
{
|
||||||
public static void AddSqlsugarServer(this IServiceCollection services)
|
public static void AddSqlsugarServer(this IServiceCollection services, Action<SqlSugarClient> action = null)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
@@ -61,6 +61,10 @@ namespace Yi.Framework.WebCore.MiddlewareExtend
|
|||||||
},
|
},
|
||||||
db =>
|
db =>
|
||||||
{
|
{
|
||||||
|
if (action.IsNotNull())
|
||||||
|
{
|
||||||
|
action(db);
|
||||||
|
}
|
||||||
|
|
||||||
db.Aop.DataExecuting = (oldValue, entityInfo) =>
|
db.Aop.DataExecuting = (oldValue, entityInfo) =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -34,10 +34,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Yi.Framework.Core\Yi.Framework.Core.csproj" />
|
<ProjectReference Include="..\Yi.Framework.Core\Yi.Framework.Core.csproj" />
|
||||||
<ProjectReference Include="..\Yi.Framework.DTOModel\Yi.Framework.DTOModel.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.Interface\Yi.Framework.Interface.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.Model\Yi.Framework.Model.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.Repository\Yi.Framework.Repository.csproj" />
|
|
||||||
<ProjectReference Include="..\Yi.Framework.Service\Yi.Framework.Service.csproj" />
|
<ProjectReference Include="..\Yi.Framework.Service\Yi.Framework.Service.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,11 @@ const mutations = { //变化//载荷
|
|||||||
state.user = user
|
state.user = user
|
||||||
setUser(user)
|
setUser(user)
|
||||||
},
|
},
|
||||||
|
SET_NEW_USER(state, userInfo) {
|
||||||
|
state.user.user = userInfo
|
||||||
|
setUser(state.user)
|
||||||
|
},
|
||||||
|
|
||||||
SetGradient(state, gradient) {
|
SetGradient(state, gradient) {
|
||||||
state.drawer.gradient = gradient
|
state.drawer.gradient = gradient
|
||||||
},
|
},
|
||||||
@@ -90,7 +95,11 @@ const actions = { //动作
|
|||||||
|
|
||||||
var code=[];
|
var code=[];
|
||||||
resp2.data.menus.forEach(element => {
|
resp2.data.menus.forEach(element => {
|
||||||
code.push(element.permissionCode)
|
if(element.permissionCode!="")
|
||||||
|
{
|
||||||
|
code.push(element.permissionCode)
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
commit('SET_PER', code)
|
commit('SET_PER', code)
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,11 @@
|
|||||||
hoverable
|
hoverable
|
||||||
item-text="menuName"
|
item-text="menuName"
|
||||||
>
|
>
|
||||||
|
<template v-slot:prepend="{ item }">
|
||||||
|
<v-icon>
|
||||||
|
{{ item.menuIcon }}
|
||||||
|
</v-icon>
|
||||||
|
</template>
|
||||||
<template v-slot:append="{ item }">
|
<template v-slot:append="{ item }">
|
||||||
<app-btn v-if="item.menuType==0">路由:{{ item.router }}</app-btn>
|
<app-btn v-if="item.menuType==0">路由:{{ item.router }}</app-btn>
|
||||||
<app-btn v-if="item.menuType==1" color="secondary">权限:{{ item.permissionCode }}</app-btn>
|
<app-btn v-if="item.menuType==1" color="secondary">权限:{{ item.permissionCode }}</app-btn>
|
||||||
|
|||||||
@@ -335,7 +335,7 @@ export default {
|
|||||||
this.editInfo = Object.assign({}, this.userInfo);
|
this.editInfo = Object.assign({}, this.userInfo);
|
||||||
this.roleInfo = resp.data.roles;
|
this.roleInfo = resp.data.roles;
|
||||||
this.menuInfo = resp.data.menus;
|
this.menuInfo = resp.data.menus;
|
||||||
this.$store.commit("SET_USER", this.userInfo);
|
this.$store.commit("SET_NEW_USER", this.userInfo);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
choiceImg() {
|
choiceImg() {
|
||||||
|
|||||||
Reference in New Issue
Block a user