| 2025-11-20 | sunpengfei | ![]() |
| 2025-11-20 | sunpengfei | ![]() |
| 2025-11-20 | lijin | ![]() |
| 2025-11-20 | lijin | ![]() |
| 2025-11-20 | sunpengfei | ![]() |
| 2025-11-20 | sunpengfei | ![]() |
| 2025-11-20 | lijin | ![]() |
| 2025-11-20 | lijin | ![]() |
| 2025-11-20 | lijin | ![]() |
ApiTools.Application/ApiTools.Application.xml
@@ -191,6 +191,14 @@ <param name="cancellationToken"></param> <returns></returns> </member> <member name="M:ApiTools.Application.ChannelWalletQueryHandler.Handle(ApiTools.Core.GetChannelWalletTransactionsQuery,System.Threading.CancellationToken)"> <summary> 查询渠道钱包交易详情 </summary> <param name="request"></param> <param name="cancellationToken"></param> <returns></returns> </member> <member name="M:ApiTools.Application.SendSmsCommandHandler.Handle(ApiTools.Core.SendSmsCommand,System.Threading.CancellationToken)"> <summary> 发送短信 @@ -279,5 +287,21 @@ <param name="cancellationToken"></param> <returns></returns> </member> <member name="M:ApiTools.CommonServer.Application.SaveWxCommandHandler.Handle(ApiTools.Core.SaveWxSendCommand,System.Threading.CancellationToken)"> <summary> 该接口用于发送订阅消息 </summary> <param name="request"></param> <param name="cancellationToken"></param> <returns></returns> </member> <member name="M:ApiTools.CommonServer.Application.SaveWxSettingCommandHandler.Handle(ApiTools.Core.SaveWxSettingCommand,System.Threading.CancellationToken)"> <summary> 保存微信配置 </summary> <param name="request"></param> <param name="cancellationToken"></param> <returns></returns> </member> </members> </doc> ApiTools.Application/ChannelWallets/Commands/ChannelWalletCommandHandler.cs
@@ -90,37 +90,76 @@ await channelWalletService.GetEnterpriseWalletBalance(wallet); if (request.Amount > wallet.Balance) throw Oops.Oh(EnumErrorCodeType.s404, "余额不足"); var checkExist = await channelWalletTransactionRepository.GetQueryable() .AnyAsync(it => it.WalletId == wallet.Id && it.OutCode == request.OutCode); if (checkExist) throw Oops.Oh(EnumErrorCodeType.s405, "交易单号"); var transaction = new ChannelWalletTransaction(); transaction.Type = EnumWalletTransactionType.Transfer; transaction.WalletId = wallet.Id; transaction.OutCode = request.OutCode; transaction.ConcurrencyLock = $"{logier.ChannelId}:{transaction.OutCode}"; transaction.Amount = request.Amount; transaction.Remark = request.Remark; transaction.TransactionStatus = EnumWalletTransactionStatus.WaitSubmit; transaction.Balance = wallet.Balance; transaction.AfterBalance = wallet.Balance - transaction.Amount; transaction.OutOperatorId = request.OutOperatorId; transaction.OperatorTime = request.OperatorTime; transaction.PayerName = wallet.Name; transaction.PayerAccount = wallet.Identity; transaction.PayerBank = wallet.Bank; transaction.PayerBankBranch = wallet.BankBranch; transaction.OutReceiveId = request.OutReceiveId; transaction.ReceiveBank = request.ReceiveBank; transaction.ReceiveBankBranch = request.ReceiveBankBranch; transaction.ReceiveName = request.ReceiveName; transaction.ReceiveIdentity = request.ReceiveIdentity; transaction.ReceiveAccount = request.ReceiveAccount; transaction.Currency = request.Currency; transaction.Purpose = request.Purpose; transaction.Remark = request.Remark; await channelWalletTransactionRepository.SetCode(transaction); await channelWalletTransactionRepository.InsertNowAsync(transaction); var transaction = await channelWalletTransactionRepository.GetQueryable() .Where(it => it.WalletId == wallet.Id && it.OutCode == request.OutCode) .FirstOrDefaultAsync(); if (transaction == null) { transaction = new ChannelWalletTransaction(); transaction.Type = EnumWalletTransactionType.Transfer; transaction.WalletId = wallet.Id; transaction.OutCode = request.OutCode; transaction.ConcurrencyLock = $"{logier.ChannelId}:{transaction.OutCode}"; transaction.TransactionStatus = EnumWalletTransactionStatus.WaitSubmit; transaction.Amount = request.Amount; transaction.Balance = wallet.Balance; transaction.AfterBalance = wallet.Balance - transaction.Amount; transaction.OutOperatorId = request.OutOperatorId; transaction.OperatorTime = request.OperatorTime; transaction.PayerName = wallet.Name; transaction.PayerAccount = wallet.Identity; transaction.PayerBank = wallet.Bank; transaction.PayerBankBranch = wallet.BankBranch; transaction.OutReceiveId = request.OutReceiveId; transaction.ReceiveBank = request.ReceiveBank; transaction.ReceiveBankBranch = request.ReceiveBankBranch; transaction.ReceiveName = request.ReceiveName; transaction.ReceiveIdentity = request.ReceiveIdentity; transaction.ReceiveAccount = request.ReceiveAccount; transaction.Currency = request.Currency; transaction.Purpose = request.Purpose; transaction.Remark = request.Remark; await channelWalletTransactionRepository.SetCode(transaction); await channelWalletTransactionRepository.InsertNowAsync(transaction); } else { switch (transaction.TransactionStatus) { case EnumWalletTransactionStatus.WaitSubmit: throw Oops.Oh(EnumErrorCodeType.s510, "已存在正在处理的转账,请勿重复操作"); case EnumWalletTransactionStatus.WaitPay: case EnumWalletTransactionStatus.Dealing: throw Oops.Oh(EnumErrorCodeType.s510, "转账正在进行中,请勿重复操作"); case EnumWalletTransactionStatus.Success: throw Oops.Oh(EnumErrorCodeType.s510, "已转账,请勿重复操作"); case EnumWalletTransactionStatus.Refund: case EnumWalletTransactionStatus.Fail: transaction.TransactionStatus = EnumWalletTransactionStatus.WaitSubmit; transaction.Amount = request.Amount; transaction.Balance = wallet.Balance; transaction.AfterBalance = wallet.Balance - transaction.Amount; transaction.OutOperatorId = request.OutOperatorId; transaction.OperatorTime = request.OperatorTime; transaction.PayerName = wallet.Name; transaction.PayerAccount = wallet.Identity; transaction.PayerBank = wallet.Bank; transaction.PayerBankBranch = wallet.BankBranch; transaction.OutReceiveId = request.OutReceiveId; transaction.ReceiveBank = request.ReceiveBank; transaction.ReceiveBankBranch = request.ReceiveBankBranch; transaction.ReceiveName = request.ReceiveName; transaction.ReceiveIdentity = request.ReceiveIdentity; transaction.ReceiveAccount = request.ReceiveAccount; transaction.Currency = request.Currency; transaction.Purpose = request.Purpose; transaction.Remark = request.Remark; await channelWalletTransactionRepository.UpdateNowAsync(transaction); break; default: break; } } await channelWalletService.Transfer(wallet, transaction); return new SubmitChannelWalletTransferCommandResult { ApiTools.Application/ChannelWallets/Queries/ChannelWalletQueryHandler.cs
@@ -18,7 +18,8 @@ ChannelWalletTransactionRepository channelWalletTransactionRepository ) : IRequestHandler<GetChannelPingAnPayWalletQuery, GetChannelPingAnPayWalletQueryResult>, IRequestHandler<GetChannelWalletTransactionQuery, GetChannelWalletTransactionQueryResult> IRequestHandler<GetChannelWalletTransactionQuery, GetChannelWalletTransactionQueryResult>, IRequestHandler<GetChannelWalletTransactionsQuery, GetChannelWalletTransactionsQueryResult> { private readonly ChannelWalletRepository channelWalletRepository = channelWalletRepository; private readonly ChannelWalletTransactionRepository channelWalletTransactionRepository = channelWalletTransactionRepository; @@ -51,5 +52,28 @@ .FirstOrDefaultAsync(); } /// <summary> /// 查询渠道钱包交易详情 /// </summary> /// <param name="request"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public async Task<GetChannelWalletTransactionsQueryResult> Handle(GetChannelWalletTransactionsQuery request, CancellationToken cancellationToken) { var list = new GetChannelWalletTransactionsQueryResult(); list.Items = await channelWalletTransactionRepository.GetQueryable() .Where(it => request.OutCodes.Contains(it.OutCode)) .ProjectToType<GetChannelWalletTransactionsQueryResultItem>() .ToListAsync(); foreach (var item in list.Items) { if (item.EreceiptStatus == EnumWalletTransactionEreceiptStatus.SUCCESS && item.EreceiptDownloadOssUrl.IsNotNull()) { item.EreceiptDownloadOssFullUrl = AliyunOSSUtils.GetUrl(item.EreceiptDownloadOssUrl); } } return list; } } } ApiTools.Application/WxUtils/Commands/SaveWxCommandHandler.cs
New file @@ -0,0 +1,36 @@ using ApiTools.Core; using Baidu.Aip; using MediatR; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ApiTools.CommonServer.Application { public class SaveWxCommandHandler(WxmpUtils utils) : IRequestHandler<SaveWxSendCommand, string> { private readonly WxmpUtils utils = utils; /// <summary> /// 该接口用于发送订阅消息 /// </summary> /// <param name="request"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public async Task<string> Handle(SaveWxSendCommand request, CancellationToken cancellationToken) { return await utils.WxSend(new SendRequest { WxmpCode = request.WxmpCode, EnvVersion = request.EnvVersion, template_id = request.template_id, page = request.page, touser = request.touser, data = request.data, miniprogram_state = request.miniprogram_state, lang = request.lang }); } } } ApiTools.Application/WxUtils/Commands/SaveWxSettingCommandHandler.cs
New file @@ -0,0 +1,53 @@ using ApiTools.Core; using Furion.DatabaseAccessor; using Mapster; using MediatR; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ApiTools.CommonServer.Application { public class SaveWxSettingCommandHandler(IRepository<WxSetting> rep) :IRequestHandler<SaveWxSettingCommand, Guid> { private readonly IRepository<WxSetting> rep = rep; /// <summary> /// 保存微信配置 /// </summary> /// <param name="request"></param> /// <param name="cancellationToken"></param> /// <returns></returns> public async Task<Guid> Handle(SaveWxSettingCommand request, CancellationToken cancellationToken) { var logier = JwtUtils.GetCurrentLogier(); var entity = await rep.AsQueryable() .Where(it => it.Code == request.Code) .FirstOrDefaultAsync(); var add = false; if (entity == null) { entity = new WxSetting(); entity.Code = request.Code; entity.AppId = request.AppId; entity.AppSecret = request.AppSecret; entity.EnvVersion = request.EnvVersion; add = true; } request.Adapt(entity); if (add) { await rep.InsertAsync(entity); } else { await rep.UpdateAsync(entity); } return entity.Id; //return Guid.NewGuid(); } } } ApiTools.Application/WxUtils/Queries/GetWxSettingQueryHandler.cs
New file @@ -0,0 +1,34 @@ using ApiTools.Core; using Furion.DatabaseAccessor; using MediatR; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ApiTools.CommonServer.Application { //public class GetWxSettingQueryHandler(IRepository<SmsSetting> rep, IRepository<SmsLog> repSmsLog) : IRequestHandler<GetSmsSettingQuery, GetSmsSettingQueryResult> //{ // private readonly IRepository<SmsSetting> rep = rep; // private readonly IRepository<SmsLog> repSmsLog = repSmsLog; // /// <summary> // /// 查询短信配置 // /// </summary> // /// <param name="request"></param> // /// <param name="cancellationToken"></param> // /// <returns></returns> // public async Task<GetSmsSettingQueryResult> Handle(GetSmsSettingQuery request, CancellationToken cancellationToken) // { // var logier = JwtUtils.GetCurrentLogier(); // var detail = await rep.AsQueryable().AsNoTracking() // .Where(it => it.ChannelId == logier.ChannelId) // .GetDetail<SmsSetting, GetSmsSettingQueryResult>(); // detail.Accesses = detail.Accesses.OrderBy(it => it.Sort).ToList(); // return detail; // } //} } ApiTools.Core/ApiTools.Core.csproj
@@ -39,4 +39,8 @@ </None> </ItemGroup> <ItemGroup> <Folder Include="Models\WxmpUtils\Queries\" /> </ItemGroup> </Project> ApiTools.Core/ApiTools.Core.xml
@@ -1280,6 +1280,31 @@ 状态 </summary> </member> <member name="T:ApiTools.Core.WxSetting"> <summary> 微信配置 </summary> </member> <member name="P:ApiTools.Core.WxSetting.Code"> <summary> 编号 </summary> </member> <member name="P:ApiTools.Core.WxSetting.AppId"> <summary> 小程序ID </summary> </member> <member name="P:ApiTools.Core.WxSetting.AppSecret"> <summary> 小程序密钥 </summary> </member> <member name="P:ApiTools.Core.WxSetting.EnvVersion"> <summary> 版本 </summary> </member> <member name="F:ApiTools.Core.EnumErrorCodeType.s400"> <summary> 参数错误 @@ -1498,6 +1523,11 @@ <member name="F:ApiTools.Core.EnumResourceController.CommonServerSmsUtils"> <summary> 短信工具 </summary> </member> <member name="F:ApiTools.Core.EnumResourceController.CommonServerWxUtils"> <summary> 微信工具 </summary> </member> <member name="F:ApiTools.Core.EnumResourceController.UserServerAuth"> @@ -2418,6 +2448,11 @@ 余额 </summary> </member> <member name="P:ApiTools.Core.GetChannelPingAnPayWalletQueryResult.SignStatus"> <summary> 签约状态 </summary> </member> <member name="T:ApiTools.Core.GetChannelWalletTransactionQuery"> <summary> 查询渠道钱包交易详情 @@ -2594,6 +2629,201 @@ </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionQueryResult.TransactionStatus"> <summary> 状态 </summary> </member> <member name="T:ApiTools.Core.GetChannelWalletTransactionsQuery"> <summary> 查询渠道钱包交易详情 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQuery.OutCodes"> <summary> 外部订单号 </summary> </member> <member name="T:ApiTools.Core.GetChannelWalletTransactionsQueryResult"> <summary> 查询渠道钱包交易详情 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResult.Items"> <summary> 项 </summary> </member> <member name="T:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem"> <summary> 查询渠道钱包交易详情 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.Id"> <summary> 交易Id </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.Type"> <summary> 类型 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.Code"> <summary> 订单号 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.OutCode"> <summary> 外部订单号 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.ConcurrencyLock"> <summary> 并发锁 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.Amount"> <summary> 金额 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.Balance"> <summary> 余额 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.AfterBalance"> <summary> 收支后余额 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.OutOperatorId"> <summary> 外部操作人Id </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.OperatorTime"> <summary> 操作时间 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.PayerAccount"> <summary> 付款人账户 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.PayerName"> <summary> 付款人名称 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.PayerBank"> <summary> 付款人开户行 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.PayerBankBranch"> <summary> 付款人支行 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.OutReceiveId"> <summary> 外部收款人Id </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.ReceiveName"> <summary> 收款人姓名 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.ReceiveIdentity"> <summary> 收款人身份证号 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.ReceiveAccount"> <summary> 收款账户 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.ReceiveBank"> <summary> 收款人开户行 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.ReceiveBankBranch"> <summary> 收款人支行 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.Currency"> <summary> 币种 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.Purpose"> <summary> 用途 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.Remark"> <summary> 备注 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.ErrorCode"> <summary> 查询到的订单状态为FAIL失败或REFUND退票时,返回错误代码 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.FailReason"> <summary> 查询到的订单状态为FAIL失败或REFUND退票时,返回具体的原因。 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.TransDate"> <summary> 订单支付时间 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.OrderFee"> <summary> 预计收费金额(元),转账到银行卡专用 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.EreceiptFileId"> <summary> 电子收据Id </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.EreceiptDownloadUrl"> <summary> 电子收据下载链接 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.EreceiptDownloadOssUrl"> <summary> 电子收据下载链接 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.EreceiptDownloadOssFullUrl"> <summary> 电子收据下载链接 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.EreceiptStatus"> <summary> 电子收据状态 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.EreceiptErrorMessage"> <summary> 电子收据错误信息 </summary> </member> <member name="P:ApiTools.Core.GetChannelWalletTransactionsQueryResultItem.TransactionStatus"> <summary> 状态 </summary> @@ -3036,6 +3266,66 @@ <member name="P:ApiTools.Core.GetSmsSettingQueryResultAccess.SignName"> <summary> 签名名称 </summary> </member> <member name="T:ApiTools.Core.SaveWxSendCommand"> <summary> 该接口用于发送订阅消息 </summary> </member> <member name="P:ApiTools.Core.SaveWxSendCommand.WxmpCode"> <summary> 小程序编号 </summary> </member> <member name="P:ApiTools.Core.SaveWxSendCommand.EnvVersion"> <summary> 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop"。默认是正式版。 </summary> </member> <member name="P:ApiTools.Core.SaveWxSendCommand.template_id"> <summary> 所需下发的订阅模板id </summary> </member> <member name="P:ApiTools.Core.SaveWxSendCommand.page"> <summary> 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转 </summary> </member> <member name="P:ApiTools.Core.SaveWxSendCommand.touser"> <summary> 接收者(用户)的 openid </summary> </member> <member name="P:ApiTools.Core.SaveWxSendCommand.data"> <summary> 模板内容,格式形如{ "phrase3": { "value": "审核通过" }, "name1": { "value": "订阅" }, "date2": { "value": "2019-12-25 09:42" } } </summary> </member> <member name="T:ApiTools.Core.SaveWxSettingCommand"> <summary> 保存微信配置 </summary> </member> <member name="P:ApiTools.Core.SaveWxSettingCommand.Code"> <summary> 编号 </summary> </member> <member name="P:ApiTools.Core.SaveWxSettingCommand.AppId"> <summary> 小程序ID </summary> </member> <member name="P:ApiTools.Core.SaveWxSettingCommand.AppSecret"> <summary> 小程序密钥 </summary> </member> <member name="P:ApiTools.Core.SaveWxSettingCommand.EnvVersion"> <summary> 版本 </summary> </member> <member name="M:ApiTools.Core.BaseRepository`2.GetQueryable(System.Boolean)"> @@ -6700,6 +6990,46 @@ 错误信息 </summary> </member> <member name="T:ApiTools.Core.SendRequest"> <summary> 发送订阅消息 </summary> </member> <member name="P:ApiTools.Core.SendRequest.WxmpCode"> <summary> 小程序编号 </summary> </member> <member name="P:ApiTools.Core.SendRequest.EnvVersion"> <summary> 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop"。默认是正式版。 </summary> </member> <member name="P:ApiTools.Core.SendRequest.template_id"> <summary> 所需下发的订阅模板id </summary> </member> <member name="P:ApiTools.Core.SendRequest.page"> <summary> 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转 </summary> </member> <member name="P:ApiTools.Core.SendRequest.touser"> <summary> 接收者(用户)的 openid </summary> </member> <member name="P:ApiTools.Core.SendRequest.data"> <summary> 模板内容,格式形如{ "phrase3": { "value": "审核通过" }, "name1": { "value": "订阅" }, "date2": { "value": "2019-12-25 09:42" } } </summary> </member> <member name="T:ApiTools.Core.SendResponse"> <summary> 发送订阅消息-结果 </summary> </member> <member name="T:ApiTools.Core.WxmpOptions"> <summary> 微信小程序配置 @@ -6744,6 +7074,13 @@ <param name="command"></param> <returns></returns> </member> <member name="M:ApiTools.Core.WxmpUtils.WxSend(ApiTools.Core.SendRequest)"> <summary> 该接口用于发送订阅消息 </summary> <param name="command"></param> <returns></returns> </member> <member name="T:ApiTools.Core.XmlDoc"> <summary> 注释文档 ApiTools.Core/Entities/WxmpUtils/WxSetting.cs
New file @@ -0,0 +1,36 @@ using Furion.DatabaseAccessor; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ApiTools.Core { /// <summary> /// 微信配置 /// </summary> public class WxSetting : CommonEntity<MasterDbContextLocator> { /// <summary> /// 编号 /// </summary> public string Code { get; set; } /// <summary> /// 小程序ID /// </summary> public string AppId { get; set; } /// <summary> /// 小程序密钥 /// </summary> public string AppSecret { get; set; } /// <summary> /// 版本 /// </summary> public string EnvVersion { get; set; } } } ApiTools.Core/Enums/Resources/EnumResourceController.cs
@@ -33,6 +33,12 @@ CommonServerSmsUtils, /// <summary> /// 微信工具 /// </summary> [ResourceController(EnumResourceService.CommonServer, "Wx")] CommonServerWxUtils, /// <summary> /// 用户认证 /// </summary> [ResourceController(EnumResourceService.UserServer, "Auth")] ApiTools.Core/Jobs/RefreshChannelWalletTransactionStatusJob.cs
@@ -33,11 +33,15 @@ if (env != "Local") { var transactions = await channelWalletTransactionRepository.GetQueryable(false) .Include(it => it.PingAnPay) .Where(it => it.TransactionStatus == EnumWalletTransactionStatus.WaitPay || it.TransactionStatus == EnumWalletTransactionStatus.Dealing) || it.TransactionStatus == EnumWalletTransactionStatus.Dealing || it.TransactionStatus == EnumWalletTransactionStatus.Success && it.EreceiptStatus != EnumWalletTransactionEreceiptStatus.SUCCESS && it.EreceiptStatus != EnumWalletTransactionEreceiptStatus.FAIL) .ToListAsync(); var walletIds = transactions.DistinctSelect(it => it.WalletId); var walletIds = transactions.Select(it => it.WalletId).Distinct().ToList(); var wallets = await channelWalletRepository.GetQueryable(false) .Where(it => walletIds.Contains(it.Id)) .ToListAsync(); @@ -46,8 +50,12 @@ var wallet = wallets.FirstOrDefault(it => it.Id == transaction.WalletId); if (wallet != null) { // 查询交易详情 await channelWalletService.GetTransactionDetail(wallet, transaction); if (transaction.TransactionStatus == EnumWalletTransactionStatus.WaitPay || transaction.TransactionStatus == EnumWalletTransactionStatus.Dealing) { // 查询交易详情 await channelWalletService.GetTransactionDetail(wallet, transaction); } // 下载回单 await channelWalletService.DownloadEreceiptUrl(wallet, transaction); } ApiTools.Core/Models/ChannelWallets/Queries/GetChannelWalletTransactionsQuery.cs
New file @@ -0,0 +1,208 @@ using MediatR; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ApiTools.Core { /// <summary> /// 查询渠道钱包交易详情 /// </summary> [Resource([EnumResourceController.UserServerChannelWallet], Method = EnumResourceMethod.Post)] public class GetChannelWalletTransactionsQuery : IRequest<GetChannelWalletTransactionsQueryResult> { /// <summary> /// 外部订单号 /// </summary> public List<string> OutCodes { get; set; } = []; } /// <summary> /// 查询渠道钱包交易详情 /// </summary> public class GetChannelWalletTransactionsQueryResult { /// <summary> /// 项 /// </summary> public List<GetChannelWalletTransactionsQueryResultItem> Items { get; set; } = []; } /// <summary> /// 查询渠道钱包交易详情 /// </summary> public class GetChannelWalletTransactionsQueryResultItem { /// <summary> /// 交易Id /// </summary> public Guid Id { get; set; } /// <summary> /// 类型 /// </summary> public EnumWalletTransactionType Type { get; set; } /// <summary> /// 订单号 /// </summary> public string Code { get; set; } /// <summary> /// 外部订单号 /// </summary> public string OutCode { get; set; } /// <summary> /// 并发锁 /// </summary> public string ConcurrencyLock { get; set; } /// <summary> /// 金额 /// </summary> public decimal Amount { get; set; } /// <summary> /// 余额 /// </summary> public decimal Balance { get; set; } /// <summary> /// 收支后余额 /// </summary> public decimal AfterBalance { get; set; } /// <summary> /// 外部操作人Id /// </summary> public string OutOperatorId { get; set; } /// <summary> /// 操作时间 /// </summary> public DateTime? OperatorTime { get; set; } /// <summary> /// 付款人账户 /// </summary> public string PayerAccount { get; set; } /// <summary> /// 付款人名称 /// </summary> public string PayerName { get; set; } /// <summary> /// 付款人开户行 /// </summary> public string PayerBank { get; set; } /// <summary> /// 付款人支行 /// </summary> public string PayerBankBranch { get; set; } /// <summary> /// 外部收款人Id /// </summary> public string OutReceiveId { get; set; } /// <summary> /// 收款人姓名 /// </summary> public string ReceiveName { get; set; } /// <summary> /// 收款人身份证号 /// </summary> public string ReceiveIdentity { get; set; } /// <summary> /// 收款账户 /// </summary> public string ReceiveAccount { get; set; } /// <summary> /// 收款人开户行 /// </summary> public string ReceiveBank { get; set; } /// <summary> /// 收款人支行 /// </summary> public string ReceiveBankBranch { get; set; } /// <summary> /// 币种 /// </summary> public string Currency { get; set; } /// <summary> /// 用途 /// </summary> public string Purpose { get; set; } /// <summary> /// 备注 /// </summary> public string Remark { get; set; } /// <summary> /// 查询到的订单状态为FAIL失败或REFUND退票时,返回错误代码 /// </summary> public string ErrorCode { get; set; } /// <summary> /// 查询到的订单状态为FAIL失败或REFUND退票时,返回具体的原因。 /// </summary> public string FailReason { get; set; } /// <summary> /// 订单支付时间 /// </summary> public DateTime? TransDate { get; set; } /// <summary> /// 预计收费金额(元),转账到银行卡专用 /// </summary> public decimal? OrderFee { get; set; } /// <summary> /// 电子收据Id /// </summary> public string EreceiptFileId { get; set; } /// <summary> /// 电子收据下载链接 /// </summary> public string EreceiptDownloadUrl { get; set; } /// <summary> /// 电子收据下载链接 /// </summary> public string EreceiptDownloadOssUrl { get; set; } /// <summary> /// 电子收据下载链接 /// </summary> public string EreceiptDownloadOssFullUrl { get; set; } /// <summary> /// 电子收据状态 /// </summary> public EnumWalletTransactionEreceiptStatus? EreceiptStatus { get; set; } /// <summary> /// 电子收据错误信息 /// </summary> public string EreceiptErrorMessage { get; set; } /// <summary> /// 状态 /// </summary> public EnumWalletTransactionStatus TransactionStatus { get; set; } } } ApiTools.Core/Models/WxmpUtils/Commands/SaveWxSendCommand.cs
New file @@ -0,0 +1,51 @@ using MediatR; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ApiTools.Core { /// <summary> /// 该接口用于发送订阅消息 /// </summary> [Resource([EnumResourceController.CommonServerWxUtils], Method = EnumResourceMethod.Post)] public class SaveWxSendCommand : IRequest<string> { /// <summary> /// 小程序编号 /// </summary> public string WxmpCode { get; set; } /// <summary> /// 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop"。默认是正式版。 /// </summary> public string EnvVersion { get; set; } /// <summary> /// 所需下发的订阅模板id /// </summary> public string template_id { get; set; } /// <summary> /// 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转 /// </summary> public string page { get; set; } /// <summary> /// 接收者(用户)的 openid /// </summary> public string touser { get; set; } /// <summary> /// 模板内容,格式形如{ "phrase3": { "value": "审核通过" }, "name1": { "value": "订阅" }, "date2": { "value": "2019-12-25 09:42" } } /// </summary> public object data { get; set; } public string miniprogram_state { get; set; } public string lang { get; set; } } } ApiTools.Core/Models/WxmpUtils/Commands/SaveWxSettingCommand.cs
New file @@ -0,0 +1,36 @@ using MediatR; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ApiTools.Core { /// <summary> /// 保存微信配置 /// </summary> [Resource([EnumResourceController.CommonServerWxUtils], Method = EnumResourceMethod.Post)] public class SaveWxSettingCommand : IRequest<Guid> { /// <summary> /// 编号 /// </summary> public string Code { get; set; } /// <summary> /// 小程序ID /// </summary> public string AppId { get; set; } /// <summary> /// 小程序密钥 /// </summary> public string AppSecret { get; set; } /// <summary> /// 版本 /// </summary> public string EnvVersion { get; set; } } } ApiTools.Core/Utils/DbUtils/DbUtils.cs
@@ -10,6 +10,7 @@ using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -604,7 +605,12 @@ ? p.IsModified : true) .ToDictionary(p => p.Metadata.Name, p => p.OriginalValue)); Db.GetRepository<DbAuditLog, LogDbContextLocator>().InsertNow(log); // 创建独立作用域 using (var scope = App.GetRequiredService<IServiceScopeFactory>().CreateScope()) { var rep = scope.ServiceProvider.GetRequiredService<IRepository<DbAuditLog, LogDbContextLocator>>(); rep.InsertNow(log); } } } } ApiTools.Core/Utils/WxmpUtils/WxmpGetQrCodeRequest.cs
@@ -106,4 +106,73 @@ public string ErrorMessage { get; set; } } /// <summary> /// 发送订阅消息 /// </summary> public class SendRequest { /// <summary> /// 小程序编号 /// </summary> [JsonProperty("WxmpCode")] [Required] public string WxmpCode { get; set; } /// <summary> /// 要打开的小程序版本。正式版为 "release",体验版为 "trial",开发版为 "develop"。默认是正式版。 /// </summary> [JsonProperty("env_version")] public string EnvVersion { get; set; } /// <summary> /// 所需下发的订阅模板id /// </summary> [JsonProperty("template_id")] [Required] public string template_id { get; set; } /// <summary> /// 点击模板卡片后的跳转页面,仅限本小程序内的页面。支持带参数,(示例index?foo=bar)。该字段不填则模板无跳转 /// </summary> [JsonProperty("page")] [Required] public string page { get; set; } /// <summary> /// 接收者(用户)的 openid /// </summary> [JsonProperty("touser")] [Required] public string touser { get; set; } /// <summary> /// 模板内容,格式形如{ "phrase3": { "value": "审核通过" }, "name1": { "value": "订阅" }, "date2": { "value": "2019-12-25 09:42" } } /// </summary> [JsonProperty("data")] [Required] public object data { get; set; } [JsonProperty("跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版")] [Required] public string miniprogram_state { get; set; } [JsonProperty("进入小程序查看”的语言类型,支持zh_CN(简体中文)、en_US(英文)、zh_HK(繁体中文)、zh_TW(繁体中文),默认为zh_CN")] [Required] public string lang { get; set; } } /// <summary> /// 发送订阅消息-结果 /// </summary> public class SendResponse { [JsonProperty("ErrorCode")] public int ErrorCode { get; set; } [JsonProperty("errmsg")] public int errmsg { get; set; } } } ApiTools.Core/Utils/WxmpUtils/WxmpUtils.cs
@@ -1,6 +1,7 @@ using Aliyun.OSS; using Azure.Core; using Aop.Api.Domain; using ApiTools.Core.Utils.WxmpUtils; using Azure.Core; using Furion.FriendlyException; using Furion.HttpRemote; using Mapster; @@ -124,5 +125,42 @@ var result = AliyunOSSUtils.Upload(command.OssScene, stream, command.OssFileName); return result.Url; } /// <summary> /// 该接口用于发送订阅消息 /// </summary> /// <param name="command"></param> /// <returns></returns> public async Task<string> WxSend(SendRequest command) { var option = options.Value.Items.FirstOrDefault(it => it.Code == command.WxmpCode); if (option == null || option.AppId.IsNull() || option.AppSecret.IsNull()) throw Oops.Oh(EnumErrorCodeType.s400, "获取小程序码失败,缺失配置:WxmpOptions"); command.EnvVersion = option.EnvVersion; var accessToken = await GetAccessToken(command.WxmpCode); var request = command.Adapt<SendRequest>(); var jsonContent = JsonConvert.SerializeObject(request, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); var response = await httpRemoteService.PostAsync("https://api.weixin.qq.com/cgi-bin/message/subscribe/send", builder => builder .WithQueryParameter("access_token", accessToken) .SetJsonContent(jsonContent)); response.EnsureSuccessStatusCode(); if (response.Content.Headers.ContentType.ToString() == "application/json; charset=UTF-8") { var jsonResult = await response.Content.ReadAsStringAsync(); var callback = jsonResult.JsonTo<SendResponse>(); if (callback == null || callback.ErrorCode != 0) //throw Oops.Oh(EnumErrorCodeType.s510, $"获取小程序码失败:{callback.errmsg},请联系管理员"); return jsonResult; } //var stream = await response.Content.ReadAsStreamAsync(); //var result = AliyunOSSUtils.Upload(command, stream, command.OssFileName); return ""; } } }