diff --git a/.github/workflows/dotnet-publish-prerelease.yml b/.github/workflows/dotnet-publish-prerelease.yml index e5d24d2..9398914 100644 --- a/.github/workflows/dotnet-publish-prerelease.yml +++ b/.github/workflows/dotnet-publish-prerelease.yml @@ -1,4 +1,4 @@ -name: .NET +name: Build Pre-Release on: push: @@ -17,11 +17,16 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: 6.0.x - - name: Publish for Linux-x64 - run: dotnet publish -c Release -r linux-x64 --self-contained -p:PublishTrimmed=true -p:PublishSingleFile=true -p:PublishReadyToRun=true -o app/linux-x64 - - name: Publish for Windows-x64 - run: dotnet publish -c Release -r win-x64 --self-contained -p:PublishTrimmed=true -p:PublishSingleFile=true -p:PublishReadyToRun=true -o app/win-x64 + dotnet-version: 8.0.x + - name: Publish + run: | + dotnet publish -c Release -r linux-x64 --self-contained -p:PublishTrimmed=true -p:PublishSingleFile=true -o app/linux-x64 + dotnet publish -c Release -r win-x64 --self-contained -p:PublishTrimmed=true -p:PublishSingleFile=true -o app/win-x64 + dotnet publish -c Release -r linux-arm64 --self-contained -p:PublishTrimmed=true -p:PublishSingleFile=true -o app/linux-arm64 + cd app + find . -type f -name 'appsettings.Example.json' -execdir mv {} appsettings.json \; + find . -maxdepth 1 -type d ! -name '.' | xargs -I {} sh -c 'cd "{}" && zip -r "../${{ github.ref_name }}-$(basename {}).zip" *' + find . -maxdepth 1 -type d ! -name '.' -exec rm -rf {} + - name: Upload artifacts uses: actions/upload-artifact@v3 with: @@ -39,14 +44,6 @@ jobs: with: name: app path: app - - - name: Zip artifact - run: | - cd app - mv linux-x64/appsettings.Example.json linux-x64/appsettings.json - zip -r linux-x64.zip linux-x64/* - mv win-x64/appsettings.Example.json win-x64/appsettings.json - zip -r win-x64.zip win-x64/* - name: Create Release id: create_release @@ -59,27 +56,27 @@ jobs: draft: false prerelease: true - - name: Upload Release Asset for Linux-x64 - id: upload_linux - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: app/linux-x64.zip - asset_name: linux-x64.zip - asset_content_type: application/zip - - - name: Upload Release Asset for Windows-x64 - id: upload_win - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload ZIP files to Release + id: upload_zip + uses: actions/github-script@v3 with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: app/win-x64.zip - asset_name: win-x64.zip - asset_content_type: application/zip + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const path = require('path'); + const fs = require('fs'); + const release_id = '${{ steps.create_release.outputs.id }}'; + for (let file of await fs.readdirSync('app/')) { + if (path.extname(file) === '.zip') { + console.log('uploadReleaseAsset', file); + await github.repos.uploadReleaseAsset({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: release_id, + name: file, + data: await fs.readFileSync(`app/${file}`) + }); + } + } cleanup: name: Cleanup diff --git a/.github/workflows/dotnet-publish-release.yml b/.github/workflows/dotnet-publish-release.yml index 1f59517..895bbdc 100644 --- a/.github/workflows/dotnet-publish-release.yml +++ b/.github/workflows/dotnet-publish-release.yml @@ -1,4 +1,4 @@ -name: .NET +name: Build Release on: push: @@ -17,11 +17,16 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: 6.0.x - - name: Publish for Linux-x64 - run: dotnet publish -c Release -r linux-x64 --self-contained -p:PublishTrimmed=true -p:PublishSingleFile=true -p:PublishReadyToRun=true -o app/linux-x64 - - name: Publish for Windows-x64 - run: dotnet publish -c Release -r win-x64 --self-contained -p:PublishTrimmed=true -p:PublishSingleFile=true -p:PublishReadyToRun=true -o app/win-x64 + dotnet-version: 8.0.x + - name: Publish + run: | + dotnet publish -c Release -r linux-x64 --self-contained -p:PublishTrimmed=true -p:PublishSingleFile=true -o app/linux-x64 + dotnet publish -c Release -r win-x64 --self-contained -p:PublishTrimmed=true -p:PublishSingleFile=true -o app/win-x64 + dotnet publish -c Release -r linux-arm64 --self-contained -p:PublishTrimmed=true -p:PublishSingleFile=true -o app/linux-arm64 + cd app + find . -type f -name 'appsettings.Example.json' -execdir mv {} appsettings.json \; + find . -maxdepth 1 -type d ! -name '.' | xargs -I {} sh -c 'cd "{}" && zip -r "../${{ github.ref_name }}-$(basename {}).zip" *' + find . -maxdepth 1 -type d ! -name '.' -exec rm -rf {} + - name: Upload artifacts uses: actions/upload-artifact@v3 with: @@ -39,14 +44,6 @@ jobs: with: name: app path: app - - - name: Zip artifact - run: | - cd app - mv linux-x64/appsettings.Example.json linux-x64/appsettings.json - zip -r linux-x64.zip linux-x64/* - mv win-x64/appsettings.Example.json win-x64/appsettings.json - zip -r win-x64.zip win-x64/* - name: Create Release id: create_release @@ -59,27 +56,27 @@ jobs: draft: false prerelease: false - - name: Upload Release Asset for Linux-x64 - id: upload_linux - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: app/linux-x64.zip - asset_name: linux-x64.zip - asset_content_type: application/zip - - - name: Upload Release Asset for Windows-x64 - id: upload_win - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload ZIP files to Release + id: upload_zip + uses: actions/github-script@v3 with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: app/win-x64.zip - asset_name: win-x64.zip - asset_content_type: application/zip + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + const path = require('path'); + const fs = require('fs'); + const release_id = '${{ steps.create_release.outputs.id }}'; + for (let file of await fs.readdirSync('app/')) { + if (path.extname(file) === '.zip') { + console.log('uploadReleaseAsset', file); + await github.repos.uploadReleaseAsset({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: release_id, + name: file, + data: await fs.readFileSync(`app/${file}`) + }); + } + } cleanup: name: Cleanup diff --git a/.gitignore b/.gitignore index 52d341c..440a036 100644 --- a/.gitignore +++ b/.gitignore @@ -366,3 +366,4 @@ FodyWeavers.xsd /src/TokenPay/EVMChains.json /src/TokenPay/EVMChains.Development.json /src/TokenPay/手续费钱包私钥.txt +/src/TokenPay/.config diff --git a/src/TokenPay.sln b/src/TokenPay.sln index 3e591a4..4711f6f 100644 --- a/src/TokenPay.sln +++ b/src/TokenPay.sln @@ -8,7 +8,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D54245C4-8D70-442B-91C0-0053856152B0}" ProjectSection(SolutionItems) = preProject .dockerignore = .dockerignore - .editorconfig = .editorconfig .gitattributes = .gitattributes .gitignore = .gitignore docker-compose.yml = docker-compose.yml diff --git a/src/TokenPay/BgServices/BaseScheduledService.cs b/src/TokenPay/BgServices/BaseScheduledService.cs index c78fcb2..2dfe30f 100644 --- a/src/TokenPay/BgServices/BaseScheduledService.cs +++ b/src/TokenPay/BgServices/BaseScheduledService.cs @@ -1,60 +1,38 @@ namespace TokenPay.BgServices { - public abstract class BaseScheduledService : IHostedService, IDisposable + public abstract class BaseScheduledService : BackgroundService { - private readonly Timer _timer; protected readonly string jobName; - private readonly TimeSpan _period; protected readonly ILogger Logger; + private readonly PeriodicTimer _timer; protected BaseScheduledService(string JobName, TimeSpan period, ILogger logger) { Logger = logger; jobName = JobName; - _period = period; - _timer = new Timer(Execute, null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan); + _timer = new PeriodicTimer(period); } - - public void Execute(object? state = null) + protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - try - { - - ExecuteAsync().Wait(); - //Logger.LogInformation("Begin execute service"); - } - catch (Exception ex) - { - Logger.LogError(ex, $"定时任务[{jobName}]执行出现错误"); - } - finally + Logger.LogInformation("Service {JobName} is starting.", jobName); + do { - //Logger.LogInformation("Execute finished"); - _timer.Change(_period, Timeout.InfiniteTimeSpan); - } + try + { + await ExecuteAsync(); + } + catch (Exception ex) + { + Logger.LogError(ex, $"定时任务[{jobName}]执行出现错误"); + } + } while (!stoppingToken.IsCancellationRequested && await _timer.WaitForNextTickAsync(stoppingToken)); } - protected abstract Task ExecuteAsync(); - - public virtual void Dispose() - { - _timer?.Dispose(); - } - - public Task StartAsync(CancellationToken cancellationToken) - { - Logger.LogInformation("Service {JobName} is starting.", jobName); - _timer.Change(TimeSpan.FromSeconds(3), Timeout.InfiniteTimeSpan); - return Task.CompletedTask; - } - - public Task StopAsync(CancellationToken cancellationToken) + public override Task StopAsync(CancellationToken cancellationToken) { Logger.LogInformation("Service {JobName} is stopping.", jobName); - - _timer?.Change(Timeout.Infinite, 0); - - return Task.CompletedTask; + _timer.Dispose(); + return base.StopAsync(cancellationToken); } } } diff --git a/src/TokenPay/BgServices/CollectionTRONService.cs b/src/TokenPay/BgServices/CollectionTRONService.cs index ce03434..c9c864f 100644 --- a/src/TokenPay/BgServices/CollectionTRONService.cs +++ b/src/TokenPay/BgServices/CollectionTRONService.cs @@ -16,7 +16,7 @@ public class CollectionTRONService : BaseScheduledService { private readonly IConfiguration _configuration; private readonly TelegramBot _bot; - private readonly IServiceProvider _serviceProvider; + private readonly IFreeSql freeSql; private readonly ILogger _logger; /// /// 是否启用归集功能 @@ -63,14 +63,13 @@ public class CollectionTRONService : BaseScheduledService public CollectionTRONService( IConfiguration configuration, TelegramBot bot, - IServiceProvider serviceProvider, + IFreeSql freeSql, ILogger logger) : base("TRON归集任务", TimeSpan.FromHours(configuration.GetValue("Collection:CheckTime", 1)), logger) { this._configuration = configuration; - this._serviceProvider = serviceProvider; this._logger = logger; this._bot = bot; - + this.freeSql = freeSql; } protected override async Task ExecuteAsync() { @@ -160,8 +159,7 @@ await _bot.SendTextMessageAsync(@$"归集收款地址余额 当前TRX余额:{trx} USDT 当前USDT余额:{usdt} USDT"); } - using IServiceScope scope = _serviceProvider.CreateScope(); - var _repository = scope.ServiceProvider.GetRequiredService>(); + var _repository = freeSql.GetRepository(); var list = await _repository.Where(x => x.Currency == TokenCurrency.TRX).Where(x => ForceCheckAllAddress || (x.USDT > MinUSDT || x.Value > 0.5m)).ToListAsync(); var count = 0; foreach (var item in list) diff --git a/src/TokenPay/BgServices/OrderCheckEVMBaseService.cs b/src/TokenPay/BgServices/OrderCheckEVMBaseService.cs index f78cef0..4f94182 100644 --- a/src/TokenPay/BgServices/OrderCheckEVMBaseService.cs +++ b/src/TokenPay/BgServices/OrderCheckEVMBaseService.cs @@ -16,8 +16,7 @@ public class OrderCheckEVMBaseService : BaseScheduledService private readonly IHostEnvironment _env; private readonly Channel _channel; private readonly List _chains; - private readonly IServiceProvider _serviceProvider; - private readonly FlurlClient client; + private readonly IFreeSql freeSql; private bool UseDynamicAddress => _configuration.GetValue("UseDynamicAddress", true); private bool UseDynamicAddressAmountMove => _configuration.GetValue("DynamicAddressConfig:AmountMove", false); public OrderCheckEVMBaseService(ILogger logger, @@ -25,26 +24,19 @@ public OrderCheckEVMBaseService(ILogger logger, IHostEnvironment env, Channel channel, List Chains, - IServiceProvider serviceProvider) : base("ETH订单检测", TimeSpan.FromSeconds(15), logger) + IFreeSql freeSql) : base("ETH订单检测", TimeSpan.FromSeconds(15), logger) { _logger = logger; this._configuration = configuration; this._env = env; this._channel = channel; _chains = Chains; - _serviceProvider = serviceProvider; - var WebProxy = configuration.GetValue("WebProxy"); - client = new FlurlClient(); - if (!string.IsNullOrEmpty(WebProxy)) - { - client.Settings.HttpClientFactory = new ProxyHttpClientFactory(WebProxy); - } + this.freeSql = freeSql; } protected override async Task ExecuteAsync() { - using IServiceScope scope = _serviceProvider.CreateScope(); - var _repository = scope.ServiceProvider.GetRequiredService>(); + var _repository = freeSql.GetRepository(); foreach (var chain in _chains) { if (chain == null || !chain.Enable) continue; @@ -87,7 +79,6 @@ protected override async Task ExecuteAsync() var req = BaseUrl .AppendPathSegment($"api") .SetQueryParams(query) - .WithClient(client) .WithTimeout(15); var result = await req .GetJsonAsync>(); diff --git a/src/TokenPay/BgServices/OrderCheckEVMERC20Service.cs b/src/TokenPay/BgServices/OrderCheckEVMERC20Service.cs index ad491ef..57ab019 100644 --- a/src/TokenPay/BgServices/OrderCheckEVMERC20Service.cs +++ b/src/TokenPay/BgServices/OrderCheckEVMERC20Service.cs @@ -17,8 +17,7 @@ public class OrderCheckEVMERC20Service : BaseScheduledService private readonly IHostEnvironment _env; private readonly List _chains; private readonly Channel _channel; - private readonly IServiceProvider _serviceProvider; - private readonly FlurlClient client; + private readonly IFreeSql freeSql; private bool UseDynamicAddress => _configuration.GetValue("UseDynamicAddress", true); private bool UseDynamicAddressAmountMove => _configuration.GetValue("DynamicAddressConfig:AmountMove", false); public OrderCheckEVMERC20Service(ILogger logger, @@ -26,26 +25,19 @@ public OrderCheckEVMERC20Service(ILogger logger, IHostEnvironment env, List Chains, Channel channel, - IServiceProvider serviceProvider) : base("ERC20订单检测", TimeSpan.FromSeconds(15), logger) + IFreeSql freeSql) : base("ERC20订单检测", TimeSpan.FromSeconds(15), logger) { _logger = logger; this._configuration = configuration; this._env = env; _chains = Chains; this._channel = channel; - _serviceProvider = serviceProvider; - var WebProxy = configuration.GetValue("WebProxy"); - client = new FlurlClient(); - if (!string.IsNullOrEmpty(WebProxy)) - { - client.Settings.HttpClientFactory = new ProxyHttpClientFactory(WebProxy); - } + this.freeSql = freeSql; } protected override async Task ExecuteAsync() { - using IServiceScope scope = _serviceProvider.CreateScope(); - var _repository = scope.ServiceProvider.GetRequiredService>(); + var _repository = freeSql.GetRepository(); foreach (var chain in _chains) { if (chain == null || !chain.Enable || chain.ERC20 == null) continue; @@ -108,7 +100,6 @@ private async Task ERC20(IBaseRepository _repository, string Curren var req = BaseUrl .AppendPathSegment($"api") .SetQueryParams(query) - .WithClient(client) .WithTimeout(15); var result = await req .GetJsonAsync>(); diff --git a/src/TokenPay/BgServices/OrderCheckTRC20Service.cs b/src/TokenPay/BgServices/OrderCheckTRC20Service.cs index 509de40..a75986a 100644 --- a/src/TokenPay/BgServices/OrderCheckTRC20Service.cs +++ b/src/TokenPay/BgServices/OrderCheckTRC20Service.cs @@ -15,27 +15,27 @@ public class OrderCheckTRC20Service : BaseScheduledService private readonly IConfiguration _configuration; private readonly IHostEnvironment _env; private readonly Channel _channel; - private readonly IServiceProvider _serviceProvider; + private readonly IFreeSql freeSql; + private bool UseDynamicAddress => _configuration.GetValue("UseDynamicAddress", true); private bool UseDynamicAddressAmountMove => _configuration.GetValue("DynamicAddressConfig:AmountMove", false); public OrderCheckTRC20Service(ILogger logger, IConfiguration configuration, IHostEnvironment env, Channel channel, - IServiceProvider serviceProvider) : base("TRC20订单检测", TimeSpan.FromSeconds(3), logger) + IFreeSql freeSql) : base("TRC20订单检测", TimeSpan.FromSeconds(3), logger) { _logger = logger; this._configuration = configuration; this._env = env; this._channel = channel; - _serviceProvider = serviceProvider; + this.freeSql = freeSql; } protected override async Task ExecuteAsync() { - using IServiceScope scope = _serviceProvider.CreateScope(); - var _repository = scope.ServiceProvider.GetRequiredService>(); - var _TokensRepository = scope.ServiceProvider.GetRequiredService>(); + var _repository = freeSql.GetRepository(); + var _TokensRepository = freeSql.GetRepository(); var Address = await _repository .Where(x => x.Status == OrderStatus.Pending) diff --git a/src/TokenPay/BgServices/OrderCheckTRXService.cs b/src/TokenPay/BgServices/OrderCheckTRXService.cs index 284dd06..33d1363 100644 --- a/src/TokenPay/BgServices/OrderCheckTRXService.cs +++ b/src/TokenPay/BgServices/OrderCheckTRXService.cs @@ -15,7 +15,7 @@ public class OrderCheckTRXService : BaseScheduledService private readonly IConfiguration _configuration; private readonly IHostEnvironment _env; private readonly Channel _channel; - private readonly IServiceProvider _serviceProvider; + private readonly IFreeSql freeSql; private bool UseDynamicAddress => _configuration.GetValue("UseDynamicAddress", true); private bool UseDynamicAddressAmountMove => _configuration.GetValue("DynamicAddressConfig:AmountMove", false); @@ -23,20 +23,19 @@ public OrderCheckTRXService(ILogger logger, IConfiguration configuration, IHostEnvironment env, Channel channel, - IServiceProvider serviceProvider) : base("TRX订单检测", TimeSpan.FromSeconds(3), logger) + IFreeSql freeSql) : base("TRX订单检测", TimeSpan.FromSeconds(3), logger) { _logger = logger; this._configuration = configuration; this._env = env; this._channel = channel; - _serviceProvider = serviceProvider; + this.freeSql = freeSql; } protected override async Task ExecuteAsync() { - using IServiceScope scope = _serviceProvider.CreateScope(); - var _repository = scope.ServiceProvider.GetRequiredService>(); - var _TokensRepository = scope.ServiceProvider.GetRequiredService>(); + var _repository = freeSql.GetRepository(); + var _TokensRepository = freeSql.GetRepository(); var Address = await _repository .Where(x => x.Status == OrderStatus.Pending) diff --git a/src/TokenPay/BgServices/OrderExpiredService.cs b/src/TokenPay/BgServices/OrderExpiredService.cs index a5674f9..f6713eb 100644 --- a/src/TokenPay/BgServices/OrderExpiredService.cs +++ b/src/TokenPay/BgServices/OrderExpiredService.cs @@ -7,21 +7,20 @@ public class OrderExpiredService : BaseScheduledService { private readonly ILogger _logger; private readonly IConfiguration _configuration; - private readonly IServiceProvider _serviceProvider; + private readonly IFreeSql freeSql; public OrderExpiredService(ILogger logger, IConfiguration configuration, - IServiceProvider serviceProvider) : base("订单过期", TimeSpan.FromSeconds(10), logger) + IFreeSql freeSql) : base("订单过期", TimeSpan.FromSeconds(10), logger) { _logger = logger; this._configuration = configuration; - _serviceProvider = serviceProvider; + this.freeSql = freeSql; } protected override async Task ExecuteAsync() { - using IServiceScope scope = _serviceProvider.CreateScope(); - var _repository = scope.ServiceProvider.GetRequiredService>(); + var _repository = freeSql.GetRepository(); var ExpireTime = _configuration.GetValue("ExpireTime", 10 * 60); var ExpireDateTime = DateTime.Now.AddSeconds(-1 * ExpireTime); diff --git a/src/TokenPay/BgServices/OrderNotifyService.cs b/src/TokenPay/BgServices/OrderNotifyService.cs index 0a940d4..51a0779 100644 --- a/src/TokenPay/BgServices/OrderNotifyService.cs +++ b/src/TokenPay/BgServices/OrderNotifyService.cs @@ -10,39 +10,35 @@ public class OrderNotifyService : BaseScheduledService { private readonly ILogger _logger; private readonly IConfiguration _configuration; - private readonly IServiceProvider _serviceProvider; + private readonly IFreeSql freeSql; private readonly FlurlClient client; public OrderNotifyService(ILogger logger, IConfiguration configuration, - IServiceProvider serviceProvider) : base("订单通知", TimeSpan.FromSeconds(1), logger) + IFreeSql freeSql) : base("订单通知", TimeSpan.FromSeconds(3), logger) { _logger = logger; this._configuration = configuration; - _serviceProvider = serviceProvider; + this.freeSql = freeSql; client = new FlurlClient(); client.Settings.Timeout = TimeSpan.FromSeconds(configuration.GetValue("NotifyTimeOut", 3)); - client.Configure(settings => + client.BeforeCall(c => { - settings.BeforeCall = c => - { - c.Request.WithHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 TokenPay/1.0"); - _logger.LogInformation("发起请求\nURL:{url}\n参数:{body}", c.Request.Url, c.RequestBody); - }; - settings.AfterCallAsync = async c => + c.Request.WithHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 TokenPay/1.0"); + _logger.LogInformation("发起请求\nURL:{url}\n参数:{body}", c.Request.Url, c.RequestBody); + }); + client.AfterCall(async c => + { + if (c.Response != null) { - if (c.Response != null) - { - _logger.LogInformation("收到响应\nURL:{url}\n响应:{@body}", c.Request.Url, await c.Response.GetStringAsync()); - } - }; + _logger.LogInformation("收到响应\nURL:{url}\n响应:{@body}", c.Request.Url, await c.Response.GetStringAsync()); + } }); } protected override async Task ExecuteAsync() { - using IServiceScope scope = _serviceProvider.CreateScope(); - var _repository = scope.ServiceProvider.GetRequiredService>(); + var _repository = freeSql.GetRepository(); var start = DateTime.Now.AddMinutes(-1); var Orders = await _repository .Where(x => x.Status == OrderStatus.Paid) @@ -80,7 +76,7 @@ private async Task Notify(TokenOrders order) SignatureStr += ApiToken; var Signature = SignatureStr.ToMD5(); dic.Add(nameof(Signature), Signature); - var result = await order.NotifyUrl.WithClient(client).PostJsonAsync(dic); + var result = await client.Request(order.NotifyUrl).PostJsonAsync(dic); var message = await result.GetStringAsync(); if (result.StatusCode == 200 && message == "ok") { diff --git a/src/TokenPay/BgServices/OrderPaySuccessService.cs b/src/TokenPay/BgServices/OrderPaySuccessService.cs index 79d131a..2b07be9 100644 --- a/src/TokenPay/BgServices/OrderPaySuccessService.cs +++ b/src/TokenPay/BgServices/OrderPaySuccessService.cs @@ -33,13 +33,13 @@ public OrderPaySuccessService( protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - while (!stoppingToken.IsCancellationRequested && await _channel.Reader.WaitToReadAsync()) + while (!stoppingToken.IsCancellationRequested && await _channel.Reader.WaitToReadAsync(stoppingToken)) { while (!stoppingToken.IsCancellationRequested && _channel.Reader.TryRead(out var item)) { try { - await SendAdminMessage(item); + await SendAdminMessage(item, stoppingToken); } catch (Exception e) { @@ -48,7 +48,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } } } - private async Task SendAdminMessage(TokenOrders order) + private async Task SendAdminMessage(TokenOrders order, CancellationToken? cancellationToken = null) { //默认货币 var BaseCurrency = _configuration.GetValue("BaseCurrency", "CNY"); @@ -93,7 +93,7 @@ private async Task SendAdminMessage(TokenOrders order) } } } - await _bot.SendTextMessageAsync(message); + await _bot.SendTextMessageAsync(message, cancellationToken: cancellationToken); } } } diff --git a/src/TokenPay/BgServices/UpdateRateService.cs b/src/TokenPay/BgServices/UpdateRateService.cs index c09240f..b2cab02 100644 --- a/src/TokenPay/BgServices/UpdateRateService.cs +++ b/src/TokenPay/BgServices/UpdateRateService.cs @@ -15,29 +15,20 @@ public class UpdateRateService : BaseScheduledService const string baseUrl = "https://www.okx.com"; const string User_Agent = "TokenPay/1.0 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36"; private readonly IConfiguration _configuration; - private readonly IServiceProvider _serviceProvider; private readonly List _chain; + private readonly IFreeSql freeSql; private readonly ILogger _logger; - private readonly FlurlClient client; private FiatCurrency BaseCurrency => Enum.Parse(_configuration.GetValue("BaseCurrency", "CNY")!); public UpdateRateService( IConfiguration configuration, - IServiceProvider serviceProvider, List chain, + IFreeSql freeSql, ILogger logger) : base("更新汇率", TimeSpan.FromSeconds(3600), logger) { this._configuration = configuration; - this._serviceProvider = serviceProvider; this._chain = chain; + this.freeSql = freeSql; this._logger = logger; - var WebProxy = configuration.GetValue("WebProxy"); - client = new FlurlClient(); - client.Settings.Timeout = TimeSpan.FromSeconds(5); - if (!string.IsNullOrEmpty(WebProxy)) - { - client.Settings.HttpClientFactory = new ProxyHttpClientFactory(WebProxy); - } - } private List GetActiveCurrency() @@ -79,8 +70,7 @@ protected override async Task ExecuteAsync() _logger.LogInformation("没有需要更新汇率的币种"); } _logger.LogInformation("------------------{tips}------------------", "开始更新汇率"); - using IServiceScope scope = _serviceProvider.CreateScope(); - var _repository = scope.ServiceProvider.GetRequiredService>(); + var _repository = freeSql.GetRepository(); var list = new List(); foreach (var item in baseCurrencyList) @@ -89,8 +79,8 @@ protected override async Task ExecuteAsync() try { var result = await baseUrl - .WithClient(client) - .WithHeaders(new { User_Agent = User_Agent }) + .WithTimeout(5) + .WithHeaders(new { User_Agent }) .AppendPathSegment("/v3/c2c/otc-ticker/quotedPrice") .SetQueryParams(new { @@ -130,13 +120,14 @@ protected override async Task ExecuteAsync() { item.Rate += RateMove; } - _logger.LogInformation("更新汇率,{a}=>{b} = {c}", item.Currency, item.FiatCurrency, $"{item.Rate}{(RateMove!=0?$" ({RateMove:+0.##;-0.##;0})":"")}"); + _logger.LogInformation("更新汇率,{a}=>{b} = {c}", item.Currency, item.FiatCurrency, $"{item.Rate}{(RateMove != 0 ? $" ({RateMove:+0.##;-0.##;0})" : "")}"); await _repository.InsertOrUpdateAsync(item); } _logger.LogInformation("------------------{tips}------------------", "结束更新汇率"); } } +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 class Datum { public bool bestOption { get; set; } @@ -160,4 +151,5 @@ enum OkxSide Sell } +#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 } diff --git a/src/TokenPay/Domains/TokenOrders.cs b/src/TokenPay/Domains/TokenOrders.cs index 2e33722..ac828a0 100644 --- a/src/TokenPay/Domains/TokenOrders.cs +++ b/src/TokenPay/Domains/TokenOrders.cs @@ -49,7 +49,7 @@ public class TokenOrders /// /// 区块链币种 /// - public string Currency { get; set; } + public required string Currency { get; set; } /// /// 订单金额,保留4位小数 /// diff --git a/src/TokenPay/Domains/TokenRate.cs b/src/TokenPay/Domains/TokenRate.cs index 9a76924..7d4938c 100644 --- a/src/TokenPay/Domains/TokenRate.cs +++ b/src/TokenPay/Domains/TokenRate.cs @@ -10,7 +10,7 @@ public class TokenRate /// 币种 /// [Column(MapType = typeof(string))] - public string Currency { get; set; } + public required string Currency { get; set; } /// /// 法币 /// diff --git a/src/TokenPay/Domains/Tokens.cs b/src/TokenPay/Domains/Tokens.cs index 38dae54..22c6083 100644 --- a/src/TokenPay/Domains/Tokens.cs +++ b/src/TokenPay/Domains/Tokens.cs @@ -5,15 +5,15 @@ namespace TokenPay.Domains public class Tokens { [Key] - public string Id { get; set; } + public required string Id { get; set; } /// /// 地址 /// - public string Address { get; set; } + public required string Address { get; set; } /// /// 密钥 /// - public string Key { get; set; } + public required string Key { get; set; } /// /// 币种 /// diff --git a/src/TokenPay/Helper/EnergyApi.cs b/src/TokenPay/Helper/EnergyApi.cs index 5020e34..8bd337e 100644 --- a/src/TokenPay/Helper/EnergyApi.cs +++ b/src/TokenPay/Helper/EnergyApi.cs @@ -28,23 +28,14 @@ public EnergyApi(ILogger logger, IConfiguration configuration) _logger = logger; _configuration = configuration; client = new FlurlClient(baseUrl); - client.Configure(settings => + client.BeforeCall(c => { - settings.BeforeCall = c => - { - c.Request.WithHeader("Lang", "zh-CN"); - }; + c.Request.WithHeader("Lang", "zh-CN"); + _logger.LogInformation("发起请求\nURL:{url}\n参数:{body}", c.Request.Url, c.RequestBody); }); - client.Configure(settings => + client.AfterCall(async c => { - settings.BeforeCall = c => - { - _logger.LogInformation("发起请求\nURL:{url}\n参数:{body}", c.Request.Url, c.RequestBody); - }; - settings.AfterCallAsync = async c => - { - _logger.LogInformation("收到响应\nURL:{url}\n响应:{@body}", c.Request.Url, c.Response != null ? await c.Response.GetStringAsync() : null); - }; + _logger.LogInformation("收到响应\nURL:{url}\n响应:{@body}", c.Request.Url, c.Response != null ? await c.Response.GetStringAsync() : null); }); } /// @@ -87,6 +78,7 @@ public async Task> CreateOrder(CreateOrderModel model) } } +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 public class CreateOrderModel { /// @@ -316,4 +308,5 @@ public class OrderPriceData [JsonProperty("pay_amount")] public decimal Price { get; set; } } +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 } diff --git a/src/TokenPay/Helper/ProxyHttpClientFactory.cs b/src/TokenPay/Helper/ProxyHttpClientFactory.cs index 2219f50..f820745 100644 --- a/src/TokenPay/Helper/ProxyHttpClientFactory.cs +++ b/src/TokenPay/Helper/ProxyHttpClientFactory.cs @@ -3,20 +3,17 @@ namespace TokenPay.Helper { - public class ProxyHttpClientFactory : DefaultHttpClientFactory + public class ProxyHttpClientFactory : DelegatingHandler { - private string _address; - public ProxyHttpClientFactory(string address) + public ProxyHttpClientFactory(string address) : base(CreateMessageHandler(address)) { - _address = address; } - - public override HttpMessageHandler CreateMessageHandler() + public static HttpMessageHandler CreateMessageHandler(string address) { - var uri = new Uri(_address); + var uri = new Uri(address); var userinfo = uri.UserInfo.Split(":"); - if (_address.ToLower().StartsWith("http")) + if (address.ToLower().StartsWith("http")) { var webProxy = new WebProxy(Host: uri.Host, Port: uri.Port) { @@ -30,7 +27,7 @@ public override HttpMessageHandler CreateMessageHandler() }; } - else if (_address.ToLower().StartsWith("socks")) + else if (address.ToLower().StartsWith("socks")) { var proxy = new WebProxy($"{uri.Scheme}://{uri.Authority}") { @@ -46,7 +43,7 @@ public override HttpMessageHandler CreateMessageHandler() { return new HttpClientHandler { - Proxy = new WebProxy(_address), + Proxy = new WebProxy(address), UseProxy = true }; } diff --git a/src/TokenPay/Helper/QueryTronAction.cs b/src/TokenPay/Helper/QueryTronAction.cs index 681584a..1f409da 100644 --- a/src/TokenPay/Helper/QueryTronAction.cs +++ b/src/TokenPay/Helper/QueryTronAction.cs @@ -28,7 +28,7 @@ namespace TokenPay.Helper public static partial class QueryTronAction { - public static IConfiguration configuration { get; set; } + public static IConfiguration configuration { get; set; } = null!; /// /// 获取USDT余额 /// @@ -370,11 +370,11 @@ public static async Task GetTRXAsync(string address) var result = await resultData.ReceiveJson(); if (result.ContainsKey("Error") && result["Error"] != null) { - return (false, result["Error"].ToString(), null); + return (false, result["Error"]?.ToString(), null); } var transaction = result; // Sign - var txId = transaction["txID"].ToString().FromHexToByteArray(); + var txId = transaction["txID"]?.ToString().FromHexToByteArray(); var wallet0 = new TronWallet(privateKey); Signature signature = wallet0.Sign(txId); TronSignature tronSignature = new TronSignature(signature); diff --git a/src/TokenPay/Helper/TelegramBot.cs b/src/TokenPay/Helper/TelegramBot.cs index 4c77155..f08ef79 100644 --- a/src/TokenPay/Helper/TelegramBot.cs +++ b/src/TokenPay/Helper/TelegramBot.cs @@ -11,22 +11,13 @@ public class TelegramBot private readonly string _botToken; private readonly long _userId; private readonly IConfiguration _configuration; - private readonly FlurlClient client; - public TelegramBot(IConfiguration configuration) { _botToken = configuration.GetValue("Telegram:BotToken")!; _userId = configuration.GetValue("Telegram:AdminUserId"); this._configuration = configuration; - var WebProxy = configuration.GetValue("WebProxy"); - client = new FlurlClient(); - client.Settings.Timeout = TimeSpan.FromSeconds(5); - if (!string.IsNullOrEmpty(WebProxy)) - { - client.Settings.HttpClientFactory = new ProxyHttpClientFactory(WebProxy); - } } - public static TelegramBotInfo BotInfo; + public static TelegramBotInfo BotInfo = null!; public async Task?> GetMeAsync(string? TelegramApiHost = null) { if (string.IsNullOrEmpty(_botToken) || _userId == 0) @@ -37,8 +28,8 @@ public TelegramBot(IConfiguration configuration) var ApiHost = TelegramApiHost ?? BaseTelegramApiHost; var request = ApiHost + .WithTimeout(5) .AppendPathSegment($"bot{_botToken}/getMe") - .WithClient(client) .WithTimeout(10); var result = await request.GetJsonAsync>(); Log.Logger.Information("机器人启动成功!我是{@result}。", result.Result.FirstName); @@ -46,7 +37,7 @@ public TelegramBot(IConfiguration configuration) await SendTextMessageAsync("你好呀~我是TokenPay通知机器人!"); return result; } - public async Task?> SendTextMessageAsync(string Message, string? TelegramApiHost = null) + public async Task?> SendTextMessageAsync(string Message, string? TelegramApiHost = null, CancellationToken? cancellationToken = null) { if (string.IsNullOrEmpty(_botToken) || _userId == 0) { @@ -56,8 +47,8 @@ public TelegramBot(IConfiguration configuration) var ApiHost = TelegramApiHost ?? BaseTelegramApiHost; var request = ApiHost + .WithTimeout(5) .AppendPathSegment($"bot{_botToken}/sendMessage") - .WithClient(client) .SetQueryParams(new { chat_id = _userId, @@ -68,7 +59,7 @@ public TelegramBot(IConfiguration configuration) .WithTimeout(10); try { - var result = await request.GetJsonAsync>(); + var result = await request.GetJsonAsync>(cancellationToken: cancellationToken ?? default); Log.Logger.Information("机器人消息发送结果:{result}", result.Ok); return result; } @@ -80,6 +71,7 @@ public TelegramBot(IConfiguration configuration) } } +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 public class TelegramBotInfo { [JsonProperty("id")] @@ -158,4 +150,5 @@ public class TelegramResult [JsonProperty("result")] public T Result { get; set; } } +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 } diff --git a/src/TokenPay/Models/CreateOrderViewModel.cs b/src/TokenPay/Models/CreateOrderViewModel.cs index dd5830f..339d42e 100644 --- a/src/TokenPay/Models/CreateOrderViewModel.cs +++ b/src/TokenPay/Models/CreateOrderViewModel.cs @@ -29,7 +29,7 @@ public class CreateOrderViewModel [Display(Name = "币种")] [Required(ErrorMessage = "{0}为必传参数")] //[(ErrorMessage = "{1}不是有效的{0}")] - public string Currency { get; set; } + public required string Currency { get; set; } /// /// 在回调通知或订单信息中原样返回 /// diff --git a/src/TokenPay/Models/EthModel/BaseResponse.cs b/src/TokenPay/Models/EthModel/BaseResponse.cs index 60900b2..c8267ec 100644 --- a/src/TokenPay/Models/EthModel/BaseResponse.cs +++ b/src/TokenPay/Models/EthModel/BaseResponse.cs @@ -5,12 +5,12 @@ namespace TokenPay.Models.EthModel public class BaseResponse { [JsonProperty("status")] - public string Status { get; set; } + public string? Status { get; set; } [JsonProperty("message")] - public string Message { get; set; } + public string? Message { get; set; } [JsonProperty("result")] - public List Result { get; set; } + public List Result { get; set; } = []; } } diff --git a/src/TokenPay/Models/EthModel/ERC20Transaction.cs b/src/TokenPay/Models/EthModel/ERC20Transaction.cs index f73cc12..b7cbcee 100644 --- a/src/TokenPay/Models/EthModel/ERC20Transaction.cs +++ b/src/TokenPay/Models/EthModel/ERC20Transaction.cs @@ -3,6 +3,7 @@ namespace TokenPay.Models.EthModel { +#pragma warning disable CS8618 // ˳캯ʱΪ null ֶα null ֵ뿼ΪΪ null public class ERC20Transaction { [JsonProperty("blockNumber")] @@ -64,4 +65,5 @@ public class ERC20Transaction [JsonProperty("confirmations")] public decimal Confirmations { get; set; } } +#pragma warning restore CS8618 // ˳캯ʱΪ null ֶα null ֵ뿼ΪΪ null } diff --git a/src/TokenPay/Models/EthModel/EVMChain.cs b/src/TokenPay/Models/EthModel/EVMChain.cs index 99da67a..bf73f8a 100644 --- a/src/TokenPay/Models/EthModel/EVMChain.cs +++ b/src/TokenPay/Models/EthModel/EVMChain.cs @@ -1,5 +1,7 @@ namespace TokenPay.Models.EthModel { + +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 public class EVMChain { /// @@ -58,4 +60,5 @@ public class EVMErc20 /// public string ContractAddress { get; set; } } +#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 } diff --git a/src/TokenPay/Models/EthModel/EthTransaction.cs b/src/TokenPay/Models/EthModel/EthTransaction.cs index fc44307..22280e2 100644 --- a/src/TokenPay/Models/EthModel/EthTransaction.cs +++ b/src/TokenPay/Models/EthModel/EthTransaction.cs @@ -3,6 +3,7 @@ namespace TokenPay.Models.EthModel { +#pragma warning disable CS8618 // ˳캯ʱΪ null ֶα null ֵ뿼ΪΪ null public class EthTransaction { [JsonProperty("blockNumber")] @@ -70,4 +71,5 @@ public decimal RealAmount(int decimals) [JsonProperty("functionName")] public string FunctionName { get; set; } } +#pragma warning restore CS8618 // ˳캯ʱΪ null ֶα null ֵ뿼ΪΪ null } diff --git a/src/TokenPay/Models/ReturnData.cs b/src/TokenPay/Models/ReturnData.cs index 9d6a2af..d34a077 100644 --- a/src/TokenPay/Models/ReturnData.cs +++ b/src/TokenPay/Models/ReturnData.cs @@ -7,7 +7,7 @@ public class ReturnData public bool Success { get; set; } public string? Message { get; set; } public T? Data { get; set; } - public SortedDictionary Info { get; set; } + public SortedDictionary? Info { get; set; } } public class ReturnData { diff --git a/src/TokenPay/Models/TronModel/TronTransaction.cs b/src/TokenPay/Models/TronModel/TronTransaction.cs index e80487c..1779420 100644 --- a/src/TokenPay/Models/TronModel/TronTransaction.cs +++ b/src/TokenPay/Models/TronModel/TronTransaction.cs @@ -2,6 +2,7 @@ namespace TokenPay.Models.TronModel { +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 public class TronTransaction { [JsonProperty("transaction_id")] @@ -44,4 +45,5 @@ public class TokenInfo [JsonProperty("name")] public string Name { get; set; } } +#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 } diff --git a/src/TokenPay/Models/TronModel/TrxTransaction.cs b/src/TokenPay/Models/TronModel/TrxTransaction.cs index acad313..5e926aa 100644 --- a/src/TokenPay/Models/TronModel/TrxTransaction.cs +++ b/src/TokenPay/Models/TronModel/TrxTransaction.cs @@ -9,7 +9,7 @@ namespace TokenPay.Models.TronModel { - // Root myDeserializedClass = JsonSerializer.Deserialize(myJsonResponse); +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 public class Contract { [JsonProperty("parameter")] @@ -118,6 +118,5 @@ public class Value public string ToAddress { get; set; } public string ToAddressBase58 => ToAddress.HexToeBase58(); } - - +#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。 } diff --git a/src/TokenPay/Program.cs b/src/TokenPay/Program.cs index 7f458f8..6ace370 100644 --- a/src/TokenPay/Program.cs +++ b/src/TokenPay/Program.cs @@ -1,12 +1,9 @@ +using Flurl.Http; +using Flurl.Http.Newtonsoft; using FreeSql; -using FreeSql.DataAnnotations; -using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Mvc.Razor; -using Microsoft.AspNetCore.Server.Kestrel.Core; -using Microsoft.Extensions.DependencyInjection; using Serilog; using Serilog.Events; -using System.Data.Common; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; @@ -53,6 +50,7 @@ .WriteTo.Console() ); builder.Services.AddControllersWithViews() + .AddRazorRuntimeCompilation() .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix) .AddJsonOptions(o => { @@ -69,7 +67,6 @@ .UseNoneCommandParameter(true) .Build(); - Services.AddSingleton(fsql); Services.AddScoped(); Services.AddFreeRepository(); @@ -119,6 +116,16 @@ var channel = Channel.CreateUnbounded(); Services.AddSingleton(channel); +var WebProxy = Configuration.GetValue("WebProxy"); +FlurlHttp.Clients.UseNewtonsoft(); +if (!string.IsNullOrEmpty(WebProxy)) +{ + FlurlHttp.Clients.WithDefaults(c => + { + c.AddMiddleware(() => new ProxyHttpClientFactory(WebProxy)); + }); +} + var app = builder.Build(); diff --git a/src/TokenPay/TokenPay.csproj b/src/TokenPay/TokenPay.csproj index 7f61607..d4ce3e4 100644 --- a/src/TokenPay/TokenPay.csproj +++ b/src/TokenPay/TokenPay.csproj @@ -7,16 +7,20 @@ True en;zh 1591; + false + false + true + true + partial + - - - - - + + +