✨ feat(init): init
This commit is contained in:
commit
42a4e76d33
|
@ -0,0 +1,30 @@
|
||||||
|
**/.classpath
|
||||||
|
**/.dockerignore
|
||||||
|
**/.env
|
||||||
|
**/.git
|
||||||
|
**/.gitignore
|
||||||
|
**/.project
|
||||||
|
**/.settings
|
||||||
|
**/.toolstarget
|
||||||
|
**/.vs
|
||||||
|
**/.vscode
|
||||||
|
**/*.*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
|
||||||
|
!**/.gitignore
|
||||||
|
!.git/HEAD
|
||||||
|
!.git/config
|
||||||
|
!.git/packed-refs
|
||||||
|
!.git/refs/heads/**
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Build and Release Folders
|
||||||
|
bin-debug/
|
||||||
|
bin-release/
|
||||||
|
[Oo]bj/
|
||||||
|
[Bb]in/
|
||||||
|
|
||||||
|
# Other files and folders
|
||||||
|
.settings/
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.swf
|
||||||
|
*.air
|
||||||
|
*.ipa
|
||||||
|
*.apk
|
||||||
|
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# .vs
|
||||||
|
# .idea
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
**/bin
|
||||||
|
**/obj
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Rider ignored files
|
||||||
|
/modules.xml
|
||||||
|
/.idea.Bunny.WebApi.iml
|
||||||
|
/contentModel.xml
|
||||||
|
/projectSettingsUpdater.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
|
@ -0,0 +1 @@
|
||||||
|
Bunny.WebApi
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
</project>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="UserContentModel">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,87 @@
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
namespace Bunny.Common;
|
||||||
|
|
||||||
|
public class AppSettings
|
||||||
|
{
|
||||||
|
public AppSettings(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
Configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IConfiguration Configuration { get; set; } = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 封装要操作的字符
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sections">节点配置</param>
|
||||||
|
public static string? App(params string[] sections)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (sections.Any()) return Configuration[string.Join(":", sections)];
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 递归获取配置信息数组
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="sections"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static List<T> App<T>(params string[] sections)
|
||||||
|
{
|
||||||
|
List<T> list = [];
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (sections.Any()) Configuration.Bind(string.Join(":", sections), list);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T Bind<T>(string key, T t)
|
||||||
|
{
|
||||||
|
Configuration.Bind(key, t);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static T GetAppConfig<T>(string key, T? defaultValue = default)
|
||||||
|
{
|
||||||
|
var setting = (T)Convert.ChangeType(Configuration[key], typeof(T));
|
||||||
|
var value = setting;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取配置文件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">eg: WeChat:Token</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetConfig(string key)
|
||||||
|
{
|
||||||
|
return Configuration[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取配置节点并转换成指定类型
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">节点类型</typeparam>
|
||||||
|
/// <param name="key">节点路径</param>
|
||||||
|
/// <returns>节点类型实例</returns>
|
||||||
|
public static T Get<T>(string key)
|
||||||
|
{
|
||||||
|
return Configuration.GetSection(key).Get<T>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.6.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Bunny.Dao\Bunny.Dao.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,28 @@
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace Bunny.Common.Exception;
|
||||||
|
|
||||||
|
public class BunnyException : System.Exception
|
||||||
|
{
|
||||||
|
public HttpStatusCode Code;
|
||||||
|
public new string Message;
|
||||||
|
|
||||||
|
public BunnyException(string message)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
Code = HttpStatusCode.InternalServerError;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BunnyException(HttpStatusCode code, string message)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
Code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BunnyException(string message, System.Exception innerException, HttpStatusCode code) : base(message,
|
||||||
|
innerException)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
Code = code;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
using Bunny.Common.Exception;
|
||||||
|
using Bunny.Dao.Entity.Constant;
|
||||||
|
using Bunny.Dao.Result;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Bunny.Common.Filter;
|
||||||
|
|
||||||
|
public class GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger) : IExceptionFilter
|
||||||
|
{
|
||||||
|
public void OnException(ExceptionContext context)
|
||||||
|
{
|
||||||
|
// 错误的消息
|
||||||
|
var message = context.Exception.Message;
|
||||||
|
|
||||||
|
|
||||||
|
// 异常返回结果包装 也可以自定自定义返回类型
|
||||||
|
var result = Result<string>.Error(ExceptionConstant.ServerError);
|
||||||
|
|
||||||
|
// 如果是自己自定义的异常
|
||||||
|
if (context.Exception is BunnyException)
|
||||||
|
{
|
||||||
|
var contextException = (BunnyException)context.Exception;
|
||||||
|
message = contextException.Message;
|
||||||
|
result = Result<string>.Error(contextException.Code, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 日志记录
|
||||||
|
logger.LogError(context.Exception, message);
|
||||||
|
|
||||||
|
// 重新复写返回结果
|
||||||
|
context.Result = new ObjectResult(result);
|
||||||
|
context.ExceptionHandled = true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Mail;
|
||||||
|
using System.Net.Mime;
|
||||||
|
using System.Text;
|
||||||
|
using Bunny.Dao.Entity.Email;
|
||||||
|
|
||||||
|
namespace Bunny.Common.Utils;
|
||||||
|
|
||||||
|
public class EmailUtil
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 发送邮件,建议使用QQ端口587,465发送不成功
|
||||||
|
/// </summary>
|
||||||
|
public static void SendEmail(EmailSendEntity entity)
|
||||||
|
{
|
||||||
|
// 创建邮件发送对象
|
||||||
|
var smtpClient = new SmtpClient();
|
||||||
|
|
||||||
|
// 设置发送者邮件和接受者邮件
|
||||||
|
var sendMailAddress = new MailAddress(entity.SendEmail);
|
||||||
|
var receiverMailAddress = new MailAddress(entity.ReceiverEmail);
|
||||||
|
|
||||||
|
// 创建邮件发送消息
|
||||||
|
var mailMessage = new MailMessage(sendMailAddress, receiverMailAddress)
|
||||||
|
{
|
||||||
|
Subject = entity.Subject,
|
||||||
|
SubjectEncoding = Encoding.UTF8,
|
||||||
|
Body = entity.Body,
|
||||||
|
BodyEncoding = Encoding.UTF8,
|
||||||
|
IsBodyHtml = entity.IsHtml
|
||||||
|
};
|
||||||
|
|
||||||
|
// 判断是否有抄送
|
||||||
|
if (entity.CC != null)
|
||||||
|
{
|
||||||
|
var ccArray = entity.CC.Split(',');
|
||||||
|
// 是抄送
|
||||||
|
if (entity.IsBbc == false)
|
||||||
|
foreach (var ccAddress in ccArray)
|
||||||
|
mailMessage.CC.Add(new MailAddress(ccAddress));
|
||||||
|
|
||||||
|
// 是密送
|
||||||
|
else
|
||||||
|
foreach (var ccAddress in ccArray)
|
||||||
|
mailMessage.Bcc.Add(new MailAddress(ccAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 如果设置了富文本
|
||||||
|
if (entity.IsHtml)
|
||||||
|
{
|
||||||
|
// 设置HTML文本
|
||||||
|
var htmlView = AlternateView
|
||||||
|
.CreateAlternateViewFromString(entity.Body, Encoding.UTF8, MediaTypeNames.Text.Html);
|
||||||
|
|
||||||
|
// 添加富文本视图到邮件消息
|
||||||
|
mailMessage.AlternateViews.Add(htmlView);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建SMTP客户端并发送邮件
|
||||||
|
smtpClient.Credentials = new NetworkCredential(entity.SendEmail, entity.SendEmailPassword);
|
||||||
|
smtpClient.EnableSsl = true;
|
||||||
|
smtpClient.Port = entity.SmtpPort;
|
||||||
|
smtpClient.Host = entity.SmtpService;
|
||||||
|
|
||||||
|
// 发送邮件
|
||||||
|
smtpClient.Send(mailMessage);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System.Net;
|
||||||
|
using Bunny.Common.Exception;
|
||||||
|
|
||||||
|
namespace Bunny.Common.Utils;
|
||||||
|
|
||||||
|
public class EmptyUtil
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 判断对象或者字符串是否为空
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">传入的对象</param>
|
||||||
|
/// <param name="code">出错的状态码</param>
|
||||||
|
/// <param name="message">出错的消息</param>
|
||||||
|
public static void CheckIfNullOrEmpty(object? obj, HttpStatusCode code, string message)
|
||||||
|
{
|
||||||
|
if (obj == null || (obj is string s && string.IsNullOrEmpty(s))) throw new BunnyException(code, message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
|
namespace Bunny.Common.Utils.Jwt;
|
||||||
|
|
||||||
|
public static partial class JwtUtil
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 生成token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">实体类数据</param>
|
||||||
|
/// <param name="day">多少天之后过期</param>
|
||||||
|
/// <returns>生成好的token</returns>
|
||||||
|
public static string GenerateJwt<T>(T data, int day = Day)
|
||||||
|
{
|
||||||
|
if (data == null) return "";
|
||||||
|
// 初始化字典
|
||||||
|
var propertyInfos = data.GetType().GetProperties();
|
||||||
|
var claims = (from propertyInfo in propertyInfos
|
||||||
|
let name = propertyInfo.Name
|
||||||
|
let value = propertyInfo.GetValue(data)?.ToString()
|
||||||
|
select value.IsNullOrEmpty() ? new Claim(name, "") : new Claim(name, value!)).ToList();
|
||||||
|
|
||||||
|
// 生成token
|
||||||
|
var token = new JwtSecurityToken(expires: DateTime.UtcNow.AddDays(day), claims: claims);
|
||||||
|
|
||||||
|
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生成token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="claims">负载,放入你的内容</param>
|
||||||
|
/// <param name="day">多少天之后过期</param>
|
||||||
|
/// <returns>生成好的token</returns>
|
||||||
|
public static string GenerateJwt(IEnumerable<Claim>? claims = default, int day = Day)
|
||||||
|
{
|
||||||
|
var token = new JwtSecurityToken(expires: DateTime.UtcNow.AddDays(day), claims: claims);
|
||||||
|
|
||||||
|
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生成token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jwtKey">生成JWT的key</param>
|
||||||
|
/// <param name="claims">负载,放入你的内容</param>
|
||||||
|
/// <param name="day">多少天之后过期</param>
|
||||||
|
/// <returns>生成好的token</returns>
|
||||||
|
public static string GenerateJwt(string jwtKey = JwtKey, IEnumerable<Claim>? claims = default, int day = Day)
|
||||||
|
{
|
||||||
|
var token = new JwtSecurityToken(
|
||||||
|
expires: DateTime.UtcNow.AddDays(day),
|
||||||
|
claims: claims,
|
||||||
|
signingCredentials: Credentials(jwtKey));
|
||||||
|
|
||||||
|
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生成token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="issuer">授权</param>
|
||||||
|
/// <param name="audience">认证</param>
|
||||||
|
/// <param name="jwtKey">生成JWT的key</param>
|
||||||
|
/// <param name="claims">负载,放入你的内容</param>
|
||||||
|
/// <param name="day">多少天之后过期</param>
|
||||||
|
/// <returns>生成好的token</returns>
|
||||||
|
public static string GenerateJwt(string issuer, string audience, string jwtKey = JwtKey,
|
||||||
|
IEnumerable<Claim>? claims = default, int day = Day)
|
||||||
|
{
|
||||||
|
var token = new JwtSecurityToken(
|
||||||
|
issuer,
|
||||||
|
audience,
|
||||||
|
expires: DateTime.UtcNow.AddDays(day),
|
||||||
|
claims: claims,
|
||||||
|
signingCredentials: Credentials(jwtKey));
|
||||||
|
|
||||||
|
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,188 @@
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using Bunny.Common.Exception;
|
||||||
|
using Bunny.Dao.Entity.Constant;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Bunny.Common.Utils.Jwt;
|
||||||
|
|
||||||
|
public static partial class JwtUtil
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 返回所有的 jwt
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="token">token解析</param>
|
||||||
|
public static JwtSecurityToken JwtParse(string token)
|
||||||
|
{
|
||||||
|
if (token == null) return null;
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
return tokenHandler.ReadJwtToken(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回发行者
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jwt">token解析</param>
|
||||||
|
public static string JwtParseIssuer(string jwt)
|
||||||
|
{
|
||||||
|
return JwtParse(jwt).Issuer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回授权者
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jwt">token解析</param>
|
||||||
|
public static IEnumerable<string> JwtParseAudiences(string jwt)
|
||||||
|
{
|
||||||
|
return JwtParse(jwt).Audiences;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回负载中内容
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jwt">jwt 解析</param>
|
||||||
|
public static IEnumerable<Claim> JwtParseClaim(string jwt)
|
||||||
|
{
|
||||||
|
return JwtParse(jwt).Claims;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将生成的JWT值转成字典格式
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jwt"></param>
|
||||||
|
/// <returns>Dictionary</returns>
|
||||||
|
public static Dictionary<string, object> ParseJwtToDictionary(string jwt)
|
||||||
|
{
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
var jwtSecurityToken = tokenHandler.ReadJwtToken(jwt);
|
||||||
|
|
||||||
|
// 初始化字典
|
||||||
|
var dictionary = new Dictionary<string, object>();
|
||||||
|
var jClaims = jwtSecurityToken.Claims;
|
||||||
|
|
||||||
|
// 将得到的每一个值都放入字典中
|
||||||
|
foreach (var claim in jClaims) dictionary[claim.Type] = claim.Value;
|
||||||
|
|
||||||
|
// 返回字典
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将生成的 JwtSecurityToken 值转成字典格式
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jwtToken"></param>
|
||||||
|
/// <returns>Dictionary</returns>
|
||||||
|
public static Dictionary<string, object> ParseJwtToDictionary(JwtSecurityToken jwtToken)
|
||||||
|
{
|
||||||
|
// 初始化字典
|
||||||
|
var dictionary = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
// 将得到的每一个值都放入字典中
|
||||||
|
foreach (var claim in jwtToken.Claims) dictionary[claim.Type] = claim.Value;
|
||||||
|
|
||||||
|
// 返回字典
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将 jwt 转成 实体类
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jwt">jwt</param>
|
||||||
|
/// <returns>实体类</returns>
|
||||||
|
public static T? ParseJwtToObject<T>(string jwt)
|
||||||
|
{
|
||||||
|
if (jwt == null) throw new BunnyException(ExceptionConstant.TokenIsEmpty);
|
||||||
|
var dictionary = new Dictionary<string, object>();
|
||||||
|
// 将 jwt 转成 Claim
|
||||||
|
var claims = JwtParseClaim(jwt);
|
||||||
|
|
||||||
|
// 找到对应的字典 Dictionary
|
||||||
|
foreach (var claim in claims) dictionary[claim.Type] = claim.Value;
|
||||||
|
|
||||||
|
// 返回实体类
|
||||||
|
var json = JsonConvert.SerializeObject(dictionary);
|
||||||
|
// 将JSON转为对象
|
||||||
|
return JsonConvert.DeserializeObject<T>(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将 JSON 转成 Dictionary
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jsonString"></param>
|
||||||
|
/// <returns>Dictionary</returns>
|
||||||
|
public static Dictionary<string, object> ParseJsonToDictionary(string jsonString)
|
||||||
|
{
|
||||||
|
// 初始化字典
|
||||||
|
var dictionary = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
// 将JSON转成字典格式
|
||||||
|
var claims = ParseJsonToClaimEnumerable(jsonString);
|
||||||
|
|
||||||
|
// 将得到的每一个值都放入字典中
|
||||||
|
foreach (var claim in claims) dictionary[claim.Type] = claim.Value;
|
||||||
|
|
||||||
|
// 返回 Dictionary
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将 JSON 转成 Claim
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jsonString"></param>
|
||||||
|
/// <returns>Claim</returns>
|
||||||
|
public static IEnumerable<Claim> ParseJsonToClaimEnumerable(string jsonString)
|
||||||
|
{
|
||||||
|
// 将JSON转成字典格式
|
||||||
|
var deserializeWithDictionary = JsonConvert.DeserializeObject<IDictionary<string, object>>(jsonString);
|
||||||
|
|
||||||
|
// 返回Claim
|
||||||
|
return deserializeWithDictionary!.Select(item =>
|
||||||
|
{
|
||||||
|
if (item.Value == null)
|
||||||
|
return new Claim(item.Key, string.Empty);
|
||||||
|
return new Claim(item.Key, item.Value.ToString()!);
|
||||||
|
})
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将 JSON 转成 实体类
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jsonString">JSON字符串</param>
|
||||||
|
/// <returns>实体类</returns>
|
||||||
|
public static T? ParseJsonToObject<T>(string jsonString)
|
||||||
|
{
|
||||||
|
// 将JSON转成字典格式
|
||||||
|
var claims = ParseJsonToClaimEnumerable(jsonString);
|
||||||
|
|
||||||
|
// 初始化字典
|
||||||
|
var dictionary = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
// 将得到的每一个值都放入字典中
|
||||||
|
foreach (var claim in claims) dictionary[claim.Type] = claim.Value;
|
||||||
|
|
||||||
|
// 返回实体类
|
||||||
|
var json = JsonConvert.SerializeObject(dictionary);
|
||||||
|
// 将JSON转为对象
|
||||||
|
return JsonConvert.DeserializeObject<T>(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将 Claim 抓成 Dictionary
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="claims"></param>
|
||||||
|
/// <returns>Dictionary</returns>
|
||||||
|
public static Dictionary<string, object> ParseClaimToDictionary(IEnumerable<Claim> claims)
|
||||||
|
{
|
||||||
|
// 初始化字典
|
||||||
|
var dictionary = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
// 将得到的每一个值都放入字典中
|
||||||
|
foreach (var claim in claims) dictionary[claim.Type] = claim.Value;
|
||||||
|
|
||||||
|
// 返回字典格式
|
||||||
|
return dictionary;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Bunny.Common.Utils.Jwt;
|
||||||
|
|
||||||
|
public static partial class JwtUtil
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 默认发布者
|
||||||
|
/// </summary>
|
||||||
|
private const string JwtKey = "这你放的是你的秘钥,必须要256位以上,否则会报错";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认发布者
|
||||||
|
/// </summary>
|
||||||
|
private const string Issuer = "Bunny-Issuer";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认接受者
|
||||||
|
/// </summary>
|
||||||
|
private const string Audience = "Bunny-Audience";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认有效时间,按天计算
|
||||||
|
/// </summary>
|
||||||
|
private const int Day = 7;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断是否过期
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jwtToken">token</param>
|
||||||
|
/// <returns>过期:true,未过期:false</returns>
|
||||||
|
public static bool IsJwtExpired(string? jwtToken)
|
||||||
|
{
|
||||||
|
var tokenHandler = new JwtSecurityTokenHandler();
|
||||||
|
|
||||||
|
// Token无效直接返回过期
|
||||||
|
if (tokenHandler.ReadToken(jwtToken) is not JwtSecurityToken jwtTokenObj) return true;
|
||||||
|
|
||||||
|
// 判断是否过期
|
||||||
|
return DateTime.UtcNow > jwtTokenObj.ValidTo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生成 SigningCredentials
|
||||||
|
/// </summary>
|
||||||
|
private static SigningCredentials Credentials(string jwtKey)
|
||||||
|
{
|
||||||
|
var symmetricSecurityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey));
|
||||||
|
return new SigningCredentials(symmetricSecurityKey, SecurityAlgorithms.HmacSha256Signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将字典转为实体类
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dictionary">字典</param>
|
||||||
|
/// <returns>所对应的实体类</returns>
|
||||||
|
// public static T? ConvertDictionaryToObject(Dictionary<string, object> dictionary)
|
||||||
|
// {
|
||||||
|
// var json = JsonConvert.SerializeObject(dictionary);
|
||||||
|
// // 将JSON转为对象
|
||||||
|
// return JsonConvert.DeserializeObject<T>(json);
|
||||||
|
// }
|
||||||
|
public static T? ConvertDictionaryToObject<T>(Dictionary<string, object> dictionary)
|
||||||
|
{
|
||||||
|
var json = JsonConvert.SerializeObject(dictionary);
|
||||||
|
// 将JSON转为对象
|
||||||
|
return JsonConvert.DeserializeObject<T>(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将对象转为Claim
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="object">要转换的对象</param>
|
||||||
|
/// <returns>Claim</returns>
|
||||||
|
public static IEnumerable<Claim> CreateClaim<T>(T @object)
|
||||||
|
{
|
||||||
|
// 将对象序列化成JSON
|
||||||
|
var json = JsonConvert.SerializeObject(@object);
|
||||||
|
|
||||||
|
// 将JSON复制给Claim
|
||||||
|
return ParseJsonToClaimEnumerable(json);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Bunny.Common.Utils.Net;
|
||||||
|
|
||||||
|
public static partial class NetUtil
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// MD5 加密32位-大写
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="password">密码</param>
|
||||||
|
/// <returns>加密后结果</returns>
|
||||||
|
public static string Encryption32(string password)
|
||||||
|
{
|
||||||
|
var md5 = new MD5CryptoServiceProvider();
|
||||||
|
var bytes = Encoding.UTF8.GetBytes(password);
|
||||||
|
var result = BitConverter.ToString(md5.ComputeHash(bytes));
|
||||||
|
return result.Replace("-", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// md5 加密32位-小写
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="password">密码</param>
|
||||||
|
/// <returns>加密后结果</returns>
|
||||||
|
public static string Encryption32Lower(string password)
|
||||||
|
{
|
||||||
|
var md5 = new MD5CryptoServiceProvider();
|
||||||
|
var bytes = Encoding.UTF8.GetBytes(password);
|
||||||
|
var result = BitConverter.ToString(md5.ComputeHash(bytes));
|
||||||
|
return result.Replace("-", "").ToLower();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace Bunny.Common.Utils.Net;
|
||||||
|
|
||||||
|
public static partial class NetUtil
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 仿写java对象拷贝
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source"></param>
|
||||||
|
/// <param name="target"></param>
|
||||||
|
/// <typeparam name="T1"></typeparam>
|
||||||
|
/// <typeparam name="T2"></typeparam>
|
||||||
|
public static void CopyProperties<T1, T2>(T1 source, T2 target)
|
||||||
|
{
|
||||||
|
var sourceProperties = source!.GetType().GetProperties();
|
||||||
|
var targetProperties = target!.GetType().GetProperties();
|
||||||
|
|
||||||
|
foreach (var sourceProperty in sourceProperties)
|
||||||
|
{
|
||||||
|
var targetProperty = targetProperties.FirstOrDefault(x =>
|
||||||
|
x.Name == sourceProperty.Name && x.PropertyType == sourceProperty.PropertyType);
|
||||||
|
|
||||||
|
if (targetProperty == null || !targetProperty.CanWrite) continue;
|
||||||
|
var value = sourceProperty.GetValue(source);
|
||||||
|
targetProperty.SetValue(target, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取token
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">请求</param>
|
||||||
|
/// <returns>token值</returns>
|
||||||
|
public static string? GetToken(HttpRequest request)
|
||||||
|
{
|
||||||
|
return request.Headers["token"];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取请求路径
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">请求</param>
|
||||||
|
public static string? GetRequestPath(HttpRequest request)
|
||||||
|
{
|
||||||
|
return request.Path.Value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
|
namespace Bunny.Common.Utils.Net;
|
||||||
|
|
||||||
|
public static partial class NetUtil
|
||||||
|
{
|
||||||
|
public static List<string> Ignores = ["/api/Login"];
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前路径是否匹配ant path路径
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pattern">ant path值</param>
|
||||||
|
/// <param name="path">当前路径</param>
|
||||||
|
/// <returns>是否匹配</returns>
|
||||||
|
public static bool AntPath(string pattern, string? path = "")
|
||||||
|
{
|
||||||
|
// 全部转成小写
|
||||||
|
path = path!.ToLower();
|
||||||
|
pattern = pattern!.ToLower();
|
||||||
|
|
||||||
|
// Ant Path匹配规则的正则表达式
|
||||||
|
var antPattern = "^" + Regex.Escape(pattern)
|
||||||
|
.Replace("\\?", ".")
|
||||||
|
.Replace("\\*", ".*")
|
||||||
|
.Replace("\\**", ".*") + "$";
|
||||||
|
|
||||||
|
// 判断匹配规则是否成立
|
||||||
|
return path.IsNullOrEmpty() ? Regex.IsMatch("", antPattern) : Regex.IsMatch(path!, antPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否匹配当前路径,同时判断是否在忽略列表中
|
||||||
|
/// 如果在忽略列表中,则不进行判断
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool MatchPath(string? path)
|
||||||
|
{
|
||||||
|
// 如果当前路径是在忽略列表中,返回未匹配
|
||||||
|
return !Ignores.Select(ignore => AntPath(ignore, path)).Any(isMatch => isMatch);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
using System.Net;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
|
namespace Bunny.Common.Utils.Net;
|
||||||
|
|
||||||
|
public static partial class NetUtil
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获取请求的IP地址
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">HttpContext 上下问对象</param>
|
||||||
|
/// <param name="getIpv4AddressType">是否获取IPv4地址,默认为IPv4</param>
|
||||||
|
/// <returns>IP地址</returns>
|
||||||
|
public static string GetRequestIpAddress(HttpContext context, bool getIpv4AddressType = true)
|
||||||
|
{
|
||||||
|
// 获取IP地址
|
||||||
|
var ipAddress = context.Connection.RemoteIpAddress!.ToString();
|
||||||
|
// 转换IP地址格式
|
||||||
|
var ip = IPAddress.Parse(ipAddress);
|
||||||
|
// 转换成IPv4
|
||||||
|
var toIPv4 = ip.MapToIPv4().ToString();
|
||||||
|
// 转换成IPv6
|
||||||
|
var toIPv6 = ip.MapToIPv6().ToString();
|
||||||
|
|
||||||
|
// 如果获取IPv4地址
|
||||||
|
ipAddress = getIpv4AddressType ? toIPv4 : toIPv6;
|
||||||
|
|
||||||
|
// 如果有IP直接返回
|
||||||
|
return ipAddress.IsNullOrEmpty() ? "" : ipAddress;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
using System.Net;
|
||||||
|
using Bunny.Dao.Result;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Bunny.Common.Utils.Net;
|
||||||
|
|
||||||
|
public static partial class NetUtil
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义错误消息和内容
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code"></param>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static JsonResult Out(int code, string message)
|
||||||
|
{
|
||||||
|
// 生成处理结果
|
||||||
|
var result = Result<string>.Error(code, message);
|
||||||
|
|
||||||
|
// 返回处理结果
|
||||||
|
return new JsonResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义错误消息和内容
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code"></param>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static JsonResult Out(HttpStatusCode code, string message)
|
||||||
|
{
|
||||||
|
// 生成处理结果
|
||||||
|
var result = Result<string>.Error(code, message);
|
||||||
|
|
||||||
|
// 返回处理结果
|
||||||
|
return new JsonResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认传入code是500
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">自定义错误消息</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static JsonResult Out(string message)
|
||||||
|
{
|
||||||
|
// 生成处理结果
|
||||||
|
var result = Result<string>.Error(HttpStatusCode.InternalServerError, message);
|
||||||
|
|
||||||
|
// 返回处理结果
|
||||||
|
return new JsonResult(result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="2.2.0"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,24 @@
|
||||||
|
namespace Bunny.Dao.Dto.Email;
|
||||||
|
|
||||||
|
public class EmailTemplateDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 模板名称
|
||||||
|
/// </summary>
|
||||||
|
public string? TemplateName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 主题
|
||||||
|
/// </summary>
|
||||||
|
public string? Subject { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 邮件内容
|
||||||
|
/// </summary>
|
||||||
|
public string? Body { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 邮件类型
|
||||||
|
/// </summary>
|
||||||
|
public string? Type { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
namespace Bunny.Dao.Dto.Email;
|
||||||
|
|
||||||
|
public class EmailUsersDto
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 模板名称
|
||||||
|
/// </summary>
|
||||||
|
public string? TemplateName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 主题
|
||||||
|
/// </summary>
|
||||||
|
public string? Subject { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 邮件内容
|
||||||
|
/// </summary>
|
||||||
|
public string? Body { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 邮件类型
|
||||||
|
/// </summary>
|
||||||
|
public string? Type { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Bunny.Dao.Dto.User;
|
||||||
|
|
||||||
|
public class LoginDto
|
||||||
|
{
|
||||||
|
public string? Username { get; set; }
|
||||||
|
public string? Password { get; set; }
|
||||||
|
public string? EmailCode { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
namespace Bunny.Dao.Entity.Base;
|
||||||
|
|
||||||
|
public class BaseEntity
|
||||||
|
{
|
||||||
|
public string? Id { get; set; }
|
||||||
|
|
||||||
|
public DateTime CreateTime { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// public DateTime UpdateTime { get; set; }
|
||||||
|
// public long CreateUserId { get; set; }
|
||||||
|
// public long UpdateUserId { get; set; }
|
||||||
|
// public string? OperationMessage { get; set; }
|
||||||
|
// public bool IsDelete { get; set; }
|
|
@ -0,0 +1,61 @@
|
||||||
|
namespace Bunny.Dao.Entity.Constant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 错误常量
|
||||||
|
/// </summary>
|
||||||
|
public class ExceptionConstant
|
||||||
|
{
|
||||||
|
public const string FileSystemException = "文件系统错误";
|
||||||
|
public const string UnknownException = "未知错误";
|
||||||
|
public const string TokenIsEmpty = "token为空";
|
||||||
|
public const string DataIsEmpty = "数据为空";
|
||||||
|
public const string RequestDataNotEmptyException = "请求参数为空";
|
||||||
|
public const string UpdateDtoIsNullException = "修改参数为空";
|
||||||
|
public const string AddDataIsEmptyException = "添加数据为空";
|
||||||
|
public const string DeleteIdIsNotEmptyException = "删除id不能为空";
|
||||||
|
public const string ServerError = "服务器错误";
|
||||||
|
|
||||||
|
// 文章操作相关
|
||||||
|
public const string DoLikeCommentNotExist = "点赞内容不存在";
|
||||||
|
public const string ReplyUserEmptyException = "回复的用户不存在";
|
||||||
|
public const string ReplyUserIdEmptyException = "回复的用户不能为空";
|
||||||
|
public const string MenuIsNotExistException = "菜单不存在";
|
||||||
|
public const string PostCommentEmptyException = "评论内容不能为空";
|
||||||
|
public const string ArticleIdNotEmptyException = "文章id不能为空";
|
||||||
|
public const string UpdateIdIsNotEmptyException = "修改id不能为空";
|
||||||
|
public const string CannotTopOtherUser = "不能操作此内容";
|
||||||
|
public const string ArticleNotFoundException = "文章未找到";
|
||||||
|
|
||||||
|
// 登录相关
|
||||||
|
public const string UserTokenOutOfDateException = "用户登录过期";
|
||||||
|
public const string LoginDtoIsEmptyException = "登录参数不能为空";
|
||||||
|
public const string LoginFailedException = "登录失败";
|
||||||
|
|
||||||
|
// 账号相关
|
||||||
|
public const string AccountNotFoundException = "账号不存在";
|
||||||
|
public const string AccountLockedException = "账号被锁定";
|
||||||
|
|
||||||
|
// 用户相关
|
||||||
|
public const string UserNotLoginException = "用户未登录";
|
||||||
|
public const string UsernameIsEmptyException = "用户名不能为空";
|
||||||
|
public const string AlreadyUserException = "用户已存在";
|
||||||
|
public const string UserNotFoundException = "用户不存在";
|
||||||
|
|
||||||
|
// 密码相关
|
||||||
|
public const string PasswordException = "密码错误";
|
||||||
|
public const string AccountException = "账号或密码错误";
|
||||||
|
public const string PasswordNotEmptyException = "密码不能为空";
|
||||||
|
public const string OldPasswordException = "旧密码不匹配";
|
||||||
|
public const string PasswordEditException = "密码修改失败";
|
||||||
|
public const string OldPasswordSameNewPasswordException = "旧密码与新密码相同";
|
||||||
|
|
||||||
|
// 验证码错误
|
||||||
|
public const string PleaseSendEmailCodeException = "请先发送验证码";
|
||||||
|
public const string MessageCodeNotPassException = "短信验证码未过期";
|
||||||
|
public const string MessageCodeUnauthorizedException = "短信验证码未授权,请联系管理员";
|
||||||
|
public const string VerificationCodeErrorException = "验证码错误";
|
||||||
|
public const string CaptchaIsEmptyException = "验证码不能为空";
|
||||||
|
public const string KeyIsEmptyException = "验证码key不能为空";
|
||||||
|
public const string VerificationCodeDoesNotMatchException = "验证码不匹配";
|
||||||
|
public const string VerificationCodeIsEmptyException = "验证码失效或不存在";
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Bunny.Dao.Entity.Constant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 文件消息相关常量
|
||||||
|
/// </summary>
|
||||||
|
public class FileMessageConstant
|
||||||
|
{
|
||||||
|
public static readonly string DownloadBucketException = "下载文件失败";
|
||||||
|
public static readonly string FileUploadException = "文件上传失败";
|
||||||
|
public static readonly string BucketExistsException = "查询文化部对象失败";
|
||||||
|
public static readonly string DeleteBucketException = "删除文件对象失败";
|
||||||
|
public static readonly string FileIsEmpty = "文件信息为空";
|
||||||
|
public static readonly string FileIsNotExits = "文件信息为空";
|
||||||
|
public static readonly string GetBucketException = "获取文件信息失败";
|
||||||
|
public static readonly string QueryBucketException = "查询文件信息失败";
|
||||||
|
public static readonly string CreateBucketException = "创建文件对象失败";
|
||||||
|
public static readonly string UpdateBucketException = "更新文件对象失败";
|
||||||
|
public static readonly string ComposeObjectException = "对象错误";
|
||||||
|
public static readonly string CopyBucketException = "复制文件内容失败";
|
||||||
|
public static readonly string DisableBucketException = "禁用文件失败";
|
||||||
|
public static readonly string EnableBucketException = "启用文件失败";
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Bunny.Dao.Entity.Constant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 事件相关常量
|
||||||
|
/// </summary>
|
||||||
|
public class LocalDateTimeConstant
|
||||||
|
{
|
||||||
|
public static readonly string DefaultDateFormat = "yyyy-MM-dd";
|
||||||
|
public static readonly string DefaultDateTimeFormat = "yyyy-MM-dd HH:mm";
|
||||||
|
public static readonly string DefaultDateTimeSecondFormat = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
public static readonly string DefaultTimeFormat = "HH:mm:ss";
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
namespace Bunny.Dao.Entity.Constant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 邮箱消息先关常量
|
||||||
|
/// </summary>
|
||||||
|
public class MailMessageConstant
|
||||||
|
{
|
||||||
|
public static readonly string EmptySendObject = "空发送对象";
|
||||||
|
public static readonly string AddressNotNull = "收件人不能为空";
|
||||||
|
public static readonly string TitleNotNull = "标题不能为空";
|
||||||
|
public static readonly string SendMessageNotNull = "发送消息不能为空";
|
||||||
|
public static readonly string EmailConfigNotFound = "邮箱配置为空";
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
namespace Bunny.Dao.Entity.Constant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Redis相关常量
|
||||||
|
/// </summary>
|
||||||
|
public class RedisUserConstant
|
||||||
|
{
|
||||||
|
private const string AdminLoginInfoPrefix = "ADMIN::LOGIN_INFO::";
|
||||||
|
private const string AdminEmailCodePrefix = "ADMIN::EMAIL_CODE::";
|
||||||
|
private const string UserLoginInfoPrefix = "USER::LOGIN_INFO::";
|
||||||
|
private const string UserEmailCodePrefix = "USER::EMAIL_CODE::";
|
||||||
|
private const string UserDoLikePrefix = "USER::doLike::";
|
||||||
|
|
||||||
|
public static string GetAdminLoginInfoPrefix(string adminUser)
|
||||||
|
{
|
||||||
|
return AdminLoginInfoPrefix + adminUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetAdminUserEmailCodePrefix(string adminUser)
|
||||||
|
{
|
||||||
|
return AdminEmailCodePrefix + adminUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetUserLoginInfoPrefix(string user)
|
||||||
|
{
|
||||||
|
return UserLoginInfoPrefix + user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetUserEmailCodePrefix(string user)
|
||||||
|
{
|
||||||
|
return UserEmailCodePrefix + user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetUserDoLikePrefix(string user)
|
||||||
|
{
|
||||||
|
return UserDoLikePrefix + user;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Bunny.Dao.Entity.Constant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 鉴权相关常量
|
||||||
|
/// </summary>
|
||||||
|
public class SecurityConstant
|
||||||
|
{
|
||||||
|
public static string User = "";
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Bunny.Dao.Entity.Constant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数据库相关常量
|
||||||
|
/// </summary>
|
||||||
|
public class SqlConstant
|
||||||
|
{
|
||||||
|
public static readonly string SetCreateTime = "setCreateTime";
|
||||||
|
public static readonly string SetUpdateTime = "setUpdateTime";
|
||||||
|
public static readonly string SetCreateUser = "setCreateUser";
|
||||||
|
public static readonly string SetUpdateUser = "setUpdateUser";
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Bunny.Dao.Entity.Constant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 状态相关常量
|
||||||
|
/// </summary>
|
||||||
|
public class StatusConstant
|
||||||
|
{
|
||||||
|
public const int Enable = 1;
|
||||||
|
public const int Disable = 0;
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Bunny.Dao.Entity.Constant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户相关常量
|
||||||
|
/// </summary>
|
||||||
|
public class UserConstant
|
||||||
|
{
|
||||||
|
public const string UserAvatar =
|
||||||
|
"https://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132";
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
namespace Bunny.Dao.Entity.Email;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送邮件对象
|
||||||
|
/// </summary>
|
||||||
|
public abstract class EmailSendEntity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// SMTP 服务器
|
||||||
|
/// </summary>
|
||||||
|
public string SmtpService { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SMTP 服务器端口号
|
||||||
|
/// </summary>
|
||||||
|
public int SmtpPort { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送者邮件
|
||||||
|
/// </summary>
|
||||||
|
public string SendEmail { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送者密码
|
||||||
|
/// </summary>
|
||||||
|
public string SendEmailPassword { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 接受这邮件
|
||||||
|
/// </summary>
|
||||||
|
public string ReceiverEmail { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送邮件的主题
|
||||||
|
/// </summary>
|
||||||
|
public string Subject { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送邮件的内容
|
||||||
|
/// </summary>
|
||||||
|
public string Body { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置抄送
|
||||||
|
/// </summary>
|
||||||
|
public string? CC { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否密送
|
||||||
|
/// </summary>
|
||||||
|
public bool IsBbc { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否设置HTML内容
|
||||||
|
/// </summary>
|
||||||
|
public bool IsHtml { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否已SSL发送
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSSl { get; set; } = true;
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
namespace Bunny.Dao.Entity.Enum;
|
||||||
|
|
||||||
|
public enum Enum
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
|
||||||
|
namespace Bunny.Dao.Models.Base;
|
||||||
|
|
||||||
|
public class BaseModel
|
||||||
|
{
|
||||||
|
public string? Id { get; set; }
|
||||||
|
|
||||||
|
public DateTime CreateTime { get; set; }
|
||||||
|
|
||||||
|
public DateTime UpdateTime { get; set; }
|
||||||
|
|
||||||
|
public long CreateUserId { get; set; }
|
||||||
|
|
||||||
|
public long UpdateUserId { get; set; }
|
||||||
|
|
||||||
|
public string? OperationMessage { get; set; }
|
||||||
|
|
||||||
|
public bool IsDelete { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
|
||||||
|
namespace Bunny.Dao.Models.Base;
|
||||||
|
|
||||||
|
public class BasePersonModel : BaseModel
|
||||||
|
{
|
||||||
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
public string? Type { get; set; }
|
||||||
|
|
||||||
|
public string? Description { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
namespace Bunny.Dao.Models.Base;
|
||||||
|
|
||||||
|
public class BaseStuffModel : BaseModel
|
||||||
|
{
|
||||||
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
public string? Type { get; set; }
|
||||||
|
|
||||||
|
public string? Description { get; set; }
|
||||||
|
|
||||||
|
public string? Spec { get; set; }
|
||||||
|
|
||||||
|
public bool IsUse { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using Bunny.Dao.Models.Base;
|
||||||
|
|
||||||
|
namespace Bunny.Dao.Models.System;
|
||||||
|
|
||||||
|
public class Product : BaseStuffModel
|
||||||
|
{
|
||||||
|
public string? CustomerId { get; set; }
|
||||||
|
|
||||||
|
public string? CustomerName { get; set; }
|
||||||
|
|
||||||
|
public string? PackageType { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
using Bunny.Dao.Models.Base;
|
||||||
|
|
||||||
|
namespace Bunny.Dao.Models.System;
|
||||||
|
|
||||||
|
public class User : BaseModel
|
||||||
|
{
|
||||||
|
public string? UserName { get; set; }
|
||||||
|
|
||||||
|
public string? Password { get; set; }
|
||||||
|
|
||||||
|
public string? Email { get; set; }
|
||||||
|
|
||||||
|
public string? Phone { get; set; }
|
||||||
|
|
||||||
|
public string? QrCode { get; set; }
|
||||||
|
|
||||||
|
public string? CompanyCode { get; set; }
|
||||||
|
|
||||||
|
public string? DeptCode { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
namespace Bunny.Dao.Result.Constant;
|
||||||
|
|
||||||
|
public class ErrorConstant
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
namespace Bunny.Dao.Result.Constant;
|
||||||
|
|
||||||
|
public class SuccessConstant
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace Bunny.Dao.Result.Enum;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回结果状态码枚举
|
||||||
|
/// </summary>
|
||||||
|
public enum ResultCodeEnum
|
||||||
|
{
|
||||||
|
// 成功操作 200
|
||||||
|
[Description("操作成功")] Success = 200,
|
||||||
|
|
||||||
|
// 验证错误 201
|
||||||
|
[Description("账号或密码错误")] LoginError = 201,
|
||||||
|
|
||||||
|
// 数据相关 206
|
||||||
|
[Description("非法请求")] IllegalRequest = 206,
|
||||||
|
|
||||||
|
// 身份过期 208
|
||||||
|
[Description("请先登陆")] LoginAuth = 206,
|
||||||
|
[Description("身份验证过期")] AuthenticationExpired = 207,
|
||||||
|
[Description("会话过期")] SessionExpiration = 208,
|
||||||
|
[Description("该账户被封禁")] FailNoAccessDeniedUserLocked = 209,
|
||||||
|
|
||||||
|
// 提示错误
|
||||||
|
[Description("URL编码失败")] UrlEncodeError = 216,
|
||||||
|
[Description("非法回调请求")] IllegalCallbackRequestError = 217,
|
||||||
|
[Description("获取用户信息失败")] FetchUserinfoError = 219,
|
||||||
|
|
||||||
|
// 无权访问 403
|
||||||
|
[Description("无权访问")] FailNoAccessDenied = 403,
|
||||||
|
|
||||||
|
// 系统错误 500
|
||||||
|
[Description("请求失败")] Error = 500
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义属性
|
||||||
|
/// </summary>
|
||||||
|
public static class ResultCodeExtensions
|
||||||
|
{
|
||||||
|
public static string GetDescription(this ResultCodeEnum eCodeEnum)
|
||||||
|
{
|
||||||
|
var fieldInfo = eCodeEnum.GetType().GetField(eCodeEnum.ToString());
|
||||||
|
if (fieldInfo == null) return string.Empty;
|
||||||
|
var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
|
||||||
|
|
||||||
|
return attributes.Length > 0 ? attributes[0].Description : eCodeEnum.ToString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
namespace Bunny.Dao.Result.Page;
|
||||||
|
|
||||||
|
public class Pagination<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 当前页
|
||||||
|
/// </summary>
|
||||||
|
public int PageNo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 页容量
|
||||||
|
/// </summary>
|
||||||
|
public int PageSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 总页数
|
||||||
|
/// </summary>
|
||||||
|
public int Pages { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 总条数
|
||||||
|
/// </summary>
|
||||||
|
public int Total { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数据
|
||||||
|
/// </summary>
|
||||||
|
public IList<T> Rows { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,228 @@
|
||||||
|
using System.Net;
|
||||||
|
using Bunny.Dao.Result.Enum;
|
||||||
|
|
||||||
|
namespace Bunny.Dao.Result;
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class Result<T>(HttpStatusCode code, T? data, string? message)
|
||||||
|
{
|
||||||
|
public Result(HttpStatusCode code, string? message) : this(code, default, message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private Result(int code, T? data, string message) : this((HttpStatusCode)code, data, message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回码
|
||||||
|
/// </summary>
|
||||||
|
public HttpStatusCode Code { get; set; } = code;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回说明
|
||||||
|
/// </summary>
|
||||||
|
public string? Message { get; set; } = message;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回数据体 可为空
|
||||||
|
/// </summary>
|
||||||
|
public T? Data { get; set; } = data;
|
||||||
|
|
||||||
|
// 不再继承 IActionResult
|
||||||
|
// public Task ExecuteResultAsync(ActionContext context)
|
||||||
|
// {
|
||||||
|
// return new Task<Result<T>>(() => new Result<T>(code, data, message));
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 操作成功
|
||||||
|
/// </summary>
|
||||||
|
public static Result<T> Success()
|
||||||
|
{
|
||||||
|
const ResultCodeEnum codeEnum = ResultCodeEnum.Success;
|
||||||
|
return new Result<T>((HttpStatusCode)codeEnum, default, codeEnum.GetDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回成功对象消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">数据内容分</param>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Success(T data)
|
||||||
|
{
|
||||||
|
const ResultCodeEnum codeEnum = ResultCodeEnum.Success;
|
||||||
|
return new Result<T>((HttpStatusCode)codeEnum, data, codeEnum.GetDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义返回成功消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code">code状态码</param>
|
||||||
|
/// <param name="message">消息内容</param>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Success(HttpStatusCode code, string message)
|
||||||
|
{
|
||||||
|
return new Result<T>(code, default, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义返回成功消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code">code状态码</param>
|
||||||
|
/// <param name="data">数据内容分</param>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Success(HttpStatusCode code, T data)
|
||||||
|
{
|
||||||
|
const ResultCodeEnum codeEnum = ResultCodeEnum.Success;
|
||||||
|
return new Result<T>(code, data, codeEnum.GetDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义返回成功消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">消息内容</param>
|
||||||
|
/// <param name="data">数据内容分</param>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Success(string message, T data)
|
||||||
|
{
|
||||||
|
const HttpStatusCode codeEnum = (HttpStatusCode)ResultCodeEnum.Success;
|
||||||
|
return new Result<T>(codeEnum, data, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义返回成功消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="codeEnum">code枚举类</param>
|
||||||
|
/// <param name="data">数据内容分</param>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Success(ResultCodeEnum codeEnum, T data)
|
||||||
|
{
|
||||||
|
return new Result<T>((HttpStatusCode)codeEnum, data, codeEnum.GetDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义返回成功消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code">code状态码</param>
|
||||||
|
/// <param name="data">数据内容分</param>
|
||||||
|
/// <param name="message">消息内容</param>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Success(HttpStatusCode code, T data, string message)
|
||||||
|
{
|
||||||
|
return new Result<T>(code, data, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回错误对象
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Error()
|
||||||
|
{
|
||||||
|
const ResultCodeEnum codeEnum = ResultCodeEnum.Error;
|
||||||
|
return new Result<T>((HttpStatusCode)codeEnum, default, codeEnum.GetDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回错误对象
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code">code状态码</param>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Error(HttpStatusCode code)
|
||||||
|
{
|
||||||
|
const ResultCodeEnum codeEnum = ResultCodeEnum.Error;
|
||||||
|
return new Result<T>(code, default, codeEnum.GetDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回错误对象
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data">数据内容分</param>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Error(T? data)
|
||||||
|
{
|
||||||
|
const ResultCodeEnum codeEnum = ResultCodeEnum.Error;
|
||||||
|
return new Result<T>((HttpStatusCode)codeEnum, data, codeEnum.GetDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回错误对象
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="codeEnum">code枚举类</param>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Error(ResultCodeEnum codeEnum)
|
||||||
|
{
|
||||||
|
return new Result<T>((HttpStatusCode)codeEnum, default, codeEnum.GetDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义返回成功消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code">code状态码</param>
|
||||||
|
/// <param name="message">消息内容</param>
|
||||||
|
/// <param name="data">错误数据</param>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Error(int code, string message, T? data = default)
|
||||||
|
{
|
||||||
|
return new Result<T>(code, data, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义返回成功消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code">code状态码</param>
|
||||||
|
/// <param name="message">消息内容</param>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Error(HttpStatusCode code, string message)
|
||||||
|
{
|
||||||
|
return new Result<T>(code, default, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回错误对象
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code">code状态码</param>
|
||||||
|
/// <param name="data">数据内容分</param>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Error(HttpStatusCode code, T data)
|
||||||
|
{
|
||||||
|
const ResultCodeEnum codeEnum = ResultCodeEnum.Error;
|
||||||
|
return new Result<T>(code, data, codeEnum.GetDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回错误对象
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">消息内容</param>
|
||||||
|
/// <param name="data">数据内容分</param>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Error(string message, T data)
|
||||||
|
{
|
||||||
|
const HttpStatusCode codeEnum = (HttpStatusCode)ResultCodeEnum.Error;
|
||||||
|
return new Result<T>(codeEnum, data, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回错误对象
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="codeEnum">code枚举类</param>
|
||||||
|
/// <param name="data">数据内容分</param>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Error(ResultCodeEnum codeEnum, T data)
|
||||||
|
{
|
||||||
|
return new Result<T>((HttpStatusCode)codeEnum, data, codeEnum.GetDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义返回成功消息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="code">code状态码</param>
|
||||||
|
/// <param name="data">数据内容分</param>
|
||||||
|
/// <param name="message">消息内容</param>
|
||||||
|
/// <returns>返回的数据内容</returns>
|
||||||
|
public static Result<T> Error(HttpStatusCode code, T data, string message)
|
||||||
|
{
|
||||||
|
return new Result<T>(code, data, message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
namespace Bunny.Dao.Vo.Email;
|
||||||
|
|
||||||
|
public class EmailTemplateVo
|
||||||
|
{
|
||||||
|
// 模板名称
|
||||||
|
public string? TemplateName { get; set; }
|
||||||
|
|
||||||
|
// 主题
|
||||||
|
public string? Subject { get; set; }
|
||||||
|
|
||||||
|
// 邮件内容
|
||||||
|
public string? Body { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using Bunny.Dao.Entity.Base;
|
||||||
|
|
||||||
|
namespace Bunny.Dao.Vo.User;
|
||||||
|
|
||||||
|
public class LoginVo : BaseEntity
|
||||||
|
{
|
||||||
|
public string? UserName { get; set; }
|
||||||
|
public string? Email { get; set; }
|
||||||
|
public string? Phone { get; set; }
|
||||||
|
public string? QrCode { get; set; }
|
||||||
|
public string? Token { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
namespace Bunny.Service.BackgroundModule;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 定时任务
|
||||||
|
/// </summary>
|
||||||
|
public class TemplateBackgroundModule : BackgroundService
|
||||||
|
{
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
Console.WriteLine("TemplateService started");
|
||||||
|
await Task.Delay(1000, stoppingToken);
|
||||||
|
await Task.Run(() => { Console.WriteLine("执行了。。。"); }, stoppingToken);
|
||||||
|
await Task.Delay(1000, stoppingToken);
|
||||||
|
Console.WriteLine("--------------------------------");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Fleck" Version="1.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Bunny.Common\Bunny.Common.csproj"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="IRepository\UserRepository\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,78 @@
|
||||||
|
using System.Net;
|
||||||
|
using Bunny.Common.Utils.Jwt;
|
||||||
|
using Bunny.Common.Utils.Net;
|
||||||
|
using Bunny.Dao.Entity.Constant;
|
||||||
|
using Bunny.Dao.Vo.User;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
|
namespace Bunny.Service.Filter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自定义权限过滤器
|
||||||
|
/// </summary>
|
||||||
|
public class AuthorizationFilter : IAuthorizationFilter
|
||||||
|
{
|
||||||
|
private string? _ipAddress;
|
||||||
|
private HttpRequest? _request;
|
||||||
|
private string? _requestPath;
|
||||||
|
private string? _token;
|
||||||
|
|
||||||
|
public void OnAuthorization(AuthorizationFilterContext context)
|
||||||
|
{
|
||||||
|
PathIgnore();
|
||||||
|
var request = context.HttpContext.Request;
|
||||||
|
_request = context.HttpContext.Request;
|
||||||
|
_token = NetUtil.GetToken(request);
|
||||||
|
_requestPath = NetUtil.GetRequestPath(request);
|
||||||
|
_ipAddress = NetUtil.GetRequestIpAddress(request.HttpContext);
|
||||||
|
|
||||||
|
// 是否匹配当前路径
|
||||||
|
var matchPath = NetUtil.MatchPath(_requestPath);
|
||||||
|
|
||||||
|
// 不匹配直接返回
|
||||||
|
if (!matchPath) return;
|
||||||
|
|
||||||
|
|
||||||
|
// 检查token
|
||||||
|
CheckToken(context);
|
||||||
|
|
||||||
|
// token 不为空
|
||||||
|
if (_token == null) return;
|
||||||
|
|
||||||
|
var jsonToObject = JwtUtil.ParseJwtToObject<LoginVo>(_token);
|
||||||
|
// 打印日志
|
||||||
|
Console.WriteLine(jsonToObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查token相关内容
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">上下文对象</param>
|
||||||
|
private void CheckToken(AuthorizationFilterContext context)
|
||||||
|
{
|
||||||
|
// token为空直接返回
|
||||||
|
if (_token.IsNullOrEmpty() && _token == null)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine($"未授权登录访问:{_ipAddress}");
|
||||||
|
context.Result = NetUtil.Out(HttpStatusCode.Unauthorized, ExceptionConstant.UserNotLoginException);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var loginVo = JwtUtil.ParseJwtToObject<LoginVo>(_token!);
|
||||||
|
// 验证token是否过期
|
||||||
|
if (!JwtUtil.IsJwtExpired(_token) && loginVo != null) return;
|
||||||
|
Console.Error.WriteLine($"登录过期!用户Id:{loginVo!.Id},用户邮箱:{loginVo.Email}请求IP:{_ipAddress}");
|
||||||
|
context.Result = NetUtil.Out(HttpStatusCode.Unauthorized, ExceptionConstant.UserTokenOutOfDateException);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 忽略匹配路径
|
||||||
|
/// </summary>
|
||||||
|
private static void PathIgnore()
|
||||||
|
{
|
||||||
|
var list = new List<string> { "/", "/api/**" };
|
||||||
|
NetUtil.Ignores.AddRange(list);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
using Bunny.Dao.Models.System;
|
||||||
|
|
||||||
|
namespace Bunny.Service.IService;
|
||||||
|
|
||||||
|
public interface IBaseService
|
||||||
|
{
|
||||||
|
string TestIndex();
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
using Bunny.Dao.Dto.User;
|
||||||
|
using Bunny.Dao.Vo.User;
|
||||||
|
|
||||||
|
namespace Bunny.Service.IService;
|
||||||
|
|
||||||
|
public interface ILoginService
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
using Bunny.Dao.Models.System;
|
||||||
|
|
||||||
|
namespace Bunny.Service.IService.Service;
|
||||||
|
|
||||||
|
public class BaseService : IBaseService
|
||||||
|
{
|
||||||
|
|
||||||
|
public string TestIndex()
|
||||||
|
{
|
||||||
|
const string result = "bunny-template 实现自动注入成功";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
using Bunny.Common.Exception;
|
||||||
|
using Bunny.Common.Utils.Jwt;
|
||||||
|
using Bunny.Common.Utils.Net;
|
||||||
|
using Bunny.Dao.Dto.User;
|
||||||
|
using Bunny.Dao.Entity.Constant;
|
||||||
|
using Bunny.Dao.Vo.User;
|
||||||
|
|
||||||
|
namespace Bunny.Service.IService.Service;
|
||||||
|
|
||||||
|
public class LoginService : ILoginService
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Bunny.Service.WebSocket;
|
||||||
|
|
||||||
|
public static class WebSocketInitial
|
||||||
|
{
|
||||||
|
public static void Start()
|
||||||
|
{
|
||||||
|
WebSocketTest.Start();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
using Fleck;
|
||||||
|
|
||||||
|
namespace Bunny.Service.WebSocket;
|
||||||
|
|
||||||
|
public static class WebSocketTest
|
||||||
|
{
|
||||||
|
public static void Start()
|
||||||
|
{
|
||||||
|
var webSocketServer = new WebSocketServer("ws://0.0.0.0:8000");
|
||||||
|
|
||||||
|
webSocketServer.RestartAfterListenError = true;
|
||||||
|
webSocketServer.Start(socket =>
|
||||||
|
{
|
||||||
|
socket.OnOpen = () => Console.WriteLine("Open!");
|
||||||
|
socket.OnClose = () => Console.WriteLine("Close!");
|
||||||
|
socket.OnMessage = message =>
|
||||||
|
{
|
||||||
|
Console.WriteLine(message);
|
||||||
|
socket.Send(message);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
|
||||||
|
<PackageReference Include="NUnit" Version="4.1.0"/>
|
||||||
|
<PackageReference Include="xunit.core" Version="2.9.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Bunny.Common\Bunny.Common.csproj"/>
|
||||||
|
<ProjectReference Include="..\Bunny.Dao\Bunny.Dao.csproj"/>
|
||||||
|
<ProjectReference Include="..\Bunny.WebApi\Bunny.WebApi.csproj"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,34 @@
|
||||||
|
using Bunny.Common.Utils.Jwt;
|
||||||
|
using Bunny.Dao.Dto.Email;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Bunny.Test.Until.Jwt;
|
||||||
|
|
||||||
|
[TestFixture]
|
||||||
|
public class JwtConvertTest
|
||||||
|
{
|
||||||
|
[SetUp]
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
var emailUsersDto = new EmailUsersDto
|
||||||
|
{
|
||||||
|
Body = "test@example.com",
|
||||||
|
Subject = "苏北",
|
||||||
|
TemplateName = "test@example.com",
|
||||||
|
Type = "邮件类型"
|
||||||
|
};
|
||||||
|
_emailUsersDto = emailUsersDto;
|
||||||
|
}
|
||||||
|
|
||||||
|
private EmailUsersDto? _emailUsersDto;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CreateJClaimByEmail()
|
||||||
|
{
|
||||||
|
var claims = JwtUtil.CreateClaim(_emailUsersDto!);
|
||||||
|
Console.WriteLine(claims);
|
||||||
|
|
||||||
|
var jwt = JwtUtil.GenerateJwt(claims);
|
||||||
|
Console.WriteLine(jwt);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
using System.Security.Claims;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using JwtUtil = Bunny.Common.Utils.Jwt.JwtUtil;
|
||||||
|
|
||||||
|
namespace Bunny.Test.Until.Jwt;
|
||||||
|
|
||||||
|
[TestFixture]
|
||||||
|
public class JwtTest
|
||||||
|
{
|
||||||
|
// 生层JWT
|
||||||
|
[Test]
|
||||||
|
public void JwtCreateTest()
|
||||||
|
{
|
||||||
|
var claims = new List<Claim>();
|
||||||
|
var claim1 = new Claim("bunny", "小兔子");
|
||||||
|
claims.Add(claim1);
|
||||||
|
var generateJwt = JwtUtil.GenerateJwt(claims);
|
||||||
|
|
||||||
|
Console.WriteLine(generateJwt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否过期
|
||||||
|
[Test]
|
||||||
|
public void IsJwtExpired()
|
||||||
|
{
|
||||||
|
var isJwtExpired = JwtUtil.IsJwtExpired("");
|
||||||
|
Console.WriteLine(isJwtExpired);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void JwtParse()
|
||||||
|
{
|
||||||
|
var token = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJidW5ueSI6IuWwj-WFlOWtkCIsImV4cCI6MTcxODAyODU5OH0.\n";
|
||||||
|
|
||||||
|
var jwtParse = JwtUtil.JwtParse(token);
|
||||||
|
var jwtParseIssuer = JwtUtil.JwtParseIssuer(token);
|
||||||
|
var jwtParseAudiences = JwtUtil.JwtParseAudiences(token);
|
||||||
|
var jwtParseClaim = JwtUtil.JwtParseClaim(token);
|
||||||
|
|
||||||
|
Console.WriteLine(jwtParse);
|
||||||
|
Console.WriteLine(jwtParseIssuer);
|
||||||
|
Console.WriteLine(jwtParseAudiences);
|
||||||
|
Console.WriteLine(jwtParseClaim);
|
||||||
|
foreach (var claim in jwtParseClaim)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Type:{claim.Type}");
|
||||||
|
Console.WriteLine($"Value:{claim.Value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void JwtParseClaim()
|
||||||
|
{
|
||||||
|
var token = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJidW5ueSI6IuWwj-WFlOWtkCIsImV4cCI6MTcxODAyODU5OH0.\n";
|
||||||
|
var jwtParseClaim = JwtUtil.JwtParseClaim(token);
|
||||||
|
var enumerable = jwtParseClaim.Select(s => s.Type);
|
||||||
|
var join = string.Join(" ", enumerable);
|
||||||
|
|
||||||
|
Console.WriteLine(join);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
using Bunny.Common.Utils.Net;
|
||||||
|
using Bunny.Dao.Dto.Email;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Bunny.Test.Until;
|
||||||
|
|
||||||
|
public class NetUtilTest
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void NetTest1()
|
||||||
|
{
|
||||||
|
var emailTemplateDto = new EmailTemplateDto
|
||||||
|
{
|
||||||
|
Body = "test@example.com",
|
||||||
|
Subject = "主题",
|
||||||
|
TemplateName = "hjh"
|
||||||
|
};
|
||||||
|
|
||||||
|
var emailUsersDto = new EmailUsersDto();
|
||||||
|
|
||||||
|
NetUtil.CopyProperties(emailTemplateDto, emailUsersDto);
|
||||||
|
|
||||||
|
Console.WriteLine(JsonConvert.SerializeObject(emailUsersDto));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Encryption32()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var password = "1";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = NetUtil.Encryption32(password);
|
||||||
|
Console.WriteLine(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Encryption32Lower()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var password = "1";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = NetUtil.Encryption32Lower(password);
|
||||||
|
Console.WriteLine(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void GetProperties()
|
||||||
|
{
|
||||||
|
var emailTemplateDto = new EmailTemplateDto
|
||||||
|
{
|
||||||
|
Body = "test@example.com",
|
||||||
|
Subject = "主题",
|
||||||
|
TemplateName = "hjh"
|
||||||
|
};
|
||||||
|
|
||||||
|
var propertyInfos = emailTemplateDto.GetType().GetProperties();
|
||||||
|
|
||||||
|
foreach (var propertyInfo in propertyInfos)
|
||||||
|
{
|
||||||
|
Console.WriteLine(propertyInfo.Name);
|
||||||
|
Console.WriteLine(propertyInfo.GetValue(emailTemplateDto));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
using Bunny.Service.IService.Service;
|
||||||
|
using Bunny.WebApi.Controllers.Base;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace Bunny.Test.Until.Redis;
|
||||||
|
|
||||||
|
[TestFixture]
|
||||||
|
public class RedisConnect
|
||||||
|
{
|
||||||
|
private readonly ITestOutputHelper _testOutputHelper;
|
||||||
|
private readonly ServiceProvider _provider;
|
||||||
|
|
||||||
|
|
||||||
|
public RedisConnect(ITestOutputHelper testOutputHelper)
|
||||||
|
{
|
||||||
|
_testOutputHelper = testOutputHelper;
|
||||||
|
var serviceCollection = new ServiceCollection();
|
||||||
|
serviceCollection.AddLogging();
|
||||||
|
serviceCollection.AddTransient<TemplateController>();
|
||||||
|
_provider = serviceCollection.BuildServiceProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RedisConnectTest()
|
||||||
|
{
|
||||||
|
var baseService = new BaseService();
|
||||||
|
var testIndex = baseService.TestIndex();
|
||||||
|
_testOutputHelper.WriteLine(testIndex);
|
||||||
|
|
||||||
|
var controller = _provider.GetService<TemplateController>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
using System.Net;
|
||||||
|
using Bunny.Dao.Entity.Constant;
|
||||||
|
using Bunny.Dao.Result;
|
||||||
|
using Bunny.Dao.Result.Enum;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace Bunny.Test.Until;
|
||||||
|
|
||||||
|
[TestFixture]
|
||||||
|
public class TestResultCodeEnum
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestResultCodeExtensions()
|
||||||
|
{
|
||||||
|
const ResultCodeEnum resultCodeEnum = ResultCodeEnum.Success;
|
||||||
|
Console.WriteLine(resultCodeEnum);
|
||||||
|
Console.WriteLine(resultCodeEnum.GetDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSuccessResult()
|
||||||
|
{
|
||||||
|
var success = Result<Dictionary<string, object>>.Success();
|
||||||
|
var serializeObject = JsonConvert.SerializeObject(success);
|
||||||
|
Console.WriteLine(serializeObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestErrorResult()
|
||||||
|
{
|
||||||
|
var result = Result<string>.Error();
|
||||||
|
var serializeObject = JsonConvert.SerializeObject(result);
|
||||||
|
Console.WriteLine(serializeObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCodeResult()
|
||||||
|
{
|
||||||
|
var result = Result<string>.Error(HttpStatusCode.OK);
|
||||||
|
var serializeObject = JsonConvert.SerializeObject(result);
|
||||||
|
Console.WriteLine(serializeObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMessageResult()
|
||||||
|
{
|
||||||
|
var result = Result<string>.Error(ExceptionConstant.UnknownException);
|
||||||
|
var serializeObject = JsonConvert.SerializeObject(result);
|
||||||
|
Console.WriteLine(serializeObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCodeMessageResult()
|
||||||
|
{
|
||||||
|
var result = Result<string>.Error(HttpStatusCode.Ambiguous, ExceptionConstant.UnknownException);
|
||||||
|
var serializeObject = JsonConvert.SerializeObject(result);
|
||||||
|
Console.WriteLine(serializeObject);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.10.34928.147
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bunny.WebApi", "Bunny.WebApi\Bunny.WebApi.csproj", "{28753039-0C3B-4FE5-95F8-7EDC92DCBA26}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bunny.Common", "Bunny.Common\Bunny.Common.csproj", "{40D580AA-63CD-4912-835F-ECBB2C232F9E}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bunny.Test.Until", "Bunny.Test.Until\Bunny.Test.Until.csproj", "{945BE294-8057-40EE-9A98-5598D1A728B9}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bunny.Dao", "Bunny.Dao\Bunny.Dao.csproj", "{A0A986A6-A0A4-4627-B45A-94A882FC70ED}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bunny.Service", "Bunny.Service\Bunny.Service.csproj", "{7CEBC594-134C-48C1-96ED-263E047E7D96}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{28753039-0C3B-4FE5-95F8-7EDC92DCBA26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{28753039-0C3B-4FE5-95F8-7EDC92DCBA26}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{28753039-0C3B-4FE5-95F8-7EDC92DCBA26}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{28753039-0C3B-4FE5-95F8-7EDC92DCBA26}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{40D580AA-63CD-4912-835F-ECBB2C232F9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{40D580AA-63CD-4912-835F-ECBB2C232F9E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{40D580AA-63CD-4912-835F-ECBB2C232F9E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{40D580AA-63CD-4912-835F-ECBB2C232F9E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{945BE294-8057-40EE-9A98-5598D1A728B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{945BE294-8057-40EE-9A98-5598D1A728B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{945BE294-8057-40EE-9A98-5598D1A728B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{945BE294-8057-40EE-9A98-5598D1A728B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A0A986A6-A0A4-4627-B45A-94A882FC70ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A0A986A6-A0A4-4627-B45A-94A882FC70ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A0A986A6-A0A4-4627-B45A-94A882FC70ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A0A986A6-A0A4-4627-B45A-94A882FC70ED}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{7CEBC594-134C-48C1-96ED-263E047E7D96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7CEBC594-134C-48C1-96ED-263E047E7D96}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7CEBC594-134C-48C1-96ED-263E047E7D96}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7CEBC594-134C-48C1-96ED-263E047E7D96}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {3215F6BB-D889-4D2D-9572-509020181496}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
|
@ -0,0 +1,17 @@
|
||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue"><AssemblyExplorer>
|
||||||
|
<Assembly Path="D:\software\Microsoft\Visual Studio\packages\stackexchange.redis\2.7.33\lib\net6.0\StackExchange.Redis.dll" />
|
||||||
|
<Assembly Path="D:\software\Microsoft\Visual Studio\packages\minio\6.0.2\lib\net7.0\Minio.dll" />
|
||||||
|
</AssemblyExplorer></s:String>
|
||||||
|
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=eb9da086_002D824a_002D4ac4_002Db755_002D94438e510122/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="Test1" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||||
|
<TestAncestor>
|
||||||
|
<TestId>NUnit3x::945BE294-8057-40EE-9A98-5598D1A728B9::net8.0::Bunny.Test.Until.NetUtilTest.Encryption32Lower</TestId>
|
||||||
|
<TestId>NUnit3x::945BE294-8057-40EE-9A98-5598D1A728B9::net8.0::Bunny.Test.Until.NetUtilTest.GetProperties</TestId>
|
||||||
|
<TestId>NUnit3x::945BE294-8057-40EE-9A98-5598D1A728B9::net8.0::Bunny.Test.Until.Redis.RedisConnect.RedisConnectTest</TestId>
|
||||||
|
<TestId>xUnit::945BE294-8057-40EE-9A98-5598D1A728B9::net8.0::Bunny.Test.Until.Redis.RedisConnect.RedisConnectTest</TestId>
|
||||||
|
</TestAncestor>
|
||||||
|
</SessionState></s:String>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</wpf:ResourceDictionary>
|
|
@ -0,0 +1,46 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||||
|
<DocumentationFile>bin\Debug\net8.0\Swagger.xml</DocumentationFile>
|
||||||
|
<ProduceReferenceAssembly>False</ProduceReferenceAssembly>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<NoWarn>1701;1702;1591</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<NoWarn>1701;1702;1591</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<_WebToolingArtifacts Remove="Properties\launchSettings.json"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="Properties\launchSettings.json"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="IGeekFan.AspNetCore.Knife4jUI" Version="0.0.16"/>
|
||||||
|
<PackageReference Include="log4net" Version="2.0.17"/>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Log4Net.AspNetCore" Version="8.0.0"/>
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2"/>
|
||||||
|
<PackageReference Include="System.Data.SqlClient" Version="4.8.6"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Bunny.Service\Bunny.Service.csproj"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<_ContentIncludedByDefault Remove="Config\log4net.config"/>
|
||||||
|
<_ContentIncludedByDefault Remove="Config\NLog.config"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ActiveDebugProfile>http</ActiveDebugProfile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,6 @@
|
||||||
|
@Bunny.WebApi_HostAddress = http://localhost:5106
|
||||||
|
|
||||||
|
GET {{Bunny.WebApi_HostAddress}}/weatherforecast/
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
|
@ -0,0 +1,87 @@
|
||||||
|
using Bunny.Common;
|
||||||
|
using Bunny.Service.WebSocket;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
|
||||||
|
namespace Bunny.WebApi.Config;
|
||||||
|
|
||||||
|
public class BaseConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 只要Controller和Service放在 WebApi 项目下即可完成自动注入
|
||||||
|
/// 遵守这个规则不用再启动类上注入IOC
|
||||||
|
/// 遵守这个规则在Controller可以使用属性方式注入Service
|
||||||
|
/// 只要写接口和实现、Controller中正常写即可
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder"></param>
|
||||||
|
public static void Initialize(WebApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
Console.Title = "Bunny Template";
|
||||||
|
|
||||||
|
// 配置
|
||||||
|
UseConfiguration(builder);
|
||||||
|
|
||||||
|
// 使用扩展,第三方如Redis、Minio、SQLSugar
|
||||||
|
UseExtend(builder);
|
||||||
|
|
||||||
|
// 配置跨域
|
||||||
|
UseCors(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UseConfiguration(WebApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
// 配置日志相关
|
||||||
|
builder.Logging.AddLog4Net("Config/log4net.config");
|
||||||
|
|
||||||
|
//配置文件
|
||||||
|
builder.Services.AddSingleton(new AppSettings(builder.Configuration));
|
||||||
|
|
||||||
|
// 添加 SignalR
|
||||||
|
builder.Services.AddSignalR();
|
||||||
|
|
||||||
|
// 让控制器实例由容器创建
|
||||||
|
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
|
||||||
|
|
||||||
|
// 如果将Service放在 WebApi同级目录下,可以完成Controller自动注入,不需要写构造函数
|
||||||
|
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
|
||||||
|
builder.Services.AddControllers().AddJsonOptions(options =>
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new JsonDateTimeConverter()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 扩展服务
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">WebApplicationBuilder</param>
|
||||||
|
private static void UseExtend(WebApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
// 设置过滤器
|
||||||
|
FilterConfig.Initialize(builder);
|
||||||
|
|
||||||
|
// 初始化Redis
|
||||||
|
// RedisTemplate.Initial();
|
||||||
|
|
||||||
|
// 初始化 Knife4Net文档
|
||||||
|
Knife4Net.Initialize(builder);
|
||||||
|
|
||||||
|
// 启动webSocket
|
||||||
|
WebSocketInitial.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置跨域
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder"></param>
|
||||||
|
private static void UseCors(IHostApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
// SignalR 配置跨域
|
||||||
|
builder.Services.AddSignalRCore();
|
||||||
|
|
||||||
|
// 配置跨域
|
||||||
|
builder.Services.AddCors(options =>
|
||||||
|
{
|
||||||
|
options.AddDefaultPolicy(policyBuilder => policyBuilder.WithOrigins("*")
|
||||||
|
.WithHeaders("*")
|
||||||
|
.WithMethods("*"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
using Bunny.Common.Filter;
|
||||||
|
using Bunny.Service.Filter;
|
||||||
|
|
||||||
|
namespace Bunny.WebApi.Config;
|
||||||
|
|
||||||
|
public static class FilterConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 配置过滤器
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder"></param>
|
||||||
|
public static void Initialize(WebApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
// 添加自定义过滤器---全局异常捕获
|
||||||
|
builder.Services.AddControllers(option =>
|
||||||
|
{
|
||||||
|
option.Filters.Add<GlobalExceptionFilter>();
|
||||||
|
option.Filters.Add<AuthorizationFilter>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace Bunny.WebApi.Config;
|
||||||
|
|
||||||
|
public class JsonDateTimeConverter : JsonConverter<DateTime>
|
||||||
|
{
|
||||||
|
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
return DateTime.TryParse(reader.GetString(), out var date) ? date : default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
|
||||||
|
namespace Bunny.WebApi.Config;
|
||||||
|
|
||||||
|
public static class Knife4Net
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 配置 Knife4j 文档
|
||||||
|
/// </summary>
|
||||||
|
public static void Initialize(WebApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen(options =>
|
||||||
|
{
|
||||||
|
// 读取设置的文档,将注释读取
|
||||||
|
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "Swagger.xml"), true);
|
||||||
|
|
||||||
|
// 设置文档版本和说明
|
||||||
|
options.SwaggerDoc("v1", new OpenApiInfo
|
||||||
|
{
|
||||||
|
Title = "Bunny 文档", Version = "v1", Description = "文档说明"
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
options.AddServer(new OpenApiServer
|
||||||
|
{
|
||||||
|
Url = "http://localhost:8800"
|
||||||
|
});
|
||||||
|
|
||||||
|
options.CustomOperationIds(apiDesc =>
|
||||||
|
{
|
||||||
|
var controllerAction = apiDesc.ActionDescriptor as ControllerActionDescriptor;
|
||||||
|
return controllerAction?.ControllerName + "-" + controllerAction?.ActionName;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<log4net>
|
||||||
|
<!-- Define some output appenders -->
|
||||||
|
<appender name="rollingAppender" type="log4net.Appender.RollingFileAppender">
|
||||||
|
<file value="log4\log.txt"/>
|
||||||
|
<!--追加日志内容-->
|
||||||
|
<appendToFile value="true"/>
|
||||||
|
|
||||||
|
<!--防止多线程时不能写Log,官方说线程非安全-->
|
||||||
|
<lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
|
||||||
|
|
||||||
|
<!--可以为:Once|Size|Date|Composite-->
|
||||||
|
<!--Composite为Size和Date的组合-->
|
||||||
|
<rollingStyle value="Composite"/>
|
||||||
|
|
||||||
|
<!--当备份文件时,为文件名加的后缀-->
|
||||||
|
<datePattern value="yyyyMMdd.TXT"/>
|
||||||
|
|
||||||
|
<!--日志最大个数,都是最新的-->
|
||||||
|
<!--rollingStyle节点为Size时,只能有value个日志-->
|
||||||
|
<!--rollingStyle节点为Composite时,每天有value个日志-->
|
||||||
|
<maxSizeRollBackups value="20"/>
|
||||||
|
|
||||||
|
<!--可用的单位:KB|MB|GB-->
|
||||||
|
<maximumFileSize value="3MB"/>
|
||||||
|
|
||||||
|
<!--置为true,当前最新日志文件名永远为file节中的名字-->
|
||||||
|
<staticLogFileName value="true"/>
|
||||||
|
|
||||||
|
<!--输出级别在INFO和ERROR之间的日志-->
|
||||||
|
<filter type="log4net.Filter.LevelRangeFilter">
|
||||||
|
<param name="LevelMin" value="ALL"/>
|
||||||
|
<param name="LevelMax" value="FATAL"/>
|
||||||
|
</filter>
|
||||||
|
<layout type="log4net.Layout.PatternLayout">
|
||||||
|
<conversionPattern value="%date [%thread] %-5level %logger - %Message%newline"/>
|
||||||
|
</layout>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!--SqlServer形式-->
|
||||||
|
<!--log4net日志配置:http://logging.apache.org/log4net/release/config-examples.html -->
|
||||||
|
<appender name="AdoNetAppender_SqlServer" type="log4net.Appender.AdoNetAppender">
|
||||||
|
<!--日志缓存写入条数 设置为0时只要有一条就立刻写到数据库-->
|
||||||
|
<bufferSize value="0"/>
|
||||||
|
<!--日志数据库连接串-->
|
||||||
|
<connectionType
|
||||||
|
value="System.Data.SqlClient.SqlConnection,System.Data.SqlClient, Version=4.6.1.3, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
|
||||||
|
<connectionString
|
||||||
|
value="Data Source=192.168.3.98;Initial Catalog=LogManager;Persist Security Info=True;User ID=sa;Password=abc1234."/>
|
||||||
|
<commandText
|
||||||
|
value="INSERT INTO Log4Net ([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @Message, @exception)"/>
|
||||||
|
<parameter>
|
||||||
|
<parameterName value="@log_date"/>
|
||||||
|
<dbType value="DateTime"/>
|
||||||
|
<layout type="log4net.Layout.RawTimeStampLayout"/>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<parameterName value="@thread"/>
|
||||||
|
<dbType value="String"/>
|
||||||
|
<size value="255"/>
|
||||||
|
<layout type="log4net.Layout.PatternLayout">
|
||||||
|
<conversionPattern value="%thread"/>
|
||||||
|
</layout>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<parameterName value="@log_level"/>
|
||||||
|
<dbType value="String"/>
|
||||||
|
<size value="50"/>
|
||||||
|
<layout type="log4net.Layout.PatternLayout">
|
||||||
|
<conversionPattern value="%level"/>
|
||||||
|
</layout>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<parameterName value="@logger"/>
|
||||||
|
<dbType value="String"/>
|
||||||
|
<size value="255"/>
|
||||||
|
<layout type="log4net.Layout.PatternLayout">
|
||||||
|
<conversionPattern value="%logger"/>
|
||||||
|
</layout>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<parameterName value="@Message"/>
|
||||||
|
<dbType value="String"/>
|
||||||
|
<size value="4000"/>
|
||||||
|
<layout type="log4net.Layout.PatternLayout">
|
||||||
|
<conversionPattern value="%Message"/>
|
||||||
|
</layout>
|
||||||
|
</parameter>
|
||||||
|
<parameter>
|
||||||
|
<parameterName value="@exception"/>
|
||||||
|
<dbType value="String"/>
|
||||||
|
<size value="2000"/>
|
||||||
|
<layout type="log4net.Layout.ExceptionLayout"/>
|
||||||
|
</parameter>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<root>
|
||||||
|
|
||||||
|
<!--控制级别,由低到高: ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF-->
|
||||||
|
<!--OFF:0-->
|
||||||
|
<!--FATAL:FATAL-->
|
||||||
|
<!--ERROR: ERROR,FATAL-->
|
||||||
|
<!--WARN: WARN,ERROR,FATAL-->
|
||||||
|
<!--INFO: INFO,WARN,ERROR,FATAL-->
|
||||||
|
<!--DEBUG: INFO,WARN,ERROR,FATAL-->
|
||||||
|
<!--ALL: DEBUG,INFO,WARN,ERROR,FATAL-->
|
||||||
|
<priority value="ALL"/>
|
||||||
|
<!-- TODO 修改成你自己的类型 -->
|
||||||
|
<level value="WARN"/>
|
||||||
|
<appender-ref ref="rollingAppender"/>
|
||||||
|
<appender-ref ref="AdoNetAppender_SqlServer"/>
|
||||||
|
</root>
|
||||||
|
</log4net>
|
|
@ -0,0 +1,21 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Bunny.WebApi.Controllers.Base;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 主页请求
|
||||||
|
/// </summary>
|
||||||
|
[ApiController]
|
||||||
|
[Route("/")]
|
||||||
|
public class IndexController : ControllerBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 请求主页面响应内容
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("")]
|
||||||
|
public IActionResult Index()
|
||||||
|
{
|
||||||
|
return Ok("看到这里页面说明你已经成功启动了本项目:)\n\n如果觉得项目有用,打赏作者喝杯咖啡作为奖励>>https://gitee.com/BunnyBoss/bunny-template.git");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
using Bunny.Common.Exception;
|
||||||
|
using Bunny.Dao.Models.System;
|
||||||
|
using Bunny.Dao.Result;
|
||||||
|
using Bunny.Service.IService;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Bunny.WebApi.Controllers.Base;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 测试请求模板
|
||||||
|
/// </summary>
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class TemplateController : ControllerBase
|
||||||
|
{
|
||||||
|
public IBaseService? BaseService { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 测试走缓存
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("GetCacheController")]
|
||||||
|
public Result<string> CacheControl()
|
||||||
|
{
|
||||||
|
Response.Headers.CacheControl = "max-age=20";
|
||||||
|
return Result<string>.Success($"测试走缓存内容:{Response.Headers.CacheControl}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,140 @@
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
脚本包含:
|
||||||
|
1.了Log4Net写数据库日志脚本
|
||||||
|
2. Nlog写数据库日志脚本
|
||||||
|
*/
|
||||||
|
USE [master]
|
||||||
|
GO
|
||||||
|
|
||||||
|
/*
|
||||||
|
创建数据库LogManager
|
||||||
|
*/
|
||||||
|
CREATE DATABASE [LogManager];
|
||||||
|
|
||||||
|
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET COMPATIBILITY_LEVEL = 150
|
||||||
|
GO
|
||||||
|
IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled'))
|
||||||
|
begin
|
||||||
|
EXEC [LogManager].[dbo].[sp_fulltext_database] @action = 'enable'
|
||||||
|
end
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET ANSI_NULL_DEFAULT OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET ANSI_NULLS OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET ANSI_PADDING OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET ANSI_WARNINGS OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET ARITHABORT OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET AUTO_CLOSE OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET AUTO_SHRINK OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET AUTO_UPDATE_STATISTICS ON
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET CURSOR_CLOSE_ON_COMMIT OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET CURSOR_DEFAULT GLOBAL
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET CONCAT_NULL_YIELDS_NULL OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET NUMERIC_ROUNDABORT OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET QUOTED_IDENTIFIER OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET RECURSIVE_TRIGGERS OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET DISABLE_BROKER
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET AUTO_UPDATE_STATISTICS_ASYNC OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET DATE_CORRELATION_OPTIMIZATION OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET TRUSTWORTHY OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET ALLOW_SNAPSHOT_ISOLATION OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET PARAMETERIZATION SIMPLE
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET READ_COMMITTED_SNAPSHOT OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET HONOR_BROKER_PRIORITY OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET RECOVERY FULL
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET MULTI_USER
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET PAGE_VERIFY CHECKSUM
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET DB_CHAINING OFF
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET FILESTREAM( NON_TRANSACTED_ACCESS = OFF )
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET TARGET_RECOVERY_TIME = 60 SECONDS
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET DELAYED_DURABILITY = DISABLED
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET ACCELERATED_DATABASE_RECOVERY = OFF
|
||||||
|
GO
|
||||||
|
EXEC sys.sp_db_vardecimal_storage_format N'LogManager', N'ON'
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET QUERY_STORE = OFF
|
||||||
|
GO
|
||||||
|
USE [LogManager]
|
||||||
|
GO
|
||||||
|
/****** Object: Table [dbo].[Log4Net] Script Date: 2021/11/26 10:56:35 ******/
|
||||||
|
SET ANSI_NULLS ON
|
||||||
|
GO
|
||||||
|
SET QUOTED_IDENTIFIER ON
|
||||||
|
GO
|
||||||
|
|
||||||
|
/*
|
||||||
|
创建Log4net的表
|
||||||
|
*/
|
||||||
|
|
||||||
|
CREATE TABLE [dbo].[Log4Net](
|
||||||
|
[Id] [int] IDENTITY(1,1) NOT NULL,
|
||||||
|
[Date] [datetime] NOT NULL,
|
||||||
|
[Thread] [varchar](255) NOT NULL,
|
||||||
|
[Level] [varchar](50) NOT NULL,
|
||||||
|
[Logger] [varchar](255) NOT NULL,
|
||||||
|
[Message] [varchar](4000) NOT NULL,
|
||||||
|
[Exception] [varchar](2000) NULL
|
||||||
|
) ON [PRIMARY]
|
||||||
|
GO
|
||||||
|
/****** Object: Table [dbo].[NLog] Script Date: 2021/11/26 10:56:35 ******/
|
||||||
|
SET ANSI_NULLS ON
|
||||||
|
GO
|
||||||
|
SET QUOTED_IDENTIFIER ON
|
||||||
|
GO
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
创建Log4net的表
|
||||||
|
*/
|
||||||
|
|
||||||
|
CREATE TABLE [dbo].[NLog](
|
||||||
|
[Id] [int] IDENTITY(1,1) NOT NULL,
|
||||||
|
[Application] [nvarchar](50) NOT NULL,
|
||||||
|
[Logged] [datetime] NOT NULL,
|
||||||
|
[Level] [nvarchar](50) NOT NULL,
|
||||||
|
[Message] [nvarchar](max) NOT NULL,
|
||||||
|
[Logger] [nvarchar](250) NULL,
|
||||||
|
[Callsite] [nvarchar](max) NULL,
|
||||||
|
[Exception] [nvarchar](max) NULL,
|
||||||
|
CONSTRAINT [PK_dbo.Log] PRIMARY KEY CLUSTERED
|
||||||
|
(
|
||||||
|
[Id] ASC
|
||||||
|
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
|
||||||
|
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
||||||
|
GO
|
||||||
|
USE [master]
|
||||||
|
GO
|
||||||
|
ALTER DATABASE [LogManager] SET READ_WRITE
|
||||||
|
GO
|
|
@ -0,0 +1,24 @@
|
||||||
|
#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging.
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
|
||||||
|
USER app
|
||||||
|
WORKDIR /app
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
WORKDIR /src
|
||||||
|
COPY ["Bunny.WebApi/Bunny.WebApi.csproj", "Bunny.WebApi/"]
|
||||||
|
RUN dotnet restore "./Bunny.WebApi/Bunny.WebApi.csproj"
|
||||||
|
COPY . .
|
||||||
|
WORKDIR "/src/Bunny.WebApi"
|
||||||
|
RUN dotnet build "./Bunny.WebApi.csproj" -c $BUILD_CONFIGURATION -o /app/build
|
||||||
|
|
||||||
|
FROM build AS publish
|
||||||
|
ARG BUILD_CONFIGURATION=Release
|
||||||
|
RUN dotnet publish "./Bunny.WebApi.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
|
FROM base AS final
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=publish /app/publish .
|
||||||
|
ENTRYPOINT ["dotnet", "Bunny.WebApi.dll"]
|
|
@ -0,0 +1,43 @@
|
||||||
|
using Bunny.WebApi.Config;
|
||||||
|
using IGeekFan.AspNetCore.Knife4jUI;
|
||||||
|
|
||||||
|
namespace Bunny.WebApi;
|
||||||
|
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
// 其它配置
|
||||||
|
BaseConfig.Initialize(builder);
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
if (app.Environment.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
|
||||||
|
app.UseKnife4UI(options =>
|
||||||
|
{
|
||||||
|
options.RoutePrefix = "bunnyDocs"; // serve the UI at root
|
||||||
|
options.DocumentTitle = "Bunny 文档";
|
||||||
|
options.SwaggerEndpoint("//swagger/v1/swagger.json", "Bunny 文档");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跨域配置
|
||||||
|
app.UseCors();
|
||||||
|
|
||||||
|
// 身份验证
|
||||||
|
app.UseAuthentication();
|
||||||
|
|
||||||
|
// 授权
|
||||||
|
app.UseAuthorization();
|
||||||
|
app.MapControllers();
|
||||||
|
app.UseRouting();
|
||||||
|
// app.UseWebSockets();
|
||||||
|
|
||||||
|
app.Run();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": false,
|
||||||
|
"launchUrl": "bunnyDocs",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"dotnetRunMessages": false,
|
||||||
|
"applicationUrl": "http://*:8900"
|
||||||
|
},
|
||||||
|
"Container (Dockerfile)": {
|
||||||
|
"commandName": "Docker",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_HTTPS_PORTS": "8900",
|
||||||
|
"ASPNETCORE_HTTP_PORTS": "8900"
|
||||||
|
},
|
||||||
|
"publishAllPorts": true,
|
||||||
|
"useSSL": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "https://localhost:8900",
|
||||||
|
"sslPort": 8900
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning",
|
||||||
|
"Microsoft.EntityFrameworkCore": "Information"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning",
|
||||||
|
"Microsoft.EntityFrameworkCore": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"DataBase": {
|
||||||
|
"SqlServerConnection": "Data Source=192.168.3.98;Initial Catalog=BunnyTest;Persist Security Info=True;User ID=sa;Password=abc1234.",
|
||||||
|
"TimeOut": 6
|
||||||
|
},
|
||||||
|
"JWT": {
|
||||||
|
"Issuer": "Issuer",
|
||||||
|
"Audience": "Audience"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue