✨ 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