diff --git a/Admin.Core.Common/Admin.Core.Common.csproj b/Admin.Core.Common/Admin.Core.Common.csproj index 3a029b93e..c79eacd9d 100644 --- a/Admin.Core.Common/Admin.Core.Common.csproj +++ b/Admin.Core.Common/Admin.Core.Common.csproj @@ -2,21 +2,41 @@ netcoreapp3.1 + true + 1.4.1 + xiaoxue + xiaoxue + 中台Admin后端通用库 + ZhonTai Admin;WebApi + git + https://github.com/zhontai/Admin.Core + https://github.com/zhontai/Admin.Core + MIT + + + + ..\Admin.Core\Admin.Core.Common.xml + 1701;1702;1591 + - - - - - + + + + + + + + - - - - + + + + - + + diff --git a/Admin.Core.Common/Attributes/SingleInstanceAttribute.cs b/Admin.Core.Common/Attributes/SingleInstanceAttribute.cs new file mode 100644 index 000000000..0f67acbba --- /dev/null +++ b/Admin.Core.Common/Attributes/SingleInstanceAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Admin.Core.Common.Attributes +{ + /// + /// 单例注入 + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property)] + public class SingleInstanceAttribute : Attribute + { + } +} diff --git a/Admin.Core.Common/Attributes/TransactionAttribute.cs b/Admin.Core.Common/Attributes/TransactionAttribute.cs index fc6059554..61a1d7d66 100644 --- a/Admin.Core.Common/Attributes/TransactionAttribute.cs +++ b/Admin.Core.Common/Attributes/TransactionAttribute.cs @@ -1,6 +1,8 @@ using System; +using System.Data; +using FreeSql; -namespace Admin.Core.Common +namespace Admin.Core.Common.Attributes { /// /// 启用事物 @@ -8,6 +10,14 @@ namespace Admin.Core.Common [AttributeUsage(AttributeTargets.Method, Inherited = true)] public class TransactionAttribute : Attribute { + /// + /// 事务传播方式 + /// + public Propagation Propagation { get; set; } = Propagation.Requierd; + /// + /// 事务隔离级别 + /// + public IsolationLevel? IsolationLevel { get; set; } } } diff --git a/Admin.Core.Common/Auth/ClaimAttributes.cs b/Admin.Core.Common/Auth/ClaimAttributes.cs new file mode 100644 index 000000000..38f494842 --- /dev/null +++ b/Admin.Core.Common/Auth/ClaimAttributes.cs @@ -0,0 +1,33 @@ +namespace Admin.Core.Common.Auth +{ + /// + /// Claim属性 + /// + public static class ClaimAttributes + { + /// + /// 用户Id + /// + public const string UserId = "id"; + + /// + /// 认证授权用户Id + /// + public const string IdentityServerUserId = "sub"; + + /// + /// 用户名 + /// + public const string UserName = "na"; + + /// + /// 姓名 + /// + public const string UserNickName = "nn"; + + /// + /// 刷新有效期 + /// + public const string RefreshExpires = "re"; + } +} diff --git a/Admin.Core.Common/Auth/IUser.cs b/Admin.Core.Common/Auth/IUser.cs index 1292f0d77..c86757ec5 100644 --- a/Admin.Core.Common/Auth/IUser.cs +++ b/Admin.Core.Common/Auth/IUser.cs @@ -1,14 +1,23 @@ -using System.Collections.Generic; -using System.Security.Claims; - -namespace Admin.Core.Common.Auth +namespace Admin.Core.Common.Auth { + /// + /// 用户信息接口 + /// public interface IUser { + /// + /// 主键 + /// long Id { get; } + + /// + /// 用户名 + /// string Name { get; } - bool IsAuthenticated(); - IEnumerable GetClaimsIdentity(); - List GetClaimValueByType(string ClaimType); + + /// + /// 昵称 + /// + string NickName { get; } } } diff --git a/Admin.Core.Common/Auth/IUserToken.cs b/Admin.Core.Common/Auth/IUserToken.cs index f62c326db..6af13ec4b 100644 --- a/Admin.Core.Common/Auth/IUserToken.cs +++ b/Admin.Core.Common/Auth/IUserToken.cs @@ -4,6 +4,8 @@ namespace Admin.Core.Common.Auth { public interface IUserToken { - string Build(Claim[] claims); + string Create(Claim[] claims); + + Claim[] Decode(string jwtToken); } } diff --git a/Admin.Core.Common/Auth/User.cs b/Admin.Core.Common/Auth/User.cs index 553fb8426..989db75b3 100644 --- a/Admin.Core.Common/Auth/User.cs +++ b/Admin.Core.Common/Auth/User.cs @@ -1,7 +1,5 @@ -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http; +using Admin.Core.Common.Helpers; namespace Admin.Core.Common.Auth { @@ -20,7 +18,7 @@ public User(IHttpContextAccessor accessor) /// /// 用户Id /// - public long Id + public virtual long Id { get { @@ -51,82 +49,22 @@ public string Name } } - - /// - /// 用户IP - /// - public string IP - { - get - { - if (_accessor?.HttpContext?.Connection == null) - return ""; - - return _accessor.HttpContext.Connection.RemoteIpAddress.ToString(); - } - } - /// - /// 用户IPv4 + /// 昵称 /// - public string IPv4 + public string NickName { get { - if (_accessor?.HttpContext?.Connection == null) - return ""; + var name = _accessor?.HttpContext?.User?.FindFirst(ClaimAttributes.UserNickName); - return _accessor.HttpContext.Connection.RemoteIpAddress.MapToIPv4().ToString(); - } - } - - /// - /// 用户IPv6 - /// - public string IPv6 - { - get - { - if (_accessor?.HttpContext?.Connection == null) - return ""; + if (name != null && name.Value.NotNull()) + { + return name.Value; + } - return _accessor.HttpContext.Connection.RemoteIpAddress.MapToIPv6().ToString(); + return ""; } } - - public bool IsAuthenticated() - { - return _accessor.HttpContext.User.Identity.IsAuthenticated; - } - - public IEnumerable GetClaimsIdentity() - { - return _accessor.HttpContext.User.Claims; - } - - public List GetClaimValueByType(string ClaimType) - { - - return (from item in GetClaimsIdentity() - where item.Type == ClaimType - select item.Value).ToList(); - - } - } - - /// - /// Claim属性 - /// - public static class ClaimAttributes - { - /// - /// 用户Id - /// - public const string UserId = "id"; - - /// - /// 用户名 - /// - public const string UserName = "na"; } } diff --git a/Admin.Core.Common/Auth/UserIdentiyServer.cs b/Admin.Core.Common/Auth/UserIdentiyServer.cs new file mode 100644 index 000000000..400cd0e9f --- /dev/null +++ b/Admin.Core.Common/Auth/UserIdentiyServer.cs @@ -0,0 +1,34 @@ +using Microsoft.AspNetCore.Http; +using Admin.Core.Common.Helpers; + +namespace Admin.Core.Common.Auth +{ + /// + /// 用户信息 + /// + public class UserIdentiyServer : User + { + private readonly IHttpContextAccessor _accessor; + + public UserIdentiyServer(IHttpContextAccessor accessor) : base(accessor) + { + _accessor = accessor; + } + + /// + /// 用户Id + /// + public override long Id + { + get + { + var id = _accessor?.HttpContext?.User?.FindFirst(ClaimAttributes.IdentityServerUserId); + if (id != null && id.Value.NotNull()) + { + return id.Value.ToLong(); + } + return 0; + } + } + } +} diff --git a/Admin.Core.Common/Auth/UserToken.cs b/Admin.Core.Common/Auth/UserToken.cs index 480e684f5..24d0dfcdc 100644 --- a/Admin.Core.Common/Auth/UserToken.cs +++ b/Admin.Core.Common/Auth/UserToken.cs @@ -5,9 +5,13 @@ using System.IdentityModel.Tokens.Jwt; using Microsoft.IdentityModel.Tokens; using Admin.Core.Common.Configs; +using Admin.Core.Common.Attributes; +using System.Linq; +using Admin.Core.Common.Extensions; namespace Admin.Core.Common.Auth { + [SingleInstance] public class UserToken : IUserToken { private readonly JwtConfig _jwtConfig; @@ -17,10 +21,12 @@ public UserToken(JwtConfig jwtConfig) _jwtConfig = jwtConfig; } - public string Build(Claim[] claims) + public string Create(Claim[] claims) { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtConfig.SecurityKey)); var signingCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + var timestamp = DateTime.Now.AddMinutes(_jwtConfig.Expires + _jwtConfig.RefreshExpires).ToTimestamp().ToString(); + claims = claims.Append(new Claim(ClaimAttributes.RefreshExpires, timestamp)).ToArray(); var token = new JwtSecurityToken( issuer: _jwtConfig.Issuer, @@ -32,5 +38,12 @@ public string Build(Claim[] claims) ); return new JwtSecurityTokenHandler().WriteToken(token); } + + public Claim[] Decode(string jwtToken) + { + var jwtSecurityTokenHandler = new JwtSecurityTokenHandler(); + var jwtSecurityToken = jwtSecurityTokenHandler.ReadJwtToken(jwtToken); + return jwtSecurityToken?.Claims?.ToArray(); + } } } diff --git a/Admin.Core.Model/Base/Entity.cs b/Admin.Core.Common/BaseModel/Entity.cs similarity index 62% rename from Admin.Core.Model/Base/Entity.cs rename to Admin.Core.Common/BaseModel/Entity.cs index a0d0c9ecb..0961d1e87 100644 --- a/Admin.Core.Model/Base/Entity.cs +++ b/Admin.Core.Common/BaseModel/Entity.cs @@ -1,6 +1,7 @@ using FreeSql.DataAnnotations; +using System.ComponentModel; -namespace Admin.Core.Model +namespace Admin.Core.Common.BaseModel { public interface IEntity { @@ -9,9 +10,10 @@ public interface IEntity public class Entity : IEntity { /// - /// 主键Id + /// 编号 /// - [Column(Position = 1,IsIdentity = true)] + [Description("编号")] + [Column(Position = 1, IsIdentity = true)] public virtual TKey Id { get; set; } } diff --git a/Admin.Core.Model/Base/EntityAdd.cs b/Admin.Core.Common/BaseModel/EntityAdd.cs similarity index 65% rename from Admin.Core.Model/Base/EntityAdd.cs rename to Admin.Core.Common/BaseModel/EntityAdd.cs index 5c1c38575..49a4d026b 100644 --- a/Admin.Core.Model/Base/EntityAdd.cs +++ b/Admin.Core.Common/BaseModel/EntityAdd.cs @@ -1,29 +1,33 @@ -using FreeSql.DataAnnotations; -using System; +using System; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; +using FreeSql.DataAnnotations; -namespace Admin.Core.Model +namespace Admin.Core.Common.BaseModel { /// /// 实体创建审计 /// - public class EntityAdd : Entity, IEntityAdd + public class EntityAdd : Entity, IEntityAdd where TKey : struct { /// /// 创建者Id /// + [Description("创建者Id")] [Column(Position = -3, CanUpdate = false)] - public long? CreatedUserId { get; set; } + public TKey? CreatedUserId { get; set; } /// /// 创建者 /// + [Description("创建者")] [Column(Position = -2, CanUpdate = false), MaxLength(50)] public string CreatedUserName { get; set; } /// /// 创建时间 /// + [Description("创建时间")] [Column(Position = -1, CanUpdate = false, ServerTime = DateTimeKind.Local)] public DateTime? CreatedTime { get; set; } } diff --git a/Admin.Core.Model/Base/EntityBase.cs b/Admin.Core.Common/BaseModel/EntityBase.cs similarity index 63% rename from Admin.Core.Model/Base/EntityBase.cs rename to Admin.Core.Common/BaseModel/EntityBase.cs index 6e5849aeb..806a4750f 100644 --- a/Admin.Core.Model/Base/EntityBase.cs +++ b/Admin.Core.Common/BaseModel/EntityBase.cs @@ -1,60 +1,69 @@ -using FreeSql.DataAnnotations; -using System; +using System; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; +using FreeSql.DataAnnotations; -namespace Admin.Core.Model +namespace Admin.Core.Common.BaseModel { /// /// 实体审计 /// - public class EntityBase : Entity, IEntityVersion, IEntitySoftDelete,IEntityAdd,IEntityUpdate + public class EntityBase : Entity, IEntityVersion, IEntitySoftDelete, IEntityAdd, IEntityUpdate where TKey : struct { /// /// 版本 /// + [Description("版本")] [Column(Position = -9, IsVersion = true)] public long Version { get; set; } /// /// 是否删除 /// + [Description("是否删除")] [Column(Position = -8)] public bool IsDeleted { get; set; } = false; /// /// 创建者Id /// + [Description("创建者Id")] [Column(Position = -7, CanUpdate = false)] - public long? CreatedUserId { get; set; } + public TKey? CreatedUserId { get; set; } /// /// 创建者 /// - [Column(Position = -6, CanUpdate = false),MaxLength(50)] + [Description("创建者")] + [Column(Position = -6, CanUpdate = false), MaxLength(50)] public string CreatedUserName { get; set; } /// /// 创建时间 /// + [Description("创建时间")] [Column(Position = -5, CanUpdate = false, ServerTime = DateTimeKind.Local)] public DateTime? CreatedTime { get; set; } /// /// 修改者Id /// + [Description("修改者Id")] [Column(Position = -4, CanInsert = false)] - public long? ModifiedUserId { get; set; } + public TKey? ModifiedUserId { get; set; } /// /// 修改者 /// - [Column(Position = -2, CanInsert = false),MaxLength(50)] + [Description("修改者")] + [Column(Position = -2, CanInsert = false), MaxLength(50)] public string ModifiedUserName { get; set; } /// /// 修改时间 /// - [Column(Position = -1, CanInsert = false,ServerTime = DateTimeKind.Local)] + [Description("修改时间")] + [Column(Position = -1, CanInsert = false, ServerTime = DateTimeKind.Local)] public DateTime? ModifiedTime { get; set; } } diff --git a/Admin.Core.Model/Base/EntitySoftDelete.cs b/Admin.Core.Common/BaseModel/EntitySoftDelete.cs similarity index 79% rename from Admin.Core.Model/Base/EntitySoftDelete.cs rename to Admin.Core.Common/BaseModel/EntitySoftDelete.cs index eb18f5f28..3c7f2527c 100644 --- a/Admin.Core.Model/Base/EntitySoftDelete.cs +++ b/Admin.Core.Common/BaseModel/EntitySoftDelete.cs @@ -1,6 +1,7 @@ using FreeSql.DataAnnotations; +using System.ComponentModel; -namespace Admin.Core.Model +namespace Admin.Core.Common.BaseModel { /// /// 实体软删除 @@ -10,6 +11,7 @@ public class EntitySoftDelete : Entity,IEntitySoftDelete /// /// 是否删除 /// + [Description("是否删除")] [Column(Position = -1)] public bool IsDeleted { get; set; } = false; } diff --git a/Admin.Core.Model/Base/EntityUpdate.cs b/Admin.Core.Common/BaseModel/EntityUpdate.cs similarity index 63% rename from Admin.Core.Model/Base/EntityUpdate.cs rename to Admin.Core.Common/BaseModel/EntityUpdate.cs index cf2072cfd..fa631dcd2 100644 --- a/Admin.Core.Model/Base/EntityUpdate.cs +++ b/Admin.Core.Common/BaseModel/EntityUpdate.cs @@ -1,30 +1,34 @@ -using FreeSql.DataAnnotations; -using System; +using System; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; +using FreeSql.DataAnnotations; -namespace Admin.Core.Model +namespace Admin.Core.Common.BaseModel { /// /// 实体修改审计 /// - public class EntityUpdate : Entity, IEntityUpdate + public class EntityUpdate : Entity, IEntityUpdate where TKey : struct { /// /// 修改者Id /// + [Description("修改者Id")] [Column(Position = -3, CanInsert = false)] - public long? ModifiedUserId { get; set; } + public TKey? ModifiedUserId { get; set; } /// /// 修改者 /// + [Description("修改者")] [Column(Position = -2, CanInsert = false), MaxLength(50)] public string ModifiedUserName { get; set; } /// /// 修改时间 /// - [Column(Position = -1, CanInsert = false,ServerTime = DateTimeKind.Local)] + [Description("修改时间")] + [Column(Position = -1, CanInsert = false, ServerTime = DateTimeKind.Local)] public DateTime? ModifiedTime { get; set; } } diff --git a/Admin.Core.Model/Base/EntityVersion.cs b/Admin.Core.Common/BaseModel/EntityVersion.cs similarity index 80% rename from Admin.Core.Model/Base/EntityVersion.cs rename to Admin.Core.Common/BaseModel/EntityVersion.cs index 95d8ab06b..0b7038da0 100644 --- a/Admin.Core.Model/Base/EntityVersion.cs +++ b/Admin.Core.Common/BaseModel/EntityVersion.cs @@ -1,6 +1,7 @@ using FreeSql.DataAnnotations; +using System.ComponentModel; -namespace Admin.Core.Model +namespace Admin.Core.Common.BaseModel { /// /// 实体版本 @@ -10,6 +11,7 @@ public class EntityVersion : Entity, IEntityVersion /// /// 版本 /// + [Description("版本")] [Column(Position = -1, IsVersion = true)] public long Version { get; set; } } diff --git a/Admin.Core.Common/BaseModel/IEntityAdd.cs b/Admin.Core.Common/BaseModel/IEntityAdd.cs new file mode 100644 index 000000000..7ace39cac --- /dev/null +++ b/Admin.Core.Common/BaseModel/IEntityAdd.cs @@ -0,0 +1,11 @@ +using System; + +namespace Admin.Core.Common.BaseModel +{ + public interface IEntityAdd where TKey: struct + { + TKey? CreatedUserId { get; set; } + string CreatedUserName { get; set; } + DateTime? CreatedTime { get; set; } + } +} diff --git a/Admin.Core.Model/Base/IEntitySoftDelete.cs b/Admin.Core.Common/BaseModel/IEntitySoftDelete.cs similarity index 81% rename from Admin.Core.Model/Base/IEntitySoftDelete.cs rename to Admin.Core.Common/BaseModel/IEntitySoftDelete.cs index f5e58212f..9b93e10df 100644 --- a/Admin.Core.Model/Base/IEntitySoftDelete.cs +++ b/Admin.Core.Common/BaseModel/IEntitySoftDelete.cs @@ -1,5 +1,5 @@  -namespace Admin.Core.Model +namespace Admin.Core.Common.BaseModel { public interface IEntitySoftDelete { diff --git a/Admin.Core.Common/BaseModel/IEntityUpdate.cs b/Admin.Core.Common/BaseModel/IEntityUpdate.cs new file mode 100644 index 000000000..465a90611 --- /dev/null +++ b/Admin.Core.Common/BaseModel/IEntityUpdate.cs @@ -0,0 +1,11 @@ +using System; + +namespace Admin.Core.Common.BaseModel +{ + public interface IEntityUpdate where TKey : struct + { + TKey? ModifiedUserId { get; set; } + string ModifiedUserName { get; set; } + DateTime? ModifiedTime { get; set; } + } +} diff --git a/Admin.Core.Model/Base/IEntityVersion.cs b/Admin.Core.Common/BaseModel/IEntityVersion.cs similarity index 63% rename from Admin.Core.Model/Base/IEntityVersion.cs rename to Admin.Core.Common/BaseModel/IEntityVersion.cs index 0c547eeeb..df9d54397 100644 --- a/Admin.Core.Model/Base/IEntityVersion.cs +++ b/Admin.Core.Common/BaseModel/IEntityVersion.cs @@ -1,7 +1,4 @@ -using FreeSql.DataAnnotations; -using Newtonsoft.Json; - -namespace Admin.Core.Model +namespace Admin.Core.Common.BaseModel { public interface IEntityVersion { diff --git a/Admin.Core.Common/Cache/CacheKey.cs b/Admin.Core.Common/Cache/CacheKey.cs index 06708fed2..5b5d817c9 100644 --- a/Admin.Core.Common/Cache/CacheKey.cs +++ b/Admin.Core.Common/Cache/CacheKey.cs @@ -1,4 +1,6 @@  +using System.ComponentModel; + namespace Admin.Core.Common.Cache { /// @@ -9,16 +11,19 @@ public static class CacheKey /// /// 验证码 admin:verify:code:guid /// + [Description("验证码")] public const string VerifyCodeKey = "admin:verify:code:{0}"; /// - /// 密码 admin:password:guid + /// 密码加密 admin:password:encrypt:guid /// - public const string PassWordKey = "admin:password:{0}"; + [Description("密码加密")] + public const string PassWordEncryptKey = "admin:password:encrypt:{0}"; /// /// 用户权限 admin:user:用户主键:permissions /// + [Description("用户权限")] public const string UserPermissions = "admin:user:{0}:permissions"; } } diff --git a/Admin.Core.Common/Cache/CacheType.cs b/Admin.Core.Common/Cache/CacheType.cs index 70639fbff..afa606d85 100644 --- a/Admin.Core.Common/Cache/CacheType.cs +++ b/Admin.Core.Common/Cache/CacheType.cs @@ -6,7 +6,13 @@ namespace Admin.Core.Common.Cache /// public enum CacheType { + /// + /// 内存缓存 + /// Memory, + /// + /// Redis缓存 + /// Redis } } diff --git a/Admin.Core.Common/Cache/MemoryCache.cs b/Admin.Core.Common/Cache/MemoryCache.cs index 93188bd2a..590db614f 100644 --- a/Admin.Core.Common/Cache/MemoryCache.cs +++ b/Admin.Core.Common/Cache/MemoryCache.cs @@ -7,6 +7,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.Extensions.Caching.Memory; +using Admin.Core.Common.Helpers; namespace Admin.Core.Common.Cache { diff --git a/Admin.Core.Common/Configs/AppConfig.cs b/Admin.Core.Common/Configs/AppConfig.cs index 5313d9930..b923060c4 100644 --- a/Admin.Core.Common/Configs/AppConfig.cs +++ b/Admin.Core.Common/Configs/AppConfig.cs @@ -5,24 +5,97 @@ /// public class AppConfig { + /// + /// Api地址,默认 http://*:8000 + /// + public string[] Urls { get; set; }// = new[]{ "http://*:8000" }; + + /// + /// 跨域地址,默认 http://*:9000 + /// + public string[] CorUrls { get; set; }// = new[]{ "http://*:9000" }; + /// /// Swagger文档 /// - public bool Swagger { get; set; } + public bool Swagger { get; set; } = false; + + /// + /// 统一认证授权服务器 + /// + public IdentityServer IdentityServer { get; set; } = new IdentityServer(); + + /// + /// Aop配置 + /// + public AopConfig Aop { get; set; } = new AopConfig(); /// - /// Api地址,默认 http://*:8081 + /// 日志配置 /// - public string Urls { get; set; } = "http://*:8081"; + public LogConfig Log { get; set; } = new LogConfig(); - public AopConfig Aop { get; set; } + /// + /// 限流 + /// + public bool RateLimit { get; set; } = false; + + /// + /// 验证码配置 + /// + public VarifyCodeConfig VarifyCode { get; set; } = new VarifyCodeConfig(); } + /// + /// 统一认证授权服务器配置 + /// + public class IdentityServer + { + /// + /// 启用 + /// + public bool Enable { get; set; } = false; + /// + /// 地址 + /// + public string Url { get; set; } = "https://localhost:5000"; + } + + /// + /// Aop配置 + /// public class AopConfig { /// /// 事物 /// - public bool Transaction { get; set; } + public bool Transaction { get; set; } = true; + } + + /// + /// 日志配置 + /// + public class LogConfig + { + /// + /// 操作日志 + /// + public bool Operation { get; set; } = true; + } + + /// + /// 验证码配置 + /// + public class VarifyCodeConfig + { + /// + /// 启用 + /// + public bool Enable { get; set; } = true; + + /// + /// 操作日志 + /// + public string[] Fonts { get; set; }// = new[] { "Times New Roman", "Verdana", "Arial", "Gungsuh", "Impact" }; } } diff --git a/Admin.Core.Common/Configs/CacheConfig.cs b/Admin.Core.Common/Configs/CacheConfig.cs index 32c665520..c41f6bacd 100644 --- a/Admin.Core.Common/Configs/CacheConfig.cs +++ b/Admin.Core.Common/Configs/CacheConfig.cs @@ -12,19 +12,32 @@ public class CacheConfig /// /// 缓存类型 /// - public CacheType Type { get; set; } + public CacheType Type { get; set; } = CacheType.Memory; + + /// + /// 限流缓存类型 + /// + public CacheType TypeRateLimit { get; set; } = CacheType.Memory; /// /// Redis配置 /// - public RedisConfig Redis { get; set; } + public RedisConfig Redis { get; set; } = new RedisConfig(); } + /// + /// Redis配置 + /// public class RedisConfig { /// /// 连接字符串 /// - public string ConnectionString { get; set; } + public string ConnectionString { get; set; } = "127.0.0.1:6379,password=,defaultDatabase=2"; + + /// + /// 限流连接字符串 + /// + public string ConnectionStringRateLimit { get; set; } = "127.0.0.1:6379,password=,defaultDatabase=1"; } } diff --git a/Admin.Core.Common/Configs/DbConfig.cs b/Admin.Core.Common/Configs/DbConfig.cs index 5e28160f8..2bfd88ff9 100644 --- a/Admin.Core.Common/Configs/DbConfig.cs +++ b/Admin.Core.Common/Configs/DbConfig.cs @@ -11,32 +11,32 @@ public class DbConfig /// /// 数据库类型 /// - public DataType Type { get; set; } + public DataType Type { get; set; } = DataType.Sqlite; /// /// 数据库字符串 /// - public string ConnectionString { get; set; } + public string ConnectionString { get; set; } = "Data Source=|DataDirectory|\\admindb.db; Pooling=true;Min Pool Size=1"; /// /// 生成数据 /// - public bool GenerateData { get; set; } + public bool GenerateData { get; set; } = false; /// /// 同步结构 /// - public bool SyncStructure { get; set; } + public bool SyncStructure { get; set; } = true; /// /// 同步数据 /// - public bool SyncData { get; set; } + public bool SyncData { get; set; } = true; /// /// 建库 /// - public bool CreateDb { get; set; } + public bool CreateDb { get; set; } = true; /// /// 建库连接字符串 @@ -51,11 +51,11 @@ public class DbConfig /// /// 监听所有操作 /// - public bool MonitorCommand { get; set; } + public bool MonitorCommand { get; set; } = false; /// /// 监听Curd操作 /// - public bool Curd { get; set; } + public bool Curd { get; set; } = false; } } diff --git a/Admin.Core.Common/Configs/JwtConfig.cs b/Admin.Core.Common/Configs/JwtConfig.cs index 67b75b967..aa1ccedc3 100644 --- a/Admin.Core.Common/Configs/JwtConfig.cs +++ b/Admin.Core.Common/Configs/JwtConfig.cs @@ -11,21 +11,26 @@ public class JwtConfig /// /// 发行者 /// - public string Issuer { get; set; } + public string Issuer { get; set; } = "http://127.0.0.1:8888"; /// /// 订阅者 /// - public string Audience { get; set; } + public string Audience { get; set; } = "http://127.0.0.1:8888"; /// - /// 秘钥 + /// 密钥 /// - public string SecurityKey { get; set; } + public string SecurityKey { get; set; } = "ertJKl#521*a@790asD&1#"; /// /// 有效期(分钟) /// - public int Expires { get; set; } + public int Expires { get; set; } = 120; + + /// + /// 刷新有效期(分钟) + /// + public int RefreshExpires { get; set; } = 480; } } diff --git a/Admin.Core.Common/Configs/UploadConfig.cs b/Admin.Core.Common/Configs/UploadConfig.cs new file mode 100644 index 000000000..779ec4bcd --- /dev/null +++ b/Admin.Core.Common/Configs/UploadConfig.cs @@ -0,0 +1,80 @@ +using System; +using System.IO; + +namespace Admin.Core.Common.Configs +{ + /// + /// 上传配置 + /// + public class UploadConfig + { + /// + /// 头像上传配置 + /// + public FileUploadConfig Avatar { get; set; } + + /// + /// 文档图片上传配置 + /// + public FileUploadConfig Document { get; set; } + } + + /// + /// 文件上传配置 + /// + public class FileUploadConfig + { + private string _uploadPath; + /// + /// 上传路径 + /// + public string UploadPath + { + get + { + if (_uploadPath.IsNull()) + { + _uploadPath = Path.Combine(AppContext.BaseDirectory, "upload").ToPath(); + } + + if (!Path.IsPathRooted(_uploadPath)) + { + _uploadPath = Path.Combine(AppContext.BaseDirectory, _uploadPath).ToPath(); + } + + return _uploadPath; + } + set => _uploadPath = value; + } + + /// + /// 请求路径 + /// + public string RequestPath { get; set; } + + /// + /// 路径格式 + /// + public string Format { get; set; } + + /// + /// 路径日期格式 + /// + public string DateTimeFormat { get; set; } + + /// + /// 文件大小 10M = 10 * 1024 * 1024 + /// + public long MaxSize { get; set; } + + /// + /// 最大允许上传个数 -1不限制 + /// + public int Limit { get; set; } = -1; + + /// + /// 文件格式 + /// + public string[] ContentType { get; set; } + } +} diff --git a/Admin.Core.Common/Extensions/DateTimeExtensions.cs b/Admin.Core.Common/Extensions/DateTimeExtensions.cs new file mode 100644 index 000000000..e677d2292 --- /dev/null +++ b/Admin.Core.Common/Extensions/DateTimeExtensions.cs @@ -0,0 +1,36 @@ +using System; + +namespace Admin.Core.Common.Extensions +{ + public static class DateTimeExtensions + { + /// + /// 时间戳起始日期 + /// + public static DateTime TimestampStart = new DateTime(1970, 1, 1, 0, 0, 0, 0); + + /// + /// 转换为时间戳 + /// + /// + /// 是否使用毫秒 + /// + public static long ToTimestamp(this DateTime dateTime, bool milliseconds = false) + { + var timestamp = dateTime.ToUniversalTime() - TimestampStart; + return (long)(milliseconds ? timestamp.TotalMilliseconds : timestamp.TotalSeconds); + } + + /// + /// 获取周几 + /// + /// + /// + public static string GetWeekName(this DateTime datetime) + { + var day = (int)datetime.DayOfWeek; + var week = new string[] { "周日", "周一", "周二", "周三", "周四", "周五", "周六" }; + return week[day]; + } + } +} diff --git a/Admin.Core.Common/Extensions/EnumExtensions.cs b/Admin.Core.Common/Extensions/EnumExtensions.cs new file mode 100644 index 000000000..4ad2c76ab --- /dev/null +++ b/Admin.Core.Common/Extensions/EnumExtensions.cs @@ -0,0 +1,54 @@ +using Admin.Core.Common.Output; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Reflection; + +namespace Admin.Core.Common.Extensions +{ + public static class EnumExtensions + { + public static string ToDescription(this Enum item) + { + string name = item.ToString(); + var desc = item.GetType().GetField(name)?.GetCustomAttribute(); + return desc?.Description ?? name; + } + + public static long ToInt64(this Enum item) + { + return Convert.ToInt64(item); + } + + public static List ToList(this Enum value, bool ignoreUnKnown = false) + { + var enumType = value.GetType(); + + if (!enumType.IsEnum) + return null; + + return Enum.GetValues(enumType).Cast() + .Where(m => !ignoreUnKnown || !m.ToString().Equals("UnKnown")).Select(x => new OptionOutput + { + Label = x.ToDescription(), + Value = x + }).ToList(); + } + + public static List ToList(bool ignoreUnKnown = false) + { + var enumType = typeof(T); + + if (!enumType.IsEnum) + return null; + + return Enum.GetValues(enumType).Cast() + .Where(m => !ignoreUnKnown || !m.ToString().Equals("UnKnown")).Select(x => new OptionOutput + { + Label = x.ToDescription(), + Value = x + }).ToList(); + } + } +} diff --git a/Admin.Core.Common/Extensions/GuidExtensions.cs b/Admin.Core.Common/Extensions/GuidExtensions.cs index dba34ea74..4d6902f88 100644 --- a/Admin.Core.Common/Extensions/GuidExtensions.cs +++ b/Admin.Core.Common/Extensions/GuidExtensions.cs @@ -1,6 +1,6 @@ using System; -namespace Admin.Core +namespace Admin.Core.Common.Extensions { public static class GuidExtensions { diff --git a/Admin.Core.Common/Extensions/MethodInfoExtensions.cs b/Admin.Core.Common/Extensions/MethodInfoExtensions.cs index 9890475a8..0d7645f47 100644 --- a/Admin.Core.Common/Extensions/MethodInfoExtensions.cs +++ b/Admin.Core.Common/Extensions/MethodInfoExtensions.cs @@ -1,15 +1,20 @@ -using System.Linq; +using System; +using System.Linq; using System.Reflection; using System.Threading.Tasks; -namespace Admin.Core.Extensions +namespace Admin.Core.Common.Extensions { public static class MethodInfoExtensions { public static bool HasAttribute(this MethodInfo method) { - return method.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(T)) is T; + return method.GetCustomAttributes(typeof(T), false).FirstOrDefault() is T; + } + public static T GetAttribute(this MethodInfo method) where T : Attribute + { + return method.GetCustomAttributes(typeof(T), false).FirstOrDefault() as T; } public static bool IsAsync(this MethodInfo method) diff --git a/Admin.Core.Common/Extensions/StringExtensions.cs b/Admin.Core.Common/Extensions/StringExtensions.cs index 2317349cb..af639b552 100644 --- a/Admin.Core.Common/Extensions/StringExtensions.cs +++ b/Admin.Core.Common/Extensions/StringExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using System.Text; +using Admin.Core.Common.Helpers; namespace Admin.Core { @@ -89,5 +90,13 @@ public static string ToBase64(this string s, Encoding encoding) var bytes = encoding.GetBytes(s); return bytes.ToBase64(); } + + public static string ToPath(this string s) + { + if (s.IsNull()) + return string.Empty; + + return s.Replace(@"\", "/"); + } } } diff --git a/Admin.Core.Common/Files/FileInfo.cs b/Admin.Core.Common/Files/FileInfo.cs new file mode 100644 index 000000000..1e171d1eb --- /dev/null +++ b/Admin.Core.Common/Files/FileInfo.cs @@ -0,0 +1,77 @@ +namespace Admin.Core.Common.Files +{ + /// + /// 文件信息 + /// + public class FileInfo + { + public FileInfo() { } + + /// + /// 初始化文件信息 + /// + /// 文件名称 + /// 大小 + public FileInfo(string fileName, long size = 0L) + { + FileName = fileName; + Size = new FileSize(size); + Extension = System.IO.Path.GetExtension(FileName)?.TrimStart('.'); + } + + /// + /// 上传路径 + /// + public string UploadPath { get; set; } + + /// + /// 请求路径 + /// + public string RequestPath { get; set; } + + /// + /// 相对路径 + /// + public string RelativePath { get; set; } + + /// + /// 文件名 + /// + public string FileName { get; set; } + + /// + /// 保存名 + /// + public string SaveName { get; set; } + + /// + /// 文件大小 + /// + public FileSize Size { get; set; } + + /// + /// 扩展名 + /// + public string Extension { get; set; } + + /// + /// 文件目录 + /// + public string FileDirectory => System.IO.Path.Combine(UploadPath, RelativePath).ToPath(); + + /// + /// 文件请求路径 + /// + public string FileRequestPath => System.IO.Path.Combine(RequestPath, RelativePath, SaveName).ToPath(); + + /// + /// 文件相对路径 + /// + public string FileRelativePath => System.IO.Path.Combine(RelativePath, SaveName).ToPath(); + + /// + /// 文件路径 + /// + public string FilePath => System.IO.Path.Combine(UploadPath, RelativePath, SaveName).ToPath(); + } +} diff --git a/Admin.Core.Common/Files/FileSize.cs b/Admin.Core.Common/Files/FileSize.cs new file mode 100644 index 000000000..1d32f03d9 --- /dev/null +++ b/Admin.Core.Common/Files/FileSize.cs @@ -0,0 +1,82 @@ +using Admin.Core.Common.Extensions; +using Admin.Core.Common.Helpers; + +namespace Admin.Core.Common.Files +{ + /// + /// 文件大小 + /// + public struct FileSize + { + /// + /// 初始化文件大小 + /// + /// 文件大小 + /// 文件大小单位 + public FileSize(long size, FileSizeUnit unit = FileSizeUnit.Byte) + { + switch (unit) + { + case FileSizeUnit.K: + Size = size * 1024; break; + case FileSizeUnit.M: + Size = size * 1024 * 1024; break; + case FileSizeUnit.G: + Size = size * 1024 * 1024 * 1024; break; + default: + Size = size; break; + } + } + + /// + /// 文件字节长度 + /// + public long Size { get; } + + /// + /// 获取文件大小,单位:字节 + /// + public long GetSize() + { + return Size; + } + + /// + /// 获取文件大小,单位:K + /// + public double GetSizeByK() + { + return (Size / 1024.0).ToDouble(2); + } + + /// + /// 获取文件大小,单位:M + /// + public double GetSizeByM() + { + return (Size / 1024.0 / 1024.0).ToDouble(2); + } + + /// + /// 获取文件大小,单位:G + /// + public double GetSizeByG() + { + return (Size / 1024.0 / 1024.0 / 1024.0).ToDouble(2); + } + + /// + /// 输出描述 + /// + public override string ToString() + { + if (Size >= 1024 * 1024 * 1024) + return $"{GetSizeByG()} {FileSizeUnit.G.ToDescription()}"; + if (Size >= 1024 * 1024) + return $"{GetSizeByM()} {FileSizeUnit.M.ToDescription()}"; + if (Size >= 1024) + return $"{GetSizeByK()} {FileSizeUnit.K.ToDescription()}"; + return $"{Size} {FileSizeUnit.Byte.ToDescription()}"; + } + } +} \ No newline at end of file diff --git a/Admin.Core.Common/Files/FileSizeUnit.cs b/Admin.Core.Common/Files/FileSizeUnit.cs new file mode 100644 index 000000000..b1c6fde1d --- /dev/null +++ b/Admin.Core.Common/Files/FileSizeUnit.cs @@ -0,0 +1,31 @@ +using System.ComponentModel; + +namespace Admin.Core.Common.Files +{ + /// + /// 文件大小单位 + /// + public enum FileSizeUnit + { + /// + /// 字节 + /// + [Description("B")] + Byte, + /// + /// K字节 + /// + [Description("KB")] + K, + /// + /// M字节 + /// + [Description("MB")] + M, + /// + /// G字节 + /// + [Description("GB")] + G + } +} \ No newline at end of file diff --git a/Admin.Core.Common/Helpers/ConfigHelper.cs b/Admin.Core.Common/Helpers/ConfigHelper.cs index 348cdb8f9..79ae94cef 100644 --- a/Admin.Core.Common/Helpers/ConfigHelper.cs +++ b/Admin.Core.Common/Helpers/ConfigHelper.cs @@ -9,6 +9,17 @@ namespace Admin.Core.Common.Helpers /// public class ConfigHelper { + /* 使用热更新 + var uploadConfig = new ConfigHelper().Load("uploadconfig", _env.EnvironmentName, true); + services.Configure(uploadConfig); + + private readonly UploadConfig _uploadConfig; + public ImgController(IOptionsMonitor uploadConfig) + { + _uploadConfig = uploadConfig.CurrentValue; + } + */ + /// /// 加载配置文件 /// @@ -28,7 +39,7 @@ public IConfiguration Load(string fileName, string environmentName = "", bool re if (environmentName.NotNull()) { - builder.AddJsonFile(fileName.ToLower() + "." + environmentName + ".json", true, reloadOnChange); + builder.AddJsonFile(fileName.ToLower() + "." + environmentName + ".json", optional: true, reloadOnChange: reloadOnChange); } return builder.Build(); @@ -38,8 +49,8 @@ public IConfiguration Load(string fileName, string environmentName = "", bool re /// 获得配置信息 /// /// 配置信息 - /// - /// 文件名称 + /// 文件名称 + /// 环境名称 /// 自动更新 /// public T Get(string fileName, string environmentName = "", bool reloadOnChange = false) @@ -50,5 +61,21 @@ public T Get(string fileName, string environmentName = "", bool reloadOnChang return configuration.Get(); } + + /// + /// 绑定实例配置信息 + /// + /// 文件名称 + /// 实例配置 + /// 环境名称 + /// 自动更新 + public void Bind(string fileName, object instance, string environmentName = "", bool reloadOnChange = false) + { + var configuration = Load(fileName, environmentName, reloadOnChange); + if (configuration == null || instance == null) + return; + + configuration.Bind(instance); + } } } diff --git a/Admin.Core.Common/Helpers/ConsoleHelper.cs b/Admin.Core.Common/Helpers/ConsoleHelper.cs index 5beca332b..0f875ab13 100644 --- a/Admin.Core.Common/Helpers/ConsoleHelper.cs +++ b/Admin.Core.Common/Helpers/ConsoleHelper.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace Admin.Core.Common.Helpers { diff --git a/Admin.Core.Common/Helpers/HtmlHelper.cs b/Admin.Core.Common/Helpers/HtmlHelper.cs new file mode 100644 index 000000000..16d4407ab --- /dev/null +++ b/Admin.Core.Common/Helpers/HtmlHelper.cs @@ -0,0 +1,416 @@ +using System; +using System.Text; +using System.Net; +using System.IO; +using System.Threading; +using System.Text.RegularExpressions; +using Admin.Core.Common.Attributes; + +namespace Admin.Core.Common.Helpers +{ + /// + /// Html操作相关类 + /// + [SingleInstance] + public class HtmlHelper + { + #region 私有字段 + private readonly string _ContentType = "application/json"; + private readonly string _Accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/x-silverlight, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-silverlight-2-b1, */*"; + private readonly string _UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)"; + private int _Delay = 1000; + private int _CurrentTry = 0; + #endregion + + #region 公有属性 + /// + /// Cookie + /// + public CookieContainer CookieContainer { get; } = new CookieContainer(); + + /// + /// 语言 + /// + public Encoding Encoding { get; set; } = Encoding.GetEncoding("utf-8"); + + public int NetworkDelay + { + get + { + Random r = new Random(); + return r.Next(_Delay, _Delay * 2); + } + set + { + _Delay = value; + } + } + + public int MaxTry { get; set; } = 300; + #endregion + + #region 获取HTML + /// + /// 获取HTML + /// + /// 地址 + /// post 提交的字符串 + /// 是否是post + /// CookieContainer + public string GetHtml(string url, string postData, bool isPost, CookieContainer cookieContainer) + { + if (string.IsNullOrEmpty(postData)) return GetHtml(url, cookieContainer); + Thread.Sleep(NetworkDelay); + _CurrentTry++; + HttpWebRequest httpWebRequest = null; + HttpWebResponse httpWebResponse = null; + try + { + byte[] byteRequest = Encoding.Default.GetBytes(postData); + httpWebRequest = (HttpWebRequest)WebRequest.Create(url); + httpWebRequest.CookieContainer = cookieContainer; + httpWebRequest.ContentType = _ContentType; + httpWebRequest.ServicePoint.ConnectionLimit = MaxTry; + httpWebRequest.Referer = url; + httpWebRequest.Accept = _Accept; + httpWebRequest.UserAgent = _UserAgent; + httpWebRequest.Method = isPost ? "POST" : "GET"; + httpWebRequest.ContentLength = byteRequest.Length; + Stream stream = httpWebRequest.GetRequestStream(); + stream.Write(byteRequest, 0, byteRequest.Length); + stream.Close(); + httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse(); + Stream responseStream = httpWebResponse.GetResponseStream(); + StreamReader streamReader = new StreamReader(responseStream, Encoding); + string html = streamReader.ReadToEnd(); + streamReader.Close(); + responseStream.Close(); + _CurrentTry = 0; + httpWebRequest.Abort(); + httpWebResponse.Close(); + return html; + } + catch + { + if (_CurrentTry <= MaxTry) GetHtml(url, postData, isPost, cookieContainer); + _CurrentTry--; + if (httpWebRequest != null) httpWebRequest.Abort(); + if (httpWebResponse != null) httpWebResponse.Close(); + return string.Empty; + } + } + + /// + /// 获取HTML + /// + /// 地址 + /// CookieContainer + public string GetHtml(string url, CookieContainer cookieContainer) + { + Thread.Sleep(NetworkDelay); + _CurrentTry++; + HttpWebRequest httpWebRequest = null; + HttpWebResponse httpWebResponse = null; + try + { + httpWebRequest = (HttpWebRequest)WebRequest.Create(url); + httpWebRequest.CookieContainer = cookieContainer; + httpWebRequest.ContentType = _ContentType; + httpWebRequest.ServicePoint.ConnectionLimit = MaxTry; + httpWebRequest.Referer = url; + httpWebRequest.Accept = _Accept; + httpWebRequest.UserAgent = _UserAgent; + httpWebRequest.Method = "GET"; + httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse(); + Stream responseStream = httpWebResponse.GetResponseStream(); + StreamReader streamReader = new StreamReader(responseStream, Encoding); + string html = streamReader.ReadToEnd(); + streamReader.Close(); + responseStream.Close(); + _CurrentTry--; + httpWebRequest.Abort(); + httpWebResponse.Close(); + return html; + } + catch (Exception) + { + if (_CurrentTry <= MaxTry) GetHtml(url, cookieContainer); + _CurrentTry--; + if (httpWebRequest != null) httpWebRequest.Abort(); + if (httpWebResponse != null) httpWebResponse.Close(); + return string.Empty; + } + } + #endregion + + #region 获取字符流 + //--------------------------------------------------------------------------------------------------------------- + // 示例: + // System.Net.CookieContainer cookie = new System.Net.CookieContainer(); + // Stream s = HttpHelper.GetStream("http://ptlogin2.qq.com/getimage?aid=15000102&0.43878429697395826", cookie); + // picVerify.Image = Image.FromStream(s); + //--------------------------------------------------------------------------------------------------------------- + /// + /// 获取字符流 + /// + /// 地址 + /// cookieContainer + public Stream GetStream(string url, CookieContainer cookieContainer) + { + _CurrentTry++; + + HttpWebRequest httpWebRequest = null; + HttpWebResponse httpWebResponse = null; + + try + { + httpWebRequest = (HttpWebRequest)WebRequest.Create(url); + httpWebRequest.CookieContainer = cookieContainer; + httpWebRequest.ContentType = _ContentType; + httpWebRequest.ServicePoint.ConnectionLimit = MaxTry; + httpWebRequest.Referer = url; + httpWebRequest.Accept = _Accept; + httpWebRequest.UserAgent = _UserAgent; + httpWebRequest.Method = "GET"; + + httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse(); + Stream responseStream = httpWebResponse.GetResponseStream(); + _CurrentTry--; + return responseStream; + } + catch (Exception) + { + if (_CurrentTry <= MaxTry) + { + GetHtml(url, cookieContainer); + } + + _CurrentTry--; + + if (httpWebRequest != null) + { + httpWebRequest.Abort(); + } if (httpWebResponse != null) + { + httpWebResponse.Close(); + } + return null; + } + } + #endregion + + #region 清除HTML标记 + /// + /// 清除HTML标记 + /// + /// + /// 已经去除后的文字 + public string NoHTML(string Htmlstring) + { + //删除脚本 + Htmlstring = Regex.Replace(Htmlstring, @"]*?>.*?", "", RegexOptions.IgnoreCase); + + //删除HTML + Regex regex = new Regex("<.+?>", RegexOptions.IgnoreCase); + Htmlstring = regex.Replace(Htmlstring, ""); + Htmlstring = Regex.Replace(Htmlstring, @"<(.[^>]*)>", "", RegexOptions.IgnoreCase); + Htmlstring = Regex.Replace(Htmlstring, @"([\r\n])[\s]+", "", RegexOptions.IgnoreCase); + Htmlstring = Regex.Replace(Htmlstring, @"-->", "", RegexOptions.IgnoreCase); + Htmlstring = Regex.Replace(Htmlstring, @" OutOfProcess + false + 1.4.1 + MIT + xiaoxue + xiaoxue + git + 中台Admin后端WebApi + https://github.com/zhontai/Admin.Core + https://github.com/zhontai/Admin.Core + ZhonTai Admin;WebApi + Admin.Core + Admin.Core @@ -14,16 +26,12 @@ - - - - @@ -32,16 +40,18 @@ + - - - + + + + + - - - - + + + @@ -49,6 +59,9 @@ + + Always + Always @@ -62,6 +75,7 @@ + diff --git a/Admin.Core/Admin.Core.xml b/Admin.Core/Admin.Core.xml index b357f11cb..5119ad4fa 100644 --- a/Admin.Core/Admin.Core.xml +++ b/Admin.Core/Admin.Core.xml @@ -9,6 +9,11 @@ 启用登录 + + + 禁用操作日志 + + 启用权限 @@ -21,7 +26,7 @@ - 自定义路由 /api/{version}/[controler]/[action] + 自定义路由 /api/{version}/[area]/[controler]/[action] @@ -74,7 +79,7 @@ - + 查询分页接口 @@ -128,6 +133,13 @@ 授权管理 + + + 获得token + + + + 获取验证码 @@ -135,7 +147,7 @@ 上次验证码键 - + 获取密钥 @@ -155,6 +167,32 @@ 登录信息 + + + 刷新Token + 以旧换新 + + + + + + + 缓存管理 + + + + + 获取缓存列表 + + + + + + 清除缓存 + + + + 数据字典 @@ -167,7 +205,7 @@ - + 查询分页数据字典 @@ -195,33 +233,133 @@ - + - 图片管理 + 文档管理 - + + + 查询文档列表 + + + + + + + - 获取头像 + 查询文档图片列表 - - + - + - 下载图片 + 查询单条分组 - - + - + - 上传图片 - 支持多图片上传 + 查询单条菜单 - + + + + + + 查询单条文档内容 + + + + + + + 查询精简文档列表 + + + + + + 新增分组 + + + + + + + 新增菜单 + + + + + + + 修改分组 + + + + + + + 修改菜单 + + + + + + + 修改文档内容 + + + + + + + 删除文档 + + + + + + + 删除图片 + + + + + + + + 上传文档图片 + + + + + + + 登录日志管理 + + + + + 查询分页登录日志 + + + + + + + 操作日志管理 + + + + + 查询分页操作日志 + + @@ -259,9 +397,16 @@ + + + 查询单条权限点 + + + + - 查询权限分配界面权限列表 + 查询角色权限-权限列表 @@ -293,6 +438,13 @@ + + + 新增权限点 + + + + 修改分组 @@ -314,6 +466,13 @@ + + + 修改权限点 + + + + 删除权限 @@ -340,7 +499,7 @@ - + 查询分页角色 @@ -393,7 +552,7 @@ - + 查询分页用户 @@ -442,6 +601,13 @@ + + + 上传头像 + + + + 视图管理 @@ -461,7 +627,7 @@ - + 查询分页视图 @@ -522,18 +688,20 @@ - + 同步结构 - + - 初始化数据 + 初始化数据表数据 + + @@ -543,7 +711,7 @@ - + 同步数据 @@ -613,9 +781,50 @@ 系统内部错误(非业务代码里显式抛出的异常,例如由于数据不正确导致空指针异常、数据库异常等等) - + + + 添加Ip限流 + + + + + + + + Admin异常错误过滤 + + + + + Api帮助类 + + + + + 接口名称 + + + + + 接口地址 + + + + + 操作日志处理接口 + + + + + 写操作日志 + + + + + + - 全局异常错误过滤 + 操作日志处理 diff --git a/Admin.Core/Aop/AopHelper.cs b/Admin.Core/Aop/AopHelper.cs index a909d8fe4..ab016cc92 100644 --- a/Admin.Core/Aop/AopHelper.cs +++ b/Admin.Core/Aop/AopHelper.cs @@ -8,7 +8,7 @@ namespace Admin.Core.Aop { public class AopHelper { - public static async Task ExecuteGenericMethod(Task returnValue, Action callBackAction, Action exceptionAction) + public static async Task ExecuteGenericMethod(Task returnValue, Action callBackAction, Action exceptionAction, Action finallyAction) { try { @@ -19,16 +19,20 @@ public static async Task ExecuteGenericMethod(Task returnValue, Action< catch (Exception ex) { exceptionAction?.Invoke(ex); - throw; + return default; + } + finally + { + finallyAction?.Invoke(); } } - public static object CallGenericMethod(IInvocation invocation, Action callBackAction, Action exceptionAction) + public static object CallGenericMethod(IInvocation invocation, Action callBackAction, Action exceptionAction, Action finallyAction) { return typeof(AopHelper) .GetMethod("ExecuteGenericMethod", BindingFlags.Public | BindingFlags.Static) .MakeGenericMethod(invocation.Method.ReturnType.GenericTypeArguments[0]) - .Invoke(null, new object[] { invocation.ReturnValue, callBackAction, exceptionAction }); + .Invoke(null, new object[] { invocation.ReturnValue, callBackAction, exceptionAction, finallyAction }); } } } diff --git a/Admin.Core/Aop/TransactionInterceptor.cs b/Admin.Core/Aop/TransactionInterceptor.cs index 85e844bdb..a09e61664 100644 --- a/Admin.Core/Aop/TransactionInterceptor.cs +++ b/Admin.Core/Aop/TransactionInterceptor.cs @@ -1,88 +1,67 @@ -using System; +using System.Reflection; using System.Threading.Tasks; using Castle.DynamicProxy; using FreeSql; -using Admin.Core.Common; -using Admin.Core.Extensions; -using Admin.Core.Model.Output; +using Admin.Core.Common.Extensions; +using Admin.Core.Common.Output; +using Admin.Core.Common.Attributes; namespace Admin.Core.Aop { public class TransactionInterceptor : IInterceptor { - private readonly IUnitOfWork _unitOfWork; - public TransactionInterceptor(IUnitOfWork unitOfWork) + IUnitOfWork _unitOfWork; + private readonly UnitOfWorkManager _unitOfWorkManager; + + public TransactionInterceptor(UnitOfWorkManager unitOfWorkManager) { - _unitOfWork = unitOfWork; + _unitOfWorkManager = unitOfWorkManager; } - public async void Intercept(IInvocation invocation) + public void Intercept(IInvocation invocation) { var method = invocation.MethodInvocationTarget ?? invocation.Method; if (method.HasAttribute()) { - try - { - _unitOfWork.Open(); - invocation.Proceed(); - - if (method.IsAsync()) - { - if (invocation.Method.ReturnType == typeof(Task)) - { - await (Task)invocation.ReturnValue; - } - else - { - AopHelper.CallGenericMethod( - invocation, - res => - { - if (res == null) - { - return; - } + InterceptTransaction(invocation, method); + } + else + { + invocation.Proceed(); + } + } - var responseOutput = res as IResponseOutput; - if (responseOutput != null && !responseOutput.Success) - { - _unitOfWork.Rollback(); - } - }, - ex => - { - _unitOfWork.Rollback(); - }); - } - } - else - { - if (invocation.Method.ReturnType != typeof(void)) - { - var responseOutput = invocation.ReturnValue as Task; - if (responseOutput != null && !responseOutput.Result.Success) - { - _unitOfWork.Rollback(); - } - } - } + private async void InterceptTransaction(IInvocation invocation, MethodInfo method) + { + try + { + var transaction = method.GetAttribute(); + _unitOfWork = _unitOfWorkManager.Begin(transaction.Propagation, transaction.IsolationLevel); + invocation.Proceed(); - _unitOfWork.Commit(); + dynamic returnValue = invocation.ReturnValue; + if (returnValue is Task) + { + returnValue = await returnValue; } - catch (Exception) + + if (returnValue is IResponseOutput res && !res.Success) { _unitOfWork.Rollback(); - throw; } - finally + else { - _unitOfWork.Close(); + _unitOfWork.Commit(); } } - else + catch { - invocation.Proceed(); + _unitOfWork.Rollback(); + } + finally + { + _unitOfWork.Dispose(); } } } diff --git a/Admin.Core/Attributes/NoOprationLogAttribute.cs b/Admin.Core/Attributes/NoOprationLogAttribute.cs new file mode 100644 index 000000000..736c4085f --- /dev/null +++ b/Admin.Core/Attributes/NoOprationLogAttribute.cs @@ -0,0 +1,12 @@ +using System; + +namespace Admin.Core.Attributes +{ + /// + /// 禁用操作日志 + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] + public class NoOprationLogAttribute : Attribute + { + } +} diff --git a/Admin.Core/Attributes/PermissionAttribute.cs b/Admin.Core/Attributes/PermissionAttribute.cs index d12fceed2..ed26662b1 100644 --- a/Admin.Core/Attributes/PermissionAttribute.cs +++ b/Admin.Core/Attributes/PermissionAttribute.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection; using Admin.Core.Auth; using Admin.Core.Common.Auth; +using System.Threading.Tasks; namespace Admin.Core.Attributes { @@ -13,9 +14,9 @@ namespace Admin.Core.Attributes /// 启用权限 /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] - public class PermissionAttribute : AuthorizeAttribute, IAuthorizationFilter + public class PermissionAttribute : AuthorizeAttribute, IAuthorizationFilter, IAsyncAuthorizationFilter { - public async void OnAuthorization(AuthorizationFilterContext context) + private async Task PermissionAuthorization(AuthorizationFilterContext context) { //排除匿名访问 if (context.ActionDescriptor.EndpointMetadata.Any(m => m.GetType() == typeof(AllowAnonymousAttribute))) @@ -43,5 +44,15 @@ public async void OnAuthorization(AuthorizationFilterContext context) context.Result = new ForbidResult(); } } + + public async void OnAuthorization(AuthorizationFilterContext context) + { + await PermissionAuthorization(context); + } + + public async Task OnAuthorizationAsync(AuthorizationFilterContext context) + { + await PermissionAuthorization(context); + } } } diff --git a/Admin.Core/Attributes/ValidateInputAttribute.cs b/Admin.Core/Attributes/ValidateInputAttribute.cs index 0526eb51a..7209d070c 100644 --- a/Admin.Core/Attributes/ValidateInputAttribute.cs +++ b/Admin.Core/Attributes/ValidateInputAttribute.cs @@ -1,6 +1,6 @@ using System; using System.Linq; -using Admin.Core.Model.Output; +using Admin.Core.Common.Output; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; diff --git a/Admin.Core/Attributes/VersionRouteAttribute.cs b/Admin.Core/Attributes/VersionRouteAttribute.cs index 19959b80d..9686b6590 100644 --- a/Admin.Core/Attributes/VersionRouteAttribute.cs +++ b/Admin.Core/Attributes/VersionRouteAttribute.cs @@ -6,14 +6,15 @@ namespace Admin.Core.Attributes { /// - /// 自定义路由 /api/{version}/[controler]/[action] + /// 自定义路由 /api/{version}/[area]/[controler]/[action] /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] public class VersionRouteAttribute : RouteAttribute, IApiDescriptionGroupNameProvider { public string GroupName { get; set; } - public VersionRouteAttribute(ApiVersion version, string actionName = "") : base($"/api/{version.ToString()}/[controller]/{actionName}") + public VersionRouteAttribute(ApiVersion version = ApiVersion.V2, string action = "[action]") + : base($"/api/{version}/[area]/[controller]/{action}") { GroupName = version.ToString(); } diff --git a/Admin.Core/Auth/PermissionHandler.cs b/Admin.Core/Auth/PermissionHandler.cs index b9f52fb3a..208318249 100644 --- a/Admin.Core/Auth/PermissionHandler.cs +++ b/Admin.Core/Auth/PermissionHandler.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Threading.Tasks; +using Admin.Core.Common.Attributes; using Admin.Core.Service.Admin.User; namespace Admin.Core.Auth @@ -7,6 +8,7 @@ namespace Admin.Core.Auth /// /// 权限处理 /// + [SingleInstance] public class PermissionHandler : IPermissionHandler { private readonly IUserService _userService; diff --git a/Admin.Core/Auth/ResponseAuthenticationHandler.cs b/Admin.Core/Auth/ResponseAuthenticationHandler.cs index 07af41883..191964c37 100644 --- a/Admin.Core/Auth/ResponseAuthenticationHandler.cs +++ b/Admin.Core/Auth/ResponseAuthenticationHandler.cs @@ -9,6 +9,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using StatusCodes = Admin.Core.Enums.StatusCodes; +using Admin.Core.Common.Extensions; namespace Admin.Core.Auth { @@ -39,7 +40,7 @@ await Response.WriteAsync(JsonConvert.SerializeObject( new ResponseStatusData { Code = StatusCodes.Status401Unauthorized, - Msg = StatusCodes.Status401Unauthorized.ToDescriptionOrString() + Msg = StatusCodes.Status401Unauthorized.ToDescription() }, new JsonSerializerSettings() { @@ -56,7 +57,7 @@ await Response.WriteAsync(JsonConvert.SerializeObject( new ResponseStatusData { Code = StatusCodes.Status403Forbidden, - Msg = StatusCodes.Status403Forbidden.ToDescriptionOrString() + Msg = StatusCodes.Status403Forbidden.ToDescription() }, new JsonSerializerSettings() { diff --git a/Admin.Core/Controllers/Admin/ApiController.cs b/Admin.Core/Controllers/Admin/ApiController.cs index cbdbbe3c1..8543f99da 100644 --- a/Admin.Core/Controllers/Admin/ApiController.cs +++ b/Admin.Core/Controllers/Admin/ApiController.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Admin.Core.Model.Input; -using Admin.Core.Model.Output; +using Admin.Core.Common.Input; +using Admin.Core.Common.Output; using Admin.Core.Model.Admin; using Admin.Core.Service.Admin.Api; using Admin.Core.Service.Admin.Api.Input; diff --git a/Admin.Core/Controllers/Admin/AuthController.cs b/Admin.Core/Controllers/Admin/AuthController.cs index 9fa313459..17bb5a3fc 100644 --- a/Admin.Core/Controllers/Admin/AuthController.cs +++ b/Admin.Core/Controllers/Admin/AuthController.cs @@ -1,10 +1,22 @@ -using System.Threading.Tasks; +using System; +using System.Linq; +using System.Diagnostics; +using System.Threading.Tasks; +using System.Security.Claims; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc.ModelBinding; using Admin.Core.Attributes; -using Admin.Core.Model.Output; +using Admin.Core.Common.Auth; +using Admin.Core.Common.Output; using Admin.Core.Service.Admin.Auth; using Admin.Core.Service.Admin.Auth.Input; +using Admin.Core.Service.Admin.Auth.Output; +using Admin.Core.Service.Admin.LoginLog; +using Admin.Core.Service.Admin.LoginLog.Input; +using Admin.Core.Common.Helpers; +using Admin.Core.Service.Admin.User; +using Admin.Core.Common.Extensions; namespace Admin.Core.Controllers.Admin { @@ -13,12 +25,45 @@ namespace Admin.Core.Controllers.Admin /// public class AuthController : AreaController { - - private readonly IAuthService _authServices; - - public AuthController(IAuthService authServices) + private readonly IUserToken _userToken; + private readonly IAuthService _authService; + private readonly IUserService _userServices; + private readonly ILoginLogService _loginLogService; + + public AuthController( + IUserToken userToken, + IAuthService authServices, + IUserService userServices, + ILoginLogService loginLogService + ) { - _authServices = authServices; + _userToken = userToken; + _authService = authServices; + _userServices = userServices; + _loginLogService = loginLogService; + } + + /// + /// 获得token + /// + /// + /// + private IResponseOutput GetToken(ResponseOutput output) + { + if (!output.Success) + { + return ResponseOutput.NotOk(output.Msg); + } + + var user = output.Data; + var token = _userToken.Create(new[] + { + new Claim(ClaimAttributes.UserId, user.Id.ToString()), + new Claim(ClaimAttributes.UserName, user.UserName), + new Claim(ClaimAttributes.UserNickName, user.NickName) + }); + + return ResponseOutput.Ok(new { token }); } /// @@ -28,9 +73,10 @@ public AuthController(IAuthService authServices) /// [HttpGet] [AllowAnonymous] + [NoOprationLog] public async Task GetVerifyCode(string lastKey) { - return await _authServices.GetVerifyCodeAsync(lastKey); + return await _authService.GetVerifyCodeAsync(lastKey); } /// @@ -39,9 +85,10 @@ public async Task GetVerifyCode(string lastKey) /// [HttpGet] [AllowAnonymous] - public async Task GetPassWordKey() + [NoOprationLog] + public async Task GetPassWordEncryptKey() { - return await _authServices.GetPassWordKeyAsync(); + return await _authService.GetPassWordEncryptKeyAsync(); } /// @@ -52,7 +99,7 @@ public async Task GetPassWordKey() [Login] public async Task GetUserInfo() { - return await _authServices.GetUserInfoAsync(); + return await _authService.GetUserInfoAsync(); } /// @@ -63,9 +110,78 @@ public async Task GetUserInfo() /// [HttpPost] [AllowAnonymous] + [NoOprationLog] public async Task Login(AuthLoginInput input) { - return await _authServices.LoginAsync(input); + var sw = new Stopwatch(); + sw.Start(); + var res = await _authService.LoginAsync(input); + sw.Stop(); + + #region 添加登录日志 + var loginLogAddInput = new LoginLogAddInput() + { + CreatedUserName = input.UserName, + ElapsedMilliseconds = sw.ElapsedMilliseconds, + Status = res.Success, + Msg = res.Msg + }; + + ResponseOutput output = null; + if (res.Success) + { + output = (res as ResponseOutput); + var user = output.Data; + loginLogAddInput.CreatedUserId = user.Id; + loginLogAddInput.NickName = user.NickName; + } + + await _loginLogService.AddAsync(loginLogAddInput); + #endregion + + if (!res.Success) + { + return res; + } + + return GetToken(output); + } + + /// + /// 刷新Token + /// 以旧换新 + /// + /// + /// + [HttpGet] + [AllowAnonymous] + public async Task Refresh([BindRequired] string token) + { + var userClaims = _userToken.Decode(token); + if(userClaims == null || userClaims.Length == 0) + { + return ResponseOutput.NotOk(); + } + + var refreshExpires = userClaims.FirstOrDefault(a => a.Type == ClaimAttributes.RefreshExpires)?.Value; + if (refreshExpires.IsNull()) + { + return ResponseOutput.NotOk(); + } + + if(refreshExpires.ToLong() <= DateTime.Now.ToTimestamp()) + { + return ResponseOutput.NotOk("登录信息已过期"); + } + + var userId = userClaims.FirstOrDefault(a => a.Type == ClaimAttributes.UserId)?.Value; + if (userId.IsNull()) + { + return ResponseOutput.NotOk(); + } + var output = await _userServices.GetLoginUserAsync(userId.ToLong()); + + return GetToken(output); } } } diff --git a/Admin.Core/Controllers/Admin/CacheController.cs b/Admin.Core/Controllers/Admin/CacheController.cs new file mode 100644 index 000000000..95992b1ab --- /dev/null +++ b/Admin.Core/Controllers/Admin/CacheController.cs @@ -0,0 +1,42 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Admin.Core.Common.Output; +using Admin.Core.Service.Admin.Cache; + +namespace Admin.Core.Controllers.Admin +{ + /// + /// 缓存管理 + /// + public class CacheController : AreaController + { + + private readonly ICacheService _cacheServices; + + public CacheController(ICacheService cacheServices) + { + _cacheServices = cacheServices; + } + + /// + /// 获取缓存列表 + /// + /// + [HttpGet] + public IResponseOutput List() + { + return _cacheServices.List(); + } + + /// + /// 清除缓存 + /// + /// + /// + [HttpDelete] + public async Task Clear(string cacheKey) + { + return await _cacheServices.ClearAsync(cacheKey); + } + } +} diff --git a/Admin.Core/Controllers/Admin/DictionaryController.cs b/Admin.Core/Controllers/Admin/DictionaryController.cs index b2c48feeb..0fca94cea 100644 --- a/Admin.Core/Controllers/Admin/DictionaryController.cs +++ b/Admin.Core/Controllers/Admin/DictionaryController.cs @@ -1,8 +1,8 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Admin.Core.Service.Admin.Dictionary; -using Admin.Core.Model.Input; -using Admin.Core.Model.Output; +using Admin.Core.Common.Input; +using Admin.Core.Common.Output; using Admin.Core.Model.Admin; using Admin.Core.Service.Admin.Dictionary.Input; diff --git a/Admin.Core/Controllers/Admin/DocumentController.cs b/Admin.Core/Controllers/Admin/DocumentController.cs new file mode 100644 index 000000000..8331e03e4 --- /dev/null +++ b/Admin.Core/Controllers/Admin/DocumentController.cs @@ -0,0 +1,206 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Admin.Core.Common.Output; +using Admin.Core.Service.Admin.Document; +using Admin.Core.Service.Admin.Document.Input; +using Admin.Core.Common.Configs; +using Microsoft.Extensions.Options; +using Admin.Core.Common.Helpers; + +namespace Admin.Core.Controllers.Admin +{ + /// + /// 文档管理 + /// + public class DocumentController : AreaController + { + private readonly IDocumentService _documentServices; + private readonly UploadConfig _uploadConfig; + private readonly UploadHelper _uploadHelper; + + public DocumentController( + IDocumentService documentServices, + IOptionsMonitor uploadConfig, + UploadHelper uploadHelper + ) + { + _documentServices = documentServices; + _uploadConfig = uploadConfig.CurrentValue; + _uploadHelper = uploadHelper; + } + + /// + /// 查询文档列表 + /// + /// + /// + /// + /// + [HttpGet] + public async Task GetList(string key, DateTime? start, DateTime? end) + { + return await _documentServices.GetListAsync(key,start,end); + } + + /// + /// 查询文档图片列表 + /// + /// + /// + [HttpGet] + public async Task GetImageList(long id) + { + return await _documentServices.GetImageListAsync(id); + } + + /// + /// 查询单条分组 + /// + /// + /// + [HttpGet] + public async Task GetGroup(long id) + { + return await _documentServices.GetGroupAsync(id); + } + + /// + /// 查询单条菜单 + /// + /// + /// + [HttpGet] + public async Task GetMenu(long id) + { + return await _documentServices.GetMenuAsync(id); + } + + /// + /// 查询单条文档内容 + /// + /// + /// + [HttpGet] + public async Task GetContent(long id) + { + return await _documentServices.GetContentAsync(id); + } + + /// + /// 查询精简文档列表 + /// + /// + [HttpGet] + public async Task GetPlainList() + { + return await _documentServices.GetPlainListAsync(); + } + + /// + /// 新增分组 + /// + /// + /// + [HttpPost] + public async Task AddGroup(DocumentAddGroupInput input) + { + return await _documentServices.AddGroupAsync(input); + } + + /// + /// 新增菜单 + /// + /// + /// + [HttpPost] + public async Task AddMenu(DocumentAddMenuInput input) + { + return await _documentServices.AddMenuAsync(input); + } + + /// + /// 修改分组 + /// + /// + /// + [HttpPut] + public async Task UpdateGroup(DocumentUpdateGroupInput input) + { + return await _documentServices.UpdateGroupAsync(input); + } + + /// + /// 修改菜单 + /// + /// + /// + [HttpPut] + public async Task UpdateMenu(DocumentUpdateMenuInput input) + { + return await _documentServices.UpdateMenuAsync(input); + } + + /// + /// 修改文档内容 + /// + /// + /// + [HttpPut] + public async Task UpdateContent(DocumentUpdateContentInput input) + { + return await _documentServices.UpdateContentAsync(input); + } + + /// + /// 删除文档 + /// + /// + /// + [HttpDelete] + public async Task SoftDelete(long id) + { + return await _documentServices.SoftDeleteAsync(id); + } + + /// + /// 删除图片 + /// + /// + /// + /// + [HttpDelete] + public async Task DeleteImage(long documentId, string url) + { + return await _documentServices.DeleteImageAsync(documentId, url); + } + + /// + /// 上传文档图片 + /// + /// + /// + [HttpPost] + public async Task UploadImage([FromForm]DocumentUploadImageInput input) + { + var config = _uploadConfig.Document; + var res = await _uploadHelper.UploadAsync(input.File, config, new { input.Id }); + if (res.Success) + { + //保存文档图片 + var r = await _documentServices.AddImageAsync( + new DocumentAddImageInput + { + DocumentId = input.Id, + Url = res.Data.FileRequestPath + }); + if (r.Success) + { + return ResponseOutput.Ok(res.Data.FileRequestPath); + } + } + + return ResponseOutput.NotOk("上传失败!"); + } + } +} diff --git a/Admin.Core/Controllers/Admin/ImgController.cs b/Admin.Core/Controllers/Admin/ImgController.cs deleted file mode 100644 index ac3f4bb80..000000000 --- a/Admin.Core/Controllers/Admin/ImgController.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Admin.Core.Model.Output; - -namespace Admin.Core.Controllers.Admin -{ - /// - /// 图片管理 - /// - [Area("Admin")] - [Route("api/[area]/[controller]/[action]")] - [ApiController] - public class ImgController : ControllerBase - { - /// - /// 获取头像 - /// - /// - /// - /// - [HttpGet] - [Route("{fileName}")] - public FileStreamResult Avatar([FromServices]IWebHostEnvironment environment, string fileName = "") - { - string filepath = Path.Combine(environment.WebRootPath,"avatar", fileName); - var stream = System.IO.File.OpenRead(filepath); - string fileExt = Path.GetExtension(filepath); - var contentTypeProvider = new Microsoft.AspNetCore.StaticFiles.FileExtensionContentTypeProvider(); - var contentType = contentTypeProvider.Mappings[fileExt]; - var fileDownloadName = Path.GetFileName(filepath); - - return File(stream, contentType, fileDownloadName); - } - - /// - /// 下载图片 - /// - /// - /// - /// - [HttpGet] - [Route("{fileName}")] - public FileStreamResult Download([FromServices]IWebHostEnvironment environment,string fileName = "") - { - string filepath = Path.Combine(environment.WebRootPath, "images", fileName); - var stream = System.IO.File.OpenRead(filepath); - string fileExt = Path.GetExtension(filepath); - var contentTypeProvider = new Microsoft.AspNetCore.StaticFiles.FileExtensionContentTypeProvider(); - var contentType = contentTypeProvider.Mappings[fileExt]; - var fileDownloadName = Path.GetFileName(filepath); - - return File(stream, contentType, fileDownloadName); - } - - /// - /// 上传图片 - /// 支持多图片上传 - /// - /// - /// - [HttpPost] - public async Task Upload([FromServices]IWebHostEnvironment environment) - { - string path = string.Empty; - string foldername = "images"; - IFormFileCollection files = null; - - try - { - files = Request.Form.Files; - } - catch (Exception) - { - files = null; - } - - if (files == null || !files.Any()) - { - return ResponseOutput.NotOk("请选择上传的文件。"); - } - - //格式限制 - var allowType = new string[] { "image/jpg", "image/png", "image/jpeg" }; - - string folderpath = Path.Combine(environment.WebRootPath, foldername); - if (!Directory.Exists(folderpath)) - { - Directory.CreateDirectory(folderpath); - } - - if (files.Any(c => allowType.Contains(c.ContentType))) - { - if (files.Sum(c => c.Length) <= 1024 * 1024 * 4) - { - //foreach (var file in files) - var file = files.FirstOrDefault(); - string strpath = Path.Combine(foldername, DateTime.Now.ToString("MMddHHmmss") + file.FileName); - path = Path.Combine(environment.WebRootPath, strpath); - - using (var stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite)) - { - await file.CopyToAsync(stream); - } - - return ResponseOutput.Ok(strpath); - } - else - { - return ResponseOutput.NotOk("图片过大"); - } - } - else - { - return ResponseOutput.NotOk("图片格式错误"); - } - } - } - -} diff --git a/Admin.Core/Controllers/Admin/LoginLogController.cs b/Admin.Core/Controllers/Admin/LoginLogController.cs new file mode 100644 index 000000000..19da90935 --- /dev/null +++ b/Admin.Core/Controllers/Admin/LoginLogController.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Admin.Core.Common.Input; +using Admin.Core.Common.Output; +using Admin.Core.Model.Admin; +using Admin.Core.Service.Admin.LoginLog; + +namespace Admin.Core.Controllers.Admin +{ + /// + /// 登录日志管理 + /// + public class LoginLogController : AreaController + { + private readonly ILoginLogService _loginLogService; + + public LoginLogController(ILoginLogService loginLogService) + { + _loginLogService = loginLogService; + } + + /// + /// 查询分页登录日志 + /// + /// + /// + [HttpPost] + public async Task GetPage(PageInput model) + { + return await _loginLogService.PageAsync(model); + } + } +} diff --git a/Admin.Core/Controllers/Admin/OprationLogController.cs b/Admin.Core/Controllers/Admin/OprationLogController.cs new file mode 100644 index 000000000..deb39ec1f --- /dev/null +++ b/Admin.Core/Controllers/Admin/OprationLogController.cs @@ -0,0 +1,33 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Admin.Core.Common.Input; +using Admin.Core.Common.Output; +using Admin.Core.Model.Admin; +using Admin.Core.Service.Admin.OprationLog; + +namespace Admin.Core.Controllers.Admin +{ + /// + /// 操作日志管理 + /// + public class OprationLogController : AreaController + { + private readonly IOprationLogService _oprationLogService; + + public OprationLogController(IOprationLogService loginLogService) + { + _oprationLogService = loginLogService; + } + + /// + /// 查询分页操作日志 + /// + /// + /// + [HttpPost] + public async Task GetPage(PageInput model) + { + return await _oprationLogService.PageAsync(model); + } + } +} diff --git a/Admin.Core/Controllers/Admin/PermissionController.cs b/Admin.Core/Controllers/Admin/PermissionController.cs index 11a048025..1ff354963 100644 --- a/Admin.Core/Controllers/Admin/PermissionController.cs +++ b/Admin.Core/Controllers/Admin/PermissionController.cs @@ -1,7 +1,7 @@ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Admin.Core.Model.Output; +using Admin.Core.Common.Output; using Admin.Core.Service.Admin.Permission; using Admin.Core.Service.Admin.Permission.Input; @@ -66,7 +66,18 @@ public async Task GetApi(long id) } /// - /// 查询权限分配界面权限列表 + /// 查询单条权限点 + /// + /// + /// + [HttpGet] + public async Task GetDot(long id) + { + return await _permissionServices.GetDotAsync(id); + } + + /// + /// 查询角色权限-权限列表 /// /// [HttpGet] @@ -120,11 +131,22 @@ public async Task AddApi(PermissionAddApiInput input) } /// - /// 修改分组 + /// 新增权限点 /// /// /// [HttpPost] + public async Task AddDot(PermissionAddDotInput input) + { + return await _permissionServices.AddDotAsync(input); + } + + /// + /// 修改分组 + /// + /// + /// + [HttpPut] public async Task UpdateGroup(PermissionUpdateGroupInput input) { return await _permissionServices.UpdateGroupAsync(input); @@ -135,7 +157,7 @@ public async Task UpdateGroup(PermissionUpdateGroupInput input) /// /// /// - [HttpPost] + [HttpPut] public async Task UpdateMenu(PermissionUpdateMenuInput input) { return await _permissionServices.UpdateMenuAsync(input); @@ -146,12 +168,23 @@ public async Task UpdateMenu(PermissionUpdateMenuInput input) /// /// /// - [HttpPost] + [HttpPut] public async Task UpdateApi(PermissionUpdateApiInput input) { return await _permissionServices.UpdateApiAsync(input); } + /// + /// 修改权限点 + /// + /// + /// + [HttpPut] + public async Task UpdateDot(PermissionUpdateDotInput input) + { + return await _permissionServices.UpdateDotAsync(input); + } + /// /// 删除权限 /// diff --git a/Admin.Core/Controllers/Admin/RoleController.cs b/Admin.Core/Controllers/Admin/RoleController.cs index a8deeabfe..fec258b04 100644 --- a/Admin.Core/Controllers/Admin/RoleController.cs +++ b/Admin.Core/Controllers/Admin/RoleController.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; -using Admin.Core.Model.Input; -using Admin.Core.Model.Output; +using Admin.Core.Common.Input; +using Admin.Core.Common.Output; using Admin.Core.Model.Admin; using Admin.Core.Service.Admin.Role; using Admin.Core.Service.Admin.Role.Input; diff --git a/Admin.Core/Controllers/Admin/UserController.cs b/Admin.Core/Controllers/Admin/UserController.cs index c3d06f08f..66fe3d548 100644 --- a/Admin.Core/Controllers/Admin/UserController.cs +++ b/Admin.Core/Controllers/Admin/UserController.cs @@ -1,10 +1,16 @@ using System.Threading.Tasks; using Admin.Core.Service.Admin.User; -using Admin.Core.Model.Output; -using Admin.Core.Model.Input; +using Admin.Core.Common.Output; +using Admin.Core.Common.Input; using Admin.Core.Model.Admin; using Admin.Core.Service.Admin.User.Input; using Microsoft.AspNetCore.Mvc; +using Admin.Core.Attributes; +using Microsoft.AspNetCore.Http; +using Admin.Core.Common.Auth; +using Admin.Core.Common.Configs; +using Admin.Core.Common.Helpers; +using Microsoft.Extensions.Options; namespace Admin.Core.Controllers.Admin { @@ -13,10 +19,21 @@ namespace Admin.Core.Controllers.Admin /// public class UserController : AreaController { + private readonly IUser _user; + private readonly UploadConfig _uploadConfig; + private readonly UploadHelper _uploadHelper; private readonly IUserService _userServices; - public UserController(IUserService userServices) + public UserController( + IUser user, + IOptionsMonitor uploadConfig, + UploadHelper uploadHelper, + IUserService userServices + ) { + _user = user; + _uploadConfig = uploadConfig.CurrentValue; + _uploadHelper = uploadHelper; _userServices = userServices; } @@ -118,5 +135,24 @@ public async Task UpdateBasic(UserUpdateBasicInput input) { return await _userServices.UpdateBasicAsync(input); } + + /// + /// 上传头像 + /// + /// + /// + [HttpPost] + [Login] + public async Task AvatarUpload([FromForm]IFormFile file) + { + var config = _uploadConfig.Avatar; + var res = await _uploadHelper.UploadAsync(file, config, new { _user.Id }); + if (res.Success) + { + return ResponseOutput.Ok(res.Data.FileRelativePath); + } + + return ResponseOutput.NotOk("上传失败!"); + } } } \ No newline at end of file diff --git a/Admin.Core/Controllers/Admin/ViewController.cs b/Admin.Core/Controllers/Admin/ViewController.cs index 7b76f3def..0a325a367 100644 --- a/Admin.Core/Controllers/Admin/ViewController.cs +++ b/Admin.Core/Controllers/Admin/ViewController.cs @@ -1,8 +1,8 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Admin.Core.Model.Admin; -using Admin.Core.Model.Input; -using Admin.Core.Model.Output; +using Admin.Core.Common.Input; +using Admin.Core.Common.Output; using Admin.Core.Service.Admin.View; using Admin.Core.Service.Admin.View.Input; diff --git a/Admin.Core/Db/Data/data.json b/Admin.Core/Db/Data/data.json index 648156c1c..ebe10a225 100644 --- a/Admin.Core/Db/Data/data.json +++ b/Admin.Core/Db/Data/data.json @@ -1 +1 @@ -{"dictionaries":[],"apis":[{"id":1,"parentId":0,"label":"接口管理","path":"api","description":""},{"id":2,"parentId":0,"label":"授权管理","path":"auth","description":""},{"id":3,"parentId":0,"label":"数据字典","path":"dictionary","description":""},{"id":4,"parentId":0,"label":"图片管理","path":"img","description":""},{"id":5,"parentId":0,"label":"权限管理","path":"permission","description":""},{"id":6,"parentId":0,"label":"角色管理","path":"role","description":""},{"id":7,"parentId":0,"label":"用户管理","path":"user","description":""},{"id":8,"parentId":0,"label":"视图管理","path":"view","description":""},{"id":10,"parentId":1,"label":"查询单条接口","path":"/api/admin/api/get","httpMethods":"get","description":""},{"id":11,"parentId":1,"label":"查询全部接口","path":"/api/admin/api/getlist","httpMethods":"get","description":""},{"id":12,"parentId":1,"label":"查询分页接口","path":"/api/admin/api/getpage","httpMethods":"post","description":""},{"id":13,"parentId":1,"label":"新增接口","path":"/api/admin/api/add","httpMethods":"post","description":""},{"id":14,"parentId":1,"label":"修改接口","path":"/api/admin/api/update","httpMethods":"put","description":""},{"id":15,"parentId":1,"label":"删除接口","path":"/api/admin/api/softdelete","httpMethods":"delete","description":""},{"id":16,"parentId":1,"label":"批量删除接口","path":"/api/admin/api/batchsoftdelete","httpMethods":"put","description":""},{"id":17,"parentId":1,"label":"同步接口","path":"/api/admin/api/sync","httpMethods":"post","description":"支持新增和修改接口\r\n根据接口是否存在自动禁用和启用api"},{"id":18,"parentId":2,"label":"获取验证码","path":"/api/admin/auth/getverifycode","httpMethods":"get","description":""},{"id":19,"parentId":2,"label":"获取密钥","path":"/api/admin/auth/getpasswordkey","httpMethods":"get","description":""},{"id":20,"parentId":2,"label":"查询用户信息","path":"/api/admin/auth/getuserinfo","httpMethods":"get","description":""},{"id":21,"parentId":2,"label":"用户登录","path":"/api/admin/auth/login","httpMethods":"post","description":"根据登录信息生成Token"},{"id":22,"parentId":3,"label":"查询单条数据字典","path":"/api/admin/dictionary/get","httpMethods":"get","description":""},{"id":23,"parentId":3,"label":"查询分页数据字典","path":"/api/admin/dictionary/getpage","httpMethods":"post","description":""},{"id":24,"parentId":3,"label":"新增数据字典","path":"/api/admin/dictionary/add","httpMethods":"post","description":""},{"id":25,"parentId":3,"label":"修改数据字典","path":"/api/admin/dictionary/update","httpMethods":"put","description":""},{"id":26,"parentId":3,"label":"删除数据字典","path":"/api/admin/dictionary/softdelete","httpMethods":"delete","description":""},{"id":27,"parentId":4,"label":"获取头像","path":"/api/admin/img/avatar/{filename}","httpMethods":"get","description":""},{"id":28,"parentId":4,"label":"下载图片","path":"/api/admin/img/download/{filename}","httpMethods":"get","description":""},{"id":29,"parentId":4,"label":"上传图片","path":"/api/admin/img/upload","httpMethods":"post","description":"支持多图片上传"},{"id":31,"parentId":5,"label":"查询权限列表","path":"/api/admin/permission/getlist","httpMethods":"get","description":""},{"id":32,"parentId":5,"label":"查询单条分组","path":"/api/admin/permission/getgroup","httpMethods":"get","description":""},{"id":33,"parentId":5,"label":"查询单条菜单","path":"/api/admin/permission/getmenu","httpMethods":"get","description":""},{"id":34,"parentId":5,"label":"查询单条接口","path":"/api/admin/permission/getapi","httpMethods":"get","description":""},{"id":35,"parentId":5,"label":"查询权限分配界面权限列表","path":"/api/admin/permission/getpermissionlist","httpMethods":"get","description":""},{"id":36,"parentId":5,"label":"查询角色权限","path":"/api/admin/permission/getrolepermissionlist","httpMethods":"get","description":""},{"id":37,"parentId":5,"label":"新增分组","path":"/api/admin/permission/addgroup","httpMethods":"post","description":""},{"id":38,"parentId":5,"label":"新增菜单","path":"/api/admin/permission/addmenu","httpMethods":"post","description":""},{"id":39,"parentId":5,"label":"新增接口","path":"/api/admin/permission/addapi","httpMethods":"post","description":""},{"id":40,"parentId":5,"label":"修改分组","path":"/api/admin/permission/updategroup","httpMethods":"post","description":""},{"id":41,"parentId":5,"label":"修改菜单","path":"/api/admin/permission/updatemenu","httpMethods":"post","description":""},{"id":42,"parentId":5,"label":"修改接口","path":"/api/admin/permission/updateapi","httpMethods":"post","description":""},{"id":43,"parentId":5,"label":"删除权限","path":"/api/admin/permission/softdelete","httpMethods":"delete","description":""},{"id":44,"parentId":5,"label":"保存角色权限","path":"/api/admin/permission/assign","httpMethods":"post","description":""},{"id":45,"parentId":6,"label":"查询单条角色","path":"/api/admin/role/get","httpMethods":"get","description":""},{"id":46,"parentId":6,"label":"查询分页角色","path":"/api/admin/role/getpage","httpMethods":"post","description":""},{"id":47,"parentId":6,"label":"新增角色","path":"/api/admin/role/add","httpMethods":"post","description":""},{"id":48,"parentId":6,"label":"修改角色","path":"/api/admin/role/update","httpMethods":"put","description":""},{"id":49,"parentId":6,"label":"删除角色","path":"/api/admin/role/softdelete","httpMethods":"delete","description":""},{"id":50,"parentId":6,"label":"批量删除角色","path":"/api/admin/role/batchsoftdelete","httpMethods":"put","description":""},{"id":51,"parentId":7,"label":"查询用户基本信息","path":"/api/admin/user/getbasic","httpMethods":"get","description":""},{"id":52,"parentId":7,"label":"查询单条用户","path":"/api/admin/user/get","httpMethods":"get","description":""},{"id":53,"parentId":7,"label":"查询分页用户","path":"/api/admin/user/getpage","httpMethods":"post","description":""},{"id":54,"parentId":7,"label":"新增用户","path":"/api/admin/user/add","httpMethods":"post","description":""},{"id":55,"parentId":7,"label":"修改用户","path":"/api/admin/user/update","httpMethods":"put","description":""},{"id":56,"parentId":7,"label":"删除用户","path":"/api/admin/user/softdelete","httpMethods":"delete","description":""},{"id":57,"parentId":7,"label":"批量删除用户","path":"/api/admin/user/batchsoftdelete","httpMethods":"put","description":""},{"id":58,"parentId":7,"label":"更新用户密码","path":"/api/admin/user/changepassword","httpMethods":"put","description":""},{"id":59,"parentId":7,"label":"更新用户基本信息","path":"/api/admin/user/updatebasic","httpMethods":"put","description":""},{"id":60,"parentId":8,"label":"查询单条视图","path":"/api/admin/view/get","httpMethods":"get","description":""},{"id":61,"parentId":8,"label":"查询全部视图","path":"/api/admin/view/getlist","httpMethods":"get","description":""},{"id":62,"parentId":8,"label":"查询分页视图","path":"/api/admin/view/getpage","httpMethods":"post","description":""},{"id":63,"parentId":8,"label":"新增视图","path":"/api/admin/view/add","httpMethods":"post","description":""},{"id":64,"parentId":8,"label":"修改视图","path":"/api/admin/view/update","httpMethods":"put","description":""},{"id":65,"parentId":8,"label":"删除视图","path":"/api/admin/view/softdelete","httpMethods":"delete","description":""},{"id":66,"parentId":8,"label":"批量删除视图","path":"/api/admin/view/batchsoftdelete","httpMethods":"put","description":""},{"id":67,"parentId":8,"label":"同步视图","path":"/api/admin/view/sync","httpMethods":"post","description":"支持新增和修改视图\r\n根据视图是否存在自动禁用和启用视图"}],"views":[{"id":1,"parentId":2,"label":"首页","path":"/admin/home","sort":1},{"id":2,"parentId":0,"label":"平台管理","path":"admin","sort":1},{"id":3,"parentId":14,"name":"user","label":"用户管理","path":"/admin/user","sort":1},{"id":4,"parentId":14,"name":"role","label":"角色管理","path":"/admin/role","sort":2},{"id":5,"parentId":14,"name":"api","label":"接口管理","path":"/admin/api","sort":3},{"id":6,"parentId":14,"name":"v","label":"视图管理","path":"/admin/view","sort":4},{"id":7,"parentId":14,"name":"permission","label":"权限管理","path":"/admin/permission","sort":5},{"id":8,"parentId":14,"name":"assign","label":"角色权限","path":"/admin/role-permission","sort":6},{"id":9,"parentId":2,"label":"系统配置","path":"config","sort":3},{"id":10,"parentId":9,"name":"dictonary","label":"数据字典","path":"/admin/dictionary","sort":1},{"id":11,"parentId":15,"name":"log","label":"操作日志","path":"/admin/logs","description":"","sort":2},{"id":12,"parentId":2,"label":"个人管理","path":"account","sort":5},{"id":13,"parentId":12,"name":"settings","label":"个人设置","path":"/account/settings","sort":1},{"id":14,"parentId":2,"label":"权限管理","path":"auth","description":"","sort":2},{"id":15,"parentId":2,"label":"日志管理","path":"logs","description":"","sort":4}],"permissions":[{"id":1,"parentId":65,"label":"首页","type":2,"viewId":1,"path":"/","icon":"fa fa-home","closable":false,"opened":false,"newWindow":false,"external":false,"sort":1,"description":""},{"id":2,"parentId":65,"label":"权限管理","type":1,"path":"","icon":"fa fa-sitemap","closable":false,"opened":true,"sort":2,"description":""},{"id":3,"parentId":65,"label":"系统配置","type":1,"path":"","icon":"el-icon-s-platform","closable":false,"opened":false,"sort":3,"description":""},{"id":4,"parentId":65,"label":"个人管理","type":1,"path":"","icon":"el-icon-s-custom","closable":false,"opened":false,"sort":4,"description":""},{"id":5,"parentId":66,"label":"Admin","type":2,"path":"https://zhontai.net","icon":"el-icon-s-promotion","closable":false,"newWindow":false,"external":false,"sort":5,"description":""},{"id":6,"parentId":66,"label":"FreeSql","type":2,"path":"https://github.com/2881099/FreeSql/wiki","icon":"el-icon-s-promotion","closable":false,"newWindow":false,"external":false,"sort":6,"description":""},{"id":7,"parentId":2,"label":"用户管理","type":2,"viewId":3,"path":"/admin/user","closable":true,"opened":false,"newWindow":false,"external":true,"sort":1,"description":""},{"id":8,"parentId":2,"label":"角色管理","type":2,"viewId":4,"path":"/admin/role","icon":"","closable":true,"opened":false,"sort":2,"description":""},{"id":9,"parentId":2,"label":"接口管理","type":2,"viewId":5,"path":"/admin/api","closable":true,"opened":false,"sort":3,"description":""},{"id":10,"parentId":2,"label":"视图管理","type":2,"viewId":6,"path":"/admin/view","icon":"","closable":true,"sort":4,"description":""},{"id":11,"parentId":2,"label":"权限管理","type":2,"viewId":7,"path":"/admin/permission","closable":true,"opened":false,"sort":5,"description":""},{"id":12,"parentId":2,"label":"角色权限","type":2,"viewId":8,"path":"/admin/role-permisson","closable":true,"opened":false,"newWindow":false,"external":false,"sort":6,"description":""},{"id":13,"parentId":8,"label":"查询","type":3,"apiId":46,"path":" ","closable":false,"opened":false,"sort":1,"description":""},{"id":14,"parentId":8,"label":"新增","type":3,"apiId":47,"path":" ","closable":false,"opened":false,"sort":2,"description":""},{"id":15,"parentId":8,"label":"修改","type":3,"apiId":48,"path":" ","closable":false,"opened":false,"sort":3,"description":""},{"id":16,"parentId":8,"label":"删除","type":3,"apiId":49,"path":" ","closable":true,"opened":false,"sort":4,"description":""},{"id":17,"parentId":8,"label":"批量删除","type":3,"apiId":50,"path":" ","icon":"","closable":false,"opened":false,"sort":5,"description":""},{"id":18,"parentId":8,"label":"查询单条角色","type":3,"apiId":45,"sort":6,"description":""},{"id":19,"parentId":7,"label":"查询","type":3,"apiId":53,"path":"","closable":true,"opened":false,"sort":1,"description":""},{"id":20,"parentId":7,"label":"新增","type":3,"apiId":54,"path":"","closable":false,"opened":false,"sort":2,"description":""},{"id":21,"parentId":7,"label":"修改","type":3,"apiId":55,"path":"","closable":true,"opened":false,"sort":3,"description":""},{"id":22,"parentId":7,"label":"删除","type":3,"apiId":56,"path":"","closable":true,"opened":false,"sort":4,"description":""},{"id":23,"parentId":7,"label":"批量删除","type":3,"apiId":57,"path":"","icon":"","sort":5,"description":""},{"id":24,"parentId":7,"label":"查询单条用户","type":3,"apiId":52,"sort":6,"description":""},{"id":25,"parentId":9,"label":"查询","type":3,"apiId":11,"path":"","icon":"","sort":1,"description":""},{"id":26,"parentId":9,"label":"新增","type":3,"apiId":13,"path":" ","closable":false,"opened":false,"sort":2,"description":""},{"id":27,"parentId":9,"label":"修改","type":3,"apiId":14,"path":" ","closable":false,"opened":false,"sort":3,"description":""},{"id":28,"parentId":9,"label":"删除","type":3,"apiId":15,"path":" ","closable":false,"opened":false,"sort":4,"description":""},{"id":29,"parentId":9,"label":"批量删除","type":3,"apiId":16,"path":"","icon":"","sort":5,"description":""},{"id":30,"parentId":9,"label":"同步","type":3,"apiId":17,"path":"","icon":"","sort":6,"description":""},{"id":31,"parentId":9,"label":"查询单条接口","type":3,"apiId":10,"sort":7,"description":""},{"id":32,"parentId":11,"label":"查询","type":3,"apiId":31,"path":"","icon":"","sort":1,"description":""},{"id":33,"parentId":11,"label":"新增分组","type":3,"apiId":37,"sort":2,"description":""},{"id":34,"parentId":11,"label":"新增菜单","type":3,"apiId":38,"sort":3,"description":""},{"id":35,"parentId":11,"label":"新增接口","type":3,"apiId":39,"sort":4,"description":""},{"id":36,"parentId":11,"label":"修改分组","type":3,"apiId":40,"sort":5,"description":""},{"id":37,"parentId":11,"label":"修改菜单","type":3,"apiId":41,"sort":6,"description":""},{"id":38,"parentId":11,"label":"修改接口","type":3,"apiId":42,"sort":7,"description":""},{"id":39,"parentId":11,"label":"删除","type":3,"apiId":43,"path":" ","closable":false,"opened":false,"sort":8,"description":""},{"id":40,"parentId":11,"label":"查询单条分组","type":3,"apiId":32,"sort":9,"description":""},{"id":41,"parentId":11,"label":"查询单条菜单","type":3,"apiId":33,"sort":10,"description":""},{"id":42,"parentId":11,"label":"查询单条接口","type":3,"apiId":34,"sort":11,"description":""},{"id":43,"parentId":12,"label":"查询角色","type":3,"apiId":46,"path":"","icon":"","sort":1,"description":""},{"id":44,"parentId":12,"label":"查询权限","type":3,"apiId":35,"path":"","icon":"","sort":2,"description":""},{"id":45,"parentId":12,"label":"查询角色权限","type":3,"apiId":36,"path":"","icon":"","sort":3,"description":""},{"id":46,"parentId":12,"label":"保存角色权限","type":3,"apiId":44,"path":" ","closable":false,"opened":false,"sort":4,"description":""},{"id":47,"parentId":4,"label":"个人设置","type":2,"viewId":13,"path":"/account/settings","icon":"el-icon-setting","closable":true,"opened":false,"newWindow":false,"external":false,"sort":1,"description":""},{"id":48,"parentId":47,"label":"查询基本信息","type":3,"apiId":51,"path":"","icon":"","closable":false,"opened":false,"sort":1,"description":""},{"id":49,"parentId":47,"label":"更新基本信息","type":3,"apiId":59,"sort":2,"description":""},{"id":50,"parentId":47,"label":"更新密码","type":3,"apiId":58,"path":" ","icon":"","closable":false,"opened":false,"sort":3,"description":""},{"id":51,"parentId":3,"label":"基础数据","type":1,"icon":"fa fa-database","opened":false,"sort":1},{"id":52,"parentId":64,"label":"查询","type":3,"apiId":23,"path":" ","icon":"","closable":false,"opened":false,"sort":1,"description":""},{"id":53,"parentId":64,"label":"新增","type":3,"apiId":24,"path":" ","icon":"","closable":false,"opened":false,"sort":2,"description":""},{"id":54,"parentId":64,"label":"修改","type":3,"apiId":25,"path":" ","icon":"","closable":false,"opened":false,"sort":3,"description":""},{"id":55,"parentId":64,"label":"删除","type":3,"apiId":26,"path":" ","icon":"","closable":false,"opened":false,"sort":4,"description":""},{"id":56,"parentId":64,"label":"查询单条数据字典","type":3,"apiId":22,"sort":5,"description":""},{"id":57,"parentId":10,"label":"查询","type":3,"apiId":61,"sort":1,"description":""},{"id":58,"parentId":10,"label":"新增","type":3,"apiId":63,"sort":2,"description":""},{"id":59,"parentId":10,"label":"修改","type":3,"apiId":64,"sort":3,"description":""},{"id":60,"parentId":10,"label":"删除","type":3,"apiId":65,"sort":4,"description":""},{"id":61,"parentId":10,"label":"批量删除","type":3,"apiId":66,"sort":5,"description":""},{"id":62,"parentId":10,"label":"同步","type":3,"apiId":67,"sort":6,"description":""},{"id":63,"parentId":10,"label":"查询单条视图","type":3,"apiId":60,"sort":7,"description":""},{"id":64,"parentId":3,"label":"树形字典","type":2,"viewId":10,"path":"/admin/dictionary","icon":"fa fa-database","closable":true,"opened":false,"newWindow":false,"external":false,"sort":1,"description":""},{"id":65,"label":"平台管理","type":1,"icon":"el-icon-s-platform","opened":true,"sort":1},{"id":66,"label":"帮助文档","type":1,"icon":"el-icon-question","opened":true,"sort":3}],"users":[{"id":1,"userName":"admin","password":"96E79218965EB72C92A549DD5A33112","name":"管理员","nickName":"管理员","avatar":"","remark":""},{"id":2,"userName":"xiaoxue","password":"96E79218965EB72C92A549DD5A33112","name":"小雪转中雪","nickName":"小雪转中雪","avatar":"avatar.gif","remark":""}],"roles":[{"id":1,"name":"管理员","description":"演示"},{"id":2,"name":"系统管理员","description":""}],"userRoles":[{"id":1,"userId":1,"roleId":1},{"id":2,"userId":2,"roleId":2}],"rolePermissions":[{"id":104,"roleId":1,"permissionId":1},{"id":66,"roleId":1,"permissionId":2},{"id":67,"roleId":1,"permissionId":3},{"id":68,"roleId":1,"permissionId":4},{"id":69,"roleId":1,"permissionId":5},{"id":70,"roleId":1,"permissionId":6},{"id":71,"roleId":1,"permissionId":7},{"id":72,"roleId":1,"permissionId":8},{"id":73,"roleId":1,"permissionId":9},{"id":74,"roleId":1,"permissionId":10},{"id":75,"roleId":1,"permissionId":11},{"id":76,"roleId":1,"permissionId":12},{"id":77,"roleId":1,"permissionId":13},{"id":78,"roleId":1,"permissionId":18},{"id":79,"roleId":1,"permissionId":19},{"id":80,"roleId":1,"permissionId":24},{"id":81,"roleId":1,"permissionId":25},{"id":82,"roleId":1,"permissionId":31},{"id":83,"roleId":1,"permissionId":32},{"id":84,"roleId":1,"permissionId":40},{"id":85,"roleId":1,"permissionId":41},{"id":86,"roleId":1,"permissionId":42},{"id":87,"roleId":1,"permissionId":43},{"id":88,"roleId":1,"permissionId":44},{"id":89,"roleId":1,"permissionId":45},{"id":90,"roleId":1,"permissionId":47},{"id":91,"roleId":1,"permissionId":48},{"id":92,"roleId":1,"permissionId":51},{"id":93,"roleId":1,"permissionId":52},{"id":98,"roleId":1,"permissionId":53},{"id":99,"roleId":1,"permissionId":54},{"id":100,"roleId":1,"permissionId":55},{"id":94,"roleId":1,"permissionId":56},{"id":95,"roleId":1,"permissionId":57},{"id":96,"roleId":1,"permissionId":63},{"id":97,"roleId":1,"permissionId":64},{"id":103,"roleId":1,"permissionId":65},{"id":131,"roleId":1,"permissionId":66},{"id":102,"roleId":2,"permissionId":1},{"id":2,"roleId":2,"permissionId":2},{"id":3,"roleId":2,"permissionId":3},{"id":4,"roleId":2,"permissionId":4},{"id":5,"roleId":2,"permissionId":5},{"id":6,"roleId":2,"permissionId":6},{"id":7,"roleId":2,"permissionId":7},{"id":8,"roleId":2,"permissionId":8},{"id":9,"roleId":2,"permissionId":9},{"id":10,"roleId":2,"permissionId":10},{"id":11,"roleId":2,"permissionId":11},{"id":12,"roleId":2,"permissionId":12},{"id":13,"roleId":2,"permissionId":13},{"id":14,"roleId":2,"permissionId":14},{"id":15,"roleId":2,"permissionId":15},{"id":16,"roleId":2,"permissionId":16},{"id":17,"roleId":2,"permissionId":17},{"id":18,"roleId":2,"permissionId":18},{"id":19,"roleId":2,"permissionId":19},{"id":20,"roleId":2,"permissionId":20},{"id":21,"roleId":2,"permissionId":21},{"id":22,"roleId":2,"permissionId":22},{"id":23,"roleId":2,"permissionId":23},{"id":24,"roleId":2,"permissionId":24},{"id":25,"roleId":2,"permissionId":25},{"id":26,"roleId":2,"permissionId":26},{"id":27,"roleId":2,"permissionId":27},{"id":28,"roleId":2,"permissionId":28},{"id":29,"roleId":2,"permissionId":29},{"id":30,"roleId":2,"permissionId":30},{"id":31,"roleId":2,"permissionId":31},{"id":32,"roleId":2,"permissionId":32},{"id":33,"roleId":2,"permissionId":33},{"id":34,"roleId":2,"permissionId":34},{"id":35,"roleId":2,"permissionId":35},{"id":36,"roleId":2,"permissionId":36},{"id":37,"roleId":2,"permissionId":37},{"id":38,"roleId":2,"permissionId":38},{"id":39,"roleId":2,"permissionId":39},{"id":40,"roleId":2,"permissionId":40},{"id":41,"roleId":2,"permissionId":41},{"id":42,"roleId":2,"permissionId":42},{"id":43,"roleId":2,"permissionId":43},{"id":44,"roleId":2,"permissionId":44},{"id":45,"roleId":2,"permissionId":45},{"id":46,"roleId":2,"permissionId":46},{"id":47,"roleId":2,"permissionId":47},{"id":48,"roleId":2,"permissionId":48},{"id":49,"roleId":2,"permissionId":49},{"id":50,"roleId":2,"permissionId":50},{"id":51,"roleId":2,"permissionId":51},{"id":52,"roleId":2,"permissionId":52},{"id":53,"roleId":2,"permissionId":53},{"id":54,"roleId":2,"permissionId":54},{"id":55,"roleId":2,"permissionId":55},{"id":56,"roleId":2,"permissionId":56},{"id":57,"roleId":2,"permissionId":57},{"id":58,"roleId":2,"permissionId":58},{"id":59,"roleId":2,"permissionId":59},{"id":60,"roleId":2,"permissionId":60},{"id":61,"roleId":2,"permissionId":61},{"id":62,"roleId":2,"permissionId":62},{"id":63,"roleId":2,"permissionId":63},{"id":64,"roleId":2,"permissionId":64},{"id":101,"roleId":2,"permissionId":65},{"id":132,"roleId":2,"permissionId":66}]} \ No newline at end of file +{"dictionaries":[],"apis":[{"id":1,"label":"接口管理","path":"api","description":""},{"id":2,"label":"授权管理","path":"auth","description":""},{"id":3,"label":"数据字典","path":"dictionary","description":""},{"id":5,"label":"权限管理","path":"permission","description":""},{"id":6,"label":"角色管理","path":"role","description":""},{"id":7,"label":"用户管理","path":"user","description":""},{"id":8,"label":"视图管理","path":"view","description":""},{"id":10,"parentId":1,"label":"查询单条接口","path":"/api/admin/api/get","httpMethods":"get","description":""},{"id":11,"parentId":1,"label":"查询全部接口","path":"/api/admin/api/getlist","httpMethods":"get","description":""},{"id":12,"parentId":1,"label":"查询分页接口","path":"/api/admin/api/getpage","httpMethods":"post","description":""},{"id":13,"parentId":1,"label":"新增接口","path":"/api/admin/api/add","httpMethods":"post","description":""},{"id":14,"parentId":1,"label":"修改接口","path":"/api/admin/api/update","httpMethods":"put","description":""},{"id":15,"parentId":1,"label":"删除接口","path":"/api/admin/api/softdelete","httpMethods":"delete","description":""},{"id":16,"parentId":1,"label":"批量删除接口","path":"/api/admin/api/batchsoftdelete","httpMethods":"put","description":""},{"id":17,"parentId":1,"label":"同步接口","path":"/api/admin/api/sync","httpMethods":"post","description":"支持新增和修改接口\r\n根据接口是否存在自动禁用和启用api"},{"id":18,"parentId":2,"label":"获取验证码","path":"/api/admin/auth/getverifycode","httpMethods":"get","description":""},{"id":19,"parentId":2,"label":"获取密钥","path":"/api/admin/auth/getpasswordencryptkey","httpMethods":"get","description":""},{"id":20,"parentId":2,"label":"查询用户信息","path":"/api/admin/auth/getuserinfo","httpMethods":"get","description":""},{"id":21,"parentId":2,"label":"用户登录","path":"/api/admin/auth/login","httpMethods":"post","description":"根据登录信息生成Token"},{"id":22,"parentId":3,"label":"查询单条数据字典","path":"/api/admin/dictionary/get","httpMethods":"get","description":""},{"id":23,"parentId":3,"label":"查询分页数据字典","path":"/api/admin/dictionary/getpage","httpMethods":"post","description":""},{"id":24,"parentId":3,"label":"新增数据字典","path":"/api/admin/dictionary/add","httpMethods":"post","description":""},{"id":25,"parentId":3,"label":"修改数据字典","path":"/api/admin/dictionary/update","httpMethods":"put","description":""},{"id":26,"parentId":3,"label":"删除数据字典","path":"/api/admin/dictionary/softdelete","httpMethods":"delete","description":""},{"id":31,"parentId":5,"label":"查询权限列表","path":"/api/admin/permission/getlist","httpMethods":"get","description":""},{"id":32,"parentId":5,"label":"查询单条分组","path":"/api/admin/permission/getgroup","httpMethods":"get","description":""},{"id":33,"parentId":5,"label":"查询单条菜单","path":"/api/admin/permission/getmenu","httpMethods":"get","description":""},{"id":34,"parentId":5,"label":"查询单条接口","path":"/api/admin/permission/getapi","httpMethods":"get","description":""},{"id":35,"parentId":5,"label":"查询角色权限-权限列表","path":"/api/admin/permission/getpermissionlist","httpMethods":"get","description":""},{"id":36,"parentId":5,"label":"查询角色权限","path":"/api/admin/permission/getrolepermissionlist","httpMethods":"get","description":""},{"id":37,"parentId":5,"label":"新增分组","path":"/api/admin/permission/addgroup","httpMethods":"post","description":""},{"id":38,"parentId":5,"label":"新增菜单","path":"/api/admin/permission/addmenu","httpMethods":"post","description":""},{"id":39,"parentId":5,"label":"新增接口","path":"/api/admin/permission/addapi","httpMethods":"post","description":""},{"id":40,"parentId":5,"label":"修改分组","path":"/api/admin/permission/updategroup","httpMethods":"put","description":""},{"id":41,"parentId":5,"label":"修改菜单","path":"/api/admin/permission/updatemenu","httpMethods":"put","description":""},{"id":42,"parentId":5,"label":"修改接口","path":"/api/admin/permission/updateapi","httpMethods":"put","description":""},{"id":43,"parentId":5,"label":"删除权限","path":"/api/admin/permission/softdelete","httpMethods":"delete","description":""},{"id":44,"parentId":5,"label":"保存角色权限","path":"/api/admin/permission/assign","httpMethods":"post","description":""},{"id":45,"parentId":6,"label":"查询单条角色","path":"/api/admin/role/get","httpMethods":"get","description":""},{"id":46,"parentId":6,"label":"查询分页角色","path":"/api/admin/role/getpage","httpMethods":"post","description":""},{"id":47,"parentId":6,"label":"新增角色","path":"/api/admin/role/add","httpMethods":"post","description":""},{"id":48,"parentId":6,"label":"修改角色","path":"/api/admin/role/update","httpMethods":"put","description":""},{"id":49,"parentId":6,"label":"删除角色","path":"/api/admin/role/softdelete","httpMethods":"delete","description":""},{"id":50,"parentId":6,"label":"批量删除角色","path":"/api/admin/role/batchsoftdelete","httpMethods":"put","description":""},{"id":51,"parentId":7,"label":"查询用户基本信息","path":"/api/admin/user/getbasic","httpMethods":"get","description":""},{"id":52,"parentId":7,"label":"查询单条用户","path":"/api/admin/user/get","httpMethods":"get","description":""},{"id":53,"parentId":7,"label":"查询分页用户","path":"/api/admin/user/getpage","httpMethods":"post","description":""},{"id":54,"parentId":7,"label":"新增用户","path":"/api/admin/user/add","httpMethods":"post","description":""},{"id":55,"parentId":7,"label":"修改用户","path":"/api/admin/user/update","httpMethods":"put","description":""},{"id":56,"parentId":7,"label":"删除用户","path":"/api/admin/user/softdelete","httpMethods":"delete","description":""},{"id":57,"parentId":7,"label":"批量删除用户","path":"/api/admin/user/batchsoftdelete","httpMethods":"put","description":""},{"id":58,"parentId":7,"label":"更新用户密码","path":"/api/admin/user/changepassword","httpMethods":"put","description":""},{"id":59,"parentId":7,"label":"更新用户基本信息","path":"/api/admin/user/updatebasic","httpMethods":"put","description":""},{"id":60,"parentId":8,"label":"查询单条视图","path":"/api/admin/view/get","httpMethods":"get","description":""},{"id":61,"parentId":8,"label":"查询全部视图","path":"/api/admin/view/getlist","httpMethods":"get","description":""},{"id":62,"parentId":8,"label":"查询分页视图","path":"/api/admin/view/getpage","httpMethods":"post","description":""},{"id":63,"parentId":8,"label":"新增视图","path":"/api/admin/view/add","httpMethods":"post","description":""},{"id":64,"parentId":8,"label":"修改视图","path":"/api/admin/view/update","httpMethods":"put","description":""},{"id":65,"parentId":8,"label":"删除视图","path":"/api/admin/view/softdelete","httpMethods":"delete","description":""},{"id":66,"parentId":8,"label":"批量删除视图","path":"/api/admin/view/batchsoftdelete","httpMethods":"put","description":""},{"id":67,"parentId":8,"label":"同步视图","path":"/api/admin/view/sync","httpMethods":"post","description":"支持新增和修改视图\r\n根据视图是否存在自动禁用和启用视图"},{"id":70,"label":"缓存管理","path":"cache","description":""},{"id":71,"parentId":70,"label":"获取缓存列表","path":"/api/admin/cache/list","httpMethods":"get","description":""},{"id":72,"parentId":70,"label":"清除缓存","path":"/api/admin/cache/clear","httpMethods":"delete","description":""},{"id":73,"label":"登录日志管理","path":"loginlog","description":""},{"id":74,"label":"操作日志管理","path":"oprationlog","description":""},{"id":75,"parentId":73,"label":"查询分页登录日志","path":"/api/admin/loginlog/getpage","httpMethods":"post","description":""},{"id":76,"parentId":74,"label":"查询分页操作日志","path":"/api/admin/oprationlog/getpage","httpMethods":"post","description":""},{"id":78,"label":"文档管理","path":"document","description":""},{"id":79,"parentId":78,"label":"查询文档列表","path":"/api/admin/document/getlist","httpMethods":"get","description":""},{"id":80,"parentId":78,"label":"查询单条分组","path":"/api/admin/document/getgroup","httpMethods":"get","description":""},{"id":81,"parentId":78,"label":"查询单条菜单","path":"/api/admin/document/getmenu","httpMethods":"get","description":""},{"id":82,"parentId":78,"label":"查询精简文档列表","path":"/api/admin/document/getplainlist","httpMethods":"get","description":""},{"id":83,"parentId":78,"label":"新增分组","path":"/api/admin/document/addgroup","httpMethods":"post","description":""},{"id":84,"parentId":78,"label":"新增菜单","path":"/api/admin/document/addmenu","httpMethods":"post","description":""},{"id":85,"parentId":78,"label":"修改分组","path":"/api/admin/document/updategroup","httpMethods":"put","description":""},{"id":86,"parentId":78,"label":"修改菜单","path":"/api/admin/document/updatemenu","httpMethods":"put","description":""},{"id":87,"parentId":78,"label":"删除文档","path":"/api/admin/document/softdelete","httpMethods":"delete","description":""},{"id":88,"parentId":78,"label":"查询单条文档内容","path":"/api/admin/document/getcontent","httpMethods":"get","description":""},{"id":89,"parentId":78,"label":"修改文档内容","path":"/api/admin/document/updatecontent","httpMethods":"put","description":""},{"id":90,"parentId":78,"label":"上传文档图片","path":"/api/admin/document/uploadimage","httpMethods":"post","description":""},{"id":91,"parentId":78,"label":"查询文档图片列表","path":"/api/admin/document/getimagelist","httpMethods":"get","description":""},{"id":92,"parentId":78,"label":"删除图片","path":"/api/admin/document/deleteimage","httpMethods":"delete","description":""},{"id":93,"parentId":7,"label":"上传头像","path":"/api/admin/user/avatarupload","httpMethods":"post","description":""},{"id":94,"parentId":5,"label":"查询单条权限点","path":"/api/admin/permission/getdot","httpMethods":"get","description":""},{"id":95,"parentId":5,"label":"新增权限点","path":"/api/admin/permission/adddot","httpMethods":"post","description":""},{"id":96,"parentId":5,"label":"修改权限点","path":"/api/admin/permission/updatedot","httpMethods":"put","description":""},{"id":97,"parentId":2,"label":"刷新Token","path":"/api/admin/auth/refresh","httpMethods":"get","description":"以旧换新"}],"views":[{"id":1,"parentId":2,"label":"首页","path":"/admin/home","sort":1},{"id":2,"label":"平台管理","path":"admin","sort":1},{"id":3,"parentId":14,"name":"user","label":"用户管理","path":"/admin/user","sort":1},{"id":4,"parentId":14,"name":"role","label":"角色管理","path":"/admin/role","sort":2},{"id":5,"parentId":14,"name":"api","label":"接口管理","path":"/admin/api","sort":3},{"id":6,"parentId":14,"name":"v","label":"视图管理","path":"/admin/view","sort":4},{"id":7,"parentId":14,"name":"permission","label":"权限管理","path":"/admin/permission","sort":5},{"id":8,"parentId":14,"name":"assign","label":"角色权限","path":"/admin/role-permission","sort":6},{"id":9,"parentId":2,"label":"系统配置","path":"config","sort":3},{"id":10,"parentId":9,"name":"dictonary","label":"数据字典","path":"/admin/dictionary","sort":1},{"id":11,"parentId":15,"name":"log","label":"操作日志","path":"/admin/opration-log","description":"","sort":2},{"id":12,"parentId":2,"label":"个人管理","path":"account","sort":5},{"id":13,"parentId":12,"name":"settings","label":"个人设置","path":"/account/settings","sort":1},{"id":14,"parentId":2,"label":"权限管理","path":"auth","description":"","sort":2},{"id":15,"parentId":2,"label":"日志管理","path":"logs","description":"","sort":4},{"id":16,"parentId":14,"label":"缓存管理","path":"/admin/cache","description":"","sort":7},{"id":17,"parentId":15,"label":"登录日志","path":"/admin/login-log","description":"","sort":1},{"id":18,"parentId":2,"label":"文档管理","path":"/admin/document","description":""}],"permissions":[{"id":1,"parentId":65,"label":"更新日志","type":2,"viewId":1,"path":"/","icon":"el-icon-notebook-2","closable":false,"opened":false,"newWindow":false,"external":false,"sort":1,"description":""},{"id":2,"parentId":65,"label":"权限管理","type":1,"path":"","icon":"fa fa-sitemap","closable":false,"opened":false,"sort":2,"description":""},{"id":3,"parentId":65,"label":"系统配置","type":1,"path":"","icon":"el-icon-s-platform","closable":false,"opened":false,"sort":3,"description":""},{"id":4,"label":"个人管理","type":1,"path":"","icon":"el-icon-s-custom","closable":false,"opened":false,"sort":2,"description":""},{"id":5,"parentId":66,"label":"Admin","type":2,"path":"https://zhontai.net","icon":"el-icon-s-promotion","closable":false,"newWindow":false,"external":false,"sort":5,"description":""},{"id":6,"parentId":66,"label":"FreeSql","type":2,"path":"https://github.com/dotnetcore/FreeSql/wiki","icon":"el-icon-s-promotion","closable":false,"newWindow":false,"external":false,"sort":6,"description":""},{"id":7,"parentId":2,"label":"用户管理","type":2,"viewId":3,"path":"/admin/user","closable":true,"opened":false,"newWindow":false,"external":true,"sort":1,"description":""},{"id":8,"parentId":2,"label":"角色管理","type":2,"viewId":4,"path":"/admin/role","icon":"","closable":true,"opened":false,"sort":2,"description":""},{"id":9,"parentId":2,"label":"接口管理","type":2,"viewId":5,"path":"/admin/api","closable":true,"opened":false,"sort":3,"description":""},{"id":10,"parentId":2,"label":"视图管理","type":2,"viewId":6,"path":"/admin/view","icon":"","closable":true,"sort":4,"description":""},{"id":11,"parentId":2,"label":"权限管理","type":2,"viewId":7,"path":"/admin/permission","closable":true,"opened":false,"sort":5,"description":""},{"id":12,"parentId":2,"label":"角色权限","type":2,"viewId":8,"path":"/admin/role-permisson","closable":true,"opened":false,"newWindow":false,"external":false,"sort":6,"description":""},{"id":13,"parentId":8,"label":"查询","code":"api:admin:role:getpage","type":3,"apiId":46,"path":" ","closable":false,"opened":false,"sort":1,"description":""},{"id":14,"parentId":8,"label":"新增","code":"api:admin:role:add","type":3,"apiId":47,"path":" ","closable":false,"opened":false,"sort":2,"description":""},{"id":15,"parentId":8,"label":"修改","code":"api:admin:role:update","type":3,"apiId":48,"path":" ","closable":false,"opened":false,"sort":3,"description":""},{"id":16,"parentId":8,"label":"删除","code":"api:admin:role:softdelete","type":3,"apiId":49,"path":" ","closable":true,"opened":false,"sort":4,"description":""},{"id":17,"parentId":8,"label":"批量删除","code":"api:admin:role:batchsoftdelete","type":3,"apiId":50,"path":" ","icon":"","closable":false,"opened":false,"sort":5,"description":""},{"id":18,"parentId":8,"label":"查询单条角色","code":"api:admin:role:get","type":3,"apiId":45,"sort":6,"description":""},{"id":19,"parentId":7,"label":"查询","code":"api:admin:user:getpage","type":3,"apiId":53,"path":"","closable":true,"opened":false,"sort":1,"description":""},{"id":20,"parentId":7,"label":"新增","code":"api:admin:user:add","type":3,"apiId":54,"path":"","closable":false,"opened":false,"sort":2,"description":""},{"id":21,"parentId":7,"label":"修改","code":"api:admin:user:update","type":3,"apiId":55,"path":"","closable":true,"opened":false,"sort":3,"description":""},{"id":22,"parentId":7,"label":"删除","code":"api:admin:user:softdelete","type":3,"apiId":56,"path":"","closable":true,"opened":false,"sort":4,"description":""},{"id":23,"parentId":7,"label":"批量删除","code":"api:admin:user:batchsoftdelete","type":3,"apiId":57,"path":"","icon":"","sort":5,"description":""},{"id":24,"parentId":7,"label":"查询单条用户","code":"api:admin:user:get","type":3,"apiId":52,"sort":6,"description":""},{"id":25,"parentId":9,"label":"查询","code":"api:admin:api:getlist","type":3,"apiId":11,"path":"","icon":"","sort":1,"description":""},{"id":26,"parentId":9,"label":"新增","code":"api:admin:api:add","type":3,"apiId":13,"path":" ","closable":false,"opened":false,"sort":2,"description":""},{"id":27,"parentId":9,"label":"修改","code":"api:admin:api:update","type":3,"apiId":14,"path":" ","closable":false,"opened":false,"sort":3,"description":""},{"id":28,"parentId":9,"label":"删除","code":"api:admin:api:softdelete","type":3,"apiId":15,"path":" ","closable":false,"opened":false,"sort":4,"description":""},{"id":29,"parentId":9,"label":"批量删除","code":"api:admin:api:batchsoftdelete","type":3,"apiId":16,"path":"","icon":"","sort":5,"description":""},{"id":30,"parentId":9,"label":"同步","code":"api:admin:api:sync","type":3,"apiId":17,"path":"","icon":"","sort":6,"description":""},{"id":31,"parentId":9,"label":"查询单条接口","code":"api:admin:api:get","type":3,"apiId":10,"sort":7,"description":""},{"id":32,"parentId":11,"label":"查询","code":"api:admin:permission:getlist","type":3,"apiId":31,"path":"","icon":"","sort":1,"description":""},{"id":33,"parentId":11,"label":"新增分组","code":"api:admin:permission:addgroup","type":3,"apiId":37,"sort":2,"description":""},{"id":34,"parentId":11,"label":"新增菜单","code":"api:admin:permission:addmenu","type":3,"apiId":38,"sort":3,"description":""},{"id":35,"parentId":11,"label":"新增接口","code":"api:admin:permission:addapi","type":3,"apiId":39,"sort":4,"description":""},{"id":36,"parentId":11,"label":"修改分组","code":"api:admin:permission:updategroup","type":3,"apiId":40,"sort":5,"description":""},{"id":37,"parentId":11,"label":"修改菜单","code":"api:admin:permission:updatemenu","type":3,"apiId":41,"sort":6,"description":""},{"id":38,"parentId":11,"label":"修改接口","code":"api:admin:permission:updateapi","type":3,"apiId":42,"sort":7,"description":""},{"id":39,"parentId":11,"label":"删除","code":"api:admin:permission:softdelete","type":3,"apiId":43,"path":" ","closable":false,"opened":false,"sort":8,"description":""},{"id":40,"parentId":11,"label":"查询单条分组","code":"api:admin:permission:getgroup","type":3,"apiId":32,"sort":9,"description":""},{"id":41,"parentId":11,"label":"查询单条菜单","code":"api:admin:permission:getmenu","type":3,"apiId":33,"sort":10,"description":""},{"id":42,"parentId":11,"label":"查询单条接口","code":"api:admin:permission:getapi","type":3,"apiId":34,"sort":11,"description":""},{"id":43,"parentId":12,"label":"查询角色","code":"api:admin:role:getpage","type":3,"apiId":46,"path":"","icon":"","sort":1,"description":""},{"id":44,"parentId":12,"label":"查询权限","code":"api:admin:permission:getpermissionlist","type":3,"apiId":35,"path":"","icon":"","sort":2,"description":""},{"id":45,"parentId":12,"label":"查询角色权限","code":"api:admin:permission:getrolepermissionlist","type":3,"apiId":36,"path":"","icon":"","sort":3,"description":""},{"id":46,"parentId":12,"label":"保存角色权限","code":"api:admin:permission:assign","type":3,"apiId":44,"path":" ","closable":false,"opened":false,"sort":4,"description":""},{"id":47,"parentId":4,"label":"个人设置","type":2,"viewId":13,"path":"/account/settings","icon":"el-icon-setting","closable":true,"opened":false,"newWindow":false,"external":false,"sort":1,"description":""},{"id":48,"parentId":47,"label":"查询基本信息","code":"api:admin:user:getbasic","type":3,"apiId":51,"path":"","icon":"","closable":false,"opened":false,"sort":1,"description":""},{"id":49,"parentId":47,"label":"更新基本信息","code":"api:admin:user:updatebasic","type":3,"apiId":59,"sort":2,"description":""},{"id":50,"parentId":47,"label":"更新密码","code":"api:admin:user:changepassword","type":3,"apiId":58,"path":" ","icon":"","closable":false,"opened":false,"sort":3,"description":""},{"id":51,"parentId":3,"label":"基础数据","type":1,"icon":"fa fa-database","opened":false,"sort":1},{"id":52,"parentId":64,"label":"查询","code":"api:admin:dictionary:getpage","type":3,"apiId":23,"path":" ","icon":"","closable":false,"opened":false,"sort":1,"description":""},{"id":53,"parentId":64,"label":"新增","code":"api:admin:dictionary:add","type":3,"apiId":24,"path":" ","icon":"","closable":false,"opened":false,"sort":2,"description":""},{"id":54,"parentId":64,"label":"修改","code":"api:admin:dictionary:update","type":3,"apiId":25,"path":" ","icon":"","closable":false,"opened":false,"sort":3,"description":""},{"id":55,"parentId":64,"label":"删除","code":"api:admin:dictionary:softdelete","type":3,"apiId":26,"path":" ","icon":"","closable":false,"opened":false,"sort":4,"description":""},{"id":56,"parentId":64,"label":"查询单条数据字典","code":"api:admin:dictionary:get","type":3,"apiId":22,"sort":5,"description":""},{"id":57,"parentId":10,"label":"查询","code":"api:admin:view:getlist","type":3,"apiId":61,"sort":1,"description":""},{"id":58,"parentId":10,"label":"新增","code":"api:admin:view:add","type":3,"apiId":63,"sort":2,"description":""},{"id":59,"parentId":10,"label":"修改","code":"api:admin:view:update","type":3,"apiId":64,"sort":3,"description":""},{"id":60,"parentId":10,"label":"删除","code":"api:admin:view:softdelete","type":3,"apiId":65,"sort":4,"description":""},{"id":61,"parentId":10,"label":"批量删除","code":"api:admin:view:batchsoftdelete","type":3,"apiId":66,"sort":5,"description":""},{"id":62,"parentId":10,"label":"同步","code":"api:admin:view:sync","type":3,"apiId":67,"sort":6,"description":""},{"id":63,"parentId":10,"label":"查询单条视图","code":"api:admin:view:get","type":3,"apiId":60,"sort":7,"description":""},{"id":64,"parentId":3,"label":"树形字典","type":2,"viewId":10,"path":"/admin/dictionary","icon":"fa fa-database","closable":true,"opened":false,"newWindow":false,"external":false,"sort":1,"description":""},{"id":65,"label":"平台管理","type":1,"icon":"el-icon-s-platform","opened":true,"sort":1},{"id":66,"label":"帮助文档","type":1,"icon":"el-icon-question","opened":true,"sort":3},{"id":67,"parentId":2,"label":"缓存管理","type":2,"viewId":16,"path":"/admin/cache","icon":"","closable":true,"newWindow":false,"external":false,"sort":7,"description":""},{"id":68,"parentId":67,"label":"获取缓存列表","code":"api:admin:cache:list","type":3,"apiId":71,"sort":1,"description":""},{"id":69,"parentId":67,"label":"清除缓存","code":"api:admin:cache:clear","type":3,"apiId":72,"sort":2,"description":""},{"id":70,"parentId":65,"label":"日志管理","type":1,"icon":"el-icon-notebook-2","opened":true,"sort":4},{"id":71,"parentId":70,"label":"登录日志","type":2,"viewId":17,"path":"/admin/login-log","icon":"","closable":true,"newWindow":false,"external":false,"sort":1,"description":""},{"id":72,"parentId":70,"label":"操作日志","type":2,"viewId":11,"path":"/admin/opration-log","icon":"","closable":true,"newWindow":false,"external":false,"sort":2,"description":""},{"id":73,"parentId":72,"label":"查询分页操作日志","code":"api:admin:oprationlog:getpage","type":3,"apiId":76,"sort":1,"description":""},{"id":74,"parentId":71,"label":"查询分页登录日志","code":"api:admin:loginlog:getpage","type":3,"apiId":75,"sort":1,"description":""},{"id":75,"parentId":66,"label":"文档管理","type":2,"viewId":18,"path":"/admin/document","icon":"el-icon-notebook-2","closable":true,"newWindow":false,"external":true,"sort":0,"description":""},{"id":76,"parentId":75,"label":"查询精简文档列表","code":"api:admin:document:getplainlist","type":3,"apiId":82,"sort":2,"description":""},{"id":77,"parentId":75,"label":"新增分组","code":"api:admin:document:addgroup","type":3,"apiId":83,"sort":5,"description":""},{"id":78,"parentId":75,"label":"新增菜单","code":"api:admin:document:addmenu","type":3,"apiId":84,"sort":6,"description":""},{"id":79,"parentId":75,"label":"修改分组","code":"api:admin:document:updategroup","type":3,"apiId":85,"sort":7,"description":""},{"id":80,"parentId":75,"label":"修改菜单","code":"api:admin:document:updatemenu","type":3,"apiId":86,"sort":8,"description":""},{"id":81,"parentId":75,"label":"删除文档","code":"api:admin:document:softdelete","type":3,"apiId":87,"sort":9,"description":""},{"id":82,"parentId":75,"label":"查询单条分组","code":"api:admin:document:getgroup","type":3,"apiId":80,"sort":3,"description":""},{"id":83,"parentId":75,"label":"查询单条菜单","code":"api:admin:document:getmenu","type":3,"apiId":81,"sort":4,"description":""},{"id":90,"parentId":75,"label":"查询文档列表","code":"api:admin:document:getlist","type":3,"apiId":79,"sort":1,"description":""},{"id":91,"parentId":75,"label":"查询单条文档内容","code":"api:admin:document:getcontent","type":3,"apiId":88,"sort":10,"description":""},{"id":92,"parentId":75,"label":"修改文档内容","code":"api:admin:document:updatecontent","type":3,"apiId":89,"sort":11,"description":""},{"id":93,"parentId":75,"label":"上传文档图片","code":"api:admin:document:uploadimage","type":3,"apiId":90,"sort":0,"description":""},{"id":94,"parentId":75,"label":"查询文档图片列表","code":"api:admin:document:getimagelist","type":3,"apiId":91,"sort":0,"description":""},{"id":95,"parentId":75,"label":"删除图片","code":"api:admin:document:deleteimage","type":3,"apiId":92,"sort":0,"description":""},{"id":96,"parentId":11,"label":"查询单条权限点","code":"api:admin:permission:getdot","type":3,"apiId":94,"sort":0,"description":""},{"id":97,"parentId":11,"label":"新增权限点","code":"api:admin:permission:adddot","type":3,"apiId":95,"sort":0,"description":""},{"id":98,"parentId":11,"label":"修改权限点","code":"api:admin:permission:updatedot","type":3,"apiId":96,"sort":0,"description":""}],"users":[{"id":1,"userName":"user","password":"96E79218965EB72C92A549DD5A33112","nickName":"平台用户","avatar":"","remark":""},{"id":2,"userName":"admin","password":"96E79218965EB72C92A549DD5A33112","nickName":"管理员","avatar":"","remark":""},{"id":3,"userName":"jack","password":"96E79218965EB72C92A549DD5A33112","nickName":"杰克"}],"roles":[{"id":1,"name":"平台用户","description":"演示"},{"id":2,"name":"系统管理员","description":""},{"id":3,"name":"文档管理员","description":""}],"userRoles":[{"id":10,"userId":1,"roleId":1},{"id":3,"userId":2,"roleId":2},{"id":9,"userId":3,"roleId":3}],"rolePermissions":[{"id":104,"roleId":1,"permissionId":1},{"id":66,"roleId":1,"permissionId":2},{"id":67,"roleId":1,"permissionId":3},{"id":68,"roleId":1,"permissionId":4},{"id":69,"roleId":1,"permissionId":5},{"id":70,"roleId":1,"permissionId":6},{"id":71,"roleId":1,"permissionId":7},{"id":72,"roleId":1,"permissionId":8},{"id":73,"roleId":1,"permissionId":9},{"id":74,"roleId":1,"permissionId":10},{"id":75,"roleId":1,"permissionId":11},{"id":76,"roleId":1,"permissionId":12},{"id":77,"roleId":1,"permissionId":13},{"id":78,"roleId":1,"permissionId":18},{"id":79,"roleId":1,"permissionId":19},{"id":80,"roleId":1,"permissionId":24},{"id":81,"roleId":1,"permissionId":25},{"id":82,"roleId":1,"permissionId":31},{"id":83,"roleId":1,"permissionId":32},{"id":84,"roleId":1,"permissionId":40},{"id":85,"roleId":1,"permissionId":41},{"id":86,"roleId":1,"permissionId":42},{"id":87,"roleId":1,"permissionId":43},{"id":88,"roleId":1,"permissionId":44},{"id":89,"roleId":1,"permissionId":45},{"id":90,"roleId":1,"permissionId":47},{"id":91,"roleId":1,"permissionId":48},{"id":92,"roleId":1,"permissionId":51},{"id":93,"roleId":1,"permissionId":52},{"id":98,"roleId":1,"permissionId":53},{"id":99,"roleId":1,"permissionId":54},{"id":100,"roleId":1,"permissionId":55},{"id":94,"roleId":1,"permissionId":56},{"id":95,"roleId":1,"permissionId":57},{"id":96,"roleId":1,"permissionId":63},{"id":97,"roleId":1,"permissionId":64},{"id":103,"roleId":1,"permissionId":65},{"id":131,"roleId":1,"permissionId":66},{"id":136,"roleId":1,"permissionId":67},{"id":137,"roleId":1,"permissionId":68},{"id":143,"roleId":1,"permissionId":70},{"id":144,"roleId":1,"permissionId":71},{"id":145,"roleId":1,"permissionId":72},{"id":147,"roleId":1,"permissionId":73},{"id":146,"roleId":1,"permissionId":74},{"id":156,"roleId":1,"permissionId":75},{"id":157,"roleId":1,"permissionId":76},{"id":160,"roleId":1,"permissionId":82},{"id":161,"roleId":1,"permissionId":83},{"id":163,"roleId":1,"permissionId":90},{"id":166,"roleId":1,"permissionId":91},{"id":168,"roleId":1,"permissionId":94},{"id":192,"roleId":1,"permissionId":96},{"id":102,"roleId":2,"permissionId":1},{"id":2,"roleId":2,"permissionId":2},{"id":3,"roleId":2,"permissionId":3},{"id":4,"roleId":2,"permissionId":4},{"id":5,"roleId":2,"permissionId":5},{"id":6,"roleId":2,"permissionId":6},{"id":7,"roleId":2,"permissionId":7},{"id":8,"roleId":2,"permissionId":8},{"id":9,"roleId":2,"permissionId":9},{"id":10,"roleId":2,"permissionId":10},{"id":11,"roleId":2,"permissionId":11},{"id":12,"roleId":2,"permissionId":12},{"id":13,"roleId":2,"permissionId":13},{"id":14,"roleId":2,"permissionId":14},{"id":15,"roleId":2,"permissionId":15},{"id":16,"roleId":2,"permissionId":16},{"id":17,"roleId":2,"permissionId":17},{"id":18,"roleId":2,"permissionId":18},{"id":19,"roleId":2,"permissionId":19},{"id":20,"roleId":2,"permissionId":20},{"id":21,"roleId":2,"permissionId":21},{"id":22,"roleId":2,"permissionId":22},{"id":23,"roleId":2,"permissionId":23},{"id":24,"roleId":2,"permissionId":24},{"id":25,"roleId":2,"permissionId":25},{"id":26,"roleId":2,"permissionId":26},{"id":27,"roleId":2,"permissionId":27},{"id":28,"roleId":2,"permissionId":28},{"id":29,"roleId":2,"permissionId":29},{"id":30,"roleId":2,"permissionId":30},{"id":31,"roleId":2,"permissionId":31},{"id":32,"roleId":2,"permissionId":32},{"id":33,"roleId":2,"permissionId":33},{"id":34,"roleId":2,"permissionId":34},{"id":35,"roleId":2,"permissionId":35},{"id":36,"roleId":2,"permissionId":36},{"id":37,"roleId":2,"permissionId":37},{"id":38,"roleId":2,"permissionId":38},{"id":39,"roleId":2,"permissionId":39},{"id":40,"roleId":2,"permissionId":40},{"id":41,"roleId":2,"permissionId":41},{"id":42,"roleId":2,"permissionId":42},{"id":43,"roleId":2,"permissionId":43},{"id":44,"roleId":2,"permissionId":44},{"id":45,"roleId":2,"permissionId":45},{"id":46,"roleId":2,"permissionId":46},{"id":47,"roleId":2,"permissionId":47},{"id":48,"roleId":2,"permissionId":48},{"id":49,"roleId":2,"permissionId":49},{"id":50,"roleId":2,"permissionId":50},{"id":51,"roleId":2,"permissionId":51},{"id":52,"roleId":2,"permissionId":52},{"id":53,"roleId":2,"permissionId":53},{"id":54,"roleId":2,"permissionId":54},{"id":55,"roleId":2,"permissionId":55},{"id":56,"roleId":2,"permissionId":56},{"id":57,"roleId":2,"permissionId":57},{"id":58,"roleId":2,"permissionId":58},{"id":59,"roleId":2,"permissionId":59},{"id":60,"roleId":2,"permissionId":60},{"id":61,"roleId":2,"permissionId":61},{"id":62,"roleId":2,"permissionId":62},{"id":63,"roleId":2,"permissionId":63},{"id":64,"roleId":2,"permissionId":64},{"id":101,"roleId":2,"permissionId":65},{"id":132,"roleId":2,"permissionId":66},{"id":133,"roleId":2,"permissionId":67},{"id":134,"roleId":2,"permissionId":68},{"id":135,"roleId":2,"permissionId":69},{"id":138,"roleId":2,"permissionId":70},{"id":139,"roleId":2,"permissionId":71},{"id":140,"roleId":2,"permissionId":72},{"id":142,"roleId":2,"permissionId":73},{"id":141,"roleId":2,"permissionId":74},{"id":148,"roleId":2,"permissionId":75},{"id":150,"roleId":2,"permissionId":76},{"id":151,"roleId":2,"permissionId":77},{"id":152,"roleId":2,"permissionId":78},{"id":153,"roleId":2,"permissionId":79},{"id":154,"roleId":2,"permissionId":80},{"id":155,"roleId":2,"permissionId":81},{"id":158,"roleId":2,"permissionId":82},{"id":159,"roleId":2,"permissionId":83},{"id":162,"roleId":2,"permissionId":90},{"id":165,"roleId":2,"permissionId":91},{"id":164,"roleId":2,"permissionId":92},{"id":167,"roleId":2,"permissionId":93},{"id":169,"roleId":2,"permissionId":94},{"id":170,"roleId":2,"permissionId":95},{"id":194,"roleId":2,"permissionId":96},{"id":193,"roleId":2,"permissionId":97},{"id":195,"roleId":2,"permissionId":98},{"id":173,"roleId":3,"permissionId":4},{"id":174,"roleId":3,"permissionId":47},{"id":189,"roleId":3,"permissionId":48},{"id":190,"roleId":3,"permissionId":49},{"id":191,"roleId":3,"permissionId":50},{"id":171,"roleId":3,"permissionId":66},{"id":172,"roleId":3,"permissionId":75},{"id":179,"roleId":3,"permissionId":76},{"id":182,"roleId":3,"permissionId":77},{"id":183,"roleId":3,"permissionId":78},{"id":184,"roleId":3,"permissionId":79},{"id":185,"roleId":3,"permissionId":80},{"id":186,"roleId":3,"permissionId":81},{"id":180,"roleId":3,"permissionId":82},{"id":181,"roleId":3,"permissionId":83},{"id":178,"roleId":3,"permissionId":90},{"id":187,"roleId":3,"permissionId":91},{"id":188,"roleId":3,"permissionId":92},{"id":176,"roleId":3,"permissionId":93},{"id":177,"roleId":3,"permissionId":94},{"id":175,"roleId":3,"permissionId":95}]} \ No newline at end of file diff --git a/Admin.Core/Db/DbHelper.cs b/Admin.Core/Db/DbHelper.cs index 9409d6b9f..b7caddb32 100644 --- a/Admin.Core/Db/DbHelper.cs +++ b/Admin.Core/Db/DbHelper.cs @@ -5,11 +5,11 @@ using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using FreeSql; +using FreeSql.Aop; using FreeSql.DataAnnotations; using Admin.Core.Common.Configs; using Admin.Core.Common.Helpers; using Admin.Core.Model.Admin; -using FreeSql.Aop; namespace Admin.Core.Db { @@ -27,14 +27,14 @@ public async static Task CreateDatabase(DbConfig dbConfig) return; } - var fsql = new FreeSqlBuilder() + var db = new FreeSqlBuilder() .UseConnectionString(dbConfig.Type, dbConfig.CreateDbConnectionString) .Build(); try { Console.WriteLine("\r\ncreate database started"); - await fsql.Ado.ExecuteNonQueryAsync(dbConfig.CreateDbSql); + await db.Ado.ExecuteNonQueryAsync(dbConfig.CreateDbSql); Console.WriteLine("create database succeed\r\n"); } catch (Exception e) @@ -46,7 +46,7 @@ public async static Task CreateDatabase(DbConfig dbConfig) /// /// 同步结构 /// - public static void SyncStructure(IFreeSql db) + public static void SyncStructure(IFreeSql db, string msg = null, DbConfig dbConfig = null) { //打印结构比对脚本 //var dDL = db.CodeFirst.GetComparisonDDLStatements(); @@ -62,8 +62,14 @@ public static void SyncStructure(IFreeSql db) //}; // 同步结构 - Console.WriteLine("\r\nsync structure started"); - db.CodeFirst.SyncStructure( + var dbType = dbConfig.Type.ToString(); + Console.WriteLine($"{(msg.NotNull() ? msg : $"sync {dbType} structure")} started"); + if(dbConfig.Type == DataType.Oracle) + { + db.CodeFirst.IsSyncStructureToUpper = true; + } + db.CodeFirst.SyncStructure(new Type[] + { typeof(DictionaryEntity), typeof(ApiEntity), typeof(ViewEntity), @@ -71,22 +77,32 @@ public static void SyncStructure(IFreeSql db) typeof(UserEntity), typeof(RoleEntity), typeof(UserRoleEntity), - typeof(RolePermissionEntity) - ); - Console.WriteLine("sync structure succeed\r\n"); + typeof(RolePermissionEntity), + typeof(OprationLogEntity), + typeof(LoginLogEntity), + typeof(DocumentEntity), + typeof(DocumentImageEntity) + }); + Console.WriteLine($"{(msg.NotNull() ? msg : $"sync {dbType} structure")} succeed\r\n"); } /// - /// 初始化数据 + /// 初始化数据表数据 /// /// /// /// + /// + /// /// - private static async Task InitData(IFreeSql db,T[] data) where T : class + private static async Task InitDtData( + IFreeSql db, + T[] data, + System.Data.Common.DbTransaction tran, + DbConfig dbConfig = null + ) where T : class { - var table = typeof(T).GetCustomAttributes(true) - .FirstOrDefault(x => x.GetType() == typeof(TableAttribute)) as TableAttribute; + var table = typeof(T).GetCustomAttributes(typeof(TableAttribute),false).FirstOrDefault() as TableAttribute; var tableName = table.Name; try @@ -95,8 +111,24 @@ private static async Task InitData(IFreeSql db,T[] data) where T : class { if (data?.Length > 0) { - await db.Insert().AppendData(data).ExecuteAffrowsAsync(); - Console.WriteLine($"table:{tableName} sync data succeed"); + var insert = db.Insert(); + + if(tran != null) + { + insert = insert.WithTransaction(tran); + } + + if(dbConfig.Type == DataType.SqlServer) + { + var insrtSql = insert.AppendData(data).InsertIdentity().ToSql(); + await db.Ado.ExecuteNonQueryAsync($"SET IDENTITY_INSERT {tableName} ON\n {insrtSql} \nSET IDENTITY_INSERT {tableName} OFF"); + } + else + { + await insert.AppendData(data).InsertIdentity().ExecuteAffrowsAsync(); + } + + Console.WriteLine($"table: {tableName} sync data succeed"); } else { @@ -126,10 +158,10 @@ private static void SyncDataAuditValue(object s, AuditValueEventArgs e) switch (e.Property.Name) { case "CreatedUserId": - e.Value = 1; + e.Value = 2; break; case "CreatedUserName": - e.Value = "小雪转中雪"; + e.Value = "admin"; break; } } @@ -138,10 +170,10 @@ private static void SyncDataAuditValue(object s, AuditValueEventArgs e) switch (e.Property.Name) { case "ModifiedUserId": - e.Value = 1; + e.Value = 2; break; case "ModifiedUserName": - e.Value = "小雪转中雪"; + e.Value = "admin"; break; } } @@ -151,25 +183,37 @@ private static void SyncDataAuditValue(object s, AuditValueEventArgs e) /// 同步数据 /// /// - public static async Task SyncData(IFreeSql db) + public static async Task SyncData(IFreeSql db, DbConfig dbConfig = null) { try { - Console.WriteLine("\r\nsync data started"); + //db.Aop.CurdBefore += (s, e) => + //{ + // Console.WriteLine($"{e.Sql}\r\n"); + //}; - db.Aop.AuditValue += SyncDataAuditValue; + Console.WriteLine("sync data started"); - var filePath = Path.Combine(Directory.GetCurrentDirectory(), @"Db\Data\data.json"); + db.Aop.AuditValue += SyncDataAuditValue; + + var filePath = Path.Combine(AppContext.BaseDirectory, "Db/Data/data.json").ToPath(); var jsonData = FileHelper.ReadFile(filePath); var data = JsonConvert.DeserializeObject(jsonData); - await InitData(db, data.Dictionaries); - await InitData(db, data.Apis); - await InitData(db, data.Views); - await InitData(db, data.Permissions); - await InitData(db, data.Users); - await InitData(db, data.Roles); - await InitData(db, data.UserRoles); - await InitData(db, data.RolePermissions); + + using (var uow = db.CreateUnitOfWork()) + using (var tran = uow.GetOrBeginTransaction()) + { + await InitDtData(db, data.Dictionaries, tran, dbConfig); + await InitDtData(db, data.Apis, tran, dbConfig); + await InitDtData(db, data.Views, tran, dbConfig); + await InitDtData(db, data.Permissions, tran, dbConfig); + await InitDtData(db, data.Users, tran, dbConfig); + await InitDtData(db, data.Roles, tran, dbConfig); + await InitDtData(db, data.UserRoles, tran, dbConfig); + await InitDtData(db, data.RolePermissions, tran, dbConfig); + + uow.Commit(); + } db.Aop.AuditValue -= SyncDataAuditValue; @@ -240,6 +284,7 @@ public static async Task GenerateSimpleJsonData(IFreeSql db) a.Id, a.ParentId, a.Label, + a.Code, a.Type, a.ViewId, a.ApiId, @@ -260,7 +305,6 @@ public static async Task GenerateSimpleJsonData(IFreeSql db) a.Id, a.UserName, a.Password, - a.Name, a.NickName, a.Avatar, a.Status, @@ -322,7 +366,7 @@ public static async Task GenerateSimpleJsonData(IFreeSql db) //Formatting.Indented, settings ); - var filePath = Path.Combine(Directory.GetCurrentDirectory(), @"Db\Data\data.json"); + var filePath = Path.Combine(Directory.GetCurrentDirectory(), "Db/Data/data.json").ToPath(); FileHelper.WriteFile(filePath, jsonData); #endregion @@ -333,7 +377,5 @@ public static async Task GenerateSimpleJsonData(IFreeSql db) throw new Exception($"generate data failed。\n{ex.Message}\r\n"); } } - - } } diff --git a/Admin.Core/Db/ServiceCollectionExtensions.cs b/Admin.Core/Db/ServiceCollectionExtensions.cs index 77e2eaef0..a857ee52f 100644 --- a/Admin.Core/Db/ServiceCollectionExtensions.cs +++ b/Admin.Core/Db/ServiceCollectionExtensions.cs @@ -1,11 +1,12 @@ using System; +using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using FreeSql; using Admin.Core.Common.Configs; using Admin.Core.Common.Helpers; -using Admin.Core.Model; using Admin.Core.Common.Auth; +using Admin.Core.Common.BaseModel; namespace Admin.Core.Db { @@ -16,7 +17,7 @@ public static class ServiceCollectionExtensions /// /// /// - public async static void AddDb(this IServiceCollection services, IHostEnvironment env) + public async static Task AddDb(this IServiceCollection services, IHostEnvironment env) { var dbConfig = new ConfigHelper().Get("dbconfig", env.EnvironmentName); @@ -45,18 +46,22 @@ public async static void AddDb(this IServiceCollection services, IHostEnvironmen #endregion var fsql = freeSqlBuilder.Build(); + //fsql.GlobalFilter.Apply("SoftDelete", a => a.IsDeleted == false); + services.AddFreeRepository(filter => filter.Apply("SoftDelete", a => a.IsDeleted == false)); + services.AddScoped(); + services.AddSingleton(fsql); #region 初始化数据库 //同步结构 if (dbConfig.SyncStructure) { - DbHelper.SyncStructure(fsql); + DbHelper.SyncStructure(fsql, dbConfig: dbConfig); } //同步数据 if (dbConfig.SyncData) { - await DbHelper.SyncData(fsql); + await DbHelper.SyncData(fsql, dbConfig); } #endregion @@ -79,20 +84,24 @@ public async static void AddDb(this IServiceCollection services, IHostEnvironmen #region 审计数据 //计算服务器时间 //var serverTime = fsql.Select().Limit(1).First(a => DateTime.local); - //var timeOffset = DateTime.UtcNow.Subtract(serverTime); + //var timeOffset = DateTime.UtcNow.Subtract(serverTime); + var user = services.BuildServiceProvider().GetService(); fsql.Aop.AuditValue += (s, e) => { - var user = services.BuildServiceProvider().GetService(); + if (user == null || user.Id <= 0) + { + return; + } if (e.AuditValueType == FreeSql.Aop.AuditValueType.Insert) { switch (e.Property.Name) { case "CreatedUserId": - e.Value = user?.Id; + e.Value = user.Id; break; case "CreatedUserName": - e.Value = user?.Name; + e.Value = user.Name; break; //case "CreatedTime": // e.Value = DateTime.Now.Subtract(timeOffset); @@ -104,10 +113,10 @@ public async static void AddDb(this IServiceCollection services, IHostEnvironmen switch (e.Property.Name) { case "ModifiedUserId": - e.Value = user?.Id; + e.Value = user.Id; break; case "ModifiedUserName": - e.Value = user?.Name; + e.Value = user.Name; break; //case "ModifiedTime": // e.Value = DateTime.Now.Subtract(timeOffset); @@ -116,10 +125,6 @@ public async static void AddDb(this IServiceCollection services, IHostEnvironmen } }; #endregion - - services.AddSingleton(fsql); - services.AddFreeRepository(filter => filter.Apply("SoftDelete", a => a.IsDeleted == false)); - services.AddScoped(sp => sp.GetRequiredService().CreateUnitOfWork()); #endregion } } diff --git a/Admin.Core/Extensions/RateLimitServiceCollectionExtensions.cs b/Admin.Core/Extensions/RateLimitServiceCollectionExtensions.cs new file mode 100644 index 000000000..e9ecaf9fa --- /dev/null +++ b/Admin.Core/Extensions/RateLimitServiceCollectionExtensions.cs @@ -0,0 +1,43 @@ +using AspNetCoreRateLimit; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Caching.Distributed; +using Microsoft.Extensions.Caching.Redis; +using Admin.Core.Common.Configs; + +namespace Admin.Core.Extensions +{ + public static class RateLimitServiceCollectionExtensions + { + /// + /// 添加Ip限流 + /// + /// + /// + /// + public static void AddIpRateLimit(this IServiceCollection services, IConfiguration configuration, CacheConfig cacheConfig) + { + #region IP限流 + services.Configure(configuration.GetSection("IpRateLimiting")); + services.Configure(configuration.GetSection("IpRateLimitPolicies")); + + if (cacheConfig.TypeRateLimit == Common.Cache.CacheType.Redis) + { + //redis + var redisRateLimit = new CSRedis.CSRedisClient(cacheConfig.Redis.ConnectionStringRateLimit); + services.AddSingleton(new CSRedisCache(redisRateLimit)); + services.AddSingleton(); + services.AddSingleton(); + } + else + { + //内存 + services.AddMemoryCache(); + services.AddSingleton(); + services.AddSingleton(); + } + services.AddSingleton(); + #endregion + } + } +} diff --git a/Admin.Core/Extensions/UploadConfigApplicationBuilderExtensions.cs b/Admin.Core/Extensions/UploadConfigApplicationBuilderExtensions.cs new file mode 100644 index 000000000..64513517a --- /dev/null +++ b/Admin.Core/Extensions/UploadConfigApplicationBuilderExtensions.cs @@ -0,0 +1,36 @@ +using System.IO; +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.DependencyInjection; +using Admin.Core.Common.Configs; +using Microsoft.Extensions.FileProviders; + +namespace Admin.Core.Extensions +{ + public static class UploadConfigApplicationBuilderExtensions + { + private static void UseFileUploadConfig(IApplicationBuilder app, FileUploadConfig config) + { + if (!Directory.Exists(config.UploadPath)) + { + Directory.CreateDirectory(config.UploadPath); + } + + app.UseStaticFiles(new StaticFileOptions() + { + RequestPath = config.RequestPath, + FileProvider = new PhysicalFileProvider(config.UploadPath) + }); + } + + public static IApplicationBuilder UseUploadConfig(this IApplicationBuilder app) + { + var uploadConfig = app.ApplicationServices.GetRequiredService>(); + UseFileUploadConfig(app, uploadConfig.Value.Avatar); + UseFileUploadConfig(app, uploadConfig.Value.Document); + + return app; + } + } + +} diff --git a/Admin.Core/Filters/GlobalExceptionFilter.cs b/Admin.Core/Filters/AdminExceptionFilter.cs similarity index 65% rename from Admin.Core/Filters/GlobalExceptionFilter.cs rename to Admin.Core/Filters/AdminExceptionFilter.cs index d48359747..3835c3c89 100644 --- a/Admin.Core/Filters/GlobalExceptionFilter.cs +++ b/Admin.Core/Filters/AdminExceptionFilter.cs @@ -1,22 +1,23 @@ -using System; -using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Hosting; -using Admin.Core.Model.Output; using Microsoft.Extensions.Logging; +using Admin.Core.Common.Output; +using System.Threading.Tasks; +using Admin.Core.Common.Extensions; namespace Admin.Core.Filters { /// - /// 全局异常错误过滤 + /// Admin异常错误过滤 /// - public class GlobalExceptionFilter : IExceptionFilter + public class AdminExceptionFilter : IExceptionFilter, IAsyncExceptionFilter { private readonly IWebHostEnvironment _env; - private readonly ILogger _logger; + private readonly ILogger _logger; - public GlobalExceptionFilter(IWebHostEnvironment env, ILogger logger) + public AdminExceptionFilter(IWebHostEnvironment env, ILogger logger) { _env = env; _logger = logger; @@ -27,7 +28,7 @@ public void OnException(ExceptionContext context) string message; if (_env.IsProduction()) { - message = Enums.StatusCodes.Status500InternalServerError.ToDescriptionOrString(); + message = Enums.StatusCodes.Status500InternalServerError.ToDescription(); } else { @@ -35,11 +36,15 @@ public void OnException(ExceptionContext context) } _logger.LogError(context.Exception,""); - var data = ResponseOutput.NotOk(message); - context.Result = new InternalServerErrorResult(data); } + + public Task OnExceptionAsync(ExceptionContext context) + { + OnException(context); + return Task.CompletedTask; + } } public class InternalServerErrorResult : ObjectResult { diff --git a/Admin.Core/Filters/LogActionFilter.cs b/Admin.Core/Filters/LogActionFilter.cs new file mode 100644 index 000000000..63f40694d --- /dev/null +++ b/Admin.Core/Filters/LogActionFilter.cs @@ -0,0 +1,28 @@ +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Filters; +using Admin.Core.Logs; +using Admin.Core.Attributes; + +namespace Admin.Core.Filters +{ + public class LogActionFilter : IAsyncActionFilter + { + private readonly ILogHandler _logHandler; + + public LogActionFilter(ILogHandler logHandler) + { + _logHandler = logHandler; + } + + public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + if (context.ActionDescriptor.EndpointMetadata.Any(m => m.GetType() == typeof(NoOprationLogAttribute))) + { + return next(); + } + + return _logHandler.LogAsync(context, next); + } + } +} diff --git a/Admin.Core/Logs/ApiHelper.cs b/Admin.Core/Logs/ApiHelper.cs new file mode 100644 index 000000000..159712a4e --- /dev/null +++ b/Admin.Core/Logs/ApiHelper.cs @@ -0,0 +1,63 @@ +using System; +using System.IO; +using System.Linq; +using System.Collections.Generic; +using Newtonsoft.Json; +using Admin.Core.Common.Helpers; +using Admin.Core.Db; +using Admin.Core.Common.Attributes; + + +namespace Admin.Core.Logs +{ + /// + /// Api帮助类 + /// + [SingleInstance] + public class ApiHelper + { + private List _apis; + private static readonly object _lockObject = new object(); + + public List GetApis() + { + if (_apis != null && _apis.Any()) + return _apis; + + lock (_lockObject) + { + if (_apis != null && _apis.Any()) + return _apis; + + _apis = new List(); + var filePath = Path.Combine(AppContext.BaseDirectory, "Db/Data/data.json").ToPath(); + var jsonData = FileHelper.ReadFile(filePath); + var apis = JsonConvert.DeserializeObject(jsonData).Apis; + foreach (var api in apis) + { + var parentLabel = apis.FirstOrDefault(a => a.Id == api.ParentId)?.Label; + + _apis.Add(new ApiHelperDto + { + Label = parentLabel.NotNull() ? $"{parentLabel} / {api.Label}" : api.Label, + Path = api.Path?.ToLower().Trim('/') + }); + } + + return _apis; + } + } + } + + public class ApiHelperDto + { + /// + /// 接口名称 + /// + public string Label { get; set; } + /// + /// 接口地址 + /// + public string Path { get; set; } + } +} diff --git a/Admin.Core/Logs/ILogHandler.cs b/Admin.Core/Logs/ILogHandler.cs new file mode 100644 index 000000000..d8f191a22 --- /dev/null +++ b/Admin.Core/Logs/ILogHandler.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Filters; + + +namespace Admin.Core.Logs +{ + /// + /// 操作日志处理接口 + /// + public interface ILogHandler + { + /// + /// 写操作日志 + /// + /// + /// + /// + Task LogAsync(ActionExecutingContext context, ActionExecutionDelegate next); + } +} diff --git a/Admin.Core/Logs/LogHandler.cs b/Admin.Core/Logs/LogHandler.cs new file mode 100644 index 000000000..fd6bc6b3f --- /dev/null +++ b/Admin.Core/Logs/LogHandler.cs @@ -0,0 +1,72 @@ +using System; +using System.Linq; +using System.Diagnostics; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; +using Admin.Core.Common.Output; +using Admin.Core.Service.Admin.OprationLog; +using Admin.Core.Service.Admin.OprationLog.Input; +//using Newtonsoft.Json; + +namespace Admin.Core.Logs +{ + /// + /// 操作日志处理 + /// + public class LogHandler : ILogHandler + { + private readonly ILogger _logger; + private readonly ApiHelper _apiHelper; + private readonly IOprationLogService _oprationLogService; + + public LogHandler( + ILogger logger, + ApiHelper apiHelper, + IOprationLogService oprationLogService + ) + { + _logger = logger; + _apiHelper = apiHelper; + _oprationLogService = oprationLogService; + } + + public async Task LogAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + var sw = new Stopwatch(); + sw.Start(); + var actionExecutedContext = await next(); + sw.Stop(); + + //操作参数 + //var args = JsonConvert.SerializeObject(context.ActionArguments); + //操作结果 + //var result = JsonConvert.SerializeObject(actionResult?.Value); + + try + { + var input = new OprationLogAddInput + { + ApiMethod = context.HttpContext.Request.Method.ToLower(), + ApiPath = context.ActionDescriptor.AttributeRouteInfo.Template.ToLower(), + ElapsedMilliseconds = sw.ElapsedMilliseconds + }; + + if (actionExecutedContext.Result is ObjectResult result && result.Value is IResponseOutput res) + { + input.Status = res.Success; + input.Msg = res.Msg; + } + + input.ApiLabel = _apiHelper.GetApis().FirstOrDefault(a => a.Path == input.ApiPath)?.Label; + + await _oprationLogService.AddAsync(input); + } + catch (Exception ex) + { + _logger.LogError("操作日志插入异常:{@ex}", ex); + } + } + } +} diff --git a/Admin.Core/Program.cs b/Admin.Core/Program.cs index 804623be5..e5665ca99 100644 --- a/Admin.Core/Program.cs +++ b/Admin.Core/Program.cs @@ -2,26 +2,41 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Autofac.Extensions.DependencyInjection; +using Microsoft.Extensions.Configuration; +using NLog; using NLog.Web; -//using NLog; -//using NLog.Extensions.Logging; +using Autofac.Extensions.DependencyInjection; using Admin.Core.Common.Helpers; -using LogLevel = Microsoft.Extensions.Logging.LogLevel; using Admin.Core.Common.Configs; -//using EnvironmentName = Microsoft.AspNetCore.Hosting.EnvironmentName; +using LogLevel = Microsoft.Extensions.Logging.LogLevel; +using System.Threading.Tasks; +//using NLog.Extensions.Logging; namespace Admin.Core { public class Program { - public static void Main(string[] args) + private static AppConfig appConfig = new ConfigHelper().Get("appconfig") ?? new AppConfig(); + public static async Task Main(string[] args) { - Console.WriteLine("launching..."); - var host = CreateHostBuilder(args).Build(); - var appConfig = new ConfigHelper().Get("appconfig") ?? new AppConfig(); - Console.WriteLine($"{appConfig.Urls}\r\n"); - host.Run(); + var logger = LogManager.GetCurrentClassLogger(); + try + { + Console.WriteLine("launching..."); + var host = CreateHostBuilder(args).Build(); + Console.WriteLine($"{string.Join("\r\n", appConfig.Urls)}\r\n"); + await host.RunAsync(); + return 0; + } + catch (Exception ex) + { + logger.Error(ex, "Stopped program because of exception"); + return 1; + } + finally + { + LogManager.Shutdown(); + } } public static IHostBuilder CreateHostBuilder(string[] args) @@ -30,15 +45,24 @@ public static IHostBuilder CreateHostBuilder(string[] args) //var logConfig = new ConfigHelper().Load("logconfig", reloadOnChange: true).GetSection("nLog"); //LogManager.Configuration = new NLogLoggingConfiguration(logConfig); - var appConfig = new ConfigHelper().Get("appconfig") ?? new AppConfig(); - return Host.CreateDefaultBuilder(args) .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureWebHostDefaults(webBuilder => { webBuilder - //.UseEnvironment(EnvironmentName.Production) + //.UseEnvironment(Environments.Production) .UseStartup() + .ConfigureAppConfiguration((host, config) => + { + if (appConfig.RateLimit) + { + config.AddJsonFile("./configs/ratelimitconfig.json", optional: false, reloadOnChange: true) +#if DEBUG + .AddJsonFile("./configs/ratelimitconfig.Development.json", true) +#endif + ; + } + }) .UseUrls(appConfig.Urls); }) .ConfigureLogging(logging => diff --git a/Admin.Core/Properties/launchSettings.json b/Admin.Core/Properties/launchSettings.json index a900e557d..606c18ab9 100644 --- a/Admin.Core/Properties/launchSettings.json +++ b/Admin.Core/Properties/launchSettings.json @@ -7,7 +7,7 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, - "applicationUrl": "http://localhost:8081" + "applicationUrl": "http://localhost:8000" } } } \ No newline at end of file diff --git a/Admin.Core/Startup.cs b/Admin.Core/Startup.cs index f5e5ea7ff..23960afa3 100644 --- a/Admin.Core/Startup.cs +++ b/Admin.Core/Startup.cs @@ -12,9 +12,9 @@ using Microsoft.OpenApi.Models; using Microsoft.IdentityModel.Tokens; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.PlatformAbstractions; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using Autofac; @@ -24,58 +24,152 @@ //using FluentValidation.AspNetCore; using Admin.Core.Common.Helpers; using Admin.Core.Common.Configs; -using Admin.Core.Common.Auth; using Admin.Core.Auth; using Admin.Core.Enums; using Admin.Core.Filters; using Admin.Core.Db; using Admin.Core.Common.Cache; -using PermissionHandler = Admin.Core.Auth.PermissionHandler; using Admin.Core.Aop; +using Admin.Core.Logs; +using Admin.Core.Extensions; +using Admin.Core.Common.Attributes; +using Admin.Core.Common.Auth; +using AspNetCoreRateLimit; +using IdentityServer4.AccessTokenValidation; namespace Admin.Core { public class Startup { + private static string basePath => AppContext.BaseDirectory; + private readonly IConfiguration _configuration; private readonly IHostEnvironment _env; + private readonly ConfigHelper _configHelper; private readonly AppConfig _appConfig; - private static string basePath => PlatformServices.Default.Application.ApplicationBasePath; - public Startup(IWebHostEnvironment env) + public Startup(IConfiguration configuration, IWebHostEnvironment env) { + _configuration = configuration; _env = env; - _appConfig = new ConfigHelper().Get("appconfig",env.EnvironmentName) ?? new AppConfig(); + _configHelper = new ConfigHelper(); + _appConfig = _configHelper.Get("appconfig", env.EnvironmentName) ?? new AppConfig(); } public void ConfigureServices(IServiceCollection services) { + //用户信息 + services.AddSingleton(); + if (_appConfig.IdentityServer.Enable) + { + //is4 + services.TryAddSingleton(); + } + else + { + //jwt + services.TryAddSingleton(); + } + + //数据库 + services.AddDb(_env).Wait(); + + //应用配置 + services.AddSingleton(_appConfig); + + //上传配置 + var uploadConfig = _configHelper.Load("uploadconfig", _env.EnvironmentName, true); + services.Configure(uploadConfig); + #region AutoMapper 自动映射 - var ServiceDll = Path.Combine(basePath, "Admin.Core.Service.dll"); - var serviceAssembly = Assembly.LoadFrom(ServiceDll); + var serviceAssembly = Assembly.Load("Admin.Core.Service"); services.AddAutoMapper(serviceAssembly); #endregion #region Cors 跨域 - services.AddCors(c => + services.AddCors(options => { - c.AddPolicy("Limit", policy => + options.AddPolicy("Limit", policy => + { + policy + .WithOrigins(_appConfig.CorUrls) + .AllowAnyHeader() + .AllowAnyMethod() + .AllowCredentials(); + }); + + /* + //浏览器会发起2次请求,使用OPTIONS发起预检请求,第二次才是api异步请求 + options.AddPolicy("All", policy => { policy - .WithOrigins(_appConfig.Urls) + .AllowAnyOrigin() + .SetPreflightMaxAge(new TimeSpan(0, 10, 0)) .AllowAnyHeader() - .AllowAnyMethod(); + .AllowAnyMethod() + .AllowCredentials(); }); + */ }); #endregion + #region 身份认证授权 + var jwtConfig = _configHelper.Get("jwtconfig", _env.EnvironmentName); + services.TryAddSingleton(jwtConfig); + + if (_appConfig.IdentityServer.Enable) + { + //is4 + services.AddAuthentication(options => + { + options.DefaultScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = nameof(ResponseAuthenticationHandler); //401 + options.DefaultForbidScheme = nameof(ResponseAuthenticationHandler); //403 + }) + .AddIdentityServerAuthentication(options => + { + options.Authority = _appConfig.IdentityServer.Url; + options.RequireHttpsMetadata = false; + options.SupportedTokens = SupportedTokens.Jwt; + options.ApiName = "admin.server.api"; + options.ApiSecret = "secret"; + }) + .AddScheme(nameof(ResponseAuthenticationHandler), o => { }); + } + else + { + //jwt + services.AddAuthentication(options => + { + options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = nameof(ResponseAuthenticationHandler); //401 + options.DefaultForbidScheme = nameof(ResponseAuthenticationHandler); //403 + }) + .AddJwtBearer(options => + { + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidIssuer = jwtConfig.Issuer, + ValidAudience = jwtConfig.Audience, + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecurityKey)), + ClockSkew = TimeSpan.Zero + }; + }) + .AddScheme(nameof(ResponseAuthenticationHandler), o => { }); + } + #endregion + #region Swagger Api文档 if (_env.IsDevelopment() || _appConfig.Swagger) { - services.AddSwaggerGen(c => + services.AddSwaggerGen(options => { typeof(ApiVersion).GetEnumNames().ToList().ForEach(version => { - c.SwaggerDoc(version, new OpenApiInfo + options.SwaggerDoc(version, new OpenApiInfo { Version = version, Title = "Admin.Core" @@ -83,82 +177,103 @@ public void ConfigureServices(IServiceCollection services) //c.OrderActionsBy(o => o.RelativePath); }); - var xmlModelPath = Path.Combine(basePath, "Admin.Core.Model.xml"); - c.IncludeXmlComments(xmlModelPath); - var xmlPath = Path.Combine(basePath, "Admin.Core.xml"); - c.IncludeXmlComments(xmlPath, true); + options.IncludeXmlComments(xmlPath, true); + + var xmlCommonPath = Path.Combine(basePath, "Admin.Core.Common.xml"); + options.IncludeXmlComments(xmlCommonPath, true); + + var xmlModelPath = Path.Combine(basePath, "Admin.Core.Model.xml"); + options.IncludeXmlComments(xmlModelPath); var xmlServicesPath = Path.Combine(basePath, "Admin.Core.Service.xml"); - c.IncludeXmlComments(xmlServicesPath); + options.IncludeXmlComments(xmlServicesPath); - //添加设置Token的按钮 - c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + #region 添加设置Token的按钮 + if (_appConfig.IdentityServer.Enable) { - Description = "Value: Bearer {token}", - Name = "Authorization", - In = ParameterLocation.Header, - Type = SecuritySchemeType.ApiKey, - Scheme = "Bearer" - }); + //添加Jwt验证设置 + options.AddSecurityRequirement(new OpenApiSecurityRequirement() + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Id = "oauth2", + Type = ReferenceType.SecurityScheme + } + }, + new List() + } + }); - //添加Jwt验证设置 - c.AddSecurityRequirement(new OpenApiSecurityRequirement() + //统一认证 + options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme + { + Type = SecuritySchemeType.OAuth2, + Description = "oauth2登录授权", + Flows = new OpenApiOAuthFlows + { + Implicit = new OpenApiOAuthFlow + { + AuthorizationUrl = new Uri($"{_appConfig.IdentityServer.Url}/connect/authorize"), + Scopes = new Dictionary + { + { "admin.server.api", "admin后端api" } + } + } + } + }); + } + else { + //添加Jwt验证设置 + options.AddSecurityRequirement(new OpenApiSecurityRequirement() { - new OpenApiSecurityScheme { - Reference = new OpenApiReference + new OpenApiSecurityScheme { - Type = ReferenceType.SecurityScheme, - Id = "Bearer" + Reference = new OpenApiReference + { + Id = "Bearer", + Type = ReferenceType.SecurityScheme + } }, - Scheme = "oauth2", - Name = "Bearer", - In = ParameterLocation.Header, - }, - new List() - } - }); + new List() + } + }); + + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "Value: Bearer {token}", + Name = "Authorization", + In = ParameterLocation.Header, + Type = SecuritySchemeType.ApiKey + }); + } + #endregion }); } #endregion - #region Jwt身份认证 - var jwtConfig = new ConfigHelper().Get("jwtconfig", _env.EnvironmentName); - services.TryAddSingleton(jwtConfig); - services.AddSingleton(); - services.TryAddSingleton(); - services.TryAddSingleton(); - services.AddScoped(); - - services.AddAuthentication(options => - { - options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = nameof(ResponseAuthenticationHandler); //401 - options.DefaultForbidScheme = nameof(ResponseAuthenticationHandler); //403 - }) - .AddJwtBearer(options => + #region 操作日志 + if (_appConfig.Log.Operation) { - options.TokenValidationParameters = new TokenValidationParameters - { - ValidateIssuer = true, - ValidateAudience = true, - ValidateLifetime = true, - ValidateIssuerSigningKey = true, - ValidIssuer = jwtConfig.Issuer, - ValidAudience = jwtConfig.Audience, - IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig.SecurityKey)), - ClockSkew = TimeSpan.Zero - }; - }) - .AddScheme(nameof(ResponseAuthenticationHandler), o => { }); ; + services.AddSingleton(); + } #endregion #region 控制器 - services.AddControllers(options => - { - options.Filters.Add(typeof(GlobalExceptionFilter)); + services.AddControllers(options => + { + options.Filters.Add(); + if (_appConfig.Log.Operation) + { + options.Filters.Add(); + } + //禁止去除ActionAsync后缀 + options.SuppressAsyncSuffixInActionNames = false; }) //.AddFluentValidation(config => //{ @@ -175,13 +290,10 @@ public void ConfigureServices(IServiceCollection services) options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; }); #endregion - - //数据库 - services.AddDb(_env); #region 缓存 - var cacheConfig = new ConfigHelper().Get("cacheconfig", _env.EnvironmentName); - if(cacheConfig.Type == CacheType.Redis) + var cacheConfig = _configHelper.Get("cacheconfig", _env.EnvironmentName); + if (cacheConfig.Type == CacheType.Redis) { var csredis = new CSRedis.CSRedisClient(cacheConfig.Redis.ConnectionString); RedisHelper.Initialization(csredis); @@ -194,6 +306,13 @@ public void ConfigureServices(IServiceCollection services) } #endregion + #region IP限流 + if (_appConfig.RateLimit) + { + services.AddIpRateLimit(_configuration, cacheConfig); + } + #endregion + //阻止NLog接收状态消息 services.Configure(opts => opts.SuppressStatusMessages = true); } @@ -201,35 +320,47 @@ public void ConfigureServices(IServiceCollection services) public void ConfigureContainer(ContainerBuilder builder) { #region AutoFac IOC容器 - try { + #region SingleInstance + //无接口注入单例 + var assemblyCore = Assembly.Load("Admin.Core"); + var assemblyCommon = Assembly.Load("Admin.Core.Common"); + builder.RegisterAssemblyTypes(assemblyCore, assemblyCommon) + .Where(t => t.GetCustomAttribute() != null) + .SingleInstance(); + + //有接口注入单例 + builder.RegisterAssemblyTypes(assemblyCore, assemblyCommon) + .Where(t => t.GetCustomAttribute() != null) + .AsImplementedInterfaces() + .SingleInstance(); + #endregion + #region Aop var interceptorServiceTypes = new List(); if (_appConfig.Aop.Transaction) { builder.RegisterType(); interceptorServiceTypes.Add(typeof(TransactionInterceptor)); - } + } #endregion - #region Service - var servicesDllFile = Path.Combine(basePath, "Admin.Core.Service.dll"); - var assemblysServices = Assembly.LoadFrom(servicesDllFile); + #region Repository + var assemblyRepository = Assembly.Load("Admin.Core.Repository"); + builder.RegisterAssemblyTypes(assemblyRepository) + .AsImplementedInterfaces() + .InstancePerDependency(); + #endregion - builder.RegisterAssemblyTypes(assemblysServices) + #region Service + var assemblyServices = Assembly.Load("Admin.Core.Service"); + builder.RegisterAssemblyTypes(assemblyServices) .AsImplementedInterfaces() - .InstancePerLifetimeScope() + .InstancePerDependency() .EnableInterfaceInterceptors() .InterceptedBy(interceptorServiceTypes.ToArray()); #endregion - - #region Repository - var repositoryDllFile = Path.Combine(basePath, "Admin.Core.Repository.dll"); - var assemblysRepository = Assembly.LoadFrom(repositoryDllFile); - builder.RegisterAssemblyTypes(assemblysRepository) - .AsImplementedInterfaces(); - #endregion } catch (Exception ex) { @@ -238,21 +369,20 @@ public void ConfigureContainer(ContainerBuilder builder) #endregion } - public void Configure(IApplicationBuilder app, IHostEnvironment env) + public void Configure(IApplicationBuilder app) { #region app配置 - //异常 - if (env.IsDevelopment()) + //IP限流 + if (_appConfig.RateLimit) { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Error"); + app.UseIpRateLimiting(); } + //异常 + app.UseExceptionHandler("/Error"); + //静态文件 - app.UseStaticFiles(); + app.UseUploadConfig(); //路由 app.UseRouting(); @@ -283,10 +413,12 @@ public void Configure(IApplicationBuilder app, IHostEnvironment env) { c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"Admin.Core {version}"); }); - c.RoutePrefix = "";//直接根目录访问 + c.RoutePrefix = "";//直接根目录访问,如果是IIS发布可以注释该语句,并打开launchSettings.launchUrl + c.DocExpansion(Swashbuckle.AspNetCore.SwaggerUI.DocExpansion.None);//折叠Api + //c.DefaultModelsExpandDepth(-1);//不显示Models }); } #endregion } } -} +} \ No newline at end of file diff --git a/Admin.Core/configs/appconfig.Development.json b/Admin.Core/configs/appconfig.Development.json index 0db3279e4..dca394619 100644 --- a/Admin.Core/configs/appconfig.Development.json +++ b/Admin.Core/configs/appconfig.Development.json @@ -1,3 +1,6 @@ { - + "IdentityServer": { + //ַ + "url": "https://localhost:5000" + } } diff --git a/Admin.Core/configs/appconfig.json b/Admin.Core/configs/appconfig.json index fb05a46d3..dc8b7e30b 100644 --- a/Admin.Core/configs/appconfig.json +++ b/Admin.Core/configs/appconfig.json @@ -1,10 +1,34 @@ { + //Api地址 + "urls": [ "http://*:8000" ], + //跨域地址 + "corUrls": [ "http://*:9000" ], //Swagger文档 "swagger": false, - //Api地址 - "urls": "http://*:8081", + //统一认证授权服务器 + "IdentityServer": { + //启用 + "enable": false, + //地址,开发认证地址前往appconfig.Development.json修改 + "url": "https://login.zhontai.net" + }, + //面向切面编程 "aop": { //事物 - "transaction": false + "transaction": true + }, + //日志 + "log": { + //操作日志 + "operation": true + }, + //限流 + "rateLimit": false, + //验证码 + "varifyCode": { + //启用 + "enable": true, + //字体列表 + "fonts": [ "Times New Roman", "Verdana", "Arial", "Gungsuh", "Impact" ] } } diff --git a/Admin.Core/configs/cacheconfig.json b/Admin.Core/configs/cacheconfig.json index 744b3c1e1..ff9675a7b 100644 --- a/Admin.Core/configs/cacheconfig.json +++ b/Admin.Core/configs/cacheconfig.json @@ -1,8 +1,13 @@ { //缓存类型 Memory = 0,Redis = 1 "type": 0, + //限流缓存类型 Memory = 0,Redis = 1 + "typeRateLimit": 0, //Redis配置 "redis": { - "connectionString": "127.0.0.1:6379,password=,defaultDatabase=2" + //连接字符串 + "connectionString": "127.0.0.1:6379,password=,defaultDatabase=2", + //限流连接字符串 + "connectionStringRateLimit": "127.0.0.1:6379,password=,defaultDatabase=0" } } diff --git a/Admin.Core/configs/dbconfig.Development.json b/Admin.Core/configs/dbconfig.Development.json index a4c9569a4..3e8f2f79f 100644 --- a/Admin.Core/configs/dbconfig.Development.json +++ b/Admin.Core/configs/dbconfig.Development.json @@ -1,27 +1,6 @@ { - // - "generateData": false, - //в + //监听所有操作 "monitorCommand": false, - //Curd - "curd": true, - - //ͬṹ - "syncStructure": true, - //ͬ - "syncData": true, - // - "createDb": true, - //SqlServer,PostgreSQL,Oracle,OdbcOracle,OdbcSqlServer,OdbcMySql,OdbcPostgreSQL,Odbc,OdbcDameng,MsAccess - //ַ - //MySql "Server=localhost; Port=3306; Database=mysql; Uid=root; Pwd={}; Charset=utf8mb4;" - "createDbConnectionString": "Server=localhost; Port=3306; Database=mysql; Uid=root; Pwd=pwd; Charset=utf8mb4;", - //ű - //MySql "CREATE DATABASE `{admindb}` CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci'" - "createDbSql": "CREATE DATABASE `admindb` CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci'", - - //ݿ MySql = 0, SqlServer = 1, PostgreSQL = 2, Oracle = 3, Sqlite = 4, OdbcOracle = 5, OdbcSqlServer = 6, OdbcMySql = 7, OdbcPostgreSQL = 8, Odbc = 9, OdbcDameng = 10, MsAccess = 11 - "type": 4, - //ַ - "connectionString": "Data Source=|DataDirectory|\\admindb.db; Pooling=true;Min Pool Size=1" + //监听Curd操作 + "curd": true } diff --git a/Admin.Core/configs/dbconfig.json b/Admin.Core/configs/dbconfig.json index 6de9ac1c4..19ae37faa 100644 --- a/Admin.Core/configs/dbconfig.json +++ b/Admin.Core/configs/dbconfig.json @@ -1,8 +1,40 @@ { + //监听所有操作 + "monitorCommand": false, + //监听Curd操作 + "curd": false, + + //建库 + "createDb": true, + //SqlServer,PostgreSQL,Oracle,OdbcOracle,OdbcSqlServer,OdbcMySql,OdbcPostgreSQL,Odbc,OdbcDameng,MsAccess + //建库连接字符串 + //MySql "Server=localhost; Port=3306; Database=mysql; Uid=root; Pwd=pwd; Charset=utf8mb4;" + //SqlServer "Data Source=.;Integrated Security=True;Initial Catalog=master;Pooling=true;Min Pool Size=1" + //PostgreSQL "Host=localhost;Port=5432;Username=postgres;Password=; Database=postgres;Pooling=true;Minimum Pool Size=1", + "createDbConnectionString": "Server=localhost; Port=3306; Database=mysql; Uid=root; Pwd=pwd; Charset=utf8mb4;", + //建库脚本 + //MySql "CREATE DATABASE `admindb` CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci'" + //SqlServer "CREATE DATABASE [admindb]" + //PostgreSQL "CREATE DATABASE \"admindb\" WITH ENCODING = 'UTF8'" + "createDbSql": "CREATE DATABASE `admindb` CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci'", + + //同步结构 + "syncStructure": true, + //同步数据 + "syncData": true, + + //项目初始化不开启生成数据,发布生产环境前,如果开发环境有配置数据需要更新数据包,可以开启生成数据包,使用完记得关闭 + //开启生成数据前先关闭syncStructure syncData createDb + //生成数据 + "generateData": false, + + //数据库配置 https://github.com/dotnetcore/FreeSql/wiki/入门 //数据库类型 MySql = 0, SqlServer = 1, PostgreSQL = 2, Oracle = 3, Sqlite = 4, OdbcOracle = 5, OdbcSqlServer = 6, OdbcMySql = 7, OdbcPostgreSQL = 8, Odbc = 9, OdbcDameng = 10, MsAccess = 11 - "type": 0, + "type": 4, //连接字符串 //MySql "Server=localhost; Port=3306; Database=admindb; Uid=root; Pwd=pwd; Charset=utf8mb4;" - //Sqlite "Data Source=|DataDirectory|\\admindb.db; Pooling=true;Min Pool Size=1" "Data Source=F:\\data\\admindb.db; Pooling=true;Min Pool Size=1" - "connectionString": "Server=localhost; Port=3306; Database=admindb; Uid=root; Pwd=pwd; Charset=utf8mb4;" + //SqlServer "Data Source=.;Integrated Security=True;Initial Catalog=admindb;Pooling=true;Min Pool Size=1" + //PostgreSQL "Host=localhost;Port=5432;Username=postgres;Password=; Database=admindb;Pooling=true;Minimum Pool Size=1" + //Sqlite "Data Source=|DataDirectory|\\admindb.db; Pooling=true;Min Pool Size=1" + "connectionString": "Data Source=|DataDirectory|\\admindb.db; Pooling=true;Min Pool Size=1" } diff --git a/Admin.Core/configs/jwtconfig.json b/Admin.Core/configs/jwtconfig.json index e5628cead..dcc06c772 100644 --- a/Admin.Core/configs/jwtconfig.json +++ b/Admin.Core/configs/jwtconfig.json @@ -1,10 +1,12 @@ { - // - "issuer": "http://www.zhontai.com", - // - "audience": "http://www.zhontai.com", - //Կ - "securityKey": "ertJKl#521*a@790asD&", - //Ч() - "expires": 120 + //发行者 + "issuer": "http://127.0.0.1:8000", + //订阅者 + "audience": "http://127.0.0.1:8000", + //密钥 + "securityKey": "ertJKl#521*a@790asD&1#", + //有效期(分钟) 120 = 2小时 + "expires": 120, + //刷新有效期(分钟) 1440 = 1天 + "refreshExpires": 1440 } diff --git a/Admin.Core/configs/logconfig.json b/Admin.Core/configs/logconfig.json index 394a5bbef..1ef978bc5 100644 --- a/Admin.Core/configs/logconfig.json +++ b/Admin.Core/configs/logconfig.json @@ -7,18 +7,18 @@ { _logger = logger; } - _logger.LogDebug(1, ""); + _logger.LogDebug(1, "调试"); - + 或 private readonly ILogger _logger; constructor() { _logger = LogManager.GetLogger("loggerName"); - + 或 _logger = LogManager.GetCurrentClassLogger(); } - _logger.Error(""); + _logger.Error("错误"); */ "nLog": { "extensions": { @@ -27,22 +27,22 @@ } }, "targets": { - // + //调试 "debug": { "type": "File", - "fileName": "logs\\debug-${shortdate}.log", + "fileName": "../logs/debug-${shortdate}.log", "layout": "${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" }, - // + //警告 "warn": { "type": "File", - "fileName": "logs\\warn-${shortdate}.log", + "fileName": "../logs/warn-${shortdate}.log", "layout": "${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" }, - // + //错误 "error": { "type": "File", - "fileName": "logs\\error-${shortdate}.log", + "fileName": "../logs/error-${shortdate}.log", "layout": "${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" } }, @@ -65,7 +65,7 @@ "maxlevel": "Fatal", "writeTo": "error" }, - //Ҫ΢־ + //跳过不重要的微软日志 { "logger": "Microsoft.*", "maxLevel": "Info", diff --git a/Admin.Core/configs/ratelimitconfig.Development.json b/Admin.Core/configs/ratelimitconfig.Development.json new file mode 100644 index 000000000..0db3279e4 --- /dev/null +++ b/Admin.Core/configs/ratelimitconfig.Development.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/Admin.Core/configs/ratelimitconfig.json b/Admin.Core/configs/ratelimitconfig.json new file mode 100644 index 000000000..3337df810 --- /dev/null +++ b/Admin.Core/configs/ratelimitconfig.json @@ -0,0 +1,33 @@ +{ + /* + https://github.com/stefanprodan/AspNetCoreRateLimit/wiki/IpRateLimitMiddleware + https://github.com/stefanprodan/AspNetCoreRateLimit/wiki/Using-Redis-as-a-distributed-counter-store + */ + "IpRateLimiting": { + "EnableEndpointRateLimiting": true, + "StackBlockedRequests": false, + "RealIpHeader": "X-Real-IP", + "ClientIdHeader": "X-ClientId", + "IpWhitelist": [], // "127.0.0.1" + "EndpointWhitelist": ["get:/api/admin/auth/refresh"], // "get:/api/a", "*:/api/b" + "ClientWhitelist": [], + "HttpStatusCode": 429, + "QuotaExceededResponse": { + "Content": "{{\"code\":429,\"msg\":\"访问过于频繁!\"}}", + "ContentType": "application/json", + "StatusCode": 429 + }, + "GeneralRules": [ + { + "Endpoint": "*", + "Period": "1s", + "Limit": 3 + }, + { + "Endpoint": "*", + "Period": "10m", + "Limit": 200 + } + ] + } +} diff --git a/Admin.Core/configs/uploadconfig.Development.json b/Admin.Core/configs/uploadconfig.Development.json new file mode 100644 index 000000000..0e0dcd235 --- /dev/null +++ b/Admin.Core/configs/uploadconfig.Development.json @@ -0,0 +1,3 @@ +{ + +} \ No newline at end of file diff --git a/Admin.Core/configs/uploadconfig.json b/Admin.Core/configs/uploadconfig.json new file mode 100644 index 000000000..74d17817b --- /dev/null +++ b/Admin.Core/configs/uploadconfig.json @@ -0,0 +1,38 @@ +{ + //头像 + "avatar": { + //上传路径 D:/upload/admin/avatar + "uploadPath": "../upload/admin/avatar", + //请求路径 + "requestPath": "/upload/admin/avatar", + + //路径日期格式 yyyy/MM/dd + "dateTimeFormat": "", + //{用户编号} + "format": "{Id}", + //图片大小不超过 1M = 1 * 1024 * 1024 + "maxSize": 1048576, + //最大允许上传张数,-1不限制 + "limit": 1, + //图片格式 + "contentType": [ "image/jpg", "image/png", "image/jpeg", "image/gif" ] + }, + //文档图片 + "document": { + //上传路径 D:/upload/admin/document + "uploadPath": "../upload/admin/document", + //请求路径 + "requestPath": "/images", + + //路径日期格式 yyyy/MM/dd + "dateTimeFormat": "", + //{文档编号} + "format": "{Id}", + //图片大小不超过 1M = 1 * 1024 * 1024 + "maxSize": 1048576, + //最大允许上传张数,-1不限制 + "limit": -1, + //图片格式 + "contentType": [ "image/jpg", "image/png", "image/jpeg", "image/gif" ] + } +} diff --git a/Admin.Core/nlog.config b/Admin.Core/nlog.config index c1a0ce190..0106de227 100644 --- a/Admin.Core/nlog.config +++ b/Admin.Core/nlog.config @@ -7,16 +7,19 @@ - + - + - + + + + diff --git a/Admin.Core/wwwroot/avatar/avatar.gif b/Admin.Core/wwwroot/avatar/avatar.gif deleted file mode 100644 index ddc4b1998..000000000 Binary files a/Admin.Core/wwwroot/avatar/avatar.gif and /dev/null differ diff --git a/README.md b/README.md index 1804aa29c..a1756888d 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ 一个基于.Net Core开发的权限管理系统,后续会增加更多实用的功能。 ### 在线体验 -演示地址:https://www.admin.zhontai.net +演示地址:https://www.admin.zhontai.net/ -文档地址:https://www.zhontai.net +文档地址:https://www.zhontai.net/ ********************************************************* ### 项目下载后,直接编译运行项目