diff --git a/Falcon.SugarApi.Test/BackTaskTest.cs b/Falcon.SugarApi.Test/BackTaskTest.cs new file mode 100644 index 0000000..4733012 --- /dev/null +++ b/Falcon.SugarApi.Test/BackTaskTest.cs @@ -0,0 +1,61 @@ +using Falcon.SugarApi.BackTask; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Threading; +using System; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Hosting; + +namespace Falcon.SugarApi.Test +{ + [TestClass] + public class BackTaskTest + { + [TestMethod] + public void BackgroundLongTaskTestMethod() { + var task = new BackTaskObject(); + Assert.IsTrue(task.State == 0, "初始化状态错误"); + var token = new CancellationTokenSource(); + task.StartAsync(token.Token); + Thread.Sleep(1 * 1000); + Assert.IsTrue(task.State == 1, "启动状态错误"); + Thread.Sleep(5 * 1000); + task.StopAsync(token.Token).Wait(); + Thread.Sleep(2 * 1000); + Assert.IsTrue(task.State == 2, "停止状态错误"); + Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss")}:测试完成"); + } + } + + public class BackTaskObject : BackgroundLongTask + { + public override float RunTimespan => 1; + public ILogger Log { get; set; } + public int State { get; set; } = 0; + + public BackTaskObject() { + this.Log = new TestLog(); + } + + protected override void OnStop(BackgroundLongTask t) { + State = 2; + this.Log.LogInformation($"{DateTime.Now.ToString("HH:mm:ss")}:Test OnStop!"); + base.OnStop(t); + } + + protected override void OnStart(BackgroundLongTask t) { + State = 1; + this.Log.LogInformation($"{DateTime.Now.ToString("HH:mm:ss")}:Test OnStart!"); + base.OnStart(t); + } + + protected override void OnCompleted(BackgroundLongTask t) { + this.Log.LogInformation($"{DateTime.Now.ToString("HH:mm:ss")}:Test OnCompleted!"); + base.OnCompleted(t); + } + + protected override void Run() { + this.Log.LogInformation($"{DateTime.Now.ToString("HH:mm:ss")}:Test Run!"); + } + + } +} \ No newline at end of file diff --git a/Falcon.SugarApi.Test/Falcon.SugarApi.Test.csproj b/Falcon.SugarApi.Test/Falcon.SugarApi.Test.csproj new file mode 100644 index 0000000..cdd13dd --- /dev/null +++ b/Falcon.SugarApi.Test/Falcon.SugarApi.Test.csproj @@ -0,0 +1,21 @@ + + + + net5;net6 + enable + + false + + + + + + + + + + + + + + diff --git a/Falcon.SugarApi.Test/TestLog.cs b/Falcon.SugarApi.Test/TestLog.cs new file mode 100644 index 0000000..0372f72 --- /dev/null +++ b/Falcon.SugarApi.Test/TestLog.cs @@ -0,0 +1,21 @@ +锘縰sing System; +using Microsoft.Extensions.Logging; + +namespace Falcon.SugarApi.Test +{ + /// + /// 娴嬭瘯鐢↙ogger瀹炰緥 + /// + public class TestLog : ILogger + { + public IDisposable BeginScope(TState state) { + throw new NotImplementedException(); + } + + public bool IsEnabled(LogLevel logLevel) => true; + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { + Console.WriteLine(state?.ToString()); + } + } +} \ No newline at end of file diff --git a/Falcon.SugarApi.sln b/Falcon.SugarApi.sln new file mode 100644 index 0000000..1626b14 --- /dev/null +++ b/Falcon.SugarApi.sln @@ -0,0 +1,31 @@ +锘 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32228.430 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Falcon.SugarApi", "Falcon.SugarApi\Falcon.SugarApi.csproj", "{0E6BB911-30D2-416F-9B82-23B2C7F0C83F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Falcon.SugarApi.Test", "Falcon.SugarApi.Test\Falcon.SugarApi.Test.csproj", "{EADC8D8F-449C-4C4C-8168-0075B333494B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0E6BB911-30D2-416F-9B82-23B2C7F0C83F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0E6BB911-30D2-416F-9B82-23B2C7F0C83F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0E6BB911-30D2-416F-9B82-23B2C7F0C83F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0E6BB911-30D2-416F-9B82-23B2C7F0C83F}.Release|Any CPU.Build.0 = Release|Any CPU + {EADC8D8F-449C-4C4C-8168-0075B333494B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EADC8D8F-449C-4C4C-8168-0075B333494B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EADC8D8F-449C-4C4C-8168-0075B333494B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EADC8D8F-449C-4C4C-8168-0075B333494B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {88773585-6316-44D6-B95A-F419F746F361} + EndGlobalSection +EndGlobal diff --git a/Falcon.SugarApi/ApiDefinistions/ApiBaseOptionController.cs b/Falcon.SugarApi/ApiDefinistions/ApiBaseOptionController.cs new file mode 100644 index 0000000..eef21ac --- /dev/null +++ b/Falcon.SugarApi/ApiDefinistions/ApiBaseOptionController.cs @@ -0,0 +1,90 @@ +锘縰sing Falcon.SugarApi.DatabaseDefinitions; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; + +namespace Falcon.SugarApi.ApiDefinistions +{ + /// + /// api鎺ュ彛鍩虹鏁版嵁鎿嶄綔鎺у埗鍣 + /// + /// 鏁版嵁绫诲瀷 + public abstract class ApiBaseOptionController : ApiControllerBase, IApiBaseControllerOption where T : SugarTableBase, new() + { + /// + /// 瀹炰緥鍖栧熀纭鏁版嵁鎿嶄綔鎺у埗鍣 + /// + /// 鏈嶅姟鎻愪緵鍣 + public ApiBaseOptionController(IServiceProvider service) : base(service) { + } + /// + /// 鎻掑叆涓鏉℃柊璁板綍 + /// + /// 瑕佸鍔犵殑鏁版嵁 + /// 澧炲姞浠ュ悗鐨勬暟鎹 + [HttpPost] + public virtual T AddOne(T data) { + this.SugarDb.Insert(data, data.CreateBy); + return data; + } + /// + /// 鍒犻櫎涓鏉℃暟鎹 + /// + /// 鍒犻櫎鐨勬暟鎹潯浠讹紝鏁版嵁Id + /// 鍒犻櫎鍚庣殑鏁版嵁 + [HttpPost] + public virtual T DeleteOne(OneRecoder filter) { + var id = filter.Id; + this.SugarDb.Delete(id, filter.OptionBy); + return this.SugarDb.Queryable().First(m => m.Id == id); + } + /// + /// 淇敼鏁版嵁 + /// + /// 瑕佷慨鏀圭殑鏁版嵁 + /// 淇敼鍚庣殑鏁版嵁 + [HttpPost] + public virtual T Edit(T data) { + this.SugarDb.Update(data, data.UpdateBy); + return data; + } + /// + /// 鏌ヨ涓鏉℃暟鎹 + /// + /// 鏁版嵁鏌ヨ鏉′欢锛屾彁渚涙暟鎹甀D + /// 鏌ヨ鍒扮殑鏁版嵁闆嗗悎 + [HttpPost] + public virtual IEnumerable GetOne(OneRecoder filter) { + return this.SugarDb.Queryable().In(filter.Id).ToList(); + } + /// + /// 鑾峰彇涓鏉℃祴璇曟暟鎹紝浠呭湪娴嬭瘯闃舵浣跨敤 + /// + /// 娴嬭瘯鏁版嵁 + [HttpPost] + public virtual T GetTest() { + return new T().SetTestModel() as T; + } + /// + /// 鑾峰彇鏁版嵁鍒楄〃 + /// + /// 鍒嗛〉鏉′欢 + /// 鍒嗛〉鍚庣殑鏁版嵁鍒楄〃 + /// 鏁版嵁寮傚父 + [HttpPost] + public virtual IEnumerable ListAll(PageData page) { + if (page.OrderBy.IsNullOrEmpty()) { + throw new ApiException("蹇呴』鎻愪緵OrderBy锛屽垎椤靛皢鎸夋杩涜鎺掑簭銆"); + } + return this.SugarDb.Queryable().OrderBy(page.OrderBy).ToPageList(page.Page, page.PageSize); + } + + /// + /// 鐩稿叧琛ㄥ垵濮嬪寲 + /// + /// 闇瑕佸垵濮嬪寲鐨勮〃绫诲瀷 + protected void DataTableInit(params Type[] types) { + this.SugarDb.UpdateTableStructure(types); + } + } +} diff --git a/Falcon.SugarApi/ApiDefinistions/ApiControllerBase.cs b/Falcon.SugarApi/ApiDefinistions/ApiControllerBase.cs new file mode 100644 index 0000000..11bd30a --- /dev/null +++ b/Falcon.SugarApi/ApiDefinistions/ApiControllerBase.cs @@ -0,0 +1,100 @@ +锘縰sing Falcon.SugarApi.DatabaseDefinitions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.Text.Encodings.Web; +using System.Text.Json; +using System.Text.Unicode; + +namespace Falcon.SugarApi.ApiDefinistions +{ + /// + /// api鎺у埗鍣ㄥ熀绫 + /// + [Area("api")] + [ApiController] + [Route("api/[Controller]/[Action]")] + public abstract class ApiControllerBase : ControllerBase + { + /// + /// 鏃ュ織璁板綍鏈嶅姟 + /// + public ILogger Logger { get; set; } + /// + /// 鏈嶅姟闆嗗悎 + /// + public IServiceProvider Services { get; set; } + /// + /// Sugar鏁版嵁搴 + /// + public SugarDbContext SugarDb { get; set; } + /// + /// 鑾峰彇璇锋眰鐨勬柟娉曞墠缂 + /// + protected virtual string Prefix { + get { + var con = this.RouteData.Values["controller"]; + var ac = this.RouteData.Values["action"]; + return $":{con}:{ac}"; + } + } + + protected ApiControllerBase(IServiceProvider service) { + this.Services = service; + this.Logger = service.GetService(typeof(ILogger<>).MakeGenericType(GetType())) as ILogger; + this.SugarDb = service.GetService(); + } + + /// + /// 璁板綍淇濆瓨璇锋眰鍜屽搷搴旀棩蹇 + /// + /// 璇锋眰淇℃伅绫诲瀷 + /// 鍝嶅簲淇℃伅绫诲瀷 + /// 璇锋眰鏁版嵁 + /// 鍝嶅簲鏁版嵁 + protected virtual 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); + } + + /// + /// 浠庡璞″簭鍒楀寲瀛楃涓 + /// + /// 瀵硅薄绫诲瀷 + /// 瑕佸簭鍒楀寲鐨勫璞 + /// 瀛楃涓 + 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); + } + + /// + /// 鎶涘嚭api寮傚父 + /// + /// 寮傚父娑堟伅 + /// 鍐呴儴寮傚父 + /// api寮傚父 + protected virtual void ThrowApiException(string msg, Exception innException) + => throw new ApiException(msg, innException); + /// + /// 鎶涘嚭api寮傚父 + /// + /// 寮傚父娑堟伅 + /// api寮傚父 + protected virtual void ThrowApiException(string msg) + => throw new ApiException(msg); + } +} diff --git a/Falcon.SugarApi/ApiDefinistions/ApiException.cs b/Falcon.SugarApi/ApiDefinistions/ApiException.cs new file mode 100644 index 0000000..727cfdc --- /dev/null +++ b/Falcon.SugarApi/ApiDefinistions/ApiException.cs @@ -0,0 +1,26 @@ +锘縰sing System; + +namespace Falcon.SugarApi.ApiDefinistions +{ + /// + /// api寮傚父鍩虹被 + /// + public class ApiException : Exception + { + /// + /// 閫氳繃寮傚父淇℃伅鍒涘缓寮傚父 + /// + /// 寮傚父淇℃伅 + public ApiException(string message) : base(message) + { + } + /// + /// 閫氳繃寮傚父淇℃伅鍜屽唴閮ㄥ紓甯稿垱寤哄紓甯 + /// + /// 寮傚父淇℃伅 + /// 鍐呴儴寮傚父 + public ApiException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/Falcon.SugarApi/ApiDefinistions/ApiResponseTypeModelProvider.cs b/Falcon.SugarApi/ApiDefinistions/ApiResponseTypeModelProvider.cs new file mode 100644 index 0000000..8f38acb --- /dev/null +++ b/Falcon.SugarApi/ApiDefinistions/ApiResponseTypeModelProvider.cs @@ -0,0 +1,60 @@ +锘縰sing Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace Falcon.SugarApi.ApiDefinistions +{ + /// + /// api鎺ュ彛杩斿洖鍊兼ā鍨嬪畾涔 + /// + public class ApiResponseTypeModelProvider : IApplicationModelProvider + { + public int Order => 1; + + public void OnProvidersExecuted(ApplicationModelProviderContext context) + { + } + + public void OnProvidersExecuting(ApplicationModelProviderContext context) + { + foreach (ControllerModel controller in context.Result.Controllers) + { + foreach (ActionModel action in controller.Actions) + { + if (!isApiAction(action)) + { + continue; + } + var art = action.ActionMethod.ReturnType; + Type returnType = + art.IsGenericType && art.IsAssignableFrom(typeof(Task<>)) ? art.GenericTypeArguments[0].GetGenericArguments()[0] : + art; + action.Filters.Add(new ProducesResponseTypeAttribute(returnType, StatusCodes.Status200OK)); + action.Filters.Add(new ProducesResponseTypeAttribute(typeof(ExceptionModel), StatusCodes.Status400BadRequest)); + } + } + } + + /// + /// 鏂规硶鏄惁涓篴pi鏂规硶 + /// + /// Action妯″瀷 + /// 鏄痑pi鏂规硶杩斿洖true锛屽惁鍒檉alse + protected static bool isApiAction(ActionModel am) + { + if (am.Controller.Attributes.Any(c => c is ApiControllerAttribute)) + { + return true; + } + if (am.Attributes.Any(c => c is ApiControllerAttribute)) + { + return true; + } + return false; + } + + } +} diff --git a/Falcon.SugarApi/ApiDefinistions/ExceptionModel.cs b/Falcon.SugarApi/ApiDefinistions/ExceptionModel.cs new file mode 100644 index 0000000..d64f324 --- /dev/null +++ b/Falcon.SugarApi/ApiDefinistions/ExceptionModel.cs @@ -0,0 +1,21 @@ +锘縩amespace Falcon.SugarApi.ApiDefinistions +{ + /// + /// 寮傚父杩斿洖妯″瀷 + /// + public class ExceptionModel + { + /// + /// 寮傚父缂栧彿 + /// + public string Id { get; set; } + /// + /// 寮傚父淇℃伅 + /// + public string Message { get; set; } + /// + /// 寮傚父鍙戠敓鏃堕棿 + /// + public string CreateDateTime { get; set; } + } +} diff --git a/Falcon.SugarApi/ApiDefinistions/HttpResponseExceptionFilter.cs b/Falcon.SugarApi/ApiDefinistions/HttpResponseExceptionFilter.cs new file mode 100644 index 0000000..5dbf0d0 --- /dev/null +++ b/Falcon.SugarApi/ApiDefinistions/HttpResponseExceptionFilter.cs @@ -0,0 +1,52 @@ +锘縰sing Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Logging; +using System; +using System.Text; + +namespace Falcon.SugarApi.ApiDefinistions +{ + public class HttpResponseExceptionFilter : IActionFilter, IOrderedFilter + { + /// + /// 鏃ュ織璁板綍鍣 + /// + public ILogger Logger { get; set; } + + public HttpResponseExceptionFilter(ILogger logger) + { + this.Logger = logger; + } + + public int Order { get; } = int.MaxValue - 10; + + public void OnActionExecuting(ActionExecutingContext context) { } + + public void OnActionExecuted(ActionExecutedContext context) + { + if (context.Exception == null || context.ExceptionHandled) + { + return; + } + var exception = context.Exception; + var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + var model = new ExceptionModel + { + Id = Guid.NewGuid().ToString(), + CreateDateTime = now, + Message = exception.Message, + }; + if (exception is ApiException) + { + context.Result = new ObjectResult(model) { StatusCode = 400, }; + context.ExceptionHandled = true; + } + var logmsg = new StringBuilder(); + logmsg.AppendLine($"寮傚父缂栧彿:{model.Id}"); + logmsg.AppendLine($"閿欒淇℃伅:{model.Message}"); + logmsg.AppendLine($"璇︾粏淇℃伅:"); + logmsg.AppendLine($"{exception}"); + this.Logger.LogError(logmsg.ToString()); + } + } +} diff --git a/Falcon.SugarApi/ApiDefinistions/IApiBaseControllerOption.cs b/Falcon.SugarApi/ApiDefinistions/IApiBaseControllerOption.cs new file mode 100644 index 0000000..75347b1 --- /dev/null +++ b/Falcon.SugarApi/ApiDefinistions/IApiBaseControllerOption.cs @@ -0,0 +1,48 @@ +锘縰sing Falcon.SugarApi.DatabaseDefinitions; +using System.Collections.Generic; + +namespace Falcon.SugarApi.ApiDefinistions +{ + /// + /// 瀹氫箟api鍩虹瀹炵幇鏂规硶 + /// + /// api瀵瑰簲鐨勬暟鎹被鍨 + public interface IApiBaseControllerOption where T : SugarTableBase + { + /// + /// 澧炲姞鏁版嵁 + /// + /// 鏁版嵁瀹炰綋 + /// 澧炲姞鍚庣殑鏁版嵁瀹炰綋 + T AddOne(T data); + /// + /// 鏍规嵁id鍒犻櫎涓涓暟鎹褰 + /// + /// 鍒犻櫎鏉′欢锛屼粎鎻愪緵鏁版嵁id + /// 鍒犻櫎鍚庣殑鏁版嵁瀹炰綋 + T DeleteOne(OneRecoder filter); + /// + /// 淇敼瀹炰綋鏁版嵁 + /// + /// 鏁版嵁瀹炰綋 + /// 淇敼鍚庣殑鏁版嵁瀹炰綋 + T Edit(T data); + /// + /// 鑾峰彇涓涓祴璇曟暟鎹 + /// + /// 涓涓祴璇曡褰 + T GetTest(); + /// + /// 鍒嗛〉鑾峰彇鏁版嵁 + /// + /// 鍒嗛〉鏁版嵁 + /// 鏁版嵁鍒楄〃 + IEnumerable ListAll(PageData page); + /// + /// 鏍规嵁id鑾峰彇涓鏉¤褰 + /// + /// 鍒犻櫎鏉′欢锛屼粎鎻愪緵鏁版嵁id + /// 鍒犻櫎鍚庣殑鏁版嵁鍒楄〃 + IEnumerable GetOne(OneRecoder filter); + } +} \ No newline at end of file diff --git a/Falcon.SugarApi/ApiDefinistions/OneRecoder.cs b/Falcon.SugarApi/ApiDefinistions/OneRecoder.cs new file mode 100644 index 0000000..dbb5b42 --- /dev/null +++ b/Falcon.SugarApi/ApiDefinistions/OneRecoder.cs @@ -0,0 +1,19 @@ +锘縰sing System; + +namespace Falcon.SugarApi.ApiDefinistions +{ + /// + /// 涓鏉¤褰 + /// + public class OneRecoder + { + /// + /// 鏁版嵁ID + /// + public Guid Id { get; set; } + /// + /// 鎿嶄綔鍛 + /// + public string OptionBy { get; set; } + } +} \ No newline at end of file diff --git a/Falcon.SugarApi/ApiDefinistions/PageData.cs b/Falcon.SugarApi/ApiDefinistions/PageData.cs new file mode 100644 index 0000000..385a2cb --- /dev/null +++ b/Falcon.SugarApi/ApiDefinistions/PageData.cs @@ -0,0 +1,21 @@ +锘縩amespace Falcon.SugarApi.ApiDefinistions +{ + /// + /// 鍒嗛〉鏁版嵁 + /// + public class PageData + { + /// + /// 椤靛彿鐮 + /// + public int Page { get; set; } = 0; + /// + /// 椤靛ぇ灏 + /// + public int PageSize { get; set; } = 50; + /// + /// 鎺掑簭渚濇嵁 鍒楀悕 desc鍊掑簭 + /// + public string OrderBy { get; set; } = "ID DESC"; + } +} \ No newline at end of file diff --git a/Falcon.SugarApi/BackTask/BackgroundLongTask.cs b/Falcon.SugarApi/BackTask/BackgroundLongTask.cs new file mode 100644 index 0000000..c494f35 --- /dev/null +++ b/Falcon.SugarApi/BackTask/BackgroundLongTask.cs @@ -0,0 +1,73 @@ +锘縰sing Microsoft.Extensions.Hosting; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Falcon.SugarApi.BackTask +{ + /// + /// 闀挎椂闂存墽琛岀殑鍚庡彴浠诲姟 + /// + public abstract class BackgroundLongTask : BackgroundService + { + /// + /// 瑕佹墽琛岀殑浠诲姟 + /// + protected abstract void Run(); + /// + /// 鎵ц鏃堕棿闂撮殧 + /// + public abstract float RunTimespan { get; } + /// + /// 鍚庡彴浠诲姟寮濮 + /// + protected virtual void OnStart(BackgroundLongTask t) { } + /// + /// 鍚庡彴浠诲姟鍋滄 + /// + protected virtual void OnStop(BackgroundLongTask t) { } + /// + /// 瀹屾垚涓娆℃墽琛 + /// + protected virtual void OnCompleted(BackgroundLongTask t) { } + + /// + /// 鐢ㄤ簬瀹氭湡鎵ц浠诲姟鐨勫鎵 + /// + private Action action; + + private CancellationTokenSource TokenSource { get; set; } = new(); + + /// + /// 鏋勯犱竴涓悗鍙伴暱鏈熶换鍔 + /// + public BackgroundLongTask() { + action = async () => { + OnStart(this); + while (!TokenSource.Token.IsCancellationRequested) { + await Task.Delay(TimeSpan.FromSeconds(RunTimespan)) + .ContinueWith(wt => { Run(); }).ContinueWith(wt => OnCompleted(this)); + } + OnStop(this); + }; + } + /// + /// 鏈嶅姟鍣ㄥ惎鍔ㄦ椂鎵ц + /// + /// 閫鍑轰俊鍙 + /// + protected override Task ExecuteAsync(CancellationToken stoppingToken) { + _ = Task.Factory.StartNew(action, stoppingToken); + return Task.CompletedTask; + } + /// + /// 鍋滄浠诲姟 + /// + /// 閫鍑烘爣璁 + /// 浠诲姟 + public override Task StopAsync(CancellationToken cancellationToken) { + this.TokenSource.Cancel(); + return base.StopAsync(cancellationToken); + } + } +} diff --git a/Falcon.SugarApi/BackTask/IServiceCollectionExtend.cs b/Falcon.SugarApi/BackTask/IServiceCollectionExtend.cs new file mode 100644 index 0000000..09230c6 --- /dev/null +++ b/Falcon.SugarApi/BackTask/IServiceCollectionExtend.cs @@ -0,0 +1,18 @@ +锘縰sing Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Falcon.SugarApi.BackTask +{ + public static class IServiceCollectionExtend + { + /// + /// 娉ㄥ唽BackgroundLongTask鍚庡彴浠诲姟銆 + /// + /// 鏈嶅姟闆嗗悎 + /// 鏈嶅姟闆嗗悎 + public static IServiceCollection AddBackgroundLongTask(this IServiceCollection services) + where T : BackgroundLongTask { + return services.AddHostedService(); + } + } +} diff --git a/Falcon.SugarApi/DatabaseDefinitions/ICreateNew.cs b/Falcon.SugarApi/DatabaseDefinitions/ICreateNew.cs new file mode 100644 index 0000000..15b0649 --- /dev/null +++ b/Falcon.SugarApi/DatabaseDefinitions/ICreateNew.cs @@ -0,0 +1,14 @@ +锘縩amespace Falcon.SugarApi.DatabaseDefinitions +{ + /// + /// 瀹炰綋绫绘敮鎸佹柊寤烘暟鎹柟娉 + /// + public interface ICreateNew + { + /// + /// 鍒涘缓鏂版暟鎹疄浣 + /// + /// 鍒涘缓浜 + void CreateNew(string createBy); + } +} \ No newline at end of file diff --git a/Falcon.SugarApi/DatabaseDefinitions/IDelete.cs b/Falcon.SugarApi/DatabaseDefinitions/IDelete.cs new file mode 100644 index 0000000..b98acb2 --- /dev/null +++ b/Falcon.SugarApi/DatabaseDefinitions/IDelete.cs @@ -0,0 +1,14 @@ +锘縩amespace Falcon.SugarApi.DatabaseDefinitions +{ + /// + /// 瀹炰綋杞垹闄ゆ柟娉 + /// + public interface IDelete + { + /// + /// 鍒犻櫎鏈潯鏁版嵁銆傝蒋鍒犻櫎 + /// + /// 鍒犻櫎浜 + void Delete(string deleteBy); + } +} \ No newline at end of file diff --git a/Falcon.SugarApi/DatabaseDefinitions/IModify.cs b/Falcon.SugarApi/DatabaseDefinitions/IModify.cs new file mode 100644 index 0000000..34d3dee --- /dev/null +++ b/Falcon.SugarApi/DatabaseDefinitions/IModify.cs @@ -0,0 +1,14 @@ +锘縩amespace Falcon.SugarApi.DatabaseDefinitions +{ + /// + /// 瀹炰綋鏀寔淇敼鏂规硶 + /// + public interface IModify + { + /// + /// 鏇存柊鏁版嵁鎻愮ず + /// + /// 淇敼浜 + void Modify(string updateBy); + } +} \ No newline at end of file diff --git a/Falcon.SugarApi/DatabaseDefinitions/MyConnectionConfig.cs b/Falcon.SugarApi/DatabaseDefinitions/MyConnectionConfig.cs new file mode 100644 index 0000000..3f6a19d --- /dev/null +++ b/Falcon.SugarApi/DatabaseDefinitions/MyConnectionConfig.cs @@ -0,0 +1,48 @@ +锘縰sing SqlSugar; +using System; +using System.ComponentModel.DataAnnotations; +using System.Reflection; +using System.Linq; + +namespace Falcon.SugarApi.DatabaseDefinitions +{ + /// + /// SqlSugar鏁版嵁搴撹繛鎺ラ厤缃 + /// + public class SugarConnectionConfig : ConnectionConfig + { + /// + /// 鏄惁浣跨敤log + /// + public bool Log { get; set; } + + public SugarConnectionConfig() { + this.ConfigureExternalServices ??= new ConfigureExternalServices { }; + this.ConfigureExternalServices.EntityService += (p, c) => { + var pt = p.PropertyType; + if (pt.GetCustomAttribute() != null) { + c.IsNullable = false; + return; + } + if (pt.IsGenericType && pt.GetGenericTypeDefinition() == typeof(Nullable<>)) { + c.IsNullable = true; + return; + } + //var sc = pt.GetCustomAttribute(); + //if (sc != null) { + // c.IsNullable = sc.IsNullable; + //} + var isNullableTypes = new Type[] { typeof(string) }; + if (isNullableTypes.Contains(pt)) { + c.IsNullable = true; + } + else { + c.IsNullable = false; + } + //if (pt == typeof(string) && pt.GetCustomAttribute() == null) { + // c.IsNullable = true; + //} + }; + } + } +} diff --git a/Falcon.SugarApi/DatabaseDefinitions/RecordStetus.cs b/Falcon.SugarApi/DatabaseDefinitions/RecordStetus.cs new file mode 100644 index 0000000..a35c550 --- /dev/null +++ b/Falcon.SugarApi/DatabaseDefinitions/RecordStetus.cs @@ -0,0 +1,17 @@ +锘縩amespace Falcon.SugarApi.DatabaseDefinitions +{ + /// + /// 璁板綍鐘舵 + /// + public static class RecordStetus + { + /// + /// 鏈夋晥璁板綍 + /// + public static string Effective => "鏈夋晥"; + /// + /// 鏃犳晥锛屽凡鍒犻櫎璁板綍 + /// + public static string Invalid => "鏃犳晥"; + } +} diff --git a/Falcon.SugarApi/DatabaseDefinitions/SugarDbContext.cs b/Falcon.SugarApi/DatabaseDefinitions/SugarDbContext.cs new file mode 100644 index 0000000..69cd80f --- /dev/null +++ b/Falcon.SugarApi/DatabaseDefinitions/SugarDbContext.cs @@ -0,0 +1,193 @@ +锘縰sing Microsoft.Extensions.Logging; +using SqlSugar; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Falcon.SugarApi.DatabaseDefinitions +{ + /// + /// Sugar鏁版嵁搴撲笂涓嬫枃 + /// + public class SugarDbContext : SqlSugarClient + { + /// + /// 閫氳繃閾炬帴閰嶇疆ConnectionConfig鏋勯燬qlSugarClient鏁版嵁涓婁笅鏂 + /// + /// + /// 鏃ュ織璁板綍鍣 + public SugarDbContext(SugarConnectionConfig config, ILogger logger) : base(config) { + this.Logger = logger; + if (config.Log) { + this.Aop.OnLogExecuting = (string sql, SugarParameter[] paras) => { + StringBuilder sb = new(); + sb.AppendLine(sql); + sb.AppendLine(this.Utilities.SerializeObject(paras.ToDictionary(it => it.ParameterName, it => it.Value))); + this.Logger.LogInformation(sb.ToString()); + }; + } + } + /// + /// 鏃ュ織璁板綍 + /// + public ILogger Logger { get; } + + #region 鎻掑叆 + /// + /// 鍦ㄦ暟鎹簱涓彃鍏ユ暟鎹 + /// + /// 鏁版嵁瀹炰綋绫诲瀷 + /// 瑕佹彃鍏ョ殑鏁版嵁鍒楄〃 + /// 鍒涘缓浜 + public void Insert(List data, string createBy) where T : class, new() { + foreach (var i in data) { + if (i is ICreateNew cn) { + cn.CreateNew(createBy); + } + } + this.Insertable(data).ExecuteCommand(); + } + + /// + /// 鍦ㄦ暟鎹簱涓彃鍏ユ暟鎹 + /// + /// 鏁版嵁妯″瀷 + /// 瑕佹彃鍏ョ殑鏁版嵁 + /// 鍒涘缓浜 + public void Insert(T data, string createBy) where T : class, new() { + this.Insert(new List { data }, createBy); + } + + #endregion + + #region 鏇存柊 + /// + /// 鍦ㄦ暟鎹簱涓洿鏂版暟鎹 + /// + /// 鏁版嵁妯″瀷 + /// 瑕佹洿鏂扮殑鏁版嵁 + /// 鏇存柊浜 + public List Update(List data, string updateBy) where T : class, new() { + foreach (var i in data) { + if (i is IModify cn) { + cn.Modify(updateBy); + } + } + this.Updateable(data).ExecuteCommand(); + return data; + } + + /// + /// 淇敼鏁版嵁 + /// + /// 鏁版嵁妯″瀷 + /// 瑕佹洿鏂扮殑鏁版嵁 + /// 鏇存柊浜 + public T Update(T data, string updateBy) where T : class, new() { + this.Update(new List { data }, updateBy); + return data; + } + + #endregion + + #region 杞垹闄 + /// + /// 鏍规嵁涓婚敭璋冪敤IDelete瀹炵幇鍒犻櫎鏁版嵁璁板綍,杞垹闄ゅ苟闈炵湡姝e垹闄ゆ暟鎹 + /// + /// 鏁版嵁瀹炰綋绫诲瀷锛屽繀椤荤户鎵胯嚜SugarTableBase + /// 瑕佸垹闄ょ殑鏁版嵁id + /// 鍒犻櫎浜 + public void Delete(Guid id, string deleteBy) where T : SugarTableBase, new() { + var data = new T(); + data.Delete(deleteBy); + this.Updateable() + .Where(m => m.Id == id) + .SetColumns(m => m.Status == data.Status) + .SetColumns(m => m.UpdateBy == data.UpdateBy) + .SetColumns(m => m.UpdateTime == data.UpdateTime) + .ExecuteCommand(); + } + + /// + /// 鎵归噺杞垹闄ゅ璞″垪琛紝璋冪敤瀹炰綋鐨処Delete瀹炵幇鍒犻櫎瀹炰綋銆 + /// + /// 瀹炰綋鐨勭被鍨 + /// 瀹炰綋瀵硅薄鍒楄〃 + /// 鍒犻櫎浜 + /// 鍒犻櫎鍚庣殑瀹炰綋鍒楄〃 + public List Delete(List data, string deleteBy) where T : class, new() { + foreach (var item in data) { + if (item is IDelete d) { + d.Delete(deleteBy); + } + } + this.Updateable(data).ExecuteCommand(); + return data; + } + /// + /// 瀵硅薄杞垹闄わ紝璋冪敤瀹炰綋鐨処Delete瀹炵幇鍒犻櫎瀹炰綋銆 + /// + /// 瀹炰綋鐨勭被鍨 + /// 瀹炰綋瀵硅薄 + /// 鍒犻櫎浜 + /// 鍒犻櫎鍚庣殑瀵硅薄 + public T Delete(T data, string deleteBy) where T : class, new() { + this.Delete(new List { data }, deleteBy); + return data; + } + + #endregion + + #region 鍒濆鍖 + /// + /// 宸茬粡鍒濆鍖栫殑绫诲瀷鍒楄〃 + /// + private static readonly List InitdTable = new(); + + /// + /// 鍗囩骇鏁版嵁搴撹〃鏋舵瀯骞跺浠借〃鍜屾暟鎹 + /// + /// 瑕佸崌绾х殑琛ㄦā鍨 + public void UpdateTableStructure(params Type[] types) { + for (int i = 0; i < types.Length; i++) { + var type = types[i]; + if (!InitdTable.Any(m => m == type.FullName)) { + this.CodeFirst.BackupTable().SetStringDefaultLength(200).InitTables(type); + InitdTable.Add(type.FullName); + } + } + + } + + /// + /// 鍗囩骇鏁版嵁搴撹〃鏋舵瀯骞跺浠借〃鍜屾暟鎹 + /// + /// 琛ㄦā鍨 + public void UpdateTableStructure() { + this.UpdateTableStructure(typeof(TableType)); + } + + #endregion + + #region 鎵ц瀛樺偍杩囩▼ + public IAdo GetAdo() { + return this.Ado.UseStoredProcedure(); + } + + /// + /// 鑾峰彇涓涓狾racle娓告爣绫诲瀷鍙傛暟鐢ㄤ綔杩斿洖鍊 + /// + /// + /// + public SugarParameter OracleRefCursor(string name = "v_data") => + new(name, null, true) { IsRefCursor = true }; + + + public object GetParameters(T data) { + //return SqlSugarTool.GetParameters(new { pageStart = 1, pageEnd = 5, recordCount = 0 }); + throw new NotSupportedException(); + } + #endregion + } +} diff --git a/Falcon.SugarApi/DatabaseDefinitions/SugarTableBase.cs b/Falcon.SugarApi/DatabaseDefinitions/SugarTableBase.cs new file mode 100644 index 0000000..6eabec9 --- /dev/null +++ b/Falcon.SugarApi/DatabaseDefinitions/SugarTableBase.cs @@ -0,0 +1,119 @@ +锘縰sing SqlSugar; +using System; +using System.Reflection; + +namespace Falcon.SugarApi.DatabaseDefinitions +{ + /// + /// 琛ㄥ熀绫汇備竴鑸〃搴旇缁ф壙姝ょ被 + /// + public abstract class SugarTableBase : ICreateNew, IModify, IDelete + { + /// + /// 涓婚敭 + /// + [SugarColumn(IsPrimaryKey = true, ColumnDescription = "涓婚敭")] + public Guid Id { get; set; } = Guid.NewGuid(); + /// + /// 鍒涘缓浜 + /// + [SugarColumn(IsOnlyIgnoreUpdate = true, IsNullable = false, ColumnDescription = "棣栨鍒涘缓浜")] + public string CreateBy { get; set; } + /// + /// 鍒涘缓鏃堕棿 + /// + [SugarColumn(IsOnlyIgnoreUpdate = true, IsNullable = false, ColumnDescription = "棣栨鍒涘缓鏃堕棿")] + public DateTime CreateTime { get; set; } = DateTime.Now; + /// + /// 鏇存柊浜 + /// + [SugarColumn(IsNullable = false, ColumnDescription = "鏈杩戞洿鏂颁汉")] + public string UpdateBy { get; set; } + /// + /// 鏇存柊鏃堕棿 + /// + [SugarColumn(IsNullable = false, ColumnDescription = "鏈鏂版洿鏂版椂闂")] + public DateTime UpdateTime { get; set; } = DateTime.Now; + /// + /// 璁板綍鐘舵 + /// + [SugarColumn(IsNullable = false, ColumnDescription = "璁板綍鐘舵併傛湁鏁 鏃犳晥")] + public string Status { get; set; } = "鏈夋晥"; + + /// + /// 鍒涘缓鏂版暟鎹褰曘 + /// + /// 鍒涘缓浜 + public virtual void CreateNew(string createBy) { + this.Id = Guid.NewGuid(); + this.CreateBy = createBy; + this.CreateTime = DateTime.Now; + this.UpdateBy = this.CreateBy; + this.UpdateTime = this.CreateTime; + this.Status = RecordStetus.Effective; + } + + /// + /// 淇敼瀹炰綋鏁版嵁 + /// + /// 淇敼浜 + public virtual void Modify(string updateBy) { + this.UpdateBy = updateBy; + this.UpdateTime = DateTime.Now; + } + /// + /// 鍒犻櫎鏈潯鏁版嵁銆傝蒋鍒犻櫎 + /// + /// 鍒犻櫎浜 + public virtual void Delete(string deleteBy) { + this.UpdateBy = deleteBy; + this.UpdateTime = DateTime.Now; + this.Status = RecordStetus.Invalid; + } + + /// + /// 灏嗗疄浣撹缃负娴嬭瘯鐢ㄦ暟鎹!鐢熸垚娴嬭瘯鏁版嵁灏嗕涪澶卞師鏈夋暟鎹紝鍙湪娴嬭瘯鎯呭喌涓嬩娇鐢 + /// + public virtual SugarTableBase SetTestModel() { + foreach (PropertyInfo pro in this.GetType().GetProperties()) { + if (pro.Name == "Id") { + this.Id = Guid.NewGuid(); + continue; + } + if (pro.Name == "CreateBy" || pro.Name == "UpdateBy") { + pro.SetValue(this, "admin"); + continue; + } + if (pro.Name == "CreateTime" || pro.Name == "UpdateTime") { + pro.SetValue(this, DateTime.Now); + continue; + } + if (pro.Name == "Status") { + this.Status = "Effective"; + continue; + } + if (pro.CanWrite && pro.PropertyType == typeof(string)) { + pro.SetValue(this, pro.Name); + continue; + } + } + return this; + } + + /// + /// 璁剧疆璁板綍鏈夋晥 + /// + public virtual SugarTableBase SetEnable() { + this.Status = RecordStetus.Effective; + return this; + } + /// + /// 璁剧疆璁板綍鏃犳晥 + /// + public virtual SugarTableBase SetDisable() { + this.Status = RecordStetus.Invalid; + return this; + } + + } +} diff --git a/Falcon.SugarApi/Falcon.SugarApi.csproj b/Falcon.SugarApi/Falcon.SugarApi.csproj new file mode 100644 index 0000000..4b476b3 --- /dev/null +++ b/Falcon.SugarApi/Falcon.SugarApi.csproj @@ -0,0 +1,26 @@ + + + + net5;net6 + disable + enable + Falcon.SugarApi鍩虹瀹氫箟 + bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml + true + 1.0.0 + + + + + + + + + + + + + + + + diff --git a/Falcon.SugarApi/IServiceCollectionExtend.cs b/Falcon.SugarApi/IServiceCollectionExtend.cs new file mode 100644 index 0000000..f28e8f2 --- /dev/null +++ b/Falcon.SugarApi/IServiceCollectionExtend.cs @@ -0,0 +1,51 @@ +锘縰sing Falcon.SugarApi.ApiDefinistions; +using Falcon.SugarApi.DatabaseDefinitions; +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using System; + +namespace Falcon.SugarApi +{ + public static class IServiceCollectionExtend + { + /// + /// 娉ㄥ唽Falcon.Sugar鐩稿叧api鏂规硶鍜屾暟鎹簱涓婁笅鏂囥 + /// + /// 鏈嶅姟闆嗗悎 + /// 鏁版嵁涓婁笅鏂囬厤缃妭鐐 + /// 鏈嶅姟闆嗗悎 + public static IServiceCollection AddSugarApiWithDbContext(this IServiceCollection services, SugarConnectionConfig config) { + return services.AddSugarApiDbContext(config).AddApiReturnModelProvider(); + } + + /// + /// 娉ㄥ唽sugarDbcontext鏁版嵁涓婁笅鏂 + /// + /// 鏈嶅姟闆嗗悎 + /// 鐢ㄤ簬鐢熸垚閰嶇疆瀹炰緥鐨勬柟娉 + /// 鏈嶅姟闆嗗悎 + public static IServiceCollection AddSugarApiDbContext(this IServiceCollection services, Func builder) { + return services.AddSingleton(p => builder(p)).AddTransient(); + } + + /// + /// 娉ㄥ唽sugarDbcontext鏁版嵁涓婁笅鏂 + /// + /// 鏈嶅姟闆嗗悎 + /// 鏁版嵁搴撻厤缃 + /// 鏈嶅姟闆嗗悎 + public static IServiceCollection AddSugarApiDbContext(this IServiceCollection service, SugarConnectionConfig config) { + return service.AddSingleton(config).AddTransient(); + } + /// + /// 娉ㄥ唽api杩斿洖缁撴灉妯″瀷鎻愪緵鍣 + /// + /// 鏈嶅姟闆嗗悎 + /// 鏈嶅姟闆嗗悎 + public static IServiceCollection AddApiReturnModelProvider(this IServiceCollection services) { + services.TryAddEnumerable(ServiceDescriptor.Transient()); + return services; + } + } +} diff --git a/Falcon.SugarApi/StringExtend.cs b/Falcon.SugarApi/StringExtend.cs new file mode 100644 index 0000000..3b56fdd --- /dev/null +++ b/Falcon.SugarApi/StringExtend.cs @@ -0,0 +1,21 @@ +锘縩amespace Falcon.SugarApi +{ + /// + /// 瀛楃涓叉墿灞 + /// + internal static class StringExtend + { + /// + /// 瀛楃涓叉槸鍚︿负绌烘垨null + /// + /// 瀛楃涓 + /// 绌烘垨null涓篢rue锛屽惁鍒橣alse + public static bool IsNullOrEmpty(this string str) => str == null || string.IsNullOrEmpty(str); + /// + /// 瀛楃涓叉槸鍚︿笉涓虹┖鎴杗ull + /// + /// 瀛楃涓 + /// 涓嶪sNullOrEmpty鐩稿弽 + public static bool IsNotNullOrEmpty(this string str) => !str.IsNullOrEmpty(); + } +}