diff --git a/Tools/RegisterService.bat b/Tools/RegisterService.bat new file mode 100644 index 0000000..524564e --- /dev/null +++ b/Tools/RegisterService.bat @@ -0,0 +1,2 @@ +sc create StaffManagement start= auto binPath= "C:\Services\FAuth\FAuth.exe" +pause \ No newline at end of file diff --git a/Tools/StartService.bat b/Tools/StartService.bat new file mode 100644 index 0000000..93fc6aa --- /dev/null +++ b/Tools/StartService.bat @@ -0,0 +1,2 @@ +sc start StaffManagement +pause \ No newline at end of file diff --git a/Tools/StopService.bat b/Tools/StopService.bat new file mode 100644 index 0000000..4f4a1e2 --- /dev/null +++ b/Tools/StopService.bat @@ -0,0 +1,2 @@ +sc stop StaffManagement +pause \ No newline at end of file diff --git a/Tools/UnRegisterService.bat b/Tools/UnRegisterService.bat new file mode 100644 index 0000000..2cb11e9 --- /dev/null +++ b/Tools/UnRegisterService.bat @@ -0,0 +1,2 @@ +sc delete StaffManagement +pause \ No newline at end of file diff --git a/src/StaffManagement/Controllers/ApiControllerBase.cs b/src/StaffManagement/Controllers/ApiControllerBase.cs new file mode 100644 index 0000000..9e49e61 --- /dev/null +++ b/src/StaffManagement/Controllers/ApiControllerBase.cs @@ -0,0 +1,41 @@ +using System; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Unicode; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using StaffManagement.Database; +using StaffManagement.Extensions; + +namespace StaffManagement.Controllers +{ + /// + /// api控制器基类 + /// + [Area("api")] + //[ApiController] + [Route("api/[Controller]/[Action]")] + [ServiceFilter(typeof(ApiExceptionFilterAttribute))] + [ProducesResponseType(typeof(ApiErrorResult),500)] + [ProducesResponseType(typeof(ApiErrorResult),400)] + public abstract class ApiControllerBase:ControllerBase + { + public ApiControllerBase(ILogger logger,IServiceProvider service) : base(logger,service) { + } + + /// + /// 记录保存请求和响应日志 + /// + /// 请求信息类型 + /// 响应信息类型 + /// 请求数据 + /// 响应数据 + protected void SaveLogger(TRequest data,TResponse result) { + var requestStr = this.JsonSerialize(data); + var responseStr = this.JsonSerialize(result); + var logmsg = $"{this.Prefix}\n请求消息:{requestStr}\n响应消息{responseStr}"; + this.Logger.LogInformation(logmsg); + } + } +} diff --git a/src/StaffManagement/Controllers/ControllerBase.cs b/src/StaffManagement/Controllers/ControllerBase.cs new file mode 100644 index 0000000..baf0896 --- /dev/null +++ b/src/StaffManagement/Controllers/ControllerBase.cs @@ -0,0 +1,79 @@ +using System; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Unicode; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using StaffManagement.Database; +using StaffManagement.Extensions; + +namespace StaffManagement.Controllers +{ + /// + /// 控制器类基类 + /// + //[Route("[Controller]/[Action]")] + public abstract class ControllerBase:Controller + { + /// + /// 日志记录服务 + /// + public ILogger Logger { get; set; } + /// + /// 服务集合 + /// + public IServiceProvider Services { get; set; } + /// + /// 数据库 + /// + public SMDbContext Db { get; set; } + /// + /// 缓冲帮助器 + /// + public CacheHelper Cache { get; private set; } + /// + /// 获取请求的方法前缀 + /// + protected string Prefix { + get { + var con = this.RouteData.Values["controller"]; + var ac = this.RouteData.Values["action"]; + return $":{con}:{ac}"; + } + } + + /// + /// 通过日志组件和服务集合生成控制器 + /// + /// 控制器日志组件 + /// 服务集合 + public ControllerBase(ILogger logger,IServiceProvider service) { + this.Logger = logger; + this.Services = service; + this.Db = service.GetService(); + this.Cache = service.GetService(); + } + + /// + /// 从对象序列化字符串 + /// + /// 对象类型 + /// 要序列化的对象 + /// 字符串 + protected string JsonSerialize(T obj) { + return JsonSerializer.Serialize(obj,new JsonSerializerOptions { + Encoder = JavaScriptEncoder.Create(UnicodeRanges.All), + }); + } + /// + /// 从字符串反序列化对象 + /// + /// 对象的类型 + /// json字符串 + /// 对象实例 + protected T JsonDeserialize(string json) { + return JsonSerializer.Deserialize(json); + } + } +} diff --git a/src/StaffManagement/Controllers/HomeController.cs b/src/StaffManagement/Controllers/Web/HomeController.cs similarity index 71% rename from src/StaffManagement/Controllers/HomeController.cs rename to src/StaffManagement/Controllers/Web/HomeController.cs index 030adae..6521f31 100644 --- a/src/StaffManagement/Controllers/HomeController.cs +++ b/src/StaffManagement/Controllers/Web/HomeController.cs @@ -7,14 +7,11 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using StaffManagement.Models; -namespace StaffManagement.Controllers +namespace StaffManagement.Controllers.Web { - public class HomeController:Controller + public class HomeController:WebControllerBase { - private readonly ILogger _logger; - - public HomeController(ILogger logger) { - _logger = logger; + public HomeController(ILogger logger,IServiceProvider service) : base(logger,service) { } public IActionResult Index() { diff --git a/src/StaffManagement/Controllers/WebControllerBase.cs b/src/StaffManagement/Controllers/WebControllerBase.cs new file mode 100644 index 0000000..963ae3b --- /dev/null +++ b/src/StaffManagement/Controllers/WebControllerBase.cs @@ -0,0 +1,15 @@ +using System; +using Microsoft.Extensions.Logging; + +namespace StaffManagement.Controllers +{ + /// + /// 控制器类基类 + /// + //[Route("[Controller]/[Action]")] + public abstract class WebControllerBase:ControllerBase + { + public WebControllerBase(ILogger logger,IServiceProvider service) : base(logger,service) { + } + } +} diff --git a/src/StaffManagement/Database/SMDbContext.cs b/src/StaffManagement/Database/SMDbContext.cs new file mode 100644 index 0000000..77386eb --- /dev/null +++ b/src/StaffManagement/Database/SMDbContext.cs @@ -0,0 +1,16 @@ +using Microsoft.EntityFrameworkCore; + +namespace StaffManagement.Database +{ + /// + /// 员工管理数据上下文 + /// + public class SMDbContext:DbContext + { + /// + /// 通过配置的数据库选项获取数据库上下文 + /// + /// + public SMDbContext(DbContextOptions options) : base(options) { } + } +} diff --git a/src/StaffManagement/Extensions/ApiErrorResult.cs b/src/StaffManagement/Extensions/ApiErrorResult.cs new file mode 100644 index 0000000..5b219c2 --- /dev/null +++ b/src/StaffManagement/Extensions/ApiErrorResult.cs @@ -0,0 +1,17 @@ +namespace StaffManagement.Extensions +{ + /// + /// api返回异常 + /// + public class ApiErrorResult + { + /// + /// 异常信息 + /// + public string Message { get; set; } + /// + /// 异常编号 + /// + public string Id { get; set; } + } +} diff --git a/src/StaffManagement/Extensions/ApiException.cs b/src/StaffManagement/Extensions/ApiException.cs new file mode 100644 index 0000000..6bda5a0 --- /dev/null +++ b/src/StaffManagement/Extensions/ApiException.cs @@ -0,0 +1,29 @@ +using System; + +namespace StaffManagement.Extensions +{ + /// + /// 表示请求错误,服务器无法处理 + /// + public class ApiException:Exception + { + public object Param { get; set; } + + /// + /// 通过提供异常信息,返回api错误异常 + /// + /// 错误消息 + public ApiException(string msg) : base(msg) { + this.Param = null; + } + + /// + /// 通过提供异常信息,返回api错误异常 + /// + /// 错误消息 + /// 请求绑定参数 + public ApiException(object param,string msg) : base(msg) { + this.Param = param; + } + } +} diff --git a/src/StaffManagement/Extensions/ApiExceptionFilterAttribute.cs b/src/StaffManagement/Extensions/ApiExceptionFilterAttribute.cs new file mode 100644 index 0000000..f4ffe1c --- /dev/null +++ b/src/StaffManagement/Extensions/ApiExceptionFilterAttribute.cs @@ -0,0 +1,46 @@ +using System; +using System.Text; +using System.Text.Json; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; + +namespace StaffManagement.Extensions +{ + /// + /// Api控制器返回异常 + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,AllowMultiple = true,Inherited = true)] + public class ApiExceptionFilterAttribute:ExceptionFilterAttribute + { + public ILogger Logger { get; set; } + + public ApiExceptionFilterAttribute(ILogger logger) { + this.Logger = logger; + } + public override void OnException(ExceptionContext context) { + var id = Guid.NewGuid().ToString("N"); + int code; + var errMsg = new StringBuilder(); + errMsg.Append($"错误ID:{id}\n"); + if(context.Exception is ApiException ae) { + code = StatusCodes.Status400BadRequest; + var rqStr = JsonSerializer.Serialize(ae.Param); + errMsg.Append($"请求参数:{rqStr}\n"); + } else { + code = StatusCodes.Status500InternalServerError; + } + errMsg.Append($"异常信息:{ context.Exception}\n"); + this.Logger.LogError(errMsg.ToString()); + + var result = new ApiErrorResult { + Message = context.Exception.Message, + Id = id, + }; + + context.Result = new JsonResult(result) { StatusCode = code }; + } + + } +} diff --git a/src/StaffManagement/Extensions/CacheHelper.cs b/src/StaffManagement/Extensions/CacheHelper.cs new file mode 100644 index 0000000..7ba3b2b --- /dev/null +++ b/src/StaffManagement/Extensions/CacheHelper.cs @@ -0,0 +1,99 @@ +using System; +using System.Text; +using Falcon.Extend; +using Microsoft.Extensions.Logging; + +namespace StaffManagement.Extensions +{ + /// + /// 缓冲帮助器 + /// + public class CacheHelper + { + /// + /// 基础缓冲 + /// + public ICacheProvider Cache { get; set; } + /// + /// Json序列化 + /// + public IJsonProvider JsonProvider { get; set; } + /// + /// 日志记录 + /// + public ILogger Logger { get; set; } + + public CacheHelper(ICacheProvider c,IJsonProvider j,ILogger logger) { + this.Cache = c; + this.JsonProvider = j; + this.Logger = logger; + } + /// + /// 从缓冲中获取数据 + /// + /// 用于生成key的对象类型 + /// 数据对象类型 + /// 缓冲方法前缀 + /// 用于生成key的对象 + /// 数据对象 + public T GetT(string prefix,TKey keyObj) where T : class, new() { + if(this.Cache == null) { + return null; + } + if(this.JsonProvider == null) { + return null; + } + var key = generateKey(prefix,keyObj); + if(string.IsNullOrEmpty(key)) { + return null; + } + var result = this.Cache.GetObj(key); + if(result != null) { + this.Logger.LogInformation($"Get data cache by [{key}]"); + } + return result; + } + /// + /// 保存缓冲 + /// + /// 用于生成key的对象类型 + /// 数据对象类型 + /// 缓冲方法前缀 + /// 用于生成key的对象 + /// 数据对象 + /// 缓冲时间 + /// + public T SetT(string prefix,TKey keyObj,T obj,TimeSpan timeSpan) where T : class { + if(this.Cache == null) { + return obj; + } + if(this.JsonProvider == null) { + return obj; + } + var key = generateKey(prefix,keyObj); + if(string.IsNullOrEmpty(key)) { + return obj; + } + this.Cache.SetCache(key,obj,timeSpan); + this.Logger.LogInformation($"Set data cache by [{key}]"); + return obj; + } + /// + /// 根据对象生成Key + /// + /// Key的类型 + /// 前缀 + /// Key对象 + /// 对应的Key字符串 + private string generateKey(string prefix,TK keyObj) { + var key = new StringBuilder(); + key.Append(prefix); + foreach(var p in keyObj.GetType().GetProperties()) { + var pn = p.Name; + var pv = p.GetValue(keyObj) ?? "nul"; + key.Append($":{pn}:{pv}"); + } + return key.ToString(); + } + } +} diff --git a/src/StaffManagement/Extensions/CookieHelper.cs b/src/StaffManagement/Extensions/CookieHelper.cs new file mode 100644 index 0000000..a1d20b9 --- /dev/null +++ b/src/StaffManagement/Extensions/CookieHelper.cs @@ -0,0 +1,79 @@ +using System; +using Microsoft.AspNetCore.CookiePolicy; +using Microsoft.AspNetCore.Http; + +namespace StaffManagement.Extensions +{ + /// + /// cookie帮助类 + /// + public static class CookieHelper + { + /// + /// 设置Cookie值 + /// + /// http上下文 + /// Cookie的键 + /// cookie值 + /// cookie有效期。分钟,无限传null或不传 + public static void SetCookies(this HttpContext httpContext,string key,string value,int? minutes = null) { + SetCookies(httpContext.Response,key,value,minutes); + } + + /// + /// 设置Cookie值 + /// + /// http响应 + /// Cookie的键 + /// cookie值 + /// cookie有效期。分钟,无限传null或不传 + public static void SetCookies(this HttpResponse response,string key,string value,int? minutes = null) { + if(minutes.HasValue) { + response.Cookies.Append(key,value,new CookieOptions { + Expires = DateTime.Now.AddMinutes(minutes.Value) + }); + } else { + response.Cookies.Append(key,value); + } + } + /// + /// 删除cookie + /// + /// http上下文 + /// 要删除的键 + public static void DeleteCookies(this HttpContext httpContext,string key) { + DeleteCookies(httpContext.Response,key); + } + + /// + /// 删除cookie + /// + /// http响应 + /// 要删除的键 + public static void DeleteCookies(this HttpResponse response,string key) { + response.Cookies.Delete(key); + } + /// + /// 获取cookie中的值 + /// + /// http上下文 + /// cookie的键 + /// 保存的值 + public static string GetCookiesValue(this HttpContext httpContext,string key) { + return GetCookiesValue(httpContext.Request,key); + } + + /// + /// 获取cookie中的值 + /// + /// http请求 + /// cookie的键 + /// 保存的值 + public static string GetCookiesValue(this HttpRequest request,string key) { + if(request.Cookies.TryGetValue(key,out string value)) { + return value; + } + return string.Empty; + } + } +} diff --git a/src/StaffManagement/StaffManagement.csproj b/src/StaffManagement/StaffManagement.csproj index f44e8dd..3bf5232 100644 --- a/src/StaffManagement/StaffManagement.csproj +++ b/src/StaffManagement/StaffManagement.csproj @@ -22,6 +22,12 @@ + + + + + + diff --git a/src/StaffManagement/Startup.cs b/src/StaffManagement/Startup.cs index 8af2fd3..d984c2c 100644 --- a/src/StaffManagement/Startup.cs +++ b/src/StaffManagement/Startup.cs @@ -1,12 +1,18 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.IO; +using System.Text.Encodings.Web; +using System.Text.Unicode; +using Falcon.Extend; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Redis; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.OpenApi.Models; +using StaffManagement.Database; +using StaffManagement.Extensions; namespace StaffManagement { @@ -18,9 +24,40 @@ namespace StaffManagement public IConfiguration Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddControllersWithViews(); + //עJsonл + services.AddMsJsonProvider(); + //עݿ + services.AddDbContext(option => { + //option.UseSqlServer(this.Configuration.GetValue("Database:connStr")); + option.UseMySql(this.Configuration.GetValue("Database:dbConnStr")); + }); + //עRedis + var rop = this.Configuration.GetSection("Redis").Get(); + services.AddRedis(rop); + //עỺ + services.AddSingleton(); + //עMVC + services.AddControllersWithViews() + .AddJsonOptions(option => { + option.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All); + option.JsonSerializerOptions.PropertyNamingPolicy = null; + }); + + //עSwagger + services.AddSwaggerGen(c => { + var option = this.Configuration.GetSection("SwaggerDoc"); + c.SwaggerDoc("V1",new OpenApiInfo { + Title = option.GetValue("Title"), + Version = option.GetValue("Version"), + Description = option.GetValue("Description"), + }); + var basePath = AppContext.BaseDirectory; + c.IncludeXmlComments(Path.Combine(basePath,"StaffManagement.xml"),true); + c.AddXmlEnumEnable(Path.Combine(basePath,"StaffManagement.xml")); + }); + //עapi + services.AddScoped(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -32,6 +69,16 @@ namespace StaffManagement } app.UseStaticFiles(); + app.UseSwagger(); + app.UseSwaggerUI(c => { + c.SwaggerEndpoint("/swagger/V1/swagger.json","ӿĵ"); + //c.RoutePrefix = ""; + c.DocumentTitle = "һվʽҽƾӿĵ"; + c.InjectStylesheet("/css/swagger.css"); + c.InjectJavascript("/lib/jquery/dist/jquery.min.js"); + c.InjectJavascript("/js/swagger.js"); + }); + app.UseRouting(); app.UseAuthorization(); diff --git a/src/StaffManagement/appsettings.json b/src/StaffManagement/appsettings.json index 81ff877..d1ad45a 100644 --- a/src/StaffManagement/appsettings.json +++ b/src/StaffManagement/appsettings.json @@ -6,5 +6,21 @@ "Microsoft.Hosting.Lifetime": "Information" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "Database": { + "dbConnStr": "Server=localhost;Port=3306;Database=FAuth;Uid=falcon;Pwd=falcon;" + }, + "Redis": { + "InstanceName": "", + "Configuration": "127.0.0.1:7001,password=123654" + }, + "SwaggerDoc": { + "Title": "ԱAPIӿ", + "Version": "1.0", + "Description": "ͳһAPIӿƣ鿴ϸӿڹ淶", + "Contact": { + "Name": "֧(ӣ)", + "Url": "" + } + } }