sunpengfei
2025-11-20 a47038826eb7d1c152a50e95b3c9be177a130e8d
feat:开发平安转账
5个文件已修改
1个文件已添加
553 ■■■■■ 已修改文件
ApiTools.Application/ApiTools.Application.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ApiTools.Application/ChannelWallets/Commands/ChannelWalletCommandHandler.cs 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ApiTools.Application/ChannelWallets/Queries/ChannelWalletQueryHandler.cs 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ApiTools.Core/ApiTools.Core.xml 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ApiTools.Core/Jobs/RefreshChannelWalletTransactionStatusJob.cs 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ApiTools.Core/Models/ChannelWallets/Queries/GetChannelWalletTransactionsQuery.cs 208 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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>
            发送短信
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.Core/ApiTools.Core.xml
@@ -2418,6 +2418,11 @@
            余额
            </summary>
        </member>
        <member name="P:ApiTools.Core.GetChannelPingAnPayWalletQueryResult.SignStatus">
            <summary>
            签约状态
            </summary>
        </member>
        <member name="T:ApiTools.Core.GetChannelWalletTransactionQuery">
            <summary>
            查询渠道钱包交易详情
@@ -2598,6 +2603,201 @@
            状态
            </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>
        </member>
        <member name="T:ApiTools.Core.CheckHealthQuery">
            <summary>
            健康校验
ApiTools.Core/Jobs/RefreshChannelWalletTransactionStatusJob.cs
@@ -29,15 +29,15 @@
        public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
        {
            var env = App.GetConfig<string>("Environment");
            if (env != "Local")
            {
            //var env = App.GetConfig<string>("Environment");
            //if (env != "Local")
            //{
                var transactions = await channelWalletTransactionRepository.GetQueryable(false)
                .Where(it =>
                    it.TransactionStatus == EnumWalletTransactionStatus.WaitPay
                    || it.TransactionStatus == EnumWalletTransactionStatus.Dealing)
                .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();
@@ -52,7 +52,7 @@
                        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; }
    }
}