fix 多钱包调整

This commit is contained in:
2024-11-08 16:36:10 +08:00
parent 36bfd2e8f6
commit 1a0ba28249
36 changed files with 418 additions and 434 deletions

View File

@@ -1,5 +1,4 @@
using Dlhis.Canteen.Dishes.Request; using Dlhis.Canteen.Order.Request;
using Dlhis.Canteen.Order.Request;
using Dlhis.Canteen.Order.Response; using Dlhis.Canteen.Order.Response;
using Dlhis.Canteen.Order.Services; using Dlhis.Canteen.Order.Services;
using Kurisu.AspNetCore.DataAccess.Dto; using Kurisu.AspNetCore.DataAccess.Dto;
@@ -35,8 +34,7 @@ public class OrdersController : ControllerBase
[IgnoreTenant] [IgnoreTenant]
public async Task<DefaultApiResult<OrderResultResponse>> AddAsync(OrderAddRequest request) public async Task<DefaultApiResult<OrderResultResponse>> AddAsync(OrderAddRequest request)
{ {
var result = await _orderService.AddAsync(request); return await _orderService.AddAsync(request);
return result.Response;
} }
/// <summary> /// <summary>
@@ -49,8 +47,7 @@ public class OrdersController : ControllerBase
[IgnoreTenant] [IgnoreTenant]
public async Task<DefaultApiResult<OrderResultResponse>> DeviceConsume(OrderAddRequest request) public async Task<DefaultApiResult<OrderResultResponse>> DeviceConsume(OrderAddRequest request)
{ {
var result = await _orderService.AddAsync(request); return await _orderService.AddAsync(request);
return result.Response;
} }
/// <summary> /// <summary>

View File

@@ -16,15 +16,18 @@ public class BalanceRepository : IBalanceRepository
public async Task<decimal> GetBalanceAsync(string userCode, string factory) public async Task<decimal> GetBalanceAsync(string userCode, string factory)
{ {
var factories = await GetWalletFactoriesAsync(factory); var queryable = _dbContext.Queryable<BalanceRecordEntity>()
return await _dbContext.Queryable<BalanceRecordEntity>()
.Where(x => x.UserCode == userCode && x.Status == BalanceRecordStatusEnumType.Valid) .Where(x => x.UserCode == userCode && x.Status == BalanceRecordStatusEnumType.Valid)
.Where(x => factories.Contains(x.TenantId)) .Where(x => x.Source != BalanceRecordSourceEnumType.EMealVoucher);
.Where(x => x.Source != BalanceRecordSourceEnumType.EMealVoucher)
.SumAsync(x => x.Amount);
}
if (!factory.IsPresent())
{
return await queryable.SumAsync(x => x.Amount);
}
var factories = await GetWalletFactoriesAsync(factory);
return await queryable.Where(x => factories.Contains(x.TenantId)).SumAsync(x => x.Amount);
}
public async Task<List<BalanceRecordEntity>> GetBalanceRecordsAsync(string userCode, string factory, DateTime startTime, DateTime endTime) public async Task<List<BalanceRecordEntity>> GetBalanceRecordsAsync(string userCode, string factory, DateTime startTime, DateTime endTime)
{ {

View File

@@ -32,4 +32,13 @@ public class OrderResultResponse
/// 余额 /// 余额
/// </summary> /// </summary>
public string Balance => CurrentBalance.ToString("f2"); public string Balance => CurrentBalance.ToString("f2");
[JsonIgnore]
public decimal CurrentTotalBalance { get; set; }
/// <summary>
/// 总余额
/// </summary>
public string TotalBalance => CurrentTotalBalance.ToString("f2");
} }

View File

@@ -1,19 +1,20 @@
using Dlhis.Canteen.Order.Request; using Dlhis.Canteen.Order.Request;
using Dlhis.Canteen.Order.Response; using Dlhis.Canteen.Order.Response;
using Kurisu.AspNetCore.DataAccess.Dto; using Kurisu.AspNetCore.DataAccess.Dto;
using Kurisu.AspNetCore.UnifyResultAndValidation;
namespace Dlhis.Canteen.Order.Services; namespace Dlhis.Canteen.Order.Services;
public interface IOrderService : IScopeDependency public interface IOrderService : IScopeDependency
{ {
Task<OrderResultResponseWithLogs> AddAsync(OrderAddRequest request); Task<DefaultApiResult<OrderResultResponse>> AddAsync(OrderAddRequest request);
/// <summary> /// <summary>
/// 设备消费 /// 设备消费
/// </summary> /// </summary>
/// <param name="request"></param> /// <param name="request"></param>
/// <returns></returns> /// <returns></returns>
Task<OrderResultResponseWithLogs> DeviceConsume(OrderAddRequest request); Task<DefaultApiResult<OrderResultResponse>> DeviceConsume(OrderAddRequest request);
Task<Pagination<OrderAggregationResponse>> GetOrdersAggregationAsync(PageDto page); Task<Pagination<OrderAggregationResponse>> GetOrdersAggregationAsync(PageDto page);
} }

View File

@@ -57,7 +57,7 @@ public class OrderService : IOrderService
/// </summary> /// </summary>
/// <param name="request"></param> /// <param name="request"></param>
/// <returns></returns> /// <returns></returns>
public async Task<OrderResultResponseWithLogs> DeviceConsume(OrderAddRequest request) public async Task<DefaultApiResult<OrderResultResponse>> DeviceConsume(OrderAddRequest request)
{ {
return await AddAsync(request); return await AddAsync(request);
} }
@@ -67,7 +67,7 @@ public class OrderService : IOrderService
/// </summary> /// </summary>
/// <param name="request"></param> /// <param name="request"></param>
/// <returns></returns> /// <returns></returns>
public async Task<OrderResultResponseWithLogs> AddAsync(OrderAddRequest request) public async Task<DefaultApiResult<OrderResultResponse>> AddAsync(OrderAddRequest request)
{ {
await SetDeviceIpInfoAsync(request.DeviceUniqueNo); await SetDeviceIpInfoAsync(request.DeviceUniqueNo);
@@ -75,10 +75,7 @@ public class OrderService : IOrderService
handler.Acquired.ThrowIfFalse("请勿频繁消费"); handler.Acquired.ThrowIfFalse("请勿频繁消费");
var gainfo = await GetUserAsync(request); var gainfo = await GetUserAsync(request);
return new OrderResultResponseWithLogs return await ToDoAsync(request, gainfo);
{
Response = await ToDoAsync(request, gainfo)
};
} }
@@ -256,7 +253,7 @@ public class OrderService : IOrderService
/// </summary> /// </summary>
/// <param name="request"></param> /// <param name="request"></param>
/// <param name="gainfo"></param> /// <param name="gainfo"></param>
private async Task HandleOfflineAsync(ConsumeRequest request, GainfoEntity gainfo) private async Task OfflineHandleAsync(ConsumeRequest request, GainfoEntity gainfo)
{ {
var bindingInfo = await _dbContext.Queryable<DeviceRegistryEntity>() var bindingInfo = await _dbContext.Queryable<DeviceRegistryEntity>()
.LeftJoin<BusinessPlaceEntity>((x, b) => x.BusinessPlaceCode == b.Code) .LeftJoin<BusinessPlaceEntity>((x, b) => x.BusinessPlaceCode == b.Code)
@@ -273,11 +270,13 @@ public class OrderService : IOrderService
var shifts = await _dbContext.Queryable<BusinessShiftMealRelationshipEntity>() var shifts = await _dbContext.Queryable<BusinessShiftMealRelationshipEntity>()
.Where(x => x.ShiftCode == bindingInfo.ShiftCode).ToListAsync(); .Where(x => x.ShiftCode == bindingInfo.ShiftCode).ToListAsync();
var shift = shifts.FirstOrDefault(x => new TimePeriod var shift = shifts.FirstOrDefault(x =>
{ new TimePeriod
Start = x.Start, {
End = x.End Start = x.Start,
}.IsPresent(request.CostDateTime)); End = x.End
}.IsPresent(request.CostDateTime)
);
var costDate = await _canteenSettingService.GetCostDateAsync(bindingInfo.Factory, request.CostDateTime); var costDate = await _canteenSettingService.GetCostDateAsync(bindingInfo.Factory, request.CostDateTime);
@@ -317,7 +316,7 @@ public class OrderService : IOrderService
TenantId = bindingInfo.Factory, TenantId = bindingInfo.Factory,
}; };
await _dbContext.InsertReturnIdentityAsync(order); await _dbContext.InsertAsync(order);
var balanceRecord = order.Adapt<BalanceRecordEntity>(); var balanceRecord = order.Adapt<BalanceRecordEntity>();
await _dbContext.InsertAsync(balanceRecord); await _dbContext.InsertAsync(balanceRecord);
} }
@@ -391,16 +390,36 @@ public class OrderService : IOrderService
//获取餐券余额 //获取餐券余额
var data = await _dbContext.Queryable<EMealVoucherEntity>() var data = await _dbContext.Queryable<EMealVoucherEntity>()
.LeftJoin<EMealVoucherDetailEntity>((x, d) => x.No == d.No) .LeftJoin<EMealVoucherDetailEntity>((x, d) => x.No == d.No)
.LeftJoin<GainfoEntity>((x, d, u) => u.IdNo == d.IdNo)
.Where((x, d) => x.No == no && d.IdNo == idNo) .Where((x, d) => x.No == no && d.IdNo == idNo)
.Select((x, d, u) => new OrderResultResponse .Select((x, d) => new OrderResultResponse
{ {
UserCode = u.Code, UserCode = d.IdNo,
UserName = u.Name, UserName = d.Name,
CurrentBalance = d.Balance CurrentBalance = d.Balance
}) })
.SingleAsync(); .SingleAsync();
try
{
var v = data.UserCode ?? string.Empty;
var vc = v.ToCharArray().Reverse().ToList();
for (int i = 0; i < vc.Count; i++)
{
if (i + 1 > 4)
{
vc[i] = '*';
}
}
vc.Reverse();
data.UserCode = string.Join("", vc);
}
catch (Exception e)
{
//ignore
}
return new DefaultApiResult<OrderResultResponse> return new DefaultApiResult<OrderResultResponse>
{ {
Code = msg.IsPresent() ? ApiStateCode.Error : ApiStateCode.Success, Code = msg.IsPresent() ? ApiStateCode.Error : ApiStateCode.Success,
@@ -409,6 +428,30 @@ public class OrderService : IOrderService
}; };
} }
private async Task OfflineConsumeAsync(OrderAddRequest request, GainfoEntity gainfo)
{
try
{
if (request.ThirdNo.IsPresent() && await ToCheckAbleOfflineConsumeAsync(request.ThirdNo))
{
await using var handler = await _redisCache.LockAsync($"consume:device-offline:{request.ThirdNo}");
if (handler.Acquired)
{
await OfflineHandleAsync(request.Adapt<ConsumeRequest>(), gainfo);
}
}
else
{
await OfflineHandleAsync(request.Adapt<ConsumeRequest>(), gainfo);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "离线入库错误: 订单号:{no} .{message}", request.ThirdNo, ex.Message);
}
}
/// <summary> /// <summary>
/// 分支处理在线/离线消费 /// 分支处理在线/离线消费
/// </summary> /// </summary>
@@ -423,27 +466,7 @@ public class OrderService : IOrderService
return await EMealVoucherConsumeAsync(request, gainfo); return await EMealVoucherConsumeAsync(request, gainfo);
if (request.IsOffline) if (request.IsOffline)
{ await OfflineConsumeAsync(request, gainfo);
try
{
if (request.ThirdNo.IsPresent() && await ToCheckAbleOfflineConsumeAsync(request.ThirdNo))
{
await using var handler = await _redisCache.LockAsync($"consume:device-offline:{request.ThirdNo}");
if (handler.Acquired)
{
await HandleOfflineAsync(request.Adapt<ConsumeRequest>(), gainfo);
}
}
else
{
await HandleOfflineAsync(request.Adapt<ConsumeRequest>(), gainfo);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "离线入库错误: 订单号:{no} .{message}", request.ThirdNo, ex.Message);
}
}
else else
{ {
try try
@@ -454,52 +477,17 @@ public class OrderService : IOrderService
{ {
message = ex.Message; message = ex.Message;
} }
//获取餐券余额
var data = await DbContext.Queryable<EMealVoucherEntity>()
.LeftJoin<EMealVoucherDetailEntity>((x, d) => x.No == d.No)
.Where((x, d) => x.No == no && d.IdNo == idNo)
.Select((x, d) => new OrderResultResponse
{
UserCode = d.IdNo,
UserName = d.Name,
CurrentBalance = d.Balance
})
.SingleAsync();
try
{
var v = data.UserCode ?? string.Empty;
var vc = v.ToCharArray().Reverse().ToList();
for (int i = 0; i < vc.Count; i++)
{
if (i + 1 > 4)
{
vc[i] = '*';
}
}
vc.Reverse();
data.UserCode = string.Join("", vc);
}
catch (Exception e)
{
}
return new DefaultApiResult<OrderResultResponse>
{
Code = message.IsPresent() ? ApiStateCode.Error : ApiStateCode.Success,
Data = data,
Msg = message
};
} }
var currentFactory = await _dbContext.Queryable<DeviceEntity>().Where(x => x.UniqueNo == request.DeviceUniqueNo).Select(x => x.TenantId).SingleAsync(); //获取人员信息
var balance = await _balanceRepository.GetBalanceAsync(gainfo.Code, currentFactory); var currentFactory = await _dbContext.Queryable<DeviceEntity>()
.Where(x => x.UniqueNo == request.DeviceUniqueNo)
.Select(x => x.TenantId)
.SingleAsync();
var balance = await _balanceRepository.GetBalanceAsync(gainfo.Code, currentFactory);
var totalBalance = await _balanceRepository.GetBalanceAsync(gainfo.Code, string.Empty);
//获取人员余额
var data = await _dbContext.Queryable<GainfoEntity>() var data = await _dbContext.Queryable<GainfoEntity>()
.LeftJoin<DepartmentEntity>((x, department) => x.DepartmentNo == department.No) .LeftJoin<DepartmentEntity>((x, department) => x.DepartmentNo == department.No)
.Where(x => x.Code == gainfo.Code) .Where(x => x.Code == gainfo.Code)
@@ -509,7 +497,9 @@ public class OrderService : IOrderService
UserName = x.Name, UserName = x.Name,
DepartmentNo = x.DepartmentNo, DepartmentNo = x.DepartmentNo,
DepartmentName = department.Name, DepartmentName = department.Name,
CurrentBalance = balance
CurrentBalance = balance,
CurrentTotalBalance = totalBalance
}) })
.SingleAsync(); .SingleAsync();
@@ -555,6 +545,7 @@ public class OrderService : IOrderService
(nowDate >= current.Start && nowDate <= current.End).ThrowIfFalse("餐券已过期"); (nowDate >= current.Start && nowDate <= current.End).ThrowIfFalse("餐券已过期");
var eMealVoucher = await _dbContext.Queryable<EMealVoucherEntity>().Where(x => x.No == no).SingleAsync(); var eMealVoucher = await _dbContext.Queryable<EMealVoucherEntity>().Where(x => x.No == no).SingleAsync();
var checkData = await PreCheckAsync(request.DeviceUniqueNo, request.CostDateTime); var checkData = await PreCheckAsync(request.DeviceUniqueNo, request.CostDateTime);

View File

@@ -44,7 +44,13 @@ public interface ICanteenSettingService : IScopeDependency
Task<TimeOnly?> GetBusinessSwitchTimeAsync(string tenantId); Task<TimeOnly?> GetBusinessSwitchTimeAsync(string tenantId);
Task<T> GetSettingAsync<T>(CanteenSettingItemEnumType item, string factory, string userNo , T defaultValue = default); Task<T> GetSettingAsync<T>(CanteenSettingItemEnumType item, string factory, string userNo, T defaultValue = default);
/// <summary>
/// 获取消费日期
/// </summary>
/// <param name="factory"></param>
/// <param name="costDateTime"></param>
/// <returns></returns>
Task<DateTime> GetCostDateAsync(string factory, DateTime costDateTime); Task<DateTime> GetCostDateAsync(string factory, DateTime costDateTime);
} }

View File

@@ -1,37 +0,0 @@
using System.Text;
using Dlhis.Middle.Response;
using Kurisu.AspNetCore.UnifyResultAndValidation;
using Kurisu.RemoteCall.Abstractions;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
namespace Dlhis.Middle.Api
{
public class FaceBearerTokenHandler : IAuthTokenHandler
{
/// <inheritdoc />
public string GetToken(IServiceProvider serviceProvider)
{
var cache = serviceProvider.GetService<IMemoryCache>();
if (cache.TryGetValue("token", out string token)) return "Bearer " + token;
var httpClient = serviceProvider.GetService<IHttpClientFactory>().CreateClient("face");
var hostName = serviceProvider.GetService<IConfiguration>().GetSection("HostName").Value;
var tokenResponse = httpClient.PostAsync($"https://{hostName}/api/user/oauth2/clientgrant", new StringContent(JsonConvert.SerializeObject(new
{
ClientId = "e509c2aaa98e300c",
ClientSecret = "6184d8979fc48105"
}), Encoding.UTF8, "application/json")).GetAwaiter().GetResult();
var tokenJson = tokenResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult();
var tokenInfo = JsonConvert.DeserializeObject<DefaultApiResult<TokenResponse>>(tokenJson);
token = tokenInfo.Data.AccessToken;
cache.Set("token", token, TimeSpan.FromSeconds(tokenInfo.Data.Expires));
return token;
}
}
}

View File

@@ -0,0 +1,23 @@
using Kurisu.RemoteCall.Abstractions;
namespace Dlhis.Middle.Apis.Handlers;
public class ClientHandler : IHttpRemoteCallPolicyHandler
{
/// <inheritdoc />
public IHttpClientBuilder ConfigureHttpClient(IHttpClientBuilder builder)
{
builder.ConfigureHttpClient(client => { client.Timeout = TimeSpan.FromSeconds(10); }).ConfigurePrimaryHttpMessageHandler(() =>
{
var handler = new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual,
ServerCertificateCustomValidationCallback = (_, _, _, _) => true
};
return handler;
});
return builder;
}
}

View File

@@ -0,0 +1,14 @@
using Dlhis.Middle.Services;
using Kurisu.RemoteCall.Abstractions;
namespace Dlhis.Middle.Apis.Handlers;
public class FaceBearerTokenHandler : IAsyncAuthTokenHandler
{
/// <inheritdoc />
public async Task<string> GetTokenAsync(IServiceProvider serviceProvider)
{
var tokenService = serviceProvider.GetService<ITokenService>();
return await tokenService.AuthAsync();
}
}

View File

@@ -1,11 +1,13 @@
using Dlhis.Middle.Apis.Handlers;
using Dlhis.Middle.Request; using Dlhis.Middle.Request;
using Dlhis.Middle.Response; using Dlhis.Middle.Response;
using Kurisu.AspNetCore.UnifyResultAndValidation; using Kurisu.AspNetCore.UnifyResultAndValidation;
using Kurisu.RemoteCall.Attributes; using Kurisu.RemoteCall.Attributes;
namespace Dlhis.Middle.Api; namespace Dlhis.Middle.Apis;
[EnableRemoteClient(Name = "face-service", BaseUrl = "${Authority}")] [RequestLog]
[EnableRemoteClient(Name = "face-service", BaseUrl = "${BaseUrl}", PolicyHandler = typeof(ClientHandler))]
public interface IAssignmentDeviceTaskApi public interface IAssignmentDeviceTaskApi
{ {
[Auth(TokenHandler = typeof(FaceBearerTokenHandler))] [Auth(TokenHandler = typeof(FaceBearerTokenHandler))]

View File

@@ -1,26 +1,29 @@
using Dlhis.Middle.Apis.Handlers;
using Dlhis.Middle.Entities;
using Dlhis.Middle.Request; using Dlhis.Middle.Request;
using Dlhis.Middle.Response; using Dlhis.Middle.Response;
using Kurisu.AspNetCore.DataAccess.Dto; using Kurisu.AspNetCore.DataAccess.Dto;
using Kurisu.AspNetCore.UnifyResultAndValidation; using Kurisu.AspNetCore.UnifyResultAndValidation;
using Kurisu.RemoteCall.Attributes; using Kurisu.RemoteCall.Attributes;
namespace Dlhis.Middle.Api; namespace Dlhis.Middle.Apis;
[EnableRemoteClient(Name = "face-service", BaseUrl = "${Authority}")] [RequestLog]
[EnableRemoteClient(Name = "face-service", BaseUrl = "${BaseUrl}", PolicyHandler = typeof(ClientHandler))]
public interface ICanteenApi public interface ICanteenApi
{ {
[Auth(TokenHandler = typeof(FaceBearerTokenHandler))] [Auth(TokenHandler = typeof(FaceBearerTokenHandler))]
[Post("/api/canteen/orders")] [Post("/api/canteen/orders")]
Task<DefaultApiResult<OrderResultResponse>> AddAsync(OrderAddRequest request); Task<DefaultApiResult<OrderResultResponse>> AddAsync(OrderAddRequest request);
[Auth(TokenHandler = typeof(FaceBearerTokenHandler))]
[Post("/api/canteen/middle/upload")]
Task<DefaultApiResult<OrderResultResponse>> UploadAsync(LocalBalanceRecordEntity entity);
[Auth(TokenHandler = typeof(FaceBearerTokenHandler))] [Auth(TokenHandler = typeof(FaceBearerTokenHandler))]
[Post("/api/canteen/balances/device/page")] [Post("/api/canteen/balances/device/page")]
Task<DefaultApiResult<Pagination<BalanceRecordDeviceResponse>>> GetPageByDeviceUniqueNoAsync(BalanceRecordPageByDeviceUniqueNoRequest request); Task<DefaultApiResult<Pagination<BalanceRecordDeviceResponse>>> GetPageByDeviceUniqueNoAsync(BalanceRecordPageByDeviceUniqueNoRequest request);
[Auth(TokenHandler = typeof(FaceBearerTokenHandler))] [Auth(TokenHandler = typeof(FaceBearerTokenHandler))]
[Post("/api/canteen/balances/refund")] [Post("/api/canteen/balances/refund")]
Task<DefaultApiResult<OrderResultResponse>> RefundAsync(BalanceRecordRefundRequest request); Task<DefaultApiResult<OrderResultResponse>> RefundAsync(BalanceRecordRefundRequest request);

View File

@@ -0,0 +1,22 @@
using Dlhis.Middle.Apis.Handlers;
using Dlhis.Middle.Request;
using Dlhis.Middle.Response;
using Kurisu.AspNetCore.UnifyResultAndValidation;
using Kurisu.RemoteCall.Attributes;
namespace Dlhis.Middle.Apis;
[EnableRemoteClient(Name = "face-service", BaseUrl = "${BaseUrl}", PolicyHandler = typeof(ClientHandler))]
public interface ICommonApi
{
[Post("/api/user/oauth2/clientgrant")]
public Task<DefaultApiResult<TokenResponse>> AuthAsync(object request);
[Auth(TokenHandler = typeof(FaceBearerTokenHandler))]
[Post("/api/common/middles/healthz")]
public Task<DefaultApiResult> CheckHealthAsync(HealthCheckRequest request);
[Auth(TokenHandler = typeof(FaceBearerTokenHandler))]
[Get("/api/common/SysData/download")]
public Task<DefaultApiResult<List<T>>> DownloadAsync<T>(string factory, string tableName);
}

View File

@@ -1,46 +1,43 @@
using System.Net.Http.Headers; using System.Net.Http.Headers;
using System.Text; using System.Text;
using Dlhis.Middle.Apis;
using Dlhis.Middle.Entities; using Dlhis.Middle.Entities;
using Dlhis.Middle.Response; using Dlhis.Middle.Response;
using Dlhis.Middle.Services;
using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services;
using Kurisu.AspNetCore.UnifyResultAndValidation; using Kurisu.AspNetCore.UnifyResultAndValidation;
using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.AspNetCore.Utils.Extensions;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
namespace Dlhis.Middle.Services.Implements; namespace Dlhis.Middle.BackgroundServices;
public class BalanceRecordCheckBackgroundService : BackgroundService public class BalanceRecordUploadBackgroundService : BackgroundService
{ {
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly ILogger<BalanceRecordCheckBackgroundService> _logger; private readonly ILogger<BalanceRecordUploadBackgroundService> _logger;
private readonly IMemoryCache _memoryCache; private readonly IWebHostEnvironment _environment;
private readonly IHttpClientFactory _httpClientFactory; private readonly ISettingService _settingService;
private readonly ICanteenApi _canteenApi;
private readonly IHealthCheckService _healthCheckService; private readonly IHealthCheckService _healthCheckService;
private readonly string _factory; public BalanceRecordUploadBackgroundService(IServiceProvider serviceProvider,
private readonly string _hostName; ILogger<BalanceRecordUploadBackgroundService> logger,
IWebHostEnvironment environment,
public BalanceRecordCheckBackgroundService(IServiceProvider serviceProvider, ISettingService settingService,
ILogger<BalanceRecordCheckBackgroundService> logger, ICanteenApi canteenApi,
IMemoryCache memoryCache, IHealthCheckService healthCheckService)
IHttpClientFactory httpClientFactory,
IHealthCheckService healthCheckService,
IConfiguration configuration)
{ {
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
_logger = logger; _logger = logger;
_memoryCache = memoryCache; _environment = environment;
_httpClientFactory = httpClientFactory; _settingService = settingService;
_canteenApi = canteenApi;
_healthCheckService = healthCheckService; _healthCheckService = healthCheckService;
_factory = configuration.GetSection("Factory").Value;
_hostName = configuration.GetSection("HostName").Value;
} }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
while (!stoppingToken.IsCancellationRequested) while (!stoppingToken.IsCancellationRequested && _environment.IsProduction())
{ {
if (!_healthCheckService.GetIsOffline()) if (!_healthCheckService.GetIsOffline())
{ {
@@ -50,7 +47,8 @@ public class BalanceRecordCheckBackgroundService : BackgroundService
var db = scope.ServiceProvider.GetService<IDbContext>(); var db = scope.ServiceProvider.GetService<IDbContext>();
var ds = scope.ServiceProvider.GetService<IDataDownloadService>(); var ds = scope.ServiceProvider.GetService<IDataDownloadService>();
foreach (var factory in _factory.Split(',')) var factories = _settingService.GetFactories();
foreach (var factory in factories)
{ {
try try
{ {
@@ -64,7 +62,7 @@ public class BalanceRecordCheckBackgroundService : BackgroundService
await db.Client.Ado.BeginTranAsync(); await db.Client.Ado.BeginTranAsync();
try try
{ {
var r = await UploadAsync(balanceRecord); var r = await _canteenApi.UploadAsync(balanceRecord);
if (r.Code != ApiStateCode.Success) if (r.Code != ApiStateCode.Success)
{ {
throw new Exception(r.Msg); throw new Exception(r.Msg);
@@ -108,62 +106,23 @@ public class BalanceRecordCheckBackgroundService : BackgroundService
&& !_healthCheckService.GetIsOffline() && !_healthCheckService.GetIsOffline()
&& !await db.Queryable<LocalBalanceRecordEntity>().Where(x => !x.IsSync).AnyAsync()) && !await db.Queryable<LocalBalanceRecordEntity>().Where(x => !x.IsSync).AnyAsync())
{ {
await ds.InitAsync(_factory); await ds.InitAsync(factories);
} }
else if (now is { Hour: 15, Minute: 1 } else if (now is { Hour: 15, Minute: 1 }
&& !_healthCheckService.GetIsOffline() && !_healthCheckService.GetIsOffline()
&& !await db.Queryable<LocalBalanceRecordEntity>().Where(x => !x.IsSync).AnyAsync()) && !await db.Queryable<LocalBalanceRecordEntity>().Where(x => !x.IsSync).AnyAsync())
{ {
await ds.InitAsync(_factory); await ds.InitAsync(factories);
} }
else if (now is { Hour: 20, Minute: 1 } else if (now is { Hour: 20, Minute: 1 }
&& !_healthCheckService.GetIsOffline() && !_healthCheckService.GetIsOffline()
&& !await db.Queryable<LocalBalanceRecordEntity>().Where(x => !x.IsSync).AnyAsync()) && !await db.Queryable<LocalBalanceRecordEntity>().Where(x => !x.IsSync).AnyAsync())
{ {
await ds.InitAsync(_factory); await ds.InitAsync(factories);
} }
} }
await Task.Delay(60 * 1000, stoppingToken); await Task.Delay(60 * 1000, stoppingToken);
} }
} }
public async Task<DefaultApiResult<OrderResultResponse>> 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<DefaultApiResult<OrderResultResponse>>();
}
private async Task<string> 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 = "e509c2aaa98e300c",
ClientSecret = "6184d8979fc48105"
}), Encoding.UTF8, "application/json"));
var tokenJson = await tokenResponse.Content.ReadAsStringAsync();
var tokenInfo = JsonConvert.DeserializeObject<DefaultApiResult<TokenResponse>>(tokenJson);
token = tokenInfo.Data.AccessToken;
_memoryCache.Set("token", token, TimeSpan.FromSeconds(tokenInfo.Data.Expires));
return token;
}
} }

View File

@@ -1,4 +1,6 @@
namespace Dlhis.Middle.Services.Implements; using Dlhis.Middle.Services;
namespace Dlhis.Middle.BackgroundServices;
public class HealthCheckBackgroundService : BackgroundService public class HealthCheckBackgroundService : BackgroundService
{ {

View File

@@ -1,5 +1,5 @@
using Dlhis.Entity.Device.Enums; using Dlhis.Entity.Device.Enums;
using Dlhis.Middle.Api; using Dlhis.Middle.Apis;
using Dlhis.Middle.Request; using Dlhis.Middle.Request;
using Dlhis.Middle.Response; using Dlhis.Middle.Response;
using Dlhis.Middle.Services; using Dlhis.Middle.Services;

View File

@@ -1,4 +1,4 @@
using Dlhis.Middle.Api; using Dlhis.Middle.Apis;
using Dlhis.Middle.Request; using Dlhis.Middle.Request;
using Dlhis.Middle.Response; using Dlhis.Middle.Response;
using Dlhis.Middle.Services; using Dlhis.Middle.Services;

View File

@@ -1,4 +1,5 @@
using Dlhis.Middle.Filters; using Dlhis.Middle.Filters;
using Dlhis.Middle.Services;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -12,14 +13,13 @@ namespace Dlhis.Middle.Controllers;
public class GainfosController : ControllerBase public class GainfosController : ControllerBase
{ {
private readonly IHttpClientFactory _httpClientFactory; private readonly IHttpClientFactory _httpClientFactory;
private readonly IConfiguration _configuration; private readonly ISettingService _settingService;
private readonly ILogger<GainfosController> _logger;
public GainfosController(IHttpClientFactory httpClientFactory, IConfiguration configuration, ILogger<GainfosController> logger) public GainfosController(IHttpClientFactory httpClientFactory,
ISettingService settingService)
{ {
_httpClientFactory = httpClientFactory; _httpClientFactory = httpClientFactory;
_configuration = configuration; _settingService = settingService;
_logger = logger;
} }
/// <summary> /// <summary>
@@ -32,14 +32,11 @@ public class GainfosController : ControllerBase
[HttpGet("face")] [HttpGet("face")]
public async Task<IActionResult> GetFaceAsync(string token) public async Task<IActionResult> GetFaceAsync(string token)
{ {
var hostName = _configuration.GetSection("HostName").Value; var httpClient = _httpClientFactory.CreateClient();
var httpClient = _httpClientFactory.CreateClient("face");
_logger.LogInformation("token:{token}", token);
try try
{ {
var faceBytes = await httpClient.GetByteArrayAsync($"https://{hostName}/api/basic/gainfos/face?token={token}"); var faceBytes = await httpClient.GetByteArrayAsync($"{_settingService.GetBaseUrl()}/api/basic/gainfos/face?token={token}");
if (faceBytes.Length == 0) if (faceBytes.Length == 0)
{ {

View File

@@ -1,4 +1,5 @@
using System.Text; using System.Text;
using Dlhis.Middle.Apis;
using Dlhis.Middle.Request; using Dlhis.Middle.Request;
using Dlhis.Middle.Services; using Dlhis.Middle.Services;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@@ -13,23 +14,20 @@ namespace Dlhis.Middle.Controllers;
[ApiController] [ApiController]
public class OAuth2Controller : ControllerBase public class OAuth2Controller : ControllerBase
{ {
private readonly IHttpClientFactory _httpClientFactory; private readonly ICommonApi _commonApi;
private readonly IConfiguration _configuration;
private readonly IHealthCheckService _healthCheckService; private readonly IHealthCheckService _healthCheckService;
public OAuth2Controller(IHttpClientFactory httpClientFactory, IConfiguration configuration, IHealthCheckService healthCheckService) public OAuth2Controller(
ICommonApi commonApi,
IHealthCheckService healthCheckService)
{ {
_httpClientFactory = httpClientFactory; _commonApi = commonApi;
_configuration = configuration;
_healthCheckService = healthCheckService; _healthCheckService = healthCheckService;
} }
[HttpPost("clientgrant")] [HttpPost("clientgrant")]
public async Task<object> ClientGrant(ClientGrantRequest request) public async Task<object> ClientGrant(ClientGrantRequest request)
{ {
var authority = _configuration.GetSection("Authority").Value;
var httpClient = _httpClientFactory.CreateClient("face");
if (_healthCheckService.GetIsOffline()) if (_healthCheckService.GetIsOffline())
{ {
return new return new
@@ -41,9 +39,12 @@ public class OAuth2Controller : ControllerBase
}; };
} }
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 r = await _commonApi.AuthAsync(new
var response = await httpClient.PostAsync($"{authority}/oauth2/connect/token", content); {
var result = await response.Content.ReadAsStringAsync(); request.ClientId,
return JObject.Parse(result); request.ClientSecret
});
return r.Data;
} }
} }

View File

@@ -17,10 +17,23 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Kurisu.RemoteCall" Version="0.4.2" /> <PackageReference Include="Kurisu.RemoteCall" Version="0.4.4" />
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="8.0.6" /> <PackageReference Include="Microsoft.Data.Sqlite.Core" Version="8.0.6" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" /> <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlcipher" Version="2.1.8" /> <PackageReference Include="SQLitePCLRaw.bundle_e_sqlcipher" Version="2.1.8" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Update="appsettings.Development.json">
<DependentUpon>appsettings.json</DependentUpon>
</Content>
</ItemGroup>
<ItemGroup>
<Compile Remove="Api\FaceBearerTokenHandler.cs" />
<Compile Remove="Api\IAssignmentDeviceTaskApi.cs" />
<Compile Remove="Services\Implements\BalanceRecordCheckBackgroundService.cs" />
<Compile Remove="Services\Implements\HealthCheckBackgroundService.cs" />
</ItemGroup>
</Project> </Project>

View File

@@ -1,4 +1,3 @@
using Dlhis.Entity.Basic.Entity;
using Kurisu.AspNetCore.DataAccess.Entity; using Kurisu.AspNetCore.DataAccess.Entity;
using Kurisu.AspNetCore.DataAccess.SqlSugar; using Kurisu.AspNetCore.DataAccess.SqlSugar;
using SqlSugar; using SqlSugar;
@@ -12,7 +11,7 @@ namespace Dlhis.Middle.Entities
/// <summary> /// <summary>
/// 就餐人员 /// 就餐人员
/// </summary> /// </summary>
[SugarTable(TableName = "GainfoMeal", TableDescription = "就餐人员")] [SugarTable(TableName = "GainfoMeal", TableDescription = "就餐人员")]
public class GainfoMealEntity : SugarBaseEntity, IVersion public class GainfoMealEntity : SugarBaseEntity, IVersion
@@ -24,25 +23,13 @@ namespace Dlhis.Middle.Entities
public string UserCode { get; set; } public string UserCode { get; set; }
/// <summary> /// <summary>
/// 余额 /// 余额
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "余额")] [SugarColumn(ColumnDescription = "余额")]
public decimal Balance { get; set; } public decimal Balance { get; set; }
/// <summary> /// <summary>
/// 余额(充值) /// 版本号
/// </summary>
[SugarColumn(ColumnDescription = "余额(充值)")]
public decimal BalanceOfRecharge { get; set; }
/// <summary>
/// 余额(餐补)
/// </summary>
[SugarColumn(ColumnDescription = "余额(餐补)")]
public decimal BalanceOfSubsidies { get; set; }
/// <summary>
/// 版本号
/// </summary> /// </summary>
[SugarColumn(ColumnDescription = "版本号")] [SugarColumn(ColumnDescription = "版本号")]
public int Version { get; set; } public int Version { get; set; }

View File

@@ -1,5 +1,5 @@
using Dlhis.Middle.BackgroundServices;
using Dlhis.Middle.Services; using Dlhis.Middle.Services;
using Dlhis.Middle.Services.Implements;
using Kurisu.AspNetCore; using Kurisu.AspNetCore;
using Kurisu.AspNetCore.Startup; using Kurisu.AspNetCore.Startup;
using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Logging;
@@ -25,19 +25,10 @@ public class Startup : DefaultStartup
{ {
base.ConfigureServices(services); base.ConfigureServices(services);
services.AddRemoteCall(App.ActiveTypes); services.AddHostedService<BalanceRecordUploadBackgroundService>();
services.AddHttpClient("face", client => { client.Timeout = TimeSpan.FromSeconds(30); }).ConfigurePrimaryHttpMessageHandler(() =>
{
var handler = new HttpClientHandler
{
ClientCertificateOptions = ClientCertificateOption.Manual,
ServerCertificateCustomValidationCallback = (_, _, _, _) => true
};
return handler;
});
services.AddHostedService<BalanceRecordCheckBackgroundService>();
services.AddHostedService<HealthCheckBackgroundService>(); services.AddHostedService<HealthCheckBackgroundService>();
services.AddRemoteCall(App.ActiveTypes);
services.AddMemoryCache(); services.AddMemoryCache();
} }
@@ -49,10 +40,9 @@ public class Startup : DefaultStartup
if (Configuration.GetSection("Download").Value == "1") if (Configuration.GetSection("Download").Value == "1")
{ {
using var scope = app.ApplicationServices.CreateScope(); using var scope = app.ApplicationServices.CreateScope();
var factory = Configuration.GetSection("Factory").Value; var factories = scope.ServiceProvider.GetService<ISettingService>().GetFactories();
var ds = scope.ServiceProvider.GetService<IDataDownloadService>(); var ds = scope.ServiceProvider.GetService<IDataDownloadService>();
ds.InitAsync(factory).Wait(); ds.InitAsync(factories).Wait();
} }
} }
} }

View File

@@ -0,0 +1,6 @@
namespace Dlhis.Middle.Request;
public class HealthCheckRequest
{
public List<string> Factories { get; set; }
}

View File

@@ -28,4 +28,7 @@ public class OrderResultResponse
/// 余额 /// 余额
/// </summary> /// </summary>
public decimal Balance { get; set; } public decimal Balance { get; set; }
public decimal TotalBalance { get; set; }
} }

View File

@@ -2,9 +2,7 @@ namespace Dlhis.Middle.Services;
public interface IDataDownloadService : IScopeDependency public interface IDataDownloadService : IScopeDependency
{ {
Task InitAsync(string factory); Task InitAsync(string[] factories);
Task DownloadAsync<T>(string factory) where T : class, new(); Task DownloadAsync<T>(string factory) where T : class, new();
Task TruncateAsync();
} }

View File

@@ -1,12 +1,8 @@
using Dlhis.Middle.Request;
using Dlhis.Middle.Response;
using Kurisu.AspNetCore.UnifyResultAndValidation;
namespace Dlhis.Middle.Services; namespace Dlhis.Middle.Services;
public interface IRemoteOrderService : IScopeDependency public interface IRemoteOrderService : IScopeDependency
{ {
Task<DefaultApiResult<OrderResultResponse>> AddAsync(OrderAddRequest request); //Task<DefaultApiResult<OrderResultResponse>> AddAsync(OrderAddRequest request);
Task<bool> AbleRemoteCallAsync(); Task<bool> AbleRemoteCallAsync();
} }

View File

@@ -0,0 +1,8 @@
namespace Dlhis.Middle.Services;
public interface ISettingService : ISingletonDependency
{
string[] GetFactories();
string GetBaseUrl();
}

View File

@@ -0,0 +1,6 @@
namespace Dlhis.Middle.Services;
public interface ITokenService : ISingletonDependency
{
Task<string> AuthAsync();
}

View File

@@ -1,43 +1,32 @@
using System.Net.Http.Headers;
using System.Reflection; using System.Reflection;
using System.Text; using Dlhis.Middle.Apis;
using Dlhis.Middle.Entities; using Dlhis.Middle.Entities;
using Dlhis.Middle.Response;
using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; 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; using SqlSugar;
namespace Dlhis.Middle.Services.Implements; namespace Dlhis.Middle.Services.Implements;
public class DataDownloadService : IDataDownloadService public class DataDownloadService : IDataDownloadService
{ {
private readonly IHttpClientFactory _httpClientFactory;
private readonly IMemoryCache _memoryCache;
private readonly IDbContext _dbContext; private readonly IDbContext _dbContext;
private readonly ICommonApi _commonApi;
private readonly ILogger<DataDownloadService> _logger; private readonly ILogger<DataDownloadService> _logger;
private readonly string _hostName;
public DataDownloadService(IHttpClientFactory httpClientFactory, IMemoryCache memoryCache, IConfiguration configuration, IDbContext dbContext, ILogger<DataDownloadService> logger) public DataDownloadService(
IDbContext dbContext,
ICommonApi commonApi,
ILogger<DataDownloadService> logger)
{ {
_httpClientFactory = httpClientFactory;
_memoryCache = memoryCache;
_dbContext = dbContext; _dbContext = dbContext;
_commonApi = commonApi;
_logger = logger; _logger = logger;
_hostName = configuration.GetSection("HostName").Value;
} }
public async Task InitAsync(string factory) public async Task InitAsync(string[] factories)
{ {
//需要上传完记录才可下载
if (await _dbContext.Queryable<LocalBalanceRecordEntity>().Where(x => x.IsSync == false).AnyAsync()) if (await _dbContext.Queryable<LocalBalanceRecordEntity>().Where(x => x.IsSync == false).AnyAsync())
{
return; return;
}
var factories = factory.Split(',');
await _dbContext.Client.Ado.BeginTranAsync(); await _dbContext.Client.Ado.BeginTranAsync();
try try
@@ -84,9 +73,10 @@ public class DataDownloadService : IDataDownloadService
_logger.LogInformation("档案{count}", await _dbContext.Queryable<LocalGainfoEntity>().CountAsync()); _logger.LogInformation("档案{count}", await _dbContext.Queryable<LocalGainfoEntity>().CountAsync());
_logger.LogInformation("余额{count}", await _dbContext.Queryable<LocalGainfoMealEntity>().CountAsync()); _logger.LogInformation("余额{count}", await _dbContext.Queryable<LocalGainfoMealEntity>().CountAsync());
_logger.LogInformation("下载完成"); _logger.LogInformation("下载完成");
await _dbContext.Client.Ado.CommitTranAsync(); await _dbContext.Client.Ado.CommitTranAsync();
} }
catch (Exception e) catch (Exception)
{ {
await _dbContext.Client.Ado.RollbackTranAsync(); await _dbContext.Client.Ado.RollbackTranAsync();
throw; throw;
@@ -95,27 +85,11 @@ public class DataDownloadService : IDataDownloadService
public async Task DownloadAsync<T>(string factory) where T : class, new() public async Task DownloadAsync<T>(string factory) where T : class, new()
{ {
var token = await AuthAsync(); var pack = await _commonApi.DownloadAsync<T>(factory, typeof(T).GetCustomAttribute<SugarTable>()!.TableName);
var httpClient = _httpClientFactory.CreateClient("face");
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var url = $"https://{_hostName}/api/common/SysData/download";
var requestUrl = QueryHelpers.AddQueryString(url, new Dictionary<string, string>
{
["factory"] = factory,
["tableName"] = typeof(T).GetCustomAttribute<SugarTable>()!.TableName
});
var response = await httpClient.GetStringAsync(requestUrl);
var pack = response.ToObject<DefaultApiResult<List<T>>>();
await _dbContext.InsertAsync(pack.Data); await _dbContext.InsertAsync(pack.Data);
} }
/// <inheritdoc /> private async Task TruncateAsync()
public async Task TruncateAsync()
{ {
await _dbContext.Client.Deleteable<LocalDepartmentEntity>().ExecuteCommandAsync(); await _dbContext.Client.Deleteable<LocalDepartmentEntity>().ExecuteCommandAsync();
await _dbContext.Client.Deleteable<LocalDeviceEntity>().ExecuteCommandAsync(); await _dbContext.Client.Deleteable<LocalDeviceEntity>().ExecuteCommandAsync();
@@ -128,26 +102,4 @@ public class DataDownloadService : IDataDownloadService
await _dbContext.Client.Deleteable<LocalGainfoEntity>().ExecuteCommandAsync(); await _dbContext.Client.Deleteable<LocalGainfoEntity>().ExecuteCommandAsync();
await _dbContext.Client.Deleteable<LocalGainfoMealEntity>().ExecuteCommandAsync(); await _dbContext.Client.Deleteable<LocalGainfoMealEntity>().ExecuteCommandAsync();
} }
private async Task<string> 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 = "e509c2aaa98e300c",
ClientSecret = "6184d8979fc48105"
}), Encoding.UTF8, "application/json"));
var tokenJson = await tokenResponse.Content.ReadAsStringAsync();
var tokenInfo = JsonConvert.DeserializeObject<DefaultApiResult<TokenResponse>>(tokenJson);
token = tokenInfo.Data.AccessToken;
_memoryCache.Set("token", token, TimeSpan.FromSeconds(tokenInfo.Data.Expires));
return token;
}
} }

View File

@@ -1,19 +1,26 @@
using System.Net.Sockets; using Dlhis.Middle.Apis;
using Dlhis.Middle.Request;
using Kurisu.AspNetCore.UnifyResultAndValidation;
namespace Dlhis.Middle.Services.Implements; namespace Dlhis.Middle.Services.Implements;
public class HealthCheckService : IHealthCheckService public class HealthCheckService : IHealthCheckService
{ {
private bool _isOffline; private bool _isOffline;
private readonly string _hostName;
private readonly ILogger<HealthCheckService> _logger; private readonly ILogger<HealthCheckService> _logger;
private int _successRetry; private readonly ISettingService _settingService;
private int _retry; private readonly ICommonApi _healthCheckApi;
private int _attempted;
public HealthCheckService(ILogger<HealthCheckService> logger, IConfiguration configuration) private bool _isBegin;
public HealthCheckService(ILogger<HealthCheckService> logger,
ISettingService settingService,
ICommonApi healthCheckApi)
{ {
_logger = logger; _logger = logger;
_hostName = configuration.GetSection("HostName").Value; _settingService = settingService;
_healthCheckApi = healthCheckApi;
} }
public bool GetIsOffline() public bool GetIsOffline()
@@ -23,44 +30,44 @@ public class HealthCheckService : IHealthCheckService
public async Task DoHealthCheckAsync() public async Task DoHealthCheckAsync()
{ {
var cancellationTokenSource = new CancellationTokenSource(5000);
var tcpClient = new TcpClient();
try try
{ {
_logger.LogDebug("tcp connecting {host} {port}", _hostName, 443); var factories = _settingService.GetFactories();
await tcpClient.ConnectAsync(_hostName, 443, cancellationTokenSource.Token);
_retry++; var response = await _healthCheckApi.CheckHealthAsync(new HealthCheckRequest { Factories = factories.ToList() });
_logger.LogDebug("retry {retry}", _retry); if (response.Code == ApiStateCode.Success)
if (tcpClient.Connected)
{ {
_successRetry++; _attempted++;
_logger.LogDebug("success connected {count}", _successRetry); if (_attempted == 5)
if (_successRetry == 4)
{ {
_isOffline = false; _isOffline = false;
_successRetry = 0; _attempted = 0;
_retry = 0;
_logger.LogInformation("current is {status}", "healthy");
} }
else
if (!_isBegin)
{ {
_logger.LogDebug("wait next check in {waitTime}", _successRetry * 1000); _isOffline = false;
await Task.Delay(_successRetry * 1000, cancellationTokenSource.Token); _isBegin = true;
} }
if (_isOffline == false)
{
_logger.LogInformation("Health");
}
await Task.Delay(_attempted * 1000);
} }
else else
{ {
_successRetry = 0;
_isOffline = true; _isOffline = true;
_logger.LogDebug("current is {status}", "unavailable"); _logger.LogInformation("Error {message}", response.Msg);
await Task.Delay(500);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError("fail cause :{reason}", ex.Message); _logger.LogError("Failed cause :{reason}", ex.Message);
_isOffline = true; _isOffline = true;
await Task.Delay(5000);
} }
} }
} }

View File

@@ -1,5 +1,6 @@
using System.Diagnostics; using System.Diagnostics;
using Dlhis.Entity.Canteen.Enums; using Dlhis.Entity.Canteen.Enums;
using Dlhis.Middle.Apis;
using Dlhis.Middle.Entities; using Dlhis.Middle.Entities;
using Kurisu.AspNetCore.Utils; using Kurisu.AspNetCore.Utils;
using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.AspNetCore.Utils.Extensions;
@@ -17,6 +18,7 @@ public class OrderService : IOrderService
{ {
private readonly ICanteenSettingService _canteenSettingService; private readonly ICanteenSettingService _canteenSettingService;
private readonly IRemoteOrderService _remoteOrderService; private readonly IRemoteOrderService _remoteOrderService;
private readonly ICanteenApi _canteenApi;
private readonly ILogger<OrderService> _logger; private readonly ILogger<OrderService> _logger;
private readonly string _factory; private readonly string _factory;
private readonly IDbContext _dbContext; private readonly IDbContext _dbContext;
@@ -24,14 +26,16 @@ public class OrderService : IOrderService
public OrderService(ICanteenSettingService canteenSettingService, public OrderService(ICanteenSettingService canteenSettingService,
IRemoteOrderService remoteOrderService, IRemoteOrderService remoteOrderService,
IConfiguration configuration, ISettingService settingService,
ICanteenApi canteenApi,
ILogger<OrderService> logger, ILogger<OrderService> logger,
IDbContext dbContext) IDbContext dbContext)
{ {
_canteenSettingService = canteenSettingService; _canteenSettingService = canteenSettingService;
_remoteOrderService = remoteOrderService; _remoteOrderService = remoteOrderService;
_canteenApi = canteenApi;
_logger = logger; _logger = logger;
_factory = configuration.GetSection("Factory").Value; _factory = settingService.GetFactories().Join(",");
_dbContext = dbContext; _dbContext = dbContext;
} }
@@ -43,12 +47,12 @@ public class OrderService : IOrderService
{ {
if (await _remoteOrderService.AbleRemoteCallAsync()) if (await _remoteOrderService.AbleRemoteCallAsync())
{ {
var remoteResult = await _remoteOrderService.AddAsync(request); var remoteResult = await _canteenApi.AddAsync(request);
if (remoteResult.Code == ApiStateCode.Success) if (remoteResult.Code == ApiStateCode.Success)
{ {
var d = remoteResult.Data; var d = remoteResult.Data;
await _dbContext.Updateable<LocalGainfoMealEntity>() await _dbContext.Updateable<LocalGainfoMealEntity>()
.SetColumns(x => x.Balance == d.Balance) .SetColumns(x => x.Balance == d.TotalBalance)
.Where(x => x.UserCode == d.UserCode) .Where(x => x.UserCode == d.UserCode)
.ExecuteCommandAsync(); .ExecuteCommandAsync();
} }

View File

@@ -1,54 +1,37 @@
using System.Net.Http.Headers;
using System.Text;
using Dlhis.Middle.Entities; using Dlhis.Middle.Entities;
using Dlhis.Middle.Request;
using Dlhis.Middle.Response;
using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services;
using Kurisu.AspNetCore.UnifyResultAndValidation;
using Kurisu.AspNetCore.Utils.Extensions;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
namespace Dlhis.Middle.Services.Implements; namespace Dlhis.Middle.Services.Implements;
public class RemoteOrderService : IRemoteOrderService public class RemoteOrderService : IRemoteOrderService
{ {
private readonly string _hostName;
private readonly IHttpClientFactory _httpClientFactory;
private readonly IMemoryCache _memoryCache;
private readonly IHealthCheckService _healthCheckService; private readonly IHealthCheckService _healthCheckService;
private readonly IDbContext _dbContext; private readonly IDbContext _dbContext;
public RemoteOrderService(IHttpClientFactory httpClientFactory, public RemoteOrderService(
IMemoryCache memoryCache,
IConfiguration configuration,
IHealthCheckService healthCheckService, IHealthCheckService healthCheckService,
IDbContext dbContext) IDbContext dbContext)
{ {
_httpClientFactory = httpClientFactory;
_memoryCache = memoryCache;
_hostName = configuration.GetSection("hostName").Value;
_healthCheckService = healthCheckService; _healthCheckService = healthCheckService;
_dbContext = dbContext; _dbContext = dbContext;
} }
///// <inheritdoc />
/// <inheritdoc /> // public async Task<DefaultApiResult<OrderResultResponse>> AddAsync(OrderAddRequest request)
public async Task<DefaultApiResult<OrderResultResponse>> AddAsync(OrderAddRequest request) // {
{ // var token = await _tokenService.AuthAsync();
var token = await AuthAsync(); //
// var httpClient = _httpClientFactory.CreateClient("face");
var httpClient = _httpClientFactory.CreateClient("face"); // httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); //
// var content = new StringContent(request.ToJson(), Encoding.UTF8, "application/json");
var content = new StringContent(request.ToJson(), Encoding.UTF8, "application/json"); //
// var response = await httpClient.PostAsync($"{_settingService.GetBaseUrl()}/api/canteen/orders", content);
var response = await httpClient.PostAsync($"https://{_hostName}/api/canteen/orders", content); //
// var responseJson = await response.Content.ReadAsStringAsync();
var responseJson = await response.Content.ReadAsStringAsync(); // return responseJson.ToObject<DefaultApiResult<OrderResultResponse>>();
return responseJson.ToObject<DefaultApiResult<OrderResultResponse>>(); // }
}
/// <inheritdoc /> /// <inheritdoc />
public async Task<bool> AbleRemoteCallAsync() public async Task<bool> AbleRemoteCallAsync()
@@ -60,28 +43,4 @@ public class RemoteOrderService : IRemoteOrderService
return !_healthCheckService.GetIsOffline(); return !_healthCheckService.GetIsOffline();
} }
private async Task<string> 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 = "e509c2aaa98e300c",
ClientSecret = "6184d8979fc48105"
}), Encoding.UTF8, "application/json"));
var tokenJson = await tokenResponse.Content.ReadAsStringAsync();
var tokenInfo = JsonConvert.DeserializeObject<DefaultApiResult<TokenResponse>>(tokenJson);
token = tokenInfo.Data.AccessToken;
_memoryCache.Set("token", token, TimeSpan.FromSeconds(tokenInfo.Data.Expires));
return token;
}
} }

View File

@@ -0,0 +1,23 @@
namespace Dlhis.Middle.Services.Implements;
public class SettingService : ISettingService
{
private readonly IConfiguration _configuration;
public SettingService(IConfiguration configuration)
{
_configuration = configuration;
}
/// <inheritdoc />
public string[] GetFactories()
{
return _configuration.GetSection("Factory").Value.Split(",");
}
/// <inheritdoc />
public string GetBaseUrl()
{
return _configuration.GetSection("BaseUrl").Value;
}
}

View File

@@ -0,0 +1,43 @@
using Dlhis.Middle.Apis;
using Microsoft.Extensions.Caching.Memory;
namespace Dlhis.Middle.Services.Implements;
public class TokenService : ITokenService
{
private readonly IMemoryCache _memoryCache;
private readonly ICommonApi _commonApi;
public TokenService(IMemoryCache memoryCache, ICommonApi commonApi)
{
_memoryCache = memoryCache;
_commonApi = commonApi;
}
/// <inheritdoc />
public async Task<string> AuthAsync()
{
var exists = _memoryCache.TryGetValue("token", out string token);
try
{
if (!exists)
{
var tokenInfo = await _commonApi.AuthAsync(new
{
ClientId = "e509c2aaa98e300c",
ClientSecret = "6184d8979fc48105"
});
token = tokenInfo.Data.AccessToken;
_memoryCache.Set("token", token, TimeSpan.FromSeconds(tokenInfo.Data.Expires));
}
}
finally
{
token = "Bearer " + token;
}
return token;
}
}

View File

@@ -5,8 +5,7 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"Authority": "https://face.jinkosolar.com", "BaseUrl": "https://yikatong.isawesome.cn",
"Factory": "1201", "Factory": "1234",
"HostName": "face.jinkosolar.com",
"Download":1 "Download":1
} }

View File

@@ -12,9 +12,6 @@
"SlowSqlTime": 1, "SlowSqlTime": 1,
"EnableSqlLog": false "EnableSqlLog": false
}, },
"Authority": "https://face.jinkosolar.com",
"Factory": "",
"HostName": "face.jinkosolar.com",
"AllowedHosts": "*", "AllowedHosts": "*",
"SwaggerOptions": { "SwaggerOptions": {
"Enable": true, "Enable": true,

Binary file not shown.