From 3d67ad73e3e43acc97906ad82e46cffcf67cbaee Mon Sep 17 00:00:00 2001
From: sunpengfei <i@angelzzz.com>
Date: 星期三, 19 十一月 2025 09:43:10 +0800
Subject: [PATCH] feat:平安转账开发

---
 ApiTools.Core/Jobs/RefreshChannelWalletTransactionStatusJob.cs                             |   58 ++
 ApiTools.Web.Entry/Startup.cs                                                              |    2 
 ApiTools.Database.Migrations/Migrations/DefaultDbContextModelSnapshot.cs                   |  347 +++++++++++
 ApiTools.Core/ApiTools.Core.xml                                                            |   10 
 ApiTools.Database.Migrations/Migrations/20251119014221_UpdateChannelWallet1119.Designer.cs |  998 ++++++++++++++++++++++++++++++++++
 ApiTools.Database.Migrations/REDEME.MD                                                     |    2 
 ApiTools.Core/Utils/ScheduleUtils/DbJobPersistence.cs                                      |   88 --
 ApiTools.Database.Migrations/Migrations/20251119014221_UpdateChannelWallet1119.cs          |  185 ++++++
 8 files changed, 1,615 insertions(+), 75 deletions(-)

diff --git a/ApiTools.Core/ApiTools.Core.xml b/ApiTools.Core/ApiTools.Core.xml
index 534a73a..17bfcba 100644
--- a/ApiTools.Core/ApiTools.Core.xml
+++ b/ApiTools.Core/ApiTools.Core.xml
@@ -1973,6 +1973,16 @@
             缂栧彿
             </summary>
         </member>
+        <member name="T:ApiTools.Core.RefreshChannelWalletTransactionStatusJob">
+            <summary>
+            鍒锋柊娓犻亾閽卞寘浜ゆ槗鐘舵��
+            </summary>
+        </member>
+        <member name="M:ApiTools.Core.RefreshChannelWalletTransactionStatusJob.#ctor(ApiTools.Core.ChannelWalletRepository,ApiTools.Core.ChannelWalletTransactionRepository,ApiTools.Core.ChannelWalletService)">
+            <summary>
+            鍒锋柊娓犻亾閽卞寘浜ゆ槗鐘舵��
+            </summary>
+        </member>
         <member name="T:ApiTools.Core.ChangePhoneNumberCommand">
             <summary>
             鏇存崲鎵嬫満鍙�
diff --git a/ApiTools.Core/Jobs/RefreshChannelWalletTransactionStatusJob.cs b/ApiTools.Core/Jobs/RefreshChannelWalletTransactionStatusJob.cs
new file mode 100644
index 0000000..c92df86
--- /dev/null
+++ b/ApiTools.Core/Jobs/RefreshChannelWalletTransactionStatusJob.cs
@@ -0,0 +1,58 @@
+锘縰sing Furion;
+using Furion.DatabaseAccessor;
+using Furion.EventBus;
+using Furion.Schedule;
+using Microsoft.EntityFrameworkCore;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace ApiTools.Core
+{
+    /// <summary>
+    /// 鍒锋柊娓犻亾閽卞寘浜ゆ槗鐘舵��
+    /// </summary>
+    [JobDetail("RefreshChannelWalletTransactionStatusJob", Description = "鍒锋柊娓犻亾閽卞寘浜ゆ槗鐘舵��", Concurrent = false)]
+    [PeriodMinutes(5)]
+    public class RefreshChannelWalletTransactionStatusJob(
+            ChannelWalletRepository channelWalletRepository,
+            ChannelWalletTransactionRepository channelWalletTransactionRepository,
+            ChannelWalletService channelWalletService
+        ) : IJob
+    {
+        private readonly ChannelWalletRepository channelWalletRepository = channelWalletRepository;
+        private readonly ChannelWalletTransactionRepository channelWalletTransactionRepository = channelWalletTransactionRepository;
+        private readonly ChannelWalletService channelWalletService = channelWalletService;
+
+        public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
+        {
+            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 wallets = await channelWalletRepository.GetQueryable(false)
+                    .Where(it => walletIds.Contains(it.Id))
+                    .ToListAsync();
+                foreach (var transaction in transactions)
+                {
+                    var wallet = wallets.FirstOrDefault(it => it.Id == transaction.WalletId);
+                    if (wallet != null)
+                    {
+                        // 鏌ヨ浜ゆ槗璇︽儏
+                        await channelWalletService.GetTransactionDetail(wallet, transaction);
+                        // 涓嬭浇鍥炲崟
+                        await channelWalletService.DownloadEreceiptUrl(wallet, transaction);
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/ApiTools.Core/Utils/ScheduleUtils/DbJobPersistence.cs b/ApiTools.Core/Utils/ScheduleUtils/DbJobPersistence.cs
index b637fb8..7d8de22 100644
--- a/ApiTools.Core/Utils/ScheduleUtils/DbJobPersistence.cs
+++ b/ApiTools.Core/Utils/ScheduleUtils/DbJobPersistence.cs
@@ -16,21 +16,13 @@
     /// <summary>
     /// 浣滀笟鎸佷箙鍖栵紙鏁版嵁搴擄級
     /// </summary>
-    public class DbJobPersistence : IJobPersistence, IDisposable
+    public class DbJobPersistence : IJobPersistence
     {
-        private readonly IServiceScope _serviceScope;
-        private readonly IRepository<ScheduleJobDetail> repScheduleJobDetail;
-        private readonly IRepository<ScheduleJobTrigger> repScheduleJobTrigger;
-        private readonly IRepository<ScheduleJobTriggerTimeline, LogDbContextLocator> repScheduleJobTriggerTimeline;
+        private readonly IServiceScopeFactory serviceScopeFactory;
 
-        public DbJobPersistence(IServiceScopeFactory scopeFactory)
+        public DbJobPersistence(IServiceScopeFactory serviceScopeFactory)
         {
-            _serviceScope = scopeFactory.CreateScope();
-            var services = _serviceScope.ServiceProvider;
-
-            repScheduleJobDetail = services.GetService<IRepository<ScheduleJobDetail>>();
-            repScheduleJobTrigger = services.GetService<IRepository<ScheduleJobTrigger>>();
-            repScheduleJobTriggerTimeline = services.GetService<IRepository<ScheduleJobTriggerTimeline, LogDbContextLocator>>();
+            this.serviceScopeFactory = serviceScopeFactory;
         }
 
         /// <summary>
@@ -44,78 +36,28 @@
 
         public Task<SchedulerBuilder> OnLoadingAsync(SchedulerBuilder builder, CancellationToken stoppingToken)
         {
-            // 鏍囪浠庡叾浠栧湴鏂规洿鏂帮紝姣斿鏁版嵁搴�
             return Task.FromResult(builder);
         }
 
-        public async Task OnChangedAsync(PersistenceContext context)
+        public Task OnChangedAsync(PersistenceContext context)
         {
-            switch (context.Behavior)
-            {
-                case PersistenceBehavior.Appended:
-                    var insertEntity = new ScheduleJobDetail();
-                    context.JobDetail.Adapt(insertEntity);
-                    await repScheduleJobDetail.InsertNowAsync(insertEntity);
-                    break;
-                case PersistenceBehavior.Updated:
-                    var updateEntity = await repScheduleJobDetail.AsQueryable().FirstOrDefaultAsync(it => it.JobId == context.JobId);
-                    if (updateEntity != null)
-                    {
-                        context.JobDetail.Adapt(updateEntity);
-                        await repScheduleJobDetail.UpdateNowAsync(updateEntity);
-                    }
-                    break;
-                case PersistenceBehavior.Removed:
-                    var deleteEntity = await repScheduleJobDetail.AsQueryable().FirstOrDefaultAsync(it => it.JobId == context.JobId);
-                    if (deleteEntity != null)
-                    {
-                        await repScheduleJobDetail.DeleteNowAsync(deleteEntity);
-                    }
-                    break;
-                default:
-                    break;
-            }
+            return Task.CompletedTask;
         }
 
-        public async Task OnTriggerChangedAsync(PersistenceTriggerContext context)
+        public Task OnTriggerChangedAsync(PersistenceTriggerContext context)
         {
-            switch (context.Behavior)
-            {
-                case PersistenceBehavior.Appended:
-                    var insertEntity = new ScheduleJobTrigger();
-                    context.Trigger.Adapt(insertEntity);
-                    await repScheduleJobTrigger.InsertNowAsync(insertEntity);
-                    break;
-                case PersistenceBehavior.Updated:
-                    var updateEntity = await repScheduleJobTrigger.AsQueryable().FirstOrDefaultAsync(it => it.JobId == context.JobId && it.TriggerId == context.TriggerId);
-                    if (updateEntity != null)
-                    {
-                        context.Trigger.Adapt(updateEntity);
-                        await repScheduleJobTrigger.UpdateNowAsync(updateEntity);
-                    }
-                    break;
-                case PersistenceBehavior.Removed:
-                    var deleteEntity = await repScheduleJobTrigger.AsQueryable().FirstOrDefaultAsync(it => it.JobId == context.JobId && it.TriggerId == context.TriggerId);
-                    if (deleteEntity != null)
-                    {
-                        await repScheduleJobTrigger.DeleteNowAsync(deleteEntity);
-                    }
-                    break;
-                default:
-                    break;
-            }
+            return Task.CompletedTask;
         }
 
         public async Task OnExecutionRecordAsync(PersistenceExecutionRecordContext context)
         {
-            var entity = new ScheduleJobTriggerTimeline();
-            context.Timeline.Adapt(entity);
-            await repScheduleJobTriggerTimeline.InsertNowAsync(entity);
-        }
-
-        public void Dispose()
-        {
-            _serviceScope?.Dispose();
+            using (var scope = serviceScopeFactory.CreateScope())
+            {
+                var rep = scope.ServiceProvider.GetRequiredService<IRepository<ScheduleJobTriggerTimeline, LogDbContextLocator>>();
+                var entity = new ScheduleJobTriggerTimeline();
+                context.Timeline.Adapt(entity);
+                await rep.InsertNowAsync(entity);
+            }
         }
     }
 }
diff --git a/ApiTools.Database.Migrations/Migrations/20251119014221_UpdateChannelWallet1119.Designer.cs b/ApiTools.Database.Migrations/Migrations/20251119014221_UpdateChannelWallet1119.Designer.cs
new file mode 100644
index 0000000..827c8aa
--- /dev/null
+++ b/ApiTools.Database.Migrations/Migrations/20251119014221_UpdateChannelWallet1119.Designer.cs
@@ -0,0 +1,998 @@
+锘�// <auto-generated />
+using System;
+using ApiTools.EntityFramework.Core;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace ApiTools.Database.Migrations.Migrations
+{
+    [DbContext(typeof(DefaultDbContext))]
+    [Migration("20251119014221_UpdateChannelWallet1119")]
+    partial class UpdateChannelWallet1119
+    {
+        /// <inheritdoc />
+        protected override void BuildTargetModel(ModelBuilder modelBuilder)
+        {
+#pragma warning disable 612, 618
+            modelBuilder
+                .HasAnnotation("ProductVersion", "9.0.2")
+                .HasAnnotation("Relational:MaxIdentifierLength", 128);
+
+            SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
+
+            modelBuilder.Entity("ApiTools.Core.Channel", b =>
+                {
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("Code")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<Guid?>("CreatedChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<DateTimeOffset>("CreatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("CreatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<bool>("IsDeleted")
+                        .HasColumnType("bit");
+
+                    b.Property<bool>("IsDisabled")
+                        .HasColumnType("bit");
+
+                    b.Property<string>("Name")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int>("Sort")
+                        .HasColumnType("int");
+
+                    b.Property<string>("TraceId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<DateTimeOffset?>("UpdatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("UpdatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Channel");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.ChannelWallet", b =>
+                {
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<int>("Access")
+                        .HasColumnType("int");
+
+                    b.Property<decimal>("Balance")
+                        .HasColumnType("decimal(18,2)");
+
+                    b.Property<string>("Bank")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("BankBranch")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<Guid?>("ChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("Code")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<Guid?>("CreatedChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<DateTimeOffset>("CreatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("CreatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("ErrorCode")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("FailReason")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("Identity")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<bool>("IsDeleted")
+                        .HasColumnType("bit");
+
+                    b.Property<string>("Name")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("OutWalletId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int>("SignStatus")
+                        .HasColumnType("int");
+
+                    b.Property<int>("Sort")
+                        .HasColumnType("int");
+
+                    b.Property<string>("TraceId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<DateTimeOffset?>("UpdatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("UpdatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ChannelId");
+
+                    b.ToTable("ChannelWallet");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.ChannelWalletTransaction", b =>
+                {
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<decimal>("AfterBalance")
+                        .HasColumnType("decimal(18,2)");
+
+                    b.Property<decimal>("Amount")
+                        .HasColumnType("decimal(18,2)");
+
+                    b.Property<decimal>("Balance")
+                        .HasColumnType("decimal(18,2)");
+
+                    b.Property<string>("Code")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ConcurrencyLock")
+                        .HasColumnType("nvarchar(450)");
+
+                    b.Property<Guid?>("CreatedChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<DateTimeOffset>("CreatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("CreatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("Currency")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("EreceiptDownloadOssUrl")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("EreceiptDownloadUrl")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("EreceiptErrorMessage")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("EreceiptFileId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int?>("EreceiptStatus")
+                        .HasColumnType("int");
+
+                    b.Property<string>("ErrorCode")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("FailReason")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<bool>("IsDeleted")
+                        .HasColumnType("bit");
+
+                    b.Property<DateTime?>("OperatorTime")
+                        .HasColumnType("datetime2");
+
+                    b.Property<decimal?>("OrderFee")
+                        .HasColumnType("decimal(18,2)");
+
+                    b.Property<string>("OutCode")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("OutOperatorId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("OutReceiveId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("PayerAccount")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("PayerBank")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("PayerBankBranch")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("PayerName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("Purpose")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ReceiveAccount")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ReceiveBank")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ReceiveBankBranch")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ReceiveIdentity")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ReceiveName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("Remark")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int>("Sort")
+                        .HasColumnType("int");
+
+                    b.Property<string>("TraceId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<DateTime?>("TransDate")
+                        .HasColumnType("datetime2");
+
+                    b.Property<int>("TransactionStatus")
+                        .HasColumnType("int");
+
+                    b.Property<int>("Type")
+                        .HasColumnType("int");
+
+                    b.Property<DateTimeOffset?>("UpdatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("UpdatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<Guid>("WalletId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ConcurrencyLock")
+                        .IsUnique()
+                        .HasFilter("[ConcurrencyLock] IS NOT NULL");
+
+                    b.HasIndex("WalletId");
+
+                    b.ToTable("ChannelWalletTransaction");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.ChannelWalletTransactionPingAnPay", b =>
+                {
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("AccountDate")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("BackRem")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<Guid?>("CreatedChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<DateTimeOffset>("CreatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("CreatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("CstInnerFlowNo")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("Fee")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("FreezeNo")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("FrontLogNo")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("HostErrorCode")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("HostFlowNo")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("IsBack")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<bool>("IsDeleted")
+                        .HasColumnType("bit");
+
+                    b.Property<string>("ProxyPayAcc")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ProxyPayBankName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ProxyPayName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("RemoveStopFailReason")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("RemoveStopStt")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int>("Sort")
+                        .HasColumnType("int");
+
+                    b.Property<string>("StopFailReason")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("StopStt")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("SubmitTime")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("SysFlag")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ThirdVoucher")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("TraceId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("TransBsn")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<DateTimeOffset?>("UpdatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("UpdatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("Yhcljg")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("ChannelWalletTransactionPingAnPay");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.Resource", b =>
+                {
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("ActionName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ActionSummary")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<bool>("AllowAnonymous")
+                        .HasColumnType("bit");
+
+                    b.Property<string>("ApplicationName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("Code")
+                        .IsRequired()
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ControllerName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ControllerSummary")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<Guid?>("CreatedChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<DateTimeOffset>("CreatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("CreatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<bool>("CustomResponse")
+                        .HasColumnType("bit");
+
+                    b.Property<string>("DynamicAssemblyName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<bool>("FileUpload")
+                        .HasColumnType("bit");
+
+                    b.Property<bool>("IsDeleted")
+                        .HasColumnType("bit");
+
+                    b.Property<bool>("IsExpired")
+                        .HasColumnType("bit");
+
+                    b.Property<bool>("IsFromForm")
+                        .HasColumnType("bit");
+
+                    b.Property<int>("Method")
+                        .HasColumnType("int");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("RequestTypeFullName")
+                        .IsRequired()
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("RequestTypeName")
+                        .IsRequired()
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ResponseTypeFullName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ResponseTypeName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("Route")
+                        .IsRequired()
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("RouteArea")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ServiceName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int>("Sort")
+                        .HasColumnType("int");
+
+                    b.Property<string>("TraceId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<DateTimeOffset?>("UpdatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("UpdatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("Resource");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.ScheduleJobDetail", b =>
+                {
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("AssemblyName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<bool>("Concurrent")
+                        .HasColumnType("bit");
+
+                    b.Property<Guid?>("CreatedChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<DateTimeOffset>("CreatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("CreatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("Description")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("GroupName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<bool>("IncludeAnnotations")
+                        .HasColumnType("bit");
+
+                    b.Property<bool>("IsDeleted")
+                        .HasColumnType("bit");
+
+                    b.Property<string>("JobId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("JobType")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("Properties")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int>("Sort")
+                        .HasColumnType("int");
+
+                    b.Property<string>("TraceId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<DateTimeOffset?>("UpdatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("UpdatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("ScheduleJobDetail");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.ScheduleJobTrigger", b =>
+                {
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("Args")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("AssemblyName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<Guid?>("CreatedChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<DateTimeOffset>("CreatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("CreatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("Description")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<long>("ElapsedTime")
+                        .HasColumnType("bigint");
+
+                    b.Property<DateTime?>("EndTime")
+                        .HasColumnType("datetime2");
+
+                    b.Property<bool>("IsDeleted")
+                        .HasColumnType("bit");
+
+                    b.Property<string>("JobId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<DateTime?>("LastRunTime")
+                        .HasColumnType("datetime2");
+
+                    b.Property<long>("MaxNumberOfErrors")
+                        .HasColumnType("bigint");
+
+                    b.Property<long>("MaxNumberOfRuns")
+                        .HasColumnType("bigint");
+
+                    b.Property<DateTime?>("NextRunTime")
+                        .HasColumnType("datetime2");
+
+                    b.Property<long>("NumRetries")
+                        .HasColumnType("bigint");
+
+                    b.Property<long>("NumberOfErrors")
+                        .HasColumnType("bigint");
+
+                    b.Property<long>("NumberOfRuns")
+                        .HasColumnType("bigint");
+
+                    b.Property<bool>("ResetOnlyOnce")
+                        .HasColumnType("bit");
+
+                    b.Property<string>("Result")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int>("RetryTimeout")
+                        .HasColumnType("int");
+
+                    b.Property<bool>("RunOnStart")
+                        .HasColumnType("bit");
+
+                    b.Property<int>("Sort")
+                        .HasColumnType("int");
+
+                    b.Property<bool>("StartNow")
+                        .HasColumnType("bit");
+
+                    b.Property<DateTime?>("StartTime")
+                        .HasColumnType("datetime2");
+
+                    b.Property<long>("Status")
+                        .HasColumnType("bigint");
+
+                    b.Property<string>("TraceId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("TriggerId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("TriggerType")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<DateTimeOffset?>("UpdatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("UpdatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("ScheduleJobTrigger");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.SmsLog", b =>
+                {
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<int>("Access")
+                        .HasColumnType("int");
+
+                    b.Property<Guid?>("ChannelCreatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<Guid?>("ChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("Code")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<Guid?>("CreatedChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<DateTimeOffset>("CreatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("CreatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<DateTime?>("Expiry")
+                        .HasColumnType("datetime2");
+
+                    b.Property<bool>("IsDeleted")
+                        .HasColumnType("bit");
+
+                    b.Property<bool>("IsUsed")
+                        .HasColumnType("bit");
+
+                    b.Property<string>("Message")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("PhoneNumber")
+                        .IsRequired()
+                        .HasMaxLength(11)
+                        .HasColumnType("nvarchar(11)");
+
+                    b.Property<string>("RequestId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int>("Sort")
+                        .HasColumnType("int");
+
+                    b.Property<int>("Status")
+                        .HasColumnType("int");
+
+                    b.Property<string>("TemplateCode")
+                        .IsRequired()
+                        .HasMaxLength(128)
+                        .HasColumnType("nvarchar(128)");
+
+                    b.Property<string>("TemplateParam")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("TraceId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<DateTimeOffset?>("UpdatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("UpdatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ChannelId");
+
+                    b.ToTable("SmsLog");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.SmsSetting", b =>
+                {
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<Guid?>("ChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<Guid?>("CreatedChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<DateTimeOffset>("CreatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("CreatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<int>("DailyMaxCount")
+                        .HasColumnType("int");
+
+                    b.Property<int>("HourlyMaxCount")
+                        .HasColumnType("int");
+
+                    b.Property<bool>("IsDeleted")
+                        .HasColumnType("bit");
+
+                    b.Property<bool>("IsDisabled")
+                        .HasColumnType("bit");
+
+                    b.Property<int>("MinutelyMaxCount")
+                        .HasColumnType("int");
+
+                    b.Property<int>("Sort")
+                        .HasColumnType("int");
+
+                    b.Property<string>("TraceId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<DateTimeOffset?>("UpdatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("UpdatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<bool>("WithoutParams")
+                        .HasColumnType("bit");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ChannelId");
+
+                    b.ToTable("SmsSetting");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.SmsSettingAccess", b =>
+                {
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<int>("Access")
+                        .HasColumnType("int");
+
+                    b.Property<Guid?>("CreatedChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<DateTimeOffset>("CreatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("CreatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<bool>("IsDeleted")
+                        .HasColumnType("bit");
+
+                    b.Property<bool>("IsDisabled")
+                        .HasColumnType("bit");
+
+                    b.Property<Guid>("SettingId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("SignName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int>("Sort")
+                        .HasColumnType("int");
+
+                    b.Property<string>("TraceId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<DateTimeOffset?>("UpdatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("UpdatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("SettingId");
+
+                    b.ToTable("SmsSettingAccess");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.User", b =>
+                {
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("Avatar")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<Guid?>("ChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<Guid?>("CreatedChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<DateTimeOffset>("CreatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("CreatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<bool>("IsCheckPhoneNumber")
+                        .HasColumnType("bit");
+
+                    b.Property<bool>("IsDeleted")
+                        .HasColumnType("bit");
+
+                    b.Property<int>("Level")
+                        .HasColumnType("int");
+
+                    b.Property<string>("Name")
+                        .HasMaxLength(32)
+                        .HasColumnType("nvarchar(32)");
+
+                    b.Property<string>("Password")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("PhoneNumber")
+                        .HasMaxLength(11)
+                        .HasColumnType("nvarchar(11)");
+
+                    b.Property<string>("Remark")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int>("Sort")
+                        .HasColumnType("int");
+
+                    b.Property<int>("Status")
+                        .HasColumnType("int");
+
+                    b.Property<string>("TraceId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int>("Type")
+                        .HasColumnType("int");
+
+                    b.Property<DateTimeOffset?>("UpdatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("UpdatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("UserName")
+                        .IsRequired()
+                        .HasMaxLength(32)
+                        .HasColumnType("nvarchar(32)");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ChannelId");
+
+                    b.ToTable("User");
+
+                    b.HasData(
+                        new
+                        {
+                            Id = new Guid("11111111-1111-1111-1111-111111111111"),
+                            CreatedTime = new DateTimeOffset(new DateTime(2000, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 8, 0, 0, 0)),
+                            IsCheckPhoneNumber = false,
+                            IsDeleted = false,
+                            Level = 999,
+                            Name = "绠$悊鍛�",
+                            Password = "iEYggKrMhQ3ASUGLobra1w==:fn/DsMJUbD9FGpvBvR3moMpMPptdxzZlourPVhU479I=",
+                            Sort = 0,
+                            Status = 10,
+                            Type = 100,
+                            UserName = "system"
+                        });
+                });
+
+            modelBuilder.Entity("ApiTools.Core.ChannelWallet", b =>
+                {
+                    b.HasOne("ApiTools.Core.Channel", "Channel")
+                        .WithMany()
+                        .HasForeignKey("ChannelId");
+
+                    b.Navigation("Channel");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.ChannelWalletTransaction", b =>
+                {
+                    b.HasOne("ApiTools.Core.ChannelWallet", "Wallet")
+                        .WithMany()
+                        .HasForeignKey("WalletId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Wallet");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.ChannelWalletTransactionPingAnPay", b =>
+                {
+                    b.HasOne("ApiTools.Core.ChannelWalletTransaction", "Transaction")
+                        .WithOne("PingAnPay")
+                        .HasForeignKey("ApiTools.Core.ChannelWalletTransactionPingAnPay", "Id")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Transaction");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.SmsLog", b =>
+                {
+                    b.HasOne("ApiTools.Core.Channel", "Channel")
+                        .WithMany()
+                        .HasForeignKey("ChannelId");
+
+                    b.Navigation("Channel");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.SmsSetting", b =>
+                {
+                    b.HasOne("ApiTools.Core.Channel", "Channel")
+                        .WithMany()
+                        .HasForeignKey("ChannelId");
+
+                    b.Navigation("Channel");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.SmsSettingAccess", b =>
+                {
+                    b.HasOne("ApiTools.Core.SmsSetting", "Setting")
+                        .WithMany("Accesses")
+                        .HasForeignKey("SettingId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Setting");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.User", b =>
+                {
+                    b.HasOne("ApiTools.Core.Channel", "Channel")
+                        .WithMany()
+                        .HasForeignKey("ChannelId");
+
+                    b.Navigation("Channel");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.ChannelWalletTransaction", b =>
+                {
+                    b.Navigation("PingAnPay");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.SmsSetting", b =>
+                {
+                    b.Navigation("Accesses");
+                });
+#pragma warning restore 612, 618
+        }
+    }
+}
diff --git a/ApiTools.Database.Migrations/Migrations/20251119014221_UpdateChannelWallet1119.cs b/ApiTools.Database.Migrations/Migrations/20251119014221_UpdateChannelWallet1119.cs
new file mode 100644
index 0000000..baef442
--- /dev/null
+++ b/ApiTools.Database.Migrations/Migrations/20251119014221_UpdateChannelWallet1119.cs
@@ -0,0 +1,185 @@
+锘縰sing System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace ApiTools.Database.Migrations.Migrations
+{
+    /// <inheritdoc />
+    public partial class UpdateChannelWallet1119 : Migration
+    {
+        /// <inheritdoc />
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.CreateTable(
+                name: "ChannelWallet",
+                columns: table => new
+                {
+                    Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
+                    ChannelId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
+                    OutWalletId = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    Access = table.Column<int>(type: "int", nullable: false),
+                    Code = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    Bank = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    BankBranch = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    Name = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    Identity = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    Balance = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
+                    SignStatus = table.Column<int>(type: "int", nullable: false),
+                    ErrorCode = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    FailReason = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    Sort = table.Column<int>(type: "int", nullable: false),
+                    TraceId = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    CreatedTime = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
+                    CreatedUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
+                    CreatedChannelId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
+                    UpdatedTime = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
+                    UpdatedUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
+                    IsDeleted = table.Column<bool>(type: "bit", nullable: false)
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_ChannelWallet", x => x.Id);
+                    table.ForeignKey(
+                        name: "FK_ChannelWallet_Channel_ChannelId",
+                        column: x => x.ChannelId,
+                        principalTable: "Channel",
+                        principalColumn: "Id");
+                });
+
+            migrationBuilder.CreateTable(
+                name: "ChannelWalletTransaction",
+                columns: table => new
+                {
+                    Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
+                    WalletId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
+                    Type = table.Column<int>(type: "int", nullable: false),
+                    Code = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    OutCode = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    ConcurrencyLock = table.Column<string>(type: "nvarchar(450)", nullable: true),
+                    Amount = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
+                    Balance = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
+                    AfterBalance = table.Column<decimal>(type: "decimal(18,2)", nullable: false),
+                    OutOperatorId = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    OperatorTime = table.Column<DateTime>(type: "datetime2", nullable: true),
+                    PayerAccount = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    PayerName = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    PayerBank = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    PayerBankBranch = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    OutReceiveId = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    ReceiveName = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    ReceiveIdentity = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    ReceiveAccount = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    ReceiveBank = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    ReceiveBankBranch = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    Currency = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    Purpose = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    Remark = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    ErrorCode = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    FailReason = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    TransDate = table.Column<DateTime>(type: "datetime2", nullable: true),
+                    OrderFee = table.Column<decimal>(type: "decimal(18,2)", nullable: true),
+                    EreceiptFileId = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    EreceiptDownloadUrl = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    EreceiptDownloadOssUrl = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    EreceiptStatus = table.Column<int>(type: "int", nullable: true),
+                    EreceiptErrorMessage = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    TransactionStatus = table.Column<int>(type: "int", nullable: false),
+                    Sort = table.Column<int>(type: "int", nullable: false),
+                    TraceId = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    CreatedTime = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
+                    CreatedUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
+                    CreatedChannelId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
+                    UpdatedTime = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
+                    UpdatedUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
+                    IsDeleted = table.Column<bool>(type: "bit", nullable: false)
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_ChannelWalletTransaction", x => x.Id);
+                    table.ForeignKey(
+                        name: "FK_ChannelWalletTransaction_ChannelWallet_WalletId",
+                        column: x => x.WalletId,
+                        principalTable: "ChannelWallet",
+                        principalColumn: "Id",
+                        onDelete: ReferentialAction.Cascade);
+                });
+
+            migrationBuilder.CreateTable(
+                name: "ChannelWalletTransactionPingAnPay",
+                columns: table => new
+                {
+                    Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
+                    FreezeNo = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    StopStt = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    StopFailReason = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    RemoveStopStt = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    RemoveStopFailReason = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    ThirdVoucher = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    FrontLogNo = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    CstInnerFlowNo = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    IsBack = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    BackRem = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    Yhcljg = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    SysFlag = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    Fee = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    TransBsn = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    SubmitTime = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    AccountDate = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    HostFlowNo = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    HostErrorCode = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    ProxyPayName = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    ProxyPayAcc = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    ProxyPayBankName = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    Sort = table.Column<int>(type: "int", nullable: false),
+                    TraceId = table.Column<string>(type: "nvarchar(max)", nullable: true),
+                    CreatedTime = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: false),
+                    CreatedUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
+                    CreatedChannelId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
+                    UpdatedTime = table.Column<DateTimeOffset>(type: "datetimeoffset", nullable: true),
+                    UpdatedUserId = table.Column<Guid>(type: "uniqueidentifier", nullable: true),
+                    IsDeleted = table.Column<bool>(type: "bit", nullable: false)
+                },
+                constraints: table =>
+                {
+                    table.PrimaryKey("PK_ChannelWalletTransactionPingAnPay", x => x.Id);
+                    table.ForeignKey(
+                        name: "FK_ChannelWalletTransactionPingAnPay_ChannelWalletTransaction_Id",
+                        column: x => x.Id,
+                        principalTable: "ChannelWalletTransaction",
+                        principalColumn: "Id",
+                        onDelete: ReferentialAction.Cascade);
+                });
+
+            migrationBuilder.CreateIndex(
+                name: "IX_ChannelWallet_ChannelId",
+                table: "ChannelWallet",
+                column: "ChannelId");
+
+            migrationBuilder.CreateIndex(
+                name: "IX_ChannelWalletTransaction_ConcurrencyLock",
+                table: "ChannelWalletTransaction",
+                column: "ConcurrencyLock",
+                unique: true,
+                filter: "[ConcurrencyLock] IS NOT NULL");
+
+            migrationBuilder.CreateIndex(
+                name: "IX_ChannelWalletTransaction_WalletId",
+                table: "ChannelWalletTransaction",
+                column: "WalletId");
+        }
+
+        /// <inheritdoc />
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.DropTable(
+                name: "ChannelWalletTransactionPingAnPay");
+
+            migrationBuilder.DropTable(
+                name: "ChannelWalletTransaction");
+
+            migrationBuilder.DropTable(
+                name: "ChannelWallet");
+        }
+    }
+}
diff --git a/ApiTools.Database.Migrations/Migrations/DefaultDbContextModelSnapshot.cs b/ApiTools.Database.Migrations/Migrations/DefaultDbContextModelSnapshot.cs
index 09924a7..de7c9c6 100644
--- a/ApiTools.Database.Migrations/Migrations/DefaultDbContextModelSnapshot.cs
+++ b/ApiTools.Database.Migrations/Migrations/DefaultDbContextModelSnapshot.cs
@@ -66,6 +66,317 @@
                     b.ToTable("Channel");
                 });
 
+            modelBuilder.Entity("ApiTools.Core.ChannelWallet", b =>
+                {
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<int>("Access")
+                        .HasColumnType("int");
+
+                    b.Property<decimal>("Balance")
+                        .HasColumnType("decimal(18,2)");
+
+                    b.Property<string>("Bank")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("BankBranch")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<Guid?>("ChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("Code")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<Guid?>("CreatedChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<DateTimeOffset>("CreatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("CreatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("ErrorCode")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("FailReason")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("Identity")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<bool>("IsDeleted")
+                        .HasColumnType("bit");
+
+                    b.Property<string>("Name")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("OutWalletId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int>("SignStatus")
+                        .HasColumnType("int");
+
+                    b.Property<int>("Sort")
+                        .HasColumnType("int");
+
+                    b.Property<string>("TraceId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<DateTimeOffset?>("UpdatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("UpdatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ChannelId");
+
+                    b.ToTable("ChannelWallet");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.ChannelWalletTransaction", b =>
+                {
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<decimal>("AfterBalance")
+                        .HasColumnType("decimal(18,2)");
+
+                    b.Property<decimal>("Amount")
+                        .HasColumnType("decimal(18,2)");
+
+                    b.Property<decimal>("Balance")
+                        .HasColumnType("decimal(18,2)");
+
+                    b.Property<string>("Code")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ConcurrencyLock")
+                        .HasColumnType("nvarchar(450)");
+
+                    b.Property<Guid?>("CreatedChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<DateTimeOffset>("CreatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("CreatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("Currency")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("EreceiptDownloadOssUrl")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("EreceiptDownloadUrl")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("EreceiptErrorMessage")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("EreceiptFileId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int?>("EreceiptStatus")
+                        .HasColumnType("int");
+
+                    b.Property<string>("ErrorCode")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("FailReason")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<bool>("IsDeleted")
+                        .HasColumnType("bit");
+
+                    b.Property<DateTime?>("OperatorTime")
+                        .HasColumnType("datetime2");
+
+                    b.Property<decimal?>("OrderFee")
+                        .HasColumnType("decimal(18,2)");
+
+                    b.Property<string>("OutCode")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("OutOperatorId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("OutReceiveId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("PayerAccount")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("PayerBank")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("PayerBankBranch")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("PayerName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("Purpose")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ReceiveAccount")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ReceiveBank")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ReceiveBankBranch")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ReceiveIdentity")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ReceiveName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("Remark")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int>("Sort")
+                        .HasColumnType("int");
+
+                    b.Property<string>("TraceId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<DateTime?>("TransDate")
+                        .HasColumnType("datetime2");
+
+                    b.Property<int>("TransactionStatus")
+                        .HasColumnType("int");
+
+                    b.Property<int>("Type")
+                        .HasColumnType("int");
+
+                    b.Property<DateTimeOffset?>("UpdatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("UpdatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<Guid>("WalletId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.HasKey("Id");
+
+                    b.HasIndex("ConcurrencyLock")
+                        .IsUnique()
+                        .HasFilter("[ConcurrencyLock] IS NOT NULL");
+
+                    b.HasIndex("WalletId");
+
+                    b.ToTable("ChannelWalletTransaction");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.ChannelWalletTransactionPingAnPay", b =>
+                {
+                    b.Property<Guid>("Id")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("AccountDate")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("BackRem")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<Guid?>("CreatedChannelId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<DateTimeOffset>("CreatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("CreatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("CstInnerFlowNo")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("Fee")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("FreezeNo")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("FrontLogNo")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("HostErrorCode")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("HostFlowNo")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("IsBack")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<bool>("IsDeleted")
+                        .HasColumnType("bit");
+
+                    b.Property<string>("ProxyPayAcc")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ProxyPayBankName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ProxyPayName")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("RemoveStopFailReason")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("RemoveStopStt")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<int>("Sort")
+                        .HasColumnType("int");
+
+                    b.Property<string>("StopFailReason")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("StopStt")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("SubmitTime")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("SysFlag")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("ThirdVoucher")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("TraceId")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<string>("TransBsn")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.Property<DateTimeOffset?>("UpdatedTime")
+                        .HasColumnType("datetimeoffset");
+
+                    b.Property<Guid?>("UpdatedUserId")
+                        .HasColumnType("uniqueidentifier");
+
+                    b.Property<string>("Yhcljg")
+                        .HasColumnType("nvarchar(max)");
+
+                    b.HasKey("Id");
+
+                    b.ToTable("ChannelWalletTransactionPingAnPay");
+                });
+
             modelBuilder.Entity("ApiTools.Core.Resource", b =>
                 {
                     b.Property<Guid>("Id")
@@ -600,6 +911,37 @@
                         });
                 });
 
+            modelBuilder.Entity("ApiTools.Core.ChannelWallet", b =>
+                {
+                    b.HasOne("ApiTools.Core.Channel", "Channel")
+                        .WithMany()
+                        .HasForeignKey("ChannelId");
+
+                    b.Navigation("Channel");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.ChannelWalletTransaction", b =>
+                {
+                    b.HasOne("ApiTools.Core.ChannelWallet", "Wallet")
+                        .WithMany()
+                        .HasForeignKey("WalletId")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Wallet");
+                });
+
+            modelBuilder.Entity("ApiTools.Core.ChannelWalletTransactionPingAnPay", b =>
+                {
+                    b.HasOne("ApiTools.Core.ChannelWalletTransaction", "Transaction")
+                        .WithOne("PingAnPay")
+                        .HasForeignKey("ApiTools.Core.ChannelWalletTransactionPingAnPay", "Id")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Transaction");
+                });
+
             modelBuilder.Entity("ApiTools.Core.SmsLog", b =>
                 {
                     b.HasOne("ApiTools.Core.Channel", "Channel")
@@ -638,6 +980,11 @@
                     b.Navigation("Channel");
                 });
 
+            modelBuilder.Entity("ApiTools.Core.ChannelWalletTransaction", b =>
+                {
+                    b.Navigation("PingAnPay");
+                });
+
             modelBuilder.Entity("ApiTools.Core.SmsSetting", b =>
                 {
                     b.Navigation("Accesses");
diff --git a/ApiTools.Database.Migrations/REDEME.MD b/ApiTools.Database.Migrations/REDEME.MD
index c9a0fa9..1f6848e 100644
--- a/ApiTools.Database.Migrations/REDEME.MD
+++ b/ApiTools.Database.Migrations/REDEME.MD
@@ -1,7 +1,7 @@
 -------------------------------涓绘暟鎹簱---------------------------------------
 
 鏂板杩佺Щ鏂囦欢
-dotnet ef migrations add UpdateSmsSetting1009 -s "../ApiTools.Web.Entry" -c DefaultDbContext
+dotnet ef migrations add UpdateChannelWallet1119 -s "../ApiTools.Web.Entry" -c DefaultDbContext
 
 鍒犻櫎杩佺Щ鏂囦欢
 dotnet ef migrations remove -s "../ApiTools.Web.Entry" -c DefaultDbContext
diff --git a/ApiTools.Web.Entry/Startup.cs b/ApiTools.Web.Entry/Startup.cs
index 9c78c1b..0a4ec9c 100644
--- a/ApiTools.Web.Entry/Startup.cs
+++ b/ApiTools.Web.Entry/Startup.cs
@@ -60,7 +60,7 @@
             {
                 options.BuildSqlType = SqlTypes.SqlServer;
                 options.JobDetail.LogEnabled = true;
-                //options.AddPersistence<DbJobPersistence>();
+                options.AddPersistence<DbJobPersistence>();
             });
 
             services.AddSpecificationDocuments(options =>

--
Gitblit v1.9.1