50 Commits
Author SHA1 Message Date
不做码农 714acc0749 :zap:优化启动打印 2023-09-18 21:09:40 +08:00
不做码农 1d32f4709b :pencil:更新数据库脚本 2023-09-18 17:59:48 +08:00
不做码农 b96a4adb9f 移动端代码生成表单优化 2023-09-15 19:55:07 +08:00
不做码农 0e08a7d102 :sparkles:字典组件(DictTag)显示翻译 2023-09-13 18:40:34 +08:00
不做码农 54e5b46dbf :arrow_up:升级包 2023-09-13 18:39:36 +08:00
不做码农 f266c9a718 :pencil:update doc 2023-09-13 11:18:55 +08:00
不做码农 b02cf986d3 :zap:树形代码生成新增布局选项 2023-09-08 18:40:29 +08:00
不做码农 44ef55be00 :recycle:重新设计返回格式,同Ruoyi保持一致 2023-09-08 18:39:40 +08:00
不做码农 70371e92b3 :zap:数据返回格式改为可灵活扩展,跟ruoyi保存一致 2023-09-08 15:18:14 +08:00
不做码农 803d862513 :zap:signalr返回数据格式改为驼峰 2023-09-08 15:15:55 +08:00
不做码农 4a093470d5 :bug:fix测试代码提交 2023-09-08 13:42:13 +08:00
不做码农 b01765360d :zap:优化Nlog日志记录,解决记录到文本IP、请求地址获取不到问题 2023-09-07 09:34:01 +08:00
不做码农 02ffc18a61 :zap:优化program,jwt注入移至WebExtensions目录 2023-09-07 07:29:04 +08:00
不做码农 0fb410159a :zap:优化移动端代码生成模板 2023-09-06 20:56:07 +08:00
不做码农 d2afa84c37 :truck:移动MessageHub 2023-09-06 20:56:07 +08:00
不做码农 672fb84871 :bug:fix种子数据重复menuId问题 2023-09-06 09:27:56 +08:00
不做码农 1e7a9c7abb :zap:邮件发送失败异常处理 2023-09-04 20:42:02 +08:00
不做码农 b83c005f28 :zap:代码生成样式调整 2023-09-04 20:40:51 +08:00
不做码农 b9a3cfe377 :pencil:更新文档 2023-09-04 20:26:07 +08:00
不做码农 be515339fe :recycle:代码重构 2023-09-02 17:55:34 +08:00
不做码农 c65952202a :zap:优化数据传输 2023-08-30 16:22:51 +08:00
不做码农 ab98cf30c1 :zap:强退功能优化 2023-08-30 08:36:45 +08:00
不做码农 4d914ead0e :zap:完善扫码登录(生成新的token) 2023-08-30 07:50:06 +08:00
不做码农 b67c59710b :sparkles:优化强退功能新增批量强退在线用户 2023-08-29 21:35:09 +08:00
不做码农 24c9ceefde :sparkles:种子数据新增菜单 2023-08-29 21:20:40 +08:00
不做码农 e713bcd9b7 :zap:优化新增演示模式条件判断 2023-08-29 21:20:09 +08:00
不做码农 400aa876df :zap:优化数据校验 2023-08-29 21:19:37 +08:00
不做码农 07b4b95732 :zap:优化功能、类目录 2023-08-29 16:56:07 +08:00
不做码农 94fa69e8ef :bug:fix查询用户角色缓存不起作用问题 2023-08-29 16:35:57 +08:00
不做码农 46c9563f33 :sparkles:刷新jwttoken 2023-08-29 07:40:56 +08:00
不做码农 52240ab6a8 :zap:优化登录 2023-08-28 22:16:57 +08:00
不做码农 ea3fa0e0d6 add SqlDiffLog.vue 2023-08-28 18:23:24 +08:00
不做码农 6bf4885f85 :zap:优化jwt(非管理员用户登录重启服务器后不用重新登录) 2023-08-28 18:22:36 +08:00
不做码农 839e400ed1 :sparkles:新增单点登录功能、在线时长 2023-08-28 08:28:12 +08:00
不做码农 55733f07e8 :zap:优化功能 2023-08-26 16:39:31 +08:00
不做码农 6533020ebc :sparkles:新增移动端扫码登录 2023-08-26 15:54:41 +08:00
不做码农 710966e098 ✨ 新增剔出在线用户 2023-08-26 15:51:12 +08:00
不做码农 9495589b28 ✨ 代码生成新增组件 2023-08-26 07:37:13 +08:00
不做码农 e8cbf35abb 🐛 fix部门管理列表显示异常 2023-08-25 19:43:05 +08:00
不做码农 dc38e97296 :sparkles:Treeselect树结构新增字段 2023-08-22 18:24:26 +08:00
不做码农 ba693d1e78 :zap:优化代码生成模板 2023-08-18 13:55:36 +08:00
不做码农 e8ab6d47eb :sparkles:swagger新增分组 2023-08-18 11:20:27 +08:00
不做码农 240a607875 :zap:新增数据库差异化日志记录 2023-08-18 07:24:02 +08:00
不做码农 09c45c4ba4 :zap:Action新增角色权限判断 2023-08-18 07:09:11 +08:00
不做码农 0257fb294a :arrow_up:升级包 2023-08-17 19:27:18 +08:00
不做码农 1ca05495d8 :zap:侧边栏路由新增isNew属性 2023-08-17 18:08:15 +08:00
不做码农 d1108700e0 :zap:添加用户改为返回实体类 2023-08-15 09:39:11 +08:00
不做码农 ff4e857128 :fire:移除仓储不常用方法 2023-08-15 09:38:39 +08:00
不做码农 31cf1a82f1 :zap:优化代码生成模板 2023-08-14 18:12:48 +08:00
不做码农 0531c2b330 :zap:db日志打印新增配置开关 2023-08-14 18:11:03 +08:00
217 changed files with 2025 additions and 1103 deletions
+2 -3
View File
@@ -1,12 +1,11 @@
using Microsoft.AspNetCore.Hosting; using Infrastructure.Model;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System; using System;
using System.Security.Claims; using System.Security.Claims;
using System.Security.Principal;
using System.Xml.Linq;
namespace Infrastructure namespace Infrastructure
{ {
@@ -1,13 +1,20 @@
using Infrastructure.Extensions; using Infrastructure.Extensions;
using Infrastructure.Model;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MiniExcelLibs; using MiniExcelLibs;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Web; using System.Web;
using Io = System.IO;
namespace ZR.Admin.WebApi.Controllers namespace Infrastructure.Controllers
{ {
/// <summary>
/// web层通用数据处理
/// </summary>
public class BaseController : ControllerBase public class BaseController : ControllerBase
{ {
public static string TIME_FORMAT_FULL = "yyyy-MM-dd HH:mm:ss"; public static string TIME_FORMAT_FULL = "yyyy-MM-dd HH:mm:ss";
@@ -61,7 +68,7 @@ namespace ZR.Admin.WebApi.Controllers
{ {
throw new CustomException(fileName + "文件不存在"); throw new CustomException(fileName + "文件不存在");
} }
var stream = Io.File.OpenRead(path); //创建文件流 var stream = System.IO.File.OpenRead(path); //创建文件流
Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition"); Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", HttpUtility.UrlEncode(fileName)); return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", HttpUtility.UrlEncode(fileName));
@@ -92,6 +99,10 @@ namespace ZR.Admin.WebApi.Controllers
return new ApiResult((int)resultCode, msg, data); return new ApiResult((int)resultCode, msg, data);
} }
protected ApiResult Success()
{
return GetApiResult(ResultCode.SUCCESS);
}
/// <summary> /// <summary>
/// ///
@@ -2,7 +2,7 @@
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Infrastructure namespace Infrastructure.Converter
{ {
public class JsonConverterUtil public class JsonConverterUtil
{ {
@@ -0,0 +1,48 @@
using System;
using System.Buffers;
using System.Linq;
using System.Text;
using System.Text.Json;
namespace Infrastructure.Converter
{
/// <summary>
/// Json任何类型读取到字符串属性
/// 因为 System.Text.Json 必须严格遵守类型一致,当非字符串读取到字符属性时报错:
/// The JSON value could not be converted to System.String.
/// </summary>
public class StringConverter : System.Text.Json.Serialization.JsonConverter<string>
{
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String)
{
return reader.GetString();
}
else
{
//非字符类型,返回原生内容
return GetRawPropertyValue(reader);
}
throw new JsonException();
}
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
{
writer.WriteStringValue(value);
}
/// <summary>
/// 非字符类型,返回原生内容
/// </summary>
/// <param name="jsonReader"></param>
/// <returns></returns>
private static string GetRawPropertyValue(Utf8JsonReader jsonReader)
{
ReadOnlySpan<byte> utf8Bytes = jsonReader.HasValueSequence ?
jsonReader.ValueSequence.ToArray() :
jsonReader.ValueSpan;
return Encoding.UTF8.GetString(utf8Bytes);
}
}
}
@@ -5,9 +5,19 @@ namespace Infrastructure
public class CustomException : Exception public class CustomException : Exception
{ {
public int Code { get; set; } public int Code { get; set; }
/// <summary>
/// 前端提示语
/// </summary>
public string Msg { get; set; } public string Msg { get; set; }
/// <summary>
/// 记录到日志的详细内容
/// </summary>
public string LogMsg { get; set; } public string LogMsg { get; set; }
/// <summary>
/// 是否通知
/// </summary>
public bool Notice { get; set; } = true;
public CustomException(string msg) : base(msg) public CustomException(string msg) : base(msg)
{ {
} }
@@ -17,9 +27,10 @@ namespace Infrastructure
Msg = msg; Msg = msg;
} }
public CustomException(ResultCode resultCode, string msg) : base(msg) public CustomException(ResultCode resultCode, string msg, bool notice = true) : base(msg)
{ {
Code = (int)resultCode; Code = (int)resultCode;
Notice = notice;
} }
/// <summary> /// <summary>
+5
View File
@@ -54,5 +54,10 @@
/// 清空数据 /// 清空数据
/// </summary> /// </summary>
CLEAN = 9, CLEAN = 9,
/// <summary>
/// 下载
/// </summary>
DOWNLOAD = 10,
} }
} }
+1 -1
View File
@@ -91,7 +91,7 @@ namespace Infrastructure
#region 获取unix时间戳 #region 获取unix时间戳
/// <summary> /// <summary>
/// 获取unix时间戳 /// 获取unix时间戳(毫秒)
/// </summary> /// </summary>
/// <param name="dt"></param> /// <param name="dt"></param>
/// <returns></returns> /// <returns></returns>
@@ -2,7 +2,7 @@
using System; using System;
using System.IO; using System.IO;
namespace ZR.Common namespace Infrastructure.Helper
{ {
public class JnHelper public class JnHelper
{ {
-19
View File
@@ -1,19 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<Compile Remove="Model\PagedInfo.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AspectCore.Abstractions" Version="2.4.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>
@@ -1,18 +1,19 @@
using Infrastructure; using Infrastructure.Extensions;
using Infrastructure.Extensions; using Infrastructure.Model;
using Microsoft.AspNetCore.Http;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json; using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using System.Text; using System.Text;
using ZR.Admin.WebApi.Extensions;
using ZR.Model.System.Dto;
using ZR.Service.System;
namespace ZR.Admin.WebApi.Framework namespace Infrastructure
{ {
/// <summary> /// <summary>
/// 2020-11-20 /// 2023-8-29已从WebApi移至此
/// </summary> /// </summary>
public class JwtUtil public class JwtUtil
{ {
@@ -21,7 +22,7 @@ namespace ZR.Admin.WebApi.Framework
/// </summary> /// </summary>
/// <param name="httpContext"></param> /// <param name="httpContext"></param>
/// <returns></returns> /// <returns></returns>
public static LoginUser GetLoginUser(HttpContext httpContext) public static TokenModel GetLoginUser(HttpContext httpContext)
{ {
string token = httpContext.GetToken(); string token = httpContext.GetToken();
@@ -36,10 +37,12 @@ namespace ZR.Admin.WebApi.Framework
/// 生成token /// 生成token
/// </summary> /// </summary>
/// <param name="claims"></param> /// <param name="claims"></param>
/// <param name="jwtSettings"></param>
/// <returns></returns> /// <returns></returns>
public static string GenerateJwtToken(List<Claim> claims, JwtSettings jwtSettings) public static string GenerateJwtToken(List<Claim> claims)
{ {
JwtSettings jwtSettings = new();
AppSettings.Bind("JwtSettings", jwtSettings);
var authTime = DateTime.Now; var authTime = DateTime.Now;
var expiresAt = authTime.AddMinutes(jwtSettings.Expire); var expiresAt = authTime.AddMinutes(jwtSettings.Expire);
var tokenHandler = new JwtSecurityTokenHandler(); var tokenHandler = new JwtSecurityTokenHandler();
@@ -55,7 +58,7 @@ namespace ZR.Admin.WebApi.Framework
IssuedAt = authTime,//token生成时间 IssuedAt = authTime,//token生成时间
Expires = expiresAt, Expires = expiresAt,
//NotBefore = authTime, //NotBefore = authTime,
TokenType = "Bearer", TokenType = jwtSettings.TokenType,
//对称秘钥,签名证书 //对称秘钥,签名证书
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
}; };
@@ -96,7 +99,7 @@ namespace ZR.Admin.WebApi.Framework
/// </summary> /// </summary>
/// <param name="token">令牌</param> /// <param name="token">令牌</param>
/// <returns></returns> /// <returns></returns>
public static IEnumerable<Claim>? ParseToken(string token) public static JwtSecurityToken? ParseToken(string token)
{ {
var tokenHandler = new JwtSecurityTokenHandler(); var tokenHandler = new JwtSecurityTokenHandler();
var validateParameter = ValidParameters(); var validateParameter = ValidParameters();
@@ -105,8 +108,7 @@ namespace ZR.Admin.WebApi.Framework
{ {
tokenHandler.ValidateToken(token, validateParameter, out SecurityToken validatedToken); tokenHandler.ValidateToken(token, validateParameter, out SecurityToken validatedToken);
var jwtToken = tokenHandler.ReadJwtToken(token); return tokenHandler.ReadJwtToken(token);
return jwtToken.Claims;
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -119,25 +121,21 @@ namespace ZR.Admin.WebApi.Framework
/// <summary> /// <summary>
/// jwt token校验 /// jwt token校验
/// </summary> /// </summary>
/// <param name="jwtToken"></param> /// <param name="jwtSecurityToken"></param>
/// <returns></returns> /// <returns></returns>
public static LoginUser? ValidateJwtToken(IEnumerable<Claim> jwtToken) public static TokenModel? ValidateJwtToken(JwtSecurityToken jwtSecurityToken)
{ {
try try
{ {
LoginUser loginUser = null; if (jwtSecurityToken == null) return null;
IEnumerable<Claim> claims = jwtSecurityToken?.Claims;
var userData = jwtToken.FirstOrDefault(x => x.Type == ClaimTypes.UserData)?.Value; TokenModel loginUser = null;
var userData = claims.FirstOrDefault(x => x.Type == ClaimTypes.UserData)?.Value;
if (userData != null) if (userData != null)
{ {
loginUser = JsonConvert.DeserializeObject<LoginUser>(userData); loginUser = JsonConvert.DeserializeObject<TokenModel>(userData);
var permissions = CacheService.GetUserPerms(GlobalConstant.UserPermKEY + loginUser?.UserId); loginUser.ExpireTime = jwtSecurityToken.ValidTo;
if (loginUser?.UserName == GlobalConstant.AdminRole)
{
permissions = new List<string>() { GlobalConstant.AdminPerm };
}
if (permissions == null) return null;
loginUser.Permissions = permissions;
} }
return loginUser; return loginUser;
} }
@@ -153,7 +151,7 @@ namespace ZR.Admin.WebApi.Framework
/// </summary> /// </summary>
/// <param name="user"></param> /// <param name="user"></param>
/// <returns></returns> /// <returns></returns>
public static List<Claim> AddClaims(LoginUser user) public static List<Claim> AddClaims(TokenModel user)
{ {
var claims = new List<Claim>() var claims = new List<Claim>()
{ {
+14
View File
@@ -0,0 +1,14 @@
using System;
namespace Infrastructure
{
public class Log
{
public static void WriteLine(ConsoleColor color = ConsoleColor.Black, string msg = "")
{
Console.ForegroundColor = color;
Console.WriteLine($"{DateTime.Now} {msg}");
Console.ResetColor();
}
}
}
+49 -40
View File
@@ -1,17 +1,26 @@
using Infrastructure.Constant; using Infrastructure.Constant;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Collections.Generic;
namespace Infrastructure.Model namespace Infrastructure.Model
{ {
public class ApiResult public class ApiResult : Dictionary<string, object>
{ {
public int Code { get; set; } /** 状态码 */
public string Msg { get; set; } public static readonly string CODE_TAG = "code";
/** 返回内容 */
public static readonly string MSG_TAG = "msg";
/** 数据对象 */
public static readonly string DATA_TAG = "data";
//public int Code { get; set; }
//public string Msg { get; set; }
/// <summary> /// <summary>
/// 如果data值为null,则忽略序列化将不会返回data字段 /// 如果data值为null,则忽略序列化将不会返回data字段
/// </summary> /// </summary>
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)] //[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public object Data { get; set; } //public object Data { get; set; }
/// <summary> /// <summary>
/// 初始化一个新创建的APIResult对象,使其表示一个空消息 /// 初始化一个新创建的APIResult对象,使其表示一个空消息
@@ -27,8 +36,8 @@ namespace Infrastructure.Model
/// <param name="msg"></param> /// <param name="msg"></param>
public ApiResult(int code, string msg) public ApiResult(int code, string msg)
{ {
Code = code; Add(CODE_TAG, code);
Msg = msg; Add(MSG_TAG, msg);
} }
/// <summary> /// <summary>
@@ -36,33 +45,28 @@ namespace Infrastructure.Model
/// </summary> /// </summary>
/// <param name="code"></param> /// <param name="code"></param>
/// <param name="msg"></param> /// <param name="msg"></param>
/// <param name="data"></param>
public ApiResult(int code, string msg, object data) public ApiResult(int code, string msg, object data)
{ {
Code = code; Add(CODE_TAG, code);
Msg = msg; Add(MSG_TAG, msg);
if (data != null) if (data != null)
{ {
Data = data; Add(DATA_TAG, data);
} }
} }
/// <summary>
/// 返回成功消息
/// </summary>
/// < returns > 成功消息 </ returns >
public static ApiResult Success() { return new ApiResult(HttpStatus.SUCCESS, "success"); }
/// <summary> /// <summary>
/// 返回成功消息 /// 返回成功消息
/// </summary> /// </summary>
/// <returns></returns> /// <param name="data"></param>
public ApiResult Success() /// <returns> 成功消息 </returns >
{ public static ApiResult Success(object data) { return new ApiResult(HttpStatus.SUCCESS, "success", data); }
Code = (int)ResultCode.SUCCESS;
Msg = "success";
return this;
}
///// <summary>
///// 返回成功消息
///// </summary>
///// <param name = "data" > 数据对象 </ param >
///// < returns > 成功消息 </ returns >
//public static ApiResult Success(object data) { return new ApiResult(HttpStatus.SUCCESS, "success", data); }
/// <summary> /// <summary>
/// 返回成功消息 /// 返回成功消息
@@ -79,21 +83,9 @@ namespace Infrastructure.Model
/// <returns>成功消息</returns> /// <returns>成功消息</returns>
public static ApiResult Success(string msg, object data) { return new ApiResult(HttpStatus.SUCCESS, msg, data); } public static ApiResult Success(string msg, object data) { return new ApiResult(HttpStatus.SUCCESS, msg, data); }
/// <summary> public static ApiResult Error(ResultCode code, string msg = "")
/// 访问被拒
/// </summary>
/// <returns></returns>
public ApiResult On401()
{ {
Code = (int)ResultCode.DENY; return Error((int)code, msg);
Msg = "access denyed";
return this;
}
public ApiResult Error(ResultCode resultCode, string msg = "")
{
Code = (int)resultCode;
Msg = msg;
return this;
} }
/// <summary> /// <summary>
@@ -111,9 +103,26 @@ namespace Infrastructure.Model
/// <returns></returns> /// <returns></returns>
public static ApiResult Error(string msg) { return new ApiResult((int)ResultCode.CUSTOM_ERROR, msg); } public static ApiResult Error(string msg) { return new ApiResult((int)ResultCode.CUSTOM_ERROR, msg); }
public override string ToString()
/// <summary>
/// 是否为成功消息
/// </summary>
/// <returns></returns>
public bool IsSuccess()
{ {
return $"msg={Msg},data={Data}"; return HttpStatus.SUCCESS == (int)this[CODE_TAG];
}
/// <summary>
/// 方便链式调用
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <returns></returns>
public ApiResult Put(string key, object value)
{
Add(key, value);
return this;
} }
} }
@@ -1,18 +1,24 @@
 using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace Infrastructure namespace Infrastructure.Model
{ {
/// <summary> /// <summary>
/// 获取配置文件POCO实体类 /// 获取配置文件POCO实体类
/// </summary> /// </summary>
public class OptionsSetting public class OptionsSetting
{ {
/// <summary>
/// 是否单点登录
/// </summary>
public bool SingleLogin { get; set; }
/// <summary> /// <summary>
/// 是否演示模式 /// 是否演示模式
/// </summary> /// </summary>
public bool DemoMode { get; set; } public bool DemoMode { get; set; }
/// <summary>
/// 初始化db
/// </summary>
public bool InitDb { get; set; }
public MailOptions MailOptions { get; set; } public MailOptions MailOptions { get; set; }
public Upload Upload { get; set; } public Upload Upload { get; set; }
public ALIYUN_OSS ALIYUN_OSS { get; set; } public ALIYUN_OSS ALIYUN_OSS { get; set; }
@@ -78,6 +84,14 @@ namespace Infrastructure
/// token时间(分) /// token时间(分)
/// </summary> /// </summary>
public int Expire { get; set; } = 1440; public int Expire { get; set; } = 1440;
/// <summary>
/// 刷新token时长
/// </summary>
public int RefreshTokenTime { get; set; }
/// <summary>
/// token类型
/// </summary>
public string TokenType { get; set; } = "Bearer";
} }
public class Gen public class Gen
+48
View File
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Infrastructure.Model
{
public class TokenModel
{
public long UserId { get; set; }
public long DeptId { get; set; }
public string UserName { get; set; }
/// <summary>
/// 角色集合
/// </summary>
public List<string> RoleIds { get; set; }
/// <summary>
/// 角色集合(数据权限过滤使用)
/// </summary>
public List<Roles> Roles { get; set; }
/// <summary>
/// Jwt过期时间
/// </summary>
public DateTime ExpireTime { get; set; }
/// <summary>
/// 权限集合
/// </summary>
//public List<string> Permissions { get; set; } = new List<string>();
public TokenModel()
{
}
public TokenModel(TokenModel info, List<Roles> roles)
{
UserId = info.UserId;
UserName = info.UserName;
DeptId = info.DeptId;
Roles = roles;
RoleIds = roles.Select(f => f.RoleKey).ToList();
}
}
public class Roles
{
public long RoleId { get; set; }
public string RoleKey { get; set; }
public int DataScope { get; set; }
}
}
@@ -1,8 +1,10 @@
using Infrastructure; using Infrastructure.Attribute;
using Infrastructure.Attribute; using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using System.Reflection; using System.Reflection;
namespace ZR.Admin.WebApi.Extensions namespace Infrastructure
{ {
/// <summary> /// <summary>
/// App服务注册 /// App服务注册
@@ -1,5 +1,12 @@
namespace ZR.Admin.WebApi.Extensions using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace Infrastructure
{ {
/// <summary>
/// 跨域扩展
/// </summary>
public static class CorsExtension public static class CorsExtension
{ {
/// <summary> /// <summary>
@@ -1,14 +1,17 @@
 
using Infrastructure.Extensions;
using Microsoft.AspNetCore.Http;
using System;
using System.Reflection; using System.Reflection;
namespace ZR.Admin.WebApi.Extensions namespace Infrastructure
{ {
public static class EntityExtension public static class EntityExtension
{ {
public static TSource ToCreate<TSource>(this TSource source, HttpContext? context = null) public static TSource ToCreate<TSource>(this TSource source, HttpContext? context = null)
{ {
var types = source?.GetType(); var types = source?.GetType();
if (types == null) return source; if (types == null || context == null) return source;
BindingFlags flag = BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance; BindingFlags flag = BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance;
types.GetProperty("CreateTime", flag)?.SetValue(source, DateTime.Now, null); types.GetProperty("CreateTime", flag)?.SetValue(source, DateTime.Now, null);
@@ -23,7 +26,7 @@ namespace ZR.Admin.WebApi.Extensions
public static TSource ToUpdate<TSource>(this TSource source, HttpContext? context = null) public static TSource ToUpdate<TSource>(this TSource source, HttpContext? context = null)
{ {
var types = source?.GetType(); var types = source?.GetType();
if (types == null) return source; if (types == null || context == null) return source;
BindingFlags flag = BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance; BindingFlags flag = BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance;
types.GetProperty("UpdateTime", flag)?.SetValue(source, DateTime.Now, null); types.GetProperty("UpdateTime", flag)?.SetValue(source, DateTime.Now, null);
@@ -1,12 +1,15 @@
using Infrastructure; using IPTools.Core;
using Infrastructure.Extensions; using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using UAParser; using UAParser;
using ZR.Model.System;
namespace ZR.Admin.WebApi.Extensions namespace Infrastructure.Extensions
{ {
/// <summary> /// <summary>
/// HttpContext扩展类 /// HttpContext扩展类
@@ -50,7 +53,7 @@ namespace ZR.Admin.WebApi.Extensions
if (result.Contains("::1")) if (result.Contains("::1"))
result = "127.0.0.1"; result = "127.0.0.1";
result = result.Replace("::ffff:", "127.0.0.1"); result = result.Replace("::ffff:", "");
result = result.Split(':')?.FirstOrDefault() ?? "127.0.0.1"; result = result.Split(':')?.FirstOrDefault() ?? "127.0.0.1";
result = IsIP(result) ? result : "127.0.0.1"; result = IsIP(result) ? result : "127.0.0.1";
return result; return result;
@@ -82,7 +85,7 @@ namespace ZR.Admin.WebApi.Extensions
/// </summary> /// </summary>
/// <param name="context"></param> /// <param name="context"></param>
/// <returns></returns> /// <returns></returns>
public static string? GetName(this HttpContext context) public static string GetName(this HttpContext context)
{ {
var uid = context.User?.Identity?.Name; var uid = context.User?.Identity?.Name;
@@ -105,7 +108,7 @@ namespace ZR.Admin.WebApi.Extensions
/// </summary> /// </summary>
/// <param name="context"></param> /// <param name="context"></param>
/// <returns></returns> /// <returns></returns>
public static IEnumerable<ClaimsIdentity>? GetClaims(this HttpContext context) public static IEnumerable<ClaimsIdentity> GetClaims(this HttpContext context)
{ {
return context.User?.Identities; return context.User?.Identities;
} }
@@ -131,26 +134,12 @@ namespace ZR.Admin.WebApi.Extensions
return context.Request.Headers["Authorization"]; return context.Request.Headers["Authorization"];
} }
/// <summary>
/// 获取浏览器信息
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public static ClientInfo GetClientInfo(this HttpContext context)
{
var str = context.GetUserAgent();
var uaParser = Parser.GetDefault();
ClientInfo c = uaParser.Parse(str);
return c;
}
/// <summary> /// <summary>
/// 获取请求Url /// 获取请求Url
/// </summary> /// </summary>
/// <param name="context"></param> /// <param name="context"></param>
/// <returns></returns> /// <returns></returns>
public static string? GetRequestUrl(this HttpContext context) public static string GetRequestUrl(this HttpContext context)
{ {
return context != null ? context.Request.Path.Value : ""; return context != null ? context.Request.Path.Value : "";
} }
@@ -194,30 +183,51 @@ namespace ZR.Admin.WebApi.Extensions
return body; return body;
} }
/// <summary>
/// 获取浏览器信息
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public static ClientInfo GetClientInfo(this HttpContext context)
{
var str = context.GetUserAgent();
var uaParser = Parser.GetDefault();
ClientInfo c = uaParser.Parse(str);
return c;
}
/// <summary>
/// 根据IP获取地理位置
/// </summary>
/// <returns></returns>
public static string GetIpInfo(string IP)
{
var ipInfo = IpTool.Search(IP);
return ipInfo?.Province + "-" + ipInfo?.City + "-" + ipInfo?.NetworkOperator;
}
/// <summary> /// <summary>
/// 设置请求参数 /// 设置请求参数
/// </summary> /// </summary>
/// <param name="operLog"></param> /// <param name="reqMethod"></param>
/// <param name="context"></param> /// <param name="context"></param>
public static void GetRequestValue(this HttpContext context, SysOperLog operLog) public static string GetRequestValue(this HttpContext context, string reqMethod)
{ {
string reqMethod = operLog.RequestMethod; string param = string.Empty;
string param= string.Empty;
if (HttpMethods.IsPost(reqMethod) || HttpMethods.IsPut(reqMethod) || HttpMethods.IsDelete(reqMethod)) if (HttpMethods.IsPost(reqMethod) || HttpMethods.IsPut(reqMethod) || HttpMethods.IsDelete(reqMethod))
{ {
param = context.GetBody(); param = context.GetBody();
param = PwdRep().Replace(param, "***"); string regex = "(?<=\"password\":\")[^\",]*";
param = Regex.Replace(param, regex, "***");
} }
if (param.IsEmpty()) if (param.IsEmpty())
{ {
param = context.GetQueryString(); param = context.GetQueryString();
} }
operLog.OperParam = param; return param;
} }
[GeneratedRegex("(?<=\"password\":\")[^\",]*")]
private static partial Regex PwdRep();
} }
} }
@@ -1,6 +1,9 @@
using AspNetCoreRateLimit; using AspNetCoreRateLimit;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace ZR.Admin.WebApi.Extensions namespace ZR.Infrastructure.WebExtensions
{ {
public static class IPRateExtension public static class IPRateExtension
{ {
@@ -0,0 +1,39 @@
using Infrastructure;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Threading.Tasks;
namespace ZR.Infrastructure.WebExtensions
{
public static class JwtExtension
{
public static void AddJwt(this IServiceCollection services)
{
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddCookie()
.AddJwtBearer(o =>
{
o.TokenValidationParameters = JwtUtil.ValidParameters();
o.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
// 如果过期,把过期信息添加到头部
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
Console.WriteLine("jwt过期了");
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
},
};
});
}
}
}
@@ -1,7 +1,9 @@
using JinianNet.JNTemplate; using Infrastructure.Helper;
using ZR.Common; using JinianNet.JNTemplate;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace ZR.Admin.WebApi.Extensions namespace Infrastructure
{ {
public static class LogoExtension public static class LogoExtension
{ {
@@ -10,13 +12,14 @@ namespace ZR.Admin.WebApi.Extensions
Console.ForegroundColor = ConsoleColor.Blue; Console.ForegroundColor = ConsoleColor.Blue;
var contentTpl = JnHelper.ReadTemplate("", "logo.txt"); var contentTpl = JnHelper.ReadTemplate("", "logo.txt");
var content = contentTpl?.Render(); var content = contentTpl?.Render();
var context = App.HttpContext;
Console.WriteLine(content); Console.WriteLine(content);
Console.ForegroundColor = ConsoleColor.Blue; Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("🎉源码地址: https://gitee.com/izory/ZrAdminNetCore"); Console.WriteLine("🎉源码地址: https://gitee.com/izory/ZrAdminNetCore");
Console.WriteLine("📖官方文档:http://www.izhaorui.cn/doc"); Console.WriteLine("📖官方文档:http://www.izhaorui.cn/doc");
Console.WriteLine("🤑打赏作者:http://www.izhaorui.cn/doc/support.html"); Console.WriteLine("💰打赏作者:http://www.izhaorui.cn/doc/support.html");
Console.WriteLine("📱移动端体验:http://www.izhaorui.cn/h5"); Console.WriteLine("📱移动端体验:http://www.izhaorui.cn/h5");
Console.WriteLine($"Swagger地址:[后端启动地址]/swagger/index.html");
} }
} }
} }
+26
View File
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>8632</NoWarn>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AspectCore.Abstractions" Version="2.4.0" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="UAParser" Version="3.1.47" />
<PackageReference Include="IPTools.China" Version="1.6.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.7" />
<PackageReference Include="JinianNet.JNTemplate" Version="2.3.3" />
<PackageReference Include="MiniExcel" Version="1.31.2" />
<PackageReference Include="CSRedisCore" Version="3.8.670" />
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
</ItemGroup>
</Project>
+4 -7
View File
@@ -22,10 +22,6 @@
-Based on. NET5/. A common rights management platform (RBAC model) implemented by NET7. Integrate the latest technology for efficient and rapid development, front-end and back-end separation mode, out of the box. -Based on. NET5/. A common rights management platform (RBAC model) implemented by NET7. Integrate the latest technology for efficient and rapid development, front-end and back-end separation mode, out of the box.
- Less code, simple to learn, easy to understand, powerful, easy to extend, lightweight, make web development faster, simpler and more efficient (say goodbye to 996), solve 70% of repetitive work, focus on your business, easy development from now on! - Less code, simple to learn, easy to understand, powerful, easy to extend, lightweight, make web development faster, simpler and more efficient (say goodbye to 996), solve 70% of repetitive work, focus on your business, easy development from now on!
- 提供了技术栈(Ant Design Vue)版[Ant Design Vue](https://gitee.com/billzh/mc-dull.git) - 提供了技术栈(Ant Design Vue)版[Ant Design Vue](https://gitee.com/billzh/mc-dull.git)
- 七牛云通用云产品优惠券:[点我进入](https://s.qiniu.com/FzEfay)。
- 腾讯云秒杀场:[☛☛ 点我进入 ☚☚](https://curl.qcloud.com/4yEoRquq)。
- 腾讯云优惠券:[☛☛ 点我领取 ☚☚](https://curl.qcloud.com/5J4nag8D)。
- 阿里云特惠专区:[☛☛ 点我进入 ☚☚](https://www.aliyun.com/minisite/goods?userCode=uotn5vt1&share_source=copy_link)
``` ```
If it helps you, you can click "Star" in the upper right corner to collect it, so that the author has the motivation to continue to go on for free, thank you! ~ If it helps you, you can click "Star" in the upper right corner to collect it, so that the author has the motivation to continue to go on for free, thank you! ~
@@ -52,7 +48,7 @@ Since it is a personal project, the funds are limited, and the experience server
| repository | Github | Gitee | | repository | Github | Gitee |
| -------------- | ------------------------------------------------------ | ---------------------------------------------------------------- | | -------------- | ------------------------------------------------------ | ---------------------------------------------------------------- |
| Net7 | [Clone/Download](https://github.com/izhaorui/Zr.Admin.NET) | [Clone/Download](https://gitee.com/izory/ZrAdminNetCore/tree/net7.0/) | | Net7 | [Clone/Download](https://github.com/izhaorui/Zr.Admin.NET) | [Clone/Download](https://gitee.com/izory/ZrAdminNetCore) |
| Vue3(Hot) | [Clone/Download](https://github.com/izhaorui/ZR.Admin.Vue3) | [Clone/Download](https://gitee.com/izory/ZRAdmin-vue) | | Vue3(Hot) | [Clone/Download](https://github.com/izhaorui/ZR.Admin.Vue3) | [Clone/Download](https://gitee.com/izory/ZRAdmin-vue) |
## 🍁 Front-end technology ## 🍁 Front-end technology
@@ -61,7 +57,7 @@ Vue Front-end technology stack: Based on Vue2.x/Vue3.x/UniApp, Vue, Vue-router,
## 🍀 Back-end technology ## 🍀 Back-end technology
- Core Framework: . Net5.0/. Net7.0 + Web API + sqlsugar + swagger + signalR + IpRateLimit + Quartz.net + Redis - Core Framework: . Net7.0 + Web API + sqlsugar + swagger + signalR + IpRateLimit + Quartz.net + Redis
- Scheduled tasks: Quartz.Net component that supports the execution of assemblies or HTTP network requests - Scheduled tasks: Quartz.Net component that supports the execution of assemblies or HTTP network requests
- Security support: filters (data permission filtering), SQL injection, request forgery - Security support: filters (data permission filtering), SQL injection, request forgery
- Log management: NLog, login log, operation log, scheduled task log - Log management: NLog, login log, operation log, scheduled task log
@@ -99,7 +95,8 @@ Vue Front-end technology stack: Based on Vue2.x/Vue3.x/UniApp, Vue, Vue-router,
## 🍻 Project structure ## 🍻 Project structure
``` ```
├─ZR.Service ->[服务层类库]:提供WebApi接口调用; ├─ZR.Service ->[你的业务服务层类库]:提供WebApi接口调用;
├─ZR.ServiceCore ->[系统服务层类库]:提供WebApi接口调用;
├─ZR.Repository ->[仓库层类库]:方便提供有执行存储过程的操作; ├─ZR.Repository ->[仓库层类库]:方便提供有执行存储过程的操作;
├─ZR.Model ->[实体层类库]:提供项目中的数据库表、数据传输对象; ├─ZR.Model ->[实体层类库]:提供项目中的数据库表、数据传输对象;
├─ZR.Admin.WebApi ->[webapi接口]:为Vue版或其他三方系统提供接口服务。 ├─ZR.Admin.WebApi ->[webapi接口]:为Vue版或其他三方系统提供接口服务。
+14 -6
View File
@@ -5,7 +5,7 @@
<a href="https://gitee.com/izory/ZrAdminNetCore"><img src="https://gitee.com/izory/ZrAdminNetCore/badge/star.svg?theme=dark"></a> <a href="https://gitee.com/izory/ZrAdminNetCore"><img src="https://gitee.com/izory/ZrAdminNetCore/badge/star.svg?theme=dark"></a>
<a href='https://gitee.com/izory/ZrAdminNetCore/members'><img src='https://gitee.com/izory/ZrAdminNetCore/badge/fork.svg?theme=dark' alt='fork'></img></a> <a href='https://gitee.com/izory/ZrAdminNetCore/members'><img src='https://gitee.com/izory/ZrAdminNetCore/badge/fork.svg?theme=dark' alt='fork'></img></a>
<a href="https://gitee.com/izory/ZrAdminNetCore/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a> <a href="https://gitee.com/izory/ZrAdminNetCore/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mashape/apistatus.svg"></a>
<a href="http://www.izhaorui.cn/doc/changelog.html"><img src="https://img.shields.io/badge/更新日志-20230713-yellow"/></a> <a href="http://www.izhaorui.cn/doc/changelog.html"><img src="https://img.shields.io/badge/更新日志-20230811-yellow"/></a>
</p> </p>
--- ---
@@ -28,7 +28,7 @@
- 阿里云特惠专区:[☛☛ 点我进入 ☚☚](https://www.aliyun.com/minisite/goods?userCode=uotn5vt1&share_source=copy_link) - 阿里云特惠专区:[☛☛ 点我进入 ☚☚](https://www.aliyun.com/minisite/goods?userCode=uotn5vt1&share_source=copy_link)
``` ```
如果对您有帮助,您可以点右上角 “Star” 收藏一下 ,这样作者才有继续免费下去的动力,谢谢!~ 如果对您有帮助,您可以点右上角 “Star” 收藏一下 ,谢谢!~
``` ```
## 🍿 在线体验 ## 🍿 在线体验
@@ -38,7 +38,7 @@
- Vue3.x 版本体验:[http://www.izhaorui.cn/vue3](http://www.izhaorui.cn/vue3) - Vue3.x 版本体验:[http://www.izhaorui.cn/vue3](http://www.izhaorui.cn/vue3)
- Vue2.x 版本体验:[http://www.izhaorui.cn/admin](http://www.izhaorui.cn/admin) - Vue2.x 版本体验:[http://www.izhaorui.cn/admin](http://www.izhaorui.cn/admin)
- Uniapp 版本体验:[http://www.izhaorui.cn/h5](http://www.izhaorui.cn/h5) - Uniapp 版本体验:[http://www.izhaorui.cn/h5](http://www.izhaorui.cn/h5)
- 账号密码:admin/123456 - 账号密码:admin/123456,普通用户 user/123456
| H5 | 微信小程序 | | H5 | 微信小程序 |
| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
@@ -52,7 +52,7 @@
| 仓库 | Github | Gitee | | 仓库 | Github | Gitee |
| -------------- | ------------------------------------------------------ | ---------------------------------------------------------------- | | -------------- | ------------------------------------------------------ | ---------------------------------------------------------------- |
| Net7 | [克隆/下载](https://github.com/izhaorui/Zr.Admin.NET) | [克隆/下载](https://gitee.com/izory/ZrAdminNetCore/tree/net7.0/) | | Net7 | [克隆/下载](https://github.com/izhaorui/Zr.Admin.NET) | [克隆/下载](https://gitee.com/izory/ZrAdminNetCore) |
| Vue3(推荐使用) | [克隆/下载](https://github.com/izhaorui/ZR.Admin.Vue3) | [克隆/下载](https://gitee.com/izory/ZRAdmin-vue) | | Vue3(推荐使用) | [克隆/下载](https://github.com/izhaorui/ZR.Admin.Vue3) | [克隆/下载](https://gitee.com/izory/ZRAdmin-vue) |
## 🍁 前端技术 ## 🍁 前端技术
@@ -72,6 +72,7 @@ Vue 版前端技术栈 :基于 vue2.x/vue3.x/uniapp、vuex、vue-router 、vue
- 分库分表:使用 orm sqlsugar 可以很轻松的实现分库分库性能优越 - 分库分表:使用 orm sqlsugar 可以很轻松的实现分库分库性能优越
- 多 租 户:支持多租户功能 - 多 租 户:支持多租户功能
- 缓存数据:内置内存缓存和 Redis - 缓存数据:内置内存缓存和 Redis
- signalR:使用signalr管理用户在线状态
## 🍖 内置功能 ## 🍖 内置功能
@@ -88,18 +89,24 @@ Vue 版前端技术栈 :基于 vue2.x/vue3.x/uniapp、vuex、vue-router 、vue
11. 在线构建器:拖动表单元素生成相应的 VUE 代码(仅支持 vue2)。 11. 在线构建器:拖动表单元素生成相应的 VUE 代码(仅支持 vue2)。
12. 任务系统:基于 Quartz.NET,可以在线(添加、修改、删除、手动执行)任务调度包含执行结果日志。 12. 任务系统:基于 Quartz.NET,可以在线(添加、修改、删除、手动执行)任务调度包含执行结果日志。
13. 文章管理:可以写文章记录。 13. 文章管理:可以写文章记录。
14. 代码生成:可以一键生成前后端代码(.cs、.vue、.js、.sql 等)支持下载,自定义配置前端展示控件、让开发更快捷高效(史上最强)。 14. 代码生成:可以一键生成前后端代码(.cs、.vue、.js、.sql、uniapp 等)支持下载,自定义配置前端展示控件、让开发更快捷高效(史上最强)。
15. 参数管理:对系统动态配置常用参数。 15. 参数管理:对系统动态配置常用参数。
16. 发送邮件:可以对多个用户进行发送邮件。 16. 发送邮件:可以对多个用户进行发送邮件。
17. 文件管理:可以进行上传文件管理,目前支持上传到本地、阿里云。 17. 文件管理:可以进行上传文件管理,目前支持上传到本地、阿里云。
18. 通知管理:系统通知公告信息发布维护,使用 signalr 实现对用户实时通知。 18. 通知管理:系统通知公告信息发布维护,使用 signalr 实现对用户实时通知。
19. 账号注册:可以注册账号登录系统。 19. 账号注册:可以注册账号登录系统。
20. 多语言管理:支持静态、后端动态配置国际化。目前只支持中、英、繁体(仅支持 vue3) 20. 多语言管理:支持静态、后端动态配置国际化。目前只支持中、英、繁体(仅支持 vue3)
21. 在线用户:可以查看正在登录使用的用户,可以对其踢出、通知操作
22. db 审计日志:数据库审计功能
23. 三方登录:提供三方登录实现逻辑
## 🍻 项目结构 ## 🍻 项目结构
![alt](https://gitee.com/izory/ZrAdminNetCore/raw/master/document/images/kj.png)
``` ```
├─ZR.Service ->[服务层类库]:提供WebApi接口调用; ├─ZR.Service ->[你的业务服务层类库]:提供WebApi接口调用;
├─ZR.ServiceCore ->[系统服务层类库]:提供WebApi接口调用;
├─ZR.Repository ->[仓库层类库]:方便提供有执行存储过程的操作; ├─ZR.Repository ->[仓库层类库]:方便提供有执行存储过程的操作;
├─ZR.Model ->[实体层类库]:提供项目中的数据库表、数据传输对象; ├─ZR.Model ->[实体层类库]:提供项目中的数据库表、数据传输对象;
├─ZR.Admin.WebApi ->[webapi接口]:为Vue版或其他三方系统提供接口服务。 ├─ZR.Admin.WebApi ->[webapi接口]:为Vue版或其他三方系统提供接口服务。
@@ -195,6 +202,7 @@ Vue 版前端技术栈 :基于 vue2.x/vue3.x/uniapp、vuex、vue-router 、vue
- 👉SqlSugar:[SqlSugar](https://gitee.com/dotnetchina/SqlSugar) - 👉SqlSugar:[SqlSugar](https://gitee.com/dotnetchina/SqlSugar)
- 👉vue-element-admin:[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin) - 👉vue-element-admin:[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
- 👉Meiam.System:[Meiam.System](https://github.com/91270/Meiam.System) - 👉Meiam.System:[Meiam.System](https://github.com/91270/Meiam.System)
- 👉Furion:[Furion](https://gitee.com/dotnetchina/Furion)
## 🎀 捐赠 ## 🎀 捐赠
@@ -1,10 +1,9 @@
using Infrastructure.Extensions; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Newtonsoft.Json; using Newtonsoft.Json;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Model.System; using ZR.Model.System;
using ZR.Service.IService;
using ZR.Service.System; using ZR.Service.System;
using ZR.Service.System.IService; using ZR.Service.System.IService;
@@ -14,6 +13,8 @@ namespace ZR.Admin.WebApi.Controllers
/// 公共模块 /// 公共模块
/// </summary> /// </summary>
[Route("[controller]/[action]")] [Route("[controller]/[action]")]
[ApiExplorerSettings(GroupName = "sys")]
//[Produces("application/json")]
public class CommonController : BaseController public class CommonController : BaseController
{ {
private OptionsSetting OptionsSetting; private OptionsSetting OptionsSetting;
@@ -21,18 +22,22 @@ namespace ZR.Admin.WebApi.Controllers
private IWebHostEnvironment WebHostEnvironment; private IWebHostEnvironment WebHostEnvironment;
private ISysFileService SysFileService; private ISysFileService SysFileService;
private IHelloService HelloService;
public CommonController( public CommonController(
IOptions<OptionsSetting> options, IOptions<OptionsSetting> options,
IWebHostEnvironment webHostEnvironment, IWebHostEnvironment webHostEnvironment,
ISysFileService fileService) ISysFileService fileService,
IHelloService helloService)
{ {
WebHostEnvironment = webHostEnvironment; WebHostEnvironment = webHostEnvironment;
SysFileService = fileService; SysFileService = fileService;
OptionsSetting = options.Value; OptionsSetting = options.Value;
HelloService = helloService;
} }
/// <summary> /// <summary>
/// hello /// home
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Route("/")] [Route("/")]
@@ -43,6 +48,18 @@ namespace ZR.Admin.WebApi.Controllers
"如果觉得项目有用,打赏作者喝杯咖啡作为奖励\n☛☛http://www.izhaorui.cn/doc/support.html\n"); "如果觉得项目有用,打赏作者喝杯咖啡作为奖励\n☛☛http://www.izhaorui.cn/doc/support.html\n");
} }
/// <summary>
/// hello
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[Route("/hello")]
[HttpGet]
public IActionResult Hello(string name)
{
return Ok(HelloService.SayHello(name));
}
/// <summary> /// <summary>
/// 企业消息测试 /// 企业消息测试
/// </summary> /// </summary>
@@ -11,6 +11,7 @@ namespace ZR.Admin.WebApi.Controllers
/// 文章目录Controller /// 文章目录Controller
/// </summary> /// </summary>
[Route("article/ArticleCategory")] [Route("article/ArticleCategory")]
[ApiExplorerSettings(GroupName = "article")]
public class ArticleCategoryController : BaseController public class ArticleCategoryController : BaseController
{ {
/// <summary> /// <summary>
@@ -59,7 +60,7 @@ namespace ZR.Admin.WebApi.Controllers
public IActionResult GetArticleCategory(int CategoryId) public IActionResult GetArticleCategory(int CategoryId)
{ {
var response = _ArticleCategoryService.GetFirst(x => x.CategoryId == CategoryId); var response = _ArticleCategoryService.GetFirst(x => x.CategoryId == CategoryId);
return SUCCESS(response); return SUCCESS(response);
} }
@@ -13,6 +13,7 @@ namespace ZR.Admin.WebApi.Controllers
/// </summary> /// </summary>
[Verify] [Verify]
[Route("article")] [Route("article")]
[ApiExplorerSettings(GroupName = "article")]
public class ArticleController : BaseController public class ArticleController : BaseController
{ {
/// <summary> /// <summary>
@@ -32,7 +33,7 @@ namespace ZR.Admin.WebApi.Controllers
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[HttpGet("list")] [HttpGet("list")]
[ActionPermissionFilter(Permission = "system:article:list")] [ActionPermissionFilter(RolePermi = "admin")]
public IActionResult Query([FromQuery] ArticleQueryDto parm) public IActionResult Query([FromQuery] ArticleQueryDto parm)
{ {
var response = _ArticleService.GetList(parm); var response = _ArticleService.GetList(parm);
@@ -87,7 +88,7 @@ namespace ZR.Admin.WebApi.Controllers
var model = response.Adapt<ArticleDto>(); var model = response.Adapt<ArticleDto>();
if (model.IsPublic == 0 && userId != model.UserId) if (model.IsPublic == 0 && userId != model.UserId)
{ {
return ToResponse(Infrastructure.ResultCode.CUSTOM_ERROR, "访问失败"); return ToResponse(ResultCode.CUSTOM_ERROR, "访问失败");
} }
if (model != null) if (model != null)
{ {
@@ -1,16 +1,10 @@
using Infrastructure; using Microsoft.AspNetCore.Mvc;
using Infrastructure.Attribute;
using Infrastructure.Enums;
using Infrastructure.Extensions;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using SqlSugar; using SqlSugar;
using ZR.Admin.WebApi.Extensions; using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.CodeGenerator; using ZR.CodeGenerator;
using ZR.CodeGenerator.Model; using ZR.CodeGenerator.Model;
using ZR.CodeGenerator.Service; using ZR.CodeGenerator.Service;
using ZR.Common;
using ZR.Model; using ZR.Model;
using ZR.Model.System.Dto; using ZR.Model.System.Dto;
using ZR.Model.System.Generate; using ZR.Model.System.Generate;
@@ -23,6 +17,7 @@ namespace ZR.Admin.WebApi.Controllers
/// </summary> /// </summary>
[Verify] [Verify]
[Route("tool/gen")] [Route("tool/gen")]
[ApiExplorerSettings(GroupName = "sys")]
public class CodeGeneratorController : BaseController public class CodeGeneratorController : BaseController
{ {
private readonly CodeGeneraterService _CodeGeneraterService = new CodeGeneraterService(); private readonly CodeGeneraterService _CodeGeneraterService = new CodeGeneraterService();
@@ -14,6 +14,7 @@ namespace ZR.Admin.WebApi.Controllers
/// </summary> /// </summary>
[Verify] [Verify]
[Route("system/CommonLang")] [Route("system/CommonLang")]
[ApiExplorerSettings(GroupName = "sys")]
public class CommonLangController : BaseController public class CommonLangController : BaseController
{ {
/// <summary> /// <summary>
@@ -14,6 +14,7 @@ namespace ZR.Admin.WebApi.Controllers
/// </summary> /// </summary>
[Verify] [Verify]
[Route("system/config")] [Route("system/config")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysConfigController : BaseController public class SysConfigController : BaseController
{ {
/// <summary> /// <summary>
@@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Collections; using System.Collections;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Model.System; using ZR.Model.System;
using ZR.Model.System.Dto; using ZR.Model.System.Dto;
@@ -13,6 +12,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary> /// </summary>
[Verify] [Verify]
[Route("system/dept")] [Route("system/dept")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysDeptController : BaseController public class SysDeptController : BaseController
{ {
public ISysDeptService DeptService; public ISysDeptService DeptService;
@@ -1,5 +1,4 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Model; using ZR.Model;
using ZR.Model.System; using ZR.Model.System;
@@ -13,6 +12,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary> /// </summary>
[Verify] [Verify]
[Route("system/dict/data")] [Route("system/dict/data")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysDictDataController : BaseController public class SysDictDataController : BaseController
{ {
private readonly ISysDictDataService SysDictDataService; private readonly ISysDictDataService SysDictDataService;
@@ -13,6 +13,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary> /// </summary>
[Verify] [Verify]
[Route("system/dict/type")] [Route("system/dict/type")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysDictTypeController : BaseController public class SysDictTypeController : BaseController
{ {
private readonly ISysDictService SysDictService; private readonly ISysDictService SysDictService;
@@ -1,13 +1,9 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using SqlSugar; using SqlSugar;
using Infrastructure.Attribute;
using Infrastructure.Enums;
using Infrastructure.Model;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Common;
using ZR.Model.System; using ZR.Model.System;
using ZR.Service.System.IService;
using ZR.Model.System.Dto; using ZR.Model.System.Dto;
using ZR.Service.System.IService;
namespace ZR.Admin.WebApi.Controllers namespace ZR.Admin.WebApi.Controllers
{ {
@@ -16,6 +12,7 @@ namespace ZR.Admin.WebApi.Controllers
/// </summary> /// </summary>
[Verify] [Verify]
[Route("tool/file")] [Route("tool/file")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysFileController : BaseController public class SysFileController : BaseController
{ {
/// <summary> /// <summary>
@@ -37,15 +34,13 @@ namespace ZR.Admin.WebApi.Controllers
[ActionPermissionFilter(Permission = "tool:file:list")] [ActionPermissionFilter(Permission = "tool:file:list")]
public IActionResult QuerySysFile([FromQuery] SysFileQueryDto parm) public IActionResult QuerySysFile([FromQuery] SysFileQueryDto parm)
{ {
//开始拼装查询条件
var predicate = Expressionable.Create<SysFile>(); var predicate = Expressionable.Create<SysFile>();
//搜索条件查询语法参考Sqlsugar
predicate = predicate.AndIF(parm.BeginCreate_time != null, it => it.Create_time >= parm.BeginCreate_time); predicate = predicate.AndIF(parm.BeginCreate_time != null, it => it.Create_time >= parm.BeginCreate_time);
predicate = predicate.AndIF(parm.EndCreate_time != null, it => it.Create_time <= parm.EndCreate_time); predicate = predicate.AndIF(parm.EndCreate_time != null, it => it.Create_time <= parm.EndCreate_time);
predicate = predicate.AndIF(parm.StoreType != null, m => m.StoreType == parm.StoreType); predicate = predicate.AndIF(parm.StoreType != null, m => m.StoreType == parm.StoreType);
predicate = predicate.AndIF(parm.FileId != null, m => m.Id == parm.FileId); predicate = predicate.AndIF(parm.FileId != null, m => m.Id == parm.FileId);
//搜索条件查询语法参考Sqlsugar
var response = _SysFileService.GetPages(predicate.ToExpression(), parm, x => x.Id, OrderByType.Desc); var response = _SysFileService.GetPages(predicate.ToExpression(), parm, x => x.Id, OrderByType.Desc);
return SUCCESS(response); return SUCCESS(response);
} }
@@ -1,11 +1,7 @@
using IPTools.Core; using Lazy.Captcha.Core;
using Lazy.Captcha.Core;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using UAParser;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Admin.WebApi.Framework;
using ZR.Model.System; using ZR.Model.System;
using ZR.Model.System.Dto; using ZR.Model.System.Dto;
using ZR.Service.System; using ZR.Service.System;
@@ -16,9 +12,10 @@ namespace ZR.Admin.WebApi.Controllers.System
/// <summary> /// <summary>
/// 登录 /// 登录
/// </summary> /// </summary>
[ApiExplorerSettings(GroupName = "sys")]
public class SysLoginController : BaseController public class SysLoginController : BaseController
{ {
static readonly NLog.Logger logger = NLog.LogManager.GetLogger("LoginController"); //static readonly NLog.Logger logger = NLog.LogManager.GetLogger("LoginController");
private readonly IHttpContextAccessor httpContextAccessor; private readonly IHttpContextAccessor httpContextAccessor;
private readonly ISysUserService sysUserService; private readonly ISysUserService sysUserService;
private readonly ISysMenuService sysMenuService; private readonly ISysMenuService sysMenuService;
@@ -27,7 +24,7 @@ namespace ZR.Admin.WebApi.Controllers.System
private readonly ICaptcha SecurityCodeHelper; private readonly ICaptcha SecurityCodeHelper;
private readonly ISysConfigService sysConfigService; private readonly ISysConfigService sysConfigService;
private readonly ISysRoleService roleService; private readonly ISysRoleService roleService;
private readonly OptionsSetting jwtSettings; private readonly OptionsSetting optionSettings;
public SysLoginController( public SysLoginController(
IHttpContextAccessor contextAccessor, IHttpContextAccessor contextAccessor,
@@ -38,7 +35,7 @@ namespace ZR.Admin.WebApi.Controllers.System
ISysConfigService configService, ISysConfigService configService,
ISysRoleService sysRoleService, ISysRoleService sysRoleService,
ICaptcha captcha, ICaptcha captcha,
IOptions<OptionsSetting> jwtSettings) IOptions<OptionsSetting> optionSettings)
{ {
httpContextAccessor = contextAccessor; httpContextAccessor = contextAccessor;
SecurityCodeHelper = captcha; SecurityCodeHelper = captcha;
@@ -48,7 +45,7 @@ namespace ZR.Admin.WebApi.Controllers.System
this.permissionService = permissionService; this.permissionService = permissionService;
this.sysConfigService = configService; this.sysConfigService = configService;
roleService = sysRoleService; roleService = sysRoleService;
this.jwtSettings = jwtSettings.Value; this.optionSettings = optionSettings.Value;
} }
@@ -70,15 +67,17 @@ namespace ZR.Admin.WebApi.Controllers.System
return ToResponse(ResultCode.CAPTCHA_ERROR, "验证码错误"); return ToResponse(ResultCode.CAPTCHA_ERROR, "验证码错误");
} }
var user = sysLoginService.Login(loginBody, RecordLogInfo(httpContextAccessor.HttpContext)); sysLoginService.CheckLockUser(loginBody.Username);
string location = HttpContextExtension.GetIpInfo(loginBody.LoginIP);
var user = sysLoginService.Login(loginBody, new SysLogininfor() { LoginLocation = location });
List<SysRole> roles = roleService.SelectUserRoleListByUserId(user.UserId); List<SysRole> roles = roleService.SelectUserRoleListByUserId(user.UserId);
//权限集合 eg *:*:*,system:user:list //权限集合 eg *:*:*,system:user:list
List<string> permissions = permissionService.GetMenuPermission(user); List<string> permissions = permissionService.GetMenuPermission(user);
LoginUser loginUser = new(user, roles, permissions); TokenModel loginUser = new(user.Adapt<TokenModel>(), roles.Adapt<List<Roles>>());
CacheService.SetUserPerms(GlobalConstant.UserPermKEY + user.UserId, permissions); CacheService.SetUserPerms(GlobalConstant.UserPermKEY + user.UserId, permissions);
return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser), jwtSettings.JwtSettings)); return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser)));
} }
/// <summary> /// <summary>
@@ -118,6 +117,7 @@ namespace ZR.Admin.WebApi.Controllers.System
//权限集合 eg *:*:*,system:user:list //权限集合 eg *:*:*,system:user:list
List<string> permissions = permissionService.GetMenuPermission(user); List<string> permissions = permissionService.GetMenuPermission(user);
user.WelcomeContent = GlobalConstant.WelcomeMessages[new Random().Next(0, GlobalConstant.WelcomeMessages.Length)]; user.WelcomeContent = GlobalConstant.WelcomeMessages[new Random().Next(0, GlobalConstant.WelcomeMessages.Length)];
return SUCCESS(new { user, roles, permissions }); return SUCCESS(new { user, roles, permissions });
} }
@@ -152,28 +152,6 @@ namespace ZR.Admin.WebApi.Controllers.System
return SUCCESS(obj); return SUCCESS(obj);
} }
/// <summary>
/// 记录用户登陆信息
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public SysLogininfor RecordLogInfo(HttpContext context)
{
var ipAddr = context.GetClientUserIp();
var ip_info = IpTool.Search(ipAddr);
ClientInfo clientInfo = context.GetClientInfo();
SysLogininfor sysLogininfor = new()
{
Browser = clientInfo.ToString(),
Os = clientInfo.OS.ToString(),
Ipaddr = ipAddr,
UserName = context.GetName(),
LoginLocation = ip_info?.Province + "-" + ip_info?.City
};
return sysLogininfor;
}
/// <summary> /// <summary>
/// 注册 /// 注册
/// </summary> /// </summary>
@@ -181,7 +159,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// <returns></returns> /// <returns></returns>
[HttpPost("/register")] [HttpPost("/register")]
[AllowAnonymous] [AllowAnonymous]
[Log(Title = "注册", BusinessType = Infrastructure.Enums.BusinessType.INSERT)] [Log(Title = "注册", BusinessType = BusinessType.INSERT)]
public IActionResult Register([FromBody] RegisterDto dto) public IActionResult Register([FromBody] RegisterDto dto)
{ {
SysConfig config = sysConfigService.GetSysConfigByKey("sys.account.register"); SysConfig config = sysConfigService.GetSysConfigByKey("sys.account.register");
@@ -194,7 +172,7 @@ namespace ZR.Admin.WebApi.Controllers.System
{ {
return ToResponse(ResultCode.CAPTCHA_ERROR, "验证码错误"); return ToResponse(ResultCode.CAPTCHA_ERROR, "验证码错误");
} }
SysUser user = sysUserService.Register(dto); SysUser user = sysUserService.Register(dto);
if (user.UserId > 0) if (user.UserId > 0)
{ {
@@ -202,5 +180,86 @@ namespace ZR.Admin.WebApi.Controllers.System
} }
return ToResponse(ResultCode.CUSTOM_ERROR, "注册失败,请联系管理员"); return ToResponse(ResultCode.CUSTOM_ERROR, "注册失败,请联系管理员");
} }
#region 二维码登录
/// <summary>
/// 生成二维码
/// </summary>
/// <param name="uuid"></param>
/// <param name="deviceId"></param>
/// <returns></returns>
[HttpGet("/GenerateQrcode")]
public IActionResult GenerateQrcode(string uuid, string deviceId)
{
var state = Guid.NewGuid().ToString();
var dict = new Dictionary<string, object>
{
{ "state", state }
};
CacheService.SetScanLogin(uuid, dict);
return SUCCESS(new
{
status = 1,
state,
uuid,
codeContent = new { uuid, deviceId }// "https://qm.qq.com/cgi-bin/qm/qr?k=kgt4HsckdljU0VM-0kxND6d_igmfuPlL&authKey=r55YUbruiKQ5iwC/folG7KLCmZ++Y4rQVgNlvLbUniUMkbk24Y9+zNuOmOnjAjRc&noverify=0"
});
}
/// <summary>
/// 轮询判断扫码状态
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("/VerifyScan")]
[AllowAnonymous]
public IActionResult VerifyScan([FromBody] ScanDto dto)
{
int status = -1;
object token = string.Empty;
if (CacheService.GetScanLogin(dto.Uuid) is Dictionary<string, object> str)
{
status = 0;
str.TryGetValue("token", out token);
if (str.ContainsKey("status") && (string)str.GetValueOrDefault("status") == "success")
{
status = 2;//扫码成功
CacheService.RemoveScanLogin(dto.Uuid);
}
}
return SUCCESS(new { status, token });
}
/// <summary>
/// 移动端扫码登录
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
[HttpPost("/ScanLogin")]
[Log(Title = "扫码登录")]
[Verify]
public IActionResult ScanLogin([FromBody] ScanDto dto)
{
if (dto == null) { return ToResponse(ResultCode.CUSTOM_ERROR, "扫码失败"); }
var name = App.HttpContext.GetName();
sysLoginService.CheckLockUser(name);
TokenModel tokenModel = JwtUtil.GetLoginUser(HttpContext);
if (CacheService.GetScanLogin(dto.Uuid) is not null)
{
Dictionary<string, object> dict = new() { };
dict.Add("status", "success");
dict.Add("token", JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(tokenModel)));
CacheService.SetScanLogin(dto.Uuid, dict);
return SUCCESS(1);
}
return ToResponse(ResultCode.FAIL, "二维码已失效");
}
#endregion
} }
} }
@@ -1,5 +1,4 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Model.System; using ZR.Model.System;
using ZR.Model.System.Dto; using ZR.Model.System.Dto;
@@ -12,6 +11,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary> /// </summary>
[Verify] [Verify]
[Route("/system/menu")] [Route("/system/menu")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysMenuController : BaseController public class SysMenuController : BaseController
{ {
private readonly ISysRoleService sysRoleService; private readonly ISysRoleService sysRoleService;
@@ -1,14 +1,12 @@
using Infrastructure.Constant; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using SqlSugar; using SqlSugar;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Admin.WebApi.Hubs;
using ZR.Model; using ZR.Model;
using ZR.Model.System; using ZR.Model.System;
using ZR.Model.System.Dto; using ZR.Model.System.Dto;
using ZR.Service.System.IService; using ZR.Service.System.IService;
using ZR.ServiceCore.Signalr;
namespace ZR.Admin.WebApi.Controllers.System namespace ZR.Admin.WebApi.Controllers.System
{ {
@@ -17,6 +15,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary> /// </summary>
[Verify] [Verify]
[Route("system/notice")] [Route("system/notice")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysNoticeController : BaseController public class SysNoticeController : BaseController
{ {
/// <summary> /// <summary>
@@ -1,7 +1,5 @@
using Infrastructure.Extensions; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using SqlSugar; using SqlSugar;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Model; using ZR.Model;
using ZR.Model.System; using ZR.Model.System;
@@ -14,6 +12,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary> /// </summary>
[Verify] [Verify]
[Route("system/post")] [Route("system/post")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysPostController : BaseController public class SysPostController : BaseController
{ {
private readonly ISysPostService PostService; private readonly ISysPostService PostService;
@@ -68,7 +67,7 @@ namespace ZR.Admin.WebApi.Controllers.System
throw new CustomException($"修改岗位{post.PostName}失败,岗位编码已存在"); throw new CustomException($"修改岗位{post.PostName}失败,岗位编码已存在");
} }
post.ToCreate(HttpContext); post.ToCreate(HttpContext);
return ToResponse(PostService.Add(post)); return ToResponse(PostService.Add(post));
} }
@@ -122,7 +121,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// 岗位导出 /// 岗位导出
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[Log(BusinessType = BusinessType.EXPORT, IsSaveResponseData = false, Title= "岗位导出")] [Log(BusinessType = BusinessType.EXPORT, IsSaveResponseData = false, Title = "岗位导出")]
[HttpGet("export")] [HttpGet("export")]
[ActionPermissionFilter(Permission = "system:post:export")] [ActionPermissionFilter(Permission = "system:post:export")]
public IActionResult Export() public IActionResult Export()
@@ -1,5 +1,4 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Model.System; using ZR.Model.System;
using ZR.Model.System.Dto; using ZR.Model.System.Dto;
@@ -12,6 +11,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary> /// </summary>
[Verify] [Verify]
[Route("system/user/profile")] [Route("system/user/profile")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysProfileController : BaseController public class SysProfileController : BaseController
{ {
private readonly ISysUserService UserService; private readonly ISysUserService UserService;
@@ -1,5 +1,4 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Model; using ZR.Model;
using ZR.Model.System; using ZR.Model.System;
@@ -13,6 +12,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary> /// </summary>
[Verify] [Verify]
[Route("system/role")] [Route("system/role")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysRoleController : BaseController public class SysRoleController : BaseController
{ {
private readonly ISysRoleService sysRoleService; private readonly ISysRoleService sysRoleService;
@@ -1,7 +1,6 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MiniExcelLibs; using MiniExcelLibs;
using SqlSugar; using SqlSugar;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Model; using ZR.Model;
using ZR.Model.System; using ZR.Model.System;
@@ -15,6 +14,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary> /// </summary>
[Verify] [Verify]
[Route("system/user")] [Route("system/user")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysUserController : BaseController public class SysUserController : BaseController
{ {
private readonly ISysUserService UserService; private readonly ISysUserService UserService;
@@ -93,9 +93,10 @@ namespace ZR.Admin.WebApi.Controllers.System
} }
user.Create_by = HttpContext.GetName(); user.Create_by = HttpContext.GetName();
user.Create_time = DateTime.Now;
user.Password = NETCore.Encrypt.EncryptProvider.Md5(user.Password); user.Password = NETCore.Encrypt.EncryptProvider.Md5(user.Password);
return ToResponse(UserService.InsertUser(user)); return SUCCESS(UserService.InsertUser(user));
} }
/// <summary> /// <summary>
@@ -143,7 +144,7 @@ namespace ZR.Admin.WebApi.Controllers.System
public IActionResult Remove(int userid = 0) public IActionResult Remove(int userid = 0)
{ {
if (userid <= 0) { return ToResponse(ApiResult.Error(101, "请求参数错误")); } if (userid <= 0) { return ToResponse(ApiResult.Error(101, "请求参数错误")); }
if (userid == 1) return ToResponse(Infrastructure.ResultCode.FAIL, "不能删除管理员账号"); if (userid == 1) return ToResponse(ResultCode.FAIL, "不能删除管理员账号");
int result = UserService.DeleteUser(userid); int result = UserService.DeleteUser(userid);
return ToResponse(result); return ToResponse(result);
@@ -156,7 +157,7 @@ namespace ZR.Admin.WebApi.Controllers.System
[HttpPut("resetPwd")] [HttpPut("resetPwd")]
[Log(Title = "重置密码", BusinessType = BusinessType.UPDATE)] [Log(Title = "重置密码", BusinessType = BusinessType.UPDATE)]
[ActionPermissionFilter(Permission = "system:user:resetPwd")] [ActionPermissionFilter(Permission = "system:user:resetPwd")]
public IActionResult ResetPwd([FromBody] SysUser sysUser) public IActionResult ResetPwd([FromBody] SysUserDto sysUser)
{ {
//密码md5 //密码md5
sysUser.Password = NETCore.Encrypt.EncryptProvider.Md5(sysUser.Password); sysUser.Password = NETCore.Encrypt.EncryptProvider.Md5(sysUser.Password);
@@ -10,6 +10,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary> /// </summary>
[Verify] [Verify]
[Route("system/userRole")] [Route("system/userRole")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysUserRoleController : BaseController public class SysUserRoleController : BaseController
{ {
private readonly ISysUserRoleService SysUserRoleService; private readonly ISysUserRoleService SysUserRoleService;
@@ -43,7 +44,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// <returns></returns> /// <returns></returns>
[HttpPost("create")] [HttpPost("create")]
[ActionPermissionFilter(Permission = "system:roleusers:add")] [ActionPermissionFilter(Permission = "system:roleusers:add")]
[Log(Title = "添加角色用户", BusinessType = Infrastructure.Enums.BusinessType.INSERT)] [Log(Title = "添加角色用户", BusinessType = BusinessType.INSERT)]
public IActionResult Create([FromBody] RoleUsersCreateDto roleUsersCreateDto) public IActionResult Create([FromBody] RoleUsersCreateDto roleUsersCreateDto)
{ {
var response = SysUserRoleService.InsertRoleUser(roleUsersCreateDto); var response = SysUserRoleService.InsertRoleUser(roleUsersCreateDto);
@@ -58,7 +59,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// <returns></returns> /// <returns></returns>
[HttpPost("delete")] [HttpPost("delete")]
[ActionPermissionFilter(Permission = "system:roleusers:remove")] [ActionPermissionFilter(Permission = "system:roleusers:remove")]
[Log(Title = "删除角色用户", BusinessType = Infrastructure.Enums.BusinessType.DELETE)] [Log(Title = "删除角色用户", BusinessType = BusinessType.DELETE)]
public IActionResult Delete([FromBody] RoleUsersCreateDto roleUsersCreateDto) public IActionResult Delete([FromBody] RoleUsersCreateDto roleUsersCreateDto)
{ {
return SUCCESS(SysUserRoleService.DeleteRoleUserByUserIds(roleUsersCreateDto.RoleId, roleUsersCreateDto.UserIds)); return SUCCESS(SysUserRoleService.DeleteRoleUserByUserIds(roleUsersCreateDto.RoleId, roleUsersCreateDto.UserIds));
@@ -1,8 +1,6 @@
using Infrastructure.Extensions; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Quartz; using Quartz;
using SqlSugar; using SqlSugar;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Model.System; using ZR.Model.System;
using ZR.Model.System.Dto; using ZR.Model.System.Dto;
@@ -16,6 +14,7 @@ namespace ZR.Admin.WebApi.Controllers
/// </summary> /// </summary>
[Verify] [Verify]
[Route("system/Tasks")] [Route("system/Tasks")]
[ApiExplorerSettings(GroupName = "sys")]
public class TasksController : BaseController public class TasksController : BaseController
{ {
private ISysTasksQzService _tasksQzService; private ISysTasksQzService _tasksQzService;
@@ -87,7 +86,7 @@ namespace ZR.Admin.WebApi.Controllers
throw new CustomException($"程序集或者类名不能为空"); throw new CustomException($"程序集或者类名不能为空");
} }
//从 Dto 映射到 实体 //从 Dto 映射到 实体
var tasksQz = parm.Adapt<SysTasks>().ToCreate(); var tasksQz = parm.Adapt<SysTasks>().ToCreate(HttpContext);
tasksQz.Create_by = HttpContext.GetName(); tasksQz.Create_by = HttpContext.GetName();
tasksQz.ID = SnowFlakeSingle.Instance.NextId().ToString(); tasksQz.ID = SnowFlakeSingle.Instance.NextId().ToString();
@@ -159,7 +158,7 @@ namespace ZR.Admin.WebApi.Controllers
var tasksQz = _tasksQzService.GetFirst(m => m.ID == id); var tasksQz = _tasksQzService.GetFirst(m => m.ID == id);
var taskResult = await _schedulerServer.DeleteTaskScheduleAsync(tasksQz); var taskResult = await _schedulerServer.DeleteTaskScheduleAsync(tasksQz);
if (taskResult.Code == 200) if (taskResult.IsSuccess())
{ {
_tasksQzService.Delete(id); _tasksQzService.Delete(id);
} }
@@ -188,7 +187,7 @@ namespace ZR.Admin.WebApi.Controllers
var tasksQz = _tasksQzService.GetFirst(m => m.ID == id); var tasksQz = _tasksQzService.GetFirst(m => m.ID == id);
var taskResult = await _schedulerServer.AddTaskScheduleAsync(tasksQz); var taskResult = await _schedulerServer.AddTaskScheduleAsync(tasksQz);
if (taskResult.Code == 200) if (taskResult.IsSuccess())
{ {
tasksQz.IsStart = 1; tasksQz.IsStart = 1;
_tasksQzService.Update(tasksQz); _tasksQzService.Update(tasksQz);
@@ -219,7 +218,7 @@ namespace ZR.Admin.WebApi.Controllers
var tasksQz = _tasksQzService.GetFirst(m => m.ID == id); var tasksQz = _tasksQzService.GetFirst(m => m.ID == id);
var taskResult = await _schedulerServer.DeleteTaskScheduleAsync(tasksQz);//await _schedulerServer.PauseTaskScheduleAsync(tasksQz); var taskResult = await _schedulerServer.DeleteTaskScheduleAsync(tasksQz);//await _schedulerServer.PauseTaskScheduleAsync(tasksQz);
if (taskResult.Code == 200) if (taskResult.IsSuccess())
{ {
tasksQz.IsStart = 0; tasksQz.IsStart = 0;
_tasksQzService.Update(tasksQz); _tasksQzService.Update(tasksQz);
@@ -1,11 +1,6 @@
using Infrastructure; using Microsoft.AspNetCore.Mvc;
using Infrastructure.Attribute;
using Infrastructure.Enums;
using Infrastructure.Extensions;
using Microsoft.AspNetCore.Mvc;
using SqlSugar; using SqlSugar;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Common;
using ZR.Model; using ZR.Model;
using ZR.Model.System; using ZR.Model.System;
using ZR.Model.System.Dto; using ZR.Model.System.Dto;
@@ -18,6 +13,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary> /// </summary>
[Verify] [Verify]
[Route("/monitor/jobLog")] [Route("/monitor/jobLog")]
[ApiExplorerSettings(GroupName = "sys")]
public class TasksLogController : BaseController public class TasksLogController : BaseController
{ {
private readonly ISysTasksLogService tasksLogService; private readonly ISysTasksLogService tasksLogService;
@@ -1,11 +1,6 @@
using Infrastructure;
using Infrastructure.Extensions; using Infrastructure.Extensions;
using Infrastructure.Model;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -14,6 +9,7 @@ namespace ZR.Admin.WebApi.Controllers.monitor
/// <summary> /// <summary>
/// 系统监控 /// 系统监控
/// </summary> /// </summary>
[ApiExplorerSettings(GroupName = "sys")]
public class MonitorController : BaseController public class MonitorController : BaseController
{ {
private OptionsSetting Options; private OptionsSetting Options;
@@ -0,0 +1,77 @@
using Microsoft.AspNetCore.Mvc;
using ZR.Admin.WebApi.Filters;
using ZR.Model.System.Dto;
using ZR.Service.System.IService;
//创建时间:2023-08-17
namespace ZR.Admin.WebApi.Controllers
{
/// <summary>
/// 数据差异日志
/// </summary>
[Verify]
[Route("monitor/SqlDiffLog")]
[ApiExplorerSettings(GroupName = "sys")]
public class SqlDiffLogController : BaseController
{
/// <summary>
/// 数据差异日志接口
/// </summary>
private readonly ISqlDiffLogService _SqlDiffLogService;
public SqlDiffLogController(ISqlDiffLogService SqlDiffLogService)
{
_SqlDiffLogService = SqlDiffLogService;
}
/// <summary>
/// 查询数据差异日志列表
/// </summary>
/// <param name="parm"></param>
/// <returns></returns>
[HttpGet("list")]
[ActionPermissionFilter(Permission = "sqldifflog:list")]
public IActionResult QuerySqlDiffLog([FromQuery] SqlDiffLogQueryDto parm)
{
var response = _SqlDiffLogService.GetList(parm);
return SUCCESS(response);
}
/// <summary>
/// 删除数据差异日志
/// </summary>
/// <returns></returns>
[HttpDelete("{ids}")]
[ActionPermissionFilter(Permission = "sqldifflog:delete")]
[Log(Title = "数据差异日志", BusinessType = BusinessType.DELETE)]
public IActionResult DeleteSqlDiffLog(string ids)
{
long[] idsArr = Tools.SpitLongArrary(ids);
if (idsArr.Length <= 0) { return ToResponse(ApiResult.Error($"删除失败Id 不能为空")); }
var response = _SqlDiffLogService.Delete(idsArr);
return ToResponse(response);
}
/// <summary>
/// 导出数据差异日志
/// </summary>
/// <returns></returns>
[Log(Title = "数据差异日志", BusinessType = BusinessType.EXPORT, IsSaveResponseData = false)]
[HttpGet("export")]
[ActionPermissionFilter(Permission = "sqldifflog:export")]
public IActionResult Export([FromQuery] SqlDiffLogQueryDto parm)
{
parm.PageNum = 1;
parm.PageSize = 100000;
var list = _SqlDiffLogService.GetList(parm).Result;
if (list == null || list.Count <= 0)
{
return ToResponse(ResultCode.FAIL, "没有要导出的数据");
}
var result = ExportExcelMini(list, "数据差异日志", "数据差异日志");
return ExportExcel(result.Item2, result.Item1);
}
}
}
@@ -13,6 +13,7 @@ namespace ZR.Admin.WebApi.Controllers.monitor
/// </summary> /// </summary>
[Verify] [Verify]
[Route("/monitor/logininfor")] [Route("/monitor/logininfor")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysLogininforController : BaseController public class SysLogininforController : BaseController
{ {
private ISysLoginService sysLoginService; private ISysLoginService sysLoginService;
@@ -11,6 +11,7 @@ namespace ZR.Admin.WebApi.Controllers.monitor
/// </summary> /// </summary>
[Verify] [Verify]
[Route("/monitor/operlog")] [Route("/monitor/operlog")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysOperlogController : BaseController public class SysOperlogController : BaseController
{ {
private ISysOperLogService sysOperLogService; private ISysOperLogService sysOperLogService;
@@ -65,7 +66,7 @@ namespace ZR.Admin.WebApi.Controllers.monitor
{ {
if (!HttpContextExtension.IsAdmin(HttpContext)) if (!HttpContextExtension.IsAdmin(HttpContext))
{ {
return ToResponse(Infrastructure.ResultCode.CUSTOM_ERROR,"操作失败"); return ToResponse(ResultCode.CUSTOM_ERROR,"操作失败");
} }
sysOperLogService.CleanOperLog(); sysOperLogService.CleanOperLog();
@@ -1,18 +1,23 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
using ZR.Admin.WebApi.Filters; using ZR.Admin.WebApi.Filters;
using ZR.Admin.WebApi.Hubs;
using ZR.Model; using ZR.Model;
using ZR.Model.System.Dto;
using ZR.ServiceCore.Signalr;
namespace ZR.Admin.WebApi.Controllers.monitor namespace ZR.Admin.WebApi.Controllers.monitor
{ {
/// <summary>
/// 在线用户
/// </summary>
[Verify] [Verify]
[Route("monitor/online")] [Route("monitor/online")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysUserOnlineController : BaseController public class SysUserOnlineController : BaseController
{ {
private IHubContext<Hub> HubContext; private readonly IHubContext<MessageHub> HubContext;
public SysUserOnlineController(IHubContext<Hub> hubContext) public SysUserOnlineController(IHubContext<MessageHub> hubContext)
{ {
HubContext = hubContext; HubContext = hubContext;
} }
@@ -25,11 +30,47 @@ namespace ZR.Admin.WebApi.Controllers.monitor
[HttpGet("list")] [HttpGet("list")]
public IActionResult Index([FromQuery] PagerInfo parm) public IActionResult Index([FromQuery] PagerInfo parm)
{ {
var result = MessageHub.clientUsers var result = MessageHub.onlineClients
.OrderByDescending(f => f.LoginTime) .OrderByDescending(f => f.LoginTime)
.Skip(parm.PageNum - 1).Take(parm.PageSize); .Skip(parm.PageNum - 1).Take(parm.PageSize);
return SUCCESS(new { result, totalNum = MessageHub.clientUsers.Count }); return SUCCESS(new { result, totalNum = MessageHub.onlineClients.Count });
}
/// <summary>
/// 单个强退
/// </summary>
/// <returns></returns>
[HttpDelete("force")]
[Log(Title = "强退", BusinessType = BusinessType.FORCE)]
[ActionPermissionFilter(Permission = "monitor:online:forceLogout")]
public async Task<IActionResult> Force([FromBody] LockUserDto dto)
{
if (dto == null) { return ToResponse(ResultCode.PARAM_ERROR); }
await HubContext.Clients.Client(dto.ConnnectionId)
.SendAsync(HubsConstant.ForceUser, new { dto.Reason, dto.Time });
//var expirTime = DateTimeHelper.GetUnixTimeSeconds(DateTime.Now.AddMinutes(dto.Time));
////PC 端采用设备 + 用户名的方式进行封锁
//CacheService.SetLockUser(dto.ClientId + dto.Name, expirTime, dto.Time);
return SUCCESS(1);
}
/// <summary>
/// 批量强退
/// </summary>
/// <returns></returns>
[HttpDelete("batchForce")]
[Log(Title = "强退", BusinessType = BusinessType.FORCE)]
[ActionPermissionFilter(Permission = "monitor:online:batchLogout")]
public async Task<IActionResult> BatchforceLogout([FromBody] LockUserDto dto)
{
if (dto == null) { return ToResponse(ResultCode.PARAM_ERROR); }
await HubContext.Clients.All.SendAsync(HubsConstant.ForceUser, new { dto.Reason });
return SUCCESS(1);
} }
} }
} }
@@ -1,5 +1,4 @@
using Infrastructure.Extensions; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using System.Web; using System.Web;
namespace ZR.Admin.WebApi.Controllers namespace ZR.Admin.WebApi.Controllers
@@ -11,9 +10,6 @@ namespace ZR.Admin.WebApi.Controllers
[AllowAnonymous] [AllowAnonymous]
public class WxOpenController : BaseController public class WxOpenController : BaseController
{ {
private NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
public WxOpenController() { }
/// <summary> /// <summary>
/// 获取签名 /// 获取签名
/// </summary> /// </summary>
+40 -3
View File
@@ -1,7 +1,7 @@
using Infrastructure; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Filters; using Swashbuckle.AspNetCore.Filters;
using Swashbuckle.AspNetCore.SwaggerUI;
using System.Reflection; using System.Reflection;
namespace ZR.Admin.WebApi.Extensions namespace ZR.Admin.WebApi.Extensions
@@ -33,7 +33,13 @@ namespace ZR.Admin.WebApi.Extensions
}; };
}); });
}); });
app.UseSwaggerUI(c => c.SwaggerEndpoint("v1/swagger.json", "ZrAdmin v1")); app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("sys/swagger.json", "系统管理");
c.SwaggerEndpoint("article/swagger.json", "文章管理");
c.SwaggerEndpoint("v1/swagger.json", "business");
c.DocExpansion(DocExpansion.None); //->修改界面打开时自动折叠
});
} }
public static void AddSwaggerConfig(this IServiceCollection services) public static void AddSwaggerConfig(this IServiceCollection services)
@@ -43,6 +49,20 @@ namespace ZR.Admin.WebApi.Extensions
services.AddSwaggerGen(c => services.AddSwaggerGen(c =>
{ {
c.SwaggerDoc("sys", new OpenApiInfo
{
Title = "ZrAdmin.NET Api",
Version = "v1",
Description = "系统管理",
Contact = new OpenApiContact { Name = "ZRAdmin doc", Url = new Uri("https://www.izhaorui.cn/doc") }
});
c.SwaggerDoc("article", new OpenApiInfo
{
Title = "ZrAdmin.NET Api",
Version = "v1",
Description = "文章管理",
Contact = new OpenApiContact { Name = "ZRAdmin doc", Url = new Uri("https://www.izhaorui.cn/doc") }
});
c.SwaggerDoc("v1", new OpenApiInfo c.SwaggerDoc("v1", new OpenApiInfo
{ {
Title = "ZrAdmin.NET Api", Title = "ZrAdmin.NET Api",
@@ -55,6 +75,9 @@ namespace ZR.Admin.WebApi.Extensions
//添加文档注释 //添加文档注释
var baseDir = AppContext.BaseDirectory; var baseDir = AppContext.BaseDirectory;
c.IncludeXmlComments(Path.Combine(baseDir, "ZR.Model.xml"), true); c.IncludeXmlComments(Path.Combine(baseDir, "ZR.Model.xml"), true);
c.IncludeXmlComments(Path.Combine(baseDir, "ZR.ServiceCore.xml"), true);
c.IncludeXmlComments(Path.Combine(baseDir, "ZR.Service.xml"), true);
c.IncludeXmlComments(Path.Combine(baseDir, "ZR.Admin.WebApi.xml"), true);
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(baseDir, xmlFile); var xmlPath = Path.Combine(baseDir, xmlFile);
@@ -94,6 +117,20 @@ namespace ZR.Admin.WebApi.Extensions
new List<string>() new List<string>()
} }
}); });
//判断接口归于哪个分组
c.DocInclusionPredicate((docName, apiDescription) =>
{
if (docName == "v1")
{
//当分组为NoGroup时,只要没加特性的都属于这个组
return string.IsNullOrEmpty(apiDescription.GroupName);
}
else
{
return apiDescription.GroupName == docName;
}
});
}); });
} }
} }
+1 -1
View File
@@ -40,7 +40,7 @@ namespace ZR.Admin.WebApi.Extensions
foreach (var task in tasks.Result) foreach (var task in tasks.Result)
{ {
var result = _schedulerServer.AddTaskScheduleAsync(task); var result = _schedulerServer.AddTaskScheduleAsync(task);
if (result.Result.Code == 200) if (result.Result.IsSuccess())
{ {
Console.WriteLine($"注册任务[{task.Name}]ID:{task.ID}成功"); Console.WriteLine($"注册任务[{task.Name}]ID:{task.ID}成功");
} }
+4 -1
View File
@@ -4,4 +4,7 @@ global using Infrastructure;
global using Infrastructure.Attribute; global using Infrastructure.Attribute;
global using Infrastructure.Enums; global using Infrastructure.Enums;
global using Infrastructure.Model; global using Infrastructure.Model;
global using Mapster; global using Mapster;
global using Infrastructure.Extensions;
global using Infrastructure.Controllers;
global using ZR.ServiceCore.Middleware;
-107
View File
@@ -1,107 +0,0 @@
using Infrastructure;
using Infrastructure.Constant;
using Infrastructure.Model;
using IPTools.Core;
using Microsoft.AspNetCore.SignalR;
using UAParser;
using ZR.Admin.WebApi.Extensions;
using ZR.Service.System.IService;
namespace ZR.Admin.WebApi.Hubs
{
/// <summary>
/// msghub
/// </summary>
public class MessageHub : Hub
{
//创建用户集合,用于存储所有链接的用户数据
public static readonly List<OnlineUsers> clientUsers = new();
private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private readonly ISysNoticeService SysNoticeService;
public MessageHub(ISysNoticeService noticeService)
{
SysNoticeService = noticeService;
}
private ApiResult SendNotice()
{
var result = SysNoticeService.GetSysNotices();
return new ApiResult(200, "success", result);
}
#region 客户端连接
/// <summary>
/// 客户端连接的时候调用
/// </summary>
/// <returns></returns>
public override Task OnConnectedAsync()
{
var name = HttpContextExtension.GetName(App.HttpContext);
var ip = HttpContextExtension.GetClientUserIp(App.HttpContext);
var ip_info = IpTool.Search(ip);
ClientInfo clientInfo = HttpContextExtension.GetClientInfo(App.HttpContext);
string device = clientInfo.ToString();
var userid = HttpContextExtension.GetUId(App.HttpContext);
string uuid = device + userid + ip;
var user = clientUsers.Any(u => u.ConnnectionId == Context.ConnectionId);
var user2 = clientUsers.Any(u => u.Uuid == uuid);
//判断用户是否存在,否则添加集合!user2 && !user &&
if (!user2 && !user && Context.User.Identity.IsAuthenticated)
{
OnlineUsers users = new(Context.ConnectionId, name, userid, ip, device)
{
Location = ip_info.City,
Uuid = uuid
};
clientUsers.Add(users);
Console.WriteLine($"{DateTime.Now}:{name},{Context.ConnectionId}连接服务端success,当前已连接{clientUsers.Count}个");
//Clients.All.SendAsync("welcome", $"欢迎您:{name},当前时间:{DateTime.Now}");
Clients.All.SendAsync(HubsConstant.MoreNotice, SendNotice());
}
Clients.All.SendAsync(HubsConstant.OnlineNum, clientUsers.Count);
return base.OnConnectedAsync();
}
/// <summary>
/// 连接终止时调用。
/// </summary>
/// <returns></returns>
public override Task OnDisconnectedAsync(Exception? exception)
{
var user = clientUsers.Where(p => p.ConnnectionId == Context.ConnectionId).FirstOrDefault();
//判断用户是否存在,否则添加集合
if (user != null)
{
clientUsers.Remove(user);
Clients.All.SendAsync(HubsConstant.OnlineNum, clientUsers.Count);
Console.WriteLine($"用户{user?.Name}离开了,当前已连接{clientUsers.Count}个");
}
return base.OnDisconnectedAsync(exception);
}
#endregion
/// <summary>
/// 注册信息
/// </summary>
/// <param name="connectId"></param>
/// <param name="userName"></param>
/// <param name="message"></param>
/// <returns></returns>
[HubMethodName("SendMessage")]
public async Task SendMessage(string connectId, string userName, string message)
{
Console.WriteLine($"{connectId},message={message}");
await Clients.Client(connectId).SendAsync("receiveChat", new { userName, message });
}
}
}
-40
View File
@@ -1,40 +0,0 @@
namespace ZR.Admin.WebApi.Hubs
{
public class OnlineUsers
{
/// <summary>
/// 客户端连接Id
/// </summary>
public string ConnnectionId { get; set; }
/// <summary>
/// 用户id
/// </summary>
public long? Userid { get; set; }
public string Name { get; set; }
public DateTime LoginTime { get; set; }
public string UserIP { get; set; }
/// <summary>
/// 登录地点
/// </summary>
public string? Location { get; set; }
/// <summary>
/// 判断用户唯一
/// </summary>
public string? Uuid{ get; set; }
/// <summary>
/// 浏览器
/// </summary>
public string Browser { get; set; }
public OnlineUsers(string clientid, string name, long? userid, string userip, string browser)
{
ConnnectionId = clientid;
Name = name;
LoginTime = DateTime.Now;
Userid = userid;
UserIP = userip;
Browser = browser;
}
}
}
+13 -15
View File
@@ -24,29 +24,29 @@
<!--${basedir}表示当前应用程序域所在的根目录--> <!--${basedir}表示当前应用程序域所在的根目录-->
<target name="allfile" xsi:type="File" <target name="allfile" xsi:type="File"
fileName="${basedir}/adminlogs/all.txt" fileName="${basedir}/adminlogs/all.txt"
archiveFileName="${basedir}/adminlogs/bak/all.{###}.txt" archiveFileName="${basedir}/adminlogs/bak/all/all.{###}.txt"
archiveEvery="Day" archiveEvery="Day"
archiveNumbering="DateAndSequence" archiveNumbering="DateAndSequence"
archiveAboveSize="20000000" archiveAboveSize="20000000"
maxArchiveFiles="30" maxArchiveFiles="30"
keepFileOpen="true" keepFileOpen="true"
layout="${longdate} | ${event-properties:item=EventId_Id} | ${uppercase:${level}} | ${logger} | ${aspnet-request-iP:CheckForwardedForHeader=true} | ${event-properties:item=user} | ${aspnet-request-url} | ${message} | ${event-properties:item=requestParam} | ${event-properties:item=jsonResult} | ${onexception:${exception:format=tostring}"/> layout="${longdate}|${event-properties:item=EventId:whenEmpty=0}|${aspnet-request-connection-id}|${uppercase:${level}}|${logger}|${aspnet-request-iP:CheckForwardedForHeader=true}|${event-properties:item=user:whenEmpty=-}|url: ${aspnet-request-url}|${message:whenEmpty=-}|${event-properties:item=requestParam}|${event-properties:item=jsonResult}"/>
<!--错误日志--> <!--错误日志-->
<target name="errorfile" xsi:type="File" <target name="errorfile" xsi:type="File"
fileName="${basedir}/adminlogs/error.txt" fileName="${basedir}/adminlogs/error.txt"
archiveFileName="${basedir}/adminlogs/bak/error.{###}.txt" archiveFileName="${basedir}/adminlogs/bak/error/error.{###}.txt"
archiveEvery="Day" archiveEvery="Day"
archiveNumbering="DateAndSequence" archiveNumbering="DateAndSequence"
archiveAboveSize="20000000" archiveAboveSize="20000000"
maxArchiveFiles="30" maxArchiveFiles="30"
keepFileOpen="true" keepFileOpen="true"
layout="${longdate} | ${event-properties:item=EventId_Id} | ${uppercase:${level}} | ${logger} | ${aspnet-request-iP:CheckForwardedForHeader=true} | ${event-properties:item=user} | ${aspnet-request-url} | ${message} | ${event-properties:item=requestParam} | ${event-properties:item=jsonResult} | ${onexception:${exception:format=tostring}"/> layout="${longdate}|${event-properties:item=EventId:whenEmpty=0}|${uppercase:${level}}|${logger}${newline}用户IP:${aspnet-request-iP:CheckForwardedForHeader=true}|${event-properties:item=user}${newline}请求地址:${aspnet-request-url}${newline}错误消息:${message}${newline}请求参数:${event-properties:item=requestParam}${newline}请求结果:${event-properties:item=jsonResult}${newline}${onexception:${exception:format=tostring}"/>
<!--SQL文件--> <!--SQL文件-->
<target name="sqlfile" xsi:type="File" <target name="sqlfile" xsi:type="File"
fileName="${basedir}/adminlogs/admin-sql.txt" fileName="${basedir}/adminlogs/sql.txt"
archiveFileName="${basedir}/adminlogs/bak/admin-sql{###}.txt" archiveFileName="${basedir}/adminlogs/bak/sql/sql{###}.txt"
archiveEvery="Day" archiveEvery="Day"
archiveNumbering="DateAndSequence" archiveNumbering="DateAndSequence"
archiveAboveSize="20000000" archiveAboveSize="20000000"
@@ -56,18 +56,17 @@
<!--写入彩色控制台--> <!--写入彩色控制台-->
<target name="consoleSql" xsi:type="ColoredConsole" useDefaultRowHighlightingRules="false" <target name="consoleSql" xsi:type="ColoredConsole" useDefaultRowHighlightingRules="false"
layout="${date:format=MM-dd HH\:mm\:ss} | ${aspnet-request-iP} | ${aspnet-request-url} ${newline}${message}"> layout="${date:format=MM-dd HH\:mm\:ss}|${aspnet-request-iP}|${aspnet-request-url}${newline}${message}">
<highlight-row condition="level == LogLevel.Debug" foregroundColor="DarkGray" /> <highlight-row condition="level == LogLevel.Debug" foregroundColor="DarkGray" />
<highlight-row condition="level == LogLevel.Info" foregroundColor="Gray" /> <highlight-row condition="level == LogLevel.Info" foregroundColor="Gray" />
<highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" /> <highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" />
<highlight-row condition="level == LogLevel.Error" foregroundColor="Red" /> <highlight-row condition="level == LogLevel.Error" foregroundColor="Red" />
<highlight-row condition="level == LogLevel.Fatal" foregroundColor="Red" backgroundColor="White" /> <highlight-row condition="level == LogLevel.Fatal" foregroundColor="Red" backgroundColor="White" />
<highlight-word regex="SQL语句" foregroundColor="Blue" /> <highlight-word regex="SQL" foregroundColor="Blue" />
<highlight-word regex="【" foregroundColor="Blue" />
<highlight-word regex="】" foregroundColor="Blue" />
</target> </target>
<target name="console" xsi:type="ColoredConsole"
layout="${date:format=MM-dd HH\:mm\:ss} | ${aspnet-request-url} ${newline} ${message}"/>
<!--写入黑洞--> <!--写入黑洞-->
<target name="blackhole" xsi:type="Null" /> <target name="blackhole" xsi:type="Null" />
</targets> </targets>
@@ -80,11 +79,10 @@
<!--<logger name="System.*" writeTo="blackhole" final="true" />--> <!--<logger name="System.*" writeTo="blackhole" final="true" />-->
<!-- Quartz --> <!-- Quartz -->
<logger name="Quartz*" minlevel="Trace" maxlevel="Info" final="true" /> <logger name="Quartz*" minlevel="Trace" maxlevel="Info" final="true" />
<logger name="ZR.Admin.WebApi.Middleware.GlobalExceptionMiddleware" final="true" writeTo="console,errorfile"/> <logger name="*.SqlSugar.SqlsugarSetup" final="true" writeTo="consoleSql,sqlfile"/>
<logger name="ZR.Admin.WebApi.Extensions.DbExtension" final="true" writeTo="consoleSql,sqlfile"/>
<logger name="*" writeTo="console"/>
<logger name="*" minLevel="Trace" writeTo="allfile" /> <logger name="*" minLevel="Trace" writeTo="allfile" />
<logger name="*.GlobalExceptionMiddleware" final="true" writeTo="consoleSql,errorfile"/>
<!--Skip non-critical Microsoft logs and so log only own logs--> <!--Skip non-critical Microsoft logs and so log only own logs-->
<logger name="Microsoft.*,Quartz.Core.QuartzSchedulerThread" maxlevel="Info" final="true" /> <logger name="Microsoft.*,Quartz.Core.QuartzSchedulerThread" maxlevel="Info" final="true" />
</rules> </rules>
+31 -36
View File
@@ -1,17 +1,19 @@
using AspNetCoreRateLimit; using AspNetCoreRateLimit;
using Infrastructure; using Infrastructure.Converter;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.IdentityModel.Tokens; using NLog.Web;
using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using ZR.Admin.WebApi.Extensions; using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Admin.WebApi.Framework;
using ZR.Admin.WebApi.Hubs;
using ZR.Admin.WebApi.Middleware;
using ZR.Common.Cache; using ZR.Common.Cache;
using ZR.Infrastructure.WebExtensions;
using ZR.ServiceCore.Signalr;
using ZR.ServiceCore.SqlSugar;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// NLog: Setup NLog for Dependency injection
//builder.Logging.ClearProviders();
builder.Host.UseNLog();
// Add services to the container. // Add services to the container.
builder.Services.AddControllers(); builder.Services.AddControllers();
@@ -24,8 +26,6 @@ builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddCors(builder.Configuration); builder.Services.AddCors(builder.Configuration);
// 显示logo // 显示logo
builder.Services.AddLogo(); builder.Services.AddLogo();
//注入SignalR实时通讯,默认用json传输
builder.Services.AddSignalR();
//消除Error unprotecting the session cookie警告 //消除Error unprotecting the session cookie警告
builder.Services.AddDataProtection() builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "DataProtection")); .PersistKeysToFileSystem(new DirectoryInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "DataProtection"));
@@ -39,30 +39,10 @@ builder.Services.AddHttpContextAccessor();
builder.Services.Configure<OptionsSetting>(builder.Configuration); builder.Services.Configure<OptionsSetting>(builder.Configuration);
//jwt 认证 //jwt 认证
builder.Services.AddAuthentication(options => builder.Services.AddJwt();
{ //配置文件
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddCookie()
.AddJwtBearer(o =>
{
o.TokenValidationParameters = JwtUtil.ValidParameters();
o.Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
// 如果过期,把过期信息添加到头部
if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});
builder.Services.AddSingleton(new AppSettings(builder.Configuration)); builder.Services.AddSingleton(new AppSettings(builder.Configuration));
//app服务注册
builder.Services.AddAppService(); builder.Services.AddAppService();
//开启计划任务 //开启计划任务
builder.Services.AddTaskSchedulers(); builder.Services.AddTaskSchedulers();
@@ -80,12 +60,21 @@ builder.Services.AddMvc(options =>
}) })
.AddJsonOptions(options => .AddJsonOptions(options =>
{ {
options.JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString; //options.JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString;
options.JsonSerializerOptions.WriteIndented = true; options.JsonSerializerOptions.WriteIndented = true;
options.JsonSerializerOptions.Converters.Add(new JsonConverterUtil.DateTimeConverter()); options.JsonSerializerOptions.Converters.Add(new JsonConverterUtil.DateTimeConverter());
options.JsonSerializerOptions.Converters.Add(new JsonConverterUtil.DateTimeNullConverter()); options.JsonSerializerOptions.Converters.Add(new JsonConverterUtil.DateTimeNullConverter());
options.JsonSerializerOptions.Converters.Add(new StringConverter());
//PropertyNamingPolicy属性用于前端传过来的属性的格式策略,目前内置的仅有一种策略CamelCase
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
//options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;//属性可以忽略大小写格式,开启后性能会降低
});
//注入SignalR实时通讯,默认用json传输
builder.Services.AddSignalR()
.AddJsonProtocol(options =>
{
options.PayloadSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
}); });
builder.Services.AddSwaggerConfig(); builder.Services.AddSwaggerConfig();
var app = builder.Build(); var app = builder.Build();
@@ -93,11 +82,18 @@ InternalApp.ServiceProvider = app.Services;
InternalApp.Configuration = builder.Configuration; InternalApp.Configuration = builder.Configuration;
InternalApp.WebHostEnvironment = app.Environment; InternalApp.WebHostEnvironment = app.Environment;
//初始化db //初始化db
builder.Services.AddDb(builder.Configuration, app.Environment); builder.Services.AddDb(app.Environment);
//使用全局异常中间件 //使用全局异常中间件
app.UseMiddleware<GlobalExceptionMiddleware>(); app.UseMiddleware<GlobalExceptionMiddleware>();
//请求头转发
//ForwardedHeaders中间件会自动把反向代理服务器转发过来的X-Forwarded-For(客户端真实IP)以及X-Forwarded-Proto(客户端请求的协议)自动填充到HttpContext.Connection.RemoteIPAddress和HttpContext.Request.Scheme中,这样应用代码中读取到的就是真实的IP和真实的协议了,不需要应用做特殊处理。
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = Microsoft.AspNetCore.HttpOverrides.ForwardedHeaders.XForwardedFor | Microsoft.AspNetCore.HttpOverrides.ForwardedHeaders.XForwardedProto
});
app.Use((context, next) => app.Use((context, next) =>
{ {
//设置可以多次获取body内容 //设置可以多次获取body内容
@@ -138,5 +134,4 @@ app.MapControllerRoute(
pattern: "{controller=Home}/{action=Index}/{id?}"); pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapControllers(); app.MapControllers();
app.Run(); app.Run();
+9 -8
View File
@@ -6,7 +6,7 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1591,8603,8602,8604,8600</NoWarn> <NoWarn>1701;1702;1591,8603,8602,8604,8600,8618</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -15,17 +15,12 @@
<ProjectReference Include="..\ZR.Tasks\ZR.Tasks.csproj" /> <ProjectReference Include="..\ZR.Tasks\ZR.Tasks.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
<PackageReference Include="Lazy.Captcha.Core" Version="2.0.3" /> <PackageReference Include="Lazy.Captcha.Core" Version="2.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.7" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.6" /> <PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="7.0.6" />
<PackageReference Include="UAParser" Version="3.1.47" /> <PackageReference Include="NLog.Web.AspNetCore" Version="5.3.3" />
<PackageReference Include="IPTools.China" Version="1.6.0" />
<PackageReference Include="NLog" Version="5.2.0" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.0" />
<PackageReference Include="Mapster" Version="7.3.0" /> <PackageReference Include="Mapster" Version="7.3.0" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.88.3" /> <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.88.5" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -36,5 +31,11 @@
<None Update="ip2region.db"> <None Update="ip2region.db">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<Content Update="wwwroot\Generatecode\**\*">
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</Content>
<Content Update="wwwroot\export\**\*">
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</Content>
</ItemGroup> </ItemGroup>
</Project> </Project>
+9 -10
View File
@@ -21,7 +21,7 @@
"Conn": "Data Source=LAPTOP-STKF2M8H\\SQLEXPRESS;User ID=admin;Password=admin123;Initial Catalog={dbName};", "Conn": "Data Source=LAPTOP-STKF2M8H\\SQLEXPRESS;User ID=admin;Password=admin123;Initial Catalog={dbName};",
"DbType": 1, "DbType": 1,
"IsAutoCloseConnection": true, "IsAutoCloseConnection": true,
"DbName": "ZrAdmin" //代码生成默认连接数据库 "DbName": "ZrAdmin" //代码生成默认连接数据库,Oracle库是实例的名称
}, },
"urls": "http://localhost:8888", //项目启动url,如果改动端口前端对应devServer也需要进行修改 "urls": "http://localhost:8888", //项目启动url,如果改动端口前端对应devServer也需要进行修改
"corsUrls": [ "http://localhost:8887", "http://localhost:8886" ], //跨域地址(前端启动项目,前后端分离单独部署需要设置),多个用","隔开 "corsUrls": [ "http://localhost:8887", "http://localhost:8886" ], //跨域地址(前端启动项目,前后端分离单独部署需要设置),多个用","隔开
@@ -29,11 +29,15 @@
"Issuer": "ZRAdmin.NET", //即token的签发者。 "Issuer": "ZRAdmin.NET", //即token的签发者。
"Audience": "ZRAdmin.NET", //指该token是服务于哪个群体的(群体范围) "Audience": "ZRAdmin.NET", //指该token是服务于哪个群体的(群体范围)
"SecretKey": "SecretKey-ZRADMIN.NET-20210101", "SecretKey": "SecretKey-ZRADMIN.NET-20210101",
"Expire": 1440 //jwt登录过期时间(分) "Expire": 1440, //jwt登录过期时间(分)
"RefreshTokenTime": 5,//分钟
"TokenType": "Bearer"
}, },
"InjectClass": [ "ZR.Repository", "ZR.Service", "ZR.Tasks" ], //自动注入类 "InjectClass": [ "ZR.Repository", "ZR.Service", "ZR.Tasks", "ZR.ServiceCore" ], //自动注入类
"ShowDbLog": true, //是否打印db日志
"InitDb": false, //是否初始化db "InitDb": false, //是否初始化db
"DemoMode": false, //是否演示模式 "DemoMode": false, //是否演示模式
"SingleLogin": false,//是否单点登录
"Upload": { "Upload": {
"uploadUrl": "http://localhost:8888", //本地存储资源访问路径 "uploadUrl": "http://localhost:8888", //本地存储资源访问路径
"localSavePath": "", //本地上传默认文件存储目录 wwwroot "localSavePath": "", //本地上传默认文件存储目录 wwwroot
@@ -108,7 +112,7 @@
"RealIpHeader": "X-Real-IP", "RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId", "ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429, "HttpStatusCode": 429,
"EndpointWhitelist": [ "post:/system/dict/data/types", "*:/msghub/negotiate", "*:/LogOut", "*:/common/uploadfile" ], "EndpointWhitelist": [ "post:/system/dict/data/types", "*:/msghub/negotiate", "*:/LogOut", "*:/common/uploadfile", "*:/VerifyScan" ],
"QuotaExceededResponse": { "QuotaExceededResponse": {
"Content": "{{\"code\":429,\"msg\":\"访问过于频繁,请稍后重试\"}}", "Content": "{{\"code\":429,\"msg\":\"访问过于频繁,请稍后重试\"}}",
"ContentType": "application/json", "ContentType": "application/json",
@@ -127,12 +131,7 @@
"Period": "3s", "Period": "3s",
"Limit": 1 "Limit": 1
} }
], ]
"IpRateLimitPolicies": {
//ip规则
"IpRules": [
]
}
}, },
//验证码配置 //验证码配置
"CaptchaOptions": { "CaptchaOptions": {
@@ -2,7 +2,6 @@ using Microsoft.AspNetCore.Mvc;
using ${options.DtosNamespace}; using ${options.DtosNamespace};
using ${options.ModelsNamespace}.${options.SubNamespace}; using ${options.ModelsNamespace}.${options.SubNamespace};
using ${options.IServicsNamespace}.${options.SubNamespace}.I${options.SubNamespace}Service; using ${options.IServicsNamespace}.${options.SubNamespace}.I${options.SubNamespace}Service;
using ${options.ApiControllerNamespace}.Extensions;
using ${options.ApiControllerNamespace}.Filters; using ${options.ApiControllerNamespace}.Filters;
$if(replaceDto.ShowBtnImport) $if(replaceDto.ShowBtnImport)
using MiniExcelLibs; using MiniExcelLibs;
@@ -117,7 +117,7 @@ $end
$if(null != genTable.SubTableName && "" != genTable.SubTableName) $if(null != genTable.SubTableName && "" != genTable.SubTableName)
return Context.InsertNav(model).Include(s1 => s1.${genTable.SubTable.ClassName}Nav).ExecuteReturnEntity(); return Context.InsertNav(model).Include(s1 => s1.${genTable.SubTable.ClassName}Nav).ExecuteReturnEntity();
$else $else
return Insertable(model).IgnoreColumns(true).ExecuteReturnEntity(); return Context.Insertable(model).ExecuteReturnEntity();
$end $end
} }
@@ -69,12 +69,14 @@ $end
</u--form> </u--form>
<view class="btn-group"> <view class="form-footer">
<view class="btn-item"> <view class="btn_wrap">
<u-button text="取消" shape="circle" icon="close" type="info" @click="handleCancel"></u-button> <view class="btn-item">
</view> <u-button text="取消" shape="circle" type="info" @click="handleCancel"></u-button>
<view class="btn-item" v-if="opertype != 3"> </view>
<u-button text="确定" shape="circle" icon="checkmark" type="primary" @click="submit"></u-button> <view class="btn-item" v-if="opertype != 3">
<u-button text="确定" shape="circle" type="primary" @click="submit"></u-button>
</view>
</view> </view>
</view> </view>
</view> </view>
@@ -110,7 +112,6 @@ $end
return { return {
form: {}, form: {},
rules: { rules: {
xxx: {},
$foreach(column in genTable.Columns) $foreach(column in genTable.Columns)
$if(column.IsRequired && column.IsIncrement == false) $if(column.IsRequired && column.IsIncrement == false)
${column.CsharpFieldFl}: [{ ${column.CsharpFieldFl}: [{
@@ -185,12 +186,10 @@ $end
update${genTable.BusinessName}(this.form).then((res) => { update${genTable.BusinessName}(this.form).then((res) => {
this.${modal}modal.msgSuccess("修改成功") this.${modal}modal.msgSuccess("修改成功")
}) })
.catch(() => {})
} else { } else {
add${genTable.BusinessName}(this.form).then((res) => { add${genTable.BusinessName}(this.form).then((res) => {
this.${modal}modal.msgSuccess("新增成功") this.${modal}modal.msgSuccess("新增成功")
}) })
.catch(() => {})
} }
}).catch(errors => { }).catch(errors => {
this.${modal}modal.msg('表单校验失败') this.${modal}modal.msg('表单校验失败')
@@ -197,7 +197,7 @@ $elseif(tool.CheckTree(genTable ,column.CsharpField))
</el-form-item> </el-form-item>
</el-col> </el-col>
$elseif(column.IsPK || column.IsIncrement) $elseif(column.IsPK || column.IsIncrement)
<el-col :lg="12"> <el-col :lg="${options.ColNum}">
<el-form-item label="${labelName}" prop="${columnName}"> <el-form-item label="${labelName}" prop="${columnName}">
$if(column.IsIncrement == false) $if(column.IsIncrement == false)
<el-input-number v-model.number="form.${columnName}" controls-position="right" placeholder="请输入${labelName}" :disabled="title=='修改数据'"/> <el-input-number v-model.number="form.${columnName}" controls-position="right" placeholder="请输入${labelName}" :disabled="title=='修改数据'"/>
@@ -208,13 +208,13 @@ $end
</el-col> </el-col>
$else $else
$if(column.HtmlType == "inputNumber") $if(column.HtmlType == "inputNumber")
<el-col :lg="12"> <el-col :lg="${options.ColNum}">
<el-form-item label="${labelName}" prop="${columnName}"> <el-form-item label="${labelName}" prop="${columnName}">
<el-input-number v-model.number="form.${columnName}" controls-position="right" placeholder="请输入${labelName}" ${labelDisabled}/> <el-input-number v-model.number="form.${columnName}" controls-position="right" placeholder="请输入${labelName}" ${labelDisabled}/>
</el-form-item> </el-form-item>
</el-col> </el-col>
$elseif(column.HtmlType == "datetime") $elseif(column.HtmlType == "datetime")
<el-col :lg="12"> <el-col :lg="${options.ColNum}">
<el-form-item label="${labelName}" prop="${columnName}"> <el-form-item label="${labelName}" prop="${columnName}">
<el-date-picker v-model="form.${columnName}" type="datetime" placeholder="选择日期时间"></el-date-picker> <el-date-picker v-model="form.${columnName}" type="datetime" placeholder="选择日期时间"></el-date-picker>
</el-form-item> </el-form-item>
@@ -232,7 +232,7 @@ $elseif(column.HtmlType == "fileUpload")
</el-form-item> </el-form-item>
</el-col> </el-col>
$elseif(column.HtmlType == "radio") $elseif(column.HtmlType == "radio")
<el-col :lg="12"> <el-col :lg="${options.ColNum}">
<el-form-item label="${labelName}" prop="${columnName}"> <el-form-item label="${labelName}" prop="${columnName}">
<el-radio-group v-model="form.${columnName}"> <el-radio-group v-model="form.${columnName}">
<el-radio v-for="item in $if(column.DictType != "") options.${column.DictType} $else options.${column.CsharpFieldFl}Options$end" :key="item.dictValue" :label="${value}">{{item.dictLabel}}</el-radio> <el-radio v-for="item in $if(column.DictType != "") options.${column.DictType} $else options.${column.CsharpFieldFl}Options$end" :key="item.dictValue" :label="${value}">{{item.dictLabel}}</el-radio>
@@ -252,7 +252,7 @@ $elseif(column.HtmlType == "editor")
</el-form-item> </el-form-item>
</el-col> </el-col>
$elseif(column.HtmlType == "select" || column.HtmlType == "selectMulti") $elseif(column.HtmlType == "select" || column.HtmlType == "selectMulti")
<el-col :lg="12"> <el-col :lg="${options.ColNum}">
<el-form-item label="${labelName}" prop="${columnName}"> <el-form-item label="${labelName}" prop="${columnName}">
<el-select v-model="form.${columnName}" placeholder="请选择${labelName}"${column.DisabledStr}> <el-select v-model="form.${columnName}" placeholder="请选择${labelName}"${column.DisabledStr}>
<el-option v-for="item in $if(column.DictType != "") options.${column.DictType} $else options.${column.CsharpFieldFl}Options$end" :key="item.dictValue" :label="item.dictLabel" :value="${value}"></el-option> <el-option v-for="item in $if(column.DictType != "") options.${column.DictType} $else options.${column.CsharpFieldFl}Options$end" :key="item.dictValue" :label="item.dictLabel" :value="${value}"></el-option>
@@ -268,7 +268,7 @@ $elseif(column.HtmlType == "checkbox")
</el-form-item> </el-form-item>
</el-col> </el-col>
$else $else
<el-col :lg="12"> <el-col :lg="${options.ColNum}">
<el-form-item label="${labelName}" prop="${columnName}"> <el-form-item label="${labelName}" prop="${columnName}">
<el-input v-model="form.${columnName}" placeholder="请输入${labelName}" ${labelDisabled}/> <el-input v-model="form.${columnName}" placeholder="请输入${labelName}" ${labelDisabled}/>
</el-form-item> </el-form-item>
@@ -301,10 +301,8 @@ import Editor from '@/components/Editor'
$end $end
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
// 是否展开,默认全部折叠
const isExpandAll = ref(false) const isExpandAll = ref(false)
const refreshTable = ref(true) const refreshTable = ref(true)
// 展开/折叠操作
function toggleExpandAll() { function toggleExpandAll() {
refreshTable.value = false refreshTable.value = false
isExpandAll.value = !isExpandAll.value isExpandAll.value = !isExpandAll.value
@@ -53,7 +53,7 @@ $elseif(column.HtmlType == "datePicker")
:default-time="defaultTime"> :default-time="defaultTime">
</el-date-picker> </el-date-picker>
</el-form-item> </el-form-item>
$elseif(column.HtmlType.Contains("select") || column.HtmlType == "radio") $elseif(column.HtmlType.Contains("select"))
<el-form-item label="${labelName}" prop="${columnName}"> <el-form-item label="${labelName}" prop="${columnName}">
<el-select clearable $if(column.HtmlType == "selectMulti")multiple$end v-model="queryParams.${columnName}" placeholder="请选择${labelName}"> <el-select clearable $if(column.HtmlType == "selectMulti")multiple$end v-model="queryParams.${columnName}" placeholder="请选择${labelName}">
<el-option v-for="item in $if(column.DictType != "") options.${column.DictType} $else options.${column.CsharpFieldFl}Options$end" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue"> <el-option v-for="item in $if(column.DictType != "") options.${column.DictType} $else options.${column.CsharpFieldFl}Options$end" :key="item.dictValue" :label="item.dictLabel" :value="item.dictValue">
@@ -65,6 +65,7 @@ $elseif(column.HtmlType.Contains("select") || column.HtmlType == "radio")
$elseif(column.HtmlType == "radio") $elseif(column.HtmlType == "radio")
<el-form-item label="${labelName}" prop="${columnName}"> <el-form-item label="${labelName}" prop="${columnName}">
<el-radio-group v-model="queryParams.${columnName}"> <el-radio-group v-model="queryParams.${columnName}">
<el-radio>全部</el-radio>
<el-radio v-for="item in $if(column.DictType != "") options.${column.DictType} $else options.${column.CsharpFieldFl}Options$end" :key="item.dictValue" :label="item.dictValue">{{item.dictLabel}}</el-radio> <el-radio v-for="item in $if(column.DictType != "") options.${column.DictType} $else options.${column.CsharpFieldFl}Options$end" :key="item.dictValue" :label="item.dictValue">{{item.dictLabel}}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
@@ -81,7 +82,7 @@ $end
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 工具区域 --> <!-- 工具区域 -->
<el-row :gutter="10" class="mb8"> <el-row :gutter="15" class="mb10">
$if(replaceDto.ShowBtnAdd) $if(replaceDto.ShowBtnAdd)
<el-col :span="1.5"> <el-col :span="1.5">
<el-button type="primary" v-hasPermi="['${replaceDto.PermissionPrefix}:add']" plain icon="plus" @click="handleAdd"> <el-button type="primary" v-hasPermi="['${replaceDto.PermissionPrefix}:add']" plain icon="plus" @click="handleAdd">
@@ -187,7 +188,7 @@ $elseif(column.HtmlType == "imageUpload")
$elseif(column.HtmlType == "checkbox" || column.HtmlType.Contains("select") || column.HtmlType == "radio") $elseif(column.HtmlType == "checkbox" || column.HtmlType.Contains("select") || column.HtmlType == "radio")
<el-table-column prop="${columnName}" label="${labelName}" align="center"${column.sortStr} v-if="columns.showColumn('${columnName}')"> <el-table-column prop="${columnName}" label="${labelName}" align="center"${column.sortStr} v-if="columns.showColumn('${columnName}')">
<template #default="scope"> <template #default="scope">
<dict-tag :options="$if(column.DictType != "") options.${column.DictType} $else options.${column.CsharpFieldFl}Options$end" :value="scope.row.${columnName}" $if(column.HtmlType == "checkbox")split=","$end /> <dict-tag :options="$if(column.DictType != "") options.${column.DictType} $else options.${column.CsharpFieldFl}Options$end" :value="scope.row.${columnName}" $if(column.HtmlType == "checkbox" || column.HtmlType == "selectMulti")split=","$end />
</template> </template>
</el-table-column> </el-table-column>
$elseif(column.HtmlType == "datetime") $elseif(column.HtmlType == "datetime")
@@ -213,13 +214,13 @@ $end
</el-button-group> </el-button-group>
$else $else
$if(replaceDto.ShowBtnView) $if(replaceDto.ShowBtnView)
<el-button type="primary" icon="view" title="详情" @click="handlePreview(scope.row)"></el-button> <el-button type="primary" size="small" icon="view" title="详情" @click="handlePreview(scope.row)"></el-button>
$end $end
$if(replaceDto.ShowBtnEdit) $if(replaceDto.ShowBtnEdit)
<el-button v-hasPermi="['${replaceDto.PermissionPrefix}:edit']" type="success" icon="edit" title="编辑" @click="handleUpdate(scope.row)"></el-button> <el-button type="success" size="small" icon="edit" title="编辑" v-hasPermi="['${replaceDto.PermissionPrefix}:edit']" @click="handleUpdate(scope.row)"></el-button>
$end $end
$if(replaceDto.ShowBtnDelete) $if(replaceDto.ShowBtnDelete)
<el-button v-hasPermi="['${replaceDto.PermissionPrefix}:delete']" type="danger" icon="delete" title="删除" @click="handleDelete(scope.row)"></el-button> <el-button type="danger" size="small" icon="delete" title="删除" v-hasPermi="['${replaceDto.PermissionPrefix}:delete']" @click="handleDelete(scope.row)"></el-button>
$end $end
$end $end
</template> </template>
@@ -326,10 +327,16 @@ $elseif(column.HtmlType == "editor")
<editor v-model="form.${columnName}" :min-height="200" /> <editor v-model="form.${columnName}" :min-height="200" />
</el-form-item> </el-form-item>
</el-col> </el-col>
$elseif(column.HtmlType == "slider")
<el-col :lg="24">
<el-form-item label="${labelName}" prop="${columnName}">
<el-slider v-model="form.${columnName}" />
</el-form-item>
</el-col>
$elseif(column.HtmlType == "select" || column.HtmlType == "selectMulti") $elseif(column.HtmlType == "select" || column.HtmlType == "selectMulti")
<el-col :lg="${options.ColNum}"> <el-col :lg="${options.ColNum}">
<el-form-item label="${labelName}" prop="${columnName}"> <el-form-item label="${labelName}" prop="${columnName}">
<el-select v-model="form.${columnName}" placeholder="请选择${labelName}"${column.DisabledStr}> <el-select v-model="form.${columnName}$if(column.HtmlType == "selectMulti")Checked$end" $if(column.HtmlType == "selectMulti")multiple$end placeholder="请选择${labelName}"${column.DisabledStr}>
<el-option <el-option
v-for="item in $if(column.DictType != "")options.${column.DictType}${else}options.${column.CsharpFieldFl}Options$end" v-for="item in $if(column.DictType != "")options.${column.DictType}${else}options.${column.CsharpFieldFl}Options$end"
:key="item.dictValue" :key="item.dictValue"
@@ -442,7 +449,9 @@ $end
</el-form> </el-form>
<template #footer v-if="opertype != 3"> <template #footer v-if="opertype != 3">
<el-button text @click="cancel">{{ ${t}t('btn.cancel') }}</el-button> <el-button text @click="cancel">{{ ${t}t('btn.cancel') }}</el-button>
$if(replaceDto.ShowBtnEdit || replaceDto.ShowBtnAdd)
<el-button type="primary" @click="submitForm">{{ ${t}t('btn.submit') }}</el-button> <el-button type="primary" @click="submitForm">{{ ${t}t('btn.submit') }}</el-button>
$end
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
@@ -654,7 +663,7 @@ function cancel(){
function reset() { function reset() {
form.value = { form.value = {
$foreach(item in genTable.Columns) $foreach(item in genTable.Columns)
$if((item.HtmlType == "checkbox")) $if(item.HtmlType == "checkbox" || item.HtmlType == "selectMulti")
${item.CsharpFieldFl}Checked: [], ${item.CsharpFieldFl}Checked: [],
$else $else
$item.CsharpFieldFl: null, $item.CsharpFieldFl: null,
@@ -666,7 +675,7 @@ $if(sub)
$end $end
proxy.resetForm("formRef") proxy.resetForm("formRef")
} }
$if(replaceDto.ShowBtnAdd)
// 添加按钮操作 // 添加按钮操作
function handleAdd() { function handleAdd() {
reset(); reset();
@@ -674,7 +683,8 @@ function handleAdd() {
title.value = '添加${genTable.functionName}' title.value = '添加${genTable.functionName}'
opertype.value = 1 opertype.value = 1
} }
$end
$if(replaceDto.ShowBtnEdit)
// 修改按钮操作 // 修改按钮操作
function handleUpdate(row) { function handleUpdate(row) {
reset() reset()
@@ -689,7 +699,7 @@ function handleUpdate(row) {
form.value = { form.value = {
...data, ...data,
$foreach(item in genTable.Columns) $foreach(item in genTable.Columns)
$if(item.HtmlType == "checkbox") $if(item.HtmlType == "checkbox" || item.HtmlType == "selectMulti")
${item.CsharpFieldFl}Checked: data.${item.CsharpFieldFl} ? data.${item.CsharpFieldFl}.split(',') : [], ${item.CsharpFieldFl}Checked: data.${item.CsharpFieldFl} ? data.${item.CsharpFieldFl}.split(',') : [],
$end $end
$end $end
@@ -700,13 +710,14 @@ $end
} }
}) })
} }
$end
// 添加&修改 表单提交 // 添加&修改 表单提交
function submitForm() { function submitForm() {
proxy.${refs}refs["formRef"].validate((valid) => { proxy.${refs}refs["formRef"].validate((valid) => {
if (valid) { if (valid) {
$foreach(item in genTable.Columns) $foreach(item in genTable.Columns)
$if(item.HtmlType == "checkbox") $if(item.HtmlType == "checkbox" || item.HtmlType == "selectMulti")
form.value.${item.CsharpFieldFl} = form.value.${item.CsharpFieldFl}Checked.toString(); form.value.${item.CsharpFieldFl} = form.value.${item.CsharpFieldFl}Checked.toString();
$end $end
$end $end
@@ -715,24 +726,27 @@ $if(sub)
form.value.${tool.FirstLowerCase(genTable.SubTable.ClassName)}Nav = ${tool.FirstLowerCase(genTable.SubTable.ClassName)}List.value form.value.${tool.FirstLowerCase(genTable.SubTable.ClassName)}Nav = ${tool.FirstLowerCase(genTable.SubTable.ClassName)}List.value
$end $end
if (form.value.${replaceDto.FistLowerPk} != undefined && opertype.value === 2) { if (form.value.${replaceDto.FistLowerPk} != undefined && opertype.value === 2) {
$if(replaceDto.ShowBtnEdit)
update${genTable.BusinessName}(form.value).then((res) => { update${genTable.BusinessName}(form.value).then((res) => {
proxy.${modal}modal.msgSuccess("修改成功") proxy.${modal}modal.msgSuccess("修改成功")
open.value = false open.value = false
getList() getList()
}) })
.catch(() => {}) $end
} else { } else {
$if(replaceDto.ShowBtnAdd)
add${genTable.BusinessName}(form.value).then((res) => { add${genTable.BusinessName}(form.value).then((res) => {
proxy.${modal}modal.msgSuccess("新增成功") proxy.${modal}modal.msgSuccess("新增成功")
open.value = false open.value = false
getList() getList()
}) })
.catch(() => {}) $end
} }
} }
}) })
} }
$if(replaceDto.ShowBtnMultiDel || replaceDto.ShowBtnDelete)
// 删除按钮操作 // 删除按钮操作
function handleDelete(row) { function handleDelete(row) {
const Ids = row.${replaceDto.FistLowerPk} || ids.value const Ids = row.${replaceDto.FistLowerPk} || ids.value
@@ -746,8 +760,8 @@ function handleDelete(row) {
getList() getList()
proxy.${modal}modal.msgSuccess("删除成功") proxy.${modal}modal.msgSuccess("删除成功")
}) })
.catch(() => {})
} }
$end
$if(replaceDto.ShowBtnTruncate) $if(replaceDto.ShowBtnTruncate)
// 清空 // 清空
@@ -778,7 +792,7 @@ function handlePreview(row) {
open.value = true open.value = true
title.value = '查看' title.value = '查看'
opertype.value = 3 opertype.value = 3
form.value = row form.value = { ...row }
} }
$end $end
Binary file not shown.
+2 -1
View File
@@ -1,5 +1,7 @@
using Infrastructure; using Infrastructure;
using Infrastructure.Extensions; using Infrastructure.Extensions;
using Infrastructure.Helper;
using Infrastructure.Model;
using JinianNet.JNTemplate; using JinianNet.JNTemplate;
using SqlSugar; using SqlSugar;
using System; using System;
@@ -7,7 +9,6 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using ZR.CodeGenerator.Model; using ZR.CodeGenerator.Model;
using ZR.Common;
using ZR.Model.System.Generate; using ZR.Model.System.Generate;
namespace ZR.CodeGenerator namespace ZR.CodeGenerator
+1
View File
@@ -1,4 +1,5 @@
using Infrastructure; using Infrastructure;
using Infrastructure.Model;
using SqlSugar; using SqlSugar;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -1,4 +1,5 @@
using Infrastructure; using Infrastructure;
using Infrastructure.Model;
using SqlSugar; using SqlSugar;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
+3 -3
View File
@@ -5,13 +5,13 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" /> <ProjectReference Include="..\Infrastructure\ZR.Infrastructure.csproj" />
<ProjectReference Include="..\ZR.Common\ZR.Common.csproj" /> <ProjectReference Include="..\ZR.Common\ZR.Common.csproj" />
<ProjectReference Include="..\ZR.Model\ZR.Model.csproj" /> <ProjectReference Include="..\ZR.ServiceCore\ZR.ServiceCore.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="JinianNet.JNTemplate" Version="2.3.3" /> <PackageReference Include="JinianNet.JNTemplate" Version="2.3.3" />
<PackageReference Include="SqlSugarCoreNoDrive" Version="5.1.4.94" /> <PackageReference Include="SqlSugarCoreNoDrive" Version="5.1.4.105" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+16 -6
View File
@@ -1,4 +1,5 @@
using Infrastructure; using Infrastructure;
using Infrastructure.Model;
using MailKit.Net.Smtp; using MailKit.Net.Smtp;
using MimeKit; using MimeKit;
using MimeKit.Text; using MimeKit.Text;
@@ -19,7 +20,7 @@ namespace ZR.Common
public MailHelper() public MailHelper()
{ {
AppSettings.Bind("MailOptions", mailOptions); AppSettings.Bind("MailOptions", mailOptions);
FromEmail= mailOptions.FromEmail; FromEmail = mailOptions.FromEmail;
} }
public MailHelper(MailOptions _mailOptions) public MailHelper(MailOptions _mailOptions)
{ {
@@ -132,11 +133,20 @@ namespace ZR.Common
//特别说明,对于服务器端的中文相应,Exception中有编码问题,显示乱码了 //特别说明,对于服务器端的中文相应,Exception中有编码问题,显示乱码了
client.Authenticate(System.Text.Encoding.UTF8, mailOptions.FromEmail, mailOptions.Password); client.Authenticate(System.Text.Encoding.UTF8, mailOptions.FromEmail, mailOptions.Password);
var result = client.Send(message); try
//断开 {
client.Disconnect(true); var result = client.Send(message);
Console.WriteLine($"【{DateTime.Now}】发送邮件结果{result}"); //断开
return result; client.Disconnect(true);
Console.WriteLine($"【{DateTime.Now}】发送邮件结果{result}");
return result;
}
catch (Exception ex)
{
client.Disconnect(true);
Log.WriteLine(ConsoleColor.Red, "发送邮件失败" + ex.Message);
return "fail";
}
} }
} }
} }
+1 -5
View File
@@ -6,14 +6,10 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.13.0" /> <PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.13.0" />
<PackageReference Include="CSRedisCore" Version="3.8.670" />
<PackageReference Include="JinianNet.JNTemplate" Version="2.3.3" />
<PackageReference Include="MailKit" Version="4.1.0" /> <PackageReference Include="MailKit" Version="4.1.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageReference Include="MiniExcel" Version="1.31.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" /> <ProjectReference Include="..\Infrastructure\ZR.Infrastructure.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
-1
View File
@@ -1 +0,0 @@
此文件夹用于存放业务代码数据库实体类
-9
View File
@@ -1,9 +0,0 @@
using System;
namespace ZR.Model
{
public enum ProteryConstant
{
NOTNULL = 0
}
}
-40
View File
@@ -1,40 +0,0 @@
using System.Collections.Generic;
using System.Linq;
namespace ZR.Model.System.Dto
{
/// <summary>
/// 登录用户信息存储
/// </summary>
public class LoginUser
{
public long UserId { get; set; }
public long DeptId { get; set; }
public string UserName { get; set; }
/// <summary>
/// 角色集合
/// </summary>
public List<string> RoleIds { get; set; }
/// <summary>
/// 角色集合(数据权限过滤使用)
/// </summary>
public List<SysRole> Roles { get; set; }
/// <summary>
/// 权限集合
/// </summary>
public List<string> Permissions { get; set; } = new List<string>();
public LoginUser()
{
}
public LoginUser(SysUser user, List<SysRole> roles, List<string> permissions)
{
UserId = user.UserId;
UserName = user.UserName;
DeptId = user.DeptId;
Roles = roles;
RoleIds = roles.Select(f => f.RoleKey).ToList();
Permissions = permissions;
}
}
}
+2 -2
View File
@@ -7,9 +7,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MiniExcel" Version="1.31.0" /> <PackageReference Include="MiniExcel" Version="1.31.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SqlSugarCoreNoDrive" Version="5.1.4.94" /> <PackageReference Include="SqlSugarCoreNoDrive" Version="5.1.4.105" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" /> <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+8 -46
View File
@@ -48,7 +48,7 @@ namespace ZR.Repository
public int Insert(List<T> t) public int Insert(List<T> t)
{ {
return Context.Insertable(t).ExecuteCommand(); return InsertRange(t) ? 1 : 0;
} }
public int Insert(T parm, Expression<Func<T, object>> iClumns = null, bool ignoreNull = true) public int Insert(T parm, Expression<Func<T, object>> iClumns = null, bool ignoreNull = true)
{ {
@@ -61,10 +61,10 @@ namespace ZR.Repository
#endregion add #endregion add
#region update #region update
public IUpdateable<T> Updateable(T entity) //public IUpdateable<T> Updateable(T entity)
{ //{
return Context.Updateable(entity); // return Context.Updateable(entity);
} //}
/// <summary> /// <summary>
/// 实体根据主键更新 /// 实体根据主键更新
@@ -72,9 +72,10 @@ namespace ZR.Repository
/// <param name="entity"></param> /// <param name="entity"></param>
/// <param name="ignoreNullColumns"></param> /// <param name="ignoreNullColumns"></param>
/// <returns></returns> /// <returns></returns>
public int Update(T entity, bool ignoreNullColumns = false) public int Update(T entity, bool ignoreNullColumns = false, object data = null)
{ {
return Context.Updateable(entity).IgnoreColumns(ignoreNullColumns).ExecuteCommand(); return Context.Updateable(entity).IgnoreColumns(ignoreNullColumns)
.EnableDiffLogEventIF(data.IsNotEmpty(), data).ExecuteCommand();
} }
/// <summary> /// <summary>
@@ -103,37 +104,6 @@ namespace ZR.Repository
return Context.Updateable(entity).UpdateColumns(expression).Where(where).ExecuteCommand(); return Context.Updateable(entity).UpdateColumns(expression).Where(where).ExecuteCommand();
} }
public int Update(SqlSugarClient client, T entity, Expression<Func<T, object>> expression, Expression<Func<T, bool>> where)
{
return client.Updateable(entity).UpdateColumns(expression).Where(where).ExecuteCommand();
}
/// <summary>
///
/// </summary>
/// <param name="entity"></param>
/// <param name="list"></param>
/// <param name="isNull">默认为true</param>
/// <returns></returns>
public int Update(T entity, List<string> list = null, bool isNull = true)
{
list ??= new List<string>()
{
"Create_By",
"Create_time"
};
return Context.Updateable(entity).IgnoreColumns(isNull).IgnoreColumns(list.ToArray()).ExecuteCommand();
}
//public bool Update(List<T> entity)
//{
// var result = base.Context.Ado.UseTran(() =>
// {
// base.Context.Updateable(entity).ExecuteCommand();
// });
// return result.IsSuccess;
//}
/// <summary> /// <summary>
/// 更新指定列 eg:Update(w => w.NoticeId == model.NoticeId, it => new SysNotice(){ Update_time = DateTime.Now, Title = "通知标题" }); /// 更新指定列 eg:Update(w => w.NoticeId == model.NoticeId, it => new SysNotice(){ Update_time = DateTime.Now, Title = "通知标题" });
/// </summary> /// </summary>
@@ -160,14 +130,6 @@ namespace ZR.Repository
throw; throw;
} }
} }
public IStorageable<T> Storageable(T t)
{
return Context.Storageable<T>(t);
}
public IStorageable<T> Storageable(List<T> t)
{
return Context.Storageable(t);
}
/// <summary> /// <summary>
/// ///
+1 -6
View File
@@ -19,8 +19,7 @@ namespace ZR.Repository
#endregion add #endregion add
#region update #region update
IUpdateable<T> Updateable(T entity); int Update(T entity, bool ignoreNullColumns = false, object data = null);
int Update(T entity, bool ignoreNullColumns = false);
/// <summary> /// <summary>
/// 只更新表达式的值 /// 只更新表达式的值
@@ -32,13 +31,9 @@ namespace ZR.Repository
int Update(T entity, Expression<Func<T, object>> expression, Expression<Func<T, bool>> where); int Update(T entity, Expression<Func<T, object>> expression, Expression<Func<T, bool>> where);
int Update(SqlSugarClient client, T entity, Expression<Func<T, object>> expression, Expression<Func<T, bool>> where);
int Update(Expression<Func<T, bool>> where, Expression<Func<T, T>> columns); int Update(Expression<Func<T, bool>> where, Expression<Func<T, T>> columns);
#endregion update #endregion update
IStorageable<T> Storageable(T t);
IStorageable<T> Storageable(List<T> t);
DbResult<bool> UseTran(Action action); DbResult<bool> UseTran(Action action);
DbResult<bool> UseTran(SqlSugarClient client, Action action); DbResult<bool> UseTran(SqlSugarClient client, Action action);
+2 -4
View File
@@ -5,16 +5,14 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" /> <ProjectReference Include="..\Infrastructure\ZR.Infrastructure.csproj" />
<ProjectReference Include="..\ZR.Model\ZR.Model.csproj" /> <ProjectReference Include="..\ZR.Model\ZR.Model.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Mapster" Version="7.3.0" /> <PackageReference Include="Mapster" Version="7.3.0" />
<PackageReference Include="Microsoft.Extensions.Options" Version="7.0.1" />
<PackageReference Include="MySqlConnector" Version="2.2.6" /> <PackageReference Include="MySqlConnector" Version="2.2.6" />
<PackageReference Include="NETCore.Encrypt" Version="2.1.1" />
<PackageReference Include="SqlSugar.IOC" Version="2.0.0" /> <PackageReference Include="SqlSugar.IOC" Version="2.0.0" />
<PackageReference Include="SqlSugarCoreNoDrive" Version="5.1.4.94" /> <PackageReference Include="SqlSugarCoreNoDrive" Version="5.1.4.105" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+4 -1
View File
@@ -1 +1,4 @@
global using System.Collections.Generic; global using System.Collections.Generic;
global using System;
global using SqlSugar;
global using Newtonsoft.Json;
+59
View File
@@ -0,0 +1,59 @@
using Infrastructure;
using Infrastructure.Attribute;
using SqlSugar.IOC;
using ZR.Model.System;
using ZR.Repository;
using ZR.Service.IService;
using ZR.Service.System.IService;
namespace ZR.Service
{
/// <summary>
/// 注意:下面的AppService不要漏了
/// </summary>
[AppService(ServiceType = typeof(IHelloService), ServiceLifetime = LifeTime.Transient)]
public class HelloService : BaseService<ArticleCategory>, IHelloService
{
/// <summary>
/// 引用User服务
/// </summary>
private readonly ISysUserService userService;
/// <summary>
///
/// </summary>
/// <param name="userService"></param>
public HelloService(ISysUserService userService)
{
this.userService = userService;
}
/// <summary>
/// 数据库使用案例
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public string SayHello(string name)
{
//构造函数式使用
var user = JsonConvert.SerializeObject(userService.GetFirst(f => f.UserId == 1));
Console.WriteLine(user);
var postService = App.GetRequiredService<ISysPostService>();
Console.WriteLine(JsonConvert.SerializeObject(postService.GetId(1)));
BaseRepository<SysDept> deptRepo = new();
Console.WriteLine(JsonConvert.SerializeObject(deptRepo.GetId(1)));
var result = DbScoped.SugarScope.Queryable<SysDictType>().Where(f => f.DictId == 1).First();
Console.WriteLine(JsonConvert.SerializeObject(result));
//切换库
//DbScoped.SugarScope.GetConnectionScope(2);
GetFirst(x => x.CategoryId == 1);
Context.Queryable<SysUser>().First(f => f.UserId == 1);
return "Hello:" + name;
}
}
}
+17
View File
@@ -0,0 +1,17 @@
using ZR.Model.System;
namespace ZR.Service.IService
{
/// <summary>
/// Hello接口
/// </summary>
public interface IHelloService : IBaseService<ArticleCategory>
{
/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
string SayHello(string name);
}
}
-29
View File
@@ -1,29 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using ZR.Common;
using ZR.Common.Cache;
namespace ZR.Service.System
{
public class CacheService
{
#region 用户权限 缓存
public static List<string> GetUserPerms(string key)
{
return (List<string>)CacheHelper.GetCache(key);
//return RedisServer.Cache.Get<List<string>>(key).ToList();
}
public static void SetUserPerms(string key, object data)
{
CacheHelper.SetCache(key, data);
//RedisServer.Cache.Set(key, data);
}
public static void RemoveUserPerms(string key)
{
CacheHelper.Remove(key);
//RedisServer.Cache.Del(key);
}
#endregion
}
}
+6 -3
View File
@@ -2,11 +2,14 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1591</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ZR.Common\ZR.Common.csproj" /> <ProjectReference Include="..\ZR.ServiceCore\ZR.ServiceCore.csproj" />
<ProjectReference Include="..\ZR.Repository\ZR.Repository.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -8,11 +8,5 @@ namespace ZR.Service
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
public class BaseService<T> : BaseRepository<T> where T : class, new() public class BaseService<T> : BaseRepository<T> where T : class, new()
{ {
//public IBaseRepository<T> baseRepository;
//public BaseService(IBaseRepository<T> repository)
//{
// this.baseRepository = repository ?? throw new ArgumentNullException(nameof(repository));
//}
} }
} }
@@ -2,9 +2,11 @@
using Infrastructure.Model; using Infrastructure.Model;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using ZR.Model.System.Dto; using ZR.Model.System;
using ZR.Service.System;
using ZR.Service.System.IService;
namespace ZR.Admin.WebApi.Filters namespace ZR.ServiceCore.Middleware
{ {
/// <summary> /// <summary>
/// API授权判断 /// API授权判断
@@ -17,8 +19,11 @@ namespace ZR.Admin.WebApi.Filters
/// 权限字符串,例如 system:user:view /// 权限字符串,例如 system:user:view
/// </summary> /// </summary>
public string Permission { get; set; } = string.Empty; public string Permission { get; set; } = string.Empty;
/// <summary>
/// 角色字符串,例如 common,admin
/// </summary>
public string RolePermi { get; set; } = string.Empty;
private bool HasPermi { get; set; } private bool HasPermi { get; set; }
private bool HasRole { get; set; }
public ActionPermissionFilter() { } public ActionPermissionFilter() { }
public ActionPermissionFilter(string permission) public ActionPermissionFilter(string permission)
{ {
@@ -34,12 +39,22 @@ namespace ZR.Admin.WebApi.Filters
/// <returns></returns> /// <returns></returns>
public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{ {
LoginUser info = Framework.JwtUtil.GetLoginUser(context.HttpContext); TokenModel info = JwtUtil.GetLoginUser(context.HttpContext);
if (info != null && info?.UserId > 0) if (info != null && info?.UserId > 0)
{ {
List<string> perms = info.Permissions; long userId = info.UserId;
List<string> perms = CacheService.GetUserPerms(GlobalConstant.UserPermKEY + userId);
List<string> rolePerms = info.RoleIds; List<string> rolePerms = info.RoleIds;
if (perms == null)
{
var sysPermissionService = App.GetService<ISysPermissionService>();
perms = sysPermissionService.GetMenuPermission(new SysUser() { UserId = userId });
CacheService.SetUserPerms(GlobalConstant.UserPermKEY + userId, perms);
}
if (perms.Exists(f => f.Equals(GlobalConstant.AdminPerm))) if (perms.Exists(f => f.Equals(GlobalConstant.AdminPerm)))
{ {
HasPermi = true; HasPermi = true;
@@ -52,24 +67,22 @@ namespace ZR.Admin.WebApi.Filters
{ {
HasPermi = perms.Exists(f => f.ToLower() == Permission.ToLower()); HasPermi = perms.Exists(f => f.ToLower() == Permission.ToLower());
} }
if (!HasPermi && !string.IsNullOrEmpty(RolePermi))
{
HasPermi = info.RoleIds.Contains(RolePermi);
}
bool isDemoMode = AppSettings.GetAppConfig("DemoMode", false); bool isDemoMode = AppSettings.GetAppConfig("DemoMode", false);
var url = context.HttpContext.Request.Path; var url = context.HttpContext.Request.Path;
//演示公开环境屏蔽权限 //演示公开环境屏蔽权限
string[] denyPerms = new string[] { "update", "add", "remove", "add", "edit", "delete", "import", "run", "start", "stop", "clear", "send", "export", "upload", "common", "gencode", "reset" }; string[] denyPerms = new string[] { "update", "add", "remove", "add", "edit", "delete", "import", "run", "start", "stop", "clear", "send", "export", "upload", "common", "gencode", "reset", "forceLogout", "batchLogout" };
if (isDemoMode && denyPerms.Any(f => Permission.ToLower().Contains(f))) if (isDemoMode && denyPerms.Any(f => Permission.Contains(f, StringComparison.OrdinalIgnoreCase)))
{ {
context.Result = new JsonResult(new { code = ResultCode.FORBIDDEN, msg = "演示模式 , 不允许操作" }); context.Result = new JsonResult(new { code = (int)ResultCode.FORBIDDEN, msg = "演示模式 , 不允许操作" });
} }
if (!HasPermi && !Permission.Equals("common")) if (!HasPermi && !Permission.Equals("common"))
{ {
logger.Info($"用户{info.UserName}没有权限访问{url},当前权限[{Permission}]"); logger.Info($"用户{info.UserName}没有权限访问{url},当前权限[{Permission}]");
JsonResult result = new(new ApiResult() JsonResult result = new(new ApiResult((int)ResultCode.FORBIDDEN, $"你当前没有权限[{Permission}]访问,请联系管理员", url))
{
Code = (int)ResultCode.FORBIDDEN,
Msg = $"你当前没有权限[{Permission}]访问,请联系管理员",
Data = url
})
{ {
ContentType = "application/json", ContentType = "application/json",
}; };
@@ -2,20 +2,20 @@
using Infrastructure.Attribute; using Infrastructure.Attribute;
using Infrastructure.Model; using Infrastructure.Model;
using IPTools.Core; using IPTools.Core;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using NLog; using NLog;
using ZR.Admin.WebApi.Extensions;
using ZR.Model.System; using ZR.Model.System;
using ZR.Service.System.IService; using ZR.Service.System.IService;
namespace ZR.Admin.WebApi.Filters namespace ZR.ServiceCore.Middleware
{ {
public class GlobalActionMonitor : ActionFilterAttribute public class GlobalActionMonitor : ActionFilterAttribute
{ {
static readonly Logger logger = LogManager.GetCurrentClassLogger(); static readonly Logger logger = LogManager.GetCurrentClassLogger();
private ISysOperLogService OperLogService; private readonly ISysOperLogService OperLogService;
public GlobalActionMonitor(ISysOperLogService operLogService) public GlobalActionMonitor(ISysOperLogService operLogService)
{ {
OperLogService = operLogService; OperLogService = operLogService;
@@ -29,29 +29,25 @@ namespace ZR.Admin.WebApi.Filters
/// <returns></returns> /// <returns></returns>
public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{ {
ApiResult response = new(); string msg = string.Empty;
response.Code = (int)ResultCode.PARAM_ERROR;
var values = context.ModelState.Values; var values = context.ModelState.Values;
foreach (var item in values) foreach (var item in values)
{ {
foreach (var err in item.Errors) foreach (var err in item.Errors)
{ {
if (err.ErrorMessage.Contains("JSON")) if (!string.IsNullOrEmpty(msg))
{ {
return next(); msg += " | ";
}
if (!string.IsNullOrEmpty(response.Msg))
{
response.Msg += " | ";
} }
response.Msg += err.ErrorMessage; msg += err.ErrorMessage;
} }
} }
if (!string.IsNullOrEmpty(response.Msg)) if (!string.IsNullOrEmpty(msg))
{ {
logger.Info($"请求参数错误,{response.Msg}"); logger.Info($"请求参数错误,{msg}");
ApiResult response = new((int)ResultCode.PARAM_ERROR, msg);
context.Result = new JsonResult(response); context.Result = new JsonResult(response);
} }
return base.OnActionExecutionAsync(context, next); return base.OnActionExecutionAsync(context, next);
@@ -101,12 +97,12 @@ namespace ZR.Admin.WebApi.Filters
OperUrl = HttpContextExtension.GetRequestUrl(context.HttpContext), OperUrl = HttpContextExtension.GetRequestUrl(context.HttpContext),
RequestMethod = method, RequestMethod = method,
JsonResult = jsonResult, JsonResult = jsonResult,
OperLocation = ip_info.Province + " " + ip_info.City, OperLocation = HttpContextExtension.GetIpInfo(ip),
Method = controller + "." + action + "()", Method = controller + "." + action + "()",
//Elapsed = _stopwatch.ElapsedMilliseconds, //Elapsed = _stopwatch.ElapsedMilliseconds,
OperTime = DateTime.Now OperTime = DateTime.Now,
OperParam = HttpContextExtension.GetRequestValue(context.HttpContext, method)
}; };
HttpContextExtension.GetRequestValue(context.HttpContext, sysOperLog);
if (logAttribute != null) if (logAttribute != null)
{ {
@@ -117,7 +113,7 @@ namespace ZR.Admin.WebApi.Filters
} }
LogEventInfo ei = new(NLog.LogLevel.Info, "GlobalActionMonitor", ""); LogEventInfo ei = new(NLog.LogLevel.Info, "GlobalActionMonitor", "");
ei.Properties["jsonResult"] = !HttpMethods.IsGet(method) ? jsonResult : ""; ei.Properties["jsonResult"] = !HttpMethods.IsGet(method) ? jsonResult : "";
ei.Properties["requestParam"] = sysOperLog.OperParam; ei.Properties["requestParam"] = sysOperLog.OperParam;
ei.Properties["user"] = userName; ei.Properties["user"] = userName;
@@ -131,7 +127,7 @@ namespace ZR.Admin.WebApi.Filters
} }
} }
private LogAttribute? GetLogAttribute(ControllerActionDescriptor controllerActionDescriptor) private LogAttribute GetLogAttribute(ControllerActionDescriptor controllerActionDescriptor)
{ {
var attribute = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true) var attribute = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true)
.FirstOrDefault(a => a.GetType().Equals(typeof(LogAttribute))); .FirstOrDefault(a => a.GetType().Equals(typeof(LogAttribute)));
@@ -4,23 +4,18 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using NLog; using ZR.Common;
using System;
using System.Linq;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Framework;
using ZR.Model.System.Dto;
//本命名空间暂时先不改,改动比较大2023年9月2日
namespace ZR.Admin.WebApi.Filters namespace ZR.Admin.WebApi.Filters
{ {
/// <summary> /// <summary>
/// 授权校验访问 /// 授权校验访问
/// 如果跳过授权登录在Action 或controller加上 AllowAnonymousAttribute /// 如果跳过授权登录在Action 或controller加上 AllowAnonymousAttribute
/// </summary> /// </summary>
[AttributeUsage(AttributeTargets.All)]
public class VerifyAttribute : Attribute, IAuthorizationFilter public class VerifyAttribute : Attribute, IAuthorizationFilter
{ {
static readonly Logger logger = LogManager.GetCurrentClassLogger();
/// <summary> /// <summary>
/// 只判断token是否正确,不判断权限 /// 只判断token是否正确,不判断权限
/// 如果需要判断权限的在Action上加上ApiActionPermission属性标识权限类别,ActionPermissionFilter作权限处理 /// 如果需要判断权限的在Action上加上ApiActionPermission属性标识权限类别,ActionPermissionFilter作权限处理
@@ -40,16 +35,35 @@ namespace ZR.Admin.WebApi.Filters
string ip = HttpContextExtension.GetClientUserIp(context.HttpContext); string ip = HttpContextExtension.GetClientUserIp(context.HttpContext);
string url = context.HttpContext.Request.Path; string url = context.HttpContext.Request.Path;
var isAuthed = context.HttpContext.User.Identity.IsAuthenticated; var isAuthed = context.HttpContext.User.Identity.IsAuthenticated;
string osType = context.HttpContext.Request.Headers["os"];
//使用jwt token校验2020-11-21 //使用jwt token校验2020-11-21
LoginUser info = JwtUtil.GetLoginUser(context.HttpContext); TokenModel loginUser = JwtUtil.GetLoginUser(context.HttpContext);
if (loginUser != null)
{
var nowTime = DateTime.UtcNow;
TimeSpan ts = loginUser.ExpireTime - nowTime;
if (info == null || !isAuthed) //Console.WriteLine($"jwt到期剩余:{ts.TotalMinutes}分,{ts.TotalSeconds}秒");
var CK = "token_" + loginUser.UserId;
if (!CacheHelper.Exists(CK) && ts.TotalMinutes < 5)
{
var newToken = JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser));
CacheHelper.SetCache(CK, CK, 1);
//移动端不加下面这个获取不到自定义Header
if (osType != null)
{
context.HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "X-Refresh-Token");
}
context.HttpContext.Response.Headers.Add("X-Refresh-Token", newToken);
}
}
if (loginUser == null || !isAuthed)
{ {
string msg = $"请求访问[{url}]失败,无法访问系统资源"; string msg = $"请求访问[{url}]失败,无法访问系统资源";
logger.Info($"{msg}"); //logger.Info(msg);
context.Result = new JsonResult(ApiResult.Error(ResultCode.DENY, msg));
context.Result = new JsonResult(new ApiResult((int)ResultCode.DENY, msg));
} }
} }
} }
+5
View File
@@ -0,0 +1,5 @@
global using Newtonsoft.Json;
global using SqlSugar;
global using System;
global using System.Collections.Generic;
global using Infrastructure.Extensions;
@@ -2,17 +2,16 @@
using Infrastructure.Attribute; using Infrastructure.Attribute;
using Infrastructure.Model; using Infrastructure.Model;
using IPTools.Core; using IPTools.Core;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using NLog; using NLog;
using System.Diagnostics;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json;
using ZR.Admin.WebApi.Extensions;
using ZR.Common; using ZR.Common;
using ZR.Model.System; using ZR.Model.System;
using ZR.Service.System.IService; using ZR.Service.System.IService;
using textJson = System.Text.Json;
namespace ZR.Admin.WebApi.Middleware namespace ZR.ServiceCore.Middleware
{ {
/// <summary> /// <summary>
/// 全局异常处理中间件 /// 全局异常处理中间件
@@ -45,16 +44,18 @@ namespace ZR.Admin.WebApi.Middleware
private async Task HandleExceptionAsync(HttpContext context, Exception ex) private async Task HandleExceptionAsync(HttpContext context, Exception ex)
{ {
NLog.LogLevel logLevel = NLog.LogLevel.Info; LogLevel logLevel = LogLevel.Info;
int code = (int)ResultCode.GLOBAL_ERROR; int code = (int)ResultCode.GLOBAL_ERROR;
string msg; string msg;
string error = string.Empty; string error = string.Empty;
bool notice = true;
//自定义异常 //自定义异常
if (ex is CustomException customException) if (ex is CustomException customException)
{ {
code = customException.Code; code = customException.Code;
msg = customException.Message; msg = customException.Message;
error = customException.LogMsg; error = customException.LogMsg;
notice = customException.Notice;
} }
else if (ex is ArgumentException)//参数异常 else if (ex is ArgumentException)//参数异常
{ {
@@ -65,18 +66,18 @@ namespace ZR.Admin.WebApi.Middleware
{ {
msg = "服务器好像出了点问题,请联系系统管理员..."; msg = "服务器好像出了点问题,请联系系统管理员...";
error = $"{ex.Message}"; error = $"{ex.Message}";
logLevel = NLog.LogLevel.Error; logLevel = LogLevel.Error;
context.Response.StatusCode = 500; context.Response.StatusCode = 500;
} }
var options = new JsonSerializerOptions var options = new textJson.JsonSerializerOptions
{ {
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, PropertyNamingPolicy = textJson.JsonNamingPolicy.CamelCase,
WriteIndented = true WriteIndented = true
}; };
ApiResult apiResult = new(code, msg); ApiResult apiResult = new(code, msg);
string responseResult = JsonSerializer.Serialize(apiResult, options).ToLower(); string responseResult = textJson.JsonSerializer.Serialize(apiResult, options);
string ip = HttpContextExtension.GetClientUserIp(context); string ip = HttpContextExtension.GetClientUserIp(context);
var ip_info = IpTool.Search(ip); var ip_info = IpTool.Search(ip);
@@ -90,9 +91,9 @@ namespace ZR.Admin.WebApi.Middleware
ErrorMsg = string.IsNullOrEmpty(error) ? msg : error, ErrorMsg = string.IsNullOrEmpty(error) ? msg : error,
OperName = HttpContextExtension.GetName(context), OperName = HttpContextExtension.GetName(context),
OperLocation = ip_info.Province + " " + ip_info.City, OperLocation = ip_info.Province + " " + ip_info.City,
OperTime = DateTime.Now OperTime = DateTime.Now,
OperParam = HttpContextExtension.GetRequestValue(context, context.Request.Method)
}; };
HttpContextExtension.GetRequestValue(context, sysOperLog);
var endpoint = GetEndpoint(context); var endpoint = GetEndpoint(context);
if (endpoint != null) if (endpoint != null)
{ {
@@ -118,15 +119,16 @@ namespace ZR.Admin.WebApi.Middleware
Logger.Log(ei); Logger.Log(ei);
context.Response.ContentType = "text/json;charset=utf-8"; context.Response.ContentType = "text/json;charset=utf-8";
await context.Response.WriteAsync(responseResult, System.Text.Encoding.UTF8); await context.Response.WriteAsync(responseResult, System.Text.Encoding.UTF8);
string errorMsg = $"> 操作人:{sysOperLog.OperName}" + string errorMsg = $"> 操作人:{sysOperLog.OperName}" +
$"\n> 操作地区:{sysOperLog.OperIp}({sysOperLog.OperLocation})" + $"\n> 操作地区:{sysOperLog.OperIp}({sysOperLog.OperLocation})" +
$"\n> 操作模块:{sysOperLog.Title}" + $"\n> 操作模块:{sysOperLog.Title}" +
$"\n> 操作地址:{sysOperLog.OperUrl}" + $"\n> 操作地址:{sysOperLog.OperUrl}" +
$"\n> 错误信息:{msg}\n\n> {error}"; $"\n> 错误信息:{msg}\n\n> {error}";
WxNoticeHelper.SendMsg("系统出错", errorMsg, "", WxNoticeHelper.MsgType.markdown);
SysOperLogService.InsertOperlog(sysOperLog); SysOperLogService.InsertOperlog(sysOperLog);
if (!notice) return;
WxNoticeHelper.SendMsg("系统异常", errorMsg, msgType: WxNoticeHelper.MsgType.markdown);
} }
public static Endpoint GetEndpoint(HttpContext context) public static Endpoint GetEndpoint(HttpContext context)
+11
View File
@@ -0,0 +1,11 @@
namespace ZR.Model.System.Dto
{
public class LockUserDto
{
public string Name { get; set; }
public string ClientId { get; set; }
public string ConnnectionId { get; set; }
public string Reason { get; set; }
public int Time { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More