Compare commits

..

No commits in common. "net9.0" and "v20230811" have entirely different histories.

307 changed files with 1882 additions and 7521 deletions

View File

@ -1,25 +0,0 @@
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/.idea
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

25
.gitignore vendored
View File

@ -259,31 +259,16 @@ paket-files/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
/ZR.Admin.WebApi/appsettings.Development.json
/CodeGenerate
/ZR.Admin.WebApi/appsettings.Production.json
/ZR.Admin.WebApi/wwwroot/uploads
/ZR.Admin.WebApi/wwwroot/Generatecode
/ZR.Admin.WebApi/wwwroot/export
/ZR.Vue/src/views/business/Gendemo.vue
/ZR.Admin.WebApi/Properties/launchSettings.json
/ZR.Admin.WebApi/ZRAdmin.xml
/ZR.Admin.WebApi/DataProtection
/Quartz.NET.WindowsService
/ZRAdmin-vue/node_modules/
/ZRAdmin-vue/.DS_Store
/ZRAdmin-vue/dist/
/ZRAdmin-vue/npm-debug.log*
/ZRAdmin-vue/yarn-debug.log*
/ZRAdmin-vue/yarn-error.log*
/ZRAdmin-vue/**/*.log
/ZRAdmin-vue/tests/**/coverage/
/ZRAdmin-vue/tests/e2e/reports
/ZRAdmin-vue/selenium-debug.log
/ZRAdmin-vue/.idea
/ZRAdmin-vue/*.suo
/ZRAdmin-vue/*.ntvs*
/ZRAdmin-vue/*.njsproj
/ZRAdmin-vue/*.sln
/ZRAdmin-vue/*.local
/ZRAdmin-vue/package-lock.json
/ZRAdmin-vue/deploy.js
/ZRAdmin-vue
/ZR.Admin.WebApi/ZRModel.xml
ZRAdmin-vue/dist.zip
**/.vshistory/

View File

@ -1,11 +1,12 @@
using Infrastructure.Model;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System;
using System.Security.Claims;
using System.Security.Principal;
using System.Xml.Linq;
namespace Infrastructure
{

View File

@ -1,4 +1,10 @@
namespace ZR.ServiceCore.Signalr
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Infrastructure.Constant
{
public class HubsConstant
{
@ -7,8 +13,5 @@
public static string OnlineNum = "onlineNum";
public static string MoreNotice = "moreNotice";
public static string OnlineUser = "onlineUser";
public static string LockUser = "lockUser";
public static string ForceUser = "forceUser";
public static string ConnId = "connId";
}
}

View File

@ -1,8 +0,0 @@
namespace ZR.Infrastructure.Constant;
public class SysNoticeLogStatus
{
public const string Unread = "0";
public const string Read = "1";
}

View File

@ -1,48 +0,0 @@
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);
}
}
}

View File

@ -5,19 +5,9 @@ namespace Infrastructure
public class CustomException : Exception
{
public int Code { get; set; }
/// <summary>
/// 前端提示语
/// </summary>
public string Msg { get; set; }
/// <summary>
/// 记录到日志的详细内容
/// </summary>
public string LogMsg { get; set; }
/// <summary>
/// 是否通知
/// </summary>
public bool Notice { get; set; } = true;
public CustomException(string msg) : base(msg)
{
}
@ -27,10 +17,9 @@ namespace Infrastructure
Msg = msg;
}
public CustomException(ResultCode resultCode, string msg, bool notice = true) : base(msg)
public CustomException(ResultCode resultCode, string msg) : base(msg)
{
Code = (int)resultCode;
Notice = notice;
}
/// <summary>

View File

@ -54,10 +54,5 @@
/// 清空数据
/// </summary>
CLEAN = 9,
/// <summary>
/// 下载
/// </summary>
DOWNLOAD = 10,
}
}

View File

@ -170,7 +170,7 @@ namespace Infrastructure.Extensions
{
try
{
return bool.Parse(str.ToString() ?? string.Empty);
return bool.Parse(str.ToString());
}
catch
{
@ -284,9 +284,6 @@ namespace Infrastructure.Extensions
return DateTime.ParseExact(str, "yyyyMMddHH", System.Globalization.CultureInfo.CurrentCulture);
case 12:
return DateTime.ParseExact(str, "yyyyMMddHHmm", System.Globalization.CultureInfo.CurrentCulture);
case 13:
return DateTime.ParseExact(str, "yyyy.MM.dd ddd",
System.Globalization.CultureInfo.CurrentCulture);
case 14:
return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
default:

View File

@ -91,7 +91,7 @@ namespace Infrastructure
#region unix时间戳
/// <summary>
/// 获取unix时间戳(毫秒)
/// 获取unix时间戳
/// </summary>
/// <param name="dt"></param>
/// <returns></returns>

View File

@ -0,0 +1,19 @@
<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>

View File

@ -2,7 +2,7 @@
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Infrastructure.Converter
namespace Infrastructure
{
public class JsonConverterUtil
{

View File

@ -1,14 +0,0 @@
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();
}
}
}

View File

@ -1,26 +1,17 @@
using Infrastructure.Constant;
using Newtonsoft.Json;
using System.Collections.Generic;
namespace Infrastructure.Model
{
public class ApiResult : Dictionary<string, object>
public class ApiResult
{
/** 状态码 */
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; }
public int Code { get; set; }
public string Msg { get; set; }
/// <summary>
/// 如果data值为null则忽略序列化将不会返回data字段
/// </summary>
//[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
//public object Data { get; set; }
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public object Data { get; set; }
/// <summary>
/// 初始化一个新创建的APIResult对象使其表示一个空消息
@ -36,8 +27,8 @@ namespace Infrastructure.Model
/// <param name="msg"></param>
public ApiResult(int code, string msg)
{
Add(CODE_TAG, code);
Add(MSG_TAG, msg);
Code = code;
Msg = msg;
}
/// <summary>
@ -45,28 +36,33 @@ namespace Infrastructure.Model
/// </summary>
/// <param name="code"></param>
/// <param name="msg"></param>
/// <param name="data"></param>
public ApiResult(int code, string msg, object data)
{
Add(CODE_TAG, code);
Add(MSG_TAG, msg);
Code = code;
Msg = msg;
if (data != null)
{
Add(DATA_TAG, data);
Data = data;
}
}
/// <summary>
/// 返回成功消息
/// </summary>
/// < returns > 成功消息 </ returns >
public static ApiResult Success() { return new ApiResult(HttpStatus.SUCCESS, "success"); }
/// <summary>
/// 返回成功消息
/// </summary>
/// <param name="data"></param>
/// <returns> 成功消息 </returns >
public static ApiResult Success(object data) { return new ApiResult(HttpStatus.SUCCESS, "success", data); }
/// <returns></returns>
public ApiResult Success()
{
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>
/// 返回成功消息
@ -83,9 +79,21 @@ namespace Infrastructure.Model
/// <returns>成功消息</returns>
public static ApiResult Success(string msg, object data) { return new ApiResult(HttpStatus.SUCCESS, msg, data); }
public static ApiResult Error(ResultCode code, string msg = "")
/// <summary>
/// 访问被拒
/// </summary>
/// <returns></returns>
public ApiResult On401()
{
return Error((int)code, msg);
Code = (int)ResultCode.DENY;
Msg = "access denyed";
return this;
}
public ApiResult Error(ResultCode resultCode, string msg = "")
{
Code = (int)resultCode;
Msg = msg;
return this;
}
/// <summary>
@ -103,26 +111,9 @@ namespace Infrastructure.Model
/// <returns></returns>
public static ApiResult Error(string msg) { return new ApiResult((int)ResultCode.CUSTOM_ERROR, msg); }
/// <summary>
/// 是否为成功消息
/// </summary>
/// <returns></returns>
public bool IsSuccess()
public override string ToString()
{
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;
return $"msg={Msg},data={Data}";
}
}

View File

@ -12,9 +12,9 @@ namespace Infrastructure.Model
/// <summary>
/// 主题
/// </summary>
// [Required(ErrorMessage = "主题不能为空")]
[Required(ErrorMessage = "主题不能为空")]
public string Subject { get; set; }
// [Required(ErrorMessage = "发送人不能为空")]
[Required(ErrorMessage = "发送人不能为空")]
public string ToUser { get; set; }
public string Content { get; set; } = "";
public string HtmlContent { get; set; }

View File

@ -1,50 +0,0 @@
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; }
public string NickName { 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;
NickName = info.NickName;
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; }
}
}

View File

@ -1,24 +1,18 @@
using System.Collections.Generic;

using System;
using System.Collections.Generic;
namespace Infrastructure.Model
namespace Infrastructure
{
/// <summary>
/// 获取配置文件POCO实体类
/// </summary>
public class OptionsSetting
{
/// <summary>
/// 是否单点登录
/// </summary>
public bool SingleLogin { get; set; }
/// <summary>
/// 是否演示模式
/// </summary>
public bool DemoMode { get; set; }
/// <summary>
/// 初始化db
/// </summary>
public bool InitDb { get; set; }
public MailOptions MailOptions { get; set; }
public Upload Upload { get; set; }
public ALIYUN_OSS ALIYUN_OSS { get; set; }
@ -84,14 +78,6 @@ namespace Infrastructure.Model
/// token时间
/// </summary>
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

View File

@ -1,59 +0,0 @@
using System;
namespace ZR.Infrastructure.Resolver;
public static class AntPathMatcher
{
public static bool Match(string pattern, string value)
{
if (pattern == null || value == null)
return false;
if (pattern == "**")
return true;
var patternSegments = pattern.Split('/');
var valueSegments = value.Split('/');
int patternIndex = 0;
int valueIndex = 0;
int patternWildcardIndex = -1;
int valueWildcardIndex = -1;
while (valueIndex < valueSegments.Length)
{
if (patternIndex < patternSegments.Length && IsMatch(patternSegments[patternIndex], valueSegments[valueIndex]))
{
patternIndex++;
valueIndex++;
}
else if (patternWildcardIndex != -1)
{
patternIndex = patternWildcardIndex + 1;
valueIndex = ++valueWildcardIndex;
}
else
{
return false;
}
}
while (patternIndex < patternSegments.Length && patternSegments[patternIndex] == "**")
{
patternIndex++;
}
return patternIndex >= patternSegments.Length && valueIndex >= valueSegments.Length;
}
private static bool IsMatch(string patternSegment, string valueSegment)
{
if (patternSegment == "*")
return true;
if (patternSegment.StartsWith("{") && patternSegment.EndsWith("}"))
return true; // 支持占位符的匹配逻辑
return string.Equals(patternSegment, valueSegment, StringComparison.OrdinalIgnoreCase);
}
}

View File

@ -1,42 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json.Serialization;
namespace ZR.Infrastructure.Resolver;
public class JsonPropertyContractResolver : CamelCasePropertyNamesContractResolver
{
private HashSet<string> _propertySet;
public JsonPropertyContractResolver(IEnumerable<string> propertyNames)
{
if (propertyNames != null)
{
_propertySet = new HashSet<string>(propertyNames, StringComparer.Ordinal);
}
}
// protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
// {
// var list = base.CreateProperties(type, memberSerialization).ToList();
// var resList = list
// .Where(a => !this._lstInclude.Contains(a.PropertyName))
// .ToList();
// return resList;
// // .FindAll(p => _lstInclude.Contains(p.PropertyName));
// }
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
List<MemberInfo> serializableMembers = null;
var allMembers = base.GetSerializableMembers(objectType);
if (_propertySet != null && _propertySet.Count > 0)
{
serializableMembers = allMembers.Where(m => !_propertySet.Contains(m.Name)).ToList();
}
return serializableMembers != null && serializableMembers.Count > 0 ? serializableMembers : allMembers;
}
}

View File

@ -1,44 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace ZR.Infrastructure.Resolver;
public class PropertyFilterContractResolver : CamelCasePropertyNamesContractResolver
{
private readonly HashSet<string> _includedPaths;
private readonly HashSet<string> _excludedPaths;
public PropertyFilterContractResolver(IEnumerable<string> includedPaths, IEnumerable<string> excludedPaths)
{
_includedPaths = includedPaths.ToHashSet();
_excludedPaths = excludedPaths.ToHashSet();
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
properties = properties.Where(p => IsIncluded(p) && !IsExcluded(p)).ToList();
return properties;
}
private bool IsIncluded(JsonProperty property)
{
if (_includedPaths == null || _includedPaths.Count == 0)
return true;
return _includedPaths.Any(path => AntPathMatcher.Match(path, property.PropertyName));
}
private bool IsExcluded(JsonProperty property)
{
if (_excludedPaths == null || _excludedPaths.Count == 0)
return false;
return _excludedPaths.Any(path => AntPathMatcher.Match(path, property.PropertyName));
}
}

View File

@ -1,39 +0,0 @@
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;
},
};
});
}
}
}

View File

@ -1,31 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.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="Caching.CSRedis" Version="3.8.670" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.12" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="RulesEngine" Version="5.0.2" />
<PackageReference Include="SqlSugar.IOC" Version="2.0.0" />
<PackageReference Include="SqlSugarCoreNoDrive" Version="5.1.4.109" />
<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>

View File

@ -1,11 +1,11 @@
<h2 align="center"> ZR.Admin.NET Back-end management system</h2>
<h4 align="center">base .Net7 + vue2.x/vue3.x/uniapp Front-end and back-end separation of .NET rapid development framework</h4>
<h4 align="center">base.NET5/.Net7 + vue2.x/vue3.x/uniapp Front-end and back-end separation of .NET rapid development framework</h4>
<p align="center">
<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/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/更新日志-20230920-yellow"/></a>
<a href="http://www.izhaorui.cn/doc/changelog.html"><img src="https://img.shields.io/badge/更新日志-20230713-yellow"/></a>
</p>
---
@ -22,6 +22,10 @@
-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!
- 提供了技术栈(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! ~
@ -48,7 +52,7 @@ Since it is a personal project, the funds are limited, and the experience server
| repository | Github | Gitee |
| -------------- | ------------------------------------------------------ | ---------------------------------------------------------------- |
| Net7 | [Clone/Download](https://github.com/izhaorui/Zr.Admin.NET) | [Clone/Download](https://gitee.com/izory/ZrAdminNetCore) |
| Net7 | [Clone/Download](https://github.com/izhaorui/Zr.Admin.NET) | [Clone/Download](https://gitee.com/izory/ZrAdminNetCore/tree/net7.0/) |
| Vue3(Hot) | [Clone/Download](https://github.com/izhaorui/ZR.Admin.Vue3) | [Clone/Download](https://gitee.com/izory/ZRAdmin-vue) |
## 🍁 Front-end technology
@ -57,7 +61,7 @@ Vue Front-end technology stack: Based on Vue2.x/Vue3.x/UniApp, Vue, Vue-router,
## 🍀 Back-end technology
- Core Framework: . Net7.0 + Web API + sqlsugar + swagger + signalR + IpRateLimit + Quartz.net + Redis
- Core Framework: . Net5.0/. 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
- Security support: filters (data permission filtering), SQL injection, request forgery
- Log management: NLog, login log, operation log, scheduled task log
@ -95,8 +99,7 @@ Vue Front-end technology stack: Based on Vue2.x/Vue3.x/UniApp, Vue, Vue-router,
## 🍻 Project structure
```
├─ZR.Service ->[你的业务服务层类库]提供WebApi接口调用
├─ZR.ServiceCore ->[系统服务层类库]提供WebApi接口调用
├─ZR.Service ->[服务层类库]提供WebApi接口调用
├─ZR.Repository ->[仓库层类库]:方便提供有执行存储过程的操作;
├─ZR.Model ->[实体层类库]:提供项目中的数据库表、数据传输对象;
├─ZR.Admin.WebApi ->[webapi接口]为Vue版或其他三方系统提供接口服务。

View File

@ -1,11 +1,11 @@
<h2 align="center"> ZR.Admin.NET后台管理系统</h2>
<h4 align="center">基于.Net7 + vue2.x/vue3.x/uniapp前后端分离的.net快速开发框架</h4>
<h4 align="center">基于.NET5/.Net7 + vue2.x/vue3.x/uniapp前后端分离的.net快速开发框架</h4>
<p align="center">
<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/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/更新日志-20230920-yellow"/></a>
<a href="http://www.izhaorui.cn/doc/changelog.html"><img src="https://img.shields.io/badge/更新日志-20230713-yellow"/></a>
</p>
---
@ -28,7 +28,7 @@
- 阿里云特惠专区:[☛☛ 点我进入 ☚☚](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)
- Vue2.x 版本体验:[http://www.izhaorui.cn/admin](http://www.izhaorui.cn/admin)
- Uniapp 版本体验:[http://www.izhaorui.cn/h5](http://www.izhaorui.cn/h5)
- 账号密码admin/123456,普通用户 user/123456
- 账号密码admin/123456
| H5 | 微信小程序 |
| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
@ -50,10 +50,10 @@
## 💒 代码仓库
| 仓库 | Github | Gitee |
| -------------- | ------------------------------------------------------ | --------------------------------------------------- |
| 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) |
| 仓库 | Github | Gitee |
| -------------- | ------------------------------------------------------ | ---------------------------------------------------------------- |
| Net7 | [克隆/下载](https://github.com/izhaorui/Zr.Admin.NET) | [克隆/下载](https://gitee.com/izory/ZrAdminNetCore/tree/net7.0/) |
| Vue3(推荐使用) | [克隆/下载](https://github.com/izhaorui/ZR.Admin.Vue3) | [克隆/下载](https://gitee.com/izory/ZRAdmin-vue) |
## 🍁 前端技术
@ -72,7 +72,6 @@ Vue 版前端技术栈 :基于 vue2.x/vue3.x/uniapp、vuex、vue-router 、vue
- 分库分表:使用 orm sqlsugar 可以很轻松的实现分库分库性能优越
- 多 租 户:支持多租户功能
- 缓存数据:内置内存缓存和 Redis
- signalR使用 signalr 管理用户在线状态
## 🍖 内置功能
@ -89,29 +88,23 @@ Vue 版前端技术栈 :基于 vue2.x/vue3.x/uniapp、vuex、vue-router 、vue
11. 在线构建器:拖动表单元素生成相应的 VUE 代码(仅支持 vue2)。
12. 任务系统:基于 Quartz.NET可以在线添加、修改、删除、手动执行)任务调度包含执行结果日志。
13. 文章管理:可以写文章记录。
14. 代码生成:可以一键生成前后端代码(.cs、.vue、.js、.sql、uniapp 等)支持下载,自定义配置前端展示控件、让开发更快捷高效(史上最强)。
14. 代码生成:可以一键生成前后端代码(.cs、.vue、.js、.sql 等)支持下载,自定义配置前端展示控件、让开发更快捷高效(史上最强)。
15. 参数管理:对系统动态配置常用参数。
16. 发送邮件:可以对多个用户进行发送邮件。
17. 文件管理:可以进行上传文件管理,目前支持上传到本地、阿里云。
18. 通知管理:系统通知公告信息发布维护,使用 signalr 实现对用户实时通知。
19. 账号注册:可以注册账号登录系统。
20. 多语言管理:支持静态、后端动态配置国际化。目前只支持中、英、繁体(仅支持 vue3)
21. 在线用户:可以查看正在登录使用的用户,可以对其踢出、通知操作
22. db 审计日志:数据库审计功能
23. 三方登录:提供三方登录实现逻辑
## 🍻 项目结构
![alt](https://gitee.com/izory/ZrAdminNetCore/raw/master/document/images/kj.png)
```
├─ZR.Service ->[你的业务服务层类库]提供WebApi接口调用
├─ZR.ServiceCore ->[系统服务层类库]提供WebApi接口调用
├─ZR.Repository ->[仓库层类库]:方便提供有执行存储过程的操作;
├─ZR.Model ->[实体层类库]:提供项目中的数据库表、数据传输对象;
├─ZR.Admin.WebApi ->[webapi接口]为Vue版或其他三方系统提供接口服务。
├─ZR.Service ->[服务层类库]提供WebApi接口调用
├─ZR.Repository ->[仓库层类库]:方便提供有执行存储过程的操作;
├─ZR.Model ->[实体层类库]:提供项目中的数据库表、数据传输对象;
├─ZR.Admin.WebApi ->[webapi接口]为Vue版或其他三方系统提供接口服务。
├─ZR.Tasks ->[定时任务类库]:提供项目定时任务实现功能;
├─ZR.CodeGenerator ->[代码生成功能]:包含代码生成的模板、方法、代码生成的下载。
├─ZR.CodeGenerator ->[代码生成功能]:包含代码生成的模板、方法、代码生成的下载。
├─ZR.Vue ->[前端UI]vue2.0版本UI层(已经不再更新推荐使用vue3)。
├─document ->[文档]:数据库脚本
```
@ -153,15 +146,12 @@ Vue 版前端技术栈 :基于 vue2.x/vue3.x/uniapp、vuex、vue-router 、vue
</tr>
<tr>
<td><img src="https://gitee.com/izory/ZrAdminNetCore/raw/master/document/images/17.png"/></td>
<td><img src="https://gitee.com/izory/ZrAdminNetCore/raw/master/document/images/18.png"/></td>
<td><img src="https://gitee.com/izory/ZrAdminNetCore/raw/master/document/images/18.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/izory/ZrAdminNetCore/raw/master/document/images/19.png"/></td>
<td><img src="https://gitee.com/izory/ZrAdminNetCore/raw/master/document/images/20.png"/></td>
</tr>
<tr>
<td><img src="https://gitee.com/izory/ZrAdminNetCore/raw/master/document/images/21.png"/></td>
</tr>
</table>
## 移动端演示图
@ -198,7 +188,6 @@ Vue 版前端技术栈 :基于 vue2.x/vue3.x/uniapp、vuex、vue-router 、vue
5. 自定义的代码生成功能
6. 依赖少,上手容易
7. 文档全面
8. 支持中文表头导入数据
## 💐 特别鸣谢
@ -206,7 +195,6 @@ Vue 版前端技术栈 :基于 vue2.x/vue3.x/uniapp、vuex、vue-router 、vue
- 👉SqlSugar[SqlSugar](https://gitee.com/dotnetchina/SqlSugar)
- 👉vue-element-admin[vue-element-admin](https://github.com/PanJiaChen/vue-element-admin)
- 👉Meiam.System[Meiam.System](https://github.com/91270/Meiam.System)
- 👉Furion[Furion](https://gitee.com/dotnetchina/Furion)
## 🎀 捐赠

View File

@ -1,17 +0,0 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace ZR.Admin.Grpc.Extensions;
public static class GrpcServiceExtensions
{
public static void AddGrpcServiceConfiguration(this IServiceCollection services, IConfiguration configuration)
{
// services.AddTransient<AuthUn>()
services.AddGrpcClient<Greeter.GreeterClient>(options =>
{
options.Address = new Uri(configuration.GetValue<string>("GrpcUrls:FindTeacher"));
});
}
}

View File

@ -1,54 +0,0 @@
syntax = "proto3";
option csharp_namespace = "ZR.Admin.Grpc";
package filetransfer;
service FileTransfer {
rpc InitUpload (InitUploadRequest) returns (InitUploadResponse);
rpc UploadChunk (stream UploadChunkRequest) returns (UploadChunkResponse);
rpc CompleteUpload (CompleteUploadRequest) returns (CompleteUploadResponse);
rpc CheckUploadStatus (CheckUploadStatusRequest) returns (CheckUploadStatusResponse);
}
message InitUploadRequest {
string file_name = 1;
int64 file_size = 2;
string file_hash = 3;
}
message InitUploadResponse {
string session_id = 1;
int64 uploaded_bytes = 2;
}
message UploadChunkRequest {
string session_id = 1;
bytes chunk_data = 2;
int64 offset = 3;
}
message UploadChunkResponse {
int64 uploaded_bytes = 1;
}
message CompleteUploadRequest {
string session_id = 1;
string file_hash = 2;
}
message CompleteUploadResponse {
bool success = 1;
string message = 2;
}
message CheckUploadStatusRequest {
string file_name = 1;
string file_hash = 2;
}
message CheckUploadStatusResponse {
string session_id = 1;
int64 uploaded_bytes = 2;
bool exists = 3;
}

View File

@ -1,17 +0,0 @@
syntax = "proto3";
option csharp_namespace = "GrpcService1";
package block;
service Block {
rpc GetBlock (BlockRequest) returns (BlockReply);
}
message BlockRequest {
string in = 1;
}
message BlockReply {
string out = 1;
}

View File

@ -1,21 +0,0 @@
syntax = "proto3";
option csharp_namespace = "ZR.Admin.Grpc";
package greet;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply);
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}

View File

@ -1,35 +0,0 @@
syntax = "proto3";
option csharp_namespace = "ZR.Admin.Grpc";
package my.diary;
import "google/protobuf/timestamp.proto";
// The greeting service definition.
service MyDiary {
// Sends a greeting
rpc Edit (EditRequest) returns (EditReply);
rpc Query (QueryRequest) returns (QueryReply);
}
// The request message containing the user's name.
message EditRequest {
string content = 1;
int64 user_id = 2;
google.protobuf.Timestamp create_time = 3; // 使 snake_case
}
// The response message containing the greetings.
message EditReply {
string result_message = 1;
}
message QueryRequest {
int64 user_id = 1;
google.protobuf.Timestamp create_time = 2;
}
message QueryReply {
string content = 1;
}

View File

@ -1,109 +0,0 @@
using Grpc.Core;
using ZR.Admin.Grpc;
namespace GrpcService.Services
{
public class FileTransferService : FileTransfer.FileTransferBase
{
private readonly string _storagePath = "Îļþ";
private readonly IUploadSessionManager _sessionManager;
public FileTransferService(IUploadSessionManager sessionManager)
{
Directory.CreateDirectory(_storagePath);
_sessionManager = sessionManager;
}
public override Task<InitUploadResponse> InitUpload(InitUploadRequest request, ServerCallContext context)
{
var sessionId = _sessionManager.CreateSession(request.FileName, request.FileSize, request.FileHash);
var uploadedBytes = _sessionManager.GetUploadedBytes(sessionId);
return Task.FromResult(new InitUploadResponse
{
SessionId = sessionId,
UploadedBytes = uploadedBytes
});
}
public override async Task<UploadChunkResponse> UploadChunk(IAsyncStreamReader<UploadChunkRequest> requestStream, ServerCallContext context)
{
UploadChunkRequest currentRequest = null;
var response = new UploadChunkResponse();
while (await requestStream.MoveNext())
{
currentRequest = requestStream.Current;
var session = _sessionManager.GetSession(currentRequest.SessionId);
if (session == null)
{
throw new RpcException(new Status(StatusCode.NotFound, "Session not found"));
}
using (var fileStream = new FileStream(session.TempFilePath, FileMode.OpenOrCreate, FileAccess.Write))
{
fileStream.Seek(currentRequest.Offset, SeekOrigin.Begin);
await fileStream.WriteAsync(currentRequest.ChunkData.ToByteArray(), 0, currentRequest.ChunkData.Length);
}
_sessionManager.UpdateSessionProgress(currentRequest.SessionId, currentRequest.Offset + currentRequest.ChunkData.Length);
response.UploadedBytes = currentRequest.Offset + currentRequest.ChunkData.Length;
}
return response;
}
public override async Task<CompleteUploadResponse> CompleteUpload(CompleteUploadRequest request, ServerCallContext context)
{
var session = _sessionManager.GetSession(request.SessionId);
if (session == null)
{
return await Task.FromResult(new CompleteUploadResponse
{
Success = false,
Message = "Session not found"
});
}
// Verify file hash if needed
if (!string.IsNullOrEmpty(request.FileHash))
{
var actualHash = await FileHelper.GetQuickFileIdentity(session.TempFilePath);
if (actualHash != request.FileHash)
{
return await Task.FromResult(new CompleteUploadResponse
{
Success = false,
Message = "File hash mismatch"
});
}
}
// Move temp file to final location
var finalPath = Path.Combine(_storagePath, session.FileName);
//var finalPath = FileHelper.GetUniqueFilePath(_storagePath, session.FileName);
File.Move(session.TempFilePath, finalPath, true);
_sessionManager.CompleteSession(request.SessionId);
return await Task.FromResult(new CompleteUploadResponse
{
Success = true,
Message = "File uploaded successfully"
});
}
public override Task<CheckUploadStatusResponse> CheckUploadStatus(CheckUploadStatusRequest request, ServerCallContext context)
{
var session = _sessionManager.FindSession(request.FileName, request.FileHash);
return Task.FromResult(new CheckUploadStatusResponse
{
SessionId = session?.SessionId ?? "",
UploadedBytes = session?.UploadedBytes ?? 0,
Exists = session != null
});
}
}
}

View File

@ -1,37 +0,0 @@
using Grpc.Core;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ZR.Admin.Grpc.Services
{
public class GreeterService : Greeter.GreeterBase
{
private readonly ILogger<GreeterService> _logger;
public GreeterService(ILogger<GreeterService> logger)
{
_logger = logger;
}
// 实现 SayHello 方法
// 第一个参数是请求消息 (HelloRequest)
// 第二个参数是服务器上下文 (ServerCallContext),提供关于当前调用的信息
public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
{
_logger.LogInformation($"接收到来自 {request.Name} 的请求");
// 构建并返回回复消息
var reply = new HelloReply
{
Message = $"你好,{request.Name} 来自 gRPC 服务器!"
};
// 返回一个 Task<HelloReply>
return Task.FromResult(reply);
}
}
}

View File

@ -1,76 +0,0 @@
using Grpc.Core;
using Microsoft.Extensions.Logging;
using ZR.Service.IService;
namespace ZR.Admin.Grpc.Services
{
public class MyDiaryService(ILogger<MyDiaryService> logger, IMyDiaryService myDiaryService) : MyDiary.MyDiaryBase
{
public override async Task<EditReply> Edit(EditRequest request, ServerCallContext context)
{
logger.LogInformation($"接收到来自 {request.UserId} 的请求");
var myDiary = new Model.MyDiary()
{
Content = request.Content,
CreateTime = request.CreateTime.ToDateTime().ToLocalTime(),
UserId = request.UserId
};
var myDiaryToday = await myDiaryService.Queryable()
.Where(it => it.CreateTime.Date == myDiary.CreateTime.Date && it.UserId == myDiary.UserId)
.FirstAsync();
if (myDiaryToday != null)
{
myDiaryToday.Content = myDiary.Content;
await myDiaryService.Updateable(myDiaryToday)
.ExecuteCommandAsync();
}
else
{
await myDiaryService.Insertable(myDiary).ExecuteReturnSnowflakeIdAsync();
}
// 构建并返回回复消息
var reply = new EditReply()
{
ResultMessage = $"日记已记录 来自 gRPC 服务器!"
};
logger.LogInformation($"日记已记录");
// 返回一个 Task<HelloReply>
return reply;
}
public override async Task<QueryReply> Query(QueryRequest request, ServerCallContext context)
{
logger.LogInformation($"接收到来自 {request.UserId} 的请求");
var myDiary = new Model.MyDiary()
{
CreateTime = request.CreateTime.ToDateTime().ToLocalTime(),
UserId = request.UserId
};
var myDiaryToday = await myDiaryService.Queryable()
.Where(it => it.CreateTime.Date == myDiary.CreateTime.Date && it.UserId == myDiary.UserId)
.FirstAsync();
if (myDiaryToday != null)
{
myDiary.Content = myDiaryToday.Content;
}
// 构建并返回回复消息
var reply = new QueryReply()
{
Content = myDiary.Content ?? ""
};
return reply;
}
}
}

View File

@ -1,28 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Services\FileTransferService.cs" />
</ItemGroup>
<ItemGroup>
<Protobuf Include="Protos\greet.proto" GrpcServices="Both" />
<Protobuf Include="Protos\block.proto" GrpcServices="Client" />
<Protobuf Include="Protos\my_diary.proto" GrpcServices="Server" />
<Protobuf Include="Protos\FileTransfer.proto" GrpcServices="Server" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.49.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ZR.Service\ZR.Service.csproj" />
</ItemGroup>
</Project>

View File

@ -1,20 +1,13 @@
using Infrastructure.Extensions;
using Infrastructure.Model;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using MiniExcelLibs;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using Io = System.IO;
namespace Infrastructure.Controllers
namespace ZR.Admin.WebApi.Controllers
{
/// <summary>
/// web层通用数据处理
/// </summary>
public class BaseController : ControllerBase
{
public static string TIME_FORMAT_FULL = "yyyy-MM-dd HH:mm:ss";
@ -68,7 +61,7 @@ namespace Infrastructure.Controllers
{
throw new CustomException(fileName + "文件不存在");
}
var stream = System.IO.File.OpenRead(path); //创建文件流
var stream = Io.File.OpenRead(path); //创建文件流
Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", HttpUtility.UrlEncode(fileName));
@ -99,10 +92,6 @@ namespace Infrastructure.Controllers
return new ApiResult((int)resultCode, msg, data);
}
protected ApiResult Success()
{
return GetApiResult(ResultCode.SUCCESS);
}
/// <summary>
///
@ -194,9 +183,6 @@ namespace Infrastructure.Controllers
if (!Directory.Exists(fullPath))
{
Directory.CreateDirectory(Path.GetDirectoryName(fullPath));
}
if (!Path.Exists(fullPath))
{
MiniExcel.SaveAs(fullPath, list, overwriteFile: true);
}
return (sFileName, fullPath);

View File

@ -1,12 +1,12 @@
using Infrastructure.Helper;
using JinianNet.JNTemplate;
using Infrastructure.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Service.IService;
using ZR.Model.System;
using ZR.Service.System;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Services.IService;
using ZR.Service.System.IService;
namespace ZR.Admin.WebApi.Controllers
{
@ -14,8 +14,6 @@ namespace ZR.Admin.WebApi.Controllers
/// 公共模块
/// </summary>
[Route("[controller]/[action]")]
[ApiExplorerSettings(GroupName = "sys")]
//[Produces("application/json")]
public class CommonController : BaseController
{
private OptionsSetting OptionsSetting;
@ -23,45 +21,26 @@ namespace ZR.Admin.WebApi.Controllers
private IWebHostEnvironment WebHostEnvironment;
private ISysFileService SysFileService;
private IHelloService HelloService;
public CommonController(
IOptions<OptionsSetting> options,
IWebHostEnvironment webHostEnvironment,
ISysFileService fileService,
IHelloService helloService)
ISysFileService fileService)
{
WebHostEnvironment = webHostEnvironment;
SysFileService = fileService;
OptionsSetting = options.Value;
HelloService = helloService;
}
/// <summary>
/// home
/// hello
/// </summary>
/// <returns></returns>
[Route("/")]
[HttpGet]
public IActionResult Index()
{
var contentTpl = JnHelper.ReadTemplate("", "logo.txt");
var content = contentTpl?.Render();
// return Ok("看到这里页面说明你已经成功启动了本项目:)\n\n" +
// $"{content}如果觉得项目有用,打赏作者喝杯咖啡作为奖励\n☛☛http://www.izhaorui.cn/doc/support.html\n");
return Ok($"{content}\n\n看到这里页面说明你已经成功启动了本项目:)\n");
}
/// <summary>
/// hello
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[Route("/hello")]
[HttpGet]
public IActionResult Hello(string name)
{
return Ok(HelloService.SayHello(name));
return Ok("看到这里页面说明你已经成功启动了本项目:)\n\n" +
"如果觉得项目有用,打赏作者喝杯咖啡作为奖励\n☛☛http://www.izhaorui.cn/doc/support.html\n");
}
/// <summary>
@ -130,7 +109,7 @@ namespace ZR.Admin.WebApi.Controllers
SysFile file = new();
string fileExt = Path.GetExtension(formFile.FileName);//文件后缀
double fileSize = Math.Round(formFile.Length / 1024.0, 2);//文件大小KB
if (OptionsSetting.Upload.NotAllowedExt.Contains(fileExt))
{
return ToResponse(ResultCode.CUSTOM_ERROR, "上传失败,未经允许上传类型");
@ -151,8 +130,7 @@ namespace ZR.Admin.WebApi.Controllers
{
uploadDto.FileDir = OptionsSetting.Upload.LocalSavePath;
}
file = await SysFileService.SaveFileToLocal(savePath, uploadDto.FileName, uploadDto.FileDir,
HttpContext.GetUId(), HttpContext.GetNickName(), formFile, OptionsSetting.Upload.UploadUrl);
file = await SysFileService.SaveFileToLocal(savePath, uploadDto.FileName, uploadDto.FileDir, HttpContext.GetName(), formFile);
break;
case StoreType.REMOTE:
break;
@ -166,8 +144,7 @@ namespace ZR.Admin.WebApi.Controllers
{
return ToResponse(ResultCode.CUSTOM_ERROR, "上传文件过大,不能超过 " + AlimaxContentLength + " MB");
}
file = new(formFile.FileName, uploadDto.FileName, fileExt, fileSize + "kb",
uploadDto.FileDir, HttpContext.GetUId(), HttpContext.GetNickName())
file = new(formFile.FileName, uploadDto.FileName, fileExt, fileSize + "kb", uploadDto.FileDir, HttpContext.GetName())
{
StoreType = (int)StoreType.ALIYUN,
FileType = formFile.ContentType

View File

@ -1,10 +1,9 @@
using Microsoft.AspNetCore.Mvc;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Model.Dto;
using ZR.Model.System;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
namespace ZR.Admin.WebApi.Controllers
{
@ -12,7 +11,6 @@ namespace ZR.Admin.WebApi.Controllers
/// 文章目录Controller
/// </summary>
[Route("article/ArticleCategory")]
[ApiExplorerSettings(GroupName = "article")]
public class ArticleCategoryController : BaseController
{
/// <summary>
@ -61,7 +59,7 @@ namespace ZR.Admin.WebApi.Controllers
public IActionResult GetArticleCategory(int CategoryId)
{
var response = _ArticleCategoryService.GetFirst(x => x.CategoryId == CategoryId);
return SUCCESS(response);
}

View File

@ -2,12 +2,9 @@
using SqlSugar;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Model;
using ZR.Model.System;
using ZR.Model.System.Dto;
using ZR.Service.System.IService;
using ZR.ServiceCore.Filters;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
namespace ZR.Admin.WebApi.Controllers
{
@ -16,7 +13,6 @@ namespace ZR.Admin.WebApi.Controllers
/// </summary>
[Verify]
[Route("article")]
[ApiExplorerSettings(GroupName = "article")]
public class ArticleController : BaseController
{
/// <summary>
@ -36,8 +32,7 @@ namespace ZR.Admin.WebApi.Controllers
/// </summary>
/// <returns></returns>
[HttpGet("list")]
// [ActionPermissionFilter(RolePermi = "admin")]
[DataFieldFilter(typeof(PagedInfo<ArticleDto>))]
[ActionPermissionFilter(Permission = "system:article:list")]
public IActionResult Query([FromQuery] ArticleQueryDto parm)
{
var response = _ArticleService.GetList(parm);
@ -92,7 +87,7 @@ namespace ZR.Admin.WebApi.Controllers
var model = response.Adapt<ArticleDto>();
if (model.IsPublic == 0 && userId != model.UserId)
{
return ToResponse(ResultCode.CUSTOM_ERROR, "访问失败");
return ToResponse(Infrastructure.ResultCode.CUSTOM_ERROR, "访问失败");
}
if (model != null)
{

View File

@ -1,45 +0,0 @@
using ZR.Model;
using ZR.Model.System;
using ZR.ServiceCore.Services.IService;
namespace ZR.Admin.WebApi.Controllers.System;
[Route("base/codeRule")]
public class BaseCodeRuleController : BaseController
{
private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
private readonly IBaseCodeRuleService _baseCodeRuleService;
public BaseCodeRuleController(IBaseCodeRuleService baseCodeRuleService)
{
_baseCodeRuleService = baseCodeRuleService;
}
[HttpPost("addBaseCodeRule")]
public async Task<IActionResult> AddBaseCodeRule([FromBody] BaseCodeRule baseCodeRule)
{
var result = await _baseCodeRuleService.InsertBaseCodeRuleAsync(baseCodeRule);
return SUCCESS(result);
}
[HttpDelete("deleteBaseCodeRule/{code}")]
public async Task<IActionResult> DeleteBaseCodeRule(string code)
{
var result = await _baseCodeRuleService.DeleteBaseCodeRuleAsync(code);
return SUCCESS(result);
}
[HttpPut("updateBaseCodeRule")]
public async Task<IActionResult> UpdateBaseCodeRule([FromBody] BaseCodeRule baseCodeRule)
{
var result = await _baseCodeRuleService.UpdateBaseCodeRule(baseCodeRule);
return SUCCESS(result);
}
[HttpGet("getBaseCodeRuleList")]
public IActionResult GetBaseCodeRuleList([FromQuery] BaseCodeRule baseCodeRule, [FromQuery] PagerInfo pagerInfo)
{
var result = _baseCodeRuleService.SelectBaseCodeRulePage(baseCodeRule, pagerInfo);
return SUCCESS(result);
}
}

View File

@ -1,15 +1,20 @@
using Microsoft.AspNetCore.Mvc;
using Infrastructure;
using Infrastructure.Attribute;
using Infrastructure.Enums;
using Infrastructure.Extensions;
using Mapster;
using Microsoft.AspNetCore.Mvc;
using SqlSugar;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.CodeGenerator;
using ZR.CodeGenerator.Model;
using ZR.CodeGenerator.Service;
using ZR.Common;
using ZR.Model;
using ZR.Model.System.Dto;
using ZR.Model.System.Generate;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model.Dto;
using ZR.ServiceCore.Model.Generate;
using ZR.ServiceCore.Services.IService;
namespace ZR.Admin.WebApi.Controllers
{
@ -18,7 +23,6 @@ namespace ZR.Admin.WebApi.Controllers
/// </summary>
[Verify]
[Route("tool/gen")]
[ApiExplorerSettings(GroupName = "sys")]
public class CodeGeneratorController : BaseController
{
private readonly CodeGeneraterService _CodeGeneraterService = new CodeGeneraterService();
@ -305,8 +309,7 @@ namespace ZR.Admin.WebApi.Controllers
if (string.IsNullOrEmpty(tableName) || tableId <= 0) throw new CustomException("参数错误");
GenTable table = GenTableService.GetGenTableInfo(tableId);
if (table == null) { throw new CustomException("同步数据失败,原表结构不存在"); }
table.Update_by = HttpContext.GetUId();
table.Update_name = HttpContext.GetNickName();
table.Update_by = HttpContext.GetName();
List<DbColumnInfo> dbColumnInfos = _CodeGeneraterService.GetColumnInfo(table.DbName, tableName);
List<GenTableColumn> dbTableColumns = CodeGeneratorTool.InitGenTableColumn(table, dbColumnInfos);

View File

@ -3,9 +3,9 @@ using Microsoft.AspNetCore.Mvc;
using MiniExcelLibs;
using ZR.Admin.WebApi.Filters;
using ZR.Model;
using ZR.Model.Dto;
using ZR.Model.Models;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
namespace ZR.Admin.WebApi.Controllers
{
@ -14,7 +14,6 @@ namespace ZR.Admin.WebApi.Controllers
/// </summary>
[Verify]
[Route("system/CommonLang")]
[ApiExplorerSettings(GroupName = "sys")]
public class CommonLangController : BaseController
{
/// <summary>
@ -177,16 +176,15 @@ namespace ZR.Admin.WebApi.Controllers
public IActionResult ImportData([FromForm(Name = "file")] IFormFile formFile)
{
List<CommonLang> list = new();
var nowTime = DateTime.Now;
using (var stream = formFile.OpenReadStream())
{
var rows = stream.Query(startCell: "A2").ToList();
foreach (var item in rows)
{
list.Add(new CommonLang() { LangCode = "zh-cn", LangKey = item.A, LangName = item.B, Addtime = nowTime });
list.Add(new CommonLang() { LangCode = "en", LangKey = item.A, LangName = item.C, Addtime = nowTime });
list.Add(new CommonLang() { LangCode = "zh-tw", LangKey = item.A, LangName = item.D, Addtime = nowTime });
list.Add(new CommonLang() { LangCode = "zh-cn", LangKey = item.A, LangName = item.B, Addtime = DateTime.Now });
list.Add(new CommonLang() { LangCode = "en", LangKey = item.A, LangName = item.C, Addtime = DateTime.Now });
list.Add(new CommonLang() { LangCode = "zh-tw", LangKey = item.A, LangName = item.D, Addtime = DateTime.Now });
}
}

View File

@ -4,9 +4,8 @@ using SqlSugar;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Model.System;
using ZR.Model.System.Dto;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
namespace ZR.Admin.WebApi.Controllers
{
@ -15,7 +14,6 @@ namespace ZR.Admin.WebApi.Controllers
/// </summary>
[Verify]
[Route("system/config")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysConfigController : BaseController
{
/// <summary>

View File

@ -1,10 +1,10 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Model.System;
using ZR.Model.System.Dto;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
namespace ZR.Admin.WebApi.Controllers.System
{
@ -13,7 +13,6 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary>
[Verify]
[Route("system/dept")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysDeptController : BaseController
{
public ISysDeptService DeptService;
@ -115,8 +114,7 @@ namespace ZR.Admin.WebApi.Controllers.System
{
return ToResponse(ResultCode.CUSTOM_ERROR, $"新增部门{dept.DeptName}失败,部门名称已存在");
}
dept.Create_by = HttpContext.GetUId();
dept.Create_name = HttpContext.GetNickName();
dept.Create_by = HttpContext.GetName();
return ToResponse(DeptService.InsertDept(dept));
}
@ -138,8 +136,7 @@ namespace ZR.Admin.WebApi.Controllers.System
{
return ToResponse(ResultCode.CUSTOM_ERROR, $"修改部门{dept.DeptName}失败,上级部门不能是自己");
}
dept.Update_by = HttpContext.GetUId();
dept.Update_name = HttpContext.GetNickName();
dept.Update_by = HttpContext.GetName();
return ToResponse(DeptService.UpdateDept(dept));
}

View File

@ -1,10 +1,10 @@
using Microsoft.AspNetCore.Mvc;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Model;
using ZR.Model.System;
using ZR.Model.System.Dto;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
namespace ZR.Admin.WebApi.Controllers.System
{
@ -13,7 +13,6 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary>
[Verify]
[Route("system/dict/data")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysDictDataController : BaseController
{
private readonly ISysDictDataService SysDictDataService;
@ -66,14 +65,14 @@ namespace ZR.Admin.WebApi.Controllers.System
/// <returns></returns>
[AllowAnonymous]
[HttpPost("types")]
public IActionResult DictTypes([FromBody] List<SysDictDataDto> dto)
public IActionResult DictTypes([FromBody] List<SysdictDataDto> dto)
{
var list = SysDictDataService.SelectDictDataByTypes(dto.Select(f => f.DictType).ToArray());
List<SysDictDataDto> dataVos = new();
List<SysdictDataDto> dataVos = new();
foreach (var dic in dto)
{
SysDictDataDto vo = new()
SysdictDataDto vo = new()
{
DictType = dic.DictType,
ColumnName = dic.ColumnName,
@ -110,8 +109,7 @@ namespace ZR.Admin.WebApi.Controllers.System
[HttpPost()]
public IActionResult Add([FromBody] SysDictData dict)
{
dict.Create_by = HttpContext.GetUId();
dict.Create_name = HttpContext.GetNickName();
dict.Create_by = HttpContext.GetName();
dict.Create_time = DateTime.Now;
return SUCCESS(SysDictDataService.InsertDictData(dict));
}
@ -126,8 +124,7 @@ namespace ZR.Admin.WebApi.Controllers.System
[HttpPut()]
public IActionResult Edit([FromBody] SysDictData dict)
{
dict.Update_by = HttpContext.GetUId();
dict.Update_name = HttpContext.GetNickName();
dict.Update_by = HttpContext.GetName();
return SUCCESS(SysDictDataService.UpdateDictData(dict));
}

View File

@ -3,9 +3,8 @@ using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Model;
using ZR.Model.System;
using ZR.Model.System.Dto;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
namespace ZR.Admin.WebApi.Controllers.System
{
@ -14,7 +13,6 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary>
[Verify]
[Route("system/dict/type")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysDictTypeController : BaseController
{
private readonly ISysDictService SysDictService;
@ -66,8 +64,7 @@ namespace ZR.Admin.WebApi.Controllers.System
{
return ToResponse(ApiResult.Error($"新增字典'{dict.DictName}'失败,字典类型已存在"));
}
dict.Create_by = HttpContext.GetUId();
dict.Create_name = HttpContext.GetNickName();
dict.Create_by = HttpContext.GetName();
dict.Create_time = DateTime.Now;
return SUCCESS(SysDictService.InsertDictType(dict));
}
@ -89,8 +86,7 @@ namespace ZR.Admin.WebApi.Controllers.System
return ToResponse(ApiResult.Error($"修改字典'{dict.DictName}'失败,字典类型已存在"));
}
//设置添加人
dict.Update_by = HttpContext.GetUId();
dict.Update_name = HttpContext.GetNickName();
dict.Update_by = HttpContext.GetName();
return SUCCESS(SysDictService.UpdateDictType(dict));
}

View File

@ -1,62 +0,0 @@
using ZR.Admin.WebApi.Filters;
using ZR.Model;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
using ZR.ServiceCore.Services.IService;
namespace ZR.Admin.WebApi.Controllers.System;
[Verify]
[Route("system/field")]
public class SysFieldController : BaseController
{
private readonly ISysFieldService _sysFieldService;
private readonly ISysRoleFieldService _sysRoleFieldService;
public SysFieldController(ISysFieldService sysFieldService, ISysRoleFieldService sysRoleFieldService)
{
_sysFieldService = sysFieldService;
_sysRoleFieldService = sysRoleFieldService;
}
[HttpGet("getModelList")]
public async Task<IActionResult> GetModelList([FromQuery] PagerInfo pagerInfo)
=> SUCCESS(await _sysFieldService.GetModelList(pagerInfo));
[HttpGet("getFields")]
public async Task<IActionResult> GetFields([FromQuery] string fullName, long roleId)
=> SUCCESS(await _sysFieldService.GetFields(fullName, roleId));
[HttpPost("initFields")]
public async Task<IActionResult> InitFields()
=> SUCCESS(await _sysFieldService.InitFields());
[HttpPut("addOrUpdateSysRoleField/{roleId}")]
public async Task<IActionResult> AddOrUpdateSysRoleField([FromBody] List<SysFieldDto> sysFieldDtos,
[FromRoute] long roleId)
=> SUCCESS(await _sysRoleFieldService.InsertOrUpdateSysRoleField(sysFieldDtos, roleId));
[HttpPost("fieldDisplay")]
public async Task<IActionResult> FieldDisplay(string blockCode)
{
var info = JwtUtil.GetLoginUser(HttpContext);
var roleIds = info.Roles.Select(it => it.RoleId).ToList();
var list = await _sysFieldService.Queryable()
.LeftJoin<SysFieldBlock>((sf, sfb) => sf.Id == sfb.FieldId)
.LeftJoin<SysBlock>((sf, sfb, sb) => sfb.BlockId == sb.Id )
.LeftJoin<SysRoleField>((sf,sfb, sb, srf) => sf.Id == srf.FieldId)
.Where((sf,sfb, sb, srf) => roleIds.Contains(srf.RoleId))
.Where((sf,sfb, sb, srf) => sb.Code == blockCode)
.WithCache(10 * 60)
.Select((sf,sfb, sb, srf) => sf.FieldName)
.ToListAsync();
// var list = await _sysFieldService.Queryable()
// .LeftJoin<SysRoleField>((sf, srf) => sf.Id == srf.FieldId)
// .Where((sf, srf) => roleIds.Contains(srf.RoleId))
// .WithCache(10 * 60)
// .Select((sf, srf) => sf.FieldName)
// .ToListAsync();
return SUCCESS(list);
}
}

View File

@ -1,9 +1,13 @@
using NLog;
using Microsoft.AspNetCore.Mvc;
using SqlSugar;
using Infrastructure.Attribute;
using Infrastructure.Enums;
using Infrastructure.Model;
using ZR.Admin.WebApi.Filters;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
using ZR.ServiceCore.Services.IService;
using ZR.Common;
using ZR.Model.System;
using ZR.Service.System.IService;
using ZR.Model.System.Dto;
namespace ZR.Admin.WebApi.Controllers
{
@ -12,20 +16,17 @@ namespace ZR.Admin.WebApi.Controllers
/// </summary>
[Verify]
[Route("tool/file")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysFileController : BaseController
{
/// <summary>
/// 文件存储接口
/// </summary>
private readonly ISysFileService _sysFileService;
private readonly ISysFileService _SysFileService;
public SysFileController(ISysFileService sysFileService)
public SysFileController(ISysFileService SysFileService)
{
_sysFileService = sysFileService;
_SysFileService = SysFileService;
}
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
/// <summary>
/// 查询文件存储列表
@ -36,14 +37,16 @@ namespace ZR.Admin.WebApi.Controllers
[ActionPermissionFilter(Permission = "tool:file:list")]
public IActionResult QuerySysFile([FromQuery] SysFileQueryDto parm)
{
//开始拼装查询条件
var predicate = Expressionable.Create<SysFile>();
//搜索条件查询语法参考Sqlsugar
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.StoreType != null, m => m.StoreType == parm.StoreType);
predicate = predicate.AndIF(parm.FileId != null, m => m.Id == parm.FileId);
var response = _sysFileService.GetPages(predicate.ToExpression(), parm, x => x.Id, OrderByType.Desc);
//搜索条件查询语法参考Sqlsugar
var response = _SysFileService.GetPages(predicate.ToExpression(), parm, x => x.Id, OrderByType.Desc);
return SUCCESS(response);
}
@ -56,7 +59,7 @@ namespace ZR.Admin.WebApi.Controllers
[ActionPermissionFilter(Permission = "tool:file:query")]
public IActionResult GetSysFile(long Id)
{
var response = _sysFileService.GetFirst(x => x.Id == Id);
var response = _SysFileService.GetFirst(x => x.Id == Id);
return SUCCESS(response);
}
@ -68,12 +71,13 @@ namespace ZR.Admin.WebApi.Controllers
[HttpDelete("{ids}")]
[ActionPermissionFilter(Permission = "tool:file:delete")]
[Log(Title = "文件存储", BusinessType = BusinessType.DELETE)]
public async Task<IActionResult> DeleteSysFile(string ids)
public IActionResult DeleteSysFile(string ids)
{
long[] idsArr = Tools.SpitLongArrary(ids);
if (idsArr.Length <= 0) { return ToResponse(ApiResult.Error($"删除失败Id 不能为空")); }
var response = await _sysFileService.DeleteSysFileAsync(idsArr);
var response = _SysFileService.Delete(idsArr);
//TODO 删除本地资源
return ToResponse(response);
}
@ -87,89 +91,10 @@ namespace ZR.Admin.WebApi.Controllers
[ActionPermissionFilter(Permission = "tool:file:export")]
public IActionResult Export()
{
var list = _sysFileService.GetAll();
var list = _SysFileService.GetAll();
string sFileName = ExportExcel(list, "SysFile", "文件存储");
return SUCCESS(new { path = "/export/" + sFileName, fileName = sFileName });
}
/// <summary>
/// 通过文件ID下载文件
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
[HttpPost("download")]
public IActionResult Download([FromBody] SysFile file)
{
var sysFile = _sysFileService.Queryable().Where(it => it.Id == file.Id).First();
if (sysFile == null)
{
return NoContent();
}
Stream? stream = null;
switch ((StoreType)Enum.Parse(typeof(StoreType), sysFile.StoreType.ToString() ?? string.Empty))
{
case StoreType.LOCAL:
stream = sysFile.IsEncrypted == "1" ? _sysFileService.DecryptSysFileStream(sysFile.FileUrl)
: new FileStream(sysFile.FileUrl, FileMode.Open);
break;
case StoreType.ALIYUN:
stream = AliyunOssHelper.DownloadFile(sysFile.FileUrl, "", sysFile.IsEncrypted == "1");
break;
default:
throw new CustomException("不支持的存储类型");
}
if (stream != null)
{
return new FileStreamResult(stream, "application/octet-stream")
{
FileDownloadName = sysFile.RealName
};
}
else
{
return NoContent();
}
}
/// <summary>
/// 通过文件ID下载文件
/// </summary>
/// <param name="fileId"></param>
/// <returns></returns>
[HttpGet("download/{fileId}")]
public IActionResult Download([FromRoute] long fileId)
{
var sysFile = _sysFileService.Queryable().Where(it => it.Id == fileId).First();
if (sysFile == null)
{
throw new CustomException("服务器上不存在此文件");
}
Stream? stream;
switch ((StoreType)Enum.Parse(typeof(StoreType), sysFile.StoreType.ToString() ?? string.Empty))
{
case StoreType.LOCAL:
Logger.Info("本地存储");
stream = sysFile.IsEncrypted == "1" ? _sysFileService.DecryptSysFileStream(sysFile.FileUrl)
: new FileStream(sysFile.FileUrl, FileMode.Open);
break;
case StoreType.ALIYUN:
stream = AliyunOssHelper.DownloadFile(sysFile.FileUrl, "", sysFile.IsEncrypted == "1");
break;
default:
throw new CustomException("不支持的存储类型");
}
if (stream != null)
{
return new FileStreamResult(stream, "application/octet-stream")
{
FileDownloadName = sysFile.RealName
};
}
return NoContent();
}
}
}

View File

@ -1,23 +1,24 @@
using Lazy.Captcha.Core;
using IPTools.Core;
using Lazy.Captcha.Core;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using NETCore.Encrypt;
using UAParser;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Infrastructure.Cache;
using ZR.Admin.WebApi.Framework;
using ZR.Model.System;
using ZR.Model.System.Dto;
using ZR.Service.System;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
using ZR.ServiceCore.Services.IService;
namespace ZR.Admin.WebApi.Controllers.System
{
/// <summary>
/// 登录
/// </summary>
[ApiExplorerSettings(GroupName = "sys")]
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 ISysUserService sysUserService;
private readonly ISysMenuService sysMenuService;
@ -26,7 +27,7 @@ namespace ZR.Admin.WebApi.Controllers.System
private readonly ICaptcha SecurityCodeHelper;
private readonly ISysConfigService sysConfigService;
private readonly ISysRoleService roleService;
private readonly OptionsSetting optionSettings;
private readonly OptionsSetting jwtSettings;
public SysLoginController(
IHttpContextAccessor contextAccessor,
@ -37,7 +38,7 @@ namespace ZR.Admin.WebApi.Controllers.System
ISysConfigService configService,
ISysRoleService sysRoleService,
ICaptcha captcha,
IOptions<OptionsSetting> optionSettings)
IOptions<OptionsSetting> jwtSettings)
{
httpContextAccessor = contextAccessor;
SecurityCodeHelper = captcha;
@ -47,11 +48,9 @@ namespace ZR.Admin.WebApi.Controllers.System
this.permissionService = permissionService;
this.sysConfigService = configService;
roleService = sysRoleService;
this.optionSettings = optionSettings.Value;
this.jwtSettings = jwtSettings.Value;
}
// RSA私钥
private static readonly string PrivatePem = AppSettings.GetConfig("RSA:PrivatePem");
/// <summary>
/// 登录
@ -65,30 +64,21 @@ namespace ZR.Admin.WebApi.Controllers.System
{
if (loginBody == null) { throw new CustomException("请求参数错误"); }
loginBody.LoginIP = HttpContextExtension.GetClientUserIp(HttpContext);
var sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff");
var headers = HttpContext.Request.Headers;
var isRemoteInvoke = headers["Remote-Invoke"].FirstOrDefault().ParseToBool();
if (sysConfig?.ConfigValue != "off" && !SecurityCodeHelper.Validate(loginBody.Uuid, loginBody.Code)
&& !isRemoteInvoke)
SysConfig sysConfig = sysConfigService.GetSysConfigByKey("sys.account.captchaOnOff");
if (sysConfig?.ConfigValue != "off" && !SecurityCodeHelper.Validate(loginBody.Uuid, loginBody.Code))
{
return ToResponse(ResultCode.CAPTCHA_ERROR, "验证码错误");
}
sysLoginService.CheckLockUser(loginBody.Username);
string location = HttpContextExtension.GetIpInfo(loginBody.LoginIP);
// RSA解密
loginBody.Password = EncryptProvider.RSADecryptWithPem(PrivatePem, loginBody.Password);
var user = sysLoginService.Login(loginBody, new SysLogininfor() { LoginLocation = location });
var user = sysLoginService.Login(loginBody, RecordLogInfo(httpContextAccessor.HttpContext));
List<SysRole> roles = roleService.SelectUserRoleListByUserId(user.UserId);
//权限集合 eg *:*:*,system:user:list
List<string> permissions = permissionService.GetMenuPermission(user);
TokenModel loginUser = new(user.Adapt<TokenModel>(), roles.Adapt<List<Roles>>());
LoginUser loginUser = new(user, roles, permissions);
CacheService.SetUserPerms(GlobalConstant.UserPermKEY + user.UserId, permissions);
return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser)));
return SUCCESS(JwtUtil.GenerateJwtToken(JwtUtil.AddClaims(loginUser), jwtSettings.JwtSettings));
}
/// <summary>
@ -128,7 +118,6 @@ namespace ZR.Admin.WebApi.Controllers.System
//权限集合 eg *:*:*,system:user:list
List<string> permissions = permissionService.GetMenuPermission(user);
user.WelcomeContent = GlobalConstant.WelcomeMessages[new Random().Next(0, GlobalConstant.WelcomeMessages.Length)];
return SUCCESS(new { user, roles, permissions });
}
@ -141,7 +130,7 @@ namespace ZR.Admin.WebApi.Controllers.System
public IActionResult GetRouters()
{
long uid = HttpContext.GetUId();
var menus = sysMenuService.SelectMenuTreeByUserId(uid);
var menus = sysMenuService.SelectMenuTreeByUserId(uid);
return SUCCESS(sysMenuService.BuildMenus(menus));
}
@ -163,6 +152,28 @@ namespace ZR.Admin.WebApi.Controllers.System
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>
@ -170,7 +181,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// <returns></returns>
[HttpPost("/register")]
[AllowAnonymous]
[Log(Title = "注册", BusinessType = BusinessType.INSERT)]
[Log(Title = "注册", BusinessType = Infrastructure.Enums.BusinessType.INSERT)]
public IActionResult Register([FromBody] RegisterDto dto)
{
SysConfig config = sysConfigService.GetSysConfigByKey("sys.account.register");
@ -183,7 +194,7 @@ namespace ZR.Admin.WebApi.Controllers.System
{
return ToResponse(ResultCode.CAPTCHA_ERROR, "验证码错误");
}
SysUser user = sysUserService.Register(dto);
if (user.UserId > 0)
{
@ -191,144 +202,5 @@ namespace ZR.Admin.WebApi.Controllers.System
}
return ToResponse(ResultCode.CUSTOM_ERROR, "注册失败,请联系管理员");
}
/// <summary>
/// 发送邮箱验证码
/// </summary>
/// <param name="sendEmailVo"></param>
/// <returns></returns>
[HttpPost("getMailCode")]
public async Task<IActionResult> GetMailCode([FromBody] SendEmailDto sendEmailVo)
{
if (string.IsNullOrEmpty(sendEmailVo.ToUser))
{
return ToResponse(ApiResult.Error($"邮箱不能为空"));
}
if (string.IsNullOrEmpty(optionSettings.MailOptions.FromEmail) || string.IsNullOrEmpty(optionSettings.MailOptions.Password))
{
return ToResponse(ApiResult.Error($"请配置邮箱信息"));
}
if (sendEmailVo.ToUser.Length > 32)
{
// 解密邮箱
sendEmailVo.ToUser = EncryptProvider.RSADecryptWithPem(PrivatePem, sendEmailVo.ToUser);
// 验证邮箱是否存在于系统用户中
var user = await sysUserService.IsAnyAsync(it => it.Email == sendEmailVo.ToUser);
// if (user == null || user.UserId <= 0)
// 不存在则提示错误
if (!user)
{
return ToResponse(ApiResult.Error($"邮箱错误,请联系管理员"));
}
}
else
{
string[] saltKey = await RedisServer.Session.LRangeAsync("saltMail:" + sendEmailVo.ToUser, 0, 1);
if (saltKey.Length > 0)
{
sendEmailVo.ToUser = saltKey[0];
}
else
{
return ToResponse(ApiResult.Error($"邮箱错误,请联系管理员"));
}
}
// 实例化MailHelper以准备发送邮件
MailHelper mailHelper = new();
// 生成6位邮箱验证码
var verifyCode = mailHelper.VerifyCode(6);
// 设置存入Redis的key为 verifyCode: 前缀 + (盐 + 邮箱的MD5值
var key = "verifyCode:" + sendEmailVo.ToUser;
// 存入Redis中设置过期时间为15分钟
var res = await RedisServer.Session.SetAsync(key, verifyCode, 900);
// 发送邮件,主题为 ZRAdmin.NET邮箱验证码内容为验证码
mailHelper.SendMail(sendEmailVo.ToUser, "ZRAdmin.NET邮箱验证码", verifyCode);
return SUCCESS(res);
}
#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
}
}

View File

@ -1,10 +1,9 @@
using Microsoft.AspNetCore.Mvc;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Model.System;
using ZR.Model.System.Dto;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
using ZR.ServiceCore.Services.IService;
namespace ZR.Admin.WebApi.Controllers.System
{
@ -13,7 +12,6 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary>
[Verify]
[Route("/system/menu")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysMenuController : BaseController
{
private readonly ISysRoleService sysRoleService;
@ -115,8 +113,7 @@ namespace ZR.Admin.WebApi.Controllers.System
{
return ToResponse(ApiResult.Error($"修改菜单'{modal.MenuName}'失败,上级菜单不能选择自己"));
}
modal.Update_by = HttpContext.GetUId();
modal.Update_name = HttpContext.GetNickName();
modal.Update_by = HttpContext.GetName();
long result = sysMenuService.EditMenu(modal);
return ToResponse(result);
@ -148,8 +145,7 @@ namespace ZR.Admin.WebApi.Controllers.System
return ToResponse(ApiResult.Error($"新增菜单'{menu.MenuName}'失败地址必须以http(s)://开头"));
}
menu.Create_by = HttpContext.GetUId();
menu.Create_name = HttpContext.GetNickName();
menu.Create_by = HttpContext.GetName();
long result = sysMenuService.AddMenu(menu);
return ToResponse(result);

View File

@ -1,16 +1,14 @@
using System.Net;
using System.Net.Sockets;
using Infrastructure.Constant;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Client;
using NLog;
using SqlSugar;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Admin.WebApi.Hubs;
using ZR.Model;
using ZR.Model.System;
using ZR.Model.System.Dto;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
using ZR.ServiceCore.Services.IService;
using ZR.ServiceCore.Signalr;
namespace ZR.Admin.WebApi.Controllers.System
{
@ -19,23 +17,18 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary>
[Verify]
[Route("system/notice")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysNoticeController : BaseController
{
/// <summary>
/// 通知公告表接口
/// </summary>
private readonly ISysNoticeService _sysNoticeService;
private readonly ISysNoticeService _SysNoticeService;
private readonly IHubContext<MessageHub> _hubContext;
private readonly ISysNoticeLogService _sysNoticeLogService;
private readonly IWebHostEnvironment _webHostEnvironment;
private readonly Logger Logger = LogManager.GetCurrentClassLogger();
public SysNoticeController(ISysNoticeService sysNoticeService, IHubContext<MessageHub> hubContext, ISysNoticeLogService sysNoticeLogService, IWebHostEnvironment webHostEnvironment)
public SysNoticeController(ISysNoticeService SysNoticeService, IHubContext<MessageHub> hubContext)
{
_sysNoticeService = sysNoticeService;
_SysNoticeService = SysNoticeService;
_hubContext = hubContext;
_sysNoticeLogService = sysNoticeLogService;
_webHostEnvironment = webHostEnvironment;
}
/// <summary>
@ -48,7 +41,7 @@ namespace ZR.Admin.WebApi.Controllers.System
var predicate = Expressionable.Create<SysNotice>();
predicate = predicate.And(m => m.Status == 0);
var response = _sysNoticeService.GetPages(predicate.ToExpression(), parm);
var response = _SysNoticeService.GetPages(predicate.ToExpression(), parm);
return SUCCESS(response);
}
@ -60,19 +53,19 @@ namespace ZR.Admin.WebApi.Controllers.System
[ActionPermissionFilter(Permission = "system:notice:list")]
public IActionResult QuerySysNotice([FromQuery] SysNoticeQueryDto parm)
{
PagedInfo<SysNotice> response = _sysNoticeService.GetPageList(parm);
PagedInfo<SysNotice> response = _SysNoticeService.GetPageList(parm);
return SUCCESS(response);
}
/// <summary>
/// 查询通知公告表详情
/// </summary>
/// <param name="noticeId"></param>
/// <param name="NoticeId"></param>
/// <returns></returns>
[HttpGet("{noticeId}")]
public IActionResult GetSysNotice(long noticeId)
[HttpGet("{NoticeId}")]
public IActionResult GetSysNotice(int NoticeId)
{
var response = _sysNoticeService.GetFirst(x => x.NoticeId == noticeId);
var response = _SysNoticeService.GetFirst(x => x.NoticeId == NoticeId);
return SUCCESS(response);
}
@ -84,41 +77,21 @@ namespace ZR.Admin.WebApi.Controllers.System
[HttpPost]
[ActionPermissionFilter(Permission = "system:notice:add")]
[Log(Title = "发布通告", BusinessType = BusinessType.INSERT)]
public async Task<IActionResult> AddSysNotice([FromBody] SysNoticeDto parm)
public IActionResult AddSysNotice([FromBody] SysNoticeDto parm)
{
var modal = parm.Adapt<SysNotice>().ToCreate(HttpContext);
modal.Create_by = HttpContext.GetUId();
modal.Create_name = HttpContext.GetNickName();
modal.Create_time = DateTime.Now;
// int result = _SysNoticeService.Insert(modal, it => new
// {
// it.NoticeTitle,
// it.NoticeType,
// it.NoticeContent,
// it.Status,
// it.Remark,
// it.Create_by,
// it.Create_time
// });
var result = await _sysNoticeService
.Insertable(modal)
.ExecuteReturnSnowflakeIdAsync();
var scheme = HttpContext.Request.Scheme + "://";
var serverIP = HttpContext.Request.Host.Value;
if (_webHostEnvironment.IsProduction())
int result = _SysNoticeService.Insert(modal, it => new
{
var host = await Dns.GetHostEntryAsync(Dns.GetHostName());
var ip = host.AddressList
.FirstOrDefault(it => it.AddressFamily == AddressFamily.InterNetwork);
serverIP = ip + ":" + Request.HttpContext.Connection.LocalPort;//获取服务器IP
}
var url = scheme + serverIP;
var hubConnection = new HubConnectionBuilder().WithUrl(url + "/msghub").Build();
await hubConnection.StartAsync();
await hubConnection.InvokeAsync("SendNoticeToOnlineUsers", result, true);
await hubConnection.StopAsync();
it.NoticeTitle,
it.NoticeType,
it.NoticeContent,
it.Status,
it.Remark,
it.Create_by,
it.Create_time
});
return SUCCESS(result);
}
@ -129,56 +102,37 @@ namespace ZR.Admin.WebApi.Controllers.System
[HttpPut]
[ActionPermissionFilter(Permission = "system:notice:update")]
[Log(Title = "修改公告", BusinessType = BusinessType.UPDATE)]
public async Task<IActionResult> UpdateSysNotice([FromBody] SysNoticeDto parm)
public IActionResult UpdateSysNotice([FromBody] SysNoticeDto parm)
{
var config = new TypeAdapterConfig();
config.ForType<SysNoticeDto, SysNotice>()
.Map(dest => dest.NoticeId, src => src.NoticeId.ParseToLong());
var model = parm.Adapt<SysNotice>(config).ToUpdate(HttpContext);
var nowDate = DateTime.Now;
var response = _sysNoticeService.Update(w => w.NoticeId == model.NoticeId, it => new SysNotice()
var model = parm.Adapt<SysNotice>().ToUpdate(HttpContext);
model.Update_by = HttpContext.GetName();
var response = _SysNoticeService.Update(w => w.NoticeId == model.NoticeId, it => new SysNotice()
{
NoticeTitle = model.NoticeTitle,
NoticeType = model.NoticeType,
NoticeContent = model.NoticeContent,
Status = model.Status,
Remark = model.Remark,
Update_by = HttpContext.GetUId(),
Update_name = HttpContext.GetNickName(),
Update_time = nowDate,
Create_time = nowDate
Update_by = HttpContext.GetName(),
Update_time = DateTime.Now
});
var scheme = HttpContext.Request.Scheme + "://";
var serverIP = HttpContext.Request.Host.Value;
if (_webHostEnvironment.IsProduction())
{
var host = await Dns.GetHostEntryAsync(Dns.GetHostName());
var ip = host.AddressList
.FirstOrDefault(it => it.AddressFamily == AddressFamily.InterNetwork);
serverIP = ip + ":" + Request.HttpContext.Connection.LocalPort;//获取服务器IP
}
var url = scheme + serverIP;
var hubConnection = new HubConnectionBuilder().WithUrl(url + "/msghub").Build();
await hubConnection.StartAsync();
await hubConnection.InvokeAsync("SendNoticeToOnlineUsers", model.NoticeId, true);
await hubConnection.StopAsync();
return SUCCESS(response);
}
/// <summary>
/// 发送通知公告表
/// </summary>
/// <returns></returns>
[HttpPut("send/{noticeId}")]
[HttpPut("send/{NoticeId}")]
[ActionPermissionFilter(Permission = "system:notice:update")]
[Log(Title = "发送通知公告", BusinessType = BusinessType.OTHER)]
public IActionResult SendNotice(long noticeId = 0)
public IActionResult SendNotice(int NoticeId = 0)
{
if (noticeId <= 0)
if (NoticeId <= 0)
{
throw new CustomException("请求实体不能为空");
}
var response = _sysNoticeService.GetFirst(x => x.NoticeId == noticeId);
var response = _SysNoticeService.GetFirst(x => x.NoticeId == NoticeId);
if (response != null && response.Status == 0)
{
_hubContext.Clients.All.SendAsync(HubsConstant.ReceiveNotice, response.NoticeTitle, response.NoticeContent);
@ -193,30 +147,13 @@ namespace ZR.Admin.WebApi.Controllers.System
[HttpDelete("{ids}")]
[ActionPermissionFilter(Permission = "system:notice:delete")]
[Log(Title = "通知公告", BusinessType = BusinessType.DELETE)]
public async Task<IActionResult> DeleteSysNotice(string ids)
public IActionResult DeleteSysNotice(string ids)
{
var idsArr = Tools.SpitLongArrary(ids);
int[] idsArr = Tools.SpitIntArrary(ids);
if (idsArr.Length <= 0) { return ToResponse(ApiResult.Error($"删除失败Id 不能为空")); }
var response = _sysNoticeService.Delete(idsArr);
await _sysNoticeLogService.Deleteable()
.Where(it => idsArr.Contains(it.NoticeId))
.ExecuteCommandAsync();
var scheme = HttpContext.Request.Scheme + "://";
var serverIP = HttpContext.Request.Host.Value;
if (_webHostEnvironment.IsProduction())
{
var host = await Dns.GetHostEntryAsync(Dns.GetHostName());
var ip = host.AddressList
.FirstOrDefault(it => it.AddressFamily == AddressFamily.InterNetwork);
serverIP = ip + ":" + Request.HttpContext.Connection.LocalPort;//获取服务器IP
}
var url = scheme + serverIP;
var hubConnection = new HubConnectionBuilder().WithUrl(url + "/msghub").Build();
await hubConnection.StartAsync();
await hubConnection.InvokeAsync("SendNoticeToOnlineUsers", null, false);
await hubConnection.StopAsync();
var response = _SysNoticeService.Delete(idsArr);
return SUCCESS(response);
}
}

View File

@ -1,10 +1,11 @@
using Microsoft.AspNetCore.Mvc;
using Infrastructure.Extensions;
using Microsoft.AspNetCore.Mvc;
using SqlSugar;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Model;
using ZR.Model.System;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
namespace ZR.Admin.WebApi.Controllers.System
{
@ -13,7 +14,6 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary>
[Verify]
[Route("system/post")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysPostController : BaseController
{
private readonly ISysPostService PostService;
@ -68,7 +68,7 @@ namespace ZR.Admin.WebApi.Controllers.System
throw new CustomException($"修改岗位{post.PostName}失败,岗位编码已存在");
}
post.ToCreate(HttpContext);
return ToResponse(PostService.Add(post));
}
@ -122,7 +122,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// 岗位导出
/// </summary>
/// <returns></returns>
[Log(BusinessType = BusinessType.EXPORT, IsSaveResponseData = false, Title = "岗位导出")]
[Log(BusinessType = BusinessType.EXPORT, IsSaveResponseData = false, Title= "岗位导出")]
[HttpGet("export")]
[ActionPermissionFilter(Permission = "system:post:export")]
public IActionResult Export()

View File

@ -1,9 +1,9 @@
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Mvc;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Model.System;
using ZR.Model.System.Dto;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
using ZR.ServiceCore.Services.IService;
namespace ZR.Admin.WebApi.Controllers.System
{
@ -12,7 +12,6 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary>
[Verify]
[Route("system/user/profile")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysProfileController : BaseController
{
private readonly ISysUserService UserService;
@ -21,7 +20,6 @@ namespace ZR.Admin.WebApi.Controllers.System
private readonly ISysDeptService DeptService;
private readonly ISysFileService FileService;
private IWebHostEnvironment hostEnvironment;
private readonly OptionsSetting _optionsSetting;
public SysProfileController(
ISysUserService userService,
@ -29,8 +27,7 @@ namespace ZR.Admin.WebApi.Controllers.System
ISysUserPostService postService,
ISysDeptService deptService,
ISysFileService sysFileService,
IWebHostEnvironment hostEnvironment,
IOptions<OptionsSetting> options)
IWebHostEnvironment hostEnvironment)
{
UserService = userService;
RoleService = roleService;
@ -38,7 +35,6 @@ namespace ZR.Admin.WebApi.Controllers.System
DeptService = deptService;
FileService = sysFileService;
this.hostEnvironment = hostEnvironment;
_optionsSetting = options.Value;
}
/// <summary>
@ -122,10 +118,8 @@ namespace ZR.Admin.WebApi.Controllers.System
{
long userId = HttpContext.GetUId();
if (formFile == null) throw new CustomException("请选择文件");
var file = await FileService.SaveFileToLocal(hostEnvironment.WebRootPath, "", "avatar",
HttpContext.GetUId(), HttpContext.GetName(), formFile, _optionsSetting.Upload.UploadUrl);
SysFile file = await FileService.SaveFileToLocal(hostEnvironment.WebRootPath, "", "avatar", HttpContext.GetName(), formFile);
UserService.UpdatePhoto(new SysUser() { Avatar = file.AccessUrl, UserId = userId });
return SUCCESS(new { imgUrl = file.AccessUrl });

View File

@ -1,11 +1,10 @@
using Microsoft.AspNetCore.Mvc;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Model;
using ZR.Model.System;
using ZR.Model.System.Dto;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
using ZR.ServiceCore.Services.IService;
namespace ZR.Admin.WebApi.Controllers.System
{
@ -14,7 +13,6 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary>
[Verify]
[Route("system/role")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysRoleController : BaseController
{
private readonly ISysRoleService sysRoleService;
@ -72,8 +70,7 @@ namespace ZR.Admin.WebApi.Controllers.System
return ToResponse(ApiResult.Error((int)ResultCode.CUSTOM_ERROR, $"新增角色'{sysRoleDto.RoleName}'失败,角色权限已存在"));
}
sysRoleDto.Create_by = HttpContext.GetUId();
sysRoleDto.Create_name = HttpContext.GetNickName();
sysRoleDto.Create_by = HttpContext.GetName();
long roleId = sysRoleService.InsertRole(sysRoleDto);
return ToResponse(roleId);
@ -104,8 +101,7 @@ namespace ZR.Admin.WebApi.Controllers.System
return ToResponse(ApiResult.Error($"编辑角色'{sysRoleDto.RoleName}'失败,角色权限已存在"));
}
}
sysRoleDto.Update_by = HttpContext.GetUId();
sysRoleDto.Update_name = HttpContext.GetNickName();
sysRoleDto.Update_by = HttpContext.GetName();
int upResult = sysRoleService.UpdateRole(sysRoleDto);
if (upResult > 0)
{
@ -126,8 +122,7 @@ namespace ZR.Admin.WebApi.Controllers.System
{
if (sysRoleDto == null || sysRoleDto.RoleId <= 0) return ToResponse(ApiResult.Error(101, "请求参数错误"));
SysRole sysRole = sysRoleDto.Adapt<SysRole>();
sysRoleDto.Create_by = HttpContext.GetUId();
sysRoleDto.Create_name = HttpContext.GetNickName();
sysRoleDto.Create_by = HttpContext.GetName();
sysRoleService.CheckRoleAllowed(sysRole);
bool result = sysRoleService.AuthDataScope(sysRoleDto);

View File

@ -1,12 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using MiniExcelLibs;
using SqlSugar;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Model;
using ZR.Model.System;
using ZR.Model.System.Dto;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
namespace ZR.Admin.WebApi.Controllers.System
{
@ -15,7 +15,6 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary>
[Verify]
[Route("system/user")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysUserController : BaseController
{
private readonly ISysUserService UserService;
@ -93,12 +92,10 @@ namespace ZR.Admin.WebApi.Controllers.System
return ToResponse(ApiResult.Error($"新增用户 '{user.UserName}'失败,登录账号已存在"));
}
user.Create_by = HttpContext.GetUId();
user.Create_name = HttpContext.GetNickName();
user.Create_time = DateTime.Now;
user.Create_by = HttpContext.GetName();
user.Password = NETCore.Encrypt.EncryptProvider.Md5(user.Password);
return SUCCESS(UserService.InsertUser(user));
return ToResponse(UserService.InsertUser(user));
}
/// <summary>
@ -113,8 +110,7 @@ namespace ZR.Admin.WebApi.Controllers.System
{
if (user == null || user.UserId <= 0) { return ToResponse(ApiResult.Error(101, "请求参数错误")); }
user.Update_by = HttpContext.GetUId();
user.Update_name = HttpContext.GetNickName();
user.Update_by = HttpContext.GetName();
int upResult = UserService.UpdateUser(user);
return ToResponse(upResult);
@ -147,7 +143,7 @@ namespace ZR.Admin.WebApi.Controllers.System
public IActionResult Remove(int userid = 0)
{
if (userid <= 0) { return ToResponse(ApiResult.Error(101, "请求参数错误")); }
if (userid == 1) return ToResponse(ResultCode.FAIL, "不能删除管理员账号");
if (userid == 1) return ToResponse(Infrastructure.ResultCode.FAIL, "不能删除管理员账号");
int result = UserService.DeleteUser(userid);
return ToResponse(result);
@ -160,7 +156,7 @@ namespace ZR.Admin.WebApi.Controllers.System
[HttpPut("resetPwd")]
[Log(Title = "重置密码", BusinessType = BusinessType.UPDATE)]
[ActionPermissionFilter(Permission = "system:user:resetPwd")]
public IActionResult ResetPwd([FromBody] SysUserDto sysUser)
public IActionResult ResetPwd([FromBody] SysUser sysUser)
{
//密码md5
sysUser.Password = NETCore.Encrypt.EncryptProvider.Md5(sysUser.Password);

View File

@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using ZR.Admin.WebApi.Filters;
using ZR.Model.System.Dto;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model.Dto;
namespace ZR.Admin.WebApi.Controllers.System
{
@ -10,7 +10,6 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary>
[Verify]
[Route("system/userRole")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysUserRoleController : BaseController
{
private readonly ISysUserRoleService SysUserRoleService;
@ -44,7 +43,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// <returns></returns>
[HttpPost("create")]
[ActionPermissionFilter(Permission = "system:roleusers:add")]
[Log(Title = "添加角色用户", BusinessType = BusinessType.INSERT)]
[Log(Title = "添加角色用户", BusinessType = Infrastructure.Enums.BusinessType.INSERT)]
public IActionResult Create([FromBody] RoleUsersCreateDto roleUsersCreateDto)
{
var response = SysUserRoleService.InsertRoleUser(roleUsersCreateDto);
@ -59,7 +58,7 @@ namespace ZR.Admin.WebApi.Controllers.System
/// <returns></returns>
[HttpPost("delete")]
[ActionPermissionFilter(Permission = "system:roleusers:remove")]
[Log(Title = "删除角色用户", BusinessType = BusinessType.DELETE)]
[Log(Title = "删除角色用户", BusinessType = Infrastructure.Enums.BusinessType.DELETE)]
public IActionResult Delete([FromBody] RoleUsersCreateDto roleUsersCreateDto)
{
return SUCCESS(SysUserRoleService.DeleteRoleUserByUserIds(roleUsersCreateDto.RoleId, roleUsersCreateDto.UserIds));

View File

@ -1,11 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using Infrastructure.Extensions;
using Microsoft.AspNetCore.Mvc;
using Quartz;
using SqlSugar;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Model.System;
using ZR.Model.System.Dto;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
using ZR.Tasks;
namespace ZR.Admin.WebApi.Controllers
@ -15,7 +16,6 @@ namespace ZR.Admin.WebApi.Controllers
/// </summary>
[Verify]
[Route("system/Tasks")]
[ApiExplorerSettings(GroupName = "sys")]
public class TasksController : BaseController
{
private ISysTasksQzService _tasksQzService;
@ -87,9 +87,8 @@ namespace ZR.Admin.WebApi.Controllers
throw new CustomException($"程序集或者类名不能为空");
}
//从 Dto 映射到 实体
var tasksQz = parm.Adapt<SysTasks>().ToCreate(HttpContext);
tasksQz.Create_by = HttpContext.GetUId();
tasksQz.Create_name = HttpContext.GetNickName();
var tasksQz = parm.Adapt<SysTasks>().ToCreate();
tasksQz.Create_by = HttpContext.GetName();
tasksQz.ID = SnowFlakeSingle.Instance.NextId().ToString();
return SUCCESS(_tasksQzService.AddTasks(tasksQz));
@ -128,8 +127,7 @@ namespace ZR.Admin.WebApi.Controllers
throw new CustomException($"该任务正在运行中,请先停止在更新");
}
var model = parm.Adapt<SysTasks>();
model.Update_by = HttpContext.GetUId();
model.Update_name = HttpContext.GetNickName();
model.Update_by = HttpContextExtension.GetName(HttpContext);
int response = _tasksQzService.UpdateTasks(model);
if (response > 0)
{
@ -161,7 +159,7 @@ namespace ZR.Admin.WebApi.Controllers
var tasksQz = _tasksQzService.GetFirst(m => m.ID == id);
var taskResult = await _schedulerServer.DeleteTaskScheduleAsync(tasksQz);
if (taskResult.IsSuccess())
if (taskResult.Code == 200)
{
_tasksQzService.Delete(id);
}
@ -190,7 +188,7 @@ namespace ZR.Admin.WebApi.Controllers
var tasksQz = _tasksQzService.GetFirst(m => m.ID == id);
var taskResult = await _schedulerServer.AddTaskScheduleAsync(tasksQz);
if (taskResult.IsSuccess())
if (taskResult.Code == 200)
{
tasksQz.IsStart = 1;
_tasksQzService.Update(tasksQz);
@ -221,7 +219,7 @@ namespace ZR.Admin.WebApi.Controllers
var tasksQz = _tasksQzService.GetFirst(m => m.ID == id);
var taskResult = await _schedulerServer.DeleteTaskScheduleAsync(tasksQz);//await _schedulerServer.PauseTaskScheduleAsync(tasksQz);
if (taskResult.IsSuccess())
if (taskResult.Code == 200)
{
tasksQz.IsStart = 0;
_tasksQzService.Update(tasksQz);

View File

@ -1,11 +1,15 @@
using Microsoft.AspNetCore.Mvc;
using Infrastructure;
using Infrastructure.Attribute;
using Infrastructure.Enums;
using Infrastructure.Extensions;
using Microsoft.AspNetCore.Mvc;
using SqlSugar;
using ZR.Admin.WebApi.Filters;
using ZR.Common;
using ZR.Model;
using ZR.Model.System;
using ZR.Model.System.Dto;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Dto;
namespace ZR.Admin.WebApi.Controllers.System
{
@ -14,7 +18,6 @@ namespace ZR.Admin.WebApi.Controllers.System
/// </summary>
[Verify]
[Route("/monitor/jobLog")]
[ApiExplorerSettings(GroupName = "sys")]
public class TasksLogController : BaseController
{
private readonly ISysTasksLogService tasksLogService;

View File

@ -1,195 +0,0 @@
using AspNetCoreRateLimit;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using ZR.Admin.WebApi.Filters;
using ZR.Model;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Services.IService;
using IpRateLimitPolicy = ZR.ServiceCore.Model.IpRateLimitPolicy;
namespace ZR.Admin.WebApi.Controllers.System.monitor;
[Verify]
[Route("ip/route/limit")]
public class IpRateLimitController : BaseController
{
private readonly IpRateLimitOptions _options;
private readonly IIpPolicyStore _ipPolicyStore;
private readonly IIpRateLimitPolicyService _ipRateLimitPolicyService;
private readonly IRateLimitRuleService _rateLimitRuleService;
private readonly IIpRateLimitLogService _ipRateLimitLogService;
public IpRateLimitController(IOptions<IpRateLimitOptions> optionsAccessor, IIpPolicyStore ipPolicyStore,
IIpRateLimitPolicyService ipRateLimitPolicyService, IRateLimitRuleService rateLimitRuleService,
IIpRateLimitLogService ipRateLimitLogService)
{
_options = optionsAccessor.Value;
_ipPolicyStore = ipPolicyStore;
_ipRateLimitPolicyService = ipRateLimitPolicyService;
_rateLimitRuleService = rateLimitRuleService;
_ipRateLimitLogService = ipRateLimitLogService;
}
/// <summary>
/// 获取限制规则
/// </summary>
/// <returns></returns>
[HttpGet("getIpRateLimitPolicyPage")]
public async Task<IActionResult> GetIpRateLimitPolicyPage([FromQuery] IpRateLimitPolicy ipRateLimitPolicy,
PagerInfo pager)
{
var page = await _ipRateLimitPolicyService.SelectIpRateLimitPolicyPageAsync(ipRateLimitPolicy, pager);
return SUCCESS(page);
}
[HttpPost("addIpRateLimitPolicy")]
public async Task<IActionResult> AddIpRateLimitPolicy([FromBody] IpRateLimitPolicy ipRateLimitPolicy)
{
var isExist = await _ipRateLimitPolicyService.Queryable()
.Where(it => it.Ip == ipRateLimitPolicy.Ip)
.AnyAsync();
if (isExist) throw new CustomException("该IP已存在");
var res = await _ipRateLimitPolicyService.InsertNav(ipRateLimitPolicy)
.Include(it => it.Rules)
.ExecuteCommandAsync();
return SUCCESS(res);
}
[HttpPut("updateIpRateLimitPolicy")]
public async Task<IActionResult> UpdateIpRateLimitPolicy(
[FromBody] IpRateLimitPolicy ipRateLimitPolicy)
{
var isEnable = await _ipRateLimitPolicyService
.Queryable()
.Where(it => it.Id == ipRateLimitPolicy.Id
&& it.Flag == '1')
.AnyAsync();
var res = await _ipRateLimitPolicyService.UpdateNav(ipRateLimitPolicy)
.Include(it => it.Rules)
.ExecuteCommandAsync();
if (!isEnable) return SUCCESS(res);
{
await _ipPolicyStore.RemoveAsync(_options.IpPolicyPrefix);
var ipRateLimitPolicies = _ipRateLimitPolicyService.Queryable()
.Includes(it => it.Rules.Where(r => r.Flag == '1').ToList())
.Where(it => it.Flag == '1')
.ToListAsync()
.GetAwaiter().GetResult()
.Adapt<List<AspNetCoreRateLimit.IpRateLimitPolicy>>();
await _ipPolicyStore.SeedAsync();
var pol = await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
pol.IpRules.AddRange(ipRateLimitPolicies);
await _ipPolicyStore.SetAsync(_options.IpPolicyPrefix, pol);
}
return SUCCESS(res);
}
[HttpPatch("enableIpRateLimitPolicy/{id}")]
public async Task<IActionResult> EnableIpRateLimitPolicy([FromRoute] long id)
{
var isEnable = await _ipRateLimitPolicyService
.Queryable()
.Where(it => it.Id == id
&& it.Flag == '1')
.AnyAsync();
if (isEnable)
{
throw new CustomException("已启用,无法再次启用");
}
await _ipRateLimitPolicyService.Updateable(new IpRateLimitPolicy
{
Id = id,
Flag = '1'
}).UpdateColumns(it => it.Flag)
.ExecuteCommandAsync();
var ipRateLimitPolicy = await _ipRateLimitPolicyService.Queryable()
.Includes(it => it.Rules.Where(r => r.Flag == '1').ToList())
.Where(it => it.Id == id)
.SingleAsync();
var pol = await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
pol.IpRules.Add(ipRateLimitPolicy.Adapt<AspNetCoreRateLimit.IpRateLimitPolicy>());
await _ipPolicyStore.SetAsync(_options.IpPolicyPrefix, pol);
return SUCCESS('1');
}
[HttpPatch("disableIpRateLimitPolicy/{id}")]
public async Task<IActionResult> DisableIpRateLimitPolicy([FromRoute] long id)
{
var isEnable = await _ipRateLimitPolicyService
.Queryable()
.Where(it => it.Id == id
&& it.Flag == '1')
.AnyAsync();
if (!isEnable)
{
throw new CustomException("已禁用,无法再次禁用");
}
await _ipRateLimitPolicyService.Updateable(new IpRateLimitPolicy
{
Id = id,
Flag = '0'
}).UpdateColumns(it => it.Flag)
.ExecuteCommandAsync();
var ipRateLimitPolicies = _ipRateLimitPolicyService.Queryable()
.Includes(it => it.Rules.Where(r => r.Flag == '1').ToList())
.Where(it => it.Flag == '1')
.ToListAsync()
.GetAwaiter().GetResult()
.Adapt<List<AspNetCoreRateLimit.IpRateLimitPolicy>>();
await _ipPolicyStore.RemoveAsync(_options.IpPolicyPrefix);
await _ipPolicyStore.SeedAsync();
var pol = await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
pol.IpRules.AddRange(ipRateLimitPolicies);
await _ipPolicyStore.SetAsync(_options.IpPolicyPrefix, pol);
return SUCCESS('0');
}
[HttpDelete("deleteIpRateLimitPolicy/{id}")]
public async Task DeleteIpRateLimitPolicyAsync([FromRoute] long id)
{
var isEnable = await _ipRateLimitPolicyService
.Queryable()
.Where(it => it.Id == id
&& it.Flag == '1')
.AnyAsync();
await _ipRateLimitPolicyService
.DeleteNav(it => it.Id == id)
.Include(it => it.Rules)
.ExecuteCommandAsync();
if (isEnable)
{
var ipRateLimitPolicies = _ipRateLimitPolicyService.Queryable()
.Includes(it => it.Rules.Where(r => r.Flag == '1').ToList())
.Where(it => it.Flag == '1')
.ToListAsync()
.GetAwaiter().GetResult()
.Adapt<List<AspNetCoreRateLimit.IpRateLimitPolicy>>();
await _ipPolicyStore.RemoveAsync(_options.IpPolicyPrefix);
await _ipPolicyStore.SeedAsync();
var pol = await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
pol.IpRules.AddRange(ipRateLimitPolicies);
await _ipPolicyStore.SetAsync(_options.IpPolicyPrefix, pol);
}
}
[HttpGet("getIpRateLimitLogPage")]
public async Task<IActionResult> GetIpRateLimitLogPage([FromQuery] IpRateLimitLog ipRateLimitLog, PagerInfo pager)
{
return SUCCESS(await _ipRateLimitLogService.SelectIpRateLimitLogPageAsync(ipRateLimitLog, pager));
}
[HttpGet("get")]
public async Task<IpRateLimitPolicies> Get()
{
return await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
}
[HttpGet("checkIp/{ip}")]
public async Task<IActionResult> CheckIp(string ip)
{
var res = await _ipRateLimitPolicyService.Queryable()
.Where(it => it.Ip == ip)
.AnyAsync();
return SUCCESS(res);
}
}

View File

@ -1,9 +1,12 @@
using Infrastructure;
using Infrastructure.Extensions;
using Infrastructure.Model;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using System;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace ZR.Admin.WebApi.Controllers.monitor
@ -11,7 +14,6 @@ namespace ZR.Admin.WebApi.Controllers.monitor
/// <summary>
/// 系统监控
/// </summary>
[ApiExplorerSettings(GroupName = "sys")]
public class MonitorController : BaseController
{
private OptionsSetting Options;
@ -50,12 +52,8 @@ namespace ZR.Admin.WebApi.Controllers.monitor
string appRAM = ((double)Process.GetCurrentProcess().WorkingSet64 / 1048576).ToString("N2") + " MB";
string startTime = Process.GetCurrentProcess().StartTime.ToString("yyyy-MM-dd HH:mm:ss");
string sysRunTime = ComputerHelper.GetRunTime();
// string serverIP = Request.HttpContext.Connection.LocalIpAddress.MapToIPv4().ToString() + ":" + Request.HttpContext.Connection.LocalPort;//获取服务器IP
var host = Dns.GetHostEntry(Dns.GetHostName());
var ip = host.AddressList
.FirstOrDefault(it => it.AddressFamily == AddressFamily.InterNetwork);
var serverIP = ip + ":" + Request.HttpContext.Connection.LocalPort;//获取服务器IP
string serverIP = Request.HttpContext.Connection.LocalIpAddress.MapToIPv4().ToString() + ":" + Request.HttpContext.Connection.LocalPort;//获取服务器IP
var programStartTime = Process.GetCurrentProcess().StartTime;
string programRunTime = DateTimeHelper.FormatTime((DateTime.Now - programStartTime).TotalMilliseconds.ToString().Split('.')[0].ParseToLong());
var data = new

View File

@ -1,77 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using ZR.Admin.WebApi.Filters;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model.Dto;
//创建时间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);
}
}
}

View File

@ -5,7 +5,6 @@ using ZR.Admin.WebApi.Filters;
using ZR.Model;
using ZR.Model.System;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
namespace ZR.Admin.WebApi.Controllers.monitor
{
@ -14,7 +13,6 @@ namespace ZR.Admin.WebApi.Controllers.monitor
/// </summary>
[Verify]
[Route("/monitor/logininfor")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysLogininforController : BaseController
{
private ISysLoginService sysLoginService;

View File

@ -1,8 +1,8 @@
using Microsoft.AspNetCore.Mvc;
using ZR.Admin.WebApi.Extensions;
using ZR.Admin.WebApi.Filters;
using ZR.Model.System.Dto;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model.Dto;
namespace ZR.Admin.WebApi.Controllers.monitor
{
@ -11,7 +11,6 @@ namespace ZR.Admin.WebApi.Controllers.monitor
/// </summary>
[Verify]
[Route("/monitor/operlog")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysOperlogController : BaseController
{
private ISysOperLogService sysOperLogService;
@ -66,7 +65,7 @@ namespace ZR.Admin.WebApi.Controllers.monitor
{
if (!HttpContextExtension.IsAdmin(HttpContext))
{
return ToResponse(ResultCode.CUSTOM_ERROR,"操作失败");
return ToResponse(Infrastructure.ResultCode.CUSTOM_ERROR,"操作失败");
}
sysOperLogService.CleanOperLog();

View File

@ -1,23 +1,18 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using ZR.Admin.WebApi.Filters;
using ZR.Admin.WebApi.Hubs;
using ZR.Model;
using ZR.ServiceCore.Model.Dto;
using ZR.ServiceCore.Signalr;
namespace ZR.Admin.WebApi.Controllers.monitor
{
/// <summary>
/// 在线用户
/// </summary>
[Verify]
[Route("monitor/online")]
[ApiExplorerSettings(GroupName = "sys")]
public class SysUserOnlineController : BaseController
{
private readonly IHubContext<MessageHub> HubContext;
private IHubContext<Hub> HubContext;
public SysUserOnlineController(IHubContext<MessageHub> hubContext)
public SysUserOnlineController(IHubContext<Hub> hubContext)
{
HubContext = hubContext;
}
@ -30,47 +25,11 @@ namespace ZR.Admin.WebApi.Controllers.monitor
[HttpGet("list")]
public IActionResult Index([FromQuery] PagerInfo parm)
{
var result = MessageHub.OnlineClients
var result = MessageHub.clientUsers
.OrderByDescending(f => f.LoginTime)
.Skip(parm.PageNum - 1).Take(parm.PageSize);
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);
return SUCCESS(new { result, totalNum = MessageHub.clientUsers.Count });
}
}
}

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using Infrastructure.Extensions;
using Microsoft.AspNetCore.Mvc;
using System.Web;
namespace ZR.Admin.WebApi.Controllers
@ -10,6 +11,9 @@ namespace ZR.Admin.WebApi.Controllers
[AllowAnonymous]
public class WxOpenController : BaseController
{
private NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
public WxOpenController() { }
/// <summary>
/// 获取签名
/// </summary>

View File

@ -1,34 +1,21 @@
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
#创建 /app文件夹
WORKDIR /app
#创建挂载目录,用于将程序部署在服务器本地
#VOLUME /app
#设置docker容器对外暴露端口
EXPOSE 8888
#EXPOSE 443
VOLUME /app/adminlogs
VOLUME /app/wwwroot
VOLUME /app/logs
#COPY bin/Release/net5.0/publish/ app/
COPY . app/
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["ZR.Admin.WebApi/ZR.Admin.WebApi.csproj", "ZR.Admin.WebApi/"]
COPY ["ZR.CodeGenerator/ZR.CodeGenerator.csproj", "ZR.CodeGenerator/"]
COPY ["Infrastructure/ZR.Infrastructure.csproj", "Infrastructure/"]
COPY ["ZR.Common/ZR.Common.csproj", "ZR.Common/"]
COPY ["ZR.ServiceCore/ZR.ServiceCore.csproj", "ZR.ServiceCore/"]
COPY ["ZR.Repository/ZR.Repository.csproj", "ZR.Repository/"]
COPY ["ZR.Model/ZR.Model.csproj", "ZR.Model/"]
COPY ["ZR.Service/ZR.Service.csproj", "ZR.Service/"]
COPY ["ZR.Tasks/ZR.Tasks.csproj", "ZR.Tasks/"]
COPY ["ZR.Admin.Grpc/ZR.Admin.Grpc.csproj", "ZR.Admin.Grpc/"]
RUN dotnet restore "ZR.Admin.WebApi/ZR.Admin.WebApi.csproj"
COPY . .
WORKDIR "/src/ZR.Admin.WebApi"
RUN dotnet build "ZR.Admin.WebApi.csproj" -c Release -o /app/build
#设置容器内的时区如果不设置默认时区是标准时间比北京时间晚8个小时
RUN echo "Asia/shanghai" > /etc/timezone
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
FROM build AS publish
RUN dotnet publish "ZR.Admin.WebApi.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
# 复制发布文件到工作目录
#COPY . app/
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "ZR.Admin.WebApi.dll"]
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN apt-get update && apt-get install -y libfontconfig1
#等价于 dotnet ZR.Admin.WebApi.dll如果不指定启动端口默认在docker里面启动端口是80端口
ENTRYPOINT ["dotnet", "ZR.Admin.WebApi.dll", "--server.urls","http://*:8888"]

View File

@ -1,10 +1,8 @@
using Infrastructure.Attribute;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
using Infrastructure;
using Infrastructure.Attribute;
using System.Reflection;
namespace Infrastructure
namespace ZR.Admin.WebApi.Extensions
{
/// <summary>
/// App服务注册

View File

@ -1,12 +1,5 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace Infrastructure
namespace ZR.Admin.WebApi.Extensions
{
/// <summary>
/// 跨域扩展
/// </summary>
public static class CorsExtension
{
/// <summary>

View File

@ -1,9 +1,10 @@
using Infrastructure;
using SqlSugar.IOC;
using SqlSugar;
using ZR.Admin.WebApi.Framework;
using ZR.Model.System;
using ZR.ServiceCore.Model;
namespace ZR.ServiceCore.SqlSugar
namespace ZR.Admin.WebApi.Extensions
{
public enum DataPermiEnum
{

View File

@ -1,15 +1,16 @@
using Infrastructure;
using Infrastructure.Model;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Infrastructure;
using Infrastructure.Extensions;
using SqlSugar;
using SqlSugar.IOC;
using ZR.Infrastructure.Cache;
using ZR.ServiceCore.Model;
using ZR.Model;
using ZR.Model.System;
namespace ZR.ServiceCore.SqlSugar
namespace ZR.Admin.WebApi.Extensions
{
public static class SqlSugarSetup
/// <summary>
/// sqlsugar 数据处理
/// </summary>
public static class DbExtension
{
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
@ -17,11 +18,11 @@ namespace ZR.ServiceCore.SqlSugar
/// 初始化db
/// </summary>
/// <param name="services"></param>
/// <param name="Configuration"></param>
/// <param name="environment"></param>
public static void AddDb(this IServiceCollection services,IWebHostEnvironment environment)
public static void AddDb(this IServiceCollection services, IConfiguration Configuration, IWebHostEnvironment environment)
{
var options = App.OptionsSetting;
List<DbConfigs> dbConfigs = options.DbConfigs;
List<DbConfigs> dbConfigs = Configuration.GetSection("DbConfigs").Get<List<DbConfigs>>();
var iocList = new List<IocConfig>();
foreach (var item in dbConfigs)
@ -52,19 +53,10 @@ namespace ZR.ServiceCore.SqlSugar
});
});
if (options.InitDb && environment.IsDevelopment())
if (Configuration["InitDb"].ParseToBool() == true && environment.IsDevelopment())
{
InitTable.InitDb();
}
StaticConfig.CacheRemoveByLikeStringFunc = (service, key) =>
{
var keyList = RedisServer.Cache.Keys("cache:SqlSugarDataCache" + key + "*");
foreach (var item in keyList)
{
RedisServer.Cache.Del(item.Split("cache:")[1]);
}
};
}
/// <summary>
@ -76,30 +68,27 @@ namespace ZR.ServiceCore.SqlSugar
private static void SetSugarAop(SqlSugarClient db, IocConfig iocConfig, ICacheService cache)
{
var config = db.GetConnectionScope(iocConfig.ConfigId).CurrentConnectionConfig;
var showDbLog = AppSettings.Get<bool>("ShowDbLog");
string configId = config.ConfigId;
db.GetConnectionScope(configId).Aop.OnLogExecuting = (sql, pars) =>
{
if (showDbLog)
string log = $"【db{configId} SQL语句】{UtilMethods.GetSqlString(config.DbType, sql, pars)}\n";
if (sql.TrimStart().StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
{
string log = $"【db{configId} SQL】{UtilMethods.GetSqlString(config.DbType, sql, pars)}\n";
if (sql.TrimStart().StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
{
logger.Info(log);
}
else if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
{
logger.Warn(log);
}
else if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("TRUNCATE", StringComparison.OrdinalIgnoreCase))
{
logger.Error(log);
}
else
{
log = $"【db{configId} SQL语句】dbo.{sql} {string.Join(", ", pars.Select(x => x.ParameterName + " = " + GetParsValue(x)))};\n";
logger.Info(log);
}
logger.Info(log);
}
else if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("INSERT", StringComparison.OrdinalIgnoreCase))
{
logger.Warn(log);
}
else if (sql.StartsWith("DELETE", StringComparison.OrdinalIgnoreCase) || sql.StartsWith("TRUNCATE", StringComparison.OrdinalIgnoreCase))
{
logger.Error(log);
}
else
{
log = $"【db{configId} SQL语句】dbo.{sql} {string.Join(", ", pars.Select(x => x.ParameterName + " = " + GetParsValue(x)))};\n";
logger.Info(log);
}
};
db.GetConnectionScope(configId).Aop.OnError = (ex) =>
@ -124,33 +113,29 @@ namespace ZR.ServiceCore.SqlSugar
var data = it.BusinessData;//这边会显示你传进来的对象
var time = it.Time;
var diffType = it.DiffType;//enum insert 、update and delete
string name = App.UserName;
foreach (var item in editBeforeData)
if (diffType == DiffType.delete)
{
var pars = db.Utilities.SerializeObject(item.Columns.ToDictionary(it => it.ColumnName, it => it.Value));
string name = App.UserName;
SqlDiffLog log = new()
foreach (var item in editBeforeData)
{
BeforeData = pars,
BusinessData = data?.ToString(),
DiffType = diffType.ToString(),
Sql = sql,
TableName = item.TableName,
UserName = name,
AddTime = DateTime.Now,
ConfigId = configId
};
if (editAfterData != null)
{
var afterData = editAfterData?.First(x => x.TableName == item.TableName);
var afterPars = db.Utilities.SerializeObject(afterData?.Columns.ToDictionary(it => it.ColumnName, it => it.Value));
log.AfterData = afterPars;
var pars = db.Utilities.SerializeObject(item.Columns.ToDictionary(it => it.ColumnName, it => it.Value));
SqlDiffLog log = new()
{
BeforeData = pars,
BusinessData = data?.ToString(),
DiffType = diffType.ToString(),
Sql = sql,
TableName = item.TableName,
UserName = name,
AddTime = DateTime.Now,
ConfigId = configId
};
//logger.WithProperty("title", data).Info(pars);
db.GetConnectionScope(0).Insertable(log).ExecuteReturnSnowflakeId();
}
//logger.WithProperty("title", data).Info(pars);
db.GetConnectionScope(0)
.Insertable(log)
.ExecuteReturnSnowflakeId();
}
};
db.GetConnectionScope(configId).CurrentConnectionConfig.MoreSettings = new ConnMoreSettings()

View File

@ -1,25 +1,20 @@

using Infrastructure.Extensions;
using Microsoft.AspNetCore.Http;
using System;
using System.Reflection;
namespace Infrastructure
namespace ZR.Admin.WebApi.Extensions
{
public static class EntityExtension
{
public static TSource ToCreate<TSource>(this TSource source, HttpContext? context = null)
{
var types = source?.GetType();
if (types == null || context == null) return source;
if (types == null) return source;
BindingFlags flag = BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance;
types.GetProperty("CreateTime", flag)?.SetValue(source, DateTime.Now, null);
types.GetProperty("AddTime", flag)?.SetValue(source, DateTime.Now, null);
types.GetProperty("CreateBy", flag)?.SetValue(source, context.GetUId(), null);
types.GetProperty("CreateName", flag)?.SetValue(source, context.GetNickName(), null);
types.GetProperty("Create_by", flag)?.SetValue(source, context.GetUId(), null);
types.GetProperty("Create_name", flag)?.SetValue(source, context.GetNickName(), null);
types.GetProperty("CreateBy", flag)?.SetValue(source, context.GetName(), null);
types.GetProperty("Create_by", flag)?.SetValue(source, context.GetName(), null);
types.GetProperty("UserId", flag)?.SetValue(source, context.GetUId(), null);
return source;
@ -28,15 +23,14 @@ namespace Infrastructure
public static TSource ToUpdate<TSource>(this TSource source, HttpContext? context = null)
{
var types = source?.GetType();
if (types == null || context == null) return source;
if (types == null) return source;
BindingFlags flag = BindingFlags.Public | BindingFlags.IgnoreCase | BindingFlags.Instance;
types.GetProperty("UpdateTime", flag)?.SetValue(source, DateTime.Now, null);
types.GetProperty("Update_time", flag)?.SetValue(source, DateTime.Now, null);
types.GetProperty("UpdateBy", flag)?.SetValue(source, context.GetUId(), null);
types.GetProperty("Update_by", flag)?.SetValue(source, context.GetUId(), null);
types.GetProperty("UpdateName", flag)?.SetValue(source, context.GetNickName(), null);
types.GetProperty("Update_name", flag)?.SetValue(source, context.GetNickName(), null);
types.GetProperty("UpdateBy", flag)?.SetValue(source, context.GetName(), null);
types.GetProperty("Update_by", flag)?.SetValue(source, context.GetName(), null);
return source;
}

View File

@ -1,15 +1,12 @@
using IPTools.Core;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Infrastructure;
using Infrastructure.Extensions;
using System.Security.Claims;
using System.Text;
using System.Text.RegularExpressions;
using UAParser;
using ZR.Model.System;
namespace Infrastructure.Extensions
namespace ZR.Admin.WebApi.Extensions
{
/// <summary>
/// HttpContext扩展类
@ -53,7 +50,7 @@ namespace Infrastructure.Extensions
if (result.Contains("::1"))
result = "127.0.0.1";
result = result.Replace("::ffff:", "");
result = result.Replace("::ffff:", "127.0.0.1");
result = result.Split(':')?.FirstOrDefault() ?? "127.0.0.1";
result = IsIP(result) ? result : "127.0.0.1";
return result;
@ -85,20 +82,12 @@ namespace Infrastructure.Extensions
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public static string GetName(this HttpContext context)
public static string? GetName(this HttpContext context)
{
var uid = context.User?.Identity?.Name;
return uid;
}
public static string? GetNickName(this HttpContext context)
{
var nickName = context.User.FindFirstValue("NickName");
if (string.IsNullOrEmpty(nickName))
nickName = null;
return nickName;
}
/// <summary>
/// 判断是否是管理员
@ -116,7 +105,7 @@ namespace Infrastructure.Extensions
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public static IEnumerable<ClaimsIdentity> GetClaims(this HttpContext context)
public static IEnumerable<ClaimsIdentity>? GetClaims(this HttpContext context)
{
return context.User?.Identities;
}
@ -142,12 +131,26 @@ namespace Infrastructure.Extensions
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>
/// 获取请求Url
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public static string GetRequestUrl(this HttpContext context)
public static string? GetRequestUrl(this HttpContext context)
{
return context != null ? context.Request.Path.Value : "";
}
@ -191,51 +194,30 @@ namespace Infrastructure.Extensions
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>
/// <param name="reqMethod"></param>
/// <param name="operLog"></param>
/// <param name="context"></param>
public static string GetRequestValue(this HttpContext context, string reqMethod)
public static void GetRequestValue(this HttpContext context, SysOperLog operLog)
{
string param = string.Empty;
string reqMethod = operLog.RequestMethod;
string param= string.Empty;
if (HttpMethods.IsPost(reqMethod) || HttpMethods.IsPut(reqMethod) || HttpMethods.IsDelete(reqMethod))
{
param = context.GetBody();
string regex = "(?<=\"password\":\")[^\",]*";
param = Regex.Replace(param, regex, "***");
param = PwdRep().Replace(param, "***");
}
if (param.IsEmpty())
{
param = context.GetQueryString();
}
return param;
operLog.OperParam = param;
}
[GeneratedRegex("(?<=\"password\":\")[^\",]*")]
private static partial Regex PwdRep();
}
}

View File

@ -1,9 +1,6 @@
using AspNetCoreRateLimit;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace ZR.Infrastructure.WebExtensions
namespace ZR.Admin.WebApi.Extensions
{
public static class IPRateExtension
{
@ -17,8 +14,8 @@ namespace ZR.Infrastructure.WebExtensions
//从appsettings.json中加载Ip规则
services.Configure<IpRateLimitPolicies>(configuration.GetSection("IpRateLimitPolicies"));
//注入计数器和规则存储
services.AddSingleton<IIpPolicyStore, DistributedCacheIpPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, DistributedCacheRateLimitCounterStore>();
services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
//配置(解析器、计数器密钥生成器)
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
services.AddSingleton<IProcessingStrategy, AsyncKeyLockProcessingStrategy>();

View File

@ -1,9 +1,9 @@
using SqlSugar.IOC;
using ZR.Model.Models;
using ZR.Model.System;
using ZR.ServiceCore.Model;
using ZR.ServiceCore.Model.Generate;
using ZR.Model.System.Generate;
namespace ZR.ServiceCore.SqlSugar
namespace ZR.Admin.WebApi.Extensions
{
/// <summary>
/// 初始化表

View File

@ -1,9 +1,7 @@
using Infrastructure.Helper;
using JinianNet.JNTemplate;
using Microsoft.Extensions.DependencyInjection;
using System;
using JinianNet.JNTemplate;
using ZR.Common;
namespace Infrastructure
namespace ZR.Admin.WebApi.Extensions
{
public static class LogoExtension
{
@ -12,14 +10,13 @@ namespace Infrastructure
Console.ForegroundColor = ConsoleColor.Blue;
var contentTpl = JnHelper.ReadTemplate("", "logo.txt");
var content = contentTpl?.Render();
var context = App.HttpContext;
Console.WriteLine(content);
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine("🎉源码地址: https://gitee.com/izory/ZrAdminNetCore");
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($"Swagger地址[后端启动地址]/swagger/index.html");
}
}
}

View File

@ -0,0 +1,66 @@
using ZR.Common;
namespace ZR.Admin.WebApi.Extensions
{
public class SqlSugarCache : SqlSugar.ICacheService
{
public void Add<V>(string key, V value)
{
//RedisServer.Cache.Set(key, value, 3600 + RedisHelper.RandomExpired(5, 30));
CacheHelper.SetCache(key, value);
}
public void Add<V>(string key, V value, int cacheDurationInSeconds)
{
//RedisServer.Cache.Set(key, value, cacheDurationInSeconds);
CacheHelper.SetCaches(key, value, cacheDurationInSeconds);
}
public bool ContainsKey<V>(string key)
{
//return RedisServer.Cache.Exists(key);
return CacheHelper.Exists(key);
}
public V Get<V>(string key)
{
//return RedisServer.Cache.Get<V>(key);
return (V)CacheHelper.Get(key);
}
public IEnumerable<string> GetAllKey<V>()
{
//return RedisServer.Cache.Keys("*");
return CacheHelper.GetCacheKeys();
}
public V GetOrCreate<V>(string cacheKey, Func<V> create, int cacheDurationInSeconds = int.MaxValue)
{
if (ContainsKey<V>(cacheKey))
{
var result = Get<V>(cacheKey);
if (result == null)
{
return create();
}
else
{
return result;
}
}
else
{
var restul = create();
Add(cacheKey, restul, cacheDurationInSeconds);
return restul;
}
}
public void Remove<V>(string key)
{
//RedisServer.Cache.Del(key);
CacheHelper.Remove(key);
}
}
}

View File

@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Infrastructure;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Filters;
using Swashbuckle.AspNetCore.SwaggerUI;
using System.Reflection;
namespace ZR.Admin.WebApi.Extensions
@ -33,13 +33,7 @@ namespace ZR.Admin.WebApi.Extensions
};
});
});
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("sys/swagger.json", "系统管理");
c.SwaggerEndpoint("article/swagger.json", "文章管理");
c.SwaggerEndpoint("v1/swagger.json", "business");
c.DocExpansion(DocExpansion.None); //->修改界面打开时自动折叠
});
app.UseSwaggerUI(c => c.SwaggerEndpoint("v1/swagger.json", "ZrAdmin v1"));
}
public static void AddSwaggerConfig(this IServiceCollection services)
@ -49,20 +43,6 @@ namespace ZR.Admin.WebApi.Extensions
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
{
Title = "ZrAdmin.NET Api",
@ -75,9 +55,6 @@ namespace ZR.Admin.WebApi.Extensions
//添加文档注释
var baseDir = AppContext.BaseDirectory;
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 xmlPath = Path.Combine(baseDir, xmlFile);
@ -117,20 +94,6 @@ namespace ZR.Admin.WebApi.Extensions
new List<string>()
}
});
//判断接口归于哪个分组
c.DocInclusionPredicate((docName, apiDescription) =>
{
if (docName == "v1")
{
//当分组为NoGroup时只要没加特性的都属于这个组
return string.IsNullOrEmpty(apiDescription.GroupName);
}
else
{
return apiDescription.GroupName == docName;
}
});
});
}
}

View File

@ -1,7 +1,6 @@
using Quartz.Spi;
using SqlSugar.IOC;
using ZR.Model.System;
using ZR.ServiceCore.Model;
using ZR.Tasks;
namespace ZR.Admin.WebApi.Extensions
@ -41,7 +40,7 @@ namespace ZR.Admin.WebApi.Extensions
foreach (var task in tasks.Result)
{
var result = _schedulerServer.AddTaskScheduleAsync(task);
if (result.Result.IsSuccess())
if (result.Result.Code == 200)
{
Console.WriteLine($"注册任务[{task.Name}]ID{task.ID}成功");
}

View File

@ -2,12 +2,9 @@
using Infrastructure.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using ZR.Model.System;
using ZR.Service.System;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using ZR.Model.System.Dto;
namespace ZR.ServiceCore.Middleware
namespace ZR.Admin.WebApi.Filters
{
/// <summary>
/// API授权判断
@ -20,11 +17,8 @@ namespace ZR.ServiceCore.Middleware
/// 权限字符串,例如 system:user:view
/// </summary>
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 HasRole { get; set; }
public ActionPermissionFilter() { }
public ActionPermissionFilter(string permission)
{
@ -40,22 +34,12 @@ namespace ZR.ServiceCore.Middleware
/// <returns></returns>
public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
TokenModel info = JwtUtil.GetLoginUser(context.HttpContext);
LoginUser info = Framework.JwtUtil.GetLoginUser(context.HttpContext);
if (info != null && info?.UserId > 0)
{
long userId = info.UserId;
List<string> perms = CacheService.GetUserPerms(GlobalConstant.UserPermKEY + userId);
List<string> perms = info.Permissions;
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)))
{
HasPermi = true;
@ -68,22 +52,24 @@ namespace ZR.ServiceCore.Middleware
{
HasPermi = perms.Exists(f => f.ToLower() == Permission.ToLower());
}
if (!HasPermi && !string.IsNullOrEmpty(RolePermi))
{
HasPermi = info.RoleIds.Contains(RolePermi);
}
bool isDemoMode = AppSettings.GetAppConfig("DemoMode", false);
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", "forceLogout", "batchLogout" };
if (isDemoMode && denyPerms.Any(f => Permission.Contains(f, StringComparison.OrdinalIgnoreCase)))
string[] denyPerms = new string[] { "update", "add", "remove", "add", "edit", "delete", "import", "run", "start", "stop", "clear", "send", "export", "upload", "common", "gencode", "reset" };
if (isDemoMode && denyPerms.Any(f => Permission.ToLower().Contains(f)))
{
context.Result = new JsonResult(new { code = (int)ResultCode.FORBIDDEN, msg = "演示模式 , 不允许操作" });
context.Result = new JsonResult(new { code = ResultCode.FORBIDDEN, msg = "演示模式 , 不允许操作" });
}
if (!HasPermi && !Permission.Equals("common"))
{
logger.Info($"用户{info.UserName}没有权限访问{url},当前权限[{Permission}]");
JsonResult result = new(new ApiResult((int)ResultCode.FORBIDDEN, $"你当前没有权限[{Permission}]访问,请联系管理员", url))
JsonResult result = new(new ApiResult()
{
Code = (int)ResultCode.FORBIDDEN,
Msg = $"你当前没有权限[{Permission}]访问,请联系管理员",
Data = url
})
{
ContentType = "application/json",
};

View File

@ -2,21 +2,20 @@
using Infrastructure.Attribute;
using Infrastructure.Model;
using IPTools.Core;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using NLog;
using ZR.Admin.WebApi.Extensions;
using ZR.Model.System;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
namespace ZR.ServiceCore.Middleware
namespace ZR.Admin.WebApi.Filters
{
public class GlobalActionMonitor : ActionFilterAttribute
{
static readonly Logger logger = LogManager.GetCurrentClassLogger();
private readonly ISysOperLogService OperLogService;
private ISysOperLogService OperLogService;
public GlobalActionMonitor(ISysOperLogService operLogService)
{
OperLogService = operLogService;
@ -30,25 +29,29 @@ namespace ZR.ServiceCore.Middleware
/// <returns></returns>
public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
string msg = string.Empty;
ApiResult response = new();
response.Code = (int)ResultCode.PARAM_ERROR;
var values = context.ModelState.Values;
foreach (var item in values)
{
foreach (var err in item.Errors)
{
if (!string.IsNullOrEmpty(msg))
if (err.ErrorMessage.Contains("JSON"))
{
msg += " | ";
return next();
}
if (!string.IsNullOrEmpty(response.Msg))
{
response.Msg += " | ";
}
msg += err.ErrorMessage;
response.Msg += err.ErrorMessage;
}
}
if (!string.IsNullOrEmpty(msg))
if (!string.IsNullOrEmpty(response.Msg))
{
logger.Info($"请求参数错误,{msg}");
ApiResult response = new((int)ResultCode.PARAM_ERROR, msg);
logger.Info($"请求参数错误,{response.Msg}");
context.Result = new JsonResult(response);
}
return base.OnActionExecutionAsync(context, next);
@ -98,12 +101,12 @@ namespace ZR.ServiceCore.Middleware
OperUrl = HttpContextExtension.GetRequestUrl(context.HttpContext),
RequestMethod = method,
JsonResult = jsonResult,
OperLocation = HttpContextExtension.GetIpInfo(ip),
OperLocation = ip_info.Province + " " + ip_info.City,
Method = controller + "." + action + "()",
//Elapsed = _stopwatch.ElapsedMilliseconds,
OperTime = DateTime.Now,
OperParam = HttpContextExtension.GetRequestValue(context.HttpContext, method)
OperTime = DateTime.Now
};
HttpContextExtension.GetRequestValue(context.HttpContext, sysOperLog);
if (logAttribute != null)
{
@ -114,7 +117,7 @@ namespace ZR.ServiceCore.Middleware
}
LogEventInfo ei = new(NLog.LogLevel.Info, "GlobalActionMonitor", "");
ei.Properties["jsonResult"] = !HttpMethods.IsGet(method) ? jsonResult : "";
ei.Properties["requestParam"] = sysOperLog.OperParam;
ei.Properties["user"] = userName;
@ -128,7 +131,7 @@ namespace ZR.ServiceCore.Middleware
}
}
private LogAttribute GetLogAttribute(ControllerActionDescriptor controllerActionDescriptor)
private LogAttribute? GetLogAttribute(ControllerActionDescriptor controllerActionDescriptor)
{
var attribute = controllerActionDescriptor.MethodInfo.GetCustomAttributes(inherit: true)
.FirstOrDefault(a => a.GetType().Equals(typeof(LogAttribute)));

View File

@ -4,18 +4,23 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using ZR.Common;
using NLog;
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
{
/// <summary>
/// 授权校验访问
/// 如果跳过授权登录在Action 或controller加上 AllowAnonymousAttribute
/// </summary>
[AttributeUsage(AttributeTargets.All)]
public class VerifyAttribute : Attribute, IAuthorizationFilter
{
static readonly Logger logger = LogManager.GetCurrentClassLogger();
/// <summary>
/// 只判断token是否正确不判断权限
/// 如果需要判断权限的在Action上加上ApiActionPermission属性标识权限类别ActionPermissionFilter作权限处理
@ -35,35 +40,16 @@ namespace ZR.Admin.WebApi.Filters
string ip = HttpContextExtension.GetClientUserIp(context.HttpContext);
string url = context.HttpContext.Request.Path;
var isAuthed = context.HttpContext.User.Identity.IsAuthenticated;
string osType = context.HttpContext.Request.Headers["os"];
//使用jwt token校验2020-11-21
TokenModel loginUser = JwtUtil.GetLoginUser(context.HttpContext);
if (loginUser != null)
{
var nowTime = DateTime.UtcNow;
TimeSpan ts = loginUser.ExpireTime - nowTime;
LoginUser info = JwtUtil.GetLoginUser(context.HttpContext);
//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)
if (info == null || !isAuthed)
{
string msg = $"请求访问[{url}]失败,无法访问系统资源";
//logger.Info(msg);
context.Result = new JsonResult(ApiResult.Error(ResultCode.DENY, msg));
logger.Info($"{msg}");
context.Result = new JsonResult(new ApiResult((int)ResultCode.DENY, msg));
}
}
}

View File

@ -1,19 +1,18 @@
using Infrastructure.Extensions;
using Infrastructure.Model;
using Microsoft.AspNetCore.Http;
using Infrastructure;
using Infrastructure.Extensions;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using ZR.Admin.WebApi.Extensions;
using ZR.Model.System.Dto;
using ZR.Service.System;
namespace Infrastructure
namespace ZR.Admin.WebApi.Framework
{
/// <summary>
/// 2023-8-29已从WebApi移至此
/// 2020-11-20
/// </summary>
public class JwtUtil
{
@ -22,7 +21,7 @@ namespace Infrastructure
/// </summary>
/// <param name="httpContext"></param>
/// <returns></returns>
public static TokenModel GetLoginUser(HttpContext httpContext)
public static LoginUser GetLoginUser(HttpContext httpContext)
{
string token = httpContext.GetToken();
@ -37,12 +36,10 @@ namespace Infrastructure
/// 生成token
/// </summary>
/// <param name="claims"></param>
/// <param name="jwtSettings"></param>
/// <returns></returns>
public static string GenerateJwtToken(List<Claim> claims)
public static string GenerateJwtToken(List<Claim> claims, JwtSettings jwtSettings)
{
JwtSettings jwtSettings = new();
AppSettings.Bind("JwtSettings", jwtSettings);
var authTime = DateTime.Now;
var expiresAt = authTime.AddMinutes(jwtSettings.Expire);
var tokenHandler = new JwtSecurityTokenHandler();
@ -58,7 +55,7 @@ namespace Infrastructure
IssuedAt = authTime,//token生成时间
Expires = expiresAt,
//NotBefore = authTime,
TokenType = jwtSettings.TokenType,
TokenType = "Bearer",
//对称秘钥,签名证书
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
};
@ -99,7 +96,7 @@ namespace Infrastructure
/// </summary>
/// <param name="token">令牌</param>
/// <returns></returns>
public static JwtSecurityToken? ParseToken(string token)
public static IEnumerable<Claim>? ParseToken(string token)
{
var tokenHandler = new JwtSecurityTokenHandler();
var validateParameter = ValidParameters();
@ -108,7 +105,8 @@ namespace Infrastructure
{
tokenHandler.ValidateToken(token, validateParameter, out SecurityToken validatedToken);
return tokenHandler.ReadJwtToken(token);
var jwtToken = tokenHandler.ReadJwtToken(token);
return jwtToken.Claims;
}
catch (Exception ex)
{
@ -121,21 +119,25 @@ namespace Infrastructure
/// <summary>
/// jwt token校验
/// </summary>
/// <param name="jwtSecurityToken"></param>
/// <param name="jwtToken"></param>
/// <returns></returns>
public static TokenModel? ValidateJwtToken(JwtSecurityToken jwtSecurityToken)
public static LoginUser? ValidateJwtToken(IEnumerable<Claim> jwtToken)
{
try
{
if (jwtSecurityToken == null) return null;
IEnumerable<Claim> claims = jwtSecurityToken?.Claims;
TokenModel loginUser = null;
var userData = claims.FirstOrDefault(x => x.Type == ClaimTypes.UserData)?.Value;
LoginUser loginUser = null;
var userData = jwtToken.FirstOrDefault(x => x.Type == ClaimTypes.UserData)?.Value;
if (userData != null)
{
loginUser = JsonConvert.DeserializeObject<TokenModel>(userData);
loginUser.ExpireTime = jwtSecurityToken.ValidTo;
loginUser = JsonConvert.DeserializeObject<LoginUser>(userData);
var permissions = CacheService.GetUserPerms(GlobalConstant.UserPermKEY + loginUser?.UserId);
if (loginUser?.UserName == GlobalConstant.AdminRole)
{
permissions = new List<string>() { GlobalConstant.AdminPerm };
}
if (permissions == null) return null;
loginUser.Permissions = permissions;
}
return loginUser;
}
@ -151,14 +153,13 @@ namespace Infrastructure
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public static List<Claim> AddClaims(TokenModel user)
public static List<Claim> AddClaims(LoginUser user)
{
var claims = new List<Claim>()
{
new Claim(ClaimTypes.PrimarySid, user.UserId.ToString()),
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.UserData, JsonConvert.SerializeObject(user)),
new Claim("NickName", user.NickName)
new Claim(ClaimTypes.UserData, JsonConvert.SerializeObject(user))
};
return claims;

View File

@ -4,8 +4,4 @@ global using Infrastructure;
global using Infrastructure.Attribute;
global using Infrastructure.Enums;
global using Infrastructure.Model;
global using Mapster;
global using Infrastructure.Extensions;
global using Infrastructure.Controllers;
global using ZR.ServiceCore.Middleware;
global using Microsoft.AspNetCore.Mvc;
global using Mapster;

View File

@ -0,0 +1,107 @@
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 });
}
}
}

View File

@ -0,0 +1,40 @@
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;
}
}
}

View File

@ -1,47 +0,0 @@
{
//
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"EndpointWhitelist": [
"post:/system/dict/data/types",
"*:/msghub/negotiate",
"*:/LogOut",
"*:/common/uploadfile",
"post:/aim/msg/list/sendmsg"
],
"QuotaExceededResponse": {
"Content": "{{\"code\":429,\"msg\":\"访问过于频繁,请稍后重试\"}}",
"ContentType": "application/json",
"StatusCode": 429
},
// "IpWhitelist": [ "127.0.0.1", "::1/10", "192.168.0.0/24" ],
//api,*
"GeneralRules": [
{
"Endpoint": "*:/captchaImage",
//{}{}使s, m, h, d
"Period": "3s",
"Limit": 5
},
{
"Endpoint": "((post)|(put)):*",
"Period": "3s",
"Limit": 1
},
{
"Endpoint": "*:/aim/msg/list/SendMsg",
"Period": "1s",
"Limit": 0,
}
]
},
"IpRateLimitPolicies": {
//ip
"IpRules": [
]
}
}

View File

@ -2,18 +2,17 @@
using Infrastructure.Attribute;
using Infrastructure.Model;
using IPTools.Core;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using NLog;
using System.Diagnostics;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Routing;
using System.Text.Json;
using ZR.Admin.WebApi.Extensions;
using ZR.Common;
using ZR.Model.System;
using ZR.Service.System.IService;
using ZR.ServiceCore.Model;
using textJson = System.Text.Json;
namespace ZR.ServiceCore.Middleware
namespace ZR.Admin.WebApi.Middleware
{
/// <summary>
/// 全局异常处理中间件
@ -46,22 +45,16 @@ namespace ZR.ServiceCore.Middleware
private async Task HandleExceptionAsync(HttpContext context, Exception ex)
{
LogLevel logLevel = LogLevel.Info;
NLog.LogLevel logLevel = NLog.LogLevel.Info;
int code = (int)ResultCode.GLOBAL_ERROR;
string msg;
string error = string.Empty;
bool notice = true;
var stackTrace = string.Empty;
var controller = context.GetRouteData().Values["Controller"].ToString();
var action = context.GetRouteData().Values["Action"].ToString();
//自定义异常
if (ex is CustomException customException)
{
code = customException.Code;
msg = customException.Message;
error = customException.LogMsg;
notice = customException.Notice;
stackTrace = customException.StackTrace;
}
else if (ex is ArgumentException)//参数异常
{
@ -72,19 +65,18 @@ namespace ZR.ServiceCore.Middleware
{
msg = "服务器好像出了点问题,请联系系统管理员...";
error = $"{ex.Message}";
logLevel = LogLevel.Error;
logLevel = NLog.LogLevel.Error;
context.Response.StatusCode = 500;
stackTrace = ex.StackTrace;
}
var options = new textJson.JsonSerializerOptions
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
PropertyNamingPolicy = textJson.JsonNamingPolicy.CamelCase,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
ApiResult apiResult = new(code, msg);
string responseResult = textJson.JsonSerializer.Serialize(apiResult, options);
string responseResult = JsonSerializer.Serialize(apiResult, options).ToLower();
string ip = HttpContextExtension.GetClientUserIp(context);
var ip_info = IpTool.Search(ip);
@ -96,13 +88,11 @@ namespace ZR.ServiceCore.Middleware
RequestMethod = context.Request.Method,
JsonResult = responseResult,
ErrorMsg = string.IsNullOrEmpty(error) ? msg : error,
StackTrace = stackTrace,
OperName = HttpContextExtension.GetName(context),
OperLocation = ip_info.Province + " " + ip_info.City,
Method = controller + "." + action + "()",
OperTime = DateTime.Now,
OperParam = HttpContextExtension.GetRequestValue(context, context.Request.Method)
OperTime = DateTime.Now
};
HttpContextExtension.GetRequestValue(context, sysOperLog);
var endpoint = GetEndpoint(context);
if (endpoint != null)
{
@ -128,16 +118,15 @@ namespace ZR.ServiceCore.Middleware
Logger.Log(ei);
context.Response.ContentType = "text/json;charset=utf-8";
await context.Response.WriteAsync(responseResult, System.Text.Encoding.UTF8);
string errorMsg = $"> 操作人:{sysOperLog.OperName}" +
$"\n> 操作地区:{sysOperLog.OperIp}({sysOperLog.OperLocation})" +
$"\n> 操作模块:{sysOperLog.Title}" +
$"\n> 操作地址:{sysOperLog.OperUrl}" +
$"\n> 错误信息:{msg}\n\n> {error}";
WxNoticeHelper.SendMsg("系统出错", errorMsg, "", WxNoticeHelper.MsgType.markdown);
SysOperLogService.InsertOperlog(sysOperLog);
if (!notice) return;
WxNoticeHelper.SendMsg("系统异常", errorMsg, msgType: WxNoticeHelper.MsgType.markdown);
}
public static Endpoint GetEndpoint(HttpContext context)

View File

@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
internalLogLevel="Info"
internalLogFile="nlog-internal.log"
throwConfigExceptions="true">
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
autoReload="true"
internalLogLevel="Info"
internalLogFile="nlog-internal.log">
<!--Nlog 文档参考地址https://github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-2-->
<!--internalLogLevel Nlog内部日志记录为Off关闭。除非纠错不可以设为Trace否则速度很慢起码Debug以上-->
@ -25,77 +24,52 @@
<!--${basedir}表示当前应用程序域所在的根目录-->
<target name="allfile" xsi:type="File"
fileName="${basedir}/adminlogs/all.txt"
archiveFileName="${basedir}/adminlogs/bak/all/all.{###}.txt"
archiveFileName="${basedir}/adminlogs/bak/all.{###}.txt"
archiveEvery="Day"
archiveNumbering="DateAndSequence"
archiveAboveSize="20000000"
maxArchiveFiles="30"
keepFileOpen="true"
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}"/>
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}"/>
<!--错误日志-->
<target name="errorfile" xsi:type="File"
fileName="${basedir}/adminlogs/error.txt"
archiveFileName="${basedir}/adminlogs/bak/error/error.{###}.txt"
archiveFileName="${basedir}/adminlogs/bak/error.{###}.txt"
archiveEvery="Day"
archiveNumbering="DateAndSequence"
archiveAboveSize="20000000"
maxArchiveFiles="30"
keepFileOpen="true"
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}"/>
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}"/>
<!--SQL文件-->
<target name="sqlfile" xsi:type="File"
fileName="${basedir}/adminlogs/sql.txt"
archiveFileName="${basedir}/adminlogs/bak/sql/sql.{###}.txt"
archiveEvery="Day"
archiveNumbering="DateAndSequence"
archiveAboveSize="20000000"
maxArchiveFiles="30"
keepFileOpen="false"
layout="${longdate} | ${uppercase:${level}} | ${aspnet-request-iP} | ${aspnet-request-headers:HeaderNames=userName} | ${aspnet-request-url} ${newline}${message}"/>
fileName="${basedir}/adminlogs/admin-sql.txt"
archiveFileName="${basedir}/adminlogs/bak/admin-sql{###}.txt"
archiveEvery="Day"
archiveNumbering="DateAndSequence"
archiveAboveSize="20000000"
maxArchiveFiles="30"
keepFileOpen="false"
layout="${longdate} | ${uppercase:${level}} | ${aspnet-request-iP} | ${aspnet-request-headers:HeaderNames=userName} | ${aspnet-request-url} ${newline}${message}"/>
<!--写入彩色控制台-->
<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.Info" foregroundColor="Gray" />
<highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" />
<highlight-row condition="level == LogLevel.Error" foregroundColor="Red" />
<highlight-row condition="level == LogLevel.Fatal" foregroundColor="Red" backgroundColor="White" />
<highlight-word regex="SQL" foregroundColor="Blue" />
<highlight-word regex="【" foregroundColor="Blue" />
<highlight-word regex="】" foregroundColor="Blue" />
<highlight-word regex="SQL语句" foregroundColor="Blue" />
</target>
<!--日志打印到控制台-->
<target name="console" xsi:type="ColoredConsole"
layout="${date:format=MM-dd HH\:mm\:ss} | ${aspnet-request-url} ${newline} ${message}">
<highlight-row condition="level == LogLevel.Debug" foregroundColor="DarkGray" />
<highlight-row condition="level == LogLevel.Info" foregroundColor="Gray" />
<highlight-row condition="level == LogLevel.Warn" foregroundColor="Yellow" />
<highlight-row condition="level == LogLevel.Error" foregroundColor="Red" />
<highlight-row condition="level == LogLevel.Fatal" foregroundColor="Red" backgroundColor="White" />
</target>
layout="${date:format=MM-dd HH\:mm\:ss} | ${aspnet-request-url} ${newline} ${message}"/>
<!--写入黑洞-->
<target name="blackhole" xsi:type="Null" />
<target name="ipRateLimitFile" xsi:type="File"
fileName="${basedir}/adminlogs/ipratelimit.txt"
archiveFileName="${basedir}/adminlogs/bak/ipratelimit/ipratelimit{###}.txt"
archiveEvery="Day"
archiveNumbering="DateAndSequence"
archiveAboveSize="20000000"
maxArchiveFiles="30"
keepFileOpen="false"
layout="${date:format=MM-dd HH\:mm\:ss} | ${aspnet-request-url} ${newline} ${message}"
/>
<!-- 速率限制日志写入控制台 -->
<target name="consoleIpRateLimit" xsi:type="ColoredConsole" useDefaultRowHighlightingRules="false"
layout="${date:format=MM-dd HH\:mm\:ss} | ${aspnet-request-url} ${newline} ${message}">
<highlight-row condition="level == LogLevel.Info" foregroundColor="Blue" />
</target>
</targets>
<rules>
@ -106,10 +80,11 @@
<!--<logger name="System.*" writeTo="blackhole" final="true" />-->
<!-- Quartz -->
<logger name="Quartz*" minlevel="Trace" maxlevel="Info" final="true" />
<logger name="*.SqlSugar.SqlSugarSetup" final="true" writeTo="consoleSql,sqlfile"/>
<logger name="*" minLevel="Trace" writeTo="allfile,console" />
<logger name="*.GlobalExceptionMiddleware" final="true" writeTo="consoleSql,errorfile"/>
<logger name="ZR.Admin.WebApi.Middleware.CustomIpRateLimitMiddleware" final="true" writeTo="consoleIpRateLimit,ipRateLimitFile" />
<logger name="ZR.Admin.WebApi.Middleware.GlobalExceptionMiddleware" final="true" writeTo="console,errorfile"/>
<logger name="ZR.Admin.WebApi.Extensions.DbExtension" final="true" writeTo="consoleSql,sqlfile"/>
<logger name="*" writeTo="console"/>
<logger name="*" minLevel="Trace" writeTo="allfile" />
<!--Skip non-critical Microsoft logs and so log only own logs-->
<logger name="Microsoft.*,Quartz.Core.QuartzSchedulerThread" maxlevel="Info" final="true" />
</rules>

View File

@ -1,35 +1,18 @@
using AspNetCoreRateLimit;
using Infrastructure.Converter;
using Infrastructure;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.RateLimiting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Redis;
using Microsoft.Extensions.Options;
using NLog.Web;
using System.Text.Json;
using ZR.Admin.Grpc;
using ZR.Admin.Grpc.Services;
using Microsoft.IdentityModel.Tokens;
using System.Text.Json.Serialization;
using ZR.Admin.WebApi.Extensions;
using ZR.Infrastructure.Cache;
using ZR.Infrastructure.WebExtensions;
using ZR.ServiceCore.Services.IService;
using ZR.ServiceCore.Signalr;
using ZR.ServiceCore.SqlSugar;
using IpRateLimitPolicy = AspNetCoreRateLimit.IpRateLimitPolicy;
using ZR.Admin.WebApi.Filters;
using ZR.Admin.WebApi.Framework;
using ZR.Admin.WebApi.Hubs;
using ZR.Admin.WebApi.Middleware;
using ZR.Common.Cache;
var builder = WebApplication.CreateBuilder(args);
//load json config by ipRateLimit
builder.Configuration.AddJsonFile("IpRateLimitConfig.json", true, true);
//load json config by loginVerify
builder.Configuration.AddJsonFile("loginVerifyConf.json", true, true);
// NLog: Setup NLog for Dependency injection
//builder.Logging.ClearProviders();
builder.Host.UseNLog();
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
@ -39,10 +22,10 @@ builder.Services.AddSwaggerGen();
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// 跨域配置
builder.Services.AddCors(builder.Configuration);
// Grpc
builder.Services.AddGrpc();
// 显示logo
builder.Services.AddLogo();
//注入SignalR实时通讯默认用json传输
builder.Services.AddSignalR();
//消除Error unprotecting the session cookie警告
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "DataProtection"));
@ -56,10 +39,30 @@ builder.Services.AddHttpContextAccessor();
builder.Services.Configure<OptionsSetting>(builder.Configuration);
//jwt 认证
builder.Services.AddJwt();
//配置文件
builder.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))
{
context.Response.Headers.Add("Token-Expired", "true");
}
return Task.CompletedTask;
}
};
});
builder.Services.AddSingleton(new AppSettings(builder.Configuration));
//app服务注册
builder.Services.AddAppService();
//开启计划任务
builder.Services.AddTaskSchedulers();
@ -69,75 +72,32 @@ var openRedis = builder.Configuration["RedisServer:open"];
if (openRedis == "1")
{
RedisServer.Initalize();
builder.Services.AddSingleton<IDistributedCache>(new CSRedisCache(RedisHelper.Instance));
}
builder.Services.AddRateLimiter(o => o
.AddFixedWindowLimiter(policyName: "fixed", options =>
{
// configuration
}));
builder.Services.AddMvc(options =>
{
options.Filters.Add(typeof(GlobalActionMonitor));//全局注册
})
.AddJsonOptions(options =>
{
//options.JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString;
options.JsonSerializerOptions.NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString;
options.JsonSerializerOptions.WriteIndented = true;
options.JsonSerializerOptions.Converters.Add(new JsonConverterUtil.DateTimeConverter());
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();
//using var channel = GrpcChannel.ForAddress("https://localhost:7025");
//var client = new Greeter.GreeterClient(channel);
//var reply = await client.SayHelloAsync(new HelloRequest { Name = "GreeterClient" });
//Console.WriteLine("Greeting: " + reply.Message);
//
// var blockClient = new Block.BlockClient(channel);
// var blockReply = await blockClient.GetBlockAsync(new BlockRequest { In = "asads" });
// Console.WriteLine("BlockReply: " + blockReply.Out);
//builder.Services.AddGrpcServiceConfiguration(builder.Configuration);
// 配置 Kestrel 监听 HTTP/2 的非加密流量
//builder.WebHost.ConfigureKestrel(options =>
//{
// // 如果你只想监听一个端口,只保留其中一个 Listen
// options.ListenLocalhost(8888, o =>
// {
// o.Protocols = HttpProtocols.Http2; // 明确设置为 Http2
// });
//});
var app = builder.Build();
InternalApp.ServiceProvider = app.Services;
InternalApp.Configuration = builder.Configuration;
InternalApp.WebHostEnvironment = app.Environment;
//初始化db
builder.Services.AddDb(app.Environment);
builder.Services.AddDb(builder.Configuration, app.Environment);
//使用全局异常中间件
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) =>
{
//设置可以多次获取body内容
@ -153,7 +113,7 @@ app.UseStaticFiles();
//开启路由访问
app.UseRouting();
app.UseCors("Policy");//要放在app.UseEndpoints前。
app.UseHttpsRedirection();
//app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
@ -168,8 +128,7 @@ if (builder.Environment.IsProduction())
//使用swagger
app.UseSwagger();
//启用客户端IP限制速率
// app.UseIpRateLimiting();
app.UseMiddleware<CustomIpRateLimitMiddleware>();
app.UseIpRateLimiting();
app.UseRateLimiter();
//设置socket连接
app.MapHub<MessageHub>("/msgHub");
@ -180,35 +139,4 @@ app.MapControllerRoute(
app.MapControllers();
using (var serviceScope = app.Services.CreateScope())
{
var services = serviceScope.ServiceProvider;
// get the IpPolicyStore instance
var ipPolicyStore = services.GetRequiredService<IIpPolicyStore>();
// seed IP data from appsettings
ipPolicyStore.SeedAsync().GetAwaiter().GetResult();
var optionsAccessor = services.GetRequiredService<IOptions<IpRateLimitOptions>>();
var ipRateLimitPolicyService = services.GetRequiredService<IIpRateLimitPolicyService>();
var ipRateLimitPolicies = ipRateLimitPolicyService.Queryable()
.Includes(it => it.Rules.Where(r => r.Flag == '1').ToList())
.Where(it => it.Flag == '1')
.ToListAsync().GetAwaiter().GetResult();
var pol = await ipPolicyStore.GetAsync(optionsAccessor.Value.IpPolicyPrefix);
pol.IpRules.AddRange(ipRateLimitPolicies.Adapt<List<IpRateLimitPolicy>>());
await ipPolicyStore.SetAsync(optionsAccessor.Value.IpPolicyPrefix, pol);
//var greeterClient = services.GetRequiredService<Greeter.GreeterClient>();
//var helloReply = await greeterClient.SayHelloAsync(new HelloRequest
//{
// Name = "gree"
//});
//Console.WriteLine(helloReply);
}
app.MapGrpcService<GreeterService>();
app.MapGrpcService<MyDiaryService>();
app.Run();

View File

@ -10,13 +10,13 @@
},
"profiles": {
"ZR.Admin.WebApi": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchUrl": "",
"applicationUrl": "https://localhost:8888",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
"commandName": "Project",
"dotnetRunMessages": true,
"launchUrl": "",
"applicationUrl": "http://localhost:8888",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -1,28 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1591,8603,8602,8604,8600,8618</NoWarn>
<NoWarn>1701;1702;1591,8603,8602,8604,8600</NoWarn>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\ZR.Admin.Grpc\ZR.Admin.Grpc.csproj" />
<ProjectReference Include="..\ZR.CodeGenerator\ZR.CodeGenerator.csproj" />
<ProjectReference Include="..\ZR.Service\ZR.Service.csproj" />
<ProjectReference Include="..\ZR.Tasks\ZR.Tasks.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
<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.Filters" Version="7.0.6" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.3" />
<PackageReference Include="UAParser" Version="3.1.47" />
<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="SkiaSharp.NativeAssets.Linux" Version="2.88.5" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.88.3" />
</ItemGroup>
<ItemGroup>
@ -30,20 +33,8 @@
</ItemGroup>
<ItemGroup>
<Content Include="..\.dockerignore">
<Link>.dockerignore</Link>
</Content>
<None Update="ip2region.db">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<Content Update="wwwroot\Generatecode\**\*">
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</Content>
<Content Update="wwwroot\export\**\*">
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</Content>
<Content Update="appsettings.Production.json">
<DependentUpon>appsettings.json</DependentUpon>
</Content>
</ItemGroup>
</Project>

View File

@ -1,20 +0,0 @@
{
"urls": "http://[*]:8888", //urldevServer
"dbConfigs": [
{
"Conn": "server=8.140.174.251;user=admin;pwd=admin123;database=ZrAdmin",
"DbType": 0, // MySql = 0, SqlServer = 1, Oracle = 3PgSql = 4
"ConfigId": "0", //
"IsAutoCloseConnection": true
}
//...
],
//redis
"RedisServer": {
"open": 1, //redis
"Cache": "8.140.174.251:6379,password=Wyd210213,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix=cache:",
// "Cache": "127.0.0.1:6379,password=Wyd210213,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix=cache:",
"Session": "8.140.174.251:6379,password=Wyd210213,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix=session:"
// "Session": "127.0.0.1:6379,password=Wyd210213,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix=session:"
},
}

View File

@ -1,126 +1,141 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"dbConfigs": [
{
"Conn": "server=127.0.0.1;user=admin;pwd=admin123;database=ZrAdmin;port=3306",
"DbType": 0, // MySql = 0, SqlServer = 1, Oracle = 3PgSql = 4
"ConfigId": "0", //
"IsAutoCloseConnection": true
}
//...
],
//
"CodeGenDbConfig": {
//{dbName}
"Conn": "Data Source=127.0.0.1;user=admin;pwd=admin123;database=ZrAdmin;port=3306",
"DbType": 0,
"IsAutoCloseConnection": true,
"DbName": "ZrAdmin" //,Oracle
},
"urls": "https://localhost:8888", //urldevServer
"corsUrls": [ "http://localhost:8887", "http://localhost:8886" ], //","
"JwtSettings": {
"Issuer": "ZRAdmin.NET", //token
"Audience": "ZRAdmin.NET", //token
"SecretKey": "SecretKey-ZRADMIN.NET-20210101",
"Expire": 1440, //jwt
"RefreshTokenTime": 5, //
"TokenType": "Bearer"
},
"InjectClass": [ "ZR.Repository", "ZR.Service", "ZR.Tasks", "ZR.ServiceCore" ], //
"ShowDbLog": true, //db
"InitDb": false, //db
"DemoMode": false, //
"SingleLogin": false, //
"Upload": {
"uploadUrl": "http://localhost:8888", //访
"localSavePath": "", // wwwroot
"maxSize": 15, // 15M
"notAllowedExt": [ ".bat", ".exe", ".jar", ".js" ]
},
//
"ALIYUN_OSS": {
"REGIONID": "oss-cn-hangzhou.aliyuncs.com", //egcn-hangzhou
"KEY": "LTAI5tBjDZatWWunAhxLAAFR",
"SECRET": "Klcm77KdpkIROAl2ffPemK5I1yDshM",
"bucketName": "markdownhexo",
"domainUrl": "https://markdownhexo.oss-cn-hangzhou.aliyuncs.com", //访
"maxSize": 1000 // 1000M
},
//
"WxCorp": {
"AgentID": "",
"CorpID": "",
"CorpSecret": "",
"SendUser": "@all"
},
//
"WxOpen": {
"AppID": "",
"AppSecret": ""
},
//
"gen": {
//
"showApp": false,
//
"autoPre": true,
"author": "admin",
"tablePrefix": "sys_", //"表前缀(生成类名不会包含表前缀,多个用逗号分隔)",
"vuePath": "", //egD:\Work\ZRAdmin-Vue3
"csharpTypeArr": {
"string": [ "varchar", "nvarchar", "text", "longtext" ],
"int": [ "int", "integer", "smallint", "int4", "int8", "int2" ],
"long": [ "bigint", "number" ],
"float": [ "numeric", "real", "float" ],
"decimal": [ "money", "decimal", "smallmoney" ],
"dateTime": [ "date", "datetime", "datetime2", "smalldatetime", "timestamp" ],
"byte": [ "tinyint" ],
"bool": [ "bit" ]
}
},
//
"MailOptions": {
//
"FromName": "文永达",
//
"FromEmail": "1224169330@qq.com", //egxxxx@qq.com
//
"Password": "wbctsfvdazchibdh",
//
"Smtp": "smtp.qq.com",
"Port": 465,
"Signature": "系统邮件,请勿回复!",
"UseSsl": true
},
//redis
"RedisServer": {
"open": 1, //redis
"Cache": "127.0.0.1:6379,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix=cache:",
"Session": "127.0.0.1:6379,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix=session:"
},
//
"CaptchaOptions": {
"CaptchaType": 10,
"IgnoreCase": true, //
"ImageOption": {
"FontSize": 36, //
"Animation": true, //
"TextBold": true, //
"InterferenceLineCount": 4, // 线
"BubbleMinRadius": 5, //
"BubbleMaxRadius": 10, //
"BubbleCount": 3, //
"BubbleThickness": 1.0 // 沿
}
},
"GrpcUrls": {
"FindTeacher": "http://localhost:5212"
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"dbConfigs": [
{
"Conn": "Data Source=LAPTOP-STKF2M8H\\SQLEXPRESS;User ID=admin;Password=admin123;Initial Catalog=ZrAdmin;",
"DbType": 1, // MySql = 0, SqlServer = 1, Oracle = 3PgSql = 4
"ConfigId": "0", //
"IsAutoCloseConnection": true
}
//...
],
//
"CodeGenDbConfig": {
//{dbName}
"Conn": "Data Source=LAPTOP-STKF2M8H\\SQLEXPRESS;User ID=admin;Password=admin123;Initial Catalog={dbName};",
"DbType": 1,
"IsAutoCloseConnection": true,
"DbName": "ZrAdmin" //
},
"urls": "http://localhost:8888", //urldevServer
"corsUrls": [ "http://localhost:8887", "http://localhost:8886" ], //","
"JwtSettings": {
"Issuer": "ZRAdmin.NET", //token
"Audience": "ZRAdmin.NET", //token
"SecretKey": "SecretKey-ZRADMIN.NET-20210101",
"Expire": 1440 //jwt
},
"InjectClass": [ "ZR.Repository", "ZR.Service", "ZR.Tasks" ], //
"InitDb": false, //db
"DemoMode": false, //
"Upload": {
"uploadUrl": "http://localhost:8888", //访
"localSavePath": "", // wwwroot
"maxSize": 15, // 15M
"notAllowedExt": [ ".bat", ".exe", ".jar", ".js" ]
},
//
"ALIYUN_OSS": {
"REGIONID": "", //egcn-hangzhou
"KEY": "XX",
"SECRET": "XX",
"bucketName": "bucketName",
"domainUrl": "http://xxx.xxx.com", //访
"maxSize": 100 // 100M
},
//
"WxCorp": {
"AgentID": "",
"CorpID": "",
"CorpSecret": "",
"SendUser": "@all"
},
//
"WxOpen": {
"AppID": "",
"AppSecret": ""
},
//
"gen": {
//
"showApp": false,
//
"autoPre": true,
"author": "admin",
"tablePrefix": "sys_", //"表前缀(生成类名不会包含表前缀,多个用逗号分隔)",
"vuePath": "", //egD:\Work\ZRAdmin-Vue3
"csharpTypeArr": {
"string": [ "varchar", "nvarchar", "text", "longtext" ],
"int": [ "int", "integer", "smallint", "int4", "int8", "int2" ],
"long": [ "bigint", "number" ],
"float": [ "numeric", "real", "float" ],
"decimal": [ "money", "decimal", "smallmoney" ],
"dateTime": [ "date", "datetime", "datetime2", "smalldatetime", "timestamp" ],
"byte": [ "tinyint" ],
"bool": [ "bit" ]
}
},
//
"MailOptions": {
//
"FromName": "system",
//
"FromEmail": "", //egxxxx@qq.com
//
"Password": "",
//
"Smtp": "smtp.qq.com",
"Port": 587,
"Signature": "系统邮件,请勿回复!",
"UseSsl": true
},
//redis
"RedisServer": {
"open": 0, //redis
"Cache": "127.0.0.1:6379,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix=cache:",
"Session": "127.0.0.1:6379,defaultDatabase=0,poolsize=50,ssl=false,writeBuffer=10240,prefix=session:"
},
//
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"EndpointWhitelist": [ "post:/system/dict/data/types", "*:/msghub/negotiate", "*:/LogOut", "*:/common/uploadfile" ],
"QuotaExceededResponse": {
"Content": "{{\"code\":429,\"msg\":\"访问过于频繁,请稍后重试\"}}",
"ContentType": "application/json",
"StatusCode": 429
},
//api,*
"GeneralRules": [
{
"Endpoint": "*:/captchaImage",
//{}{}使s, m, h, d
"Period": "3s",
"Limit": 5
},
{
"Endpoint": "((post)|(put)):*",
"Period": "3s",
"Limit": 1
}
],
"IpRateLimitPolicies": {
//ip
"IpRules": [
]
}
},
//
"CaptchaOptions": {
"IgnoreCase": true //
}
}

View File

@ -1,5 +0,0 @@
{
"RSA": {
"PrivatePem": "-----BEGIN PRIVATE KEY-----MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCjvJHH6V8fHL9zuVkW1mcDcYF5D72fEG45xLP8jyXR0/klmxZUT6My2ajGcWtOAKyca4Mcw2lKV7FRzchVyylsLi1JaH+vgbL0qwjDS0hjSm6Uqw39WFX//t1qMxX8OD5F3lvAYaNauVY6nYQ9VgEcjrq5nc+kRB7cVsR1ouQlFMQ1vLCAZwajW2pSmpjkEMYC70iJfBl7iF0za6eIVLZEpxgOi8KIkTWUDvLLH0cRR30+FgyE8wu8gh09ZbYarGyLchmAbbmERHT6jfMgKCob/ZVuh0EzrklMsFj4qdjofejzRvS/arA4HtOS1W1ZBHovPxR36quWB4DO9lvkWakNAgMBAAECggEADiLHizTQq4T12m3UDS6xchColuUO/96RRZd/2nwZTcY5Kk/7W4gMlOVJtugv1WolfNOjXCZavrpXarKTayTBDL/n1QCalXJjGtBxTzr+ouPYLCmgaZDoy9MbFoIWkdHhPnL2BOPpEHoTwbe7pPf53JdN85x2Qr5G08sB5w4LGFBRPAkOoD2LgW4QK7Ut5k4GAHmcn7nqHlkMM8ksdNGkQEhSHfoQOh7FFgR1GHJMeO0JgXprJCIFjyhtyzzDj47r02LWqEaaTdokvkYPEqyzjSI/Gufvq6nvgIlf2mU9KA2NC3VwJrXtAQQezy7yB6blvBceygwXChVKAuUtEyOf2QKBgQDaKC76PlG+p61DIwfIJIcP6JgzWVXj7gJr82hpu8NIf0Wz354eYRl1gdW6dkgpkPhnDvZ/b23P5FsWG8OR0f0VMgPvKVDo+Z+cHLsoroqeCw7B1MEdv2d69gbAk343BTVkTui94152GeCu3O/C0QmaZyqOtumEbxDl8K+lsusEkwKBgQDAI7XzXx+EqKD1m2DKnj2YILEplB4pmJnTWaZi5qAKO+XxbUTGcSxRALBYxuMEvd2AIEk9VhQiLpJ9DoOzVg5LY/zeRvK1vE2tUj/cx8X8jekdY7QayP6KigWbe3juqBHm9eD97wwmQgbFRg+ZZrpKhENym4BVqCuJwc7nC02/3wKBgFIvCyuIqKDraBE/tCHI6PysQhIuGHZKZq0mQHJ8op/AGpEdr+J/7+hdphedkaffDkU81M3DO5oyy8GzaeSYOABxHBxP80QhLejQxg0YW+4X+lI5BRtgzOn8nySTK9wsYSXNuE44xpA1RXh6kPRSbYy2SskCzUvvz2zgVDtXAMRzAoGABItTrFPdnPeYBvcLzP79GfH7/q3AIN+5E6SVKNWnh1ZvU8xvyfm3QJsxkIfzqpMLABLDaK32f/B2CnsjZkRsxh+xRViMOPwhIi5F0GTxK833a3iGM/P5ATHOfCTpcUuMUNgT4C2dLfJ10oibqdciJM0IA1fmKyB1Fyb8S76asiUCgYEAu6ZQ5BfxtbhZCjZ7/hYKHuzDjRzyiaBkKjtN5ZvqLgkG6Zdfjrm2/RcFCMVtqZKXcgVcD9yxy561fmS+5gE3yTfKiFcYoYITT/N26MJ5G7Qeb/FPpmDRVQGjCaAw0o8dvaqe3pUEROdYX+JVQMxQ4WOPiV45FXazAZtFNr8Z+xA=-----END PRIVATE KEY-----"
}
}

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\ZR.Admin.WebApi.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
</system.webServer>
</location>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxQueryString="2097151" maxAllowedContentLength="1073741824" />
</requestFiltering>
</security>
</system.webServer>
</configuration>

View File

@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc;
using ${options.DtosNamespace};
using ${options.ModelsNamespace}.${options.SubNamespace};
using ${options.IServicsNamespace}.${options.SubNamespace}.I${options.SubNamespace}Service;
using ${options.ApiControllerNamespace}.Extensions;
using ${options.ApiControllerNamespace}.Filters;
$if(replaceDto.ShowBtnImport)
using MiniExcelLibs;
@ -171,17 +172,17 @@ $if(replaceDto.ShowBtnImport)
/// <param name="formFile"></param>
/// <returns></returns>
[HttpPost("importData")]
[Log(Title = "${genTable.FunctionName}导入", BusinessType = BusinessType.IMPORT, IsSaveRequestData = false)]
[Log(Title = "${genTable.FunctionName}导入", BusinessType = BusinessType.IMPORT, IsSaveRequestData = false, IsSaveResponseData = true)]
[ActionPermissionFilter(Permission = "${replaceDto.PermissionPrefix}:import")]
public IActionResult ImportData([FromForm(Name = "file")] IFormFile formFile)
{
List<${replaceDto.ModelTypeName}Dto> list = new();
List<${replaceDto.ModelTypeName}> list = new();
using (var stream = formFile.OpenReadStream())
{
list = stream.Query<${replaceDto.ModelTypeName}Dto>(startCell: "A1").ToList();
list = stream.Query<${replaceDto.ModelTypeName}>(startCell: "A1").ToList();
}
return SUCCESS(_${replaceDto.ModelTypeName}Service.Import${replaceDto.ModelTypeName}(list.Adapt<List<${replaceDto.ModelTypeName}>>()));
return SUCCESS(_${replaceDto.ModelTypeName}Service.Import${replaceDto.ModelTypeName}(list));
}
/// <summary>
@ -189,11 +190,11 @@ $if(replaceDto.ShowBtnImport)
/// </summary>
/// <returns></returns>
[HttpGet("importTemplate")]
[Log(Title = "${genTable.FunctionName}模板", BusinessType = BusinessType.EXPORT, IsSaveResponseData = false)]
[Log(Title = "${genTable.FunctionName}模板", BusinessType = BusinessType.EXPORT, IsSaveRequestData = true, IsSaveResponseData = false)]
[AllowAnonymous]
public IActionResult ImportTemplateExcel()
{
var result = DownloadImportTemplate(new List<${replaceDto.ModelTypeName}Dto>() { }, "${replaceDto.ModelTypeName}");
var result = DownloadImportTemplate(new List<${replaceDto.ModelTypeName}>() { }, "${replaceDto.ModelTypeName}");
return ExportExcel(result.Item2, result.Item1);
}
$end

View File

@ -39,7 +39,6 @@ $end
$if(replaceDto.ShowBtnExport)
$if(item.IsExport)
[ExcelColumn(Name = "$if(item.ColumnComment == "")${item.CsharpField}${else}${item.ColumnComment}${end}"$if(item.CsharpType == "DateTime"), Format = "yyyy-MM-dd HH:mm:ss"$end)]
[ExcelColumnName("$if(item.ColumnComment == "")${item.CsharpField}${else}${item.ColumnComment}${end}")]
$else
[ExcelIgnore]
$end

View File

@ -117,7 +117,7 @@ $end
$if(null != genTable.SubTableName && "" != genTable.SubTableName)
return Context.InsertNav(model).Include(s1 => s1.${genTable.SubTable.ClassName}Nav).ExecuteReturnEntity();
$else
return Context.Insertable(model).ExecuteReturnEntity();
return Insertable(model).IgnoreColumns(true).ExecuteReturnEntity();
$end
}

View File

@ -69,14 +69,12 @@ $end
</u--form>
<view class="form-footer">
<view class="btn_wrap">
<view class="btn-item">
<u-button text="取消" shape="circle" type="info" @click="handleCancel"></u-button>
</view>
<view class="btn-item" v-if="opertype != 3">
<u-button text="确定" shape="circle" type="primary" @click="submit"></u-button>
</view>
<view class="btn-group">
<view class="btn-item">
<u-button text="取消" shape="circle" icon="close" type="info" @click="handleCancel"></u-button>
</view>
<view class="btn-item" v-if="opertype != 3">
<u-button text="确定" shape="circle" icon="checkmark" type="primary" @click="submit"></u-button>
</view>
</view>
</view>
@ -112,6 +110,7 @@ $end
return {
form: {},
rules: {
xxx: {},
$foreach(column in genTable.Columns)
$if(column.IsRequired && column.IsIncrement == false)
${column.CsharpFieldFl}: [{
@ -186,10 +185,12 @@ $end
update${genTable.BusinessName}(this.form).then((res) => {
this.${modal}modal.msgSuccess("修改成功")
})
.catch(() => {})
} else {
add${genTable.BusinessName}(this.form).then((res) => {
this.${modal}modal.msgSuccess("新增成功")
})
.catch(() => {})
}
}).catch(errors => {
this.${modal}modal.msg('表单校验失败')

View File

@ -197,7 +197,7 @@ $elseif(tool.CheckTree(genTable ,column.CsharpField))
</el-form-item>
</el-col>
$elseif(column.IsPK || column.IsIncrement)
<el-col :lg="${options.ColNum}">
<el-col :lg="12">
<el-form-item label="${labelName}" prop="${columnName}">
$if(column.IsIncrement == false)
<el-input-number v-model.number="form.${columnName}" controls-position="right" placeholder="请输入${labelName}" :disabled="title=='修改数据'"/>
@ -208,13 +208,13 @@ $end
</el-col>
$else
$if(column.HtmlType == "inputNumber")
<el-col :lg="${options.ColNum}">
<el-col :lg="12">
<el-form-item label="${labelName}" prop="${columnName}">
<el-input-number v-model.number="form.${columnName}" controls-position="right" placeholder="请输入${labelName}" ${labelDisabled}/>
</el-form-item>
</el-col>
$elseif(column.HtmlType == "datetime")
<el-col :lg="${options.ColNum}">
<el-col :lg="12">
<el-form-item label="${labelName}" prop="${columnName}">
<el-date-picker v-model="form.${columnName}" type="datetime" placeholder="选择日期时间"></el-date-picker>
</el-form-item>
@ -232,7 +232,7 @@ $elseif(column.HtmlType == "fileUpload")
</el-form-item>
</el-col>
$elseif(column.HtmlType == "radio")
<el-col :lg="${options.ColNum}">
<el-col :lg="12">
<el-form-item label="${labelName}" prop="${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>
@ -252,7 +252,7 @@ $elseif(column.HtmlType == "editor")
</el-form-item>
</el-col>
$elseif(column.HtmlType == "select" || column.HtmlType == "selectMulti")
<el-col :lg="${options.ColNum}">
<el-col :lg="12">
<el-form-item label="${labelName}" prop="${columnName}">
<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>
@ -268,7 +268,7 @@ $elseif(column.HtmlType == "checkbox")
</el-form-item>
</el-col>
$else
<el-col :lg="${options.ColNum}">
<el-col :lg="12">
<el-form-item label="${labelName}" prop="${columnName}">
<el-input v-model="form.${columnName}" placeholder="请输入${labelName}" ${labelDisabled}/>
</el-form-item>
@ -301,8 +301,10 @@ import Editor from '@/components/Editor'
$end
const { proxy } = getCurrentInstance()
// 是否展开,默认全部折叠
const isExpandAll = ref(false)
const refreshTable = ref(true)
// 展开/折叠操作
function toggleExpandAll() {
refreshTable.value = false
isExpandAll.value = !isExpandAll.value

View File

@ -53,7 +53,7 @@ $elseif(column.HtmlType == "datePicker")
:default-time="defaultTime">
</el-date-picker>
</el-form-item>
$elseif(column.HtmlType.Contains("select"))
$elseif(column.HtmlType.Contains("select") || column.HtmlType == "radio")
<el-form-item label="${labelName}" prop="${columnName}">
<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">
@ -65,7 +65,6 @@ $elseif(column.HtmlType.Contains("select"))
$elseif(column.HtmlType == "radio")
<el-form-item label="${labelName}" prop="${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-group>
</el-form-item>
@ -82,7 +81,7 @@ $end
</el-form-item>
</el-form>
<!-- 工具区域 -->
<el-row :gutter="15" class="mb10">
<el-row :gutter="10" class="mb8">
$if(replaceDto.ShowBtnAdd)
<el-col :span="1.5">
<el-button type="primary" v-hasPermi="['${replaceDto.PermissionPrefix}:add']" plain icon="plus" @click="handleAdd">
@ -188,7 +187,7 @@ $elseif(column.HtmlType == "imageUpload")
$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}')">
<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" || column.HtmlType == "selectMulti")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")split=","$end />
</template>
</el-table-column>
$elseif(column.HtmlType == "datetime")
@ -214,13 +213,13 @@ $end
</el-button-group>
$else
$if(replaceDto.ShowBtnView)
<el-button type="primary" size="small" icon="view" title="详情" @click="handlePreview(scope.row)"></el-button>
<el-button type="primary" icon="view" title="详情" @click="handlePreview(scope.row)"></el-button>
$end
$if(replaceDto.ShowBtnEdit)
<el-button type="success" size="small" icon="edit" title="编辑" v-hasPermi="['${replaceDto.PermissionPrefix}:edit']" @click="handleUpdate(scope.row)"></el-button>
<el-button v-hasPermi="['${replaceDto.PermissionPrefix}:edit']" type="success" icon="edit" title="编辑" @click="handleUpdate(scope.row)"></el-button>
$end
$if(replaceDto.ShowBtnDelete)
<el-button type="danger" size="small" icon="delete" title="删除" v-hasPermi="['${replaceDto.PermissionPrefix}:delete']" @click="handleDelete(scope.row)"></el-button>
<el-button v-hasPermi="['${replaceDto.PermissionPrefix}:delete']" type="danger" icon="delete" title="删除" @click="handleDelete(scope.row)"></el-button>
$end
$end
</template>
@ -327,16 +326,10 @@ $elseif(column.HtmlType == "editor")
<editor v-model="form.${columnName}" :min-height="200" />
</el-form-item>
</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")
<el-col :lg="${options.ColNum}">
<el-form-item label="${labelName}" prop="${columnName}">
<el-select v-model="form.${columnName}$if(column.HtmlType == "selectMulti")Checked$end" $if(column.HtmlType == "selectMulti")multiple$end 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"
@ -449,9 +442,7 @@ $end
</el-form>
<template #footer v-if="opertype != 3">
<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>
$end
</template>
</el-dialog>
</div>
@ -663,7 +654,7 @@ function cancel(){
function reset() {
form.value = {
$foreach(item in genTable.Columns)
$if(item.HtmlType == "checkbox" || item.HtmlType == "selectMulti")
$if((item.HtmlType == "checkbox"))
${item.CsharpFieldFl}Checked: [],
$else
$item.CsharpFieldFl: null,
@ -675,7 +666,7 @@ $if(sub)
$end
proxy.resetForm("formRef")
}
$if(replaceDto.ShowBtnAdd)
// 添加按钮操作
function handleAdd() {
reset();
@ -683,8 +674,7 @@ function handleAdd() {
title.value = '添加${genTable.functionName}'
opertype.value = 1
}
$end
$if(replaceDto.ShowBtnEdit)
// 修改按钮操作
function handleUpdate(row) {
reset()
@ -699,7 +689,7 @@ function handleUpdate(row) {
form.value = {
...data,
$foreach(item in genTable.Columns)
$if(item.HtmlType == "checkbox" || item.HtmlType == "selectMulti")
$if(item.HtmlType == "checkbox")
${item.CsharpFieldFl}Checked: data.${item.CsharpFieldFl} ? data.${item.CsharpFieldFl}.split(',') : [],
$end
$end
@ -710,14 +700,13 @@ $end
}
})
}
$end
// 添加&修改 表单提交
function submitForm() {
proxy.${refs}refs["formRef"].validate((valid) => {
if (valid) {
$foreach(item in genTable.Columns)
$if(item.HtmlType == "checkbox" || item.HtmlType == "selectMulti")
$if(item.HtmlType == "checkbox")
form.value.${item.CsharpFieldFl} = form.value.${item.CsharpFieldFl}Checked.toString();
$end
$end
@ -726,27 +715,24 @@ $if(sub)
form.value.${tool.FirstLowerCase(genTable.SubTable.ClassName)}Nav = ${tool.FirstLowerCase(genTable.SubTable.ClassName)}List.value
$end
if (form.value.${replaceDto.FistLowerPk} != undefined && opertype.value === 2) {
$if(replaceDto.ShowBtnEdit)
update${genTable.BusinessName}(form.value).then((res) => {
proxy.${modal}modal.msgSuccess("修改成功")
open.value = false
getList()
})
$end
.catch(() => {})
} else {
$if(replaceDto.ShowBtnAdd)
add${genTable.BusinessName}(form.value).then((res) => {
proxy.${modal}modal.msgSuccess("新增成功")
open.value = false
getList()
})
$end
.catch(() => {})
}
}
})
}
$if(replaceDto.ShowBtnMultiDel || replaceDto.ShowBtnDelete)
// 删除按钮操作
function handleDelete(row) {
const Ids = row.${replaceDto.FistLowerPk} || ids.value
@ -760,8 +746,8 @@ function handleDelete(row) {
getList()
proxy.${modal}modal.msgSuccess("删除成功")
})
.catch(() => {})
}
$end
$if(replaceDto.ShowBtnTruncate)
// 清空
@ -792,7 +778,7 @@ function handlePreview(row) {
open.value = true
title.value = '查看'
opertype.value = 3
form.value = { ...row }
form.value = row
}
$end

Binary file not shown.

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