diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/AspNetCore/HttpContextExtensions.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/AspNetCore/HttpContextExtensions.cs new file mode 100644 index 00000000..10aff64b --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/AspNetCore/HttpContextExtensions.cs @@ -0,0 +1,99 @@ +using Microsoft.AspNetCore.Http; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.AspNetCore +{ + public static class HttpContextExtensions + { + /// + /// 设置文件下载名称 + /// + /// + /// + public static void FileInlineHandle(this HttpContext httpContext, string fileName) + { + string encodeFilename = System.Web.HttpUtility.UrlEncode(fileName, 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, 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 is not null) + { + res = str.Split(",")[0]; + } + return res; + + } + + /// + /// 判断是否为异步请求 + /// + /// + /// + public static bool IsAjaxRequest(this HttpRequest request) + { + string header = request.Headers["X-Requested-With"]; + return "XMLHttpRequest".Equals(header); + } + /// + /// 获取客户端IP + /// + /// + /// + public static string GetClientIp(this HttpContext context) + { + if (context == null) return ""; + var result = context.Request.Headers["X-Forwarded-For"].FirstOrDefault(); + if (string.IsNullOrEmpty(result)) + { + result = context.Connection.RemoteIpAddress?.ToString(); + } + if (string.IsNullOrEmpty(result) || result.Contains("::1")) + result = "127.0.0.1"; + + result = result.Replace("::ffff:", "127.0.0.1"); + + //Ip规则效验 + var regResult = Regex.IsMatch(result, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$"); + + result = regResult ? result : "127.0.0.1"; + return result; + } + + /// + /// 获取浏览器标识 + /// + /// + /// + public static string GetUserAgent(this HttpContext context) + { + return context.Request.Headers["User-Agent"]; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/AuditedObject.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/AuditedObject.cs new file mode 100644 index 00000000..8f298ce9 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/AuditedObject.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Data.Auditing +{ + public class AuditedObject : IAuditedObject + { + public DateTime CreationTime { get; set; } = DateTime.Now; + + public long? CreatorId { get; set; } + + public long? LastModifierId { get; set; } + + public DateTime? LastModificationTime { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IAuditedObject.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IAuditedObject.cs new file mode 100644 index 00000000..ef152e59 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IAuditedObject.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Data.Auditing +{ + public interface IAuditedObject : ICreationAuditedObject, IModificationAuditedObject + { + } + + public interface IAuditedObject : IAuditedObject, ICreationAuditedObject, IModificationAuditedObject + { + + } + +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/ICreationAuditedObject.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/ICreationAuditedObject.cs new file mode 100644 index 00000000..163abfd1 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/ICreationAuditedObject.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Data.Auditing +{ + public interface ICreationAuditedObject : IHasCreationTime, IMayHaveCreator + { + + } + + public interface ICreationAuditedObject : ICreationAuditedObject, IMayHaveCreator + { + + } + +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IDeletionAuditedObject.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IDeletionAuditedObject.cs new file mode 100644 index 00000000..7cfa8cd8 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IDeletionAuditedObject.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Data.Auditing +{ + public interface IDeletionAuditedObject : IHasDeletionTime + { + long? DeleterId { get; } + } + + public interface IDeletionAuditedObject : IDeletionAuditedObject + { + + TUser Deleter { get; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IFullAuditedObject.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IFullAuditedObject.cs new file mode 100644 index 00000000..d2e490cb --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IFullAuditedObject.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Data.Auditing +{ + public interface IFullAuditedObject : IAuditedObject, IDeletionAuditedObject + { + + } + + public interface IFullAuditedObject : IAuditedObject, IFullAuditedObject, IDeletionAuditedObject + { + + } + +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IHasCreationTime.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IHasCreationTime.cs new file mode 100644 index 00000000..3c295f18 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IHasCreationTime.cs @@ -0,0 +1,10 @@ +using System; + +namespace Yi.Framework.Infrastructure.Data.Auditing; + + +public interface IHasCreationTime +{ + + DateTime CreationTime { get; set; } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IHasDeletionTime.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IHasDeletionTime.cs new file mode 100644 index 00000000..08546e2e --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IHasDeletionTime.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Infrastructure.Data.Entities; + +namespace Yi.Framework.Infrastructure.Data.Auditing +{ + public interface IHasDeletionTime : ISoftDelete + { + DateTime? DeletionTime { get; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IHasEntityVersion.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IHasEntityVersion.cs new file mode 100644 index 00000000..ec25e44f --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IHasEntityVersion.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Data.Auditing +{ + public interface IHasEntityVersion + { + int EntityVersion { get; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IHasModificationTime.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IHasModificationTime.cs new file mode 100644 index 00000000..561b138b --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IHasModificationTime.cs @@ -0,0 +1,8 @@ +namespace Yi.Framework.Infrastructure.Data.Auditing; + + +public interface IHasModificationTime +{ + + DateTime? LastModificationTime { get; } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IMayHaveCreator.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IMayHaveCreator.cs new file mode 100644 index 00000000..828b8255 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IMayHaveCreator.cs @@ -0,0 +1,13 @@ +using System; + +namespace Yi.Framework.Infrastructure.Data.Auditing; + +public interface IMayHaveCreator +{ + TCreator Creator { get; } +} + +public interface IMayHaveCreator +{ + long? CreatorId { get; } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IModificationAuditedObject.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IModificationAuditedObject.cs new file mode 100644 index 00000000..5699ed89 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IModificationAuditedObject.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Data.Auditing +{ + public interface IModificationAuditedObject : IHasModificationTime + { + long? LastModifierId { get; } + } + + public interface IModificationAuditedObject : IModificationAuditedObject + { + TUser LastModifier { get; } + } + +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IMustHaveCreator.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IMustHaveCreator.cs new file mode 100644 index 00000000..6fa8440f --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Auditing/IMustHaveCreator.cs @@ -0,0 +1,13 @@ +using System; + +namespace Yi.Framework.Infrastructure.Data.Auditing; + +public interface IMustHaveCreator : IMustHaveCreator +{ + TCreator Creator { get; } +} + +public interface IMustHaveCreator +{ + long CreatorId { get; } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Entities/IMultiTenant.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Entities/IMultiTenant.cs new file mode 100644 index 00000000..9da9a648 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Entities/IMultiTenant.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Data.Entities +{ + public interface IMultiTenant + { + public Guid TenantId { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Entities/IOrderNum.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Entities/IOrderNum.cs new file mode 100644 index 00000000..4da6f86e --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Entities/IOrderNum.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Data.Entities +{ + public interface IOrderNum + { + int OrderNum { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Entities/ISoftDelete.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Entities/ISoftDelete.cs new file mode 100644 index 00000000..552dfadf --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Entities/ISoftDelete.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Data.Entities +{ + public interface ISoftDelete + { + public bool IsDeleted { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Entities/IState.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Entities/IState.cs new file mode 100644 index 00000000..2f393315 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Data/Entities/IState.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Data.Entities +{ + public interface IState + { + public bool State { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IEntityDto.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IEntityDto.cs new file mode 100644 index 00000000..1897dbe2 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IEntityDto.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Ddd.Dtos.Abstract +{ + public interface IEntityDto + { + } + + public interface IEntityDto : IEntityDto + { + TKey Id { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IHasTotalCount.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IHasTotalCount.cs new file mode 100644 index 00000000..5330e350 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IHasTotalCount.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Ddd.Dtos.Abstract +{ + public interface IHasTotalCount + { + long Total { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IListResult.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IListResult.cs new file mode 100644 index 00000000..c772a35d --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IListResult.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Ddd.Dtos.Abstract +{ + public interface IListResult + { + IReadOnlyList Items { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IPagedAllResultRequestDto.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IPagedAllResultRequestDto.cs new file mode 100644 index 00000000..03efe9bc --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IPagedAllResultRequestDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Infrastructure.Ddd.Services.Abstract; + +namespace Yi.Framework.Infrastructure.Ddd.Dtos.Abstract +{ + public interface IPagedAllResultRequestDto : IPageTimeResultRequestDto, IPagedAndSortedResultRequestDto + { + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IPagedAndSortedResultRequestDto.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IPagedAndSortedResultRequestDto.cs new file mode 100644 index 00000000..a616fa2f --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IPagedAndSortedResultRequestDto.cs @@ -0,0 +1,15 @@ + + +using Yi.Framework.Infrastructure.Enums; + +namespace Yi.Framework.Infrastructure.Ddd.Dtos.Abstract +{ + public interface IPagedAndSortedResultRequestDto + { + int PageNum { get; set; } + int PageSize { get; set; } + string? SortBy { get; set; } + + OrderByEnum SortType { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IPagedResult.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IPagedResult.cs new file mode 100644 index 00000000..7547fd91 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/Abstract/IPagedResult.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Ddd.Dtos.Abstract +{ + public interface IPagedResult : IListResult, IHasTotalCount + { + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/EntityDto.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/EntityDto.cs new file mode 100644 index 00000000..73ccf3b9 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/EntityDto.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; + +namespace Yi.Framework.Infrastructure.Ddd.Dtos +{ + [Serializable] + public abstract class EntityDto : EntityDto, IEntityDto, IEntityDto + { + // + // 摘要: + // Id of the entity. + public TKey Id { get; set; } + + public override string ToString() + { + return $"[DTO: {GetType().Name}] Id = {Id}"; + } + } + + [Serializable] + public abstract class EntityDto : IEntityDto + { + public override string ToString() + { + return "[DTO: " + GetType().Name + "]"; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/ListResultDto.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/ListResultDto.cs new file mode 100644 index 00000000..bb35ef6a --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/ListResultDto.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; + +namespace Yi.Framework.Infrastructure.Ddd.Dtos +{ + [Serializable] + public class ListResultDto : IListResult + { + public IReadOnlyList Items + { + get { return _items ?? (_items = new List()); } + set { _items = value; } + } + private IReadOnlyList _items; + + public ListResultDto() + { + + } + + public ListResultDto(IReadOnlyList items) + { + Items = items; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/PagedAllResultRequestDto.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/PagedAllResultRequestDto.cs new file mode 100644 index 00000000..f92781a6 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/PagedAllResultRequestDto.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; +using Yi.Framework.Infrastructure.Ddd.Services.Abstract; + +namespace Yi.Framework.Infrastructure.Ddd.Dtos +{ + public class PagedAllResultRequestDto : PagedAndSortedResultRequestDto, IPagedAllResultRequestDto, IPagedAndSortedResultRequestDto, IPageTimeResultRequestDto + { + /// + /// 查询开始时间条件 + /// + public DateTime? StartTime { get; set; } + + /// + /// 查询结束时间条件 + /// + public DateTime? EndTime { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/PagedAndSortedResultRequestDto.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/PagedAndSortedResultRequestDto.cs new file mode 100644 index 00000000..1a9d685a --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/PagedAndSortedResultRequestDto.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; +using Yi.Framework.Infrastructure.Enums; + +namespace Yi.Framework.Infrastructure.Ddd.Dtos +{ + public class PagedAndSortedResultRequestDto : IPagedAndSortedResultRequestDto + { + /// + /// 查询当前页条件 + /// + public int PageNum { get; set; } = 1; + + /// + /// 查询分页大小条件 + /// + public int PageSize { get; set; } = int.MaxValue; + + /// + /// 查询排序字段条件 + /// + public string? SortBy { get; set; } + + /// + /// 查询排序类别条件 + /// + public OrderByEnum SortType { get; set; } = OrderByEnum.Desc; + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/PagedDto.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/PagedDto.cs new file mode 100644 index 00000000..b36e5683 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/PagedDto.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Ddd.Dtos +{ + public class PagedDto + { + public PagedDto(long totalCount, List items) + { + Total = totalCount; + Items = items; + } + public long Total { get; set; } + + public List Items { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/PagedResultDto.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/PagedResultDto.cs new file mode 100644 index 00000000..030cb957 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Dtos/PagedResultDto.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; + +namespace Yi.Framework.Infrastructure.Ddd.Dtos +{ + public class PagedResultDto : ListResultDto, IPagedResult + { + public long Total { get; set; } + + public PagedResultDto() + { + + } + + public PagedResultDto(long totalCount, IReadOnlyList items) + : base(items) + { + Total = totalCount; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Entities/AggregateRoot.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Entities/AggregateRoot.cs new file mode 100644 index 00000000..bae304e0 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Entities/AggregateRoot.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Ddd.Entities +{ + public class AggregateRoot : IEntity, IAggregateRoot + { + } + public class AggregateRoot : Entity, IEntity + { + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Entities/Entity.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Entities/Entity.cs new file mode 100644 index 00000000..9fc1f0ed --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Entities/Entity.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Principal; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Ddd.Entities +{ + [Serializable] + public abstract class Entity : IEntity + { + protected Entity() + { + } + + public override string ToString() + { + return "[ENTITY: " + GetType().Name + "] Keys = " + GetKeys(); + } + + public abstract object[] GetKeys(); + + //实体比较简化 + //public bool EntityEquals(IEntity other) + //{ + // return this.GetKeys().Equals(other.GetKeys()); + //} + + } + + [Serializable] + public abstract class Entity : Entity, IEntity, IEntity + { + public virtual TKey Id { get; set; } + + protected Entity() + { + } + + protected Entity(TKey id) + { + Id = id; + } + + public override object[] GetKeys() + { + return new object[1] { Id }; + } + + public override string ToString() + { + return $"[ENTITY: {GetType().Name}] Id = {Id}"; + } + + + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Entities/IAggregateRoot.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Entities/IAggregateRoot.cs new file mode 100644 index 00000000..b0d7cd17 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Entities/IAggregateRoot.cs @@ -0,0 +1,16 @@ +using Microsoft.AspNetCore.DataProtection.KeyManagement; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Ddd.Entities +{ + public interface IAggregateRoot : IEntity + { + } + public interface IAggregateRoot : IEntity + { + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Entities/IEntity.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Entities/IEntity.cs new file mode 100644 index 00000000..7e31d1a8 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Entities/IEntity.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Ddd.Entities +{ + public interface IEntity + { + // + // 摘要: + // Returns an array of ordered keys for this entity. + + } + public interface IEntity : IEntity + { + // + // 摘要: + // Unique identifier for this entity. + TKey Id { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Repositories/IRepository.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Repositories/IRepository.cs new file mode 100644 index 00000000..032b920a --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Repositories/IRepository.cs @@ -0,0 +1,57 @@ +using System.Linq.Expressions; +using SqlSugar; +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; +using Yi.Framework.Infrastructure.Enums; + +namespace Yi.Framework.Infrastructure.Ddd.Repositories +{ + public interface IRepository + { + /// + /// 注释一下,严格意义这里应该protected,但是我认为 简易程度 与 耦合程度 中是需要进行衡量的 + /// + ISugarQueryable _DbQueryable { get; } + //单查 + Task GetByIdAsync(dynamic id); + Task GetSingleAsync(Expression> whereExpression); + Task GetFirstAsync(Expression> whereExpression); + Task IsAnyAsync(Expression> whereExpression); + Task CountAsync(Expression> whereExpression); + + //多查 + Task> GetListAsync(); + Task> GetListAsync(Expression> whereExpression); + + //分页查 + Task> GetPageListAsync(Expression> whereExpression, int pageNum, int pageSize); + Task> GetPageListAsync(Expression> whereExpression, int pageNum, int pageSize, Expression>? orderByExpression = null, OrderByEnum orderByType = OrderByEnum.Asc); + Task> GetPageListAsync(Expression> whereExpression, int pageNum, int pageSize, string? orderBy, OrderByEnum orderByType = OrderByEnum.Asc); + Task> GetPageListAsync(Expression> whereExpression, IPagedAndSortedResultRequestDto page); + Task> GetPageListAsync(Expression> whereExpression, IPagedAndSortedResultRequestDto page, Expression>? orderByExpression = null, OrderByEnum orderByType = OrderByEnum.Asc); + Task> GetPageListAsync(Expression> whereExpression, IPagedAndSortedResultRequestDto page, string? orderBy, OrderByEnum orderByType = OrderByEnum.Asc); + + //插入 + Task InsertAsync(T insertObj); + Task InsertOrUpdateAsync(T data); + Task InsertOrUpdateAsync(List datas); + Task InsertReturnIdentityAsync(T insertObj); + Task InsertReturnBigIdentityAsync(T insertObj); + Task InsertReturnSnowflakeIdAsync(T insertObj); + Task InsertReturnEntityAsync(T insertObj); + Task InsertRangeAsync(List insertObjs); + + //更新 + Task UpdateAsync(T updateObj); + Task UpdateRangeAsync(List updateObjs); + Task UpdateAsync(Expression> columns, Expression> whereExpression); + Task UpdateIgnoreNullAsync(T updateObj); + + //删除 + Task DeleteAsync(T deleteObj); + Task DeleteAsync(List deleteObjs); + Task DeleteAsync(Expression> whereExpression); + Task DeleteByIdAsync(dynamic id); + Task DeleteByIdsAsync(dynamic[] ids); + + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/IApplicationService.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/IApplicationService.cs new file mode 100644 index 00000000..e4b422af --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/IApplicationService.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Ddd.Services.Abstract +{ + public interface IApplicationService + { + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/ICreateAppService.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/ICreateAppService.cs new file mode 100644 index 00000000..8880e9ef --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/ICreateAppService.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Ddd.Services.Abstract +{ + public interface ICreateAppService + : ICreateAppService + { + + } + + public interface ICreateAppService + : IApplicationService + { + Task CreateAsync(TCreateInput input); + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/ICreateUpdateAppService.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/ICreateUpdateAppService.cs new file mode 100644 index 00000000..dab0842c --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/ICreateUpdateAppService.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Ddd.Services.Abstract +{ + public interface ICreateUpdateAppService + : ICreateUpdateAppService + { + + } + + public interface ICreateUpdateAppService + : ICreateUpdateAppService + { + + } + + public interface ICreateUpdateAppService + : ICreateAppService, + IUpdateAppService + { + + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/ICrudAppService.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/ICrudAppService.cs new file mode 100644 index 00000000..e161a12a --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/ICrudAppService.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Infrastructure.Ddd.Dtos; + +namespace Yi.Framework.Infrastructure.Ddd.Services.Abstract +{ + + public interface ICrudAppService + : ICrudAppService + { + + } + + public interface ICrudAppService + : ICrudAppService + { + + } + + public interface ICrudAppService + : ICrudAppService + { + + } + + public interface ICrudAppService + : ICrudAppService + { + + } + + public interface ICrudAppService + : IReadOnlyAppService, + ICreateUpdateAppService, + IDeleteAppService + { + + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/IDeleteAppService.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/IDeleteAppService.cs new file mode 100644 index 00000000..87517a59 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/IDeleteAppService.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Ddd.Services.Abstract +{ + public interface IDeleteAppService : IApplicationService + { + Task DeleteAsync(string id); + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/IPageTimeResultRequestDto.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/IPageTimeResultRequestDto.cs new file mode 100644 index 00000000..e565557c --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/IPageTimeResultRequestDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; + +namespace Yi.Framework.Infrastructure.Ddd.Services.Abstract +{ + public interface IPageTimeResultRequestDto : IPagedAndSortedResultRequestDto + { + DateTime? StartTime { get; set; } + DateTime? EndTime { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/IReadOnlyAppService.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/IReadOnlyAppService.cs new file mode 100644 index 00000000..55e81ccf --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/IReadOnlyAppService.cs @@ -0,0 +1,24 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos; + +namespace Yi.Framework.Infrastructure.Ddd.Services.Abstract +{ + public interface IReadOnlyAppService + : IReadOnlyAppService + { + + } + + public interface IReadOnlyAppService + : IReadOnlyAppService + { + + } + + public interface IReadOnlyAppService + : IApplicationService + { + Task GetAsync(TKey id); + + Task> GetListAsync(TGetListInput input); + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/IUpdateAppService.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/IUpdateAppService.cs new file mode 100644 index 00000000..4c19a06c --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/Abstract/IUpdateAppService.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Ddd.Services.Abstract +{ + public interface IUpdateAppService + : IUpdateAppService + { + + } + + public interface IUpdateAppService + : IApplicationService + { + Task UpdateAsync(TKey id, TUpdateInput input); + } + +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/ApplicationService.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/ApplicationService.cs new file mode 100644 index 00000000..b9cb74d6 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/ApplicationService.cs @@ -0,0 +1,11 @@ +using Furion; +using MapsterMapper; +using Microsoft.Extensions.DependencyInjection; + +namespace Yi.Framework.Infrastructure.Ddd.Services +{ + public abstract class ApplicationService + { + public IMapper _mapper { get => App.GetRequiredService(); } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/CrudAppService.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/CrudAppService.cs new file mode 100644 index 00000000..cca23a49 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/CrudAppService.cs @@ -0,0 +1,176 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Infrastructure.Ddd.Dtos; +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; +using Yi.Framework.Infrastructure.Ddd.Entities; +using Yi.Framework.Infrastructure.Ddd.Services.Abstract; +using Yi.Framework.Infrastructure.Helper; + +namespace Yi.Framework.Infrastructure.Ddd.Services +{ + + public abstract class CrudAppService + : CrudAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + { + } + + public abstract class CrudAppService + : CrudAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + { + } + + public abstract class CrudAppService + : CrudAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + { + } + + + + + public abstract class CrudAppService + : CrudAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + { + protected override Task MapToGetListOutputDtoAsync(TEntity entity) + { + return MapToGetOutputDtoAsync(entity); + } + + } + + public abstract class CrudAppService + : ReadOnlyAppService, + ICrudAppService + where TEntity : class, IEntity + where TGetOutputDto : IEntityDto + where TGetListOutputDto : IEntityDto + + { + protected virtual Task MapToEntityAsync(TGetListInput getListinput) + { + return Task.FromResult(_mapper.Map(getListinput)); + } + + + protected virtual Task MapToEntityAsync(TCreateInput createInput) + { + var entity = _mapper.Map(createInput); + + //这里判断实体的T,给id赋值 + + //雪花id + if (entity is IEntity entityForlongId) + { + if (entityForlongId.Id is default(long)) + { + //使用反射,暂时先使用sqlsuga的雪花id提供 + //ps: linshi + ReflexHelper.SetModelValue(nameof(IEntity.Id), SnowflakeHelper.NextId, entity); + } + } + if (entity is IEntity entityForGuidId) + { + if (entityForGuidId.Id == Guid.Empty) + { + ReflexHelper.SetModelValue(nameof(IEntity.Id), new Guid(), entity); + } + } + + return Task.FromResult(entity); + } + protected virtual Task MapToEntityAsync(TUpdateInput updateInput, TEntity entity) + { + _mapper.Map(updateInput, entity); + return Task.CompletedTask; + } + + protected virtual Task MapToEntityAsync(TUpdateInput updateInput) + { + var entity = _mapper.Map(updateInput); + return Task.FromResult(entity); + } + + /// + /// 增 + /// + /// + /// + public virtual async Task CreateAsync(TCreateInput input) + { + var entity = await MapToEntityAsync(input); + + //这里还可以设置租户 + await _repository.InsertAsync(entity); + + return await MapToGetOutputDtoAsync(entity); + } + + /// + /// 单、多删 + /// + /// + /// + /// + public virtual async Task DeleteAsync(string id) + { + if (id is null) + { + throw new ArgumentNullException(nameof(id)); + } + var idsValue = id.Split(','); + if (idsValue is null || idsValue.Length == 0) + { + throw new ArgumentNullException(nameof(id)); + } + return await _repository.DeleteByIdsAsync(idsValue.Select(x => (object)x!).ToArray()); + } + + ///// + ///// 删 + ///// + ///// + ///// + ///// + //public async Task DeleteAsync(TKey id) + //{ + // if (id is null) + // { + // throw new ArgumentNullException(nameof(id)); + // } + // return await _repository.DeleteByIdAsync(id); + //} + + /// + /// 改 + /// + /// + /// + /// + /// + public virtual async Task UpdateAsync(TKey id, TUpdateInput input) + { + if (id is null) + { + throw new ArgumentNullException(nameof(id)); + } + + var entity = await _repository.GetByIdAsync(id); + await MapToEntityAsync(input, entity); + await _repository.UpdateAsync(entity); + + return await MapToGetOutputDtoAsync(entity); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/ReadOnlyAppService.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/ReadOnlyAppService.cs new file mode 100644 index 00000000..33afc5c6 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Ddd/Services/ReadOnlyAppService.cs @@ -0,0 +1,128 @@ +using Furion; +using Microsoft.Extensions.DependencyInjection; +using SqlSugar; +using Yi.Framework.Infrastructure.Ddd.Dtos; +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; +using Yi.Framework.Infrastructure.Ddd.Entities; +using Yi.Framework.Infrastructure.Ddd.Repositories; +using Yi.Framework.Infrastructure.Ddd.Services.Abstract; + +namespace Yi.Framework.Infrastructure.Ddd.Services +{ + + public abstract class ReadOnlyAppService + : ReadOnlyAppService + where TEntity : class, IEntity + where TEntityDto : IEntityDto + { + } + + public abstract class ReadOnlyAppService +: ReadOnlyAppService +where TEntity : class, IEntity +where TEntityDto : IEntityDto + { + } + + + public abstract class ReadOnlyAppService : ApplicationService, + IReadOnlyAppService + where TEntity : class, IEntity + { + /// + /// 先暂时用服务定位的方式,之后将更改为属性注入 + /// + protected IRepository _repository { get => App.GetRequiredService>(); } + + protected ISugarQueryable _DbQueryable => _repository._DbQueryable; + + //Mapper + protected virtual Task MapToGetOutputDtoAsync(TEntity entity) + { + return Task.FromResult(_mapper.Map(entity)); + } + protected virtual Task> MapToGetListOutputDtosAsync(List entities) + { + var dtos = _mapper.Map>(entities); + + return Task.FromResult(dtos); + } + protected virtual Task MapToGetListOutputDtoAsync(TEntity entity) + { + var dto = _mapper.Map(entity); + return Task.FromResult(dto); + } + + /// + /// 单查 + /// + /// + /// + /// + public virtual async Task GetAsync(TKey id) + { + if (id is null) + { + throw new ArgumentNullException(nameof(id)); + } + + var entity = await _repository.GetByIdAsync(id); + + return await MapToGetOutputDtoAsync(entity); + } + + /// + /// 多查 + /// + /// + /// + public virtual async Task> GetListAsync(TGetListInput input) + { + var totalCount = -1; + + var entities = new List(); + var entityDtos = new List(); + + bool isPageList = true; + + //if (totalCount > 0) + //{ + + //这里还可以追加如果是审计日志,继续拼接条件即可 + if (input is IPageTimeResultRequestDto timeInput) + { + if (timeInput.StartTime is not null) + { + timeInput.EndTime = timeInput.EndTime ?? DateTime.Now; + } + } + + + + + if (input is IPagedAndSortedResultRequestDto sortInput) + { + entities = await _repository.GetPageListAsync(_ => true, sortInput, sortInput.SortBy, sortInput.SortType); + } + + + else + { + isPageList = false; + entities = await _repository.GetListAsync(); + } + entityDtos = await MapToGetListOutputDtosAsync(entities); + //} + + //如果是分页查询,还需要统计数量 + if (isPageList) + { + totalCount = await _repository.CountAsync(_ => true); + } + return new PagedResultDto( + totalCount, + entityDtos + ); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Enums/FileTypeEnum.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Enums/FileTypeEnum.cs new file mode 100644 index 00000000..1885e14a --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Enums/FileTypeEnum.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Enums +{ + /// + /// 定义公共文件路径 + /// + public enum FileTypeEnum + { + File, + Image, + Thumbnail, + Excel, + Temp + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Enums/OrderByEnum.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Enums/OrderByEnum.cs new file mode 100644 index 00000000..5856cf3b --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Enums/OrderByEnum.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Enums +{ + public enum OrderByEnum + { + Asc, + Desc + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Enums/QueryOperatorEnum.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Enums/QueryOperatorEnum.cs new file mode 100644 index 00000000..7f959c2f --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Enums/QueryOperatorEnum.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Enums +{ + public enum QueryOperatorEnum + { + /// + /// 相等 + /// + Equal, + /// + /// 匹配 + /// + Like, + /// + /// 大于 + /// + GreaterThan, + /// + /// 大于或等于 + /// + GreaterThanOrEqual, + /// + /// 小于 + /// + LessThan, + /// + /// 小于或等于 + /// + LessThanOrEqual, + /// + /// 等于集合 + /// + In, + /// + /// 不等于集合 + /// + NotIn, + /// + /// 左边匹配 + /// + LikeLeft, + /// + /// 右边匹配 + /// + LikeRight, + /// + /// 不相等 + /// + NoEqual, + /// + /// 为空或空 + /// + IsNullOrEmpty, + /// + /// 不为空 + /// + IsNot, + /// + /// 不匹配 + /// + NoLike, + /// + /// 时间段 值用 "|" 隔开 + /// + DateRange + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Enums/ResultCodeEnum.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Enums/ResultCodeEnum.cs new file mode 100644 index 00000000..76a76221 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Enums/ResultCodeEnum.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Enums +{ + public enum ResultCodeEnum + { + /// + /// 操作成功。 + /// + Success = 200, + + /// + /// 操作不成功 + /// + NotSuccess = 500, + + /// + /// 无权限 + /// + NoPermission = 401, + + /// + /// 被拒绝 + /// + Denied = 403 + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/AuthException.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/AuthException.cs new file mode 100644 index 00000000..b4959d8a --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/AuthException.cs @@ -0,0 +1,48 @@ +using System.Runtime.Serialization; +using Microsoft.Extensions.Logging; +using Yi.Framework.Infrastructure.Enums; + +namespace Yi.Framework.Infrastructure.Exceptions +{ + public class AuthException : Exception, + IHasErrorCode, + IHasErrorDetails, + IHasLogLevel + { + public int Code { get; set; } + + public string? Details { get; set; } + + public LogLevel LogLevel { get; set; } + + public AuthException( + + string? message = null, + ResultCodeEnum code = ResultCodeEnum.NoPermission, + string? details = null, + Exception? innerException = null, + LogLevel logLevel = LogLevel.Warning) + : base(message, innerException) + { + Code = (int)code; + Details = details; + LogLevel = logLevel; + } + + /// + /// 序列化参数的构造函数 + /// + public AuthException(SerializationInfo serializationInfo, StreamingContext context) + : base(serializationInfo, context) + { + + } + + public AuthException WithData(string name, object value) + { + Data[name] = value; + return this; + } + + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/BusinessException.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/BusinessException.cs new file mode 100644 index 00000000..2bcc847a --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/BusinessException.cs @@ -0,0 +1,47 @@ +using System.Runtime.Serialization; +using Microsoft.Extensions.Logging; +using Yi.Framework.Infrastructure.Enums; + +namespace Yi.Framework.Infrastructure.Exceptions +{ + public class BusinessException : Exception, + IHasErrorCode, + IHasErrorDetails, + IHasLogLevel + { + public int Code { get; set; } + + public string? Details { get; set; } + + public LogLevel LogLevel { get; set; } + + public BusinessException( + int code = (int)ResultCodeEnum.Denied, + string? message = null, + string? details = null, + Exception? innerException = null, + LogLevel logLevel = LogLevel.Warning) + : base(message, innerException) + { + Code = code; + Details = details; + LogLevel = logLevel; + } + + /// + /// 序列化参数的构造函数 + /// + public BusinessException(SerializationInfo serializationInfo, StreamingContext context) + : base(serializationInfo, context) + { + + } + + public BusinessException WithData(string name, object value) + { + Data[name] = value; + return this; + } + + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/ExceptionExtensions.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/ExceptionExtensions.cs new file mode 100644 index 00000000..128bb707 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/ExceptionExtensions.cs @@ -0,0 +1,41 @@ +using System.Runtime.ExceptionServices; +using Microsoft.Extensions.Logging; +using Yi.Framework.Infrastructure.Enums; + +namespace Yi.Framework.Infrastructure.Exceptions; + +/// +/// չ +/// +public static class ExceptionExtensions +{ + /// + /// ʹ ٴ׳쳣 + /// + /// Exception to be re-thrown + public static void ReThrow(this Exception exception) + { + ExceptionDispatchInfo.Capture(exception).Throw(); + } + + /// + /// ȡ쳣е־ȼ + /// + /// + /// + /// + public static LogLevel GetLogLevel(this Exception exception, LogLevel defaultLevel = LogLevel.Error) + { + return (exception as IHasLogLevel)?.LogLevel ?? defaultLevel; + } + /// + /// ȡ쳣е־ + /// + /// + /// + /// + public static int GetLogErrorCode(this Exception exception, ResultCodeEnum defaultCode = ResultCodeEnum.NotSuccess) + { + return (exception as IHasErrorCode)?.Code ?? (int)defaultCode; + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/IHasErrorCode.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/IHasErrorCode.cs new file mode 100644 index 00000000..1484820b --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/IHasErrorCode.cs @@ -0,0 +1,7 @@ +namespace Yi.Framework.Infrastructure.Exceptions +{ + internal interface IHasErrorCode + { + int Code { get; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/IHasErrorDetails.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/IHasErrorDetails.cs new file mode 100644 index 00000000..9600afe3 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/IHasErrorDetails.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Exceptions +{ + public interface IHasErrorDetails + { + string? Details { get; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/IHasLogLevel.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/IHasLogLevel.cs new file mode 100644 index 00000000..f4b02986 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/IHasLogLevel.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Exceptions +{ + public interface IHasLogLevel + { + LogLevel LogLevel { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/UserFriendlyException.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/UserFriendlyException.cs new file mode 100644 index 00000000..acb22783 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Exceptions/UserFriendlyException.cs @@ -0,0 +1,34 @@ +using System.Runtime.Serialization; +using Microsoft.Extensions.Logging; +using Yi.Framework.Infrastructure.Enums; + +namespace Yi.Framework.Infrastructure.Exceptions +{ + public class UserFriendlyException : BusinessException + { + public UserFriendlyException( +string message, +int code = (int)ResultCodeEnum.Denied, +string? details = null, +Exception? innerException = null, +LogLevel logLevel = LogLevel.Warning) +: base( + code, + message, + details, + innerException, + logLevel) + { + Details = details; + } + + /// + /// 序列化参数的构造函数 + /// + public UserFriendlyException(SerializationInfo serializationInfo, StreamingContext context) + : base(serializationInfo, context) + { + + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/AssemblyHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/AssemblyHelper.cs new file mode 100644 index 00000000..a88f371e --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/AssemblyHelper.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Helper +{ + public static class AssemblyHelper + { + + /// + /// 此处统一获取程序集,排除微软内部相关 + /// + /// + public static Assembly[] GetAllLoadAssembly() + { + return AppDomain.CurrentDomain.GetAssemblies(); + } + + public static List GetReferanceAssemblies(this AppDomain domain) + { + var list = new List(); + domain.GetAssemblies().ToList().ForEach(i => + { + GetReferanceAssemblies(i, list); + }); + return list; + } + private static void GetReferanceAssemblies(Assembly assembly, List list) + { + assembly.GetReferencedAssemblies().ToList().ForEach(i => + { + var ass = Assembly.Load(i); + if (!list.Contains(ass)) + { + list.Add(ass); + GetReferanceAssemblies(ass, list); + } + }); + } + + public static List GetClass(string assemblyFile, string? className = null, string? spaceName = null) + { + Assembly assembly = Assembly.Load(assemblyFile); + return assembly.GetTypes().Where(m => m.IsClass + && className == null ? true : m.Name == className + && spaceName == null ? true : m.Namespace == spaceName + && !m.Name.StartsWith("<>") + ).ToList(); + } + + public static List GetClassByParentClass(string assemblyFile, Type type) + { + Assembly assembly = Assembly.Load(assemblyFile); + + List resList = new List(); + + List typeList = assembly.GetTypes().Where(m => m.IsClass).ToList(); + foreach (var t in typeList) + { + var data = t.BaseType; + if (data == type) + { + resList.Add(t); + } + + } + return resList; + } + + + public static List GetClassByInterfaces(string assemblyFile, Type type) + { + Assembly assembly = Assembly.Load(assemblyFile); + + List resList = new List(); + + List typeList = assembly.GetTypes().Where(m => m.IsClass).ToList(); + foreach (var t in typeList) + { + var data = t.GetInterfaces(); + if (data.Contains(type)) + { + resList.Add(t); + } + + } + return resList; + } + + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/Base32Helper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/Base32Helper.cs new file mode 100644 index 00000000..b503bd3d --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/Base32Helper.cs @@ -0,0 +1,101 @@ +using System; +using System.Text; + +namespace Yi.Framework.Infrastructure.Helper +{ + public sealed class Base32Helper + { + + // the valid chars for the encoding + private static string ValidChars = "QAZ2WSX3" + "EDC4RFV5" + "TGB6YHN7" + "UJM8K9LP"; + + /// + /// Converts an array of bytes to a Base32-k string. + /// + public static string ToString(byte[] bytes) + { + StringBuilder sb = new StringBuilder(); // holds the base32 chars + byte index; + int hi = 5; + int currentByte = 0; + + while (currentByte < bytes.Length) + { + // do we need to use the next byte? + if (hi > 8) + { + // get the last piece from the current byte, shift it to the right + // and increment the byte counter + index = (byte)(bytes[currentByte++] >> hi - 5); + if (currentByte != bytes.Length) + { + // if we are not at the end, get the first piece from + // the next byte, clear it and shift it to the left + index = (byte)((byte)(bytes[currentByte] << 16 - hi) >> 3 | index); + } + + hi -= 3; + } + else if (hi == 8) + { + index = (byte)(bytes[currentByte++] >> 3); + hi -= 3; + } + else + { + + // simply get the stuff from the current byte + index = (byte)((byte)(bytes[currentByte] << 8 - hi) >> 3); + hi += 5; + } + + sb.Append(ValidChars[index]); + } + + return sb.ToString(); + } + + + /// + /// Converts a Base32-k string into an array of bytes. + /// + /// + /// Input string s contains invalid Base32-k characters. + /// + public static byte[] FromBase32String(string str) + { + int numBytes = str.Length * 5 / 8; + byte[] bytes = new byte[numBytes]; + + // all UPPERCASE chars + str = str.ToUpper(); + + int bit_buffer; + int currentCharIndex; + int bits_in_buffer; + + if (str.Length < 3) + { + bytes[0] = (byte)(ValidChars.IndexOf(str[0]) | ValidChars.IndexOf(str[1]) << 5); + return bytes; + } + + bit_buffer = ValidChars.IndexOf(str[0]) | ValidChars.IndexOf(str[1]) << 5; + bits_in_buffer = 10; + currentCharIndex = 2; + for (int i = 0; i < bytes.Length; i++) + { + bytes[i] = (byte)bit_buffer; + bit_buffer >>= 8; + bits_in_buffer -= 8; + while (bits_in_buffer < 8 && currentCharIndex < str.Length) + { + bit_buffer |= ValidChars.IndexOf(str[currentCharIndex++]) << bits_in_buffer; + bits_in_buffer += 5; + } + } + + return bytes; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/ConsoleHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/ConsoleHelper.cs new file mode 100644 index 00000000..66c2252c --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/ConsoleHelper.cs @@ -0,0 +1,54 @@ +using System; + +namespace Yi.Framework.Infrastructure.Helper +{ + public static class ConsoleHelper + { + public static void WriteColorLine(string str, ConsoleColor color) + { + ConsoleColor currentForeColor = Console.ForegroundColor; + Console.ForegroundColor = color; + Console.WriteLine(str); + Console.ForegroundColor = currentForeColor; + } + + /// + /// 打印错误信息 + /// + /// 待打印的字符串 + /// 想要打印的颜色 + public static void WriteErrorLine(this string str, ConsoleColor color = ConsoleColor.Red) + { + WriteColorLine(str, color); + } + + /// + /// 打印警告信息 + /// + /// 待打印的字符串 + /// 想要打印的颜色 + public static void WriteWarningLine(this string str, ConsoleColor color = ConsoleColor.Yellow) + { + WriteColorLine(str, color); + } + /// + /// 打印正常信息 + /// + /// 待打印的字符串 + /// 想要打印的颜色 + public static void WriteInfoLine(this string str, ConsoleColor color = ConsoleColor.White) + { + WriteColorLine(str, color); + } + /// + /// 打印成功的信息 + /// + /// 待打印的字符串 + /// 想要打印的颜色 + public static void WriteSuccessLine(this string str, ConsoleColor color = ConsoleColor.Green) + { + WriteColorLine(str, color); + } + + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/DateHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/DateHelper.cs new file mode 100644 index 00000000..44495f82 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/DateHelper.cs @@ -0,0 +1,58 @@ +using System; + +namespace Yi.Framework.Infrastructure.Helper +{ + public class DateHelper + { + public static DateTime StampToDateTime(string time) + { + time = time.Substring(0, 10); + double timestamp = Convert.ToInt64(time); + DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0); + dateTime = dateTime.AddSeconds(timestamp).ToLocalTime(); + return dateTime; + } + + public static string TimeSubTract(DateTime time1, DateTime time2) + { + TimeSpan subTract = time1.Subtract(time2); + return $"{subTract.Days} 天 {subTract.Hours} 时 {subTract.Minutes} 分 "; + } + /// + /// 时间戳转本地时间-时间戳精确到秒 + /// + public static DateTime ToLocalTimeDateBySeconds(long unix) + { + var dto = DateTimeOffset.FromUnixTimeSeconds(unix); + return dto.ToLocalTime().DateTime; + } + + /// + /// 时间转时间戳Unix-时间戳精确到秒 + /// + public static long ToUnixTimestampBySeconds(DateTime dt) + { + DateTimeOffset dto = new DateTimeOffset(dt); + return dto.ToUnixTimeSeconds(); + } + + + /// + /// 时间戳转本地时间-时间戳精确到毫秒 + /// + public static DateTime ToLocalTimeDateByMilliseconds(long unix) + { + var dto = DateTimeOffset.FromUnixTimeMilliseconds(unix); + return dto.ToLocalTime().DateTime; + } + + /// + /// 时间转时间戳Unix-时间戳精确到毫秒 + /// + public static long ToUnixTimestampByMilliseconds(DateTime dt) + { + DateTimeOffset dto = new DateTimeOffset(dt); + return dto.ToUnixTimeMilliseconds(); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/DistinctHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/DistinctHelper.cs new file mode 100644 index 00000000..32444dda --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/DistinctHelper.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Helper +{ + public class Compare : IEqualityComparer + { + private Func _getField; + public Compare(Func getfield) + { + _getField = getfield; + } + public bool Equals(T? x, T? y) + { + return EqualityComparer.Default.Equals(_getField(x!), _getField(y!)); + } + + public int GetHashCode(T obj) + { + return EqualityComparer.Default.GetHashCode(_getField(obj)!); + } + } + public static class DistinctHelper + { + /// + /// 自定义Distinct扩展方法 + /// + /// 要去重的对象类 + /// 自定义去重的字段类型 + /// 要去重的对象 + /// 获取自定义去重字段的委托 + /// + public static IEnumerable DistinctNew(this IEnumerable source, Func getfield) + { + return source.Distinct(new Compare(getfield)); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/EnumHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/EnumHelper.cs new file mode 100644 index 00000000..af984a8f --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/EnumHelper.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Helper +{ + public static class EnumHelper + { + public static New EnumToEnum(this object oldEnum) + { + if (oldEnum is null) + { + throw new ArgumentNullException(nameof(oldEnum)); + } + return (New)Enum.ToObject(typeof(New), oldEnum.GetHashCode()); + } + + public static TEnum StringToEnum(this string str) + { + return (TEnum)Enum.Parse(typeof(TEnum), str); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/ExpressionHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/ExpressionHelper.cs new file mode 100644 index 00000000..734e6e62 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/ExpressionHelper.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Helper +{ + public static class ExpressionHelper + { + + + /// + /// Expression表达式树lambda参数拼接组合 + /// + /// + /// + /// + /// + /// + public static Expression Compose(this Expression first, Expression second, Func merge) + { + var parameterMap = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f); + var secondBody = LambdaParameteRebinder.ReplaceParameter(parameterMap, second.Body); + return Expression.Lambda(merge(first.Body, secondBody), first.Parameters); + } + + /// + /// Expression表达式树lambda参数拼接--false + /// + /// + /// + public static Expression> False() => f => false; + + /// + /// Expression表达式树lambda参数拼接-true + /// + /// + /// + public static Expression> True() => f => true; + + /// + /// Expression表达式树lambda参数拼接--and + /// + /// + /// + /// + /// + public static Expression> And(this Expression> first, Expression> second) => first.Compose(second, Expression.And); + + /// + /// Expression表达式树lambda参数拼接--or + /// + /// + /// + /// + /// + public static Expression> Or(this Expression> first, Expression> second) => first.Compose(second, Expression.Or); + } + + public class LambdaParameteRebinder : ExpressionVisitor + { + /// + /// 存放表达式树的参数的字典 + /// + private readonly Dictionary map; + + /// + /// 构造函数 + /// + /// + public LambdaParameteRebinder(Dictionary map) + { + this.map = map ?? new Dictionary(); + } + + /// + /// 重载参数访问的方法,访问表达式树参数,如果字典中包含,则取出 + /// + /// 表达式树参数 + /// + protected override Expression VisitParameter(ParameterExpression node) + { + if (map.TryGetValue(node, out ParameterExpression expression)) + { + node = expression; + } + return base.VisitParameter(node); + } + + public static Expression ReplaceParameter(Dictionary map, Expression exp) + { + return new LambdaParameteRebinder(map).Visit(exp); + } + } + +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/FileHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/FileHelper.cs new file mode 100644 index 00000000..80c331ed --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/FileHelper.cs @@ -0,0 +1,490 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Yi.Framework.Infrastructure.Helper +{ + public class FileHelper : IDisposable + { + + private bool _alreadyDispose = false; + + + + #region 构造函数 + public FileHelper() + { + // + // TODO: 在此处添加构造函数逻辑 + // + } + ~FileHelper() + { + Dispose(); ; + } + + protected virtual void Dispose(bool isDisposing) + { + if (_alreadyDispose) return; + _alreadyDispose = true; + } + #endregion + + #region IDisposable 成员 + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + + #region 取得文件后缀名 + /**************************************** + * 函数名称:GetPostfixStr + * 功能说明:取得文件后缀名 + * 参 数:filename:文件名称 + * 调用示列: + * string filename = "aaa.aspx"; + * string s = EC.FileObj.GetPostfixStr(filename); + *****************************************/ + /// + /// 取后缀名 + /// + /// 文件名 + /// .gif|.html格式 + public static string GetPostfixStr(string filename) + { + int start = filename.LastIndexOf("."); + int length = filename.Length; + string postfix = filename.Substring(start, length - start); + return postfix; + } + #endregion + + #region 根据文件大小获取指定前缀的可用文件名 + /// + /// 根据文件大小获取指定前缀的可用文件名 + /// + /// 文件夹 + /// 文件前缀 + /// 文件大小(1m) + /// 文件后缀(.log) + /// 可用文件名 + //public static string GetAvailableFileWithPrefixOrderSize(string folderPath, string prefix, int size = 1 * 1024 * 1024, string ext = ".log") + //{ + // var allFiles = new DirectoryInfo(folderPath); + // var selectFiles = allFiles.GetFiles().Where(fi => fi.Name.ToLower().Contains(prefix.ToLower()) && fi.Extension.ToLower() == ext.ToLower() && fi.Length < size).OrderByDescending(d=>d.Name).ToList(); + + // if (selectFiles.Count > 0) + // { + // return selectFiles.FirstOrDefault().FullName; + // } + + // return Path.Combine(folderPath, $@"{prefix}_{DateTime.Now.DateToTimeStamp()}.log"); + //} + //public static string GetAvailableFileNameWithPrefixOrderSize(string _contentRoot, string prefix, int size = 1 * 1024 * 1024, string ext = ".log") + //{ + // var folderPath = Path.Combine(_contentRoot, "Log"); + // if (!Directory.Exists(folderPath)) + // { + // Directory.CreateDirectory(folderPath); + // } + + // var allFiles = new DirectoryInfo(folderPath); + // var selectFiles = allFiles.GetFiles().Where(fi => fi.Name.ToLower().Contains(prefix.ToLower()) && fi.Extension.ToLower() == ext.ToLower() && fi.Length < size).OrderByDescending(d => d.Name).ToList(); + + // if (selectFiles.Count > 0) + // { + // return selectFiles.FirstOrDefault().Name.Replace(".log",""); + // } + + // return $@"{prefix}_{DateTime.Now.DateToTimeStamp()}"; + //} + #endregion + + #region 写文件 + /**************************************** + * 函数名称:WriteFile + * 功能说明:写文件,会覆盖掉以前的内容 + * 参 数:Path:文件路径,Strings:文本内容 + * 调用示列: + * string Path = Server.MapPath("Default2.aspx"); + * string Strings = "这是我写的内容啊"; + * EC.FileObj.WriteFile(Path,Strings); + *****************************************/ + /// + /// 写文件 + /// + /// 文件路径 + /// 文件内容 + public static void WriteFile(string Path, string Strings) + { + if (!File.Exists(Path)) + { + FileStream f = File.Create(Path); + f.Close(); + } + StreamWriter f2 = new StreamWriter(Path, false, Encoding.GetEncoding("gb2312")); + f2.Write(Strings); + f2.Close(); + f2.Dispose(); + } + + /// + /// 写文件 + /// + /// 文件路径 + /// 文件内容 + /// 编码格式 + public static void WriteFile(string Path, string Strings, Encoding encode) + { + if (!File.Exists(Path)) + { + FileStream f = File.Create(Path); + f.Close(); + } + StreamWriter f2 = new StreamWriter(Path, false, encode); + f2.Write(Strings); + f2.Close(); + f2.Dispose(); + } + #endregion + + #region 读文件 + /**************************************** + * 函数名称:ReadFile + * 功能说明:读取文本内容 + * 参 数:Path:文件路径 + * 调用示列: + * string Path = Server.MapPath("Default2.aspx"); + * string s = EC.FileObj.ReadFile(Path); + *****************************************/ + /// + /// 读文件 + /// + /// 文件路径 + /// + public static string ReadFile(string Path) + { + string s = ""; + if (!File.Exists(Path)) + s = "不存在相应的目录"; + else + { + StreamReader f2 = new StreamReader(Path, Encoding.GetEncoding("gb2312")); + s = f2.ReadToEnd(); + f2.Close(); + f2.Dispose(); + } + + return s; + } + + /// + /// 读文件 + /// + /// 文件路径 + /// 编码格式 + /// + public static string ReadFile(string Path, Encoding encode) + { + string s = ""; + if (!File.Exists(Path)) + s = "不存在相应的目录"; + else + { + StreamReader f2 = new StreamReader(Path, encode); + s = f2.ReadToEnd(); + f2.Close(); + f2.Dispose(); + } + + return s; + } + #endregion + + #region 追加文件 + /**************************************** + * 函数名称:FileAdd + * 功能说明:追加文件内容 + * 参 数:Path:文件路径,strings:内容 + * 调用示列: + * string Path = Server.MapPath("Default2.aspx"); + * string Strings = "新追加内容"; + * EC.FileObj.FileAdd(Path, Strings); + *****************************************/ + /// + /// 追加文件 + /// + /// 文件路径 + /// 内容 + public static void FileAdd(string Path, string strings) + { + StreamWriter sw = File.AppendText(Path); + sw.Write(strings); + sw.Flush(); + sw.Close(); + } + #endregion + + #region 拷贝文件 + /**************************************** + * 函数名称:FileCoppy + * 功能说明:拷贝文件 + * 参 数:OrignFile:原始文件,NewFile:新文件路径 + * 调用示列: + * string orignFile = Server.MapPath("Default2.aspx"); + * string NewFile = Server.MapPath("Default3.aspx"); + * EC.FileObj.FileCoppy(OrignFile, NewFile); + *****************************************/ + /// + /// 拷贝文件 + /// + /// 原始文件 + /// 新文件路径 + public static void FileCoppy(string orignFile, string NewFile) + { + File.Copy(orignFile, NewFile, true); + } + + #endregion + + #region 删除文件 + /**************************************** + * 函数名称:FileDel + * 功能说明:删除文件 + * 参 数:Path:文件路径 + * 调用示列: + * string Path = Server.MapPath("Default3.aspx"); + * EC.FileObj.FileDel(Path); + *****************************************/ + /// + /// 删除文件 + /// + /// 路径 + public static void FileDel(string Path) + { + File.Delete(Path); + } + #endregion + + #region 移动文件 + /**************************************** + * 函数名称:FileMove + * 功能说明:移动文件 + * 参 数:OrignFile:原始路径,NewFile:新文件路径 + * 调用示列: + * string orignFile = Server.MapPath("../说明.txt"); + * string NewFile = Server.MapPath("http://www.cnblogs.com/说明.txt"); + * EC.FileObj.FileMove(OrignFile, NewFile); + *****************************************/ + /// + /// 移动文件 + /// + /// 原始路径 + /// 新路径 + public static void FileMove(string orignFile, string NewFile) + { + File.Move(orignFile, NewFile); + } + #endregion + + #region 在当前目录下创建目录 + /**************************************** + * 函数名称:FolderCreate + * 功能说明:在当前目录下创建目录 + * 参 数:OrignFolder:当前目录,NewFloder:新目录 + * 调用示列: + * string orignFolder = Server.MapPath("test/"); + * string NewFloder = "new"; + * EC.FileObj.FolderCreate(OrignFolder, NewFloder); + *****************************************/ + /// + /// 在当前目录下创建目录 + /// + /// 当前目录 + /// 新目录 + public static void FolderCreate(string orignFolder, string NewFloder) + { + Directory.SetCurrentDirectory(orignFolder); + Directory.CreateDirectory(NewFloder); + } + #endregion + + #region 递归删除文件夹目录及文件 + /**************************************** + * 函数名称:DeleteFolder + * 功能说明:递归删除文件夹目录及文件 + * 参 数:dir:文件夹路径 + * 调用示列: + * string dir = Server.MapPath("test/"); + * EC.FileObj.DeleteFolder(dir); + *****************************************/ + /// + /// 递归删除文件夹目录及文件 + /// + /// + /// + public static void DeleteFolder(string dir) + { + if (Directory.Exists(dir)) //如果存在这个文件夹删除之 + { + foreach (string d in Directory.GetFileSystemEntries(dir)) + { + if (File.Exists(d)) + File.Delete(d); //直接删除其中的文件 + else + DeleteFolder(d); //递归删除子文件夹 + } + Directory.Delete(dir); //删除已空文件夹 + } + + } + #endregion + + #region 将指定文件夹下面的所有内容copy到目标文件夹下面 果目标文件夹为只读属性就会报错。 + /**************************************** + * 函数名称:CopyDir + * 功能说明:将指定文件夹下面的所有内容copy到目标文件夹下面 果目标文件夹为只读属性就会报错。 + * 参 数:srcPath:原始路径,aimPath:目标文件夹 + * 调用示列: + * string srcPath = Server.MapPath("test/"); + * string aimPath = Server.MapPath("test1/"); + * EC.FileObj.CopyDir(srcPath,aimPath); + *****************************************/ + /// + /// 指定文件夹下面的所有内容copy到目标文件夹下面 + /// + /// 原始路径 + /// 目标文件夹 + public static void CopyDir(string srcPath, string aimPath) + { + try + { + // 检查目标目录是否以目录分割字符结束如果不是则添加之 + if (aimPath[aimPath.Length - 1] != Path.DirectorySeparatorChar) + aimPath += Path.DirectorySeparatorChar; + // 判断目标目录是否存在如果不存在则新建之 + if (!Directory.Exists(aimPath)) + Directory.CreateDirectory(aimPath); + // 得到源目录的文件列表,该里面是包含文件以及目录路径的一个数组 + //如果你指向copy目标文件下面的文件而不包含目录请使用下面的方法 + //string[] fileList = Directory.GetFiles(srcPath); + string[] fileList = Directory.GetFileSystemEntries(srcPath); + //遍历所有的文件和目录 + foreach (string file in fileList) + { + //先当作目录处理如果存在这个目录就递归Copy该目录下面的文件 + + if (Directory.Exists(file)) + CopyDir(file, aimPath + Path.GetFileName(file)); + //否则直接Copy文件 + else + File.Copy(file, aimPath + Path.GetFileName(file), true); + } + + } + catch (Exception ee) + { + throw new Exception(ee.ToString()); + } + } + #endregion + + /// + /// 获取目录下全部文件名 + /// + /// + /// + /// + public static List GetAllFileNames(string path, string pattern = "*") + { + List folder = new DirectoryInfo(path).GetFiles(pattern).ToList(); + + return folder.Select(x => x.Name).ToList(); + } + /// + /// 文件内容替换 + /// + public static string FileContentReplace(string path, string oldStr, string newStr) + { + var content = File.ReadAllText(path); + + if (content.Contains(oldStr)) + { + File.Delete(path); + File.WriteAllText(path, content.Replace(oldStr, newStr)); + } + + return path; + } + /// + /// 文件名称 + /// + public static string FileNameReplace(string path, string oldStr, string newStr) + { + string fileName = Path.GetFileName(path); + if (!fileName.Contains(oldStr)) + { + return path; + } + + string? directoryName = Path.GetDirectoryName(path); + string newFileName = fileName.Replace(oldStr, newStr); + string newPath = Path.Combine(directoryName ?? "", newFileName); + File.Move(path, newPath); + + return newPath; + } + /// + /// 目录名替换 + /// + public static string DirectoryNameReplace(string path, string oldStr, string newStr) + { + string fileName = Path.GetFileName(path); + if (!fileName.Contains(oldStr)) + { + return path; + } + + string? directoryName = Path.GetDirectoryName(path); + string newFileName = fileName.Replace(oldStr, newStr); + string newPath = Path.Combine(directoryName ?? "", newFileName); + Directory.Move(path, newPath); + return newPath; + } + + /// + /// 全部信息递归替换 + /// + /// + /// + /// + public static void AllInfoReplace(string dirPath, string oldStr, string newStr) + { + var path = DirectoryNameReplace(dirPath, oldStr, newStr); + var dirInfo = new DirectoryInfo(path); + var files = dirInfo.GetFiles(); + var dirs = dirInfo.GetDirectories(); + if (files.Length > 0) + { + foreach (var f in files) + { + FileContentReplace(f.FullName, oldStr, newStr); + FileNameReplace(f.FullName, oldStr, newStr); + } + } + if (dirs.Length > 0) + { + foreach (var d in dirs) + { + AllInfoReplace(d.FullName, oldStr, newStr); + } + } + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/HtmlHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/HtmlHelper.cs new file mode 100644 index 00000000..893253b9 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/HtmlHelper.cs @@ -0,0 +1,24 @@ +namespace Yi.Framework.Infrastructure.Helper +{ + public static class HtmlHelper + { + #region 去除富文本中的HTML标签 + /// + /// 去除富文本中的HTML标签 + /// + /// + /// + /// + public static string ReplaceHtmlTag(string html, int length = 0) + { + string strText = System.Text.RegularExpressions.Regex.Replace(html, "<[^>]+>", ""); + strText = System.Text.RegularExpressions.Regex.Replace(strText, "&[^;]+;", ""); + + if (length > 0 && strText.Length > length) + return strText.Substring(0, length); + + return strText; + } + #endregion + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/HttpHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/HttpHelper.cs new file mode 100644 index 00000000..8abcc5b1 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/HttpHelper.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Mime; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Helper +{ + public static class HttpHelper + { + + public static HttpClient Client { get; set; } = new HttpClient(); + + public static async Task Get(string url) + { + return await Client.GetStringAsync(url); + } + + public static async Task GetIO(string url) + { + return await Client.GetStreamAsync(url); + } + + + public static async Task Post(string url, object? item = null, Dictionary? head = null) + { + + using StringContent json = new(JsonSerializer.Serialize(item), Encoding.UTF8, MediaTypeNames.Application.Json); + + + if (head is not null) + { + foreach (var d in head) + { + json.Headers.Add(d.Key, d.Value); + } + } + + var httpResponse = await Client.PostAsync(url, json); + + httpResponse.EnsureSuccessStatusCode(); + + var content = httpResponse.Content; + + return await content.ReadAsStringAsync(); + } + + + // public static string HttpGet(string Url, string postDataStr="") + // { + //#pragma warning disable SYSLIB0014 // 类型或成员已过时 + // HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url + (postDataStr == "" ? "" : "?") + postDataStr); + //#pragma warning restore SYSLIB0014 // 类型或成员已过时 + // request.Method = "GET"; + // request.ContentType = "text/html;charset=UTF-8"; + + // HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + // Stream myResponseStream = response.GetResponseStream(); + // StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8")); + // string retString = myStreamReader.ReadToEnd(); + // myStreamReader.Close(); + // myResponseStream.Close(); + + // return retString; + // } + + // public static bool HttpIOGet(string Url, string file, string postDataStr="") + // { + // HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url + (postDataStr == "" ? "" : "?") + postDataStr); + // request.Method = "GET"; + // request.ContentType = "text/html;charset=UTF-8"; + + // HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + // Stream myResponseStream = response.GetResponseStream(); + // FileStream writer = new FileStream(file, FileMode.OpenOrCreate, FileAccess.Write); + // byte[] buffer = new byte[1024]; + // int c; + // while ((c = myResponseStream.Read(buffer, 0, buffer.Length)) > 0) + // { + // writer.Write(buffer, 0, c); + // } + // writer.Close(); + // myResponseStream.Close(); + + // return true; + // } + + // public static string HttpPost(string Url, string postDataStr="") + // { + // CookieContainer cookie = new CookieContainer(); + //#pragma warning disable SYSLIB0014 // 类型或成员已过时 + // HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url); + //#pragma warning restore SYSLIB0014 // 类型或成员已过时 + // request.Method = "POST"; + // request.ContentType = "application/x-www-form-urlencoded"; + // request.ContentLength = Encoding.UTF8.GetByteCount(postDataStr); + // request.CookieContainer = cookie; + + // Stream myRequestStream = request.GetRequestStream(); + // StreamWriter myStreamWriter = new StreamWriter(myRequestStream, Encoding.GetEncoding("gb2312")); + // myStreamWriter.Write(postDataStr); + // myStreamWriter.Close(); + + // HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + + // response.Cookies = cookie.GetCookies(response.ResponseUri); + // Stream myResponseStream = response.GetResponseStream(); + // StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8")); + // string retString = myStreamReader.ReadToEnd(); + // myStreamReader.Close(); + // myResponseStream.Close(); + + // return retString; + // } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/IdHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/IdHelper.cs new file mode 100644 index 00000000..ed4e7821 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/IdHelper.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Helper +{ + public static class IdHelper + { + public static dynamic[] ToDynamicArray(this IEnumerable ids) + { + return ids.Select(id => (dynamic)id).ToArray(); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/IpHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/IpHelper.cs new file mode 100644 index 00000000..888f9db1 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/IpHelper.cs @@ -0,0 +1,56 @@ +using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; + +namespace Yi.Framework.Infrastructure.Helper +{ + public class IpHelper + { + /// + /// 获取当前IP地址 + /// + /// + /// + public static string GetCurrentIp(string preferredNetworks) + { + var instanceIp = "127.0.0.1"; + + try + { + // 获取可用网卡 + var nics = NetworkInterface.GetAllNetworkInterfaces()?.Where(network => network.OperationalStatus == OperationalStatus.Up); + + // 获取所有可用网卡IP信息 + var ipCollection = nics?.Select(x => x.GetIPProperties())?.SelectMany(x => x.UnicastAddresses); + + if (ipCollection is null) + { + return instanceIp; + } + + foreach (var ipadd in ipCollection) + { + if (!IPAddress.IsLoopback(ipadd.Address) && ipadd.Address.AddressFamily == AddressFamily.InterNetwork) + { + if (string.IsNullOrEmpty(preferredNetworks)) + { + instanceIp = ipadd.Address.ToString(); + break; + } + + if (!ipadd.Address.ToString().StartsWith(preferredNetworks)) continue; + instanceIp = ipadd.Address.ToString(); + break; + } + } + } + catch + { + // ignored + } + + return instanceIp; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/JsonHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/JsonHelper.cs new file mode 100644 index 00000000..2daf839f --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/JsonHelper.cs @@ -0,0 +1,518 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; +using System.Collections.Generic; +using System.Text.Json; + +namespace Yi.Framework.Infrastructure.Helper +{ + public class JsonHelper + { + public static string ObjToStr(T obj, string dateTimeFormat) + { + IsoDateTimeConverter timeConverter = new IsoDateTimeConverter() + { + DateTimeFormat = dateTimeFormat + }; + return JsonConvert.SerializeObject(obj, Formatting.Indented, timeConverter); + } + + public static string ObjToStr(T obj) + { + return JsonConvert.SerializeObject(obj); + } + + public static T StrToObj(string str) + { + return JsonConvert.DeserializeObject(str)!; + } + /// + /// 转换对象为JSON格式数据 + /// + /// + /// 对象 + /// 字符格式的JSON数据 + public static string GetJSON(object obj) + { + string result = string.Empty; + try + { + System.Text.Json.JsonSerializer.Serialize(""); + System.Runtime.Serialization.Json.DataContractJsonSerializer serializer = + new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T)); + using (MemoryStream ms = new MemoryStream()) + { + serializer.WriteObject(ms, obj); + result = System.Text.Encoding.UTF8.GetString(ms.ToArray()); + } + } + catch (Exception) + { + throw; + } + return result; + } + /// + /// 转换List的数据为JSON格式 + /// + /// + /// 列表值 + /// JSON格式数据 + public string JSON(List vals) + { + System.Text.StringBuilder st = new System.Text.StringBuilder(); + try + { + System.Runtime.Serialization.Json.DataContractJsonSerializer s = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T)); + + foreach (T city in vals) + { + using (MemoryStream ms = new MemoryStream()) + { + s.WriteObject(ms, city); + st.Append(System.Text.Encoding.UTF8.GetString(ms.ToArray())); + } + } + } + catch (Exception) + { + } + + return st.ToString(); + } + /// + /// JSON格式字符转换为T类型的对象 + /// + /// + /// + /// + public static T ParseFormByJson(string jsonStr) + { + T obj = Activator.CreateInstance(); + using (MemoryStream ms = + new MemoryStream(System.Text.Encoding.UTF8.GetBytes(jsonStr))) + { + System.Runtime.Serialization.Json.DataContractJsonSerializer serializer = + new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T)); + return (T)serializer.ReadObject(ms)!; + } + } + + public string JSON1(List vals) + { + System.Text.StringBuilder st = new System.Text.StringBuilder(); + try + { + System.Runtime.Serialization.Json.DataContractJsonSerializer s = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(SendData)); + + foreach (SendData city in vals) + { + using (MemoryStream ms = new MemoryStream()) + { + s.WriteObject(ms, city); + st.Append(System.Text.Encoding.UTF8.GetString(ms.ToArray())); + } + } + } + catch (Exception) + { + } + + return st.ToString(); + } + + private static bool IsJsonStart(ref string json) + { + if (!string.IsNullOrEmpty(json)) + { + json = json.Trim('\r', '\n', ' '); + if (json.Length > 1) + { + char s = json[0]; + char e = json[json.Length - 1]; + return s == '{' && e == '}' || s == '[' && e == ']'; + } + } + return false; + } + public static bool IsJson(string json) + { + int errIndex; + return IsJson(json, out errIndex); + } + public static bool IsJson(string json, out int errIndex) + { + errIndex = 0; + if (IsJsonStart(ref json)) + { + CharState cs = new CharState(); + char c; + for (int i = 0; i < json.Length; i++) + { + c = json[i]; + if (SetCharState(c, ref cs) && cs.childrenStart)//设置关键符号状态。 + { + string item = json.Substring(i); + int err; + int length = GetValueLength(item, true, out err); + cs.childrenStart = false; + if (err > 0) + { + errIndex = i + err; + return false; + } + i = i + length - 1; + } + if (cs.isError) + { + errIndex = i; + return false; + } + } + + return !cs.arrayStart && !cs.jsonStart; + } + return false; + } + + /// + /// 获取值的长度(当Json值嵌套以"{"或"["开头时) + /// + private static int GetValueLength(string json, bool breakOnErr, out int errIndex) + { + errIndex = 0; + int len = 0; + if (!string.IsNullOrEmpty(json)) + { + CharState cs = new CharState(); + char c; + for (int i = 0; i < json.Length; i++) + { + c = json[i]; + if (!SetCharState(c, ref cs))//设置关键符号状态。 + { + if (!cs.jsonStart && !cs.arrayStart)//json结束,又不是数组,则退出。 + { + break; + } + } + else if (cs.childrenStart)//正常字符,值状态下。 + { + int length = GetValueLength(json.Substring(i), breakOnErr, out errIndex);//递归子值,返回一个长度。。。 + cs.childrenStart = false; + cs.valueStart = 0; + //cs.state = 0; + i = i + length - 1; + } + if (breakOnErr && cs.isError) + { + errIndex = i; + return i; + } + if (!cs.jsonStart && !cs.arrayStart)//记录当前结束位置。 + { + len = i + 1;//长度比索引+1 + break; + } + } + } + return len; + } + + /// + /// 设置字符状态(返回true则为关键词,返回false则当为普通字符处理) + /// + private static bool SetCharState(char c, ref CharState cs) + { + cs.CheckIsError(c); + switch (c) + { + case '{'://[{ "[{A}]":[{"[{B}]":3,"m":"C"}]}] + #region 大括号 + if (cs.keyStart <= 0 && cs.valueStart <= 0) + { + cs.keyStart = 0; + cs.valueStart = 0; + if (cs.jsonStart && cs.state == 1) + { + cs.childrenStart = true; + } + else + { + cs.state = 0; + } + cs.jsonStart = true;//开始。 + return true; + } + #endregion + break; + case '}': + #region 大括号结束 + if (cs.keyStart <= 0 && cs.valueStart < 2 && cs.jsonStart) + { + cs.jsonStart = false;//正常结束。 + cs.state = 0; + cs.keyStart = 0; + cs.valueStart = 0; + cs.setDicValue = true; + return true; + } + // cs.isError = !cs.jsonStart && cs.state == 0; + #endregion + break; + case '[': + #region 中括号开始 + if (!cs.jsonStart) + { + cs.arrayStart = true; + return true; + } + else if (cs.jsonStart && cs.state == 1) + { + cs.childrenStart = true; + return true; + } + #endregion + break; + case ']': + #region 中括号结束 + if (cs.arrayStart && !cs.jsonStart && cs.keyStart <= 2 && cs.valueStart <= 0)//[{},333]//这样结束。 + { + cs.keyStart = 0; + cs.valueStart = 0; + cs.arrayStart = false; + return true; + } + #endregion + break; + case '"': + case '\'': + #region 引号 + if (cs.jsonStart || cs.arrayStart) + { + if (cs.state == 0)//key阶段,有可能是数组["aa",{}] + { + if (cs.keyStart <= 0) + { + cs.keyStart = c == '"' ? 3 : 2; + return true; + } + else if (cs.keyStart == 2 && c == '\'' || cs.keyStart == 3 && c == '"') + { + if (!cs.escapeChar) + { + cs.keyStart = -1; + return true; + } + else + { + cs.escapeChar = false; + } + } + } + else if (cs.state == 1 && cs.jsonStart)//值阶段必须是Json开始了。 + { + if (cs.valueStart <= 0) + { + cs.valueStart = c == '"' ? 3 : 2; + return true; + } + else if (cs.valueStart == 2 && c == '\'' || cs.valueStart == 3 && c == '"') + { + if (!cs.escapeChar) + { + cs.valueStart = -1; + return true; + } + else + { + cs.escapeChar = false; + } + } + + } + } + #endregion + break; + case ':': + #region 冒号 + if (cs.jsonStart && cs.keyStart < 2 && cs.valueStart < 2 && cs.state == 0) + { + if (cs.keyStart == 1) + { + cs.keyStart = -1; + } + cs.state = 1; + return true; + } + // cs.isError = !cs.jsonStart || (cs.keyStart < 2 && cs.valueStart < 2 && cs.state == 1); + #endregion + break; + case ',': + #region 逗号 //["aa",{aa:12,}] + + if (cs.jsonStart) + { + if (cs.keyStart < 2 && cs.valueStart < 2 && cs.state == 1) + { + cs.state = 0; + cs.keyStart = 0; + cs.valueStart = 0; + //if (cs.valueStart == 1) + //{ + // cs.valueStart = 0; + //} + cs.setDicValue = true; + return true; + } + } + else if (cs.arrayStart && cs.keyStart <= 2) + { + cs.keyStart = 0; + //if (cs.keyStart == 1) + //{ + // cs.keyStart = -1; + //} + return true; + } + #endregion + break; + case ' ': + case '\r': + case '\n'://[ "a",\r\n{} ] + case '\0': + case '\t': + if (cs.keyStart <= 0 && cs.valueStart <= 0) //cs.jsonStart && + { + return true;//跳过空格。 + } + break; + default: //值开头。。 + if (c == '\\') //转义符号 + { + if (cs.escapeChar) + { + cs.escapeChar = false; + } + else + { + cs.escapeChar = true; + return true; + } + } + else + { + cs.escapeChar = false; + } + if (cs.jsonStart || cs.arrayStart) // Json 或数组开始了。 + { + if (cs.keyStart <= 0 && cs.state == 0) + { + cs.keyStart = 1;//无引号的 + } + else if (cs.valueStart <= 0 && cs.state == 1 && cs.jsonStart)//只有Json开始才有值。 + { + cs.valueStart = 1;//无引号的 + } + } + break; + } + return false; + } + } + /// + /// 字符状态 + /// + public class CharState + { + internal bool jsonStart = false;//以 "{"开始了... + internal bool setDicValue = false;// 可以设置字典值了。 + internal bool escapeChar = false;//以"\"转义符号开始了 + /// + /// 数组开始【仅第一开头才算】,值嵌套的以【childrenStart】来标识。 + /// + internal bool arrayStart = false;//以"[" 符号开始了 + internal bool childrenStart = false;//子级嵌套开始了。 + /// + /// 【0 初始状态,或 遇到“,”逗号】;【1 遇到“:”冒号】 + /// + internal int state = 0; + + /// + /// 【-1 取值结束】【0 未开始】【1 无引号开始】【2 单引号开始】【3 双引号开始】 + /// + internal int keyStart = 0; + /// + /// 【-1 取值结束】【0 未开始】【1 无引号开始】【2 单引号开始】【3 双引号开始】 + /// + internal int valueStart = 0; + internal bool isError = false;//是否语法错误。 + + internal void CheckIsError(char c)//只当成一级处理(因为GetLength会递归到每一个子项处理) + { + if (keyStart > 1 || valueStart > 1) + { + return; + } + //示例 ["aa",{"bbbb":123,"fff","ddd"}] + switch (c) + { + case '{'://[{ "[{A}]":[{"[{B}]":3,"m":"C"}]}] + isError = jsonStart && state == 0;//重复开始错误 同时不是值处理。 + break; + case '}': + isError = !jsonStart || keyStart != 0 && state == 0;//重复结束错误 或者 提前结束{"aa"}。正常的有{} + break; + case '[': + isError = arrayStart && state == 0;//重复开始错误 + break; + case ']': + isError = !arrayStart || jsonStart;//重复开始错误 或者 Json 未结束 + break; + case '"': + case '\'': + isError = !(jsonStart || arrayStart); //json 或数组开始。 + if (!isError) + { + //重复开始 [""",{"" "}] + isError = state == 0 && keyStart == -1 || state == 1 && valueStart == -1; + } + if (!isError && arrayStart && !jsonStart && c == '\'')//['aa',{}] + { + isError = true; + } + break; + case ':': + isError = !jsonStart || state == 1;//重复出现。 + break; + case ',': + isError = !(jsonStart || arrayStart); //json 或数组开始。 + if (!isError) + { + if (jsonStart) + { + isError = state == 0 || state == 1 && valueStart > 1;//重复出现。 + } + else if (arrayStart)//["aa,] [,] [{},{}] + { + isError = keyStart == 0 && !setDicValue; + } + } + break; + case ' ': + case '\r': + case '\n'://[ "a",\r\n{} ] + case '\0': + case '\t': + break; + default: //值开头。。 + isError = !jsonStart && !arrayStart || state == 0 && keyStart == -1 || valueStart == -1 && state == 1;// + break; + } + //if (isError) + //{ + + //} + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/MD5Hepler.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/MD5Hepler.cs new file mode 100644 index 00000000..e7c2c000 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/MD5Hepler.cs @@ -0,0 +1,132 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace Yi.Framework.Infrastructure.Helper +{ + public class MD5Helper + { + /// + /// 生成PasswordSalt + /// + /// 返回string + public static string GenerateSalt() + { + byte[] buf = new byte[16]; +#pragma warning disable SYSLIB0023 // 类型或成员已过时 + new RNGCryptoServiceProvider().GetBytes(buf); +#pragma warning restore SYSLIB0023 // 类型或成员已过时 + return Convert.ToBase64String(buf); + } + + /// + /// 加密密码 + /// + /// 密码 + /// 加密类型 + /// PasswordSalt + /// 加密后的密码 + public static string SHA2Encode(string pass, string salt, int passwordFormat = 1) + { + if (passwordFormat == 0) // MembershipPasswordFormat.Clear + return pass; + + byte[] bIn = Encoding.Unicode.GetBytes(pass); + byte[] bSalt = Convert.FromBase64String(salt); + byte[] bAll = new byte[bSalt.Length + bIn.Length]; + byte[]? bRet = null; + + Buffer.BlockCopy(bSalt, 0, bAll, 0, bSalt.Length); + Buffer.BlockCopy(bIn, 0, bAll, bSalt.Length, bIn.Length); + +#pragma warning disable SYSLIB0021 // 类型或成员已过时 + var s = SHA512.Create(); +#pragma warning restore SYSLIB0021 // 类型或成员已过时 + bRet = s.ComputeHash(bAll); + + return ConvertEx.ToUrlBase64String(bRet); + } + + /// + /// 16位MD5加密 + /// + /// + /// + public static string MD5Encrypt16(string password) + { + var md5 = MD5.Create(); + string t2 = BitConverter.ToString(md5.ComputeHash(Encoding.Default.GetBytes(password)), 4, 8); + t2 = t2.Replace("-", string.Empty); + return t2; + } + + /// + /// 32位MD5加密 + /// + /// + /// + public static string MD5Encrypt32(string password = "") + { + string pwd = string.Empty; + try + { + if (!string.IsNullOrEmpty(password) && !string.IsNullOrWhiteSpace(password)) + { + MD5 md5 = MD5.Create(); //实例化一个md5对像 + // 加密后是一个字节类型的数组,这里要注意编码UTF8/Unicode等的选择  + byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(password)); + // 通过使用循环,将字节类型的数组转换为字符串,此字符串是常规字符格式化所得 + foreach (var item in s) + { + // 将得到的字符串使用十六进制类型格式。格式后的字符是小写的字母,如果使用大写(X)则格式后的字符是大写字符 + pwd = string.Concat(pwd, item.ToString("X2")); + } + } + } + catch + { + throw new Exception($"错误的 password 字符串:【{password}】"); + } + return pwd; + } + + /// + /// 64位MD5加密 + /// + /// + /// + public static string MD5Encrypt64(string password) + { + // 实例化一个md5对像 + // 加密后是一个字节类型的数组,这里要注意编码UTF8/Unicode等的选择  + MD5 md5 = MD5.Create(); + byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(password)); + return Convert.ToBase64String(s); + } + } + public class ConvertEx + { + static readonly char[] padding = { '=' }; + public static string ToUrlBase64String(byte[] inArray) + { + var str = Convert.ToBase64String(inArray); + str = str.TrimEnd(padding).Replace('+', '-').Replace('/', '_'); + + return str; + } + + public static byte[] FromUrlBase64String(string s) + { + string incoming = s.Replace('_', '/').Replace('-', '+'); + switch (s.Length % 4) + { + case 2: incoming += "=="; break; + case 3: incoming += "="; break; + } + byte[] bytes = Convert.FromBase64String(incoming); + + return bytes; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/MimeHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/MimeHelper.cs new file mode 100644 index 00000000..50398493 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/MimeHelper.cs @@ -0,0 +1,260 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Infrastructure.Enums; + +namespace Yi.Framework.Infrastructure.Helper +{ + public static class MimeHelper + { + // 通过自己定义一个静态类 + // 将所有的Content Type都扔进去吧 + // 调用的时候直接调用静态方法即可。 + + public static List ImageType { get; set; } = new List + { + ".jpg",".png",".jpge" + }; + + private static Hashtable _mimeMappingTable; + + private static void AddMimeMapping(string extension, string MimeType) + { + _mimeMappingTable.Add(extension, MimeType); + } + + public static string GetMimeMapping(string FileName) + { + string text = null!; + int num = FileName.LastIndexOf('.'); + if (0 < num && num > FileName.LastIndexOf('\\')) + { + text = (string)_mimeMappingTable[FileName.Substring(num)]!; + } + if (text == null) + { + text = (string)_mimeMappingTable[".*"]!; + } + return text; + } + + public static FileTypeEnum GetFileType(string fileName) + { + var extension = Path.GetExtension(fileName); + if (ImageType.Contains(extension.ToLower())) + return FileTypeEnum.Image; + return FileTypeEnum.File; + + + } + + static MimeHelper() + { + _mimeMappingTable = new Hashtable(190, StringComparer.CurrentCultureIgnoreCase); + AddMimeMapping(".323", "text/h323"); + AddMimeMapping(".asx", "video/x-ms-asf"); + AddMimeMapping(".acx", "application/internet-property-stream"); + AddMimeMapping(".ai", "application/postscript"); + AddMimeMapping(".aif", "audio/x-aiff"); + AddMimeMapping(".aiff", "audio/aiff"); + AddMimeMapping(".axs", "application/olescript"); + AddMimeMapping(".aifc", "audio/aiff"); + AddMimeMapping(".asr", "video/x-ms-asf"); + AddMimeMapping(".avi", "video/x-msvideo"); + AddMimeMapping(".asf", "video/x-ms-asf"); + AddMimeMapping(".au", "audio/basic"); + AddMimeMapping(".application", "application/x-ms-application"); + AddMimeMapping(".bin", "application/octet-stream"); + AddMimeMapping(".bas", "text/plain"); + AddMimeMapping(".bcpio", "application/x-bcpio"); + AddMimeMapping(".bmp", "image/bmp"); + AddMimeMapping(".cdf", "application/x-cdf"); + AddMimeMapping(".cat", "application/vndms-pkiseccat"); + AddMimeMapping(".crt", "application/x-x509-ca-cert"); + AddMimeMapping(".c", "text/plain"); + AddMimeMapping(".css", "text/css"); + AddMimeMapping(".cer", "application/x-x509-ca-cert"); + AddMimeMapping(".crl", "application/pkix-crl"); + AddMimeMapping(".cmx", "image/x-cmx"); + AddMimeMapping(".csh", "application/x-csh"); + AddMimeMapping(".cod", "image/cis-cod"); + AddMimeMapping(".cpio", "application/x-cpio"); + AddMimeMapping(".clp", "application/x-msclip"); + AddMimeMapping(".crd", "application/x-mscardfile"); + AddMimeMapping(".deploy", "application/octet-stream"); + AddMimeMapping(".dll", "application/x-msdownload"); + AddMimeMapping(".dot", "application/msword"); + AddMimeMapping(".doc", "application/msword"); + AddMimeMapping(".dvi", "application/x-dvi"); + AddMimeMapping(".dir", "application/x-director"); + AddMimeMapping(".dxr", "application/x-director"); + AddMimeMapping(".der", "application/x-x509-ca-cert"); + AddMimeMapping(".dib", "image/bmp"); + AddMimeMapping(".dcr", "application/x-director"); + AddMimeMapping(".disco", "text/xml"); + AddMimeMapping(".exe", "application/octet-stream"); + AddMimeMapping(".etx", "text/x-setext"); + AddMimeMapping(".evy", "application/envoy"); + AddMimeMapping(".eml", "message/rfc822"); + AddMimeMapping(".eps", "application/postscript"); + AddMimeMapping(".flr", "x-world/x-vrml"); + AddMimeMapping(".fif", "application/fractals"); + AddMimeMapping(".gtar", "application/x-gtar"); + AddMimeMapping(".gif", "image/gif"); + AddMimeMapping(".gz", "application/x-gzip"); + AddMimeMapping(".hta", "application/hta"); + AddMimeMapping(".htc", "text/x-component"); + AddMimeMapping(".htt", "text/webviewhtml"); + AddMimeMapping(".h", "text/plain"); + AddMimeMapping(".hdf", "application/x-hdf"); + AddMimeMapping(".hlp", "application/winhlp"); + AddMimeMapping(".html", "text/html"); + AddMimeMapping(".htm", "text/html"); + AddMimeMapping(".hqx", "application/mac-binhex40"); + AddMimeMapping(".isp", "application/x-internet-signup"); + AddMimeMapping(".iii", "application/x-iphone"); + AddMimeMapping(".ief", "image/ief"); + AddMimeMapping(".ivf", "video/x-ivf"); + AddMimeMapping(".ins", "application/x-internet-signup"); + AddMimeMapping(".ico", "image/x-icon"); + AddMimeMapping(".jpg", "image/jpeg"); + AddMimeMapping(".jfif", "image/pjpeg"); + AddMimeMapping(".jpe", "image/jpeg"); + AddMimeMapping(".jpeg", "image/jpeg"); + AddMimeMapping(".js", "application/x-javascript"); + AddMimeMapping(".lsx", "video/x-la-asf"); + AddMimeMapping(".latex", "application/x-latex"); + AddMimeMapping(".lsf", "video/x-la-asf"); + AddMimeMapping(".manifest", "application/x-ms-manifest"); + AddMimeMapping(".mhtml", "message/rfc822"); + AddMimeMapping(".mny", "application/x-msmoney"); + AddMimeMapping(".mht", "message/rfc822"); + AddMimeMapping(".mid", "audio/mid"); + AddMimeMapping(".mpv2", "video/mpeg"); + AddMimeMapping(".man", "application/x-troff-man"); + AddMimeMapping(".mvb", "application/x-msmediaview"); + AddMimeMapping(".mpeg", "video/mpeg"); + AddMimeMapping(".m3u", "audio/x-mpegurl"); + AddMimeMapping(".mdb", "application/x-msaccess"); + AddMimeMapping(".mpp", "application/vnd.ms-project"); + AddMimeMapping(".m1v", "video/mpeg"); + AddMimeMapping(".mpa", "video/mpeg"); + AddMimeMapping(".me", "application/x-troff-me"); + AddMimeMapping(".m13", "application/x-msmediaview"); + AddMimeMapping(".movie", "video/x-sgi-movie"); + AddMimeMapping(".m14", "application/x-msmediaview"); + AddMimeMapping(".mpe", "video/mpeg"); + AddMimeMapping(".mp2", "video/mpeg"); + AddMimeMapping(".mov", "video/quicktime"); + AddMimeMapping(".mp3", "audio/mpeg"); + AddMimeMapping(".mpg", "video/mpeg"); + AddMimeMapping(".ms", "application/x-troff-ms"); + AddMimeMapping(".nc", "application/x-netcdf"); + AddMimeMapping(".nws", "message/rfc822"); + AddMimeMapping(".oda", "application/oda"); + AddMimeMapping(".ods", "application/oleobject"); + AddMimeMapping(".pmc", "application/x-perfmon"); + AddMimeMapping(".p7r", "application/x-pkcs7-certreqresp"); + AddMimeMapping(".p7b", "application/x-pkcs7-certificates"); + AddMimeMapping(".p7s", "application/pkcs7-signature"); + AddMimeMapping(".pmw", "application/x-perfmon"); + AddMimeMapping(".ps", "application/postscript"); + AddMimeMapping(".p7c", "application/pkcs7-mime"); + AddMimeMapping(".pbm", "image/x-portable-bitmap"); + AddMimeMapping(".ppm", "image/x-portable-pixmap"); + AddMimeMapping(".pub", "application/x-mspublisher"); + AddMimeMapping(".pnm", "image/x-portable-anymap"); + AddMimeMapping(".png", "image/png"); + AddMimeMapping(".pml", "application/x-perfmon"); + AddMimeMapping(".p10", "application/pkcs10"); + AddMimeMapping(".pfx", "application/x-pkcs12"); + AddMimeMapping(".p12", "application/x-pkcs12"); + AddMimeMapping(".pdf", "application/pdf"); + AddMimeMapping(".pps", "application/vnd.ms-powerpoint"); + AddMimeMapping(".p7m", "application/pkcs7-mime"); + AddMimeMapping(".pko", "application/vndms-pkipko"); + AddMimeMapping(".ppt", "application/vnd.ms-powerpoint"); + AddMimeMapping(".pmr", "application/x-perfmon"); + AddMimeMapping(".pma", "application/x-perfmon"); + AddMimeMapping(".pot", "application/vnd.ms-powerpoint"); + AddMimeMapping(".prf", "application/pics-rules"); + AddMimeMapping(".pgm", "image/x-portable-graymap"); + AddMimeMapping(".qt", "video/quicktime"); + AddMimeMapping(".ra", "audio/x-pn-realaudio"); + AddMimeMapping(".rgb", "image/x-rgb"); + AddMimeMapping(".ram", "audio/x-pn-realaudio"); + AddMimeMapping(".rmi", "audio/mid"); + AddMimeMapping(".ras", "image/x-cmu-raster"); + AddMimeMapping(".roff", "application/x-troff"); + AddMimeMapping(".rtf", "application/rtf"); + AddMimeMapping(".rtx", "text/richtext"); + AddMimeMapping(".sv4crc", "application/x-sv4crc"); + AddMimeMapping(".spc", "application/x-pkcs7-certificates"); + AddMimeMapping(".setreg", "application/set-registration-initiation"); + AddMimeMapping(".snd", "audio/basic"); + AddMimeMapping(".stl", "application/vndms-pkistl"); + AddMimeMapping(".setpay", "application/set-payment-initiation"); + AddMimeMapping(".stm", "text/html"); + AddMimeMapping(".shar", "application/x-shar"); + AddMimeMapping(".sh", "application/x-sh"); + AddMimeMapping(".sit", "application/x-stuffit"); + AddMimeMapping(".spl", "application/futuresplash"); + AddMimeMapping(".sct", "text/scriptlet"); + AddMimeMapping(".scd", "application/x-msschedule"); + AddMimeMapping(".sst", "application/vndms-pkicertstore"); + AddMimeMapping(".src", "application/x-wais-source"); + AddMimeMapping(".sv4cpio", "application/x-sv4cpio"); + AddMimeMapping(".tex", "application/x-tex"); + AddMimeMapping(".tgz", "application/x-compressed"); + AddMimeMapping(".t", "application/x-troff"); + AddMimeMapping(".tar", "application/x-tar"); + AddMimeMapping(".tr", "application/x-troff"); + AddMimeMapping(".tif", "image/tiff"); + AddMimeMapping(".txt", "text/plain"); + AddMimeMapping(".texinfo", "application/x-texinfo"); + AddMimeMapping(".trm", "application/x-msterminal"); + AddMimeMapping(".tiff", "image/tiff"); + AddMimeMapping(".tcl", "application/x-tcl"); + AddMimeMapping(".texi", "application/x-texinfo"); + AddMimeMapping(".tsv", "text/tab-separated-values"); + AddMimeMapping(".ustar", "application/x-ustar"); + AddMimeMapping(".uls", "text/iuls"); + AddMimeMapping(".vcf", "text/x-vcard"); + AddMimeMapping(".wps", "application/vnd.ms-works"); + AddMimeMapping(".wav", "audio/wav"); + AddMimeMapping(".wrz", "x-world/x-vrml"); + AddMimeMapping(".wri", "application/x-mswrite"); + AddMimeMapping(".wks", "application/vnd.ms-works"); + AddMimeMapping(".wmf", "application/x-msmetafile"); + AddMimeMapping(".wcm", "application/vnd.ms-works"); + AddMimeMapping(".wrl", "x-world/x-vrml"); + AddMimeMapping(".wdb", "application/vnd.ms-works"); + AddMimeMapping(".wsdl", "text/xml"); + AddMimeMapping(".xap", "application/x-silverlight-app"); + AddMimeMapping(".xml", "text/xml"); + AddMimeMapping(".xlm", "application/vnd.ms-excel"); + AddMimeMapping(".xaf", "x-world/x-vrml"); + AddMimeMapping(".xla", "application/vnd.ms-excel"); + AddMimeMapping(".xls", "application/vnd.ms-excel"); + AddMimeMapping(".xlsx", "application/vnd.ms-excel"); + AddMimeMapping(".xof", "x-world/x-vrml"); + AddMimeMapping(".xlt", "application/vnd.ms-excel"); + AddMimeMapping(".xlc", "application/vnd.ms-excel"); + AddMimeMapping(".xsl", "text/xml"); + AddMimeMapping(".xbm", "image/x-xbitmap"); + AddMimeMapping(".xlw", "application/vnd.ms-excel"); + AddMimeMapping(".xpm", "image/x-xpixmap"); + AddMimeMapping(".xwd", "image/x-xwindowdump"); + AddMimeMapping(".xsd", "text/xml"); + AddMimeMapping(".z", "application/x-compress"); + AddMimeMapping(".zip", "application/x-zip-compressed"); + AddMimeMapping(".*", "application/octet-stream"); + } + } + + + +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/RSAFileHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/RSAFileHelper.cs new file mode 100644 index 00000000..45457292 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/RSAFileHelper.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Helper +{ + public class RSAFileHelper + { + public static RSA GetKey() + { + return GetRSA("key.pem"); + } + public static RSA GetPublicKey() + { + return GetRSA("public.pem"); + } + + private static RSA GetRSA(string fileName) + { + string rootPath = Directory.GetCurrentDirectory(); + string filePath = Path.Combine(rootPath, fileName); + if (!File.Exists(filePath)) + throw new Exception("文件不存在"); + string key = File.ReadAllText(filePath); + var rsa = RSA.Create(); + rsa.ImportFromPem(key.AsSpan()); + return rsa; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/RSAHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/RSAHelper.cs new file mode 100644 index 00000000..fe171e12 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/RSAHelper.cs @@ -0,0 +1,390 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace Yi.Framework.Infrastructure.Helper +{ + /// + /// RSA加解密 使用OpenSSL的公钥加密/私钥解密 + /// 公私钥请使用openssl生成 + /// + public class RSAHelper + { + public readonly RSA? _privateKeyRsaProvider; + public readonly RSA? _publicKeyRsaProvider; + private readonly HashAlgorithmName _hashAlgorithmName; + private readonly Encoding _encoding; + + /// + /// 实例化RSAHelper + /// + /// 加密算法类型 RSA SHA1;RSA2 SHA256 密钥长度至少为2048 + /// 编码类型 + /// 私钥 + /// 公钥 + public RSAHelper(RSAType rsaType, Encoding encoding, string privateKey, string? publicKey = null) + { + _encoding = encoding; + if (!string.IsNullOrEmpty(privateKey)) + { + _privateKeyRsaProvider = CreateRsaProviderFromPrivateKey(privateKey); + } + + if (!string.IsNullOrEmpty(publicKey)) + { + _publicKeyRsaProvider = CreateRsaProviderFromPublicKey(publicKey); + } + + _hashAlgorithmName = rsaType == RSAType.RSA ? HashAlgorithmName.SHA1 : HashAlgorithmName.SHA256; + } + + #region 使用私钥签名 + + /// + /// 使用私钥签名 + /// + /// 原始数据 + /// + public string Sign(string data) + { + byte[] dataBytes = _encoding.GetBytes(data); + + var signatureBytes = _privateKeyRsaProvider!.SignData(dataBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1); + + return Convert.ToBase64String(signatureBytes); + } + + #endregion + + #region 使用公钥验签 + + /// + /// 使用公钥验签 + /// + /// 原始数据 + /// 签名 + /// + public bool Verify(string data, string sign) + { + byte[] dataBytes = _encoding.GetBytes(data); + byte[] signBytes = Convert.FromBase64String(sign); + + var verify = _publicKeyRsaProvider!.VerifyData(dataBytes, signBytes, _hashAlgorithmName, RSASignaturePadding.Pkcs1); + + return verify; + } + + #endregion + + #region 解密 + /// + /// 私钥解密(原) + /// + /// 解密字符串(base64) + /// + + //public string Decrypt(string cipherText) + //{ + // if (_privateKeyRsaProvider == null) + // { + // throw new Exception("_privateKeyRsaProvider is null"); + // } + // return _encoding.GetString(_privateKeyRsaProvider.Decrypt(Convert.FromBase64String(cipherText), RSAEncryptionPadding.Pkcs1)); + //} + /// + /// 私钥解密(支持大量数据) + /// + /// + /// + public string Decrypt(string cipherText) + { + if (_privateKeyRsaProvider == null) + { + throw new Exception("_privateKeyRsaProvider is null"); + } + var bufferSize = _privateKeyRsaProvider.KeySize / 8; + byte[] buffer = new byte[bufferSize];//待解密块 + using (MemoryStream msInput = new MemoryStream(Convert.FromBase64String(cipherText))) + { + using (MemoryStream msOutput = new MemoryStream()) + { + int readLen; while ((readLen = msInput.Read(buffer, 0, bufferSize)) > 0) + { + byte[] dataToEnc = new byte[readLen]; + Array.Copy(buffer, 0, dataToEnc, 0, readLen); byte[] encData = _privateKeyRsaProvider.Decrypt(dataToEnc, RSAEncryptionPadding.Pkcs1); + msOutput.Write(encData, 0, encData.Length); + } + byte[] result = msOutput.ToArray(); + return _encoding.GetString(result); + } + } + } + + #endregion + + #region 加密 + + /// + /// 公钥加密(原) + /// + /// + /// + //public string Encrypt(string text) + //{ + // if (_publicKeyRsaProvider == null) + // { + // throw new Exception("_publicKeyRsaProvider is null"); + // } + // return Convert.ToBase64String(_publicKeyRsaProvider.Encrypt(Encoding.UTF8.GetBytes(text), RSAEncryptionPadding.Pkcs1)); + //} + /// + /// 公钥加密(支持大量数据) + /// + /// + /// + public string Encrypt(string text) + { + if (_publicKeyRsaProvider == null) + { + throw new Exception("_publicKeyRsaProvider is null"); + } + var bufferSize = _publicKeyRsaProvider.KeySize / 8 - 11; + byte[] buffer = new byte[bufferSize];//待加密块 + + using (MemoryStream msInput = new MemoryStream(_encoding.GetBytes(text))) + { + using (MemoryStream msOutput = new MemoryStream()) + { + int readLen; while ((readLen = msInput.Read(buffer, 0, bufferSize)) > 0) + { + byte[] dataToEnc = new byte[readLen]; + Array.Copy(buffer, 0, dataToEnc, 0, readLen); byte[] encData = _publicKeyRsaProvider.Encrypt(dataToEnc, RSAEncryptionPadding.Pkcs1); + msOutput.Write(encData, 0, encData.Length); + } + byte[] result = msOutput.ToArray(); + return Convert.ToBase64String(result); + } + } + } + + #endregion + + #region 使用私钥创建RSA实例 + /// + /// 使用私钥创建RSA实例 + /// + /// + /// + private RSA CreateRsaProviderFromPrivateKey(string privateKey) + { + var privateKeyBits = Convert.FromBase64String(privateKey); + + var rsa = RSA.Create(); + var rsaParameters = new RSAParameters(); + + using (BinaryReader binr = new BinaryReader(new MemoryStream(privateKeyBits))) + { + byte bt = 0; + ushort twobytes = 0; + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130) + binr.ReadByte(); + else if (twobytes == 0x8230) + binr.ReadInt16(); + else + throw new Exception("Unexpected value read binr.ReadUInt16()"); + + twobytes = binr.ReadUInt16(); + if (twobytes != 0x0102) + throw new Exception("Unexpected version"); + + bt = binr.ReadByte(); + if (bt != 0x00) + throw new Exception("Unexpected value read binr.ReadByte()"); + + rsaParameters.Modulus = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.Exponent = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.D = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.P = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.Q = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.DP = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.DQ = binr.ReadBytes(GetIntegerSize(binr)); + rsaParameters.InverseQ = binr.ReadBytes(GetIntegerSize(binr)); + } + + rsa.ImportParameters(rsaParameters); + return rsa; + } + + #endregion + + #region 使用公钥创建RSA实例 + /// + /// 使用公钥创建RSA实例 + /// + /// + /// + public RSA? CreateRsaProviderFromPublicKey(string publicKeyString) + { + // encoded OID sequence for PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1" + byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 }; + byte[] seq = new byte[15]; + + var x509Key = Convert.FromBase64String(publicKeyString); + + // --------- Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob ------ + using (MemoryStream mem = new MemoryStream(x509Key)) + { + using (BinaryReader binr = new BinaryReader(mem)) //wrap Memory Stream with BinaryReader for easy reading + { + byte bt = 0; + ushort twobytes = 0; + + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8230) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + seq = binr.ReadBytes(15); //read the Sequence OID + if (!CompareBytearrays(seq, seqOid)) //make sure Sequence for OID is correct + return null; + + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8203) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + bt = binr.ReadByte(); + if (bt != 0x00) //expect null byte next + return null; + + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8230) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + twobytes = binr.ReadUInt16(); + byte lowbyte = 0x00; + byte highbyte = 0x00; + + if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81) + lowbyte = binr.ReadByte(); // read next bytes which is bytes in modulus + else if (twobytes == 0x8202) + { + highbyte = binr.ReadByte(); //advance 2 bytes + lowbyte = binr.ReadByte(); + } + else + return null; + byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; //reverse byte order since asn.1 key uses big endian order + int modsize = BitConverter.ToInt32(modint, 0); + + int firstbyte = binr.PeekChar(); + if (firstbyte == 0x00) + { //if first byte (highest order) of modulus is zero, don't include it + binr.ReadByte(); //skip this null byte + modsize -= 1; //reduce modulus buffer size by 1 + } + + byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes + + if (binr.ReadByte() != 0x02) //expect an Integer for the exponent data + return null; + int expbytes = binr.ReadByte(); // should only need one byte for actual exponent data (for all useful values) + byte[] exponent = binr.ReadBytes(expbytes); + + // ------- create RSACryptoServiceProvider instance and initialize with public key ----- + var rsa = RSA.Create(); + RSAParameters rsaKeyInfo = new RSAParameters + { + Modulus = modulus, + Exponent = exponent + }; + rsa.ImportParameters(rsaKeyInfo); + + return rsa; + } + + } + } + + #endregion + + #region 导入密钥算法 + + private int GetIntegerSize(BinaryReader binr) + { + byte bt = 0; + int count = 0; + bt = binr.ReadByte(); + if (bt != 0x02) + return 0; + bt = binr.ReadByte(); + + if (bt == 0x81) + count = binr.ReadByte(); + else + if (bt == 0x82) + { + var highbyte = binr.ReadByte(); + var lowbyte = binr.ReadByte(); + byte[] modint = { lowbyte, highbyte, 0x00, 0x00 }; + count = BitConverter.ToInt32(modint, 0); + } + else + { + count = bt; + } + + while (binr.ReadByte() == 0x00) + { + count -= 1; + } + binr.BaseStream.Seek(-1, SeekOrigin.Current); + return count; + } + + private bool CompareBytearrays(byte[] a, byte[] b) + { + if (a.Length != b.Length) + return false; + int i = 0; + foreach (byte c in a) + { + if (c != b[i]) + return false; + i++; + } + return true; + } + + #endregion + + } + + /// + /// RSA算法类型 + /// + public enum RSAType + { + /// + /// SHA1 + /// + RSA = 0, + /// + /// RSA2 密钥长度至少为2048 + /// SHA256 + /// + RSA2 + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/RandomHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/RandomHelper.cs new file mode 100644 index 00000000..5846e9b8 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/RandomHelper.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Yi.Framework.Infrastructure.Helper +{ + public class RandomHelper + { + public static string replaceBianLiang(string content) + { + content = content.Replace("{当前时间}", DateTime.Now.TimeOfDay.ToString()); + string[] bianliang = new string[] { "{随机字母}", "{随机数字}", "{随机汉字}" }; + Regex r; + int count; + string readstr = ""; + foreach (string str in bianliang) + { + count = (content.Length - content.Replace(str, "").Length) / str.Length; + if (str == "{随机汉字}") readstr = RandChina(count); + if (str == "{随机数字}") readstr = GenerateCheckCodeNum(count); + if (str == "{随机字母}") readstr = GenerateRandomLetter(count); + if (count > readstr.Length) count = readstr.Length; + r = new Regex(str.Replace("{", "\\{").Replace("}", "\\}")); + for (int i = 0; i < count; i++) + { + content = r.Replace(content, readstr.Substring(i, 1), 1); + } + } + return content; + } + + + /// + /// 随机生成字母 + /// + /// + /// + public static string GenerateRandomLetter(int Length) + { + char[] Pattern = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; + string result = ""; + int n = Pattern.Length; + Random random = new Random(~unchecked((int)DateTime.Now.Ticks)); + for (int i = 0; i < Length; i++) + { + int rnd = random.Next(0, n); + result += Pattern[rnd]; + } + return result; + } + + /// + /// 随机生成数字 + /// + /// + /// + public static string GenerateCheckCodeNum(int codeCount) + { + int rep = 0; + string str = string.Empty; + long num2 = DateTime.Now.Ticks + rep; + rep++; + Random random = new Random((int)((ulong)num2 & 0xffffffffL) | (int)(num2 >> rep)); + for (int i = 0; i < codeCount; i++) + { + int num = random.Next(); + str = str + ((char)(0x30 + (ushort)(num % 10))).ToString(); + } + return str; + } + + /// + /// 此函数为生成指定数目的汉字 + /// + /// 汉字数目 + /// 所有汉字 + public static string RandChina(int charLen) + { + int area, code;//汉字由区位和码位组成(都为0-94,其中区位16-55为一级汉字区,56-87为二级汉字区,1-9为特殊字符区) + StringBuilder strtem = new StringBuilder(); + Random rand = new Random(); + for (int i = 0; i < charLen; i++) + { + area = rand.Next(16, 88); + if (area == 55)//第55区只有89个字符 + { + code = rand.Next(1, 90); + } + else + { + code = rand.Next(1, 94); + } + strtem.Append(Encoding.GetEncoding("GB2312").GetString(new byte[] { Convert.ToByte(area + 160), Convert.ToByte(code + 160) })); + } + return strtem.ToString(); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/ReflexHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/ReflexHelper.cs new file mode 100644 index 00000000..8ad4300d --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/ReflexHelper.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Helper +{ + public static class ReflexHelper + { + + #region 对象相关 + /// + /// 取对象属性值 + /// + /// + /// + /// + public static string GetModelValue(string FieldName, object obj) + { + try + { + Type Ts = obj.GetType(); + object o = Ts.GetProperty(FieldName).GetValue(obj, null); + if (null == o) + return null; + string Value = Convert.ToString(o); + if (string.IsNullOrEmpty(Value)) + return null; + return Value; + } + catch (Exception ex) + { + throw ex; + } + return null; + } + + + /// + /// 设置对象属性值 + /// + /// + /// + /// + /// + public static bool SetModelValue(string FieldName, object Value, object obj) + { + try + { + Type Ts = obj.GetType(); + Ts.GetProperty(FieldName).SetValue(obj, Value, null); + return true; + } + catch (Exception ex) + { + throw ex; + } + return false; + } + #endregion + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/SnowflakeHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/SnowflakeHelper.cs new file mode 100644 index 00000000..c2b5babd --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/SnowflakeHelper.cs @@ -0,0 +1,102 @@ +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Helper +{ + public static class SnowflakeHelper + { + public static long NextId { get => SnowFlakeSingle.Instance.NextId(); } + + public static long Next() + { + SnowflakeTool snowflakeTool = new SnowflakeTool(1); + return snowflakeTool.NextId(); + } + + private class SnowflakeTool + { + //机器ID + private static long nodeId; + private static long twepoch = 687888001020L; //唯一时间,这是一个避免重复的随机量,自行设定不要大于当前时间戳 + private static long sequence = 0L; + private static int workerIdBits = 4; //机器码字节数。4个字节用来保存机器码(定义为Long类型会出现,最大偏移64位,所以左移64位没有意义) + public static long maxWorkerId = -1L ^ -1L << workerIdBits; //最大机器ID + private static int sequenceBits = 10; //计数器字节数,10个字节用来保存计数码 + private static int workerIdShift = sequenceBits; //机器码数据左移位数,就是后面计数器占用的位数 + private static int timestampLeftShift = sequenceBits + workerIdBits; //时间戳左移动位数就是机器码和计数器总字节数 + public static long sequenceMask = -1L ^ -1L << sequenceBits; //一微秒内可以产生计数,如果达到该值则等到下一微妙在进行生成 + private long lastTimestamp = -1L; + + /// + /// 机器码 + /// + /// + public SnowflakeTool(long workerId) + { + if (workerId > maxWorkerId || workerId < 0) + throw new Exception(string.Format("节点id 不能大于 {0} 或者 小于 0 ", workerId)); + nodeId = workerId; + + } + + public long NextId() + { + lock (this) + { + long timestamp = TimeGen(); + if (lastTimestamp == timestamp) + { //同一微妙中生成ID + sequence = sequence + 1 & sequenceMask; //用&运算计算该微秒内产生的计数是否已经到达上限 + if (sequence == 0) + { + //一微妙内产生的ID计数已达上限,等待下一微妙 + timestamp = TillNextMillis(lastTimestamp); + } + } + else + { //不同微秒生成ID + sequence = 0; //计数清0 + } + if (timestamp < lastTimestamp) + { //如果当前时间戳比上一次生成ID时时间戳还小,抛出异常,因为不能保证现在生成的ID之前没有生成过 + throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds", + lastTimestamp - timestamp)); + } + lastTimestamp = timestamp; //把当前时间戳保存为最后生成ID的时间戳 + long nextId = timestamp - twepoch << timestampLeftShift | nodeId << workerIdShift | sequence; + return nextId; + } + } + + /// + /// 获取下一微秒时间戳 + /// + /// + /// + private long TillNextMillis(long lastTimestamp) + { + long timestamp = TimeGen(); + while (timestamp <= lastTimestamp) + { + timestamp = TimeGen(); + } + return timestamp; + } + + /// + /// 生成当前时间戳 + /// + /// + private long TimeGen() + { + return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds; + } + } + } + + +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/StringHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/StringHelper.cs new file mode 100644 index 00000000..c6b21cb0 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/StringHelper.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Yi.Framework.Infrastructure.Helper +{ + public class StringHelper + { + /// + /// 根据分隔符返回前n条数据 + /// + /// 数据内容 + /// 分隔符 + /// 前n条 + /// 是否倒序(默认false) + /// + public static List GetTopDataBySeparator(string content, string separator, int top, bool isDesc = false) + { + if (string.IsNullOrEmpty(content)) + { + return new List() { }; + } + + if (string.IsNullOrEmpty(separator)) + { + throw new ArgumentException("message", nameof(separator)); + } + + var dataArray = content.Split(separator).Where(d => !string.IsNullOrEmpty(d)).ToArray(); + if (isDesc) + { + Array.Reverse(dataArray); + } + + if (top > 0) + { + dataArray = dataArray.Take(top).ToArray(); + } + + return dataArray.ToList(); + } + /// + /// 根据字段拼接get参数 + /// + /// + /// + public static string GetPars(Dictionary dic) + { + + StringBuilder sb = new StringBuilder(); + string? urlPars = null; + bool isEnter = false; + foreach (var item in dic) + { + sb.Append($"{(isEnter ? "&" : "")}{item.Key}={item.Value}"); + isEnter = true; + } + urlPars = sb.ToString(); + return urlPars; + } + /// + /// 根据字段拼接get参数 + /// + /// + /// + public static string GetPars(Dictionary dic) + { + + StringBuilder sb = new StringBuilder(); + string? urlPars = null; + bool isEnter = false; + foreach (var item in dic) + { + sb.Append($"{(isEnter ? "&" : "")}{item.Key}={item.Value}"); + isEnter = true; + } + urlPars = sb.ToString(); + return urlPars; + } + /// + /// 获取一个GUID + /// + /// 格式-默认为N + /// + public static string GetGUID(string format = "N") + { + return Guid.NewGuid().ToString(format); + } + /// + /// 根据GUID获取19位的唯一数字序列 + /// + /// + public static long GetGuidToLongID() + { + byte[] buffer = Guid.NewGuid().ToByteArray(); + return BitConverter.ToInt64(buffer, 0); + } + /// + /// 获取字符串最后X行 + /// + /// + /// + /// + public static string GetCusLine(string resourceStr, int length) + { + string[] arrStr = resourceStr.Split("\r\n"); + return string.Join("", (from q in arrStr select q).Skip(arrStr.Length - length + 1).Take(length).ToArray()); + } + + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/TreeHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/TreeHelper.cs new file mode 100644 index 00000000..1e972eec --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/TreeHelper.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Infrastructure.Helper +{ + public static class TreeHelper + { + public static List SetTree(List list, Action action = null!) + { + if (list is not null && list.Count > 0) + { + IList result = new List(); + long pid = list.Min(m => (m as ITreeModel)!.ParentId); + IList t = list.Where(m => (m as ITreeModel)!.ParentId == pid).ToList(); + foreach (T model in t) + { + if (action is not null) + { + action(model); + } + result.Add(model); + var item = model as ITreeModel; + IList children = list.Where(m => (m as ITreeModel)!.ParentId == item!.Id).ToList(); + if (children.Count > 0) + { + SetTreeChildren(list, children, model, action!); + } + } + return result.OrderByDescending(m => (m as ITreeModel)!.OrderNum).ToList(); + } + return null!; + } + private static void SetTreeChildren(IList list, IList children, T model, Action action = null!) + { + var mm = model as ITreeModel; + mm!.Children = new List(); + foreach (T item in children) + { + if (action is not null) + { + action(item); + } + mm.Children.Add(item); + var _item = item as ITreeModel; + IList _children = list.Where(m => (m as ITreeModel)!.ParentId == _item!.Id).ToList(); + if (_children.Count > 0) + { + SetTreeChildren(list, _children, item, action!); + } + } + mm.Children = mm.Children.OrderByDescending(m => (m as ITreeModel)!.OrderNum).ToList(); + } + + + public interface ITreeModel + { + public long Id { get; set; } + public long ParentId { get; set; } + public int OrderNum { get; set; } + + public List? Children { get; set; } + } + } + +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/UnicodeHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/UnicodeHelper.cs new file mode 100644 index 00000000..45023aca --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/UnicodeHelper.cs @@ -0,0 +1,47 @@ +using System; +using System.Text; +using System.Text.RegularExpressions; + +namespace Yi.Framework.Infrastructure.Helper +{ + public static class UnicodeHelper + { + /// + /// 字符串转Unicode码 + /// + /// The to unicode. + /// Value. + public static string StringToUnicode(string value) + { + byte[] bytes = Encoding.Unicode.GetBytes(value); + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < bytes.Length; i += 2) + { + // 取两个字符,每个字符都是右对齐。 + stringBuilder.AppendFormat("u{0}{1}", bytes[i + 1].ToString("x").PadLeft(2, '0'), bytes[i].ToString("x").PadLeft(2, '0')); + } + return stringBuilder.ToString(); + } + + /// + /// Unicode转字符串 + /// + /// The to string. + /// Unicode. + public static string UnicodeToString(string unicode) + { + unicode = unicode.Replace("%", "\\"); + + return new Regex(@"\\u([0-9A-F]{4})", RegexOptions.IgnoreCase | RegexOptions.Compiled).Replace( + unicode, x => string.Empty + Convert.ToChar(Convert.ToUInt16(x.Result("$1"), 16))); + + //string resultStr = ""; + //string[] strList = unicode.Split('u'); + //for (int i = 1; i < strList.Length; i++) + //{ + // resultStr += (char)int.Parse(strList[i], System.Globalization.NumberStyles.HexNumber); + //} + //return resultStr; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/UrlHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/UrlHelper.cs new file mode 100644 index 00000000..62f227d9 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/UrlHelper.cs @@ -0,0 +1,24 @@ +namespace Yi.Framework.Infrastructure.Helper +{ + public class UrlHelper + { + /// + /// UrlEncode编码 + /// + /// url + /// + public static string UrlEncode(string url) + { + return System.Web.HttpUtility.UrlEncode(url, System.Text.Encoding.UTF8); + } + /// + /// UrlEncode解码 + /// + /// 数据 + /// + public static string UrlDecode(string data) + { + return System.Web.HttpUtility.UrlDecode(data, System.Text.Encoding.UTF8); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/XmlHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/XmlHelper.cs new file mode 100644 index 00000000..952a2959 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Helper/XmlHelper.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml.Serialization; + +namespace Yi.Framework.Infrastructure.Helper +{ + public class XmlHelper + { + /// + /// 转换对象为JSON格式数据 + /// + /// + /// 对象 + /// 字符格式的JSON数据 + public static string? GetXML(object obj) + { + try + { + XmlSerializer xs = new XmlSerializer(typeof(T)); + + using (TextWriter tw = new StringWriter()) + { + xs.Serialize(tw, obj); + return tw.ToString(); + } + } + catch (Exception) + { + return string.Empty; + } + } + + /// + /// Xml格式字符转换为T类型的对象 + /// + /// + /// + /// + public static T ParseFormByXml(string xml, string rootName = "root") + { + XmlSerializer serializer = new XmlSerializer(typeof(T), new XmlRootAttribute(rootName)); + StringReader reader = new StringReader(xml); + + T res = (T)serializer.Deserialize(reader)!; + reader.Close(); + reader.Dispose(); + return res; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Sqlsugar/Repositories/SqlsugarRepository.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Sqlsugar/Repositories/SqlsugarRepository.cs new file mode 100644 index 00000000..7e2c704f --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Sqlsugar/Repositories/SqlsugarRepository.cs @@ -0,0 +1,147 @@ +using System.Linq.Expressions; +using Furion.DependencyInjection; +using SqlSugar; +using Yi.Framework.Infrastructure.Data.Entities; +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; +using Yi.Framework.Infrastructure.Ddd.Repositories; +using Yi.Framework.Infrastructure.Enums; +using Yi.Framework.Infrastructure.Helper; + +namespace Yi.Framework.Infrastructure.Sqlsugar.Repositories +{ + public class SqlsugarRepository : SimpleClient, IRepository ,ITransient where T : class, new() + { + public SqlsugarRepository(ISqlSugarClient context) : base(context) + { + } + /// + /// 注释一下,严格意义这里应该protected,但是我认为 简易程度 与 耦合程度 中是需要进行衡量的 + /// + public ISugarQueryable _DbQueryable => AsQueryable(); + + protected ISqlSugarClient _Db { get { return Context; } set { } } + + public async Task> GetPageListAsync(Expression> whereExpression, IPagedAndSortedResultRequestDto page) + { + return await base.GetPageListAsync(whereExpression, new PageModel { PageIndex = page.PageNum, PageSize = page.PageSize }); + } + + public async Task> GetPageListAsync(Expression> whereExpression, IPagedAndSortedResultRequestDto page, Expression>? orderByExpression = null, OrderByEnum orderByType = OrderByEnum.Asc) + { + return await base.GetPageListAsync(whereExpression, new PageModel { PageIndex = page.PageNum, PageSize = page.PageSize }, orderByExpression, orderByType.EnumToEnum()); + } + + public async Task> GetPageListAsync(Expression> whereExpression, IPagedAndSortedResultRequestDto page, string? orderBy, OrderByEnum orderByType = OrderByEnum.Asc) + { + return await _DbQueryable.Where(whereExpression).OrderByIF(orderBy is not null, orderBy + " " + orderByType.ToString().ToLower()).ToPageListAsync(page.PageNum, page.PageSize); + } + + + + + public async Task> GetPageListAsync(Expression> whereExpression, int pageNum, int pageSize) + { + return await base.GetPageListAsync(whereExpression, new PageModel { PageIndex = pageNum, PageSize = pageSize }); + } + + public async Task> GetPageListAsync(Expression> whereExpression, int pageNum, int pageSize, Expression>? orderByExpression = null, OrderByEnum orderByType = OrderByEnum.Asc) + { + return await base.GetPageListAsync(whereExpression, new PageModel { PageIndex = pageNum, PageSize = pageSize }, orderByExpression, orderByType.EnumToEnum()); + } + + public async Task> GetPageListAsync(Expression> whereExpression, int pageNum, int pageSize, string? orderBy, OrderByEnum orderByType = OrderByEnum.Asc) + { + return await _DbQueryable.Where(whereExpression).OrderByIF(orderBy is not null, orderBy + " " + orderByType.ToString().ToLower()).ToPageListAsync(pageNum, pageSize); + } + + + + + + + + public async Task UpdateIgnoreNullAsync(T updateObj) + { + return await _Db.Updateable(updateObj).IgnoreColumns(true).ExecuteCommandAsync() > 0; + } + + public override async Task DeleteAsync(T deleteObj) + { + //逻辑删除 + if (deleteObj is ISoftDelete) + { + //反射赋值 + ReflexHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, deleteObj); + return await UpdateAsync(deleteObj); + + } + else + { + return await base.DeleteAsync(deleteObj); + + } + } + public override async Task DeleteAsync(List deleteObjs) + { + if (typeof(ISoftDelete).IsAssignableFrom(typeof(T))) + { + //反射赋值 + deleteObjs.ForEach(e => ReflexHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, e)); + return await UpdateRangeAsync(deleteObjs); + } + else + { + return await base.DeleteAsync(deleteObjs); + } + } + public override async Task DeleteAsync(Expression> whereExpression) + { + if (typeof(ISoftDelete).IsAssignableFrom(typeof(T))) + { + var entities = await GetListAsync(whereExpression); + //反射赋值 + entities.ForEach(e => ReflexHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, e)); + return await UpdateRangeAsync(entities); + } + else + { + return await base.DeleteAsync(whereExpression); + } + } + public override async Task DeleteByIdAsync(dynamic id) + { + if (typeof(ISoftDelete).IsAssignableFrom(typeof(T))) + { + var entity = await GetByIdAsync(id); + //反射赋值 + ReflexHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, entity); + return await UpdateAsync(entity); + } + else + { + return await _Db.Deleteable().In(id).ExecuteCommandAsync() > 0; + } + + } + public override async Task DeleteByIdsAsync(dynamic[] ids) + { + if (typeof(ISoftDelete).IsAssignableFrom(typeof(T))) + { + var entities = await _DbQueryable.In(ids).ToListAsync(); + if (entities.Count == 0) + { + return false; + } + //反射赋值 + entities.ForEach(e => ReflexHelper.SetModelValue(nameof(ISoftDelete.IsDeleted), true, e)); + return await UpdateRangeAsync(entities); + } + else + { + return await base.DeleteByIdsAsync(ids); + } + + } + + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Sqlsugar/Uow/SqlsugarUnitOfWork.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Sqlsugar/Uow/SqlsugarUnitOfWork.cs new file mode 100644 index 00000000..7c1ca7d7 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Sqlsugar/Uow/SqlsugarUnitOfWork.cs @@ -0,0 +1,51 @@ +using Furion; +using Furion.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; +using SqlSugar; +using Yi.Framework.Infrastructure.Ddd.Repositories; +using Yi.Framework.Infrastructure.Uow; + +namespace Yi.Framework.Infrastructure.Sqlsugar.Uow +{ + public class SqlsugarUnitOfWork : IUnitOfWork, ISingleton + { + public ISqlSugarClient Db { get; set; } + public ITenant Tenant { get; set; } + public bool IsTran { get; set; } + public bool IsCommit { get; set; } + public bool IsClose { get; set; } + + public void Dispose() + { + + if (IsTran && IsCommit == false) + { + Tenant.RollbackTran(); + } + if (Db.Ado.Transaction == null && IsClose == false) + { + Db.Close(); + } + } + + public bool Commit() + { + if (IsTran && IsCommit == false) + { + Tenant.CommitTran(); + IsCommit = true; + } + if (Db.Ado.Transaction == null && IsClose == false) + { + Db.Close(); + IsClose = true; + } + return IsCommit; + } + + public IRepository GetRepository() + { + return App.GetRequiredService>(); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Sqlsugar/Uow/SqlsugarUnitOfWorkManager.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Sqlsugar/Uow/SqlsugarUnitOfWorkManager.cs new file mode 100644 index 00000000..f4dc75a7 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Sqlsugar/Uow/SqlsugarUnitOfWorkManager.cs @@ -0,0 +1,38 @@ +using Furion.DependencyInjection; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Yi.Framework.Infrastructure.Uow; + +namespace Yi.Framework.Infrastructure.Sqlsugar.Uow +{ + /// + /// 此部分为sqlsugr的魔改版本 + /// + internal class SqlsugarUnitOfWorkManager : IUnitOfWorkManager,ISingleton + { + public SqlsugarUnitOfWorkManager(ISqlSugarClient db) + { + Db = db; + } + public ISqlSugarClient Db { get; set; } + public IUnitOfWork CreateContext(bool isTran = true) + { + SqlsugarUnitOfWork uow = new SqlsugarUnitOfWork(); + return CreateContext(isTran, uow); + } + private IUnitOfWork CreateContext(bool isTran, SqlsugarUnitOfWork sugarUnitOf) + { + sugarUnitOf.Db = Db; + sugarUnitOf.Tenant = Db.AsTenant(); + sugarUnitOf.IsTran = isTran; + Db.Open(); + if (isTran) + Db.AsTenant().BeginTran(); + return sugarUnitOf; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Uow/DefaultUnitOfWork.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Uow/DefaultUnitOfWork.cs new file mode 100644 index 00000000..fea60739 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Uow/DefaultUnitOfWork.cs @@ -0,0 +1,26 @@ +using Yi.Framework.Infrastructure.Ddd.Repositories; + +namespace Yi.Framework.Infrastructure.Uow +{ + internal class DefaultUnitOfWork : IUnitOfWork + { + public DefaultUnitOfWork() { } + public bool IsTran { get; set; } + public bool IsCommit { get; set; } + public bool IsClose { get; set; } + + public bool Commit() + { + return true; + } + + public void Dispose() + { + } + + public IRepository GetRepository() + { + throw new NotImplementedException(); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Uow/DefaultUnitOfWorkManager.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Uow/DefaultUnitOfWorkManager.cs new file mode 100644 index 00000000..3c84aa5a --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Uow/DefaultUnitOfWorkManager.cs @@ -0,0 +1,10 @@ +namespace Yi.Framework.Infrastructure.Uow +{ + internal class DefaultUnitOfWorkManager : IUnitOfWorkManager + { + public IUnitOfWork CreateContext(bool isTran = true) + { + return new DefaultUnitOfWork(); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Uow/IUnitOfWork.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Uow/IUnitOfWork.cs new file mode 100644 index 00000000..b1e5e7b3 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Uow/IUnitOfWork.cs @@ -0,0 +1,14 @@ +using Yi.Framework.Infrastructure.Ddd.Repositories; + +namespace Yi.Framework.Infrastructure.Uow +{ + public interface IUnitOfWork : IDisposable + { + bool IsTran { get; set; } + bool IsCommit { get; set; } + bool IsClose { get; set; } + + IRepository GetRepository(); + bool Commit(); + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Uow/IUnitOfWorkManager.cs b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Uow/IUnitOfWorkManager.cs new file mode 100644 index 00000000..25a53c64 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Uow/IUnitOfWorkManager.cs @@ -0,0 +1,8 @@ +namespace Yi.Framework.Infrastructure.Uow +{ + public interface IUnitOfWorkManager + { + IUnitOfWork CreateContext(bool isTran = true); + } + +} \ No newline at end of file diff --git a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Yi.Framework.Infrastructure.csproj b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Yi.Framework.Infrastructure.csproj index b34164b4..2bc8a350 100644 --- a/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Yi.Framework.Infrastructure.csproj +++ b/Yi.Furion.Rbac/Yi.Framework.Infrastructure/Yi.Framework.Infrastructure.csproj @@ -1,4 +1,4 @@ - + net6.0 @@ -6,8 +6,14 @@ enable - - - + + + + + + + + + diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/HeiCaptchaExtension.cs b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/HeiCaptchaExtension.cs new file mode 100644 index 00000000..423f732d --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/HeiCaptchaExtension.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Yi.Framework.Module.ImageSharp.HeiCaptcha +{ + public static class HeiCaptchaExtension + { + /// + /// 启用HeiCaptcha + /// + /// + /// + public static IServiceCollection AddHeiCaptcha(this IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + services.AddScoped(); + return services; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/ImageRgba32Extension.cs b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/ImageRgba32Extension.cs new file mode 100644 index 00000000..f53415fb --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/ImageRgba32Extension.cs @@ -0,0 +1,32 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Yi.Framework.Module.ImageSharp.HeiCaptcha +{ + public static class ImageRgba32Extension + { + public static byte[] ToPngArray(this Image img) where TPixel : unmanaged, IPixel + { + using (var ms = new MemoryStream()) + { + img.Save(ms, PngFormat.Instance); + return ms.ToArray(); + } + } + + public static byte[] ToGifArray(this Image img) where TPixel : unmanaged, IPixel + { + using (var ms = new MemoryStream()) + { + img.Save(ms, new GifEncoder()); + return ms.ToArray(); + } + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/ImageSharpExtension.cs b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/ImageSharpExtension.cs new file mode 100644 index 00000000..8e2845ae --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/ImageSharpExtension.cs @@ -0,0 +1,216 @@ +//using SixLabors.Fonts; +//using SixLabors.ImageSharp; +//using SixLabors.ImageSharp.PixelFormats; +//using SixLabors.ImageSharp.Processing; +//using System; +//using System.Collections.Generic; + +//namespace Hei.Captcha +//{ +// public static class ImageSharpExtension +// { + +// / +// / 绘制中文字符(可以绘制字母数字,但样式可能需要改) +// / +// / +// / < param name="processingContext"> +// / +// / < param name="containerHeight"> +// / +// / < param name="color"> +// / +// / < returns > +// public static IImageProcessingContext DrawingCnText(this IImageProcessingContext processingContext, int containerWidth, int containerHeight, string text, Rgba32 color, Font font) +// where TPixel : struct, IPixel +// { +// return processingContext.Apply(img => +// { +// if (string.IsNullOrEmpty(text) == false) +// { +// Random random = new Random(); +// var textWidth = (img.Width / text.Length); +// var img2Size = Math.Min(textWidth, img.Height); +// var fontMiniSize = (int)(img2Size * 0.6); +// var fontMaxSize = (int)(img2Size * 0.95); + +// for (int i = 0; i < text.Length; i++) +// { +// using (Image img2 = new Image(img2Size, img2Size)) +// { +// Font scaledFont = new Font(font, random.Next(fontMiniSize, fontMaxSize)); +// var point = new Point(i * textWidth, (containerHeight - img2.Height) / 2); +// var textGraphicsOptions = new TextGraphicsOptions(true) +// { +// HorizontalAlignment = HorizontalAlignment.Left, +// VerticalAlignment = VerticalAlignment.Top +// }; + +// img2.Mutate(ctx => ctx +// .DrawText(textGraphicsOptions, text[i].ToString(), scaledFont, color, new Point(0, 0)) +// .Rotate(random.Next(-45, 45)) +// ); +// img.Mutate(ctx => ctx.DrawImage(img2, point, 1)); +// } +// } +// } +// }); +// } + +// public static IImageProcessingContext DrawingEnText(this IImageProcessingContext processingContext, int containerWidth, int containerHeight, string text, string[] colorHexArr, Font[] fonts) +// where TPixel : struct, IPixel +// { +// return processingContext.Apply(img => +// { +// if (string.IsNullOrEmpty(text) == false) +// { +// Random random = new Random(); +// var textWidth = (img.Width / text.Length); +// var img2Size = Math.Min(textWidth, img.Height); +// var fontMiniSize = (int)(img2Size * 0.9); +// var fontMaxSize = (int)(img2Size * 1.37); +// Array fontStyleArr = Enum.GetValues(typeof(FontStyle)); + +// for (int i = 0; i < text.Length; i++) +// { +// using (Image img2 = new Image(img2Size, img2Size)) +// { +// Font scaledFont = new Font(fonts[random.Next(0, fonts.Length)], random.Next(fontMiniSize, fontMaxSize), (FontStyle)fontStyleArr.GetValue(random.Next(fontStyleArr.Length))); +// var point = new Point(i * textWidth, (containerHeight - img2.Height) / 2); +// var colorHex = colorHexArr[random.Next(0, colorHexArr.Length)]; +// var textGraphicsOptions = new TextGraphicsOptions(true) +// { +// HorizontalAlignment = HorizontalAlignment.Left, +// VerticalAlignment = VerticalAlignment.Top +// }; + +// img2.Mutate(ctx => ctx +// .DrawText(textGraphicsOptions, text[i].ToString(), scaledFont, Rgba32.FromHex(colorHex), new Point(0, 0)) +// .DrawingGrid(containerWidth, containerHeight, Rgba32.FromHex(colorHex), 6, 1) +// .Rotate(random.Next(-45, 45)) +// ); +// img.Mutate(ctx => ctx.DrawImage(img2, point, 1)); +// } +// } +// } +// }); +// } + +// / +// / 画圆圈(泡泡) +// / +// / +// / < param name="processingContext"> +// / +// / < param name="containerHeight"> +// / +// / < param name="miniR"> +// / +// / < param name="color"> +// / +// / < returns > +// public static IImageProcessingContext DrawingCircles(this IImageProcessingContext processingContext, int containerWidth, int containerHeight, int count, int miniR, int maxR, TPixel color, bool canOverlap = false) +// where TPixel : struct, IPixel +// { +// return processingContext.Apply(img => +// { +// EllipsePolygon ep = null; +// Random random = new Random(); +// PointF tempPoint = new PointF(); +// List points = new List(); + +// if (count > 0) +// { +// for (int i = 0; i < count; i++) +// { +// if (canOverlap) +// { +// tempPoint = new PointF(random.Next(0, containerWidth), random.Next(0, containerHeight)); +// } +// else +// { +// tempPoint = getCirclePoginF(containerWidth, containerHeight, (miniR + maxR), ref points); +// } +// ep = new EllipsePolygon(tempPoint, random.Next(miniR, maxR)); + +// img.Mutate(ctx => ctx +// .Draw(color, (float)(random.Next(94, 145) / 100.0), ep.Clip()) +// ); +// } +// } +// }); +// } +// / +// / 画杂线 +// / +// / +// / < param name="processingContext"> +// / +// / < param name="containerHeight"> +// / +// / < param name="count"> +// / +// / < returns > +// public static IImageProcessingContext DrawingGrid(this IImageProcessingContext processingContext, int containerWidth, int containerHeight, TPixel color, int count, float thickness) +// where TPixel : struct, IPixel +// { +// return processingContext.Apply(img => +// { +// var points = new List { new PointF(0, 0) }; +// for (int i = 0; i < count; i++) +// { +// getCirclePoginF(containerWidth, containerHeight, 9, ref points); +// } +// points.Add(new PointF(containerWidth, containerHeight)); +// img.Mutate(ctx => ctx +// .DrawLines(color, thickness, points.ToArray()) +// ); +// }); +// } + +// / +// / 散 随机点 +// / +// / +// / < param name="containerHeight"> +// / +// / < param name="list"> +// / +// private static PointF getCirclePoginF(int containerWidth, int containerHeight, double lapR, ref List list) +// { +// Random random = new Random(); +// PointF newPoint = new PointF(); +// int retryTimes = 10; +// double tempDistance = 0; + +// do +// { +// newPoint.X = random.Next(0, containerWidth); +// newPoint.Y = random.Next(0, containerHeight); +// bool tooClose = false; +// foreach (var p in list) +// { +// tooClose = false; +// tempDistance = Math.Sqrt((Math.Pow((p.X - newPoint.X), 2) + Math.Pow((p.Y - newPoint.Y), 2))); +// if (tempDistance < lapR) +// { +// tooClose = true; +// break; +// } +// } +// if (tooClose == false) +// { +// list.Add(newPoint); +// break; +// } +// } +// while (retryTimes-- > 0); + +// if (retryTimes <= 0) +// { +// list.Add(newPoint); +// } +// return newPoint; +// } +// } +//} diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/SecurityCodeHelper.cs b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/SecurityCodeHelper.cs new file mode 100644 index 00000000..720eb436 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/SecurityCodeHelper.cs @@ -0,0 +1,182 @@ +using SixLabors.Fonts; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing.Processing; +using SixLabors.ImageSharp.Formats.Gif; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; + +namespace Yi.Framework.Module.ImageSharp.HeiCaptcha +{ + /// + /// 验证码配置和绘制逻辑 + /// + public class SecurityCodeHelper + { + /// + /// 验证码文本池 + /// + private static readonly string[] _cnTextArr = new string[] { "的", "一", "国", "在", "人", "了", "有", "中", "是", "年", "和", "大", "业", "不", "为", "发", "会", "工", "经", "上", "地", "市", "要", "个", "产", "这", "出", "行", "作", "生", "家", "以", "成", "到", "日", "民", "来", "我", "部", "对", "进", "多", "全", "建", "他", "公", "开", "们", "场", "展", "时", "理", "新", "方", "主", "企", "资", "实", "学", "报", "制", "政", "济", "用", "同", "于", "法", "高", "长", "现", "本", "月", "定", "化", "加", "动", "合", "品", "重", "关", "机", "分", "力", "自", "外", "者", "区", "能", "设", "后", "就", "等", "体", "下", "万", "元", "社", "过", "前", "面", "农", "也", "得", "与", "说", "之", "员", "而", "务", "利", "电", "文", "事", "可", "种", "总", "改", "三", "各", "好", "金", "第", "司", "其", "从", "平", "代", "当", "天", "水", "省", "提", "商", "十", "管", "内", "小", "技", "位", "目", "起", "海", "所", "立", "已", "通", "入", "量", "子", "问", "度", "北", "保", "心", "还", "科", "委", "都", "术", "使", "明", "着", "次", "将", "增", "基", "名", "向", "门", "应", "里", "美", "由", "规", "今", "题", "记", "点", "计", "去", "强", "两", "些", "表", "系", "办", "教 正", "条", "最", "达", "特", "革", "收", "二", "期", "并", "程", "厂", "如", "道", "际 及", "西", "口", "京", "华", "任", "调", "性", "导", "组", "东", "路", "活", "广", "意", "比", "投", "决", "交", "统", "党", "南", "安", "此", "领", "结", "营", "项", "情", "解", "议", "义", "山", "先", "车", "然", "价", "放", "世", "间", "因", "共", "院", "步", "物", "界", "集", "把", "持", "无", "但", "城", "相", "书", "村", "求", "治", "取", "原", "处", "府", "研", "质", "信", "四", "运", "县", "军", "件", "育", "局", "干", "队", "团", "又", "造", "形", "级", "标", "联", "专", "少", "费", "效", "据", "手", "施", "权", "江", "近", "深", "更", "认", "果", "格", "几", "看", "没", "职", "服", "台", "式", "益", "想", "数", "单", "样", "只", "被", "亿", "老", "受", "优", "常", "销", "志", "战", "流", "很", "接", "乡", "头", "给", "至", "难", "观", "指", "创", "证", "织", "论", "别", "五", "协", "变", "风", "批", "见", "究", "支", "那", "查", "张", "精", "每", "林", "转", "划", "准", "做", "需", "传", "争", "税", "构", "具", "百", "或", "才", "积", "势", "举", "必", "型", "易", "视", "快", "李", "参", "回", "引", "镇", "首", "推", "思", "完", "消", "值", "该", "走", "装", "众", "责", "备", "州", "供", "包", "副", "极", "整", "确", "知", "贸", "己", "环", "话", "反", "身", "选", "亚", "么", "带", "采", "王", "策", "真", "女", "谈", "严", "斯", "况", "色", "打", "德", "告", "仅", "它", "气", "料", "神", "率", "识", "劳", "境", "源", "青", "护", "列", "兴", "许", "户", "马", "港", "则", "节", "款", "拉", "直", "案", "股", "光", "较", "河", "花", "根", "布", "线", "土", "克", "再", "群", "医", "清", "速", "律", "她", "族", "历", "非", "感", "占", "续", "师", "何", "影", "功", "负", "验", "望", "财", "类", "货", "约", "艺", "售", "连", "纪", "按", "讯", "史", "示", "象", "养", "获", "石", "食", "抓", "富", "模", "始", "住", "赛", "客", "越", "闻", "央", "席", "坚" }; + + private static readonly string[] _enTextArr = new string[] { "a", "b", "c", "d", "e", "f", "g", "h", "k", "m", "n", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" }; + + /// + /// 验证码图片宽高 + /// + private readonly int _imageWidth = 120; + + private readonly int _imageHeight = 50; + + /// + /// 泡泡数量 + /// + private int _circleCount = 14; + + /// + /// 泡泡半径范围 + /// + private readonly int _miniCircleR = 2; + + private readonly int _maxCircleR = 8; + + /// + /// 颜色池,较深的颜色 + /// https://tool.oschina.net/commons?type=3 + /// + private static readonly string[] _colorHexArr = new string[] { "#00E5EE", "#000000", "#2F4F4F", "#000000", "#43CD80", "#191970", "#006400", "#458B00", "#8B7765", "#CD5B45" }; + + ///较浅的颜色 + private static readonly string[] _lightColorHexArr = new string[] { "#FFFACD", "#FDF5E6", "#F0FFFF", "#BBFFFF", "#FAFAD2", "#FFE4E1", "#DCDCDC", "#F0E68C" }; + + private static readonly Random _random = new Random(); + + /// + /// 字体池 + /// + private static Font[] _fontArr; + + public SecurityCodeHelper() + { + initFonts(_imageHeight); + } + + /// + /// 生成随机中文字符串 + /// + /// + /// + public string GetRandomCnText(int length) + { + StringBuilder sb = new StringBuilder(); + if (length > 0) + { + do + { + sb.Append(_cnTextArr[_random.Next(0, _cnTextArr.Length)]); + } + while (--length > 0); + } + return sb.ToString(); + } + + /// + /// 生成随机英文字母/数字组合字符串 + /// + /// + /// + public string GetRandomEnDigitalText(int length) + { + StringBuilder sb = new StringBuilder(); + if (length > 0) + { + do + { + if (_random.Next(0, 2) > 0) + { + sb.Append(_random.Next(2, 10)); + } + else + { + sb.Append(_enTextArr[_random.Next(0, _enTextArr.Length)]); + } + } + while (--length > 0); + } + return sb.ToString(); + } + + /// + /// 英文字母+数字组合验证码 + /// + /// + /// 验证码图片字节数组 + public byte[] GetEnDigitalCodeByte(string text) + { + using (Image img = getEnDigitalCodeImage(text)) + { + return img.ToGifArray(); + } + } + + + + /// + /// 生成一个数组组合验证码素材(Image) + /// + /// + /// + private Image getEnDigitalCodeImage(string text) + { + Image img = new Image(_imageWidth, _imageHeight); + var colorTextHex = _colorHexArr[_random.Next(0, _colorHexArr.Length)]; + var lignthColorHex = _lightColorHexArr[_random.Next(0, _lightColorHexArr.Length)]; + + img.Mutate(ctx => ctx + .Fill(Rgba32.ParseHex(_lightColorHexArr[_random.Next(0, _lightColorHexArr.Length)])) + .Glow(Rgba32.ParseHex(lignthColorHex)) + //.DrawingGrid(_imageWidth, _imageHeight, Rgba32.ParseHex(lignthColorHex), 8, 1) + .DrawText(text, _fontArr[0], Rgba32.ParseHex(_colorHexArr[0]), new PointF(0, 0)) + //.DrawingEnText(, text, _colorHexArr, _fontArr) + .GaussianBlur(0.4f) + //.DrawingCircles(_imageWidth, _imageHeight, 15, _miniCircleR, _maxCircleR, Color.White) + ); + return img; + } + + /// + /// 初始化字体池 + /// + /// 一个初始大小 + private void initFonts(int fontSize) + { + if (_fontArr == null) + { + var assembly = Assembly.GetExecutingAssembly(); + var names = assembly.GetManifestResourceNames(); + + if (names?.Length > 0 == true) + { + var fontList = new List(); + var fontCollection = new FontCollection(); + + foreach (var name in names) + { + fontList.Add(new Font(fontCollection.Add(assembly.GetManifestResourceStream(name)), fontSize)); + } + + _fontArr = fontList.ToArray(); + } + else + { + throw new Exception($"绘制验证码字体文件加载失败"); + } + } + } + } +} \ No newline at end of file diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/fonts/Candara.ttf b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/fonts/Candara.ttf new file mode 100644 index 00000000..dbc73697 Binary files /dev/null and b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/fonts/Candara.ttf differ diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/fonts/STCAIYUN.ttf b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/fonts/STCAIYUN.ttf new file mode 100644 index 00000000..f2c31b3a Binary files /dev/null and b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/fonts/STCAIYUN.ttf differ diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/fonts/impact.ttf b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/fonts/impact.ttf new file mode 100644 index 00000000..52701465 Binary files /dev/null and b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/fonts/impact.ttf differ diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/fonts/monbaiti.ttf b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/fonts/monbaiti.ttf new file mode 100644 index 00000000..64f759ff Binary files /dev/null and b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/HeiCaptcha/fonts/monbaiti.ttf differ diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/ImageSharpManager.cs b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/ImageSharpManager.cs new file mode 100644 index 00000000..db6eed49 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/ImageSharp/ImageSharpManager.cs @@ -0,0 +1,65 @@ +using Furion.DependencyInjection; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Processing; + +namespace Yi.Framework.Module.ImageSharp; +public class ImageSharpManager:ISingleton +{ + public void ImageCompress(string fileName, Stream stream, string savePath) + { + var extensionName = Path.GetExtension(fileName).ToLower(); + if (extensionName == ".png") + { + PngImageCompress(stream, savePath); + } + else if (extensionName == ".jpg" || extensionName == ".jpeg") + { + JpgImageCompress(stream, savePath); + } + else + { + using (var fileStream = new FileStream(savePath, FileMode.Create, FileAccess.Write)) + { + stream.CopyTo(fileStream); + } + } + + } + + public void PngImageCompress(Stream stream, string savePath) + { + using (var image = Image.Load(stream)) + { + var encoder = new PngEncoder() + { + CompressionLevel = PngCompressionLevel.Level6, + + }; + if (image.Width > 300) + { + image.Mutate(a => a.Resize(image.Width / 2, image.Height / 2)); + } + + image.Save(savePath, encoder); + } + } + public void JpgImageCompress(Stream stream, string savePath) + { + using (var image = Image.Load(stream)) + { + var encoder = new JpegEncoder() + { + Quality = 30 + }; + if (image.Width > 300) + { + image.Mutate(a => a.Resize(image.Width / 2, image.Height / 2)); + } + + + image.Save(savePath, encoder); + } + } + +} \ No newline at end of file diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/GlobalOperLogAttribute.cs b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/GlobalOperLogAttribute.cs new file mode 100644 index 00000000..ab32bff3 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/GlobalOperLogAttribute.cs @@ -0,0 +1,93 @@ +using IPTools.Core; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; +using Yi.Framework.Infrastructure.AspNetCore; +using Yi.Framework.Infrastructure.CurrentUsers; +using Yi.Framework.Infrastructure.Ddd.Repositories; +using Yi.Framework.Infrastructure.Helper; + +namespace Yi.Framework.Module.OperLogManager +{ + public class GlobalOperLogAttribute : ActionFilterAttribute + { + private ILogger _logger; + private IRepository _repository; + private ICurrentUser _currentUser; + //注入一个日志服务 + public GlobalOperLogAttribute(ILogger logger, IRepository repository, ICurrentUser currentUser) + { + _logger = logger; + _repository = repository; + _currentUser = currentUser; + } + + public override async void OnResultExecuted(ResultExecutedContext context) + { + //判断标签是在方法上 + if (context.ActionDescriptor is not ControllerActionDescriptor controllerActionDescriptor) return; + + //查找标签,获取标签对象 + OperLogAttribute? operLogAttribute = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true) + .FirstOrDefault(a => a.GetType().Equals(typeof(OperLogAttribute))) as OperLogAttribute; + //空对象直接返回 + if (operLogAttribute is null) return; + + ////获取控制器名 + //string controller = context.RouteData.Values["Controller"].ToString(); + + ////获取方法名 + //string action = context.RouteData.Values["Action"].ToString(); + + //获取Ip + string ip = context.HttpContext.GetClientIp(); + + //根据ip获取地址 + + var ipTool = IpTool.Search(ip); + string location = ipTool.Province + " " + ipTool.City; + + //日志服务插入一条操作记录即可 + + var logEntity = new OperationLogEntity(); + logEntity.Id = SnowflakeHelper.NextId; + logEntity.OperIp = ip; + //logEntity.OperLocation = location; + logEntity.OperType = operLogAttribute.OperType; + logEntity.Title = operLogAttribute.Title; + logEntity.RequestMethod = context.HttpContext.Request.Method; + logEntity.Method = context.HttpContext.Request.Path.Value; + logEntity.OperLocation = location; + + + logEntity.OperUser = _currentUser.UserName; + if (operLogAttribute.IsSaveResponseData) + { + if (context.Result is ContentResult result && result.ContentType == "application/json") + { + logEntity.RequestResult = result.Content?.Replace("\r\n", "").Trim(); + } + if (context.Result is JsonResult result2) + { + logEntity.RequestResult = result2.Value?.ToString(); + } + + if (context.Result is ObjectResult result3) + { + logEntity.RequestResult = JsonHelper.ObjToStr(result3.Value); + } + + } + + if (operLogAttribute.IsSaveRequestData) + { + //logEntity.RequestParam = context.HttpContext.GetRequestValue(logEntity.RequestMethod); + } + + await _repository.InsertAsync(logEntity); + + + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/IOperationLogService.cs b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/IOperationLogService.cs new file mode 100644 index 00000000..da283aae --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/IOperationLogService.cs @@ -0,0 +1,12 @@ +using Yi.Framework.Infrastructure.Ddd.Services.Abstract; + +namespace Yi.Framework.Module.OperLogManager +{ + /// + /// OperationLog服务抽象 + /// + public interface IOperationLogService : ICrudAppService + { + + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperEnum.cs b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperEnum.cs new file mode 100644 index 00000000..583192e4 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperEnum.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Module.OperLogManager +{ + public enum OperEnum + { + Insert = 1, + Update = 2, + Delete = 3, + Auth = 4, + Export = 5, + Import = 6, + ForcedOut = 7, + GenerateCode = 8, + ClearData = 9 + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperLogAttribute.cs b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperLogAttribute.cs new file mode 100644 index 00000000..3881f292 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperLogAttribute.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Framework.Module.OperLogManager +{ + [AttributeUsage(AttributeTargets.Method)] + public class OperLogAttribute : Attribute + { + /// + /// 操作类型 + /// + public OperEnum OperType { get; set; } + + /// + /// 日志标题(模块) + /// + public string Title { get; set; } + + /// + /// 是否保存请求数据 + /// + public bool IsSaveRequestData { get; set; } = true; + + /// + /// 是否保存返回数据 + /// + public bool IsSaveResponseData { get; set; } = true; + + public OperLogAttribute(string title, OperEnum operationType) + { + Title = title; + OperType = operationType; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperationLogEntity.cs b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperationLogEntity.cs new file mode 100644 index 00000000..800a50cc --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperationLogEntity.cs @@ -0,0 +1,65 @@ +using SqlSugar; +using Yi.Framework.Infrastructure.Data.Auditing; +using Yi.Framework.Infrastructure.Ddd.Entities; + +namespace Yi.Framework.Module.OperLogManager +{ + /// + /// 操作日志表 + /// + [SugarTable("OperationLog")] + public class OperationLogEntity : IEntity, ICreationAuditedObject + { + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true)] + public long Id { get; set; } + /// + /// 操作模块 + /// + [SugarColumn(ColumnName = "Title")] + public string? Title { get; set; } + /// + /// 操作类型 + /// + [SugarColumn(ColumnName = "OperType")] + public OperEnum OperType { get; set; } + /// + /// 请求方法 + /// + [SugarColumn(ColumnName = "RequestMethod")] + public string? RequestMethod { get; set; } + /// + /// 操作人员 + /// + [SugarColumn(ColumnName = "OperUser")] + public string? OperUser { get; set; } + /// + /// 操作Ip + /// + [SugarColumn(ColumnName = "OperIp")] + public string? OperIp { get; set; } + /// + /// 操作地点 + /// + [SugarColumn(ColumnName = "OperLocation")] + public string? OperLocation { get; set; } + /// + /// 操作方法 + /// + [SugarColumn(ColumnName = "Method")] + public string? Method { get; set; } + /// + /// 请求参数 + /// + [SugarColumn(ColumnName = "RequestParam")] + public string? RequestParam { get; set; } + /// + /// 请求结果 + /// + [SugarColumn(ColumnName = "RequestResult", Length = 9999)] + public string? RequestResult { get; set; } + + public DateTime CreationTime { get; set; } + + public long? CreatorId { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperationLogGetListInputVo.cs b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperationLogGetListInputVo.cs new file mode 100644 index 00000000..e290d334 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperationLogGetListInputVo.cs @@ -0,0 +1,10 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos; + +namespace Yi.Framework.Module.OperLogManager +{ + public class OperationLogGetListInputVo : PagedAllResultRequestDto + { + public OperEnum? OperType { get; set; } + public string? OperUser { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperationLogGetListOutputDto.cs b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperationLogGetListOutputDto.cs new file mode 100644 index 00000000..69cbf559 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperationLogGetListOutputDto.cs @@ -0,0 +1,19 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; + +namespace Yi.Framework.Module.OperLogManager +{ + public class OperationLogGetListOutputDto : IEntityDto + { + public long Id { get; set; } + public string? Title { get; set; } + public OperEnum OperType { get; set; } + public string? RequestMethod { get; set; } + public string? OperUser { get; set; } + public string? OperIp { get; set; } + public string? OperLocation { get; set; } + public string? Method { get; set; } + public string? RequestParam { get; set; } + public string? RequestResult { get; set; } + public DateTime CreationTime { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperationLogService.cs b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperationLogService.cs new file mode 100644 index 00000000..2df65f49 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/OperLogManager/OperationLogService.cs @@ -0,0 +1,36 @@ +using Furion.DependencyInjection; +using Furion.DynamicApiController; +using Microsoft.AspNetCore.Mvc; +using SqlSugar; +using Yi.Framework.Infrastructure.Ddd.Dtos; +using Yi.Framework.Infrastructure.Ddd.Services; + +namespace Yi.Framework.Module.OperLogManager +{ + /// + /// OperationLog服务实现 + /// + //[AppService] + public class OperationLogService : CrudAppService, + IOperationLogService, IDynamicApiController, ITransient + { + public override async Task> GetListAsync(OperationLogGetListInputVo input) + { + var entity = await MapToEntityAsync(input); + + RefAsync total = 0; + + var entities = await _DbQueryable.WhereIF(!string.IsNullOrEmpty(input.OperUser), x => x.OperUser.Contains(input.OperUser!)) + .WhereIF(input.OperType is not null, x => x.OperType == input.OperType) + .WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime) + .ToPageListAsync(input.PageNum, input.PageSize, total); + return new PagedResultDto(total, await MapToGetListOutputDtosAsync(entities)); + } + + [NonAction] + public override Task UpdateAsync(long id, OperationLogGetListOutputDto input) + { + return base.UpdateAsync(id, input); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/Sms/Aliyun/SmsAliyunManager.cs b/Yi.Furion.Rbac/Yi.Framework.Module/Sms/Aliyun/SmsAliyunManager.cs new file mode 100644 index 00000000..8e2756cd --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/Sms/Aliyun/SmsAliyunManager.cs @@ -0,0 +1,66 @@ +using AlibabaCloud.SDK.Dysmsapi20170525; +using Furion.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Yi.Framework.Module.Sms.Aliyun +{ + public class SmsAliyunManager:ISingleton + { + public Client AliyunClient { get; set; } + private ILogger _logger; + private SmsAliyunOptions Options { get; set; } + public SmsAliyunManager(ILogger logger, IOptions options) + { + Options = options.Value; + if (Options.EnableFeature) + { + _logger = logger; + AliyunClient = CreateClient(Options.AccessKeyId, Options.AccessKeySecret); + } + + } + + private static Client CreateClient(string accessKeyId, string accessKeySecret) + { + AlibabaCloud.OpenApiClient.Models.Config config = new AlibabaCloud.OpenApiClient.Models.Config + { + // 必填,您的 AccessKey ID + AccessKeyId = accessKeyId, + // 必填,您的 AccessKey Secret + AccessKeySecret = accessKeySecret, + }; + // 访问的域名 + config.Endpoint = "dysmsapi.aliyuncs.com"; + return new Client(config); + } + + + /// + /// 发送短信 + /// + /// + /// + /// + public async Task Send(string phoneNumbers, string code) + { + try + { + AlibabaCloud.SDK.Dysmsapi20170525.Models.SendSmsRequest sendSmsRequest = new AlibabaCloud.SDK.Dysmsapi20170525.Models.SendSmsRequest + { + PhoneNumbers = phoneNumbers, + SignName = Options.SignName, + TemplateCode = Options.TemplateCode, + TemplateParam = System.Text.Json.JsonSerializer.Serialize(new { code }) + }; + + var response = await AliyunClient.SendSmsAsync(sendSmsRequest); + } + + catch (Exception _error) + { + _logger.LogError(_error, _error.Message); + } + } + } +} \ No newline at end of file diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/Sms/Aliyun/SmsAliyunOptions.cs b/Yi.Furion.Rbac/Yi.Framework.Module/Sms/Aliyun/SmsAliyunOptions.cs new file mode 100644 index 00000000..43395beb --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/Sms/Aliyun/SmsAliyunOptions.cs @@ -0,0 +1,14 @@ +namespace Yi.Framework.Module.Sms.Aliyun +{ + public class SmsAliyunOptions + { + public string AccessKeyId { get; set; } + public string AccessKeySecret { get; set; } + + public string SignName { get; set; } + + + public string TemplateCode { get; set; } + public bool EnableFeature { get; set; } = true; + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/Startup.cs b/Yi.Furion.Rbac/Yi.Framework.Module/Startup.cs new file mode 100644 index 00000000..a64c966c --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/Startup.cs @@ -0,0 +1,26 @@ +using Furion; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Yi.Framework.Infrastructure.AspNetCore; +using Yi.Framework.Infrastructure.Sqlsugar; + +namespace Yi.Framework.Module; + +public class Startup : AppStartup +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddCurrentUserServer(); + + services.Configure(App.Configuration.GetSection("DbConnOptions")); + + services.AddDbSqlsugarContextServer(); + } + + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + + } +} diff --git a/Yi.Furion.Rbac/Yi.Framework.Module/Yi.Framework.Module.csproj b/Yi.Furion.Rbac/Yi.Framework.Module/Yi.Framework.Module.csproj new file mode 100644 index 00000000..a238c36d --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Framework.Module/Yi.Framework.Module.csproj @@ -0,0 +1,19 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + + diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Domain/AccountManager.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Domain/AccountManager.cs new file mode 100644 index 00000000..a61972fa --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Domain/AccountManager.cs @@ -0,0 +1,140 @@ +using Yi.Framework.Infrastructure.Const; +using Yi.Framework.Infrastructure.Ddd.Repositories; +using Yi.Framework.Infrastructure.Exceptions; +using Yi.Framework.Infrastructure.Helper; +using Yi.Furion.Rbac.Core.ConstClasses; +using Yi.Furion.Rbac.Core.Dtos; +using Yi.Furion.Rbac.Core.Entities; + +namespace Yi.Furion.Rbac.Application.System.Domain +{ + + /// + /// 用户领域服务 + /// + public class AccountManager:ITransient + { + private readonly IRepository _repository; + public AccountManager(IRepository repository) + { + _repository = repository; + } + + /// + /// 登录效验 + /// + /// + /// + /// + /// + public async Task LoginValidationAsync(string userName, string password, Action userAction = null) + { + var user = new UserEntity(); + if (await ExistAsync(userName, o => user = o)) + { + if (userAction is not null) + { + userAction.Invoke(user); + } + if (user.Password == MD5Helper.SHA2Encode(password, user.Salt)) + { + return; + } + throw new UserFriendlyException(UserConst.登录失败_错误); + } + throw new UserFriendlyException(UserConst.登录失败_不存在); + } + + /// + /// 判断账户合法存在 + /// + /// + /// + /// + public async Task ExistAsync(string userName, Action userAction = null) + { + var user = await _repository.GetFirstAsync(u => u.UserName == userName && u.State == true); + if (userAction is not null) + { + userAction.Invoke(user); + } + if (user == null) + { + return false; + } + return true; + } + + /// + /// 令牌转换 + /// + /// + /// + + public Dictionary UserInfoToClaim(UserRoleMenuDto dto) + { + var claims = new Dictionary(); + claims.Add(TokenTypeConst.Id, dto.User.Id); + claims.Add(TokenTypeConst.UserName, dto.User.UserName); + if (dto.User.Email is not null) + { + claims.Add(TokenTypeConst.Email, dto.User.Email); + } + if (dto.User.Phone is not null) + { + claims.Add(TokenTypeConst.PhoneNumber, dto.User.Phone); + } + if (UserConst.Admin.Equals(dto.User.UserName)) + { + claims.Add(TokenTypeConst.Permission, UserConst.AdminPermissionCode); + claims.Add(TokenTypeConst.Roles, UserConst.AdminRolesCode); + } + else + { + claims.Add(TokenTypeConst.Permission, dto.PermissionCodes.Where(x => !string.IsNullOrEmpty(x))); + claims.Add(TokenTypeConst.Roles, dto.RoleCodes.Where(x => !string.IsNullOrEmpty(x))); + } + + return claims; + } + + /// + /// 更新密码 + /// + /// + /// + /// + /// + /// + public async Task UpdatePasswordAsync(long userId, string newPassword, string oldPassword) + { + var user = await _repository.GetByIdAsync(userId); + + if (!user.JudgePassword(oldPassword)) + { + throw new UserFriendlyException("无效更新!原密码错误!"); + } + user.Password = newPassword; + user.BuildPassword(); + await _repository.UpdateAsync(user); + } + + /// + /// 重置密码 + /// + /// + /// + /// + public async Task RestPasswordAsync(long userId, string password) + { + var user = await _repository.GetByIdAsync(userId); + user.Id = userId; + user.Password = password; + user.BuildPassword(); + return await _repository.UpdateAsync(user); + + + } + } + +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Domain/RoleManager.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Domain/RoleManager.cs new file mode 100644 index 00000000..53fda996 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Domain/RoleManager.cs @@ -0,0 +1,42 @@ +using Yi.Framework.Infrastructure.Ddd.Repositories; +using Yi.Framework.Infrastructure.Helper; +using Yi.Furion.Rbac.Core.Entities; + +namespace Yi.Furion.Rbac.Application.System.Domain +{ + public class RoleManager:ITransient + { + private IRepository _repository; + private IRepository _roleMenuRepository; + public RoleManager(IRepository repository, IRepository roleMenuRepository) + { + _repository = repository; + _roleMenuRepository = roleMenuRepository; + } + + /// + /// 给角色设置菜单 + /// + /// + /// + /// + public async Task GiveRoleSetMenuAsync(List roleIds, List menuIds) + { + //这个是需要事务的,在service中进行工作单元 + await _roleMenuRepository.DeleteAsync(u => roleIds.Contains(u.RoleId)); + //遍历用户 + foreach (var roleId in roleIds) + { + //添加新的关系 + List roleMenuEntity = new(); + foreach (var menu in menuIds) + { + roleMenuEntity.Add(new RoleMenuEntity() { Id = SnowflakeHelper.NextId, RoleId = roleId, MenuId = menu }); + } + //一次性批量添加 + await _roleMenuRepository.InsertRangeAsync(roleMenuEntity); + } + + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Domain/UserManager.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Domain/UserManager.cs new file mode 100644 index 00000000..2ae244fa --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Domain/UserManager.cs @@ -0,0 +1,77 @@ +using Yi.Framework.Infrastructure.Ddd.Repositories; +using Yi.Framework.Infrastructure.Helper; +using Yi.Furion.Rbac.Core.Entities; + +namespace Yi.Furion.Rbac.Application.System.Domain +{ + public class UserManager:ITransient + { + private readonly IRepository _repository; + private readonly IRepository _repositoryUserRole; + private readonly IRepository _repositoryUserPost; + public UserManager(IRepository repository, IRepository repositoryUserRole, IRepository repositoryUserPost) => + (_repository, _repositoryUserRole, _repositoryUserPost) = + (repository, repositoryUserRole, repositoryUserPost); + + /// + /// 给用户设置角色 + /// + /// + /// + /// + public async Task GiveUserSetRoleAsync(List userIds, List roleIds) + { + //删除用户之前所有的用户角色关系(物理删除,没有恢复的必要) + await _repositoryUserRole.DeleteAsync(u => userIds.Contains(u.UserId)); + + if (roleIds is not null) + { + //遍历用户 + foreach (var userId in userIds) + { + //添加新的关系 + List userRoleEntities = new(); + + foreach (var roleId in roleIds) + { + userRoleEntities.Add(new UserRoleEntity() { Id = SnowflakeHelper.NextId, UserId = userId, RoleId = roleId }); + } + //一次性批量添加 + await _repositoryUserRole.InsertRangeAsync(userRoleEntities); + } + } + } + + + /// + /// 给用户设置岗位 + /// + /// + /// + /// + public async Task GiveUserSetPostAsync(List userIds, List postIds) + { + //删除用户之前所有的用户角色关系(物理删除,没有恢复的必要) + await _repositoryUserPost.DeleteAsync(u => userIds.Contains(u.UserId)); + if (postIds is not null) + { + //遍历用户 + foreach (var userId in userIds) + { + //添加新的关系 + List userPostEntities = new(); + foreach (var post in postIds) + { + userPostEntities.Add(new UserPostEntity() { Id = SnowflakeHelper.NextId, UserId = userId, PostId = post }); + } + + //一次性批量添加 + await _repositoryUserPost.InsertRangeAsync(userPostEntities); + } + + } + } + + } + +} \ No newline at end of file diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/CaptchaImageDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/CaptchaImageDto.cs new file mode 100644 index 00000000..e6dc2cc5 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/CaptchaImageDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Account +{ + public class CaptchaImageDto + { + public string Code { get; set; } = string.Empty; + public Guid Uuid { get; set; } = Guid.Empty; + public byte[] Img { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/LoginInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/LoginInputVo.cs new file mode 100644 index 00000000..ef9a7eb1 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/LoginInputVo.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Account +{ + public class LoginInputVo + { + public string UserName { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; + + public string Uuid { get; set; } + + public string Code { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/PhoneCaptchaImageDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/PhoneCaptchaImageDto.cs new file mode 100644 index 00000000..0882c629 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/PhoneCaptchaImageDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Account +{ + public class PhoneCaptchaImageDto + { + public string Phone { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/RegisterDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/RegisterDto.cs new file mode 100644 index 00000000..3a73ce0b --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/RegisterDto.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Account +{ + public class RegisterDto + { + + //电话号码,根据code的表示来获取 + + /// + /// 账号 + /// + public string UserName { get; set; } = string.Empty; + + /// + /// 密码 + /// + public string Password { get; set; } = string.Empty; + + /// + /// 唯一标识码 + /// + public string Uuid { get; set; } + + /// + /// 电话 + /// + public long Phone { get; set; } + + /// + /// 验证码 + /// + public string Code { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/RestPasswordDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/RestPasswordDto.cs new file mode 100644 index 00000000..81b836b5 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/RestPasswordDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Account +{ + public class RestPasswordDto + { + public string Password = string.Empty; + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/UpdateIconDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/UpdateIconDto.cs new file mode 100644 index 00000000..44158ecd --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/UpdateIconDto.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Account +{ + public class UpdateIconDto + { + public string Icon { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/UpdatePasswordDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/UpdatePasswordDto.cs new file mode 100644 index 00000000..6c5a0394 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Account/UpdatePasswordDto.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Account +{ + public class UpdatePasswordDto + { + public string NewPassword { get; set; } = string.Empty; + public string OldPassword { get; set; } = string.Empty; + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Dept/DeptCreateInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Dept/DeptCreateInputVo.cs new file mode 100644 index 00000000..b202b5e6 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Dept/DeptCreateInputVo.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Dept +{ + /// + /// Dept输入创建对象 + /// + public class DeptCreateInputVo + { + public long Id { get; set; } + public DateTime CreationTime { get; set; } = DateTime.Now; + public long? CreatorId { get; set; } + public bool State { get; set; } + public string DeptName { get; set; } = string.Empty; + public string DeptCode { get; set; } = string.Empty; + public string Leader { get; set; } + public long ParentId { get; set; } + public string Remark { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Dept/DeptGetListInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Dept/DeptGetListInputVo.cs new file mode 100644 index 00000000..54c4213d --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Dept/DeptGetListInputVo.cs @@ -0,0 +1,16 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Dept +{ + public class DeptGetListInputVo : PagedAllResultRequestDto + { + public long Id { get; set; } + public bool? State { get; set; } + public string DeptName { get; set; } + public string DeptCode { get; set; } + public string Leader { get; set; } + + public DateTime? StartTime { get; set; } + public DateTime? EndTime { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Dept/DeptGetListOutputDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Dept/DeptGetListOutputDto.cs new file mode 100644 index 00000000..8f0fa31b --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Dept/DeptGetListOutputDto.cs @@ -0,0 +1,19 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Dept +{ + public class DeptGetListOutputDto : IEntityDto + { + public long Id { get; set; } + public DateTime CreationTime { get; set; } = DateTime.Now; + public long? CreatorId { get; set; } + public bool State { get; set; } + public string DeptName { get; set; } = string.Empty; + public string DeptCode { get; set; } = string.Empty; + public string Leader { get; set; } + public long ParentId { get; set; } + public string Remark { get; set; } + + public int OrderNum { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Dept/DeptGetOutputDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Dept/DeptGetOutputDto.cs new file mode 100644 index 00000000..b05d552d --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Dept/DeptGetOutputDto.cs @@ -0,0 +1,20 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Dept +{ + public class DeptGetOutputDto : IEntityDto + { + public long Id { get; set; } + public bool State { get; set; } + public string DeptName { get; set; } = string.Empty; + public string DeptCode { get; set; } = string.Empty; + public string Leader { get; set; } + public string Remark { get; set; } + + public long? deptId { get; set; } + + public int OrderNum { get; set; } + + public long ParentId { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Dept/DeptUpdateInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Dept/DeptUpdateInputVo.cs new file mode 100644 index 00000000..a0035f9c --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Dept/DeptUpdateInputVo.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Dept +{ + public class DeptUpdateInputVo + { + public long Id { get; set; } + public DateTime CreationTime { get; set; } = DateTime.Now; + public long? CreatorId { get; set; } + public bool State { get; set; } + public string DeptName { get; set; } = string.Empty; + public string DeptCode { get; set; } = string.Empty; + public string Leader { get; set; } + public long ParentId { get; set; } + public string Remark { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Mapper.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Mapper.cs deleted file mode 100644 index 890092aa..00000000 --- a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Mapper.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Yi.Furion.Rbac.Application; - -public class Mapper : IRegister -{ - public void Register(TypeAdapterConfig config) - { - } -} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Menu/MenuCreateInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Menu/MenuCreateInputVo.cs new file mode 100644 index 00000000..2da087a2 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Menu/MenuCreateInputVo.cs @@ -0,0 +1,27 @@ +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Menu +{ + /// + /// Menu输入创建对象 + /// + public class MenuCreateInputVo + { + public long Id { get; set; } + public DateTime CreationTime { get; set; } = DateTime.Now; + public long? CreatorId { get; set; } + public bool State { get; set; } + public string MenuName { get; set; } = string.Empty; + public MenuTypeEnum MenuType { get; set; } = MenuTypeEnum.Menu; + public string PermissionCode { get; set; } + public long ParentId { get; set; } + public string MenuIcon { get; set; } + public string Router { get; set; } + public bool IsLink { get; set; } + public bool IsCache { get; set; } + public bool IsShow { get; set; } = true; + public string Remark { get; set; } + public string Component { get; set; } + public string Query { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Menu/MenuGetListInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Menu/MenuGetListInputVo.cs new file mode 100644 index 00000000..dbc90525 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Menu/MenuGetListInputVo.cs @@ -0,0 +1,12 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Menu +{ + public class MenuGetListInputVo : PagedAndSortedResultRequestDto + { + + public bool? State { get; set; } + public string MenuName { get; set; } + + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Menu/MenuGetListOutputDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Menu/MenuGetListOutputDto.cs new file mode 100644 index 00000000..be61837e --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Menu/MenuGetListOutputDto.cs @@ -0,0 +1,28 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Menu +{ + public class MenuGetListOutputDto : IEntityDto + { + public long Id { get; set; } + public DateTime CreationTime { get; set; } = DateTime.Now; + public long? CreatorId { get; set; } + public bool State { get; set; } + public string MenuName { get; set; } = string.Empty; + public MenuTypeEnum MenuType { get; set; } = MenuTypeEnum.Menu; + public string PermissionCode { get; set; } + public long ParentId { get; set; } + public string MenuIcon { get; set; } + public string Router { get; set; } + public bool IsLink { get; set; } + public bool IsCache { get; set; } + public bool IsShow { get; set; } = true; + public string Remark { get; set; } + public string Component { get; set; } + public string Query { get; set; } + + public int OrderNum { get; set; } + //public List? Children { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Menu/MenuGetOutputDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Menu/MenuGetOutputDto.cs new file mode 100644 index 00000000..57da99e7 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Menu/MenuGetOutputDto.cs @@ -0,0 +1,29 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Menu +{ + public class MenuGetOutputDto : IEntityDto + { + public long Id { get; set; } + public DateTime CreationTime { get; set; } = DateTime.Now; + public long? CreatorId { get; set; } + public bool State { get; set; } + public string MenuName { get; set; } = string.Empty; + public MenuTypeEnum MenuType { get; set; } = MenuTypeEnum.Menu; + public string PermissionCode { get; set; } + public long ParentId { get; set; } + public string MenuIcon { get; set; } + public string Router { get; set; } + public bool IsLink { get; set; } + public bool IsCache { get; set; } + public bool IsShow { get; set; } = true; + public string Remark { get; set; } + public string Component { get; set; } + public string Query { get; set; } + + public int OrderNum { get; set; } + + //public List? Children { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Menu/MenuUpdateInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Menu/MenuUpdateInputVo.cs new file mode 100644 index 00000000..230a6fb4 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Menu/MenuUpdateInputVo.cs @@ -0,0 +1,25 @@ +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Menu +{ + public class MenuUpdateInputVo + { + public long Id { get; set; } + public DateTime CreationTime { get; set; } = DateTime.Now; + public long? CreatorId { get; set; } + public bool State { get; set; } + public string MenuName { get; set; } = string.Empty; + public MenuTypeEnum MenuType { get; set; } = MenuTypeEnum.Menu; + public string PermissionCode { get; set; } + public long ParentId { get; set; } + public string MenuIcon { get; set; } + public string Router { get; set; } + public bool IsLink { get; set; } + public bool IsCache { get; set; } + public bool IsShow { get; set; } = true; + public string Remark { get; set; } + public string Component { get; set; } + public string Query { get; set; } + //public List? Children { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Post/PostCreateInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Post/PostCreateInputVo.cs new file mode 100644 index 00000000..d702306f --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Post/PostCreateInputVo.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Post +{ + /// + /// Post输入创建对象 + /// + public class PostCreateInputVo + { + public long Id { get; set; } + public DateTime CreationTime { get; set; } = DateTime.Now; + public long? CreatorId { get; set; } + public bool State { get; set; } + public string PostCode { get; set; } = string.Empty; + public string PostName { get; set; } = string.Empty; + public string Remark { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Post/PostGetListInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Post/PostGetListInputVo.cs new file mode 100644 index 00000000..5eaff4f2 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Post/PostGetListInputVo.cs @@ -0,0 +1,11 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Post +{ + public class PostGetListInputVo : PagedAndSortedResultRequestDto + { + public bool? State { get; set; } + //public string? PostCode { get; set; }=string.Empty; + public string PostName { get; set; } = string.Empty; + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Post/PostGetListOutputDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Post/PostGetListOutputDto.cs new file mode 100644 index 00000000..45431490 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Post/PostGetListOutputDto.cs @@ -0,0 +1,16 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Post +{ + public class PostGetListOutputDto : IEntityDto + { + public long Id { get; set; } + public DateTime CreationTime { get; set; } = DateTime.Now; + public bool State { get; set; } + public string PostCode { get; set; } = string.Empty; + public string PostName { get; set; } = string.Empty; + public string Remark { get; set; } + + public int OrderNum { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Post/PostGetOutputDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Post/PostGetOutputDto.cs new file mode 100644 index 00000000..67d140bd --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Post/PostGetOutputDto.cs @@ -0,0 +1,17 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Post +{ + public class PostGetOutputDto : IEntityDto + { + public long Id { get; set; } + public DateTime CreationTime { get; set; } = DateTime.Now; + public long? CreatorId { get; set; } + public bool State { get; set; } + public string PostCode { get; set; } = string.Empty; + public string PostName { get; set; } = string.Empty; + public string Remark { get; set; } + + public int OrderNum { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Post/PostUpdateInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Post/PostUpdateInputVo.cs new file mode 100644 index 00000000..8293c0cc --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Post/PostUpdateInputVo.cs @@ -0,0 +1,13 @@ +namespace Yi.Furion.Rbac.Application.System.Dtos.Post +{ + public class PostUpdateInputVo + { + public long Id { get; set; } + public DateTime CreationTime { get; set; } = DateTime.Now; + public long? CreatorId { get; set; } + public bool State { get; set; } + public string PostCode { get; set; } = string.Empty; + public string PostName { get; set; } = string.Empty; + public string Remark { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Role/RoleCreateInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Role/RoleCreateInputVo.cs new file mode 100644 index 00000000..c79196f7 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Role/RoleCreateInputVo.cs @@ -0,0 +1,22 @@ +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Role +{ + /// + /// Role输入创建对象 + /// + public class RoleCreateInputVo + { + public string RoleName { get; set; } + public string RoleCode { get; set; } + public string Remark { get; set; } + public DataScopeEnum DataScope { get; set; } = DataScopeEnum.ALL; + public bool State { get; set; } = true; + + public int OrderNum { get; set; } + + public List DeptIds { get; set; } + + public List MenuIds { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Role/RoleGetListInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Role/RoleGetListInputVo.cs new file mode 100644 index 00000000..cc1ee90e --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Role/RoleGetListInputVo.cs @@ -0,0 +1,12 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Role +{ + public class RoleGetListInputVo : PagedAllResultRequestDto + { + public string RoleName { get; set; } + public string RoleCode { get; set; } + public bool? State { get; set; } + + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Role/RoleGetListOutputDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Role/RoleGetListOutputDto.cs new file mode 100644 index 00000000..9495d183 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Role/RoleGetListOutputDto.cs @@ -0,0 +1,19 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Role +{ + public class RoleGetListOutputDto : IEntityDto + { + public long Id { get; set; } + public DateTime CreationTime { get; set; } = DateTime.Now; + public long? CreatorId { get; set; } + public string RoleName { get; set; } + public string RoleCode { get; set; } + public string Remark { get; set; } + public DataScopeEnum DataScope { get; set; } = DataScopeEnum.ALL; + public bool State { get; set; } + + public int OrderNum { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Role/RoleGetOutputDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Role/RoleGetOutputDto.cs new file mode 100644 index 00000000..93104a9a --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Role/RoleGetOutputDto.cs @@ -0,0 +1,19 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Role +{ + public class RoleGetOutputDto : IEntityDto + { + public long Id { get; set; } + public DateTime CreationTime { get; set; } = DateTime.Now; + public long? CreatorId { get; set; } + public string RoleName { get; set; } + public string RoleCode { get; set; } + public string Remark { get; set; } + public DataScopeEnum DataScope { get; set; } = DataScopeEnum.ALL; + public bool State { get; set; } + + public int OrderNum { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Role/RoleUpdateInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Role/RoleUpdateInputVo.cs new file mode 100644 index 00000000..3365bdf3 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/Role/RoleUpdateInputVo.cs @@ -0,0 +1,19 @@ +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Application.System.Dtos.Role +{ + public class RoleUpdateInputVo + { + public string RoleName { get; set; } + public string RoleCode { get; set; } + public string Remark { get; set; } + public DataScopeEnum DataScope { get; set; } = DataScopeEnum.ALL; + public bool State { get; set; } + + public int OrderNum { get; set; } + + public List DeptIds { get; set; } + + public List MenuIds { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/ProfileUpdateInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/ProfileUpdateInputVo.cs new file mode 100644 index 00000000..f3e7cb8a --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/ProfileUpdateInputVo.cs @@ -0,0 +1,17 @@ +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Application.System.Dtos.User +{ + public class ProfileUpdateInputVo + { + public string Name { get; set; } + public int? Age { get; set; } + public string Nick { get; set; } + public string Email { get; set; } + public string Address { get; set; } + public long? Phone { get; set; } + public string Introduction { get; set; } + public string Remark { get; set; } + public SexEnum? Sex { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/UserCreateInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/UserCreateInputVo.cs new file mode 100644 index 00000000..e8e112fb --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/UserCreateInputVo.cs @@ -0,0 +1,27 @@ +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Application.System.Dtos.User +{ + /// + /// User输入创建对象 + /// + public class UserCreateInputVo + { + public string Name { get; set; } + public int? Age { get; set; } + public string UserName { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; + public string Icon { get; set; } + public string Nick { get; set; } + public string Email { get; set; } + public string Address { get; set; } + public long? Phone { get; set; } + public string Introduction { get; set; } + public string Remark { get; set; } + public SexEnum Sex { get; set; } = SexEnum.Unknown; + public List RoleIds { get; set; } + public List PostIds { get; set; } + public long? DeptId { get; set; } + public bool State { get; set; } = true; + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/UserGetListInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/UserGetListInputVo.cs new file mode 100644 index 00000000..a19cb23b --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/UserGetListInputVo.cs @@ -0,0 +1,18 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos; + +namespace Yi.Furion.Rbac.Application.System.Dtos.User +{ + public class UserGetListInputVo : PagedAllResultRequestDto + { + public string Name { get; set; } + public string UserName { get; set; } + public long? Phone { get; set; } + + public bool? State { get; set; } + + public long? DeptId { get; set; } + + public string Ids { get; set; } + + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/UserGetListOutputDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/UserGetListOutputDto.cs new file mode 100644 index 00000000..45c49083 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/UserGetListOutputDto.cs @@ -0,0 +1,30 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Application.System.Dtos.User +{ + public class UserGetListOutputDto : IEntityDto + { + public long Id { get; set; } + public string Name { get; set; } + public int? Age { get; set; } + public string UserName { get; set; } = string.Empty; + public string Icon { get; set; } + public string Nick { get; set; } + public string Email { get; set; } + public string Ip { get; set; } + public string Address { get; set; } + public long? Phone { get; set; } + public string Introduction { get; set; } + public string Remark { get; set; } + public SexEnum Sex { get; set; } = SexEnum.Unknown; + public long? DeptId { get; set; } + public DateTime CreationTime { get; set; } = DateTime.Now; + public long? CreatorId { get; set; } + + public bool State { get; set; } + + + public string DeptName { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/UserGetOutputDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/UserGetOutputDto.cs new file mode 100644 index 00000000..bf1f7e6b --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/UserGetOutputDto.cs @@ -0,0 +1,35 @@ +using Yi.Framework.Infrastructure.Ddd.Dtos.Abstract; +using Yi.Furion.Rbac.Application.System.Dtos.Dept; +using Yi.Furion.Rbac.Application.System.Dtos.Post; +using Yi.Furion.Rbac.Application.System.Dtos.Role; +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Application.System.Dtos.User +{ + public class UserGetOutputDto : IEntityDto + { + public long Id { get; set; } + public string Name { get; set; } + public int? Age { get; set; } + public string UserName { get; set; } = string.Empty; + public string Icon { get; set; } + public string Nick { get; set; } + public string Email { get; set; } + public string Ip { get; set; } + public string Address { get; set; } + public long? Phone { get; set; } + public string Introduction { get; set; } + public string Remark { get; set; } + public SexEnum Sex { get; set; } = SexEnum.Unknown; + public bool State { get; set; } + public DateTime CreationTime { get; set; } + + public long DeptId { get; set; } + + public DeptGetOutputDto Dept { get; set; } + + public List Posts { get; set; } + + public List Roles { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/UserUpdateInputVo.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/UserUpdateInputVo.cs new file mode 100644 index 00000000..423ab831 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Dtos/User/UserUpdateInputVo.cs @@ -0,0 +1,28 @@ +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Application.System.Dtos.User +{ + public class UserUpdateInputVo + { + public string Name { get; set; } + public int? Age { get; set; } + public string UserName { get; set; } + + [AdaptIgnore] + public string Password { get; set; } + public string Icon { get; set; } + public string Nick { get; set; } + public string Email { get; set; } + public string Ip { get; set; } + public string Address { get; set; } + public long? Phone { get; set; } + public string Introduction { get; set; } + public string Remark { get; set; } + public SexEnum? Sex { get; set; } + public long? DeptId { get; set; } + public List PostIds { get; set; } + + public List RoleIds { get; set; } + public bool? State { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Event/LoginEventHandler.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Event/LoginEventHandler.cs new file mode 100644 index 00000000..c011019c --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Event/LoginEventHandler.cs @@ -0,0 +1,77 @@ +using Furion.EventBus; +using IPTools.Core; +using UAParser; +using Yi.Framework.Infrastructure.AspNetCore; +using Yi.Framework.Infrastructure.Ddd.Repositories; +using Yi.Framework.Infrastructure.Helper; +using Yi.Furion.Rbac.Core.Entities; +using Yi.Furion.Rbac.Core.Etos; + +namespace Yi.Furion.Rbac.Application.System.Event +{ + public class LoginEventHandler : IEventSubscriber, ISingleton + { + private readonly IRepository _loginLogRepository; + private readonly HttpContext _httpContext; + public LoginEventHandler(IRepository loginLogRepository, IHttpContextAccessor httpContextAccessor) + { + _loginLogRepository = loginLogRepository; + _httpContext = httpContextAccessor.HttpContext; + } + [EventSubscribe(nameof(LoginEventSource))] + public Task HandlerAsync(EventHandlerExecutingContext context) + { + var eventData=(LoginEventArgs)context.Source.Payload; + var loginLogEntity = GetLoginLogInfo(_httpContext); + loginLogEntity.Id = SnowflakeHelper.NextId; + loginLogEntity.LogMsg = eventData.UserName + "登录系统"; + loginLogEntity.LoginUser = eventData.UserName; + loginLogEntity.LoginIp = _httpContext.GetClientIp(); + + _loginLogRepository.InsertAsync(loginLogEntity); + return Task.CompletedTask; + } + + /// + /// 获取客户端信息 + /// + /// + /// + private static ClientInfo GetClientInfo(HttpContext context) + { + var str = context.GetUserAgent(); + var uaParser = Parser.GetDefault(); + ClientInfo c = uaParser.Parse(str); + return c; + } + + /// + /// 记录用户登陆信息 + /// + /// + /// + private static LoginLogEntity GetLoginLogInfo(HttpContext context) + { + var ipAddr = context.GetClientIp(); + IpInfo location; + if (ipAddr == "127.0.0.1") + { + location = new IpInfo() { Province = "本地", City = "本机" }; + } + else + { + location = IpTool.Search(ipAddr); + } + ClientInfo clientInfo = GetClientInfo(context); + LoginLogEntity entity = new() + { + Browser = clientInfo.Device.Family, + Os = clientInfo.OS.ToString(), + LoginIp = ipAddr, + LoginLocation = location.Province + "-" + location.City + }; + + return entity; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/IDeptService.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/IDeptService.cs new file mode 100644 index 00000000..1d7ea8e4 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/IDeptService.cs @@ -0,0 +1,13 @@ +using Yi.Framework.Infrastructure.Ddd.Services.Abstract; +using Yi.Furion.Rbac.Application.System.Dtos.Dept; + +namespace Yi.Furion.Rbac.Application.System.Services +{ + /// + /// Dept服务抽象 + /// + public interface IDeptService : ICrudAppService + { + + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/IMenuService.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/IMenuService.cs new file mode 100644 index 00000000..5a91d470 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/IMenuService.cs @@ -0,0 +1,13 @@ +using Yi.Framework.Infrastructure.Ddd.Services.Abstract; +using Yi.Furion.Rbac.Application.System.Dtos.Menu; + +namespace Yi.Furion.Rbac.Application.System.Services +{ + /// + /// Menu服务抽象 + /// + public interface IMenuService : ICrudAppService + { + + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/IPostService.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/IPostService.cs new file mode 100644 index 00000000..bc896083 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/IPostService.cs @@ -0,0 +1,13 @@ +using Yi.Framework.Infrastructure.Ddd.Services.Abstract; +using Yi.Furion.Rbac.Application.System.Dtos.Post; + +namespace Yi.Furion.Rbac.Application.System.Services +{ + /// + /// Post服务抽象 + /// + public interface IPostService : ICrudAppService + { + + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/IRoleService.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/IRoleService.cs new file mode 100644 index 00000000..e06003e0 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/IRoleService.cs @@ -0,0 +1,13 @@ +using Yi.Framework.Infrastructure.Ddd.Services.Abstract; +using Yi.Furion.Rbac.Application.System.Dtos.Role; + +namespace Yi.Furion.Rbac.Application.System.Services +{ + /// + /// Role服务抽象 + /// + public interface IRoleService : ICrudAppService + { + + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/ISystemService.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/ISystemService.cs deleted file mode 100644 index af3af72f..00000000 --- a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/ISystemService.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Yi.Furion.Rbac.Application; - -public interface ISystemService -{ - string GetDescription(); -} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/IUserService.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/IUserService.cs new file mode 100644 index 00000000..90c72f29 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/IUserService.cs @@ -0,0 +1,12 @@ +using Yi.Framework.Infrastructure.Ddd.Services.Abstract; +using Yi.Furion.Rbac.Application.System.Dtos.User; + +namespace Yi.Furion.Rbac.Application.System.Services +{ + /// + /// User服务抽象 + /// + public interface IUserService : ICrudAppService + { + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/AccountService.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/AccountService.cs new file mode 100644 index 00000000..3782c4ce --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/AccountService.cs @@ -0,0 +1,375 @@ +using System.Text.RegularExpressions; +using Furion.EventBus; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; +using SqlSugar; +using Yi.Framework.Infrastructure.CurrentUsers; +using Yi.Framework.Infrastructure.Ddd.Repositories; +using Yi.Framework.Infrastructure.Ddd.Services; +using Yi.Framework.Infrastructure.Exceptions; +using Yi.Framework.Infrastructure.Uow; +using Yi.Framework.Module.ImageSharp.HeiCaptcha; +using Yi.Framework.Module.Sms.Aliyun; +using Yi.Furion.Rbac.Application.System.Domain; +using Yi.Furion.Rbac.Application.System.Dtos.Account; +using Yi.Furion.Rbac.Core.ConstClasses; +using Yi.Furion.Rbac.Core.Dtos; +using Yi.Furion.Rbac.Core.Entities; +using Yi.Furion.Rbac.Core.Etos; +using Yi.Furion.Rbac.Sqlsugar.Core.Repositories; + +namespace Yi.Furion.Rbac.Application.System.Services.Impl +{ + public class AccountService : ApplicationService, ITransient, IDynamicApiController + { + + public AccountService(IUserRepository userRepository, ICurrentUser currentUser, AccountManager accountManager, IRepository menuRepository, SmsAliyunManager smsAliyunManager, IOptions smsAliyunManagerOptions, SecurityCodeHelper securityCode, IMemoryCache memoryCache, IEventPublisher eventPublisher) => + (_userRepository, _currentUser, _accountManager, _menuRepository, _smsAliyunManager, _smsAliyunManagerOptions, _securityCode, _memoryCache, _eventPublisher) = + (userRepository, currentUser, accountManager, menuRepository, smsAliyunManager, smsAliyunManagerOptions, securityCode, memoryCache, eventPublisher); + + private JwtTokenManager _jwtTokenManager { get; set; } + + private IUserRepository _userRepository { get; set; } + + private ICurrentUser _currentUser { get; set; } + + private AccountManager _accountManager { get; set; } + + + private IRepository _menuRepository { get; set; } + + + private SecurityCodeHelper _securityCode { get; set; } + + + private IEventPublisher _eventPublisher { get; set; } + + + + + private IUserService _userService { get; set; } + + + + private UserManager _userManager { get; set; } + + + private IUnitOfWorkManager _unitOfWorkManager { get; set; } + + + private IRepository _roleRepository { get; set; } + + + private IMemoryCache _memoryCache { get; set; } + + + private SmsAliyunManager _smsAliyunManager { get; set; } + + + private IOptions _smsAliyunManagerOptions { get; set; } + + /// + /// 效验图片登录验证码,无需和账号绑定 + /// + private void ValidationImageCaptcha(LoginInputVo input) + { + //登录不想要验证码 ,不效验 + return; + var value = _memoryCache.Get($"Yi:Captcha:{input.Code}"); + if (value is not null && value.Equals(input.Uuid)) + { + return; + } + throw new UserFriendlyException("验证码错误"); + } + + /// + /// 效验电话验证码,需要与电话号码绑定 + /// + private void ValidationPhoneCaptcha(RegisterDto input) + { + var value = _memoryCache.Get($"Yi:Phone:{input.Phone}"); + if (value is not null && value.Equals($"{input.Code}")) + { + //成功,需要清空 + _memoryCache.Remove($"Yi:Phone:{input.Phone}"); + return; + } + throw new UserFriendlyException("验证码错误"); + } + + /// + /// 登录 + /// + /// + /// + public async Task PostLoginAsync(LoginInputVo input) + { + if (string.IsNullOrEmpty(input.Password) || string.IsNullOrEmpty(input.UserName)) + { + throw new UserFriendlyException("请输入合理数据!"); + } + + //效验验证码 + ValidationImageCaptcha(input); + + UserEntity user = new(); + //登录成功 + await _accountManager.LoginValidationAsync(input.UserName, input.Password, x => user = x); + + //获取用户信息 + var userInfo = await _userRepository.GetUserAllInfoAsync(user.Id); + + if (userInfo.RoleCodes.Count == 0) + { + throw new UserFriendlyException(UserConst.用户无角色分配); + } + //这里抛出一个登录的事件 + + //不阻碍执行,无需等待 +#pragma warning disable CS4014 // 由于此调用不会等待,因此在调用完成前将继续执行当前方法 + + _eventPublisher.PublishAsync(new LoginEventSource(new LoginEventArgs + { + + UserId = userInfo.User.Id, + UserName = user.UserName + + }) + ); +#pragma warning restore CS4014 // 由于此调用不会等待,因此在调用完成前将继续执行当前方法 + + //创建token + var token = _jwtTokenManager.CreateToken(_accountManager.UserInfoToClaim(userInfo)); + return new { Token = token }; + } + + /// + /// 生成验证码 + /// + /// + + [AllowAnonymous] + public CaptchaImageDto GetCaptchaImage() + { + var uuid = Guid.NewGuid(); + var code = _securityCode.GetRandomEnDigitalText(4); + //将uuid与code,Redis缓存中心化保存起来,登录根据uuid比对即可 + //10分钟过期 + _memoryCache.Set($"Yi:Captcha:{code}", uuid, new TimeSpan(0, 10, 0)); + var imgbyte = _securityCode.GetEnDigitalCodeByte(code); + return new CaptchaImageDto { Img = imgbyte, Code = code, Uuid = uuid }; + } + + /// + /// 验证电话号码 + /// + /// + private async Task ValidationPhone(string str_handset) + { + var res = Regex.IsMatch(str_handset, "^(0\\d{2,3}-?\\d{7,8}(-\\d{3,5}){0,1})|(((13[0-9])|(15([0-3]|[5-9]))|(18[0-9])|(17[0-9])|(14[0-9]))\\d{8})$"); + if (res == false) + { + throw new UserFriendlyException("手机号码格式错误!请检查"); + } + if (await _userRepository.IsAnyAsync(x => x.Phone.ToString() == str_handset)) + { + throw new UserFriendlyException("该手机号已被注册!"); + + } + + } + + + /// + /// 注册 手机验证码 + /// + /// + [AllowAnonymous] + public async Task PostCaptchaPhone(PhoneCaptchaImageDto input) + { + await ValidationPhone(input.Phone); + var value = _memoryCache.Get($"Yi:Phone:{input.Phone}"); + + //防止暴刷 + if (value is not null) + { + throw new UserFriendlyException($"{input.Phone}已发送过验证码,10分钟后可重试"); + } + //生成一个4位数的验证码 + //发送短信,同时生成uuid + //key: 电话号码 value:验证码+uuid + var code = _securityCode.GetRandomEnDigitalText(4); + var uuid = Guid.NewGuid(); + + //未开启短信验证,默认8888 + if (_smsAliyunManagerOptions.Value.EnableFeature) + { + await _smsAliyunManager.Send(input.Phone, code); + } + else + { + code = "8888"; + } + _memoryCache.Set($"Yi:Phone:{input.Phone}", $"{code}", new TimeSpan(0, 10, 0)); + + + return new { Uuid = uuid }; + } + + /// + /// 注册,需要验证码通过 + /// + /// + /// + [AllowAnonymous] + public async Task PostRegisterAsync(RegisterDto input) + { + if (input.UserName == UserConst.Admin) + { + throw new UserFriendlyException("用户名无效注册!"); + } + + if (input.UserName.Length < 2) + { + throw new UserFriendlyException("账号名需大于等于2位!"); + } + if (input.Password.Length < 6) + { + throw new UserFriendlyException("密码需大于等于6位!"); + } + //效验验证码,根据电话号码获取 value,比对验证码已经uuid + ValidationPhoneCaptcha(input); + + + + //输入的用户名与电话号码都不能在数据库中存在 + UserEntity user = new(); + var isExist = await _userRepository.IsAnyAsync(x => + x.UserName == input.UserName + || x.Phone == input.Phone); + if (isExist) + { + throw new UserFriendlyException("用户已存在,注册失败"); + } + using (var uow = _unitOfWorkManager.CreateContext()) + { + var newUser = new UserEntity(input.UserName, input.Password, input.Phone); + + var entity = await _userRepository.InsertReturnEntityAsync(newUser); + //赋上一个初始角色 + var roleRepository = _roleRepository; + var role = await roleRepository.GetFirstAsync(x => x.RoleCode == UserConst.GuestRoleCode); + if (role is not null) + { + await _userManager.GiveUserSetRoleAsync(new List { entity.Id }, new List { role.Id }); + } + uow.Commit(); + } + + return true; + } + + + /// + /// 查询已登录的账户信息 + /// + /// + /// + [Route("/api/account")] + [Authorize] + public async Task Get() + { + //通过鉴权jwt获取到用户的id + var userId = _currentUser.Id; + //此处从缓存中获取即可 + //var data = _cacheManager.Get($"Yi:UserInfo:{userId}"); + var data = await _userRepository.GetUserAllInfoAsync(userId); + //系统用户数据被重置,老前端访问重新授权 + if (data is null) + { + throw new AuthException(); + } + + data.Menus.Clear(); + return data; + } + + + /// + /// 获取当前登录用户的前端路由 + /// + /// + [Authorize] + public async Task> GetVue3Router() + { + var userId = _currentUser.Id; + var data = await _userRepository.GetUserAllInfoAsync(userId); + var menus = data.Menus.ToList(); + + //为超级管理员直接给全部路由 + if (UserConst.Admin.Equals(data.User.UserName)) + { + menus = await _menuRepository.GetListAsync(); + } + //将后端菜单转换成前端路由,组件级别需要过滤 + List routers = menus.Vue3RouterBuild(); + return routers; + } + + /// + /// 退出登录 + /// + /// + public Task PostLogout() + { + return Task.FromResult(true); + } + + /// + /// 更新密码 + /// + /// + /// + public async Task UpdatePasswordAsync(UpdatePasswordDto input) + { + if (input.OldPassword.Equals(input.NewPassword)) + { + throw new UserFriendlyException("无效更新!输入的数据,新密码不能与老密码相同"); + } + await _accountManager.UpdatePasswordAsync(_currentUser.Id, input.NewPassword, input.OldPassword); + return true; + } + + /// + /// 重置密码 + /// + /// + /// + /// + [HttpPut] + public async Task RestPasswordAsync(long userId, RestPasswordDto input) + { + if (!string.IsNullOrEmpty(input.Password)) + { + throw new UserFriendlyException("重置密码不能为空!"); + } + await _accountManager.RestPasswordAsync(userId, input.Password); + return true; + } + + /// + /// 更新头像 + /// + /// + /// + public async Task UpdateIconAsync(UpdateIconDto input) + { + var entity = await _userRepository.GetByIdAsync(_currentUser.Id); + entity.Icon = input.Icon; + await _userRepository.UpdateAsync(entity); + + return true; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/DeptService.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/DeptService.cs new file mode 100644 index 00000000..04b25458 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/DeptService.cs @@ -0,0 +1,47 @@ +using SqlSugar; +using Yi.Framework.Infrastructure.Ddd.Dtos; +using Yi.Framework.Infrastructure.Ddd.Services; +using Yi.Furion.Rbac.Application.System.Dtos.Dept; +using Yi.Furion.Rbac.Core.Entities; + +namespace Yi.Furion.Rbac.Application.System.Services.Impl +{ + /// + /// Dept服务实现 + /// + public class DeptService : CrudAppService, + IDeptService, ITransient, IDynamicApiController + { + + /// + /// 通过角色id查询该角色全部部门 + /// + /// + //[Route("{roleId}")] + public async Task> GetListRoleIdAsync([FromRoute] long roleId) + { + var entities = await _DbQueryable.Where(d => SqlFunc.Subqueryable().Where(rd => rd.RoleId == roleId && d.Id == rd.DeptId).Any()).ToListAsync(); + return await MapToGetListOutputDtosAsync(entities); + } + + /// + /// 多查 + /// + /// + /// + public override async Task> GetListAsync(DeptGetListInputVo input) + { + RefAsync total = 0; + var entities = await _DbQueryable + .WhereIF(!string.IsNullOrEmpty(input.DeptName), u => u.DeptName.Contains(input.DeptName!)) + .WhereIF(input.State is not null, u => u.State == input.State) + .OrderBy(u => u.OrderNum, OrderByType.Asc) + .ToPageListAsync(input.PageNum, input.PageSize, total); + return new PagedResultDto + { + Items = await MapToGetListOutputDtosAsync(entities), + Total = total + }; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/MenuService.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/MenuService.cs new file mode 100644 index 00000000..e4dbaca4 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/MenuService.cs @@ -0,0 +1,41 @@ +using SqlSugar; +using Yi.Framework.Infrastructure.Ddd.Dtos; +using Yi.Framework.Infrastructure.Ddd.Services; +using Yi.Furion.Rbac.Application.System.Dtos.Menu; +using Yi.Furion.Rbac.Core.Entities; + +namespace Yi.Furion.Rbac.Application.System.Services.Impl +{ + /// + /// Menu服务实现 + /// + public class MenuService : CrudAppService, + IMenuService, ITransient, IDynamicApiController + { + + public override async Task> GetListAsync(MenuGetListInputVo input) + { + var entity = await MapToEntityAsync(input); + + RefAsync total = 0; + + var entities = await _DbQueryable.WhereIF(!string.IsNullOrEmpty(input.MenuName), x => x.MenuName.Contains(input.MenuName!)) + .WhereIF(input.State is not null, x => x.State == input.State) + .OrderByDescending(x => x.OrderNum) + .ToPageListAsync(input.PageNum, input.PageSize, total); + return new PagedResultDto(total, await MapToGetListOutputDtosAsync(entities)); + } + + /// + /// 查询当前角色的菜单 + /// + /// + /// + public async Task> GetListRoleIdAsync(long roleId) + { + var entities = await _DbQueryable.Where(m => SqlFunc.Subqueryable().Where(rm => rm.RoleId == roleId && rm.MenuId == m.Id).Any()).ToListAsync(); + + return await MapToGetListOutputDtosAsync(entities); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/PostService.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/PostService.cs new file mode 100644 index 00000000..58efb681 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/PostService.cs @@ -0,0 +1,27 @@ +using SqlSugar; +using Yi.Framework.Infrastructure.Ddd.Dtos; +using Yi.Framework.Infrastructure.Ddd.Services; +using Yi.Furion.Rbac.Application.System.Dtos.Post; +using Yi.Furion.Rbac.Core.Entities; + +namespace Yi.Furion.Rbac.Application.System.Services.Impl +{ + /// + /// Post服务实现 + /// + public class PostService : CrudAppService, + IPostService, ITransient, IDynamicApiController + { + public override async Task> GetListAsync(PostGetListInputVo input) + { + var entity = await MapToEntityAsync(input); + + RefAsync total = 0; + + var entities = await _DbQueryable.WhereIF(!string.IsNullOrEmpty(input.PostName), x => x.PostName.Contains(input.PostName!)) + .WhereIF(input.State is not null, x => x.State == input.State) + .ToPageListAsync(input.PageNum, input.PageSize, total); + return new PagedResultDto(total, await MapToGetListOutputDtosAsync(entities)); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/RoleService.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/RoleService.cs new file mode 100644 index 00000000..078ddbff --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/RoleService.cs @@ -0,0 +1,103 @@ +using SqlSugar; +using Yi.Framework.Infrastructure.Ddd.Dtos; +using Yi.Framework.Infrastructure.Ddd.Services; +using Yi.Framework.Infrastructure.Uow; +using Yi.Furion.Rbac.Application.System.Domain; +using Yi.Furion.Rbac.Application.System.Dtos.Role; +using Yi.Furion.Rbac.Core.Entities; + +namespace Yi.Furion.Rbac.Application.System.Services.Impl +{ + /// + /// Role服务实现 + /// + public class RoleService : CrudAppService, + IRoleService, ITransient, IDynamicApiController + { + public RoleService(RoleManager roleManager, IUnitOfWorkManager unitOfWorkManager) => + (_roleManager, _unitOfWorkManager) = + (roleManager, unitOfWorkManager); + private RoleManager _roleManager { get; set; } + + + private IUnitOfWorkManager _unitOfWorkManager { get; set; } + + + public override async Task> GetListAsync(RoleGetListInputVo input) + { + var entity = await MapToEntityAsync(input); + + RefAsync total = 0; + + var entities = await _DbQueryable.WhereIF(!string.IsNullOrEmpty(input.RoleCode), x => x.RoleCode.Contains(input.RoleCode!)) + .WhereIF(!string.IsNullOrEmpty(input.RoleName), x => x.RoleName.Contains(input.RoleName!)) + .WhereIF(input.State is not null, x => x.State == input.State) + .ToPageListAsync(input.PageNum, input.PageSize, total); + return new PagedResultDto(total, await MapToGetListOutputDtosAsync(entities)); + } + + /// + /// 添加角色 + /// + /// + /// + public override async Task CreateAsync(RoleCreateInputVo input) + { + RoleGetOutputDto outputDto; + using (var uow = _unitOfWorkManager.CreateContext()) + { + var entity = await MapToEntityAsync(input); + await _repository.InsertAsync(entity); + outputDto = await MapToGetOutputDtoAsync(entity); + await _roleManager.GiveRoleSetMenuAsync(new List { entity.Id }, input.MenuIds); + uow.Commit(); + } + + return outputDto; + } + + /// + /// 修改角色 + /// + /// + /// + /// + public override async Task UpdateAsync(long id, RoleUpdateInputVo input) + { + var dto = new RoleGetOutputDto(); + using (var uow = _unitOfWorkManager.CreateContext()) + { + var entity = await _repository.GetByIdAsync(id); + await MapToEntityAsync(input, entity); + await _repository.UpdateAsync(entity); + + await _roleManager.GiveRoleSetMenuAsync(new List { id }, input.MenuIds); + + dto = await MapToGetOutputDtoAsync(entity); + uow.Commit(); + } + return dto; + } + + + /// + /// 更新状态 + /// + /// + /// + /// + [Route("/api/role/{id}/{state}")] + public async Task UpdateStateAsync([FromRoute] long id, [FromRoute] bool state) + { + var entity = await _repository.GetByIdAsync(id); + if (entity is null) + { + throw new ApplicationException("角色未存在"); + } + + entity.State = state; + await _repository.UpdateAsync(entity); + return await MapToGetOutputDtoAsync(entity); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/UserService.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/UserService.cs new file mode 100644 index 00000000..1431305b --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/Impl/UserService.cs @@ -0,0 +1,190 @@ +using SqlSugar; +using Yi.Framework.Infrastructure.CurrentUsers; +using Yi.Framework.Infrastructure.Ddd.Dtos; +using Yi.Framework.Infrastructure.Ddd.Services; +using Yi.Framework.Infrastructure.Exceptions; +using Yi.Framework.Infrastructure.Uow; +using Yi.Framework.Module.OperLogManager; +using Yi.Furion.Rbac.Application.System.Domain; +using Yi.Furion.Rbac.Application.System.Dtos.User; +using Yi.Furion.Rbac.Core.ConstClasses; +using Yi.Furion.Rbac.Core.Entities; +using Yi.Furion.Rbac.Sqlsugar.Core.Repositories; + +namespace Yi.Furion.Rbac.Application.System.Services.Impl +{ + /// + /// User服务实现 + /// + public class UserService : CrudAppService, + IUserService, ITransient, IDynamicApiController + { + + + public UserService(UserManager userManager, IUserRepository userRepository, ICurrentUser currentUser, IUnitOfWorkManager unitOfWorkManager) => + (_userManager, _userRepository, _currentUser, _unitOfWorkManager) = + (userManager, userRepository, currentUser, unitOfWorkManager); + private UserManager _userManager { get; set; } + + + private IUnitOfWorkManager _unitOfWorkManager { get; set; } + + + private IUserRepository _userRepository { get; set; } + + + private ICurrentUser _currentUser { get; set; } + + /// + /// 查询用户 + /// + /// + /// + public override async Task> GetListAsync(UserGetListInputVo input) + { + var entity = await MapToEntityAsync(input); + + RefAsync total = 0; + + + List ids = input.Ids?.Split(",").Select(x => long.Parse(x)).ToList(); + var outPut = await _DbQueryable.WhereIF(!string.IsNullOrEmpty(input.UserName), x => x.UserName.Contains(input.UserName!)) + .WhereIF(input.Phone is not null, x => x.Phone.ToString()!.Contains(input.Phone.ToString()!)) + .WhereIF(!string.IsNullOrEmpty(input.Name), x => x.Name!.Contains(input.Name!)) + .WhereIF(input.State is not null, x => x.State == input.State) + .WhereIF(input.StartTime is not null && input.EndTime is not null, x => x.CreationTime >= input.StartTime && x.CreationTime <= input.EndTime) + + //这个为过滤当前部门,加入数据权限后,将由数据权限控制 + .WhereIF(input.DeptId is not null, x => x.DeptId == input.DeptId) + + .WhereIF(ids is not null, x => ids.Contains(x.Id)) + + + .LeftJoin((user, dept) => user.DeptId == dept.Id) + .Select((user, dept) => new UserGetListOutputDto(), true) + .ToPageListAsync(input.PageNum, input.PageSize, total); + + var result = new PagedResultDto(); + result.Items = outPut; + result.Total = total; + return result; + } + + /// + /// 添加用户 + /// + /// + /// + /// + [OperLog("添加用户", OperEnum.Insert)] + public async override Task CreateAsync(UserCreateInputVo input) + { + if (string.IsNullOrEmpty(input.Password)) + { + throw new UserFriendlyException(UserConst.添加失败_密码为空); + } + if (await _repository.IsAnyAsync(u => input.UserName.Equals(u.UserName))) + { + throw new UserFriendlyException(UserConst.添加失败_用户存在); + } + var entities = await MapToEntityAsync(input); + + entities.BuildPassword(); + + using (var uow = _unitOfWorkManager.CreateContext()) + { + var returnEntity = await _repository.InsertReturnEntityAsync(entities); + await _userManager.GiveUserSetRoleAsync(new List { returnEntity.Id }, input.RoleIds); + await _userManager.GiveUserSetPostAsync(new List { returnEntity.Id }, input.PostIds); + uow.Commit(); + + var result = await MapToGetOutputDtoAsync(returnEntity); + return result; + } + } + /// + /// 单查 + /// + /// + /// + public override async Task GetAsync(long id) + { + //使用导航树形查询 + var entity = await _DbQueryable.Includes(u => u.Roles).Includes(u => u.Posts).Includes(u => u.Dept).InSingleAsync(id); + + return await MapToGetOutputDtoAsync(entity); + } + + /// + /// 更新用户 + /// + /// + /// + /// + [OperLog("更新用户", OperEnum.Update)] + public async override Task UpdateAsync(long id, UserUpdateInputVo input) + { + if (await _repository.IsAnyAsync(u => input.UserName!.Equals(u.UserName) && !id.Equals(u.Id))) + { + throw new UserFriendlyException("用户已经在,更新失败"); + } + var entity = await _repository.GetByIdAsync(id); + //更新密码,特殊处理 + if (input.Password is not null) + { + entity.Password = input.Password; + entity.BuildPassword(); + } + await MapToEntityAsync(input, entity); + using (var uow = _unitOfWorkManager.CreateContext()) + { + var res1 = await _repository.UpdateAsync(entity); + await _userManager.GiveUserSetRoleAsync(new List { id }, input.RoleIds); + await _userManager.GiveUserSetPostAsync(new List { id }, input.PostIds); + uow.Commit(); + } + return await MapToGetOutputDtoAsync(entity); + } + + /// + /// 更新个人中心 + /// + /// + /// + [OperLog("更新个人信息", OperEnum.Update)] + public async Task UpdateProfileAsync(ProfileUpdateInputVo input) + { + var entity = await _repository.GetByIdAsync(_currentUser.Id); + _mapper.Map(input, entity); + await _repository.UpdateAsync(entity); + var dto = _mapper.Map(entity); + return dto; + } + + /// + /// 更新状态 + /// + /// + /// + /// + [Route("/api/user/{id}/{state}")] + [OperLog("更新用户状态", OperEnum.Update)] + public async Task UpdateStateAsync([FromRoute] long id, [FromRoute] bool state) + { + var entity = await _repository.GetByIdAsync(id); + if (entity is null) + { + throw new ApplicationException("用户未存在"); + } + + entity.State = state; + await _repository.UpdateAsync(entity); + return await MapToGetOutputDtoAsync(entity); + } + [OperLog("删除用户", OperEnum.Delete)] + public override Task DeleteAsync(string id) + { + return base.DeleteAsync(id); + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/SystemService.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/SystemService.cs deleted file mode 100644 index 4ee3379c..00000000 --- a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/Services/SystemService.cs +++ /dev/null @@ -1,16 +0,0 @@ -using SqlSugar; - -namespace Yi.Furion.Rbac.Application; - -public class SystemService : ISystemService, ITransient -{ - private readonly ISqlSugarClient _sqlSugarClient; - public SystemService(ISqlSugarClient sqlSugarClient) - { - _sqlSugarClient=sqlSugarClient; - } - public string GetDescription() - { - return "让 .NET 开发更简单,更通用,更流行。"; - } -} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/SystemAppService.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/SystemAppService.cs deleted file mode 100644 index 7c527b66..00000000 --- a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/System/SystemAppService.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Yi.Furion.Rbac.Application; - -/// -/// 系统服务接口 -/// -public class SystemAppService : IDynamicApiController -{ - private readonly ISystemService _systemService; - public SystemAppService(ISystemService systemService) - { - _systemService = systemService; - } - - /// - /// 获取系统描述 - /// - /// - public string GetDescription() - { - return _systemService.GetDescription(); - } -} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/Yi.Furion.Rbac.Application.csproj b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/Yi.Furion.Rbac.Application.csproj index 84fca2d5..337d0f8e 100644 --- a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/Yi.Furion.Rbac.Application.csproj +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/Yi.Furion.Rbac.Application.csproj @@ -23,11 +23,12 @@ + - + diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/Yi.Furion.Rbac.Application.xml b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/Yi.Furion.Rbac.Application.xml index f92482e1..028ce291 100644 --- a/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/Yi.Furion.Rbac.Application.xml +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Application/Yi.Furion.Rbac.Application.xml @@ -4,16 +4,5 @@ Yi.Furion.Rbac.Application - - - 系统服务接口 - - - - - 获取系统描述 - - - diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/ConstClasses/DeptConst.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/ConstClasses/DeptConst.cs new file mode 100644 index 00000000..3de7965e --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/ConstClasses/DeptConst.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Core.ConstClasses +{ + /// + /// 常量定义 + /// + + public class DeptConst + { + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/ConstClasses/MenuConst.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/ConstClasses/MenuConst.cs new file mode 100644 index 00000000..f305b444 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/ConstClasses/MenuConst.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Core.ConstClasses +{ + /// + /// 常量定义 + /// + + public class MenuConst + { + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/ConstClasses/PostConst.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/ConstClasses/PostConst.cs new file mode 100644 index 00000000..dbda92ab --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/ConstClasses/PostConst.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Core.ConstClasses +{ + /// + /// 常量定义 + /// + + public class PostConst + { + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/ConstClasses/RoleConst.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/ConstClasses/RoleConst.cs new file mode 100644 index 00000000..ed7c16da --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/ConstClasses/RoleConst.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Core.ConstClasses +{ + /// + /// 常量定义 + /// + + public class RoleConst + { + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/ConstClasses/UserConst.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/ConstClasses/UserConst.cs new file mode 100644 index 00000000..342f9433 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/ConstClasses/UserConst.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Core.ConstClasses +{ + /// + /// 常量定义 + /// + + public class UserConst + { + public const string 登录失败_错误 = "登录失败!用户名或密码错误!"; + public const string 登录失败_不存在 = "登录失败!用户名不存在!"; + public const string 添加失败_密码为空 = "密码为空,添加失败!"; + public const string 添加失败_用户存在 = "用户已经存在,添加失败!"; + public const string 用户无权限分配 = "登录禁用!该用户分配无任何权限,无意义登录!"; + public const string 用户无角色分配 = "登录禁用!该用户分配无任何角色,无意义登录!"; + + public const string Admin = "cc"; + public const string AdminRolesCode = "admin"; + public const string AdminPermissionCode = "*:*:*"; + + public const string GuestRoleCode = "guest"; + public const string CommonRoleName = "common"; + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Dtos/UserRoleMenuDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Dtos/UserRoleMenuDto.cs new file mode 100644 index 00000000..760fe062 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Dtos/UserRoleMenuDto.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using Yi.Furion.Rbac.Core.Entities; + +namespace Yi.Furion.Rbac.Core.Dtos +{ + public class UserRoleMenuDto + { + public UserEntity User { get; set; } = new(); + public HashSet Roles { get; set; } = new(); + public HashSet Menus { get; set; } = new(); + + public List RoleCodes { get; set; } = new(); + public List PermissionCodes { get; set; } = new(); + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Dtos/Vue3RouterDto.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Dtos/Vue3RouterDto.cs new file mode 100644 index 00000000..30232f76 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Dtos/Vue3RouterDto.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using static Yi.Framework.Infrastructure.Helper.TreeHelper; + +namespace Yi.Furion.Rbac.Core.Dtos +{ + public class Vue3RouterDto : ITreeModel + { + public long Id { get; set; } + public long ParentId { get; set; } + public int OrderNum { get; set; } + + public string Name { get; set; } = string.Empty; + public string Path { get; set; } = string.Empty; + public bool Hidden { get; set; } + public string Redirect { get; set; } = string.Empty; + public string Component { get; set; } = string.Empty; + public bool AlwaysShow { get; set; } + public Meta Meta { get; set; } = new Meta(); + public List Children { get; set; } + } + + + public class Meta + { + public string Title { get; set; } = string.Empty; + public string Icon { get; set; } = string.Empty; + public bool NoCache { get; set; } + public string link { get; set; } = string.Empty; + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/DeptEntity.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/DeptEntity.cs new file mode 100644 index 00000000..476af71d --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/DeptEntity.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; +using SqlSugar; +using Yi.Framework.Infrastructure.Data.Auditing; +using Yi.Framework.Infrastructure.Data.Entities; +using Yi.Framework.Infrastructure.Ddd.Entities; + +namespace Yi.Furion.Rbac.Core.Entities +{ + /// + /// 部门表 + /// + [SugarTable("Dept")] + public partial class DeptEntity : IEntity, ISoftDelete, IAuditedObject, IOrderNum, IState + { + /// + /// 主键 + /// + [SugarColumn(IsPrimaryKey = true)] + public long Id { get; set; } + + /// + /// 逻辑删除 + /// + public bool IsDeleted { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreationTime { get; set; } = DateTime.Now; + + /// + /// 创建者 + /// + public long? CreatorId { get; set; } + + /// + /// 最后修改者 + /// + public long? LastModifierId { get; set; } + + /// + /// 最后修改时间 + /// + public DateTime? LastModificationTime { get; set; } + + /// + /// 排序 + /// + public int OrderNum { get; set; } = 0; + + /// + /// 状态 + /// + public bool State { get; set; } = true; + + /// + /// 部门名称 + /// + public string DeptName { get; set; } = string.Empty; + /// + /// 部门编码 + /// + [SugarColumn(ColumnName = "DeptCode")] + public string DeptCode { get; set; } = string.Empty; + /// + /// 负责人 + /// + [SugarColumn(ColumnName = "Leader")] + public string Leader { get; set; } + /// + /// 父级id + /// + [SugarColumn(ColumnName = "ParentId")] + public long ParentId { get; set; } + + /// + /// 描述 + /// + [SugarColumn(ColumnName = "Remark")] + public string Remark { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/LoginLogEntity.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/LoginLogEntity.cs new file mode 100644 index 00000000..0d2f4131 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/LoginLogEntity.cs @@ -0,0 +1,48 @@ +using System; +using SqlSugar; +using Yi.Framework.Infrastructure.Data.Auditing; +using Yi.Framework.Infrastructure.Ddd.Entities; + +namespace Yi.Furion.Rbac.Core.Entities +{ + [SugarTable("LoginLog")] + public class LoginLogEntity : IEntity, ICreationAuditedObject + { + [SugarColumn(ColumnName = "Id", IsPrimaryKey = true)] + public long Id { get; set; } + public DateTime CreationTime { get; set; } + + /// + /// 登录用户 + /// + [SugarColumn(ColumnName = "LoginUser")] + public string LoginUser { get; set; } + /// + /// 登录地点 + /// + [SugarColumn(ColumnName = "LoginLocation")] + public string LoginLocation { get; set; } + /// + /// 登录Ip + /// + [SugarColumn(ColumnName = "LoginIp")] + public string LoginIp { get; set; } + /// + /// 浏览器 + /// + [SugarColumn(ColumnName = "Browser")] + public string Browser { get; set; } + /// + /// 操作系统 + /// + [SugarColumn(ColumnName = "Os")] + public string Os { get; set; } + /// + /// 登录信息 + /// + [SugarColumn(ColumnName = "LogMsg")] + public string LogMsg { get; set; } + + public long? CreatorId { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/MenuEntity.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/MenuEntity.cs new file mode 100644 index 00000000..6178e877 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/MenuEntity.cs @@ -0,0 +1,196 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SqlSugar; +using Yi.Framework.Infrastructure.Data.Auditing; +using Yi.Framework.Infrastructure.Data.Entities; +using Yi.Framework.Infrastructure.Ddd.Entities; +using Yi.Framework.Infrastructure.Helper; +using Yi.Furion.Rbac.Core.Dtos; +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Core.Entities +{ + /// + /// 菜单表 + /// + [SugarTable("Menu")] + public partial class MenuEntity : IEntity, ISoftDelete, IAuditedObject, IOrderNum, IState + { + /// + /// 主键 + /// + [SugarColumn(IsPrimaryKey = true)] + public long Id { get; set; } + + /// + /// 逻辑删除 + /// + public bool IsDeleted { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreationTime { get; set; } = DateTime.Now; + + /// + /// 创建者 + /// + public long? CreatorId { get; set; } + + /// + /// 最后修改者 + /// + public long? LastModifierId { get; set; } + + /// + /// 最后修改时间 + /// + public DateTime? LastModificationTime { get; set; } + + /// + /// 排序 + /// + public int OrderNum { get; set; } = 0; + + /// + /// 状态 + /// + public bool State { get; set; } + + /// + /// 菜单名 + /// + public string MenuName { get; set; } = string.Empty; + /// + /// + /// + [SugarColumn(ColumnName = "MenuType")] + public MenuTypeEnum MenuType { get; set; } = MenuTypeEnum.Menu; + /// + /// + /// + [SugarColumn(ColumnName = "PermissionCode")] + public string PermissionCode { get; set; } + /// + /// + /// + [SugarColumn(ColumnName = "ParentId")] + public long ParentId { get; set; } + + /// + /// 菜单图标 + /// + [SugarColumn(ColumnName = "MenuIcon")] + public string MenuIcon { get; set; } + /// + /// 菜单组件路由 + /// + [SugarColumn(ColumnName = "Router")] + public string Router { get; set; } + /// + /// 是否为外部链接 + /// + [SugarColumn(ColumnName = "IsLink")] + public bool IsLink { get; set; } + /// + /// 是否缓存 + /// + [SugarColumn(ColumnName = "IsCache")] + public bool IsCache { get; set; } + /// + /// 是否显示 + /// + [SugarColumn(ColumnName = "IsShow")] + public bool IsShow { get; set; } = true; + + /// + /// 描述 + /// + [SugarColumn(ColumnName = "Remark")] + public string Remark { get; set; } + /// + /// 组件路径 + /// + [SugarColumn(ColumnName = "Component")] + public string Component { get; set; } + /// + /// 路由参数 + /// + [SugarColumn(ColumnName = "Query")] + public string Query { get; set; } + + [SugarColumn(IsIgnore = true)] + public List Children { get; set; } + } + + /// + /// 实体扩展 + /// + public static class MenuEntityExtensions + { + /// + /// 构建vue3路由 + /// + /// + /// + public static List Vue3RouterBuild(this List menus) + { + menus = menus.Where(m => m.MenuType != MenuTypeEnum.Component).ToList(); + List routers = new(); + foreach (var m in menus) + { + + var r = new Vue3RouterDto(); + r.OrderNum = m.OrderNum; + var routerName = m.Router?.Split("/").LastOrDefault(); + r.Id = m.Id; + r.ParentId = m.ParentId; + + //开头大写 + r.Name = routerName?.First().ToString().ToUpper() + routerName?.Substring(1); + r.Path = m.Router!; + r.Hidden = !m.IsShow; + + + if (m.MenuType == MenuTypeEnum.Catalogue) + { + r.Redirect = "noRedirect"; + r.AlwaysShow = true; + + //判断是否为最顶层的路由 + if (0 == m.ParentId) + { + r.Component = "Layout"; + } + else + { + r.Component = "ParentView"; + } + } + if (m.MenuType == MenuTypeEnum.Menu) + { + r.Redirect = "noRedirect"; + r.AlwaysShow = true; + r.Component = m.Component!; + r.AlwaysShow = false; + } + r.Meta = new Meta + { + Title = m.MenuName!, + Icon = m.MenuIcon!, + NoCache = !m.IsCache + }; + if (m.IsLink) + { + r.Meta.link = m.Router!; + r.AlwaysShow = false; + } + + routers.Add(r); + } + return TreeHelper.SetTree(routers); + + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/PostEntity.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/PostEntity.cs new file mode 100644 index 00000000..12fe278e --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/PostEntity.cs @@ -0,0 +1,74 @@ +using System; +using SqlSugar; +using Yi.Framework.Infrastructure.Data.Auditing; +using Yi.Framework.Infrastructure.Data.Entities; +using Yi.Framework.Infrastructure.Ddd.Entities; + +namespace Yi.Furion.Rbac.Core.Entities +{ + /// + /// 岗位表 + /// + [SugarTable("Post")] + public partial class PostEntity : IEntity, ISoftDelete, IAuditedObject, IOrderNum, IState + { + + /// + /// 主键 + /// + [SugarColumn(IsPrimaryKey = true)] + public long Id { get; set; } + + /// + /// 逻辑删除 + /// + public bool IsDeleted { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreationTime { get; set; } = DateTime.Now; + + /// + /// 创建者 + /// + public long? CreatorId { get; set; } + + /// + /// 最后修改者 + /// + public long? LastModifierId { get; set; } + + /// + /// 最后修改时间 + /// + public DateTime? LastModificationTime { get; set; } + + /// + /// 排序 + /// + public int OrderNum { get; set; } = 0; + + /// + /// 状态 + /// + public bool State { get; set; } = true; + + /// + /// 岗位编码 + /// + [SugarColumn(ColumnName = "PostCode")] + public string PostCode { get; set; } = string.Empty; + /// + /// 岗位名称 + /// + [SugarColumn(ColumnName = "PostName")] + public string PostName { get; set; } = string.Empty; + + /// + /// 描述 + /// + [SugarColumn(ColumnName = "Remark")] + public string Remark { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/RoleDeptEntity.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/RoleDeptEntity.cs new file mode 100644 index 00000000..c0c91c08 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/RoleDeptEntity.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; +using SqlSugar; +using Yi.Framework.Infrastructure.Ddd.Entities; + +namespace Yi.Furion.Rbac.Core.Entities; + +/// +/// 角色部门关系表 +/// +[SugarTable("RoleDept")] +public partial class RoleDeptEntity : IEntity +{ + /// + /// 主键 + /// + [SugarColumn(IsPrimaryKey = true)] + public long Id { get; set; } + + /// + /// 角色id + /// + [SugarColumn(ColumnName = "RoleId")] + public long? RoleId { get; set; } + /// + /// 部门id + /// + [SugarColumn(ColumnName = "DeptId")] + public long? DeptId { get; set; } + + +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/RoleEntity.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/RoleEntity.cs new file mode 100644 index 00000000..3f720ccf --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/RoleEntity.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using SqlSugar; +using Yi.Framework.Infrastructure.Data.Auditing; +using Yi.Framework.Infrastructure.Data.Entities; +using Yi.Framework.Infrastructure.Ddd.Entities; +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Core.Entities +{ + /// + /// 角色表 + /// + [SugarTable("Role")] + public class RoleEntity : IEntity, ISoftDelete, IAuditedObject, IOrderNum, IState + { + /// + /// 主键 + /// + [SugarColumn(IsPrimaryKey = true)] + public long Id { get; set; } + + /// + /// 逻辑删除 + /// + public bool IsDeleted { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreationTime { get; set; } = DateTime.Now; + + /// + /// 创建者 + /// + public long? CreatorId { get; set; } + + /// + /// 最后修改者 + /// + public long? LastModifierId { get; set; } + + /// + /// 最后修改时间 + /// + public DateTime? LastModificationTime { get; set; } + + /// + /// 排序 + /// + public int OrderNum { get; set; } = 0; + + + /// + /// 角色名 + /// + public string RoleName { get; set; } = string.Empty; + + /// + /// 角色编码 + /// + [SugarColumn(ColumnName = "RoleCode")] + public string RoleCode { get; set; } = string.Empty; + + /// + /// 描述 + /// + [SugarColumn(ColumnName = "Remark")] + public string Remark { get; set; } + /// + /// 角色数据范围 + /// + [SugarColumn(ColumnName = "DataScope")] + public DataScopeEnum DataScope { get; set; } = DataScopeEnum.ALL; + + /// + /// 状态 + /// + public bool State { get; set; } = true; + + + [Navigate(typeof(RoleMenuEntity), nameof(RoleMenuEntity.RoleId), nameof(RoleMenuEntity.MenuId))] + public List Menus { get; set; } + + [Navigate(typeof(RoleDeptEntity), nameof(RoleDeptEntity.RoleId), nameof(RoleDeptEntity.DeptId))] + public List Depts { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/RoleMenuEntity.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/RoleMenuEntity.cs new file mode 100644 index 00000000..3d54087e --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/RoleMenuEntity.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; +using SqlSugar; +using Yi.Framework.Infrastructure.Ddd.Entities; + +namespace Yi.Furion.Rbac.Core.Entities; +/// +/// 角色菜单关系表 +/// +[SugarTable("RoleMenu")] +public partial class RoleMenuEntity : IEntity + +{ + /// + /// 主键 + /// + [SugarColumn(IsPrimaryKey = true)] + public long Id { get; set; } + /// + /// + /// + [SugarColumn(ColumnName = "RoleId")] + public long RoleId { get; set; } + /// + /// + /// + [SugarColumn(ColumnName = "MenuId")] + public long MenuId { get; set; } + +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/UserEntity.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/UserEntity.cs new file mode 100644 index 00000000..4a6fd793 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/UserEntity.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using SqlSugar; +using Yi.Framework.Infrastructure.Data.Auditing; +using Yi.Framework.Infrastructure.Data.Entities; +using Yi.Framework.Infrastructure.Ddd.Entities; +using Yi.Framework.Infrastructure.Helper; +using Yi.Furion.Rbac.Core.EnumClasses; + +namespace Yi.Furion.Rbac.Core.Entities +{ + /// + /// 用户表 + /// + [SugarTable("User")] + public class UserEntity : IEntity, ISoftDelete, IAuditedObject, IOrderNum, IState + { + public UserEntity() + { + + } + public UserEntity(string userName, string password, long phone, string nick = "萌新") + { + Id = SnowflakeHelper.NextId; + UserName = userName; + Password = password; + Phone = phone; + Nick = nick; + BuildPassword(); + } + + /// + /// 主键 + /// + [SugarColumn(IsPrimaryKey = true)] + public long Id { get; set; } + + /// + /// 逻辑删除 + /// + public bool IsDeleted { get; set; } + + /// + /// 姓名 + /// + public string Name { get; set; } + + /// + /// 年龄 + /// + public int? Age { get; set; } + + /// + /// 用户名 + /// + public string UserName { get; set; } = string.Empty; + + /// + /// 密码 + /// + public string Password { get; set; } = string.Empty; + + /// + /// 加密盐值 + /// + public string Salt { get; set; } = string.Empty; + + /// + /// 头像 + /// + public string Icon { get; set; } + + /// + /// 昵称 + /// + public string Nick { get; set; } + + /// + /// 邮箱 + /// + public string Email { get; set; } + + /// + /// Ip + /// + public string Ip { get; set; } + + /// + /// 地址 + /// + + public string Address { get; set; } + + /// + /// 电话 + /// + public long? Phone { get; set; } + + /// + /// 简介 + /// + public string Introduction { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 性别 + /// + public SexEnum Sex { get; set; } = SexEnum.Unknown; + + /// + /// 部门id + /// + public long? DeptId { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreationTime { get; set; } = DateTime.Now; + + /// + /// 创建者 + /// + public long? CreatorId { get; set; } + + /// + /// 最后修改者 + /// + public long? LastModifierId { get; set; } + + /// + /// 最后修改时间 + /// + public DateTime? LastModificationTime { get; set; } + + /// + /// 排序 + /// + public int OrderNum { get; set; } = 0; + + + /// + /// 状态 + /// + public bool State { get; set; } = true; + + + /// + /// 角色 + /// + [Navigate(typeof(UserRoleEntity), nameof(UserRoleEntity.UserId), nameof(UserRoleEntity.RoleId))] + public List Roles { get; set; } + + /// + /// 岗位 + /// + + [Navigate(typeof(UserPostEntity), nameof(UserPostEntity.UserId), nameof(UserPostEntity.PostId))] + public List Posts { get; set; } + + /// + /// 部门 + /// + + [Navigate(NavigateType.OneToOne, nameof(DeptId))] + public DeptEntity Dept { get; set; } + + /// + /// 构建密码,MD5盐值加密 + /// + public UserEntity BuildPassword(string password = null) + { + //如果不传值,那就把自己的password当作传进来的password + if (password == null) + { + if (Password == null) + { + throw new ArgumentNullException(nameof(Password)); + } + password = Password; + } + Salt = MD5Helper.GenerateSalt(); + Password = MD5Helper.SHA2Encode(password, Salt); + return this; + } + + /// + /// 判断密码和加密后的密码是否相同 + /// + /// + /// + public bool JudgePassword(string password) + { + if (Salt is null) + { + throw new ArgumentNullException(Salt); + } + var p = MD5Helper.SHA2Encode(password, Salt); + if (Password == MD5Helper.SHA2Encode(password, Salt)) + { + return true; + } + return false; + } + } + + +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/UserPostEntity.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/UserPostEntity.cs new file mode 100644 index 00000000..824ef6e0 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/UserPostEntity.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; +using SqlSugar; +using Yi.Framework.Infrastructure.Ddd.Entities; + +namespace Yi.Furion.Rbac.Core.Entities; +/// +/// 用户岗位表 +/// +[SugarTable("UserPost")] +public partial class UserPostEntity : IEntity +{ + /// + /// 主键 + /// + [SugarColumn(IsPrimaryKey = true)] + public long Id { get; set; } + /// + /// 用户id + /// + [SugarColumn(ColumnName = "UserId")] + public long UserId { get; set; } + /// + /// 岗位id + /// + [SugarColumn(ColumnName = "PostId")] + public long PostId { get; set; } + +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/UserRoleEntity.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/UserRoleEntity.cs new file mode 100644 index 00000000..61268da6 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Entities/UserRoleEntity.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using SqlSugar; +using Yi.Framework.Infrastructure.Ddd.Entities; + +namespace Yi.Furion.Rbac.Core.Entities +{ + /// + /// 用户角色关系表 + /// + [SugarTable("UserRole")] + public partial class UserRoleEntity : IEntity + { + /// + /// 主键 + /// + [SugarColumn(IsPrimaryKey = true)] + public long Id { get; set; } + + /// + /// 角色id + /// + public long RoleId { get; set; } + + /// + /// 用户id + /// + public long UserId { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/EnumClasses/DataScopeEnum.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/EnumClasses/DataScopeEnum.cs new file mode 100644 index 00000000..177ac330 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/EnumClasses/DataScopeEnum.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Core.EnumClasses +{ + public enum DataScopeEnum + { + ALL = 0, + CUSTOM = 1, + DEPT = 2, + DEPT_FOLLOW = 3, + USER = 4 + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/EnumClasses/MenuTypeEnum.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/EnumClasses/MenuTypeEnum.cs new file mode 100644 index 00000000..4dc05504 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/EnumClasses/MenuTypeEnum.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Core.EnumClasses +{ + public enum MenuTypeEnum + { + Catalogue = 0, //目录 + Menu = 1, //菜单 + Component = 2//组件 + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/EnumClasses/SexEnum.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/EnumClasses/SexEnum.cs new file mode 100644 index 00000000..cb751853 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/EnumClasses/SexEnum.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Yi.Furion.Rbac.Core.EnumClasses +{ + /// + /// 性别 + /// + public enum SexEnum + { + /// + /// 男性 + /// + Male = 0, + /// + /// 女性 + /// + Woman = 1, + /// + /// 未知 + /// + Unknown = 2 + + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Etos/LoginEventSource.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Etos/LoginEventSource.cs new file mode 100644 index 00000000..388e487c --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Etos/LoginEventSource.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Furion.EventBus; + +namespace Yi.Furion.Rbac.Core.Etos +{ + public class LoginEventSource : IEventSource + { + public LoginEventSource(LoginEventArgs payload) + { Payload = payload; } + public string EventId => nameof(LoginEventSource); + public DateTime CreatedTime { get; set; } = DateTime.UtcNow; + + [Newtonsoft.Json.JsonIgnore] + [System.Text.Json.Serialization.JsonIgnore] + public CancellationToken CancellationToken { get; set; } + + + public object Payload { get; set; } + } + + public class LoginEventArgs + { + public long UserId { get; set; } + public string UserName { get; set; } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Yi.Furion.Rbac.Core.csproj b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Yi.Furion.Rbac.Core.csproj index 2eae7e5f..c03b3a97 100644 --- a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Yi.Furion.Rbac.Core.csproj +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Yi.Furion.Rbac.Core.csproj @@ -13,11 +13,18 @@ + + - - - - + + + + + + + + + diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Yi.Furion.Rbac.Core.xml b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Yi.Furion.Rbac.Core.xml index 53e58dac..d92cca82 100644 --- a/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Yi.Furion.Rbac.Core.xml +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Core/Yi.Furion.Rbac.Core.xml @@ -4,5 +4,619 @@ Yi.Furion.Rbac.Core + + + 常量定义 + + + + + 常量定义 + + + + + 常量定义 + + + + + 常量定义 + + + + + 常量定义 + + + + + 部门表 + + + + + 主键 + + + + + 逻辑删除 + + + + + 创建时间 + + + + + 创建者 + + + + + 最后修改者 + + + + + 最后修改时间 + + + + + 排序 + + + + + 状态 + + + + + 部门名称 + + + + + 部门编码 + + + + + 负责人 + + + + + 父级id + + + + + 描述 + + + + + 登录用户 + + + + + 登录地点 + + + + + 登录Ip + + + + + 浏览器 + + + + + 操作系统 + + + + + 登录信息 + + + + + 菜单表 + + + + + 主键 + + + + + 逻辑删除 + + + + + 创建时间 + + + + + 创建者 + + + + + 最后修改者 + + + + + 最后修改时间 + + + + + 排序 + + + + + 状态 + + + + + 菜单名 + + + + + + + + + + + + + + + + + + + + 菜单图标 + + + + + 菜单组件路由 + + + + + 是否为外部链接 + + + + + 是否缓存 + + + + + 是否显示 + + + + + 描述 + + + + + 组件路径 + + + + + 路由参数 + + + + + 实体扩展 + + + + + 构建vue3路由 + + + + + + + 岗位表 + + + + + 主键 + + + + + 逻辑删除 + + + + + 创建时间 + + + + + 创建者 + + + + + 最后修改者 + + + + + 最后修改时间 + + + + + 排序 + + + + + 状态 + + + + + 岗位编码 + + + + + 岗位名称 + + + + + 描述 + + + + + 角色部门关系表 + + + + + 主键 + + + + + 角色id + + + + + 部门id + + + + + 角色表 + + + + + 主键 + + + + + 逻辑删除 + + + + + 创建时间 + + + + + 创建者 + + + + + 最后修改者 + + + + + 最后修改时间 + + + + + 排序 + + + + + 角色名 + + + + + 角色编码 + + + + + 描述 + + + + + 角色数据范围 + + + + + 状态 + + + + + 角色菜单关系表 + + + + + 主键 + + + + + + + + + + + + + + + 用户表 + + + + + 主键 + + + + + 逻辑删除 + + + + + 姓名 + + + + + 年龄 + + + + + 用户名 + + + + + 密码 + + + + + 加密盐值 + + + + + 头像 + + + + + 昵称 + + + + + 邮箱 + + + + + Ip + + + + + 地址 + + + + + 电话 + + + + + 简介 + + + + + 备注 + + + + + 性别 + + + + + 部门id + + + + + 创建时间 + + + + + 创建者 + + + + + 最后修改者 + + + + + 最后修改时间 + + + + + 排序 + + + + + 状态 + + + + + 角色 + + + + + 岗位 + + + + + 部门 + + + + + 构建密码,MD5盐值加密 + + + + + 判断密码和加密后的密码是否相同 + + + + + + + 用户岗位表 + + + + + 主键 + + + + + 用户id + + + + + 岗位id + + + + + 用户角色关系表 + + + + + 主键 + + + + + 角色id + + + + + 用户id + + + + + 性别 + + + + + 男性 + + + + + 女性 + + + + + 未知 + + diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.EntityFramework.Core/Repositories/IUserRepository.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.EntityFramework.Core/Repositories/IUserRepository.cs new file mode 100644 index 00000000..fab6ae56 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.EntityFramework.Core/Repositories/IUserRepository.cs @@ -0,0 +1,18 @@ +using System.Threading.Tasks; +using Yi.Framework.Infrastructure.Ddd.Repositories; +using Yi.Furion.Rbac.Core.Dtos; +using Yi.Furion.Rbac.Core.Entities; + +namespace Yi.Furion.Rbac.Sqlsugar.Core.Repositories +{ + public interface IUserRepository : IRepository + { + /// + /// 获取当前登录用户的所有信息 + /// + /// + /// + Task GetUserAllInfoAsync(long userId); + + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.EntityFramework.Core/Repositories/Impl/UserRepository.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.EntityFramework.Core/Repositories/Impl/UserRepository.cs new file mode 100644 index 00000000..37535e60 --- /dev/null +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.EntityFramework.Core/Repositories/Impl/UserRepository.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Furion.DependencyInjection; +using SqlSugar; +using Yi.Framework.Infrastructure.Sqlsugar.Repositories; +using Yi.Furion.Rbac.Core.ConstClasses; +using Yi.Furion.Rbac.Core.Dtos; +using Yi.Furion.Rbac.Core.Entities; + +namespace Yi.Furion.Rbac.Sqlsugar.Core.Repositories.Impl +{ + public class UserRepository : SqlsugarRepository, IUserRepository, ITransient + { + public UserRepository(ISqlSugarClient context) : base(context) + { + } + + + + /// + /// 获取用户id的全部信息 + /// + /// + /// + /// + public async Task GetUserAllInfoAsync(long userId) + { + var userRoleMenu = new UserRoleMenuDto(); + //首先获取到该用户全部信息,导航到角色、菜单,(菜单需要去重,完全交给Set来处理即可) + + //得到用户 + var user = await _DbQueryable.Includes(u => u.Roles.Where(r => r.IsDeleted == false).ToList(), r => r.Menus.Where(m => m.IsDeleted == false).ToList()).InSingleAsync(userId); + if (user is null) + { + throw new ArgumentNullException(nameof(userId)); + } + user.Password = string.Empty; + user.Salt = string.Empty; + + //超级管理员特殊处理 + if (UserConst.Admin.Equals(user.UserName)) + { + userRoleMenu.User = user; + userRoleMenu.RoleCodes.Add(UserConst.AdminRolesCode); + userRoleMenu.PermissionCodes.Add(UserConst.AdminPermissionCode); + return userRoleMenu; + } + + //得到角色集合 + var roleList = user.Roles; + + //得到菜单集合 + foreach (var role in roleList) + { + userRoleMenu.RoleCodes.Add(role.RoleCode); + + if (role.Menus is not null) + { + foreach (var menu in role.Menus) + { + if (!string.IsNullOrEmpty(menu.PermissionCode)) + { + userRoleMenu.PermissionCodes.Add(menu.PermissionCode); + } + userRoleMenu.Menus.Add(menu); + } + } + + //刚好可以去除一下多余的导航属性 + role.Menus = new List(); + userRoleMenu.Roles.Add(role); + } + + user.Roles = new List(); + userRoleMenu.User = user; + + return userRoleMenu; + } + } +} diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.EntityFramework.Core/Startup.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.EntityFramework.Core/Startup.cs index 1e3db32d..36fe5822 100644 --- a/Yi.Furion.Rbac/Yi.Furion.Rbac.EntityFramework.Core/Startup.cs +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.EntityFramework.Core/Startup.cs @@ -1,7 +1,7 @@ using Furion; using Microsoft.Extensions.DependencyInjection; -namespace Yi.Furion.Rbac.EntityFramework.Core; +namespace Yi.Furion.Rbac.Sqlsugar.Core; public class Startup : AppStartup { diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Web.Core/Handlers/JwtHandler.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Web.Core/Handlers/JwtHandler.cs index 4ead6020..c4730e56 100644 --- a/Yi.Furion.Rbac/Yi.Furion.Rbac.Web.Core/Handlers/JwtHandler.cs +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Web.Core/Handlers/JwtHandler.cs @@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using System.Threading.Tasks; -namespace Yi.Furion.Rbac.Web.Core; +namespace Yi.Furion.Rbac.Web.Core.Handlers; public class JwtHandler : AppAuthorizeHandler { diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Web.Core/Startup.cs b/Yi.Furion.Rbac/Yi.Furion.Rbac.Web.Core/Startup.cs index 4cf10011..24d6ab93 100644 --- a/Yi.Furion.Rbac/Yi.Furion.Rbac.Web.Core/Startup.cs +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.Web.Core/Startup.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Yi.Furion.Rbac.Web.Core.Handlers; namespace Yi.Furion.Rbac.Web.Core; @@ -17,6 +18,8 @@ public class Startup : AppStartup services.AddControllers() .AddInjectWithUnifyResult(); + + services.AddEventBus(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.Web.Entry/yi-sqlsugar-dev.db b/Yi.Furion.Rbac/Yi.Furion.Rbac.Web.Entry/yi-sqlsugar-dev.db new file mode 100644 index 00000000..e69de29b diff --git a/Yi.Furion.Rbac/Yi.Furion.Rbac.sln b/Yi.Furion.Rbac/Yi.Furion.Rbac.sln index 421bbde2..e8b3a527 100644 --- a/Yi.Furion.Rbac/Yi.Furion.Rbac.sln +++ b/Yi.Furion.Rbac/Yi.Furion.Rbac.sln @@ -13,7 +13,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Furion.Rbac.Core", "Yi.F EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Furion.Rbac.Web.Entry", "Yi.Furion.Rbac.Web.Entry\Yi.Furion.Rbac.Web.Entry.csproj", "{C8D99F52-EDC7-411F-8300-6DB14BF59E8C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.Infrastructure", "Yi.Framework.Infrastructure\Yi.Framework.Infrastructure.csproj", "{15F8DEB8-8A78-4DED-B588-4CFB2BCA14C9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Yi.Framework.Infrastructure", "Yi.Framework.Infrastructure\Yi.Framework.Infrastructure.csproj", "{15F8DEB8-8A78-4DED-B588-4CFB2BCA14C9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Yi.Framework.Module", "Yi.Framework.Module\Yi.Framework.Module.csproj", "{C0D8C819-CBAA-47AF-A9CF-EA26B3A3D41E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -45,6 +47,10 @@ Global {15F8DEB8-8A78-4DED-B588-4CFB2BCA14C9}.Debug|Any CPU.Build.0 = Debug|Any CPU {15F8DEB8-8A78-4DED-B588-4CFB2BCA14C9}.Release|Any CPU.ActiveCfg = Release|Any CPU {15F8DEB8-8A78-4DED-B588-4CFB2BCA14C9}.Release|Any CPU.Build.0 = Release|Any CPU + {C0D8C819-CBAA-47AF-A9CF-EA26B3A3D41E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C0D8C819-CBAA-47AF-A9CF-EA26B3A3D41E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C0D8C819-CBAA-47AF-A9CF-EA26B3A3D41E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C0D8C819-CBAA-47AF-A9CF-EA26B3A3D41E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE