From 74d425350189b36fb0f0acf10cd3d4ab70be6689 Mon Sep 17 00:00:00 2001 From: zuohuaijun Date: Thu, 28 Nov 2024 02:47:41 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=98=8E1=E3=80=81=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E7=A7=9F=E6=88=B7=E5=9F=9F=E5=90=8D=E4=B8=8D=E8=83=BD=E4=B8=BA?= =?UTF-8?q?=E7=A9=BA=20=202=E3=80=81=E4=BB=A3=E7=A0=81=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Admin.NET/Admin.NET.Core/Job/EnumToDictJob.cs | 424 +++---- .../Service/Auth/Dto/LoginInput.cs | 6 +- .../Service/Auth/SysAuthService.cs | 32 +- .../Service/Wechat/SysWechatPayService.cs | 1070 ++++++++--------- Admin.NET/Admin.NET.Core/Utils/CodeGenUtil.cs | 1 + 5 files changed, 766 insertions(+), 767 deletions(-) diff --git a/Admin.NET/Admin.NET.Core/Job/EnumToDictJob.cs b/Admin.NET/Admin.NET.Core/Job/EnumToDictJob.cs index 475cb738..8aaa5b69 100644 --- a/Admin.NET/Admin.NET.Core/Job/EnumToDictJob.cs +++ b/Admin.NET/Admin.NET.Core/Job/EnumToDictJob.cs @@ -1,215 +1,215 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -namespace Admin.NET.Core; - -/// -/// 枚举转字典 -/// -[JobDetail("job_EnumToDictJob", Description = "枚举转字典", GroupName = "default", Concurrent = false)] -[PeriodSeconds(1, TriggerId = "trigger_EnumToDictJob", Description = "枚举转字典", MaxNumberOfRuns = 1, RunOnStart = true)] -public class EnumToDictJob : IJob -{ - private readonly IServiceScopeFactory _scopeFactory; - private const int OrderOffset = 10; - private const string DefaultTagType = "info"; - - public EnumToDictJob(IServiceScopeFactory scopeFactory) - { - _scopeFactory = scopeFactory; - } - - public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken) - { - using var serviceScope = _scopeFactory.CreateScope(); - var sysEnumService = serviceScope.ServiceProvider.GetRequiredService(); - // 获取数据库连接 - var db = serviceScope.ServiceProvider.GetRequiredService().CopyNew(); - - // 获取枚举类型列表 - var enumTypeList = sysEnumService.GetEnumTypeList(); - var enumCodeList = enumTypeList.Select(u => u.TypeName); - // 查询数据库中已存在的枚举类型代码 - //var exp = Expressionable.Create>().And((t1, t2) => t1.Code == t2.ColumnName).ToExpression(); - //var sysDictTypeList = await db.Queryable().Includes(t1 => t1.Children).BulkListQuery(exp, enumCodeList, stoppingToken); - var sysDictTypeList = await db.Queryable().Includes(u => u.Children) - .Where(u => enumCodeList.Contains(u.Code)).ToListAsync(stoppingToken); - // 更新的枚举转换字典 - var updatedEnumCodes = sysDictTypeList.Select(u => u.Code); - var updatedEnumType = enumTypeList.Where(u => updatedEnumCodes.Contains(u.TypeName)).ToList(); - var sysDictTypeDict = sysDictTypeList.ToDictionary(u => u.Code, u => u); - var (updatedDictTypes, updatedDictDatas, newSysDictDatas) = GetUpdatedDicts(updatedEnumType, sysDictTypeDict); - - // 新增的枚举转换字典 - var newEnumType = enumTypeList.Where(u => !updatedEnumCodes.Contains(u.TypeName)).ToList(); - var (newDictTypes, newDictDatas) = GetNewSysDicts(newEnumType); - - // 执行数据库操作 - try - { - await db.BeginTranAsync(); - - if (updatedDictTypes.Count > 0) - await db.Updateable(updatedDictTypes).ExecuteCommandAsync(stoppingToken); - - if (updatedDictDatas.Count > 0) - await db.Updateable(updatedDictDatas).ExecuteCommandAsync(stoppingToken); - - if (newSysDictDatas.Count > 0) +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +namespace Admin.NET.Core; + +/// +/// 枚举转字典 +/// +[JobDetail("job_EnumToDictJob", Description = "枚举转字典", GroupName = "default", Concurrent = false)] +[PeriodSeconds(1, TriggerId = "trigger_EnumToDictJob", Description = "枚举转字典", MaxNumberOfRuns = 1, RunOnStart = true)] +public class EnumToDictJob : IJob +{ + private readonly IServiceScopeFactory _scopeFactory; + private const int OrderOffset = 10; + private const string DefaultTagType = "info"; + + public EnumToDictJob(IServiceScopeFactory scopeFactory) + { + _scopeFactory = scopeFactory; + } + + public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken) + { + using var serviceScope = _scopeFactory.CreateScope(); + var sysEnumService = serviceScope.ServiceProvider.GetRequiredService(); + // 获取数据库连接 + var db = serviceScope.ServiceProvider.GetRequiredService().CopyNew(); + + // 获取枚举类型列表 + var enumTypeList = sysEnumService.GetEnumTypeList(); + var enumCodeList = enumTypeList.Select(u => u.TypeName); + // 查询数据库中已存在的枚举类型代码 + //var exp = Expressionable.Create>().And((t1, t2) => t1.Code == t2.ColumnName).ToExpression(); + //var sysDictTypeList = await db.Queryable().Includes(t1 => t1.Children).BulkListQuery(exp, enumCodeList, stoppingToken); + var sysDictTypeList = await db.Queryable().Includes(u => u.Children) + .Where(u => enumCodeList.Contains(u.Code)).ToListAsync(stoppingToken); + // 更新的枚举转换字典 + var updatedEnumCodes = sysDictTypeList.Select(u => u.Code); + var updatedEnumType = enumTypeList.Where(u => updatedEnumCodes.Contains(u.TypeName)).ToList(); + var sysDictTypeDict = sysDictTypeList.ToDictionary(u => u.Code, u => u); + var (updatedDictTypes, updatedDictDatas, newSysDictDatas) = GetUpdatedDicts(updatedEnumType, sysDictTypeDict); + + // 新增的枚举转换字典 + var newEnumType = enumTypeList.Where(u => !updatedEnumCodes.Contains(u.TypeName)).ToList(); + var (newDictTypes, newDictDatas) = GetNewSysDicts(newEnumType); + + // 执行数据库操作 + try + { + await db.BeginTranAsync(); + + if (updatedDictTypes.Count > 0) + await db.Updateable(updatedDictTypes).ExecuteCommandAsync(stoppingToken); + + if (updatedDictDatas.Count > 0) + await db.Updateable(updatedDictDatas).ExecuteCommandAsync(stoppingToken); + + if (newSysDictDatas.Count > 0) { - //达梦 下用db.Insertable(newDictTypes).ExecuteCommandAsync(stoppingToken);插入400条以上会内容溢出错误,所以改用逐条插入 - //达梦 下不支持storageable2.BulkUpdateAsync 功能,注意使用 - foreach (var dd in newSysDictDatas) - await db.Insertable(dd).ExecuteCommandAsync(stoppingToken); - } - - if (newDictTypes.Count > 0) - await db.Insertable(newDictTypes).ExecuteCommandAsync(stoppingToken); - - if (newDictDatas.Count > 0) + // 达梦:用db.Insertable(newDictTypes).ExecuteCommandAsync(stoppingToken);插入400条以上会内容溢出错误,所以改用逐条插入 + // 达梦:不支持storageable2.BulkUpdateAsync 功能 + foreach (var dd in newSysDictDatas) + await db.Insertable(dd).ExecuteCommandAsync(stoppingToken); + } + + if (newDictTypes.Count > 0) + await db.Insertable(newDictTypes).ExecuteCommandAsync(stoppingToken); + + if (newDictDatas.Count > 0) { - //达梦 下用db.Insertable(newDictTypes).ExecuteCommandAsync(stoppingToken);插入400条以上会内容溢出错误,所以改用逐条插入 - //达梦 下不支持storageable2.BulkUpdateAsync 功能,注意使用 - foreach (var dd in newDictDatas) - await db.Insertable(dd).ExecuteCommandAsync(stoppingToken); - } - - await db.CommitTranAsync(); - } - catch (Exception error) - { - await db.RollbackTranAsync(); - Log.Error($"系统枚举转换字典操作错误:{error.Message}\n堆栈跟踪:{error.StackTrace}", error); - throw; - } - var originColor = Console.ForegroundColor; - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine($"【{DateTime.Now}】系统枚举转换字典"); - Console.ForegroundColor = originColor; - } - - /// - /// 获取需要新增的字典列表 - /// - /// - /// - /// 一个元组,包含以下元素: - /// - /// SysDictTypes字典类型列表 - /// SysDictDatas字典数据列表 - /// - /// - private (List, List) GetNewSysDicts(List addEnumType) - { - var newDictType = new List(); - var newDictData = new List(); - if (addEnumType.Count <= 0) - return (newDictType, newDictData); - - // 新增字典类型 - newDictType = addEnumType.Select(u => new SysDictType - { - Id = YitIdHelper.NextId(), - Code = u.TypeName, - Name = u.TypeDescribe, - Remark = u.TypeRemark, - Status = StatusEnum.Enable - }).ToList(); - - // 新增字典数据 - newDictData = addEnumType.Join(newDictType, t1 => t1.TypeName, t2 => t2.Code, (t1, t2) => new - { - Data = t1.EnumEntities.Select(u => new SysDictData - { - Id = YitIdHelper.NextId(), - DictTypeId = t2.Id, - Name = u.Describe, - Value = u.Value.ToString(), - Code = u.Name, - Remark = t2.Remark, - OrderNo = u.Value + OrderOffset, - TagType = u.Theme != "" ? u.Theme : DefaultTagType, - }).ToList() - }).SelectMany(x => x.Data).ToList(); - - return (newDictType, newDictData); - } - - /// - /// 获取需要更新的字典列表 - /// - /// - /// - /// - /// 一个元组,包含以下元素: - /// - /// SysDictTypes更新字典类型列表 - /// - /// SysDictDatas更新字典数据列表 - /// - /// SysDictDatas新增字典数据列表 - /// - /// - /// - private (List, List, List) GetUpdatedDicts(List updatedEnumType, Dictionary sysDictTypeDict) - { - var updatedSysDictTypes = new List(); - var updatedSysDictData = new List(); - var newSysDictData = new List(); - foreach (var e in updatedEnumType) - { - if (!sysDictTypeDict.TryGetValue(e.TypeName, out var value)) - continue; - - var updatedDictType = value; - updatedDictType.Name = e.TypeDescribe; - updatedDictType.Remark = e.TypeRemark; - updatedSysDictTypes.Add(updatedDictType); - var updatedDictData = updatedDictType.Children.Where(u => u.DictTypeId == updatedDictType.Id).ToList(); - - // 遍历需要更新的字典数据 - foreach (var dictData in updatedDictData) - { - var enumData = e.EnumEntities.FirstOrDefault(u => dictData.Code == u.Name); - if (enumData != null) - { - dictData.Value = enumData.Value.ToString(); - dictData.OrderNo = enumData.Value + OrderOffset; - dictData.Name = enumData.Describe; - dictData.TagType = enumData.Theme != "" ? enumData.Theme : dictData.TagType != "" ? dictData.TagType : DefaultTagType; - updatedSysDictData.Add(dictData); - } - } - - // 新增的枚举值名称列表 - var newEnumDataNameList = e.EnumEntities.Select(u => u.Name).Except(updatedDictData.Select(u => u.Code)); - foreach (var newEnumDataName in newEnumDataNameList) - { - var enumData = e.EnumEntities.FirstOrDefault(u => newEnumDataName == u.Name); - if (enumData != null) - { - var dictData = new SysDictData - { - Id = YitIdHelper.NextId(), - DictTypeId = updatedDictType.Id, - Name = enumData.Describe, - Value = enumData.Value.ToString(), - Code = enumData.Name, - Remark = updatedDictType.Remark, - OrderNo = enumData.Value + OrderOffset, - TagType = enumData.Theme != "" ? enumData.Theme : DefaultTagType, - }; - dictData.TagType = enumData.Theme != "" ? enumData.Theme : dictData.TagType != "" ? dictData.TagType : DefaultTagType; - newSysDictData.Add(dictData); - } - } - - // 删除的情况暂不处理 - } - - return (updatedSysDictTypes, updatedSysDictData, newSysDictData); - } + // 达梦:用db.Insertable(newDictTypes).ExecuteCommandAsync(stoppingToken);插入400条以上会内容溢出错误,所以改用逐条插入 + // 达梦:不支持storageable2.BulkUpdateAsync 功能 + foreach (var dd in newDictDatas) + await db.Insertable(dd).ExecuteCommandAsync(stoppingToken); + } + + await db.CommitTranAsync(); + } + catch (Exception error) + { + await db.RollbackTranAsync(); + Log.Error($"系统枚举转换字典操作错误:{error.Message}\n堆栈跟踪:{error.StackTrace}", error); + throw; + } + var originColor = Console.ForegroundColor; + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine($"【{DateTime.Now}】系统枚举转换字典"); + Console.ForegroundColor = originColor; + } + + /// + /// 获取需要新增的字典列表 + /// + /// + /// + /// 一个元组,包含以下元素: + /// + /// SysDictTypes字典类型列表 + /// SysDictDatas字典数据列表 + /// + /// + private (List, List) GetNewSysDicts(List addEnumType) + { + var newDictType = new List(); + var newDictData = new List(); + if (addEnumType.Count <= 0) + return (newDictType, newDictData); + + // 新增字典类型 + newDictType = addEnumType.Select(u => new SysDictType + { + Id = YitIdHelper.NextId(), + Code = u.TypeName, + Name = u.TypeDescribe, + Remark = u.TypeRemark, + Status = StatusEnum.Enable + }).ToList(); + + // 新增字典数据 + newDictData = addEnumType.Join(newDictType, t1 => t1.TypeName, t2 => t2.Code, (t1, t2) => new + { + Data = t1.EnumEntities.Select(u => new SysDictData + { + Id = YitIdHelper.NextId(), + DictTypeId = t2.Id, + Name = u.Describe, + Value = u.Value.ToString(), + Code = u.Name, + Remark = t2.Remark, + OrderNo = u.Value + OrderOffset, + TagType = u.Theme != "" ? u.Theme : DefaultTagType, + }).ToList() + }).SelectMany(x => x.Data).ToList(); + + return (newDictType, newDictData); + } + + /// + /// 获取需要更新的字典列表 + /// + /// + /// + /// + /// 一个元组,包含以下元素: + /// + /// SysDictTypes更新字典类型列表 + /// + /// SysDictDatas更新字典数据列表 + /// + /// SysDictDatas新增字典数据列表 + /// + /// + /// + private (List, List, List) GetUpdatedDicts(List updatedEnumType, Dictionary sysDictTypeDict) + { + var updatedSysDictTypes = new List(); + var updatedSysDictData = new List(); + var newSysDictData = new List(); + foreach (var e in updatedEnumType) + { + if (!sysDictTypeDict.TryGetValue(e.TypeName, out var value)) + continue; + + var updatedDictType = value; + updatedDictType.Name = e.TypeDescribe; + updatedDictType.Remark = e.TypeRemark; + updatedSysDictTypes.Add(updatedDictType); + var updatedDictData = updatedDictType.Children.Where(u => u.DictTypeId == updatedDictType.Id).ToList(); + + // 遍历需要更新的字典数据 + foreach (var dictData in updatedDictData) + { + var enumData = e.EnumEntities.FirstOrDefault(u => dictData.Code == u.Name); + if (enumData != null) + { + dictData.Value = enumData.Value.ToString(); + dictData.OrderNo = enumData.Value + OrderOffset; + dictData.Name = enumData.Describe; + dictData.TagType = enumData.Theme != "" ? enumData.Theme : dictData.TagType != "" ? dictData.TagType : DefaultTagType; + updatedSysDictData.Add(dictData); + } + } + + // 新增的枚举值名称列表 + var newEnumDataNameList = e.EnumEntities.Select(u => u.Name).Except(updatedDictData.Select(u => u.Code)); + foreach (var newEnumDataName in newEnumDataNameList) + { + var enumData = e.EnumEntities.FirstOrDefault(u => newEnumDataName == u.Name); + if (enumData != null) + { + var dictData = new SysDictData + { + Id = YitIdHelper.NextId(), + DictTypeId = updatedDictType.Id, + Name = enumData.Describe, + Value = enumData.Value.ToString(), + Code = enumData.Name, + Remark = updatedDictType.Remark, + OrderNo = enumData.Value + OrderOffset, + TagType = enumData.Theme != "" ? enumData.Theme : DefaultTagType, + }; + dictData.TagType = enumData.Theme != "" ? enumData.Theme : dictData.TagType != "" ? dictData.TagType : DefaultTagType; + newSysDictData.Add(dictData); + } + } + + // 删除的情况暂不处理 + } + + return (updatedSysDictTypes, updatedSysDictData, newSysDictData); + } } \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Core/Service/Auth/Dto/LoginInput.cs b/Admin.NET/Admin.NET.Core/Service/Auth/Dto/LoginInput.cs index 98771558..39f83e66 100644 --- a/Admin.NET/Admin.NET.Core/Service/Auth/Dto/LoginInput.cs +++ b/Admin.NET/Admin.NET.Core/Service/Auth/Dto/LoginInput.cs @@ -28,7 +28,7 @@ public class LoginInput /// /// 租户域名 /// - [Required(ErrorMessage = "租户域名不能为空")] + //[Required(ErrorMessage = "租户域名不能为空")] public string Host { get; set; } /// @@ -67,8 +67,8 @@ public class LoginPhoneInput /// /// 租户域名 /// - [Required(ErrorMessage = "租户域名不能为空")] - public string? Host { get; set; } + //[Required(ErrorMessage = "租户域名不能为空")] + public string Host { get; set; } /// /// 登录模式 diff --git a/Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs b/Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs index 6ccd67f3..dc53bd0d 100644 --- a/Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs +++ b/Admin.NET/Admin.NET.Core/Service/Auth/SysAuthService.cs @@ -153,26 +153,24 @@ public class SysAuthService : IDynamicApiController, ITransient /// private void VerifyPassword(string password, string keyPasswordErrorTimes, int passwordErrorTimes, SysUser user) { - if (CryptogramUtil.CryptoType == CryptogramEnum.MD5.ToString()) + // 国密SM2解密(前端密码传输SM2加密后的) + try { - // 国密SM2解密(前端密码传输SM2加密后的) - try - { - password = CryptogramUtil.SM2Decrypt(password); - } - catch - { - throw Oops.Oh(ErrorCodeEnum.D0010); - } - if (user.Password.Equals(MD5Encryption.Encrypt(password))) return; - - _sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30)); - throw Oops.Oh(ErrorCodeEnum.D1000); + password = CryptogramUtil.SM2Decrypt(password); + } + catch + { + throw Oops.Oh(ErrorCodeEnum.D0010); } - // 国密SM2解密(前端密码传输SM2加密后的) - password = CryptogramUtil.SM2Decrypt(password); - if (CryptogramUtil.Decrypt(user.Password).Equals(password)) return; + if (CryptogramUtil.CryptoType == CryptogramEnum.MD5.ToString()) + { + if (user.Password.Equals(MD5Encryption.Encrypt(password))) return; + } + else + { + if (CryptogramUtil.Decrypt(user.Password).Equals(password)) return; + } _sysCacheService.Set(keyPasswordErrorTimes, ++passwordErrorTimes, TimeSpan.FromMinutes(30)); throw Oops.Oh(ErrorCodeEnum.D1000); diff --git a/Admin.NET/Admin.NET.Core/Service/Wechat/SysWechatPayService.cs b/Admin.NET/Admin.NET.Core/Service/Wechat/SysWechatPayService.cs index 821aa94a..5d0271aa 100644 --- a/Admin.NET/Admin.NET.Core/Service/Wechat/SysWechatPayService.cs +++ b/Admin.NET/Admin.NET.Core/Service/Wechat/SysWechatPayService.cs @@ -1,538 +1,538 @@ -// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 -// -// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 -// -// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! - -using Furion.Logging.Extensions; -using Newtonsoft.Json; - -namespace Admin.NET.Core.Service; - -/// -/// 微信支付服务 🧩 -/// -[ApiDescriptionSettings(Order = 210, Description = "微信支付")] -public class SysWechatPayService : IDynamicApiController, ITransient -{ - private readonly SqlSugarRepository _sysWechatPayRep; - private readonly SqlSugarRepository _sysWechatRefundRep; - private readonly WechatPayOptions _wechatPayOptions; - private readonly PayCallBackOptions _payCallBackOptions; - - private readonly WechatTenpayClient _wechatTenpayClient; - - public SysWechatPayService(SqlSugarRepository sysWechatPayRep, - SqlSugarRepository sysWechatRefundRep, - IOptions wechatPayOptions, - IOptions payCallBackOptions) - { - _sysWechatPayRep = sysWechatPayRep; - this._sysWechatRefundRep = sysWechatRefundRep; - _wechatPayOptions = wechatPayOptions.Value; - _payCallBackOptions = payCallBackOptions.Value; - - _wechatTenpayClient = CreateTenpayClient(); - } - - /// - /// 初始化微信支付客户端 - /// - /// - private WechatTenpayClient CreateTenpayClient() - { - var cerFilePath = App.WebHostEnvironment.ContentRootPath + _wechatPayOptions.MerchantCertificatePrivateKey; - - if (!File.Exists(cerFilePath)) - Log.Warning("商户证书文件不存在:" + cerFilePath); - - var tenpayClientOptions = new WechatTenpayClientOptions() - { - MerchantId = _wechatPayOptions.MerchantId, - MerchantV3Secret = _wechatPayOptions.MerchantV3Secret, - MerchantCertificateSerialNumber = _wechatPayOptions.MerchantCertificateSerialNumber, - MerchantCertificatePrivateKey = File.Exists(cerFilePath) ? File.ReadAllText(cerFilePath) : "", - PlatformCertificateManager = new InMemoryCertificateManager() - }; - return new WechatTenpayClient(tenpayClientOptions); - } - - /// - /// 生成JSAPI调起支付所需参数 🔖 - /// - /// - /// - [DisplayName("生成JSAPI调起支付所需参数")] - public dynamic GenerateParametersForJsapiPay(WechatPayParaInput input) - { - return _wechatTenpayClient.GenerateParametersForJsapiPayRequest(_wechatPayOptions.AppId, input.PrepayId); - } - - /// - /// 微信支付统一下单获取Id(商户直连) 🔖 - /// - [DisplayName("微信支付统一下单获取Id(商户直连)")] - public async Task CreatePayTransaction([FromBody] WechatPayTransactionInput input) - { - string outTradeNumber = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff") + (new Random()).Next(100, 1000); // 微信需要的订单号(唯一) - - // 检查订单信息是否已存在(使用“商户交易单号+状态”唯一性判断) - var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OrderId == input.OrderId && u.OrderStatus == input.OrderStatus); - if (wechatPay != null) - { - outTradeNumber = wechatPay.OutTradeNumber; - } - - var request = new CreatePayTransactionJsapiRequest() - { - OutTradeNumber = outTradeNumber, - AppId = _wechatPayOptions.AppId, - Description = input.Description, - Attachment = input.Attachment, - GoodsTag = input.GoodsTag, - ExpireTime = DateTimeOffset.Now.AddMinutes(10), - NotifyUrl = _payCallBackOptions.WechatPayUrl, - Amount = new CreatePayTransactionJsapiRequest.Types.Amount() { Total = input.Total }, - Payer = new CreatePayTransactionJsapiRequest.Types.Payer() { OpenId = input.OpenId } - }; - var response = await _wechatTenpayClient.ExecuteCreatePayTransactionJsapiAsync(request); - if (!response.IsSuccessful()) - throw Oops.Oh(response.ErrorMessage); - - if (wechatPay == null) - { - // 保存订单信息 - wechatPay = new SysWechatPay() - { - AppId = _wechatPayOptions.AppId, - MerchantId = _wechatPayOptions.MerchantId, - OutTradeNumber = request.OutTradeNumber, - Description = input.Description, - Attachment = input.Attachment, - GoodsTag = input.GoodsTag, - Total = input.Total, - OpenId = input.OpenId, - TransactionId = "", - OrderId = input.OrderId, - OrderStatus = input.OrderStatus, - Tags = input.Tags, - BusinessId = input.BusinessId, - }; - await _sysWechatPayRep.InsertAsync(wechatPay); - } - - //var singInfo = GenerateParametersForJsapiPay(new WechatPayParaInput() { PrepayId = response.PrepayId }); - return new CreatePayTransactionOutput - { - PrepayId = response.PrepayId, - OutTradeNumber = request.OutTradeNumber, - //SingInfo = singInfo - }; - } - - /// - /// 微信支付统一下单(商户直连)Native - /// - [DisplayName("微信支付统一下单(商户直连)Native")] - public async Task CreatePayTransactionNative([FromBody] WechatPayTransactionInput input) - { - var request = new CreatePayTransactionNativeRequest() - { - OutTradeNumber = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff") + (new Random()).Next(100, 1000), // 微信需要的订单号(唯一) - AppId = _wechatPayOptions.AppId, - Description = input.Description, - Attachment = input.Attachment, - GoodsTag = input.GoodsTag, - ExpireTime = DateTimeOffset.Now.AddMinutes(10), - NotifyUrl = _payCallBackOptions.WechatPayUrl, - Amount = new CreatePayTransactionNativeRequest.Types.Amount() { Total = input.Total }, - //Payer = new CreatePayTransactionNativeRequest.Types.Payer() { OpenId = input.OpenId } - Scene = new CreatePayTransactionNativeRequest.Types.Scene() { ClientIp = "127.0.0.1" } - }; - var response = await _wechatTenpayClient.ExecuteCreatePayTransactionNativeAsync(request); - if (!response.IsSuccessful()) - { - JsonConvert.SerializeObject(response).LogInformation(); - throw Oops.Oh(response.ErrorMessage); - } - // 保存订单信息 - var wechatPay = new SysWechatPay() - { - AppId = _wechatPayOptions.AppId, - MerchantId = _wechatPayOptions.MerchantId, - OutTradeNumber = request.OutTradeNumber, - Description = input.Description, - Attachment = input.Attachment, - GoodsTag = input.GoodsTag, - Total = input.Total, - OpenId = input.OpenId, - TransactionId = "", - QrcodeContent = response.QrcodeUrl, - Tags = input.Tags, - BusinessId = input.BusinessId, - }; - await _sysWechatPayRep.InsertAsync(wechatPay); - return new CreatePayTransactionNativeOutput - { - OutTradeNumber = request.OutTradeNumber, - QrcodeUrl = response.QrcodeUrl - }; - } - - /// - /// 微信支付统一下单获取Id(服务商模式) 🔖 - /// - [DisplayName("微信支付统一下单获取Id(服务商模式)")] - public async Task CreatePayPartnerTransaction([FromBody] WechatPayTransactionInput input) - { - string outTradeNumber = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff") + (new Random()).Next(100, 1000); // 微信需要的订单号(唯一) - - //检查订单信息是否已存在(使用“商户交易单号+状态”唯一性判断) - var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OrderId == input.OrderId && u.OrderStatus == input.OrderStatus); - if (wechatPay != null) - { - outTradeNumber = wechatPay.OutTradeNumber; - } - - var request = new CreatePayPartnerTransactionJsapiRequest() - { - OutTradeNumber = outTradeNumber, - AppId = _wechatPayOptions.AppId, - MerchantId = _wechatPayOptions.MerchantId, - SubAppId = _wechatPayOptions.AppId, - SubMerchantId = _wechatPayOptions.MerchantId, - Description = input.Description, - Attachment = input.Attachment, - GoodsTag = input.GoodsTag, - ExpireTime = DateTimeOffset.Now.AddMinutes(10), - NotifyUrl = _payCallBackOptions.WechatPayUrl, - Amount = new CreatePayPartnerTransactionJsapiRequest.Types.Amount() { Total = input.Total }, - Payer = new CreatePayPartnerTransactionJsapiRequest.Types.Payer() { OpenId = input.OpenId } - }; - var response = await _wechatTenpayClient.ExecuteCreatePayPartnerTransactionJsapiAsync(request); - if (!response.IsSuccessful()) - throw Oops.Oh($"JSAPI 下单失败(状态码:{response.GetRawStatus()},错误代码:{response.ErrorCode},错误描述:{response.ErrorMessage})"); - if (wechatPay == null) - { - // 保存订单信息 - wechatPay = new SysWechatPay() - { - AppId = _wechatPayOptions.AppId, - MerchantId = _wechatPayOptions.MerchantId, - SubAppId = _wechatPayOptions.AppId, - SubMerchantId = _wechatPayOptions.MerchantId, - OutTradeNumber = request.OutTradeNumber, - Description = input.Description, - Attachment = input.Attachment, - GoodsTag = input.GoodsTag, - Total = input.Total, - OpenId = input.OpenId, - TransactionId = "" - }; - await _sysWechatPayRep.InsertAsync(wechatPay); - } - - return new CreatePayTransactionOutput - { - PrepayId = response.PrepayId, - OutTradeNumber = request.OutTradeNumber - }; - } - - /// - /// 获取支付订单详情 🔖 - /// - /// - /// - [DisplayName("获取支付订单详情")] - public async Task GetPayInfo(string tradeId) - { - return await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == tradeId); - } - - /// - /// 微信支付成功回调(商户直连) 🔖 - /// - /// - [AllowAnonymous] - [DisplayName("微信支付成功回调(商户直连)")] - public async Task PayCallBack() - { - using var ms = new MemoryStream(); - await App.HttpContext.Request.Body.CopyToAsync(ms); - var b = ms.ToArray(); - var callbackJson = Encoding.UTF8.GetString(b); - - var callbackModel = _wechatTenpayClient.DeserializeEvent(callbackJson); - if ("TRANSACTION.SUCCESS".Equals(callbackModel.EventType)) - { - var callbackResource = _wechatTenpayClient.DecryptEventResource(callbackModel); - - // 修改订单支付状态 - var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == callbackResource.OutTradeNumber - && u.MerchantId == callbackResource.MerchantId); - if (wechatPay == null) return null; - //wechatPay.OpenId = callbackResource.Payer.OpenId; // 支付者标识 - //wechatPay.MerchantId = callbackResource.MerchantId; // 微信商户号 - //wechatPay.OutTradeNumber = callbackResource.OutTradeNumber; // 商户订单号 - wechatPay.TransactionId = callbackResource.TransactionId; // 支付订单号 - wechatPay.TradeType = callbackResource.TradeType; // 交易类型 - wechatPay.TradeState = callbackResource.TradeState; // 交易状态 - wechatPay.TradeStateDescription = callbackResource.TradeStateDescription; // 交易状态描述 - wechatPay.BankType = callbackResource.BankType; // 付款银行类型 - wechatPay.Total = callbackResource.Amount.Total; // 订单总金额 - wechatPay.PayerTotal = callbackResource.Amount.PayerTotal; // 用户支付金额 - wechatPay.SuccessTime = callbackResource.SuccessTime; // 支付完成时间 - - await _sysWechatPayRep.AsUpdateable(wechatPay).IgnoreColumns(true).ExecuteCommandAsync(); - - return new WechatPayOutput() - { - Total = wechatPay.Total, - Attachment = wechatPay.Attachment, - GoodsTag = wechatPay.GoodsTag - }; - } - - return null; - } - - /// - /// 微信支付成功回调(服务商模式) 🔖 - /// - /// - [AllowAnonymous] - [DisplayName("微信支付成功回调(服务商模式)")] - public async Task PayPartnerCallBack() - { - using var ms = new MemoryStream(); - await App.HttpContext.Request.Body.CopyToAsync(ms); - var b = ms.ToArray(); - var callbackJson = Encoding.UTF8.GetString(b); - - var callbackModel = _wechatTenpayClient.DeserializeEvent(callbackJson); - if ("TRANSACTION.SUCCESS".Equals(callbackModel.EventType)) - { - var callbackResource = _wechatTenpayClient.DecryptEventResource(callbackModel); - - // 修改订单支付状态 - var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == callbackResource.OutTradeNumber - && u.MerchantId == callbackResource.MerchantId); - if (wechatPay == null) return; - //wechatPay.OpenId = callbackResource.Payer.OpenId; // 支付者标识 - //wechatPay.MerchantId = callbackResource.MerchantId; // 微信商户号 - //wechatPay.OutTradeNumber = callbackResource.OutTradeNumber; // 商户订单号 - wechatPay.TransactionId = callbackResource.TransactionId; // 支付订单号 - wechatPay.TradeType = callbackResource.TradeType; // 交易类型 - wechatPay.TradeState = callbackResource.TradeState; // 交易状态 - wechatPay.TradeStateDescription = callbackResource.TradeStateDescription; // 交易状态描述 - wechatPay.BankType = callbackResource.BankType; // 付款银行类型 - wechatPay.Total = callbackResource.Amount.Total; // 订单总金额 - wechatPay.PayerTotal = callbackResource.Amount.PayerTotal; // 用户支付金额 - wechatPay.SuccessTime = callbackResource.SuccessTime; // 支付完成时间 - - await _sysWechatPayRep.AsUpdateable(wechatPay).IgnoreColumns(true).ExecuteCommandAsync(); - } - } - - /// - /// 退款申请 🔖 - /// https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/create.html - /// - /// - /// - [DisplayName("微信退款申请)")] - public async Task Refund(RefundRequestInput input) - { - var request = new CreateRefundDomesticRefundRequest() - { - OutTradeNumber = input.OutTradeNumber, - OutRefundNumber = "REFUND_" + DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"), - Amount = new CreateRefundDomesticRefundRequest.Types.Amount() - { - Total = input.Total, - Refund = input.Refund - }, - Reason = input.Reason, - }; - var response = await _wechatTenpayClient.ExecuteCreateRefundDomesticRefundAsync(request); - - if (!response.IsSuccessful()) - throw Oops.Oh($"JSAPI 退款申请失败(状态码:{response.GetRawStatus()},错误代码:{response.ErrorCode},错误描述:{response.ErrorMessage})"); - - var wechatRefund = await _sysWechatRefundRep.GetFirstAsync(u => u.OutTradeNumber == input.OutTradeNumber); - if (wechatRefund == null) - { - // 保存退款申请信息 - wechatRefund = new SysWechatRefund() - { - TransactionId = input.OutTradeNumber, - OutTradeNumber = request.OutTradeNumber, - OutRefundNo = request.OutTradeNumber, // 每笔付款只退一次,所以这里直接用付款单号 - Reason = request.Reason, - Refund = input.Refund, - Total = input.Total, - //NotifyUrl = "", - OrderId = input.OrderId, - OrderStatus = input.OrderStatus, - MerchantGoodsId = input.MerchantGoodsId, - GoodsName = input.GoodsName, - UnitPrice = input.UnitPrice, - RefundAmount = input.RefundAmount, - RefundQuantity = input.RefundQuantity, - Attachment = input.Attachment, - Remark = input.Remark - }; - await _sysWechatRefundRep.InsertAsync(wechatRefund); - } - } - - /// - /// 查询单笔退款(通过商户退款单号) 🔖 - /// https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/query-by-out-refund-no.html - /// - /// - /// - [DisplayName("微信查询单笔退款)")] - public async Task GetRefundByOutRefundNumber(string outRefundNumber) - { - var request = new GetRefundDomesticRefundByOutRefundNumberRequest() - { - OutRefundNumber = outRefundNumber - }; - return await _wechatTenpayClient.ExecuteGetRefundDomesticRefundByOutRefundNumberAsync(request); - } - - /// - /// 微信支付订单号查询(校正) 🔖 - /// https://api.mch.weixin.qq.com/v3/pay/transactions/id/{transaction_id} - /// - /// - /// - [DisplayName("微信支付订单号查询(校正)")] - public async Task GetPayTransactionByIdAsync(string transactionId) - { - if (string.IsNullOrEmpty(transactionId)) - throw Oops.Oh("TransactionId 不能为空"); - - if (string.IsNullOrEmpty(_wechatPayOptions.MerchantId) || string.IsNullOrEmpty(_wechatPayOptions.MerchantCertificateSerialNumber)) - throw Oops.Oh("商户号或证书序列号不能为空,请检查支付配置"); - - var request = new GetPayTransactionByIdRequest() - { - MerchantId = _wechatPayOptions.MerchantId, - TransactionId = transactionId, - WechatpaySerialNumber = _wechatPayOptions.MerchantCertificateSerialNumber - }; - var response = await _wechatTenpayClient.ExecuteGetPayTransactionByIdAsync(request); - if (response.TradeState == "SUCCESS" || response.TradeState == "CLOSED" || response.TradeState == "NOTPAY") - { - // 修正订单支付状态 - var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.TransactionId == request.TransactionId && u.MerchantId == request.MerchantId); - if (wechatPay != null && string.IsNullOrEmpty(wechatPay.TradeState)) - { - wechatPay.TradeType = response.TradeType; // 交易类型 - wechatPay.TradeState = response.TradeState; // 交易状态 +// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。 +// +// 本项目主要遵循 MIT 许可证和 Apache 许可证(版本 2.0)进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。 +// +// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任! + +using Furion.Logging.Extensions; +using Newtonsoft.Json; + +namespace Admin.NET.Core.Service; + +/// +/// 微信支付服务 🧩 +/// +[ApiDescriptionSettings(Order = 210, Description = "微信支付")] +public class SysWechatPayService : IDynamicApiController, ITransient +{ + private readonly SqlSugarRepository _sysWechatPayRep; + private readonly SqlSugarRepository _sysWechatRefundRep; + private readonly WechatPayOptions _wechatPayOptions; + private readonly PayCallBackOptions _payCallBackOptions; + + private readonly WechatTenpayClient _wechatTenpayClient; + + public SysWechatPayService(SqlSugarRepository sysWechatPayRep, + SqlSugarRepository sysWechatRefundRep, + IOptions wechatPayOptions, + IOptions payCallBackOptions) + { + _sysWechatPayRep = sysWechatPayRep; + this._sysWechatRefundRep = sysWechatRefundRep; + _wechatPayOptions = wechatPayOptions.Value; + _payCallBackOptions = payCallBackOptions.Value; + + _wechatTenpayClient = CreateTenpayClient(); + } + + /// + /// 初始化微信支付客户端 + /// + /// + private WechatTenpayClient CreateTenpayClient() + { + var cerFilePath = App.WebHostEnvironment.ContentRootPath + _wechatPayOptions.MerchantCertificatePrivateKey; + + if (!File.Exists(cerFilePath)) + Log.Warning("商户证书文件不存在:" + cerFilePath); + + var tenpayClientOptions = new WechatTenpayClientOptions() + { + MerchantId = _wechatPayOptions.MerchantId, + MerchantV3Secret = _wechatPayOptions.MerchantV3Secret, + MerchantCertificateSerialNumber = _wechatPayOptions.MerchantCertificateSerialNumber, + MerchantCertificatePrivateKey = File.Exists(cerFilePath) ? File.ReadAllText(cerFilePath) : "", + PlatformCertificateManager = new InMemoryCertificateManager() + }; + return new WechatTenpayClient(tenpayClientOptions); + } + + /// + /// 生成JSAPI调起支付所需参数 🔖 + /// + /// + /// + [DisplayName("生成JSAPI调起支付所需参数")] + public dynamic GenerateParametersForJsapiPay(WechatPayParaInput input) + { + return _wechatTenpayClient.GenerateParametersForJsapiPayRequest(_wechatPayOptions.AppId, input.PrepayId); + } + + /// + /// 微信支付统一下单获取Id(商户直连) 🔖 + /// + [DisplayName("微信支付统一下单获取Id(商户直连)")] + public async Task CreatePayTransaction([FromBody] WechatPayTransactionInput input) + { + string outTradeNumber = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff") + (new Random()).Next(100, 1000); // 微信需要的订单号(唯一) + + // 检查订单信息是否已存在(使用“商户交易单号+状态”唯一性判断) + var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OrderId == input.OrderId && u.OrderStatus == input.OrderStatus); + if (wechatPay != null) + { + outTradeNumber = wechatPay.OutTradeNumber; + } + + var request = new CreatePayTransactionJsapiRequest() + { + OutTradeNumber = outTradeNumber, + AppId = _wechatPayOptions.AppId, + Description = input.Description, + Attachment = input.Attachment, + GoodsTag = input.GoodsTag, + ExpireTime = DateTimeOffset.Now.AddMinutes(10), + NotifyUrl = _payCallBackOptions.WechatPayUrl, + Amount = new CreatePayTransactionJsapiRequest.Types.Amount() { Total = input.Total }, + Payer = new CreatePayTransactionJsapiRequest.Types.Payer() { OpenId = input.OpenId } + }; + var response = await _wechatTenpayClient.ExecuteCreatePayTransactionJsapiAsync(request); + if (!response.IsSuccessful()) + throw Oops.Oh(response.ErrorMessage); + + if (wechatPay == null) + { + // 保存订单信息 + wechatPay = new SysWechatPay() + { + AppId = _wechatPayOptions.AppId, + MerchantId = _wechatPayOptions.MerchantId, + OutTradeNumber = request.OutTradeNumber, + Description = input.Description, + Attachment = input.Attachment, + GoodsTag = input.GoodsTag, + Total = input.Total, + OpenId = input.OpenId, + TransactionId = "", + OrderId = input.OrderId, + OrderStatus = input.OrderStatus, + Tags = input.Tags, + BusinessId = input.BusinessId, + }; + await _sysWechatPayRep.InsertAsync(wechatPay); + } + + //var singInfo = GenerateParametersForJsapiPay(new WechatPayParaInput() { PrepayId = response.PrepayId }); + return new CreatePayTransactionOutput + { + PrepayId = response.PrepayId, + OutTradeNumber = request.OutTradeNumber, + //SingInfo = singInfo + }; + } + + /// + /// 微信支付统一下单(商户直连)Native + /// + [DisplayName("微信支付统一下单(商户直连)Native")] + public async Task CreatePayTransactionNative([FromBody] WechatPayTransactionInput input) + { + var request = new CreatePayTransactionNativeRequest() + { + OutTradeNumber = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff") + (new Random()).Next(100, 1000), // 微信需要的订单号(唯一) + AppId = _wechatPayOptions.AppId, + Description = input.Description, + Attachment = input.Attachment, + GoodsTag = input.GoodsTag, + ExpireTime = DateTimeOffset.Now.AddMinutes(10), + NotifyUrl = _payCallBackOptions.WechatPayUrl, + Amount = new CreatePayTransactionNativeRequest.Types.Amount() { Total = input.Total }, + //Payer = new CreatePayTransactionNativeRequest.Types.Payer() { OpenId = input.OpenId } + Scene = new CreatePayTransactionNativeRequest.Types.Scene() { ClientIp = "127.0.0.1" } + }; + var response = await _wechatTenpayClient.ExecuteCreatePayTransactionNativeAsync(request); + if (!response.IsSuccessful()) + { + JsonConvert.SerializeObject(response).LogInformation(); + throw Oops.Oh(response.ErrorMessage); + } + // 保存订单信息 + var wechatPay = new SysWechatPay() + { + AppId = _wechatPayOptions.AppId, + MerchantId = _wechatPayOptions.MerchantId, + OutTradeNumber = request.OutTradeNumber, + Description = input.Description, + Attachment = input.Attachment, + GoodsTag = input.GoodsTag, + Total = input.Total, + OpenId = input.OpenId, + TransactionId = "", + QrcodeContent = response.QrcodeUrl, + Tags = input.Tags, + BusinessId = input.BusinessId, + }; + await _sysWechatPayRep.InsertAsync(wechatPay); + return new CreatePayTransactionNativeOutput + { + OutTradeNumber = request.OutTradeNumber, + QrcodeUrl = response.QrcodeUrl + }; + } + + /// + /// 微信支付统一下单获取Id(服务商模式) 🔖 + /// + [DisplayName("微信支付统一下单获取Id(服务商模式)")] + public async Task CreatePayPartnerTransaction([FromBody] WechatPayTransactionInput input) + { + string outTradeNumber = DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff") + (new Random()).Next(100, 1000); // 微信需要的订单号(唯一) + + //检查订单信息是否已存在(使用“商户交易单号+状态”唯一性判断) + var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OrderId == input.OrderId && u.OrderStatus == input.OrderStatus); + if (wechatPay != null) + { + outTradeNumber = wechatPay.OutTradeNumber; + } + + var request = new CreatePayPartnerTransactionJsapiRequest() + { + OutTradeNumber = outTradeNumber, + AppId = _wechatPayOptions.AppId, + MerchantId = _wechatPayOptions.MerchantId, + SubAppId = _wechatPayOptions.AppId, + SubMerchantId = _wechatPayOptions.MerchantId, + Description = input.Description, + Attachment = input.Attachment, + GoodsTag = input.GoodsTag, + ExpireTime = DateTimeOffset.Now.AddMinutes(10), + NotifyUrl = _payCallBackOptions.WechatPayUrl, + Amount = new CreatePayPartnerTransactionJsapiRequest.Types.Amount() { Total = input.Total }, + Payer = new CreatePayPartnerTransactionJsapiRequest.Types.Payer() { OpenId = input.OpenId } + }; + var response = await _wechatTenpayClient.ExecuteCreatePayPartnerTransactionJsapiAsync(request); + if (!response.IsSuccessful()) + throw Oops.Oh($"JSAPI 下单失败(状态码:{response.GetRawStatus()},错误代码:{response.ErrorCode},错误描述:{response.ErrorMessage})"); + if (wechatPay == null) + { + // 保存订单信息 + wechatPay = new SysWechatPay() + { + AppId = _wechatPayOptions.AppId, + MerchantId = _wechatPayOptions.MerchantId, + SubAppId = _wechatPayOptions.AppId, + SubMerchantId = _wechatPayOptions.MerchantId, + OutTradeNumber = request.OutTradeNumber, + Description = input.Description, + Attachment = input.Attachment, + GoodsTag = input.GoodsTag, + Total = input.Total, + OpenId = input.OpenId, + TransactionId = "" + }; + await _sysWechatPayRep.InsertAsync(wechatPay); + } + + return new CreatePayTransactionOutput + { + PrepayId = response.PrepayId, + OutTradeNumber = request.OutTradeNumber + }; + } + + /// + /// 获取支付订单详情 🔖 + /// + /// + /// + [DisplayName("获取支付订单详情")] + public async Task GetPayInfo(string tradeId) + { + return await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == tradeId); + } + + /// + /// 微信支付成功回调(商户直连) 🔖 + /// + /// + [AllowAnonymous] + [DisplayName("微信支付成功回调(商户直连)")] + public async Task PayCallBack() + { + using var ms = new MemoryStream(); + await App.HttpContext.Request.Body.CopyToAsync(ms); + var b = ms.ToArray(); + var callbackJson = Encoding.UTF8.GetString(b); + + var callbackModel = _wechatTenpayClient.DeserializeEvent(callbackJson); + if ("TRANSACTION.SUCCESS".Equals(callbackModel.EventType)) + { + var callbackResource = _wechatTenpayClient.DecryptEventResource(callbackModel); + + // 修改订单支付状态 + var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == callbackResource.OutTradeNumber + && u.MerchantId == callbackResource.MerchantId); + if (wechatPay == null) return null; + //wechatPay.OpenId = callbackResource.Payer.OpenId; // 支付者标识 + //wechatPay.MerchantId = callbackResource.MerchantId; // 微信商户号 + //wechatPay.OutTradeNumber = callbackResource.OutTradeNumber; // 商户订单号 + wechatPay.TransactionId = callbackResource.TransactionId; // 支付订单号 + wechatPay.TradeType = callbackResource.TradeType; // 交易类型 + wechatPay.TradeState = callbackResource.TradeState; // 交易状态 + wechatPay.TradeStateDescription = callbackResource.TradeStateDescription; // 交易状态描述 + wechatPay.BankType = callbackResource.BankType; // 付款银行类型 + wechatPay.Total = callbackResource.Amount.Total; // 订单总金额 + wechatPay.PayerTotal = callbackResource.Amount.PayerTotal; // 用户支付金额 + wechatPay.SuccessTime = callbackResource.SuccessTime; // 支付完成时间 + + await _sysWechatPayRep.AsUpdateable(wechatPay).IgnoreColumns(true).ExecuteCommandAsync(); + + return new WechatPayOutput() + { + Total = wechatPay.Total, + Attachment = wechatPay.Attachment, + GoodsTag = wechatPay.GoodsTag + }; + } + + return null; + } + + /// + /// 微信支付成功回调(服务商模式) 🔖 + /// + /// + [AllowAnonymous] + [DisplayName("微信支付成功回调(服务商模式)")] + public async Task PayPartnerCallBack() + { + using var ms = new MemoryStream(); + await App.HttpContext.Request.Body.CopyToAsync(ms); + var b = ms.ToArray(); + var callbackJson = Encoding.UTF8.GetString(b); + + var callbackModel = _wechatTenpayClient.DeserializeEvent(callbackJson); + if ("TRANSACTION.SUCCESS".Equals(callbackModel.EventType)) + { + var callbackResource = _wechatTenpayClient.DecryptEventResource(callbackModel); + + // 修改订单支付状态 + var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == callbackResource.OutTradeNumber + && u.MerchantId == callbackResource.MerchantId); + if (wechatPay == null) return; + //wechatPay.OpenId = callbackResource.Payer.OpenId; // 支付者标识 + //wechatPay.MerchantId = callbackResource.MerchantId; // 微信商户号 + //wechatPay.OutTradeNumber = callbackResource.OutTradeNumber; // 商户订单号 + wechatPay.TransactionId = callbackResource.TransactionId; // 支付订单号 + wechatPay.TradeType = callbackResource.TradeType; // 交易类型 + wechatPay.TradeState = callbackResource.TradeState; // 交易状态 + wechatPay.TradeStateDescription = callbackResource.TradeStateDescription; // 交易状态描述 + wechatPay.BankType = callbackResource.BankType; // 付款银行类型 + wechatPay.Total = callbackResource.Amount.Total; // 订单总金额 + wechatPay.PayerTotal = callbackResource.Amount.PayerTotal; // 用户支付金额 + wechatPay.SuccessTime = callbackResource.SuccessTime; // 支付完成时间 + + await _sysWechatPayRep.AsUpdateable(wechatPay).IgnoreColumns(true).ExecuteCommandAsync(); + } + } + + /// + /// 退款申请 🔖 + /// https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/create.html + /// + /// + /// + [DisplayName("微信退款申请)")] + public async Task Refund(RefundRequestInput input) + { + var request = new CreateRefundDomesticRefundRequest() + { + OutTradeNumber = input.OutTradeNumber, + OutRefundNumber = "REFUND_" + DateTimeOffset.Now.ToString("yyyyMMddHHmmssfff"), + Amount = new CreateRefundDomesticRefundRequest.Types.Amount() + { + Total = input.Total, + Refund = input.Refund + }, + Reason = input.Reason, + }; + var response = await _wechatTenpayClient.ExecuteCreateRefundDomesticRefundAsync(request); + + if (!response.IsSuccessful()) + throw Oops.Oh($"JSAPI 退款申请失败(状态码:{response.GetRawStatus()},错误代码:{response.ErrorCode},错误描述:{response.ErrorMessage})"); + + var wechatRefund = await _sysWechatRefundRep.GetFirstAsync(u => u.OutTradeNumber == input.OutTradeNumber); + if (wechatRefund == null) + { + // 保存退款申请信息 + wechatRefund = new SysWechatRefund() + { + TransactionId = input.OutTradeNumber, + OutTradeNumber = request.OutTradeNumber, + OutRefundNo = request.OutTradeNumber, // 每笔付款只退一次,所以这里直接用付款单号 + Reason = request.Reason, + Refund = input.Refund, + Total = input.Total, + //NotifyUrl = "", + OrderId = input.OrderId, + OrderStatus = input.OrderStatus, + MerchantGoodsId = input.MerchantGoodsId, + GoodsName = input.GoodsName, + UnitPrice = input.UnitPrice, + RefundAmount = input.RefundAmount, + RefundQuantity = input.RefundQuantity, + Attachment = input.Attachment, + Remark = input.Remark + }; + await _sysWechatRefundRep.InsertAsync(wechatRefund); + } + } + + /// + /// 查询单笔退款(通过商户退款单号) 🔖 + /// https://pay.weixin.qq.com/docs/merchant/apis/mini-program-payment/query-by-out-refund-no.html + /// + /// + /// + [DisplayName("微信查询单笔退款)")] + public async Task GetRefundByOutRefundNumber(string outRefundNumber) + { + var request = new GetRefundDomesticRefundByOutRefundNumberRequest() + { + OutRefundNumber = outRefundNumber + }; + return await _wechatTenpayClient.ExecuteGetRefundDomesticRefundByOutRefundNumberAsync(request); + } + + /// + /// 微信支付订单号查询(校正) 🔖 + /// https://api.mch.weixin.qq.com/v3/pay/transactions/id/{transaction_id} + /// + /// + /// + [DisplayName("微信支付订单号查询(校正)")] + public async Task GetPayTransactionByIdAsync(string transactionId) + { + if (string.IsNullOrEmpty(transactionId)) + throw Oops.Oh("TransactionId 不能为空"); + + if (string.IsNullOrEmpty(_wechatPayOptions.MerchantId) || string.IsNullOrEmpty(_wechatPayOptions.MerchantCertificateSerialNumber)) + throw Oops.Oh("商户号或证书序列号不能为空,请检查支付配置"); + + var request = new GetPayTransactionByIdRequest() + { + MerchantId = _wechatPayOptions.MerchantId, + TransactionId = transactionId, + WechatpaySerialNumber = _wechatPayOptions.MerchantCertificateSerialNumber + }; + var response = await _wechatTenpayClient.ExecuteGetPayTransactionByIdAsync(request); + if (response.TradeState == "SUCCESS" || response.TradeState == "CLOSED" || response.TradeState == "NOTPAY") + { + // 修正订单支付状态 + var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.TransactionId == request.TransactionId && u.MerchantId == request.MerchantId); + if (wechatPay != null && string.IsNullOrEmpty(wechatPay.TradeState)) + { + wechatPay.TradeType = response.TradeType; // 交易类型 + wechatPay.TradeState = response.TradeState; // 交易状态 wechatPay.TradeStateDescription = response.TradeStateDescription; // 交易状态描述 - wechatPay.OpenId = response.Payer?.OpenId;// 付款用户OpenId - wechatPay.BankType = response.BankType; // 付款银行类型 - wechatPay.PayerTotal = response.Amount?.PayerTotal; // 用户支付金额 - wechatPay.SuccessTime = response.SuccessTime; // 支付完成时间 - await _sysWechatPayRep.AsUpdateable(wechatPay).IgnoreColumns(true).ExecuteCommandAsync(); - return wechatPay.Adapt(); - } - } - return response.Adapt(); - } - - /// - /// 商户订单号查询(校正) 🔖 - /// https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no} - /// - /// - /// - [DisplayName("微信商户订单号查询(校正)")] - public async Task GetPayTransactionByOutTradeNumberAsync(string outTradeNumber) - { - if (string.IsNullOrEmpty(outTradeNumber)) - throw Oops.Oh("商户订单号(OutTradeNumber)不能为空"); - - if (string.IsNullOrEmpty(_wechatPayOptions.MerchantId) || string.IsNullOrEmpty(_wechatPayOptions.MerchantCertificateSerialNumber)) - throw Oops.Oh("商户号或证书序列号不能为空,请检查支付配置"); - - var request = new GetPayTransactionByOutTradeNumberRequest() - { - MerchantId = _wechatPayOptions.MerchantId, - OutTradeNumber = outTradeNumber, - WechatpaySerialNumber = _wechatPayOptions.MerchantCertificateSerialNumber, - }; - var response = await _wechatTenpayClient.ExecuteGetPayTransactionByOutTradeNumberAsync(request); - if (response.TradeState == "SUCCESS" || response.TradeState == "CLOSED" || response.TradeState == "NOTPAY") - { - // 修正订单支付状态 - var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == outTradeNumber && u.MerchantId == request.MerchantId); - if (wechatPay != null && string.IsNullOrEmpty(wechatPay.TradeState)) - { - wechatPay.TransactionId = response.TransactionId; // 支付订单号 - wechatPay.TradeType = response.TradeType; // 交易类型 - wechatPay.TradeState = response.TradeState; // 交易状态 + wechatPay.OpenId = response.Payer?.OpenId;// 付款用户OpenId + wechatPay.BankType = response.BankType; // 付款银行类型 + wechatPay.PayerTotal = response.Amount?.PayerTotal; // 用户支付金额 + wechatPay.SuccessTime = response.SuccessTime; // 支付完成时间 + await _sysWechatPayRep.AsUpdateable(wechatPay).IgnoreColumns(true).ExecuteCommandAsync(); + return wechatPay.Adapt(); + } + } + return response.Adapt(); + } + + /// + /// 商户订单号查询(校正) 🔖 + /// https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/{out_trade_no} + /// + /// + /// + [DisplayName("微信商户订单号查询(校正)")] + public async Task GetPayTransactionByOutTradeNumberAsync(string outTradeNumber) + { + if (string.IsNullOrEmpty(outTradeNumber)) + throw Oops.Oh("商户订单号(OutTradeNumber)不能为空"); + + if (string.IsNullOrEmpty(_wechatPayOptions.MerchantId) || string.IsNullOrEmpty(_wechatPayOptions.MerchantCertificateSerialNumber)) + throw Oops.Oh("商户号或证书序列号不能为空,请检查支付配置"); + + var request = new GetPayTransactionByOutTradeNumberRequest() + { + MerchantId = _wechatPayOptions.MerchantId, + OutTradeNumber = outTradeNumber, + WechatpaySerialNumber = _wechatPayOptions.MerchantCertificateSerialNumber, + }; + var response = await _wechatTenpayClient.ExecuteGetPayTransactionByOutTradeNumberAsync(request); + if (response.TradeState == "SUCCESS" || response.TradeState == "CLOSED" || response.TradeState == "NOTPAY") + { + // 修正订单支付状态 + var wechatPay = await _sysWechatPayRep.GetFirstAsync(u => u.OutTradeNumber == outTradeNumber && u.MerchantId == request.MerchantId); + if (wechatPay != null && string.IsNullOrEmpty(wechatPay.TradeState)) + { + wechatPay.TransactionId = response.TransactionId; // 支付订单号 + wechatPay.TradeType = response.TradeType; // 交易类型 + wechatPay.TradeState = response.TradeState; // 交易状态 wechatPay.TradeStateDescription = response.TradeStateDescription; // 交易状态描述 - wechatPay.OpenId = response.Payer?.OpenId;// 付款用户OpenId - wechatPay.BankType = response.BankType; // 付款银行类型 - wechatPay.PayerTotal = response.Amount?.PayerTotal; // 用户支付金额 - wechatPay.SuccessTime = response.SuccessTime; // 支付完成时间 - await _sysWechatPayRep.AsUpdateable(wechatPay).IgnoreColumns(true).ExecuteCommandAsync(); - return wechatPay.Adapt(); - } - } - return response.Adapt(); - } - - /// - /// 获取支付记录分页列表 🔖 - /// - /// PageSysWechatPayInput - /// - [DisplayName("获取支付记录分页列表")] - public async Task> PageAsync(PageSysWechatPayInput input) - { - var query = _sysWechatPayRep.AsQueryable() - .WhereIF(!string.IsNullOrWhiteSpace(input.OrderId), u => u.OrderId == input.OrderId) - .WhereIF(!string.IsNullOrWhiteSpace(input.OrderStatus), u => u.OrderStatus == input.OrderStatus) - .WhereIF(!string.IsNullOrWhiteSpace(input.OutTradeNumber), u => u.OutTradeNumber.Contains(input.OutTradeNumber.Trim())); - - if (input.SuccessTimeRange != null && input.SuccessTimeRange.Count > 0) - { - DateTime? start = input.SuccessTimeRange[0]; - query.WhereIF(start.HasValue, u => u.SuccessTime > start); - if (input.SuccessTimeRange.Count > 1 && input.SuccessTimeRange[1].HasValue) - { - var end = input.SuccessTimeRange[1].Value.AddDays(1); - query.Where(u => u.SuccessTime < end); - } - } - if (input.ExpireTimeRange != null && input.ExpireTimeRange.Count > 0) - { - DateTime? start = input.ExpireTimeRange[0]; - query.WhereIF(start.HasValue, u => u.ExpireTime > start); - if (input.ExpireTimeRange.Count > 1 && input.ExpireTimeRange[1].HasValue) - { - var end = input.ExpireTimeRange[1].Value.AddDays(1); - query.Where(u => u.ExpireTime < end); - } - } - query.OrderByDescending(u => u.CreateTime); - return await query.ToPagedListAsync(input.Page, input.PageSize); - } - - /// - /// 根据支付Id获取退款信息列表 - /// - /// - /// - [DisplayName("根据支付Id获取退款信息列表")] - public async Task> GetRefundList([FromQuery] string transactionId) - { - return await _sysWechatRefundRep.AsQueryable().Where(u => u.TransactionId == transactionId).ToListAsync(); - } + wechatPay.OpenId = response.Payer?.OpenId;// 付款用户OpenId + wechatPay.BankType = response.BankType; // 付款银行类型 + wechatPay.PayerTotal = response.Amount?.PayerTotal; // 用户支付金额 + wechatPay.SuccessTime = response.SuccessTime; // 支付完成时间 + await _sysWechatPayRep.AsUpdateable(wechatPay).IgnoreColumns(true).ExecuteCommandAsync(); + return wechatPay.Adapt(); + } + } + return response.Adapt(); + } + + /// + /// 获取支付记录分页列表 🔖 + /// + /// PageSysWechatPayInput + /// + [DisplayName("获取支付记录分页列表")] + public async Task> PageAsync(PageSysWechatPayInput input) + { + var query = _sysWechatPayRep.AsQueryable() + .WhereIF(!string.IsNullOrWhiteSpace(input.OrderId), u => u.OrderId == input.OrderId) + .WhereIF(!string.IsNullOrWhiteSpace(input.OrderStatus), u => u.OrderStatus == input.OrderStatus) + .WhereIF(!string.IsNullOrWhiteSpace(input.OutTradeNumber), u => u.OutTradeNumber.Contains(input.OutTradeNumber.Trim())); + + if (input.SuccessTimeRange != null && input.SuccessTimeRange.Count > 0) + { + DateTime? start = input.SuccessTimeRange[0]; + query.WhereIF(start.HasValue, u => u.SuccessTime > start); + if (input.SuccessTimeRange.Count > 1 && input.SuccessTimeRange[1].HasValue) + { + var end = input.SuccessTimeRange[1].Value.AddDays(1); + query.Where(u => u.SuccessTime < end); + } + } + if (input.ExpireTimeRange != null && input.ExpireTimeRange.Count > 0) + { + DateTime? start = input.ExpireTimeRange[0]; + query.WhereIF(start.HasValue, u => u.ExpireTime > start); + if (input.ExpireTimeRange.Count > 1 && input.ExpireTimeRange[1].HasValue) + { + var end = input.ExpireTimeRange[1].Value.AddDays(1); + query.Where(u => u.ExpireTime < end); + } + } + query.OrderByDescending(u => u.CreateTime); + return await query.ToPagedListAsync(input.Page, input.PageSize); + } + + /// + /// 根据支付Id获取退款信息列表 + /// + /// + /// + [DisplayName("根据支付Id获取退款信息列表")] + public async Task> GetRefundList([FromQuery] string transactionId) + { + return await _sysWechatRefundRep.AsQueryable().Where(u => u.TransactionId == transactionId).ToListAsync(); + } } \ No newline at end of file diff --git a/Admin.NET/Admin.NET.Core/Utils/CodeGenUtil.cs b/Admin.NET/Admin.NET.Core/Utils/CodeGenUtil.cs index 44cb8f7c..5b2fc195 100644 --- a/Admin.NET/Admin.NET.Core/Utils/CodeGenUtil.cs +++ b/Admin.NET/Admin.NET.Core/Utils/CodeGenUtil.cs @@ -68,6 +68,7 @@ public static class CodeGenUtil { return ConvertDataType_OracleSQL(dataType, length, scale); //达梦兼容Oracle,目前先这样实现 } + // OracleSQL数据类型对应的字段类型 public static string ConvertDataType_OracleSQL(string dataType, int? length, int? scale) {