diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Payment/Alipay/AlipayPayment.cs b/src/0-core/HuanMeng.MiaoYu.Code/Payment/Alipay/AlipayPayment.cs index 1d41821..b8e49b4 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Payment/Alipay/AlipayPayment.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Payment/Alipay/AlipayPayment.cs @@ -10,13 +10,14 @@ using Alipay.EasySDK.Kernel; using Alipay.EasySDK.Kernel.Util; using Alipay.EasySDK.Payment.FaceToFace.Models; using StackExchange.Redis; +using HuanMeng.DotNetCore.MultiTenant.Contract; namespace HuanMeng.MiaoYu.Code.Payment.Alipay { /// /// /// - public class AlipayPayment : IPayment + public class AlipayPayment(Config config, ITenantInfo tenantInfo) : IPayment { public Task<(string orderId, string order)> CreateOrder(string productName, decimal price, params object[] args) { @@ -31,12 +32,11 @@ namespace HuanMeng.MiaoYu.Code.Payment.Alipay } var orderId = GenerateTimestampIdWithOffset(); //.SetOptions(GetConfig()); - var response = Factory.Payment.App().Optional("passback_params", "PaymentType%3Dzfb").Pay(productName, orderId, priceStr); - if (ResponseChecker.Success(response)) - { - Console.WriteLine("调用成功"); - } - else + //AsyncNotify + //https://pay.shhuanmeng.com/api/${tenant}/zfb/${orderId} + var notifyUrl = config.NotifyUrl.Replace("${pay}", "zfb").Replace("${orderId}", orderId).Replace("${tenant}", tenantInfo.Identifier); ; + var response = Factory.Payment.App().AsyncNotify(notifyUrl).Optional("passback_params", "PaymentType%3Dzfb").Pay(productName, orderId, priceStr); + if (!ResponseChecker.Success(response)) { throw new Exception("创建订单失败!" + response.Body); } @@ -44,6 +44,10 @@ namespace HuanMeng.MiaoYu.Code.Payment.Alipay var zfbOrderId = response.Body; return Task.FromResult((orderId, zfbOrderId)); } + /// + /// + /// + /// private string GenerateTimestampIdWithOffset() { var timestamp = DateTimeOffset.Now.ToUnixTimeMilliseconds(); // 获取Unix时间戳(毫秒) diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentExtend.cs b/src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentExtend.cs index 583f070..84c8e07 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentExtend.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentExtend.cs @@ -56,7 +56,7 @@ namespace HuanMeng.MiaoYu.Code.Payment return builder; } - + public static Config AlipayConfig { get; set; } /// /// 支付 /// @@ -80,7 +80,9 @@ namespace HuanMeng.MiaoYu.Code.Payment //可设置异步通知接收服务地址(可选) NotifyUrl = x.NotifyUrl, }; + AlipayConfig = config; Factory.SetOptions(config); + } else { @@ -93,14 +95,14 @@ namespace HuanMeng.MiaoYu.Code.Payment /// /// /// - public static IPayment GetPayment(string payment) + public static IPayment GetPayment(string payment, MiaoYuBase miaoYuBase) { if (payment == "zfb") { - return new AlipayPayment(); + return new AlipayPayment(AlipayConfig, miaoYuBase.TenantInfo); } - return new WeChatPayment(wxClient, weChatConfig); + return new WeChatPayment(wxClient, weChatConfig, miaoYuBase.TenantInfo); } /// diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Payment/WeChat/WeChatPayment.cs b/src/0-core/HuanMeng.MiaoYu.Code/Payment/WeChat/WeChatPayment.cs index 3b98225..e7f34a2 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Payment/WeChat/WeChatPayment.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Payment/WeChat/WeChatPayment.cs @@ -3,26 +3,28 @@ using HuanMeng.MiaoYu.Code.Payment.Contract; using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models; using SKIT.FlurlHttpClient.Wechat.TenpayV3; using Newtonsoft.Json; +using HuanMeng.DotNetCore.MultiTenant.Contract; namespace HuanMeng.MiaoYu.Code.Payment.WeChat { /// /// 微信支付 /// - public class WeChatPayment(WechatTenpayClient client, WeChatConfig weChatConfig) : IPayment + public class WeChatPayment(WechatTenpayClient client, WeChatConfig weChatConfig, ITenantInfo tenantInfo) : IPayment { public async Task<(string orderId, string order)> CreateOrder(string productName, decimal price, params object[] args) { var orderId = GenerateTimestampIdWithOffset(); //var client = new WechatTenpayClient(wechatTenpayClientOptions); /* 以 JSAPI 统一下单接口为例 */ + var notifyUrl = weChatConfig.NotifyUrl.Replace("${pay}", "wx").Replace("${orderId}", orderId).Replace("${tenant}", tenantInfo.Identifier); var request = new CreatePayTransactionAppRequest() { OutTradeNumber = orderId, AppId = weChatConfig.AppId, Description = productName, ExpireTime = DateTimeOffset.Now.AddMinutes(20), - NotifyUrl = weChatConfig.NotifyUrl, + NotifyUrl = notifyUrl, Amount = new CreatePayTransactionJsapiRequest.Types.Amount() { Total = (int)(price * 100) diff --git a/src/0-core/HuanMeng.MiaoYu.Code/SysDictionary/DictionaryNetwork/DictionaryInfoServerNetwork.cs b/src/0-core/HuanMeng.MiaoYu.Code/SysDictionary/DictionaryNetwork/DictionaryInfoServerNetwork.cs index 49704e8..c9a1ccb 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/SysDictionary/DictionaryNetwork/DictionaryInfoServerNetwork.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/SysDictionary/DictionaryNetwork/DictionaryInfoServerNetwork.cs @@ -68,7 +68,10 @@ namespace HuanMeng.MiaoYu.Code.SysDictionary.DictionaryNetwork { dictionaryUrl = configuration.GetSection("SystemConfig:DictionaryUrl").Get() ?? ""; } - + if (string.IsNullOrEmpty(dictionaryUrl)) + { + return; + } using var client = httpClientFactory.CreateClient(); var request = await client.GetAsync(dictionaryUrl); if (request.IsSuccessStatusCode) diff --git a/src/2-api/HuanMeng.MiaoYu.WebPayApi/Controllers/PayController.cs b/src/2-api/HuanMeng.MiaoYu.WebPayApi/Controllers/PayController.cs new file mode 100644 index 0000000..1ed1031 --- /dev/null +++ b/src/2-api/HuanMeng.MiaoYu.WebPayApi/Controllers/PayController.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace HuanMeng.MiaoYu.WebPayApi.Controllers +{ + [Route("api/[controller]/")] + [ApiController] + public class PayController : ControllerBase + { + /// + /// + /// + /// + [HttpGet("{tenant?}/{pay?}/{orderId?}")] + public string Get(string? tenant, string? pay, string? orderId) + { + return "ok"; + } + + /// + /// ${tenant}/zfb/${orderId} + /// + /// + [HttpPost("{tenant?}/{pay?}/{orderId?}")] + public string Post(string? tenant, string? pay,string? orderId) + { + return $"ok;{pay},{orderId}"; + } + } +} diff --git a/src/2-api/HuanMeng.MiaoYu.WebPayApi/HuanMeng.MiaoYu.WebPayApi.csproj b/src/2-api/HuanMeng.MiaoYu.WebPayApi/HuanMeng.MiaoYu.WebPayApi.csproj index 3171938..d9ec514 100644 --- a/src/2-api/HuanMeng.MiaoYu.WebPayApi/HuanMeng.MiaoYu.WebPayApi.csproj +++ b/src/2-api/HuanMeng.MiaoYu.WebPayApi/HuanMeng.MiaoYu.WebPayApi.csproj @@ -13,7 +13,15 @@ + + + + + + + + diff --git a/src/2-api/HuanMeng.MiaoYu.WebPayApi/Program.cs b/src/2-api/HuanMeng.MiaoYu.WebPayApi/Program.cs index df2434c..ec038d4 100644 --- a/src/2-api/HuanMeng.MiaoYu.WebPayApi/Program.cs +++ b/src/2-api/HuanMeng.MiaoYu.WebPayApi/Program.cs @@ -1,23 +1,126 @@ +using AgileConfig.Client; + +using HuanMeng.DotNetCore.CustomExtension; +using HuanMeng.DotNetCore.MiddlewareExtend; +using HuanMeng.DotNetCore.Utility.AssemblyHelper; +using HuanMeng.MiaoYu.Code.Base; +using HuanMeng.MiaoYu.Code.JwtUtil; +using HuanMeng.MiaoYu.Code.MultiTenantUtil; +using HuanMeng.MiaoYu.Code.Other; +using HuanMeng.MiaoYu.Code.Payment; +using HuanMeng.MiaoYu.Code.SysDictionary; +using HuanMeng.MiaoYu.Code.TencentUtile; +using HuanMeng.MiaoYu.Code.Users.UserAccount.VerificationCodeManager; +using HuanMeng.MiaoYu.Model.Dto; +using HuanMeng.MiaoYu.Code.AppExtend; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.Extensions.Configuration; +using Microsoft.OpenApi.Models; + +using Newtonsoft.Json.Serialization; + +using Serilog; + +using System.Diagnostics; +using System.Reflection; var builder = WebApplication.CreateBuilder(args); - -// Add services to the container. - +builder.Host.UseSerilog((context, services, configuration) => configuration + .ReadFrom.Configuration(context.Configuration) + .ReadFrom.Services(services) + .Enrich.FromLogContext()); +builder.AddAppConfigClient(); +builder.Services.AddSingleton(typeof(ILogger), serviceProvider => +{ + var loggerFactory = serviceProvider.GetRequiredService(); + return loggerFactory.CreateLogger(); +}); +// 检索程序集信息 +AssemblyInfo assemblyInfo = AssemblyInfoHelper.GetAssemblyInfo(); +// Add services to the container. +builder.Services.AddHttpClient(); +builder.Services.AddHttpContextAccessor(); //添加httpContext注入访问 +#region 添加跨域 +var _myAllowSpecificOrigins = "_myAllowSpecificOrigins"; +builder.Services.AddCustomCors(_myAllowSpecificOrigins); +#endregion +#region automap +var mapperDomain = AppDomain.CurrentDomain.GetAssemblies().Where(it => it.FullName.Contains("HuanMeng") || it.FullName.Contains("XLib.")).ToList(); +Type type = typeof(ResponseUserInfo); +if (type != null) +{ + Assembly assembly = Assembly.GetAssembly(type); + if (!mapperDomain.Any(it => it.FullName == assembly.FullName)) + { + mapperDomain.Add(assembly); + } +} builder.Services.AddControllers(); +builder.Services.AddSwaggerGen(); +builder.Services.AddAutoMapper(mapperDomain); +#endregion + // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); +builder.AddDictionaryInfo(); +//配置路由选项,使URL全部小写 +//builder.Services.AddRouting(options => options.LowercaseUrls = true); +builder.AddPayment(); +//添加多租户 +builder.AddMultiTenantMiaoYu(); var app = builder.Build(); // Configure the HTTP request pipeline. -if (app.Environment.IsDevelopment()) +//if (app.Environment.IsDevelopment()) +//{ +app.UseSwagger(); +app.UseSwaggerUI(c => { - app.UseSwagger(); - app.UseSwaggerUI(); -} + c.EnableDeepLinking(); + c.DefaultModelsExpandDepth(3); + c.DefaultModelExpandDepth(3); + c.EnableFilter("true"); + //c.RoutePrefix = string.Empty; + c.SwaggerEndpoint("/swagger/v1/swagger.json", "寰梦支付API V1"); +}); +//} +app.UseSerilogRequestLogging(); app.UseAuthorization(); - +//自定义初始化 +//使用跨域 +app.UseCors(_myAllowSpecificOrigins); app.MapControllers(); +app.UseStaticFiles();//静态文件访问配置 + +//执行扩展中间件 +app.UseExceptionMiddleware() + .UseExecutionTimeMiddleware(); + +#region 默认请求 +app.MapGet("/", () => "请求成功").WithName("默认请求"); + +var startDateTime = DateTime.Now; +var InformationalVersion = Assembly.GetEntryAssembly().GetCustomAttribute().InformationalVersion; +Console.WriteLine($"version:{InformationalVersion}"); +app.MapGet("/system", () => +{ + + using Process currentProcess = Process.GetCurrentProcess(); + // CPU使用率 (一般是一个0-100之间的值,但实际是时间占比,需要转换) + double cpuUsage = currentProcess.TotalProcessorTime.TotalMilliseconds / Environment.TickCount * 100; + // 已用内存 (字节) + long memoryUsage = currentProcess.WorkingSet64; + return new + { + msg = $"系统信息:{assemblyInfo.InformationalVersion},启动时间:{startDateTime.ToString("yyyy-MM-dd HH:mm:ss")},已安全运行时间:{DateTime.Now.Subtract(startDateTime).TotalMinutes.ToString("0.##")}分钟", + assemblyInfo, + startDateTime, + MemoryUsage = $"{memoryUsage / (1024.0 * 1024.0):F2}MB", + CPUUsage = $"{cpuUsage:F2}%" + + }; +}).WithName("获取系统数据"); +#endregion app.Run(); diff --git a/src/2-api/HuanMeng.MiaoYu.WebPayApi/appsettings.Development.json b/src/2-api/HuanMeng.MiaoYu.WebPayApi/appsettings.Development.json index 0c208ae..a2020fc 100644 --- a/src/2-api/HuanMeng.MiaoYu.WebPayApi/appsettings.Development.json +++ b/src/2-api/HuanMeng.MiaoYu.WebPayApi/appsettings.Development.json @@ -4,5 +4,12 @@ "Default": "Information", "Microsoft.AspNetCore": "Warning" } + }, + "AgileConfig": { + "appId": "huanmeng", + "secret": "dfa47997-fb5c-b644-3770-880f5e7fb403", + "nodes": "http://124.220.55.158:94", //多个节点使用逗号分隔 + "env": "DEV", + "name": "PayClient" } } diff --git a/src/2-api/HuanMeng.MiaoYu.WebPayApi/appsettings.json b/src/2-api/HuanMeng.MiaoYu.WebPayApi/appsettings.json index 10f68b8..e682769 100644 --- a/src/2-api/HuanMeng.MiaoYu.WebPayApi/appsettings.json +++ b/src/2-api/HuanMeng.MiaoYu.WebPayApi/appsettings.json @@ -5,5 +5,56 @@ "Microsoft.AspNetCore": "Warning" } }, - "AllowedHosts": "*" + "AllowedHosts": "*", + "Serilog": { + "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ], + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } + }, + "WriteTo": [ + { "Name": "Console" }, + { + "Name": "File", + "Args": { + "path": "../output/logs/info/log-.txt", + "rollingInterval": "Day", + "restrictedToMinimumLevel": "Information", //写入日志的级别 + "shared": true + //"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "../output/logs/error/log-.txt", + "rollingInterval": "Day", //日志文件按天滚动生成。 + "restrictedToMinimumLevel": "Error", //写入日志的级别 //包括 Verbose、Debug、Information、Warning、Error 和 Fatal + "shared": true //不占用文件 + // "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}" + } + }, + { + "Name": "File", + "Args": { + "path": "../output/logs/debug/log-.txt", + "rollingInterval": "Day", //日志文件按天滚动生成。 + "restrictedToMinimumLevel": "Debug", //写入日志的级别 //包括 Verbose、Debug、Information、Warning、Error 和 Fatal + "shared": true //不占用文件 + // "outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level}] {Message}{NewLine}{Exception}" + } + } + ], + "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ] + }, + "AgileConfig": { + "appId": "huanmeng", + "secret": "dfa47997-fb5c-b644-3770-880f5e7fb403", + "nodes": "http://10.0.12.5:94", //多个节点使用逗号分隔 + "env": "PROD", + "name": "PayClient" + } }