From e48ee0120142e4212308941708cde614d935c4d9 Mon Sep 17 00:00:00 2001 From: chfei Date: Mon, 14 Sep 2026 15:48:11 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0Controller=20API=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=20=E5=A2=9E=E5=8A=A0=E5=9B=BE=E7=89=87=E7=BC=93?= =?UTF-8?q?=E5=AD=98API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/ImageCacheController.cs | 93 +++++ .../Controllers/WXUserController.cs | 54 +++ .../DependencyInjection/IScopedDependency.cs | 9 + .../ISingletonDependency.cs | 9 + .../ITransientDependency.cs | 9 + KJD_WebService/Global.asax.cs | 393 +++++++++++------- KJD_WebService/KJD_WebService.csproj | 91 +++- KJD_WebService/Model/ApiResult.cs | 68 +++ KJD_WebService/NotFoundMessageFilter.cs | 49 +++ KJD_WebService/Properties/AssemblyInfo.cs | 2 + KJD_WebService/Services/ImageCacheService.cs | 194 +++++++++ KJD_WebService/WebApiConfig.cs | 28 ++ KJD_WebService/packages.config | 26 ++ test/WebServiceTest/App.config | 12 +- test/WebServiceTest/UnitTest1.cs | 2 +- test/WebServiceTest/WebServiceTest.csproj | 37 +- 16 files changed, 906 insertions(+), 170 deletions(-) create mode 100644 KJD_WebService/Controllers/ImageCacheController.cs create mode 100644 KJD_WebService/Controllers/WXUserController.cs create mode 100644 KJD_WebService/DependencyInjection/IScopedDependency.cs create mode 100644 KJD_WebService/DependencyInjection/ISingletonDependency.cs create mode 100644 KJD_WebService/DependencyInjection/ITransientDependency.cs create mode 100644 KJD_WebService/Model/ApiResult.cs create mode 100644 KJD_WebService/NotFoundMessageFilter.cs create mode 100644 KJD_WebService/Services/ImageCacheService.cs create mode 100644 KJD_WebService/WebApiConfig.cs diff --git a/KJD_WebService/Controllers/ImageCacheController.cs b/KJD_WebService/Controllers/ImageCacheController.cs new file mode 100644 index 0000000..4595e40 --- /dev/null +++ b/KJD_WebService/Controllers/ImageCacheController.cs @@ -0,0 +1,93 @@ +using KJD_Web.Model; +using KJD_Web.Services; +using MySqlX.XDevAPI.Common; +using System; +using System.Collections.Generic; +using System.Web.Http; +using System.Web.Http.Results; + +namespace KJD_Web.Controllers +{ + [RoutePrefix("wxxd_ImageCache")] + [Route("{action}")] + public class ImageCacheController : ApiController + { + private readonly ImageCacheService _imageCacheService; + + public ImageCacheController(ImageCacheService imageCacheService) + { + if (imageCacheService == null) + { + throw new ArgumentNullException("imageCacheService"); + } + + _imageCacheService = imageCacheService; + } + + + /// + /// 批量按 MD5 查询图片缓存。 + /// + [HttpPost] + public IHttpActionResult GetImageCache([FromBody] ImageCacheBatchQueryRequest request) + { + List data = new List(); + try + { + if (request != null && request.Md5s != null) + { + data = _imageCacheService.GetByMd5s(request.Md5s, request.Platform); + } + return Ok(ApiResult.Success(data)); + } + catch (ArgumentException ex) + { + return Ok(ApiResult.Fail()); + } + } + + + + /// + /// 按 MD5 和平台更新平台图片 URL。 + /// + [HttpPost] + public IHttpActionResult AddOrUpdate([FromBody] ImageCacheUpdateUrlRequest[] requests) + { + try + { + if (requests == null) + { + throw new Exception("参数错误"); + } + foreach (var item in requests) + { + if (!_imageCacheService.AddOrUpdate(item.Md5, item.Platform, item.PlatformUrl)) + { + throw new Exception("操作失败"); + } + } + + + return Ok(ApiResult.Success(true)); + } + catch (Exception ex) + { + return Ok(ApiResult.Fail(ex.Message)); + } + } + } + + public class ImageCacheBatchQueryRequest + { + public List Md5s { get; set; } + public byte? Platform { get; set; } + } + + public class ImageCacheUpdateUrlRequest + { + public string Md5 { get; set; } + public byte Platform { get; set; } + public string PlatformUrl { get; set; } + } +} \ No newline at end of file diff --git a/KJD_WebService/Controllers/WXUserController.cs b/KJD_WebService/Controllers/WXUserController.cs new file mode 100644 index 0000000..dd077ce --- /dev/null +++ b/KJD_WebService/Controllers/WXUserController.cs @@ -0,0 +1,54 @@ +//using KJD_Web.BLL.WXXD; +//using System; +//using System.Collections.Generic; +//using System.IO; +//using System.Linq; +//using System.Runtime.Remoting.Contexts; +//using System.Text; +//using System.Web; +//using System.Web.Http; + +//namespace KJD_Web.Controllers +//{ +// [RoutePrefix("wxxd_user")] +// [Route("{action}")] +// public class WXUserController : ApiController +// { +// private string GetRequestJsonString() +// { +// //Context.Response.ContentType = "text/html;charset=utf-8"; +// //Context.Response.ContentEncoding = System.Text.Encoding.GetEncoding("utf-8"); + +// using (Stream s = HttpContext.Current.Request.InputStream) // 获得json 字符流 +// { +// // 还原数据流 +// byte[] b = new byte[s.Length]; +// s.Read(b, 0, (int)s.Length); +// string jsonText = Encoding.UTF8.GetString(b); // JSON字符串 +// if (string.IsNullOrEmpty(jsonText)) +// { +// var qs = HttpContext.Current.Request.QueryString; +// if (qs != null && qs.Keys.Count > 0) +// { +// jsonText = "{"; +// foreach (var key in qs.Keys) +// { +// jsonText += "\"" + key + "\":" + "\"" + qs[key.ToString()] + "\";"; +// } +// jsonText = jsonText.TrimEnd(';') + "}"; +// } +// } +// return jsonText; +// } +// } +// public string user_login() +// { +// string jsontext = GetRequestJsonString(); + +// var result = jsontext; +// result = new WXUserManage().CheckUserLogin(jsontext); + +// return result; +// } +// } +//} \ No newline at end of file diff --git a/KJD_WebService/DependencyInjection/IScopedDependency.cs b/KJD_WebService/DependencyInjection/IScopedDependency.cs new file mode 100644 index 0000000..c3cee41 --- /dev/null +++ b/KJD_WebService/DependencyInjection/IScopedDependency.cs @@ -0,0 +1,9 @@ +namespace MAX.Core.DependencyInjection +{ + /// + /// 作用域(每个 Scope/请求一个实例) + /// + public interface IScopedDependency + { + } +} diff --git a/KJD_WebService/DependencyInjection/ISingletonDependency.cs b/KJD_WebService/DependencyInjection/ISingletonDependency.cs new file mode 100644 index 0000000..1a841ad --- /dev/null +++ b/KJD_WebService/DependencyInjection/ISingletonDependency.cs @@ -0,0 +1,9 @@ +namespace MAX.Core.DependencyInjection +{ + /// + /// 单例 + /// + public interface ISingletonDependency + { + } +} diff --git a/KJD_WebService/DependencyInjection/ITransientDependency.cs b/KJD_WebService/DependencyInjection/ITransientDependency.cs new file mode 100644 index 0000000..73006f9 --- /dev/null +++ b/KJD_WebService/DependencyInjection/ITransientDependency.cs @@ -0,0 +1,9 @@ +namespace MAX.Core.DependencyInjection +{ + /// + /// 每次实例化 + /// + public interface ITransientDependency + { + } +} diff --git a/KJD_WebService/Global.asax.cs b/KJD_WebService/Global.asax.cs index 74f5df7..be2af40 100644 --- a/KJD_WebService/Global.asax.cs +++ b/KJD_WebService/Global.asax.cs @@ -1,165 +1,246 @@ -using KJD_Web.Utils; +using Autofac; +using Autofac.Integration.WebApi; +using FreeSql; +using KJD_Web.Utils; +using MySql.Data.MySqlClient; +using MAX.Core.DependencyInjection; using NLog; using System; +using System.Configuration; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Web; +using System.Web.Http; using System.Web.Security; using System.Web.SessionState; namespace KJD_Web { - public class Global : System.Web.HttpApplication - { + public class Global : System.Web.HttpApplication + { protected void Application_Start(object sender, EventArgs e) { LogHelper.Info("应用程序启动"); + var container = BuildContainer(); + + // 让 Web API 用 Autofac 解析 Controller + GlobalConfiguration.Configuration.DependencyResolver = + new AutofacWebApiDependencyResolver(container); + GlobalConfiguration.Configure(WebApiConfig.Register); } - protected void Session_Start(object sender, EventArgs e) - { + internal static IContainer BuildContainer() + { + var builder = new ContainerBuilder(); + var appAssembly = Assembly.GetExecutingAssembly(); - } + // ImageCacheService 使用独立的 imagecache 数据库。 + builder.Register(_ => CreateImageCacheFreeSql()) + .As() + .SingleInstance(); - protected void Application_BeginRequest(object sender, EventArgs e) - { - string hPath = Request.Url.LocalPath.ToString(); - if (!hPath.Contains(".asmx") && hPath.Contains("/user/")) - { - hPath = hPath.Replace("/user/", "/UserService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/shop/")) - { - hPath = hPath.Replace("/shop/", "/ShopService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/update/")) - { - hPath = hPath.Replace("/update/", "/UpdateService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/order/")) - { - hPath = hPath.Replace("/order/", "/OrderService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/product/")) - { - hPath = hPath.Replace("/product/", "/ProductService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/product_third/")) - { - hPath = hPath.Replace("/product_third/", "/ProductThirdService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/price/")) - { - hPath = hPath.Replace("/price/", "/PriceService.asmx/"); - Context.RewritePath(hPath); - } + // 注册 Web API 所有 Controller + builder.RegisterApiControllers(appAssembly); + + #region 注册所有IOC接口 + + Type typeSingleton = typeof(ISingletonDependency); + builder.RegisterAssemblyTypes(appAssembly) + .Where(a => + typeSingleton.IsAssignableFrom(a) + && a.IsClass + && !a.IsAbstract + ) + .AsImplementedInterfaces() + .AsSelf() + .SingleInstance(); + + Type typeTransient = typeof(ITransientDependency); + builder.RegisterAssemblyTypes(appAssembly) + .Where(a => + typeTransient.IsAssignableFrom(a) + && a.IsClass + && !a.IsAbstract + ) + .AsImplementedInterfaces() + .AsSelf() + .InstancePerDependency(); + + Type typeScoped = typeof(IScopedDependency); + builder.RegisterAssemblyTypes(appAssembly) + .Where(a => typeScoped.IsAssignableFrom(a) && a.IsClass && !a.IsAbstract) + .AsImplementedInterfaces() + .AsSelf() + .InstancePerRequest(); + + #endregion + + return builder.Build(); + } + private static IFreeSql CreateImageCacheFreeSql() + { + var connection = ConfigurationManager.ConnectionStrings["ConnKJD"]; + if (connection == null || string.IsNullOrWhiteSpace(connection.ConnectionString)) + { + throw new ConfigurationErrorsException("未配置连接字符串:ConnKJD"); + } + + var connectionBuilder = new MySqlConnectionStringBuilder(connection.ConnectionString) + { + Database = "imagecache" + }; + + return new FreeSqlBuilder() + .UseConnectionString(DataType.MySql, connectionBuilder.ConnectionString) + .UseAutoSyncStructure(false) + .Build(); + } + + protected void Session_Start(object sender, EventArgs e) + { + + } + + protected void Application_BeginRequest(object sender, EventArgs e) + { + string hPath = Request.Url.LocalPath.ToString(); + if (!hPath.Contains(".asmx") && hPath.Contains("/user/")) + { + hPath = hPath.Replace("/user/", "/UserService.asmx/"); + Context.RewritePath(hPath); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/shop/")) + { + hPath = hPath.Replace("/shop/", "/ShopService.asmx/"); + Context.RewritePath(hPath); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/update/")) + { + hPath = hPath.Replace("/update/", "/UpdateService.asmx/"); + Context.RewritePath(hPath); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/order/")) + { + hPath = hPath.Replace("/order/", "/OrderService.asmx/"); + Context.RewritePath(hPath); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/product/")) + { + hPath = hPath.Replace("/product/", "/ProductService.asmx/"); + Context.RewritePath(hPath); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/product_third/")) + { + hPath = hPath.Replace("/product_third/", "/ProductThirdService.asmx/"); + Context.RewritePath(hPath); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/price/")) + { + hPath = hPath.Replace("/price/", "/PriceService.asmx/"); + Context.RewritePath(hPath); + } - if (!hPath.Contains(".asmx") && hPath.Contains("/bxx_user/")) - { - hPath = hPath.Replace("/bxx_user/", "/BXXUserService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/bxx_shop/")) - { - hPath = hPath.Replace("/bxx_shop/", "/BXXShopService.asmx/"); - Context.RewritePath(hPath); - } - //else if (!hPath.Contains(".asmx") && hPath.Contains("/update/")) - //{ - // hPath = hPath.Replace("/update/", "/UpdateService.asmx/"); - // Context.RewritePath(hPath); - //} - else if (!hPath.Contains(".asmx") && hPath.Contains("/bxx_product/")) - { - hPath = hPath.Replace("/bxx_product/", "/BXXProductService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/bxx_product_third/")) - { - hPath = hPath.Replace("/bxx_product_third/", "/BXXProductThirdService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/bxx_price/")) - { - hPath = hPath.Replace("/bxx_price/", "/BXXPriceService.asmx/"); - Context.RewritePath(hPath); - } + if (!hPath.Contains(".asmx") && hPath.Contains("/bxx_user/")) + { + hPath = hPath.Replace("/bxx_user/", "/BXXUserService.asmx/"); + Context.RewritePath(hPath); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/bxx_shop/")) + { + hPath = hPath.Replace("/bxx_shop/", "/BXXShopService.asmx/"); + Context.RewritePath(hPath); + } + //else if (!hPath.Contains(".asmx") && hPath.Contains("/update/")) + //{ + // hPath = hPath.Replace("/update/", "/UpdateService.asmx/"); + // Context.RewritePath(hPath); + //} + else if (!hPath.Contains(".asmx") && hPath.Contains("/bxx_product/")) + { + hPath = hPath.Replace("/bxx_product/", "/BXXProductService.asmx/"); + Context.RewritePath(hPath); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/bxx_product_third/")) + { + hPath = hPath.Replace("/bxx_product_third/", "/BXXProductThirdService.asmx/"); + Context.RewritePath(hPath); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/bxx_price/")) + { + hPath = hPath.Replace("/bxx_price/", "/BXXPriceService.asmx/"); + Context.RewritePath(hPath); + } - if (!hPath.Contains(".asmx") && hPath.Contains("/dyxd_user/")) - { - hPath = hPath.Replace("/dyxd_user/", "/DYUserService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/dyxd_shop/")) - { - hPath = hPath.Replace("/dyxd_shop/", "/DYShopService.asmx/"); - Context.RewritePath(hPath); - } - //else if (!hPath.Contains(".asmx") && hPath.Contains("/update/")) - //{ - // hPath = hPath.Replace("/update/", "/UpdateService.asmx/"); - // Context.RewritePath(hPath); - //} - else if (!hPath.Contains(".asmx") && hPath.Contains("/dyxd_product/")) - { - hPath = hPath.Replace("/dyxd_product/", "/DYProductService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/dyxd_product_third/")) - { - hPath = hPath.Replace("/dyxd_product_third/", "/DYProductThirdService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/dyxd_price/")) - { - hPath = hPath.Replace("/dyxd_price/", "/DYPriceService.asmx/"); - Context.RewritePath(hPath); - } + if (!hPath.Contains(".asmx") && hPath.Contains("/dyxd_user/")) + { + hPath = hPath.Replace("/dyxd_user/", "/DYUserService.asmx/"); + Context.RewritePath(hPath); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/dyxd_shop/")) + { + hPath = hPath.Replace("/dyxd_shop/", "/DYShopService.asmx/"); + Context.RewritePath(hPath); + } + //else if (!hPath.Contains(".asmx") && hPath.Contains("/update/")) + //{ + // hPath = hPath.Replace("/update/", "/UpdateService.asmx/"); + // Context.RewritePath(hPath); + //} + else if (!hPath.Contains(".asmx") && hPath.Contains("/dyxd_product/")) + { + hPath = hPath.Replace("/dyxd_product/", "/DYProductService.asmx/"); + Context.RewritePath(hPath); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/dyxd_product_third/")) + { + hPath = hPath.Replace("/dyxd_product_third/", "/DYProductThirdService.asmx/"); + Context.RewritePath(hPath); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/dyxd_price/")) + { + hPath = hPath.Replace("/dyxd_price/", "/DYPriceService.asmx/"); + Context.RewritePath(hPath); + } - if (!hPath.Contains(".asmx") && hPath.Contains("/wxxd_user/")) - { - hPath = hPath.Replace("/wxxd_user/", "/WXUserService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/wxxd_shop/")) - { - hPath = hPath.Replace("/wxxd_shop/", "/WXShopService.asmx/"); - Context.RewritePath(hPath); - } - //else if (!hPath.Contains(".asmx") && hPath.Contains("/update/")) - //{ - // hPath = hPath.Replace("/update/", "/UpdateService.asmx/"); - // Context.RewritePath(hPath); - //} - else if (!hPath.Contains(".asmx") && hPath.Contains("/wxxd_product/")) - { - hPath = hPath.Replace("/wxxd_product/", "/WXProductService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/wxxd_product_third/")) - { - hPath = hPath.Replace("/wxxd_product_third/", "/WXProductThirdService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/wxxd_price/")) - { - hPath = hPath.Replace("/wxxd_price/", "/WXPriceService.asmx/"); - Context.RewritePath(hPath); - } - else if (!hPath.Contains(".asmx") && hPath.Contains("/wxxd_order/")) - { - hPath = hPath.Replace("/wxxd_order/", "/WXOrderService.asmx/"); - Context.RewritePath(hPath); + if (!hPath.Contains(".asmx") && hPath.Contains("/wxxd_user/")) + { + hPath = hPath.Replace("/wxxd_user/", "/WXUserService.asmx/"); + Context.RewritePath(hPath, false); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/wxxd_shop/")) + { + hPath = hPath.Replace("/wxxd_shop/", "/WXShopService.asmx/"); + Context.RewritePath(hPath); + } + //else if (!hPath.Contains(".asmx") && hPath.Contains("/update/")) + //{ + // hPath = hPath.Replace("/update/", "/UpdateService.asmx/"); + // Context.RewritePath(hPath); + //} + else if (!hPath.Contains(".asmx") && hPath.Contains("/wxxd_product/")) + { + hPath = hPath.Replace("/wxxd_product/", "/WXProductService.asmx/"); + Context.RewritePath(hPath); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/wxxd_product_third/")) + { + hPath = hPath.Replace("/wxxd_product_third/", "/WXProductThirdService.asmx/"); + Context.RewritePath(hPath); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/wxxd_price/")) + { + hPath = hPath.Replace("/wxxd_price/", "/WXPriceService.asmx/"); + Context.RewritePath(hPath); + } + else if (!hPath.Contains(".asmx") && hPath.Contains("/wxxd_order/")) + { + hPath = hPath.Replace("/wxxd_order/", "/WXOrderService.asmx/"); + Context.RewritePath(hPath); } else if (!hPath.Contains(".asmx") && hPath.Contains("/wxxd_shop_revenue_statistics/")) { @@ -170,30 +251,30 @@ namespace KJD_Web //try { ApiRequestLogger.BeginRequest(Context); } catch { } } - protected void Application_AuthenticateRequest(object sender, EventArgs e) - { + protected void Application_AuthenticateRequest(object sender, EventArgs e) + { - } + } - protected void Application_EndRequest(object sender, EventArgs e) - { - // 请求录制:合并响应内容并落盘 - //try { ApiRequestLogger.EndRequest(Context); } catch { } - } + protected void Application_EndRequest(object sender, EventArgs e) + { + // 请求录制:合并响应内容并落盘 + //try { ApiRequestLogger.EndRequest(Context); } catch { } + } - protected void Application_Error(object sender, EventArgs e) - { + protected void Application_Error(object sender, EventArgs e) + { - } + } - protected void Session_End(object sender, EventArgs e) - { + protected void Session_End(object sender, EventArgs e) + { - } + } - protected void Application_End(object sender, EventArgs e) - { + protected void Application_End(object sender, EventArgs e) + { - } - } + } + } } \ No newline at end of file diff --git a/KJD_WebService/KJD_WebService.csproj b/KJD_WebService/KJD_WebService.csproj index 1406c4e..c00cbee 100644 --- a/KJD_WebService/KJD_WebService.csproj +++ b/KJD_WebService/KJD_WebService.csproj @@ -13,7 +13,7 @@ Properties KJD_Web KJD_WebService - v4.6.2 + v4.7.2 true 44313 @@ -46,22 +46,80 @@ false + + ..\packages\Autofac.9.3.2\lib\netstandard2.0\Autofac.dll + + + ..\packages\Autofac.WebApi2.6.2.0\lib\net472\Autofac.Integration.WebApi.dll + + + ..\packages\Portable.BouncyCastle.1.9.0\lib\net40\BouncyCastle.Crypto.dll + + + ..\packages\FreeSql.3.5.311\lib\net451\FreeSql.dll + + + ..\packages\FreeSql.Provider.MySql.3.5.311\lib\net452\FreeSql.Provider.MySql.dll + + + ..\packages\Google.Protobuf.3.19.4\lib\net45\Google.Protobuf.dll + + + ..\packages\K4os.Compression.LZ4.1.2.6\lib\net46\K4os.Compression.LZ4.dll + + + ..\packages\K4os.Compression.LZ4.Streams.1.2.6\lib\net46\K4os.Compression.LZ4.Streams.dll + + + ..\packages\K4os.Hash.xxHash.1.0.6\lib\net46\K4os.Hash.xxHash.dll + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.10.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll + ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.2.0.1\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll - - ..\Dll\MySql.Data.dll + + ..\packages\MySql.Data.8.0.30\lib\net452\MySql.Data.dll - - False - ..\Dll\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.13.0.4\lib\net45\Newtonsoft.Json.dll + + + ..\packages\Newtonsoft.Json.Bson.1.0.2\lib\net45\Newtonsoft.Json.Bson.dll ..\packages\NLog.6.2.0\lib\net46\NLog.dll + + ..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll + + + + + ..\packages\System.Diagnostics.DiagnosticSource.10.0.0\lib\net462\System.Diagnostics.DiagnosticSource.dll + + + + ..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll + + + ..\packages\Microsoft.AspNet.WebApi.Client.6.0.0\lib\net45\System.Net.Http.Formatting.dll + + + + ..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll + + @@ -71,11 +129,23 @@ + + ..\packages\Microsoft.AspNet.WebApi.Core.5.3.0\lib\net45\System.Web.Http.dll + + + ..\packages\Microsoft.AspNet.WebApi.WebHost.5.3.0\lib\net45\System.Web.Http.WebHost.dll + + + ..\packages\MySql.Data.8.0.30\lib\net452\Ubiety.Dns.Core.dll + + + ..\packages\MySql.Data.8.0.30\lib\net452\ZstdNet.dll + @@ -132,7 +202,13 @@ + + + + + + @@ -168,7 +244,10 @@ + + + WXOrderService.asmx Component diff --git a/KJD_WebService/Model/ApiResult.cs b/KJD_WebService/Model/ApiResult.cs new file mode 100644 index 0000000..0e11e13 --- /dev/null +++ b/KJD_WebService/Model/ApiResult.cs @@ -0,0 +1,68 @@ + +namespace KJD_Web.Model +{ + public static class ResultCode + { + public const int Success = 200; + public const int Fail = 500; + + public const int Unauthorized = 401; + public const int Forbidden = 403; + public const int NotFound = 404; + /// + /// 参数校验失败 + /// + public const int ValidationError = 1001; + /// + /// 业务逻辑异常 + /// + public const int BusinessError = 2001; + /// + /// 服务器内部错误 + /// + public const int ServerError = 5000; + } + /// + /// 非泛型基类,用于统一处理 + /// + public abstract class ApiResultBase + { + /// 业务状态码 + public int Code { get; set; } = ResultCode.Success; + + /// 提示信息 + public string Msg { get; set; } + + /// 请求是否成功 + public bool IsSuccess => Code == ResultCode.Success; + + } + + /// + /// 泛型响应 + /// + public class ApiResult : ApiResultBase + { + public T Data { get; set; } + + public static ApiResult Success(T data, string msg = "操作成功") + => new ApiResult { Code = ResultCode.Success, Data = data, Msg = msg }; + + public static ApiResult Fail(string msg = "操作失败", int code = ResultCode.Fail, T data = default) + => new ApiResult { Code = code, Msg = msg, Data = data }; + } + + /// + /// 无数据响应(等同于 ApiResult) + /// + public class ApiResult : ApiResult + { + public static ApiResult Success(string msg = "操作成功") + => new ApiResult { Code = ResultCode.Success, Msg = msg }; + + public static ApiResult Fail(string msg = "操作失败", int code = ResultCode.Fail) + => new ApiResult { Code = code, Msg = msg }; + } + + +} diff --git a/KJD_WebService/NotFoundMessageFilter.cs b/KJD_WebService/NotFoundMessageFilter.cs new file mode 100644 index 0000000..7d2249a --- /dev/null +++ b/KJD_WebService/NotFoundMessageFilter.cs @@ -0,0 +1,49 @@ +using KJD_Web.Model; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Web; +using System.Web.Http.Filters; + +namespace KJD_Web +{ + public class NotFoundMessageFilter : ActionFilterAttribute + { + public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) + { + if (actionExecutedContext.Response != null && + actionExecutedContext.Response.StatusCode == HttpStatusCode.NotFound) + { + + + actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse( + HttpStatusCode.NotFound, ApiResult.Fail("请求失败", ResultCode.NotFound)); + } + } + } + + public class NotFoundMessageHandler : DelegatingHandler + { + protected override async Task SendAsync( + HttpRequestMessage request, CancellationToken cancellationToken) + { + var response = await base.SendAsync(request, cancellationToken); + + if (response.StatusCode != HttpStatusCode.OK) + { + response.Content = new StringContent( + JsonConvert.SerializeObject(ApiResult.Fail("请求失败", ResultCode.NotFound)), + Encoding.UTF8, + "application/json"); + } + + return response; + } + } +} \ No newline at end of file diff --git a/KJD_WebService/Properties/AssemblyInfo.cs b/KJD_WebService/Properties/AssemblyInfo.cs index 700aa17..dfd10f0 100644 --- a/KJD_WebService/Properties/AssemblyInfo.cs +++ b/KJD_WebService/Properties/AssemblyInfo.cs @@ -2,6 +2,8 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +[assembly: InternalsVisibleTo("WebServiceTest")] + // 有关程序集的常规信息通过下列特性集 // 控制。更改这些特性值可修改 // 与程序集关联的信息。 diff --git a/KJD_WebService/Services/ImageCacheService.cs b/KJD_WebService/Services/ImageCacheService.cs new file mode 100644 index 0000000..cfb5710 --- /dev/null +++ b/KJD_WebService/Services/ImageCacheService.cs @@ -0,0 +1,194 @@ +using FreeSql; +using FreeSql.DataAnnotations; +using MAX.Core.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace KJD_Web.Services +{ + [Table(Name = "images_0")] + public class ImageCacheEntity + { + [Column(Name = "id", IsIdentity = true, IsPrimary = true)] + public long id { get; set; } + + [Column(Name = "platform")] + public byte platform { get; set; } + + [Column(Name = "source_url_md5", StringLength = 32)] + public string source_url_md5 { get; set; } + + [Column(Name = "platform_url", StringLength = 1000)] + public string platform_url { get; set; } + + [Column(Name = "create_time")] + public DateTime create_time { get; set; } + + [Column(Name = "update_time")] + public DateTime update_time { get; set; } + } + + public class ImageCacheService : ISingletonDependency + { + private const int Md5Length = 32; + private const int PlatformUrlMaxLength = 1000; + + private readonly IFreeSql _freeSql; + + public ImageCacheService(IFreeSql freeSql) + { + if (freeSql == null) + { + throw new ArgumentNullException("freeSql"); + } + + _freeSql = freeSql; + } + + /// + /// 按 MD5 查询图片缓存;platform 为空时返回该 MD5 在所有平台的缓存。 + /// + public ImageCacheEntity GetByMd5(string sourceUrlMd5, byte? platform = null) + { + var md5 = NormalizeMd5(sourceUrlMd5); + return GetByMd5sInternal(new List { md5 }, platform).FirstOrDefault(); + } + + /// + /// 批量按 MD5 查询图片缓存。MD5 会按首字符分组,只查询对应的 images_0 到 images_f 分表。 + /// + public List GetByMd5s(IEnumerable sourceUrlMd5s, byte? platform = null) + { + return GetByMd5sInternal(NormalizeMd5s(sourceUrlMd5s), platform); + } + + + + /// + /// 按 MD5 和平台直接修改平台图片 URL,未找到时返回 false。 + /// + public bool AddOrUpdate(string sourceUrlMd5, byte platform, string platformUrl) + { + var md5 = NormalizeMd5(sourceUrlMd5); + ValidatePlatformUrl(platformUrl); + var tableName = GetTableName(md5[0]); + var existing = _freeSql.Select() + .AsTable((type, oldName) => tableName) + .Where(a => a.source_url_md5 == sourceUrlMd5 && a.platform == platform) + .First(); + + var now = DateTime.Now; + if (existing != null) + { + var affected = _freeSql.Update() + .AsTable(tableName) + .Set(a => a.platform_url, platformUrl) + .Set(a => a.update_time, now) + .Where(a => a.source_url_md5 == md5 && a.platform == platform) + .ExecuteAffrows(); + return affected > 0; + } + else + { + ImageCacheEntity imageCache = new ImageCacheEntity(); + imageCache.id = 0; + imageCache.source_url_md5 = md5; + imageCache.platform = platform; + imageCache.platform_url = platformUrl; + imageCache.create_time = now; + imageCache.update_time = now; + imageCache.id = _freeSql.Insert(imageCache) + .AsTable(tableName) + .ExecuteIdentity(); + return imageCache.id > 0; + } + } + + private List GetByMd5sInternal(List md5s, byte? platform) + { + var result = new List(); + if (md5s.Count == 0) + { + return result; + } + + foreach (var shard in md5s.GroupBy(a => a[0])) + { + var tableName = GetTableName(shard.Key); + var md5sInTable = shard.ToList(); + var query = _freeSql.Select() + .AsTable((type, oldName) => tableName) + .Where(a => md5sInTable.Contains(a.source_url_md5)); + + if (platform.HasValue) + { + var platformValue = platform.Value; + query = query.Where(a => a.platform == platformValue); + } + + result.AddRange(query.ToList()); + } + + return result; + } + + private List NormalizeMd5s(IEnumerable sourceUrlMd5s) + { + if (sourceUrlMd5s == null) + { + throw new ArgumentNullException("sourceUrlMd5s"); + } + + var result = new List(); + foreach (var sourceUrlMd5 in sourceUrlMd5s) + { + var md5 = NormalizeMd5(sourceUrlMd5); + if (!result.Contains(md5)) + { + result.Add(md5); + } + } + + return result; + } + + private string NormalizeMd5(string sourceUrlMd5) + { + if (string.IsNullOrWhiteSpace(sourceUrlMd5)) + { + throw new ArgumentException("MD5 不能为空", "sourceUrlMd5"); + } + + var md5 = sourceUrlMd5.Trim().ToLowerInvariant(); + if (md5.Length != Md5Length || md5.Any(a => !Uri.IsHexDigit(a))) + { + throw new ArgumentException("MD5 必须是 32 位十六进制字符串", "sourceUrlMd5"); + } + + return md5; + } + + private string GetTableName(char shard) + { + return "images_" + shard; + } + + + + private void ValidatePlatformUrl(string platformUrl) + { + if (string.IsNullOrWhiteSpace(platformUrl)) + { + throw new ArgumentException("平台图片 URL 不能为空", "platformUrl"); + } + + if (platformUrl.Length > PlatformUrlMaxLength) + { + throw new ArgumentException("平台图片 URL 长度不能超过 1000", "platformUrl"); + } + } + } +} + + diff --git a/KJD_WebService/WebApiConfig.cs b/KJD_WebService/WebApiConfig.cs new file mode 100644 index 0000000..8a6dbd1 --- /dev/null +++ b/KJD_WebService/WebApiConfig.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Http; + +namespace KJD_Web +{ + public static class WebApiConfig + { + public static void Register(HttpConfiguration config) + { + // 特性路由:你 ImageCacheController 上的 [Route] 依赖这个 + config.MapHttpAttributeRoutes(); + + // 默认路由 + //config.Routes.MapHttpRoute( + // name: "DefaultApi", + // routeTemplate: "{controller}/{action}/{id}", + // defaults: new { id = RouteParameter.Optional } + //); + //config.Formatters.Remove(config.Formatters.XmlFormatter); + //config.Filters.Add(new NotFoundMessageFilter()); + //config.MessageHandlers.Add(new NotFoundMessageHandler()); + + } + } +} \ No newline at end of file diff --git a/KJD_WebService/packages.config b/KJD_WebService/packages.config index e2f0b74..2319e7e 100644 --- a/KJD_WebService/packages.config +++ b/KJD_WebService/packages.config @@ -1,5 +1,31 @@  + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/WebServiceTest/App.config b/test/WebServiceTest/App.config index 7e99579..937ebae 100644 --- a/test/WebServiceTest/App.config +++ b/test/WebServiceTest/App.config @@ -5,10 +5,10 @@ --> - + + + - - @@ -37,9 +37,9 @@ --> - + - + @@ -55,7 +55,7 @@ - + diff --git a/test/WebServiceTest/UnitTest1.cs b/test/WebServiceTest/UnitTest1.cs index f52da4e..e42cbce 100644 --- a/test/WebServiceTest/UnitTest1.cs +++ b/test/WebServiceTest/UnitTest1.cs @@ -25,7 +25,7 @@ namespace WebServiceTest }); Console.WriteLine(ret); } - catch (Exception ex) + catch (Exception) { } diff --git a/test/WebServiceTest/WebServiceTest.csproj b/test/WebServiceTest/WebServiceTest.csproj index 2c061b2..4234933 100644 --- a/test/WebServiceTest/WebServiceTest.csproj +++ b/test/WebServiceTest/WebServiceTest.csproj @@ -1,7 +1,7 @@ - net462 + net472 enable enable 12 @@ -22,7 +22,42 @@ + + ..\..\packages\Autofac.9.3.2\lib\netstandard2.0\Autofac.dll + + + ..\..\packages\Microsoft.Bcl.AsyncInterfaces.10.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll + + + ..\..\packages\System.Diagnostics.DiagnosticSource.10.0.0\lib\net462\System.Diagnostics.DiagnosticSource.dll + + + ..\..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll + + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll + + + ..\..\packages\FreeSql.3.5.311\lib\net451\FreeSql.dll + + + ..\..\packages\FreeSql.Provider.MySql.3.5.311\lib\net452\FreeSql.Provider.MySql.dll + + + ..\..\packages\MySql.Data.8.0.30\lib\net452\MySql.Data.dll + + + + + ..\..\packages\Microsoft.AspNet.WebApi.Core.5.3.0\lib\net45\System.Web.Http.dll +