diff --git a/Src/Dlhis.Middle/Controllers/OAuth2Controller.cs b/Src/Dlhis.Middle/Controllers/OAuth2Controller.cs new file mode 100644 index 00000000..dad8378d --- /dev/null +++ b/Src/Dlhis.Middle/Controllers/OAuth2Controller.cs @@ -0,0 +1,36 @@ +using System.Text; +using Dlhis.Middle.Request; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json.Linq; + +namespace Dlhis.Middle.Controllers; + +/// +/// OAuth2认证 +/// +[Route("api/user/[controller]")] +[ApiController] +public class OAuth2Controller : ControllerBase +{ + private readonly IHttpClientFactory _httpClientFactory; + private readonly IConfiguration _configuration; + + public OAuth2Controller(IHttpClientFactory httpClientFactory, IConfiguration configuration) + { + _httpClientFactory = httpClientFactory; + _configuration = configuration; + } + + [HttpPost("clientgrant")] + public async Task ClientGrant(ClientGrantRequest request) + { + var authority = _configuration.GetSection("Authority").Value; + + var httpClient = _httpClientFactory.CreateClient(); + + var content = new StringContent($"grant_type=client_credentials&client_id={request.ClientId}&client_secret={request.ClientSecret}&scope={request.Scope}", Encoding.UTF8, "application/x-www-form-urlencoded"); + var response = await httpClient.PostAsync($"{authority}/connect/token", content); + var result = await response.Content.ReadAsStringAsync(); + return JObject.Parse(result); + } +} \ No newline at end of file diff --git a/Src/Dlhis.Middle/Controllers/OrdersController.cs b/Src/Dlhis.Middle/Controllers/OrdersController.cs index ae8a6a45..223db90e 100644 --- a/Src/Dlhis.Middle/Controllers/OrdersController.cs +++ b/Src/Dlhis.Middle/Controllers/OrdersController.cs @@ -12,7 +12,7 @@ namespace Dlhis.Middle.Controllers; /// 订单 /// [Authorize] -[Route("api/[controller]")] +[Route("api/canteen/[controller]")] [ApiController] public class OrdersController : ControllerBase { diff --git a/Src/Dlhis.Middle/Entities/LocalBalanceRecordEntity.cs b/Src/Dlhis.Middle/Entities/LocalBalanceRecordEntity.cs index cbc4ecc5..35272031 100644 --- a/Src/Dlhis.Middle/Entities/LocalBalanceRecordEntity.cs +++ b/Src/Dlhis.Middle/Entities/LocalBalanceRecordEntity.cs @@ -1,9 +1,10 @@ using Dlhis.Entity.Canteen.Entities; -namespace Dlhis.Middle.Entities +namespace Dlhis.Middle.Entities; + +public class LocalBalanceRecordEntity : BalanceRecordEntity { - public class LocalBalanceRecordEntity : BalanceRecordEntity - { - public new int Id { get; set; } - } + public new int Id { get; set; } + + public bool IsSync { get; set; } } \ No newline at end of file diff --git a/Src/Dlhis.Middle/Program.cs b/Src/Dlhis.Middle/Program.cs index 9adeef49..9625dcf3 100644 --- a/Src/Dlhis.Middle/Program.cs +++ b/Src/Dlhis.Middle/Program.cs @@ -1,6 +1,3 @@ -using Dlhis.Entity.Basic.Entity; -using Dlhis.Entity.Device.Entities; -using Dlhis.Middle.Entities; using Dlhis.Middle.Services; using Dlhis.Middle.Services.Implements; using Kurisu.AspNetCore.Startup; @@ -49,16 +46,6 @@ public class Startup : DefaultStartup var factory = Configuration.GetSection("Factory").Value; var ds = scope.ServiceProvider.GetService(); - ds.TruncateAsync().Wait(); - ds.DownloadAsync(factory).Wait(); - ds.DownloadAsync(factory).Wait(); - ds.DownloadAsync(factory).Wait(); - ds.DownloadAsync(factory).Wait(); - ds.DownloadAsync(factory).Wait(); - ds.DownloadAsync(factory).Wait(); - ds.DownloadAsync(factory).Wait(); - - ds.DownloadAsync(factory).Wait(); - ds.DownloadAsync(factory).Wait(); + ds.InitAsync(factory).Wait(); } } \ No newline at end of file diff --git a/Src/Dlhis.Middle/Request/ClientGrantRequest.cs b/Src/Dlhis.Middle/Request/ClientGrantRequest.cs new file mode 100644 index 00000000..b3ba7d30 --- /dev/null +++ b/Src/Dlhis.Middle/Request/ClientGrantRequest.cs @@ -0,0 +1,10 @@ +namespace Dlhis.Middle.Request; + +public class ClientGrantRequest +{ + public string ClientId { get; set; } + + public string ClientSecret { get; set; } + + public string Scope { get; set; } = "yikatong"; +} diff --git a/Src/Dlhis.Middle/Services/IDataDownloadService.cs b/Src/Dlhis.Middle/Services/IDataDownloadService.cs index b80ed994..2fb4ddad 100644 --- a/Src/Dlhis.Middle/Services/IDataDownloadService.cs +++ b/Src/Dlhis.Middle/Services/IDataDownloadService.cs @@ -2,7 +2,11 @@ namespace Dlhis.Middle.Services; public interface IDataDownloadService : IScopeDependency { + Task InitAsync(string factory); + Task DownloadAsync(string factory) where T : class, new(); Task TruncateAsync(); + + Task DownloadGainfosAsync(string factory); } \ No newline at end of file diff --git a/Src/Dlhis.Middle/Services/Implements/BalanceRecordCheckBackgroundService.cs b/Src/Dlhis.Middle/Services/Implements/BalanceRecordCheckBackgroundService.cs new file mode 100644 index 00000000..7304311b --- /dev/null +++ b/Src/Dlhis.Middle/Services/Implements/BalanceRecordCheckBackgroundService.cs @@ -0,0 +1,151 @@ +using System.Net.Http.Headers; +using System.Reflection; +using System.Text; +using Dlhis.Middle.Entities; +using Dlhis.Middle.Response; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +using Kurisu.AspNetCore.UnifyResultAndValidation; +using Kurisu.AspNetCore.Utils.Extensions; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.Caching.Memory; +using Newtonsoft.Json; +using SqlSugar; + +namespace Dlhis.Middle.Services.Implements; + +public class BalanceRecordCheckBackgroundService : BackgroundService +{ + private readonly IServiceProvider _serviceProvider; + private readonly ILogger _logger; + private readonly IMemoryCache _memoryCache; + private readonly IHttpClientFactory _httpClientFactory; + private readonly IHealthCheckService _healthCheckService; + private readonly string _factory; + private readonly string _hostName; + + public BalanceRecordCheckBackgroundService(IServiceProvider serviceProvider, + ILogger logger, + IMemoryCache memoryCache, + IHttpClientFactory httpClientFactory, + IHealthCheckService healthCheckService, + IConfiguration configuration) + { + _serviceProvider = serviceProvider; + _logger = logger; + _memoryCache = memoryCache; + _httpClientFactory = httpClientFactory; + _healthCheckService = healthCheckService; + _factory = configuration.GetSection("Factory").Value; + _hostName = configuration.GetSection("HostName").Value; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + if (!_healthCheckService.GetIsOffline()) + { + var now = DateTime.Now; + + using (var scope = _serviceProvider.CreateScope()) + { + try + { + var db = scope.ServiceProvider.GetService(); + var ds = scope.ServiceProvider.GetService(); + var balanceRecords = await db.Queryable().Where(x => !x.IsSync).ToListAsync(stoppingToken); + if (balanceRecords.Any()) + { + foreach (var balanceRecord in balanceRecords) + { + await db.Client.Ado.BeginTranAsync(); + try + { + balanceRecord.IsSync = true; + await db.UpdateAsync(balanceRecord); + + var r = await UploadAsync(balanceRecord); + if (r.Code == ApiStateCode.Success) + { + var d = r.Data; + var balance = decimal.Parse(d.Balance); + await db.Updateable() + .SetColumns(x => x.Balance == balance) + .Where(x => x.UserCode == d.UserCode) + .ExecuteCommandAsync(stoppingToken); + + await db.Client.Ado.CommitTranAsync(); + } + } + catch (Exception e) + { + _logger.LogError(e, "{factory}上传离线消费失败:{message}", _factory, e.Message); + await db.Client.Ado.RollbackTranAsync(); + } + } + } + + + if (now.Hour == 4 && now.Minute == 1 && !_healthCheckService.GetIsOffline() && !await db.Queryable().Where(x => !x.IsSync).AnyAsync()) + { + await ds.DownloadGainfosAsync(_factory); + } + else if (now.Hour == 15 && now.Minute == 1 && !_healthCheckService.GetIsOffline() && !await db.Queryable().Where(x => !x.IsSync).AnyAsync()) + { + await ds.DownloadGainfosAsync(_factory); + } + else if (now.Hour == 20 && now.Minute == 1 && !_healthCheckService.GetIsOffline() && !await db.Queryable().Where(x => !x.IsSync).AnyAsync()) + { + await ds.DownloadGainfosAsync(_factory); + } + } + catch (Exception e) + { + _logger.LogError(e, "{factory}同步离线数据失败:{message}", _factory, e.Message); + } + } + } + + await Task.Delay(60 * 1000, stoppingToken); + } + } + + public async Task> UploadAsync(LocalBalanceRecordEntity entity) + { + var token = await AuthAsync(); + + var httpClient = _httpClientFactory.CreateClient("face"); + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); + + var url = $"https://{_hostName}/api/canteen/middle/upload"; + + var json = entity.ToJson(); + var content = new StringContent(json, Encoding.UTF8, "application/json"); + + var response = await httpClient.PostAsync(url, content); + var responseJson = await response.Content.ReadAsStringAsync(); + return responseJson.ToObject>(); + } + + private async Task AuthAsync() + { + var httpClient = _httpClientFactory.CreateClient("face"); + + if (_memoryCache.TryGetValue("token", out string token)) return token; + + var tokenResponse = await httpClient.PostAsync($"https://{_hostName}/api/User/OAuth2/clientgrant", new StringContent(JsonConvert.SerializeObject(new + { + ClientId = "e569c2aaa98e300c", + ClientSecret = "6184d8979fc48105" + }), Encoding.UTF8, "application/json")); + + var tokenJson = await tokenResponse.Content.ReadAsStringAsync(); + var tokenInfo = JsonConvert.DeserializeObject>(tokenJson); + + token = tokenInfo.Data.AccessToken; + + _memoryCache.Set("token", token, TimeSpan.FromSeconds(tokenInfo.Data.Expires)); + + return token; + } +} \ No newline at end of file diff --git a/Src/Dlhis.Middle/Services/Implements/DataDownloadService.cs b/Src/Dlhis.Middle/Services/Implements/DataDownloadService.cs index a492d71f..3d4dcab9 100644 --- a/Src/Dlhis.Middle/Services/Implements/DataDownloadService.cs +++ b/Src/Dlhis.Middle/Services/Implements/DataDownloadService.cs @@ -28,6 +28,31 @@ public class DataDownloadService : IDataDownloadService _hostName = configuration.GetSection("HostName").Value; } + public async Task InitAsync(string factory) + { + await TruncateAsync(); + + await DownloadAsync(factory); + await DownloadAsync(factory); + await DownloadAsync(factory); + + await DownloadAsync(factory); + await DownloadAsync(factory); + await DownloadAsync(factory); + + await DownloadAsync(factory); + + if (!await _dbContext.Queryable().AnyAsync()) + { + await DownloadAsync(factory); + } + + if (!await _dbContext.Queryable().AnyAsync()) + { + await DownloadAsync(factory); + } + } + public async Task DownloadAsync(string factory) where T : class, new() { var token = await AuthAsync(); @@ -37,7 +62,7 @@ public class DataDownloadService : IDataDownloadService var url = $"https://{_hostName}/api/common/SysData/download"; #if DEBUG - url = $"http://localhost:5001/api/SysData/download"; + url = "http://localhost:5001/api/SysData/download"; #endif var requestUrl = QueryHelpers.AddQueryString(url, new Dictionary @@ -62,8 +87,29 @@ public class DataDownloadService : IDataDownloadService await _dbContext.Client.Deleteable().ExecuteCommandAsync(); await _dbContext.Client.Deleteable().ExecuteCommandAsync(); await _dbContext.Client.Deleteable().ExecuteCommandAsync(); - await _dbContext.Client.Deleteable().ExecuteCommandAsync(); - await _dbContext.Client.Deleteable().ExecuteCommandAsync(); + + //await _dbContext.Client.Deleteable().ExecuteCommandAsync(); + //await _dbContext.Client.Deleteable().ExecuteCommandAsync(); + } + + /// + public async Task DownloadGainfosAsync(string factory) + { + await _dbContext.Client.Ado.BeginTranAsync(); + try + { + await _dbContext.Client.Deleteable().ExecuteCommandAsync(); + await _dbContext.Client.Deleteable().ExecuteCommandAsync(); + + await DownloadAsync(factory); + await DownloadAsync(factory); + + await _dbContext.Client.Ado.CommitTranAsync(); + } + catch (Exception) + { + await _dbContext.Client.Ado.RollbackTranAsync(); + } } private async Task AuthAsync() diff --git a/Src/Dlhis.Middle/Services/Implements/OrderService.cs b/Src/Dlhis.Middle/Services/Implements/OrderService.cs index ca86c4e2..59af67a4 100644 --- a/Src/Dlhis.Middle/Services/Implements/OrderService.cs +++ b/Src/Dlhis.Middle/Services/Implements/OrderService.cs @@ -2,6 +2,7 @@ using Dlhis.Entity.Canteen.Entities; using Dlhis.Entity.Canteen.Enums; using Dlhis.Entity.Device.Entities; +using Dlhis.Middle.Entities; using Kurisu.AspNetCore.Utils; using Kurisu.AspNetCore.Utils.Extensions; using Mapster; @@ -37,14 +38,20 @@ public class OrderService : IOrderService { if (await _remoteOrderService.AbleRemoteCallAsync()) { - return await _remoteOrderService.AddAsync(request); + var remoteResult = await _remoteOrderService.AddAsync(request); + var d = remoteResult.Data; + var balance = decimal.Parse(d.Balance); + await _dbContext.Updateable() + .SetColumns(x => x.Balance == balance) + .Where(x => x.UserCode == d.UserCode) + .ExecuteCommandAsync(); } //尝试匹配userId _ = long.TryParse(request.UserUniqueNo, out var userId); //根据档案号 - var gainfo = await _dbContext.Queryable() + var gainfo = await _dbContext.Queryable() .Where(x => x.Code == request.UserUniqueNo || x.Id == userId || x.CardNo == request.UserUniqueNo) .FirstAsync(); @@ -54,7 +61,7 @@ public class OrderService : IOrderService try { - (await _dbContext.Queryable().Where(x => x.UserNo == gainfo.No).AnyAsync()) + (await _dbContext.Queryable().Where(x => x.UserNo == gainfo.No).AnyAsync()) .ThrowIfFalse("余额不足,无法消费"); await ConsumeAsync(request.Adapt(), gainfo); @@ -95,17 +102,17 @@ public class OrderService : IOrderService /// /// /// - private async Task ConsumeAsync(ConsumeRequest request, GainfoEntity gainfo) + private async Task ConsumeAsync(ConsumeRequest request, LocalGainfoEntity gainfo) { - var registry = await _dbContext.Queryable().Where(x => x.DeviceUniqueNo == request.DeviceUniqueNo).SingleAsync(); + var registry = await _dbContext.Queryable().Where(x => x.DeviceUniqueNo == request.DeviceUniqueNo).SingleAsync(); registry.ThrowIfNull("设备尚未登记"); - var device = await _dbContext.Queryable().Where(x => x.UniqueNo == request.DeviceUniqueNo).SingleAsync(); + var device = await _dbContext.Queryable().Where(x => x.UniqueNo == request.DeviceUniqueNo).SingleAsync(); device.ThrowIfNull("设备尚未登记"); device.Enable.ThrowIfFalse("设备已被停用"); - var businessPlaceInfo = await _dbContext.Queryable() - .LeftJoin((x, sr) => x.Code == sr.PlaceCode) + var businessPlaceInfo = await _dbContext.Queryable() + .LeftJoin((x, sr) => x.Code == sr.PlaceCode) .Where(x => x.Code == registry.BusinessPlaceCode) .Select((x, sr) => new { @@ -117,7 +124,7 @@ public class OrderService : IOrderService businessPlaceInfo.ThrowIfNull("设备尚未登记营业点"); //获取当前消费餐次 - var shifts = await _dbContext.Queryable().Where(x => x.ShiftCode == businessPlaceInfo.ShiftCode).ToListAsync(); + var shifts = await _dbContext.Queryable().Where(x => x.ShiftCode == businessPlaceInfo.ShiftCode).ToListAsync(); shifts.ThrowIfEmpty("营业点餐次尚未设置"); var shift = shifts.FirstOrDefault(x => new TimePeriod @@ -129,7 +136,7 @@ public class OrderService : IOrderService var costDate = await GetCostDateAsync(shift, request.CostDateTime); - var balanceRecord = new BalanceRecordEntity + var balanceRecord = new LocalBalanceRecordEntity { // RequestJson = request.ToJson(), // ThirdNo = request.ThirdNo, @@ -178,13 +185,13 @@ public class OrderService : IOrderService { if (!allowNoBalance) { - var gainfoMeal = await _dbContext.Queryable().Where(x => x.UserNo == balanceRecord.UserNo).SingleAsync(); + var gainfoMeal = await _dbContext.Queryable().Where(x => x.UserNo == balanceRecord.UserNo).SingleAsync(); //金额+- gainfoMeal.Balance += balanceRecord.Amount; (gainfoMeal.Balance < 0).ThrowIfTrue("余额不足,无法消费"); - await _dbContext.Updateable() + await _dbContext.Updateable() .SetColumns(x => x.Balance == gainfoMeal.Balance) .SetColumns(x => x.Version == x.Version + 1) .SetColumns(x => x.ModifiedTime == DateTime.Now) @@ -230,13 +237,13 @@ public class OrderService : IOrderService /// /// /// - private async Task Verify(BalanceRecordEntity order) + private async Task Verify(LocalBalanceRecordEntity order) { var beginOfDay = DateTime.Today; var endOfDay = beginOfDay.AddDays(1).AddSeconds(-1); //今天所有消费记录 - var todayAllBalanceRecords = await _dbContext.Queryable() + var todayAllBalanceRecords = await _dbContext.Queryable() .Where(x => x.UserNo == order.UserNo) .Where(x => x.Type == BalanceRecordTypeEnumType.Out) .Where(x => x.Status == BalanceRecordStatusEnumType.Valid) @@ -244,7 +251,7 @@ public class OrderService : IOrderService .ToListAsync(); //超市营业点 - var supermarketCodes = await _dbContext.Queryable() + var supermarketCodes = await _dbContext.Queryable() .Where(x => x.Catalog == CanteenBusinessPlaceCatalogEnumType.Supermarket) .Select(x => x.Code) .ToListAsync(); diff --git a/Src/Dlhis.Middle/appsettings.json b/Src/Dlhis.Middle/appsettings.json index 3b25033b..aa5b406e 100644 --- a/Src/Dlhis.Middle/appsettings.json +++ b/Src/Dlhis.Middle/appsettings.json @@ -12,8 +12,9 @@ "SlowSqlTime": 1, "EnableSqlLog": false }, + "Authority": "https://face.jinkosolar.com", "Factory": "", - "HostName": "", + "HostName": "face.jinkosolar.com", "AllowedHosts": "*", "Serilog": { "MinimumLevel": {