diff --git a/Src/Dlhis.Basic/BusinessShift/Response/BusinessPlaceShiftTinyResponse.cs b/Src/Dlhis.Basic/BusinessShift/Response/BusinessPlaceShiftTinyResponse.cs index 359754c7..40217d10 100644 --- a/Src/Dlhis.Basic/BusinessShift/Response/BusinessPlaceShiftTinyResponse.cs +++ b/Src/Dlhis.Basic/BusinessShift/Response/BusinessPlaceShiftTinyResponse.cs @@ -3,7 +3,7 @@ public class BusinessPlaceShiftTinyResponse { /// - /// 无用字段,暂时不删除 //todo + /// 无用字段,暂时不删除 /// public string CustomCode { get; set; } diff --git a/Src/Dlhis.Basic/Common/SysData/Controller/MiddlesController.cs b/Src/Dlhis.Basic/Common/SysData/Controller/MiddlesController.cs new file mode 100644 index 00000000..c3485490 --- /dev/null +++ b/Src/Dlhis.Basic/Common/SysData/Controller/MiddlesController.cs @@ -0,0 +1,29 @@ +using Dlhis.Basic.Common.SysData.Request; +using Dlhis.Basic.Common.SysData.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Dlhis.Basic.Common.SysData.Controller; + +[Authorize] +[Route("api/[controller]")] +[ApiController] +public class MiddlesController : ControllerBase +{ + private readonly IHealthCheckService _healthCheckService; + + public MiddlesController(IHealthCheckService healthCheckService) + { + _healthCheckService = healthCheckService; + } + + /// + /// 健康检查 + /// + /// + [HttpPost("healthz")] + public async Task CheckAsync(MiddleHealthCheckRequest request) + { + await _healthCheckService.CheckAsync(request.Factories); + } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/Common/SysData/Request/HealthCheckRequest.cs b/Src/Dlhis.Basic/Common/SysData/Request/HealthCheckRequest.cs new file mode 100644 index 00000000..4e530661 --- /dev/null +++ b/Src/Dlhis.Basic/Common/SysData/Request/HealthCheckRequest.cs @@ -0,0 +1,9 @@ +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Basic.Common.SysData.Request; + +public class HealthCheckRequest +{ + [MinLength(1)] + public List Factories { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/Common/SysData/Request/MiddleHealthCheckRequest.cs b/Src/Dlhis.Basic/Common/SysData/Request/MiddleHealthCheckRequest.cs new file mode 100644 index 00000000..c08b33c0 --- /dev/null +++ b/Src/Dlhis.Basic/Common/SysData/Request/MiddleHealthCheckRequest.cs @@ -0,0 +1,6 @@ +namespace Dlhis.Basic.Common.SysData.Request; + +public class MiddleHealthCheckRequest +{ + public List Factories { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/Common/SysData/Services/IHealthCheckService.cs b/Src/Dlhis.Basic/Common/SysData/Services/IHealthCheckService.cs new file mode 100644 index 00000000..489f8e5b --- /dev/null +++ b/Src/Dlhis.Basic/Common/SysData/Services/IHealthCheckService.cs @@ -0,0 +1,6 @@ +namespace Dlhis.Basic.Common.SysData.Services; + +public interface IHealthCheckService : IScopeDependency +{ + Task CheckAsync(List factories); +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/Common/SysData/Services/Implements/HealthCheckService.cs b/Src/Dlhis.Basic/Common/SysData/Services/Implements/HealthCheckService.cs new file mode 100644 index 00000000..90203b63 --- /dev/null +++ b/Src/Dlhis.Basic/Common/SysData/Services/Implements/HealthCheckService.cs @@ -0,0 +1,10 @@ +namespace Dlhis.Basic.Common.SysData.Services.Implements; + +public class HealthCheckService : IHealthCheckService +{ + public async Task CheckAsync(List factories) + { + //todo 状态显示 + await Task.CompletedTask; + } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs b/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs index 53ec663d..976e0244 100644 --- a/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs +++ b/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs @@ -1,5 +1,7 @@ using System.Reflection; using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Canteen.Entities; +using Dlhis.Entity.Canteen.Enums; using Kurisu.AspNetCore; using Kurisu.AspNetCore.DataAccess.Entity; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; @@ -22,27 +24,53 @@ public class SysDataService : ISysDataService var type = App.ActiveTypes .Where(x => x.IsDefined(typeof(SugarTable), false)) .Where(x => !x.IsDefined(typeof(SkipScanAttribute), false)) - .First(x => x.GetCustomAttribute()!.TableName == tableName); + .FirstOrDefault(x => x.GetCustomAttribute()!.TableName == tableName); - var table = type.GetCustomAttribute()!.TableName; + var table = type?.GetCustomAttribute()!.TableName; var sql = $"select * from {table}"; - if (type.IsAssignableTo(typeof(ITenantId))) + + if (type == null) { - sql += $" where tenantId = '{factory}'"; + table = tableName; + } + else + { + if (type.IsAssignableTo(typeof(ITenantId))) + { + sql += $" where tenantId = '{factory}'"; + } } - switch (tableName) + switch (table) { case "Gainfo": - //sql = $"select * from Gainfo where factory = '{factory}'"; return await _dbContext.Queryable().Where(x => x.Factory == factory).Select(x => (object)x).ToListAsync(); case "GainfoMeal": { - var codes = await _dbContext.Queryable().Where(x => x.Factory == factory).Select(x => x.Code).ToListAsync(); - return await _dbContext.Queryable().Where(x => codes.Contains(x.UserCode)).Select(x => (object)x).ToListAsync(); + var r = await _dbContext.Queryable() + .Where(x => x.Factory == factory) + .GroupBy(x => x.Code) + .Select(x => new + { + UserCode = x.Code, + Balance = SqlFunc.Subqueryable().Where(p => p.UserCode == x.Code) + .Where(p => p.Source != BalanceRecordSourceEnumType.EMealVoucher && p.Status == BalanceRecordStatusEnumType.Valid && p.IsDeleted == false) + .Sum(p => p.Amount) + }) + .ToListAsync(); + + return r.Select(x => (object)x).ToList(); } default: return await _dbContext.Client.SqlQueryable(sql).ToListAsync(); } } + + public async Task> GetWalletFactoriesAsync(string factory) + { + var wallets = await _dbContext.Queryable().ToListAsync(); + var code = wallets.First(x => x.Factory == factory).Code; + + return wallets.Where(x => x.Code == code).Select(x => x.Factory).ToList(); + } } \ No newline at end of file diff --git a/Src/Dlhis.Basic/Organization/Controllers/GainfoMealController.cs b/Src/Dlhis.Basic/Organization/Controllers/GainfoMealController.cs index 24b50fc6..d511a2ca 100644 --- a/Src/Dlhis.Basic/Organization/Controllers/GainfoMealController.cs +++ b/Src/Dlhis.Basic/Organization/Controllers/GainfoMealController.cs @@ -2,7 +2,6 @@ using Dlhis.Basic.Organization.Response; using Dlhis.Basic.Organization.Services; using Kurisu.AspNetCore.DataAccess.Dto; -using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -33,15 +32,4 @@ public class GainfoMealController : ControllerBase { return await _mealUserService.GetPageAsync(request); } - - /// - /// 迁移 - /// - /// - [Transactional] - [HttpPost("migration")] - public async Task MigrationAsync(GainfoBalanceMigrationRequest request) - { - await _mealUserService.MigrationAsync(request); - } } \ No newline at end of file diff --git a/Src/Dlhis.Basic/Organization/Response/GainfoCurrentMealInfoResponse.cs b/Src/Dlhis.Basic/Organization/Response/GainfoCurrentMealInfoResponse.cs deleted file mode 100644 index 4ccf513f..00000000 --- a/Src/Dlhis.Basic/Organization/Response/GainfoCurrentMealInfoResponse.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Dlhis.Basic.Organization.Response; - -public class GainfoCurrentMealInfoResponse -{ - /// - /// 余额 - /// - public decimal Balance { get; set; } -} diff --git a/Src/Dlhis.Basic/Organization/Response/GainfoCurrentResponse.cs b/Src/Dlhis.Basic/Organization/Response/GainfoCurrentResponse.cs index 06f3b3db..04cd105e 100644 --- a/Src/Dlhis.Basic/Organization/Response/GainfoCurrentResponse.cs +++ b/Src/Dlhis.Basic/Organization/Response/GainfoCurrentResponse.cs @@ -49,10 +49,15 @@ public class GainfoCurrentResponse public DateTime Birthday { get; set; } /// - /// 厂区 + /// 厂区编码 /// public string Factory { get; set; } + /// + /// 厂区名称 + /// + public string FactoryName { get; set; } + /// /// 卡类型 /// @@ -62,9 +67,4 @@ public class GainfoCurrentResponse /// 卡类型名称 /// public string CardTypeName { get; set; } - - /// - /// 就餐信息 - /// - public GainfoCurrentMealInfoResponse MealInfo { get; set; } } \ No newline at end of file diff --git a/Src/Dlhis.Basic/Organization/Response/GainfoMealResponse.cs b/Src/Dlhis.Basic/Organization/Response/GainfoMealResponse.cs index 33c7472d..6ef96e89 100644 --- a/Src/Dlhis.Basic/Organization/Response/GainfoMealResponse.cs +++ b/Src/Dlhis.Basic/Organization/Response/GainfoMealResponse.cs @@ -1,5 +1,6 @@ using Dlhis.Entity.Basic.Enums; using Kurisu.AspNetCore.Utils.Extensions; +using Newtonsoft.Json; namespace Dlhis.Basic.Organization.Response; @@ -20,7 +21,10 @@ public class GainfoMealResponse /// /// 余额 /// - public decimal Balance { get; set; } + public string Balance => CurrentBalance.ToString("F2"); + + [JsonIgnore] + public decimal CurrentBalance { get; set; } // /// // /// 余额(充值) @@ -41,7 +45,9 @@ public class GainfoMealResponse /// /// 餐补总额 /// - public decimal? MealSupplementAmount { get; set; } + public string MealSupplementAmount => CurrentMealSupplementAmount.ToString("F2"); + + public decimal CurrentMealSupplementAmount { get; set; } /// /// 档案状态 diff --git a/Src/Dlhis.Basic/Organization/Services/IGainfoMealService.cs b/Src/Dlhis.Basic/Organization/Services/IGainfoMealService.cs index f657d625..3d2505cc 100644 --- a/Src/Dlhis.Basic/Organization/Services/IGainfoMealService.cs +++ b/Src/Dlhis.Basic/Organization/Services/IGainfoMealService.cs @@ -7,6 +7,4 @@ namespace Dlhis.Basic.Organization.Services; public interface IGainfoMealService : IScopeDependency { Task> GetPageAsync(GainfoMealPageRequest request); - - Task MigrationAsync(GainfoBalanceMigrationRequest request); } \ No newline at end of file diff --git a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoMealService.cs b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoMealService.cs index a97b3a01..743abed2 100644 --- a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoMealService.cs +++ b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoMealService.cs @@ -9,7 +9,6 @@ using Kurisu.AspNetCore.CustomClass; using Kurisu.AspNetCore.DataAccess.Dto; using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; -using Kurisu.AspNetCore.Utils; using Kurisu.AspNetCore.Utils.Extensions; using SqlSugar; @@ -28,138 +27,47 @@ public class GainfoMealService : IGainfoMealService public async Task> GetPageAsync(GainfoMealPageRequest request) { - var tenantId = _currentUser.GetStringTenantId(); + var factory = _currentUser.GetStringTenantId(); + var factories = await GetWalletFactoriesAsync(factory); - var page = await _dbContext.Queryable() - .LeftJoin((meal, gainfo) => meal.UserCode == gainfo.Code) - .LeftJoin((meal, gainfo, gainfoLeave) => meal.UserCode == gainfoLeave.Code) - .LeftJoin((meal, gainfo, gainfoLeave, dept1) => dept1.No == gainfo.DepartmentNo) - .LeftJoin((meal, gainfo, gainfoLeave, dept1, dept2) => dept2.No == gainfoLeave.DepartmentNo) - .WhereIF(request.Search.IsPresent(), (meal, gainfo, gainfoLeave, dept1, dept2) => meal.UserCode == request.Search - || gainfo.Name == request.Search - || gainfoLeave.Name == request.Search) - .Where((meal, gainfo, gainfoLeave) => gainfo.Factory == tenantId || gainfoLeave.Factory == tenantId) - .WhereIF(request.IsLeave, (meal, gainfo, gainfoLeave) => SqlFunc.Subqueryable().Where(x => x.Code == meal.UserCode).Any()) - .WhereIF(request.IsLeave && request.SearchDateTimePeriod.HasValue(), (meal, gainfo, gainfoLeave) => gainfoLeave.ResignationDate >= request.SearchDateTimePeriod.Start && gainfoLeave.ResignationDate <= request.SearchDateTimePeriod.End) - .Select((meal, gainfo, gainfoLeave, dept1, dept2) => new GainfoMealResponse + return await _dbContext.Queryable().AS(request.IsLeave ? "GainfoLeave" : "Gainfo") + .LeftJoin((x, dept) => dept.No == x.DepartmentNo) + .WhereIF(request.Search.IsPresent(), x => x.Code == request.Search || x.Name == request.Search) + .Where(x => SqlFunc.Subqueryable().Where(p => p.UserCode == x.Code && factories.Contains(p.TenantId)).Any()) + .WhereIF(request.IsLeave && request.SearchDateTimePeriod.HasValue(), x => x.ResignationDate >= request.SearchDateTimePeriod.Start && + x.ResignationDate <= request.SearchDateTimePeriod.End) + .Select((x, dept) => new GainfoMealResponse { - Balance = meal.Balance, - UserCode = meal.UserCode, - UserName = SqlFunc.IsNull(gainfo.Name, gainfoLeave.Name), - UserNo = SqlFunc.IsNull(gainfo.No, gainfoLeave.No), - DepartmentName = SqlFunc.IsNull(dept1.Name, dept2.Name), - Status = SqlFunc.IsNullOrEmpty(gainfo.No) ? GainfoStatusEnumType.Leave : GainfoStatusEnumType.Working + UserCode = x.Code, + UserName = x.Name, + UserNo = x.No, + DepartmentName = dept.Name, + Status = request.IsLeave + ? GainfoStatusEnumType.Leave + : GainfoStatusEnumType.Working, + CurrentMealSupplementAmount = SqlFunc.Subqueryable() + .Where(p => p.Code == x.Code) + .Where(p => factories.Contains(p.Factory)) + .WhereIF(request.SearchDateTimePeriod.HasValue(), p => p.Date >= request.SearchDateTimePeriod.Start && p.Date <= request.SearchDateTimePeriod.End) + .Sum(p => p.Amount), + CurrentBalance = SqlFunc.Subqueryable() + .Where(p => factories.Contains(p.TenantId)) + .Where(p => p.UserCode == x.Code && p.Status == BalanceRecordStatusEnumType.Valid && p.IsDeleted == false) + .Where(p => p.Source != BalanceRecordSourceEnumType.EMealVoucher) + .Sum(p => p.Amount) }) .ToPageListAsync(request); - - var userCodes = page.Data.Select(x => x.UserCode).ToList(); - var summary = await _dbContext.Queryable() - .Where(x => userCodes.Contains(x.Code)) - .WhereIF(request.SearchDateTimePeriod.HasValue(), x => x.Date >= request.SearchDateTimePeriod.Start && x.Date <= request.SearchDateTimePeriod.End) - .GroupBy(x => x.Code) - .Select(x => new - { - x.Code, - Amount = SqlFunc.AggregateSum(x.Amount) - }) - .ToListAsync(); - - page.Data.ForEach(x => { x.MealSupplementAmount = summary.FirstOrDefault(item => item.Code == x.UserCode)?.Amount ?? 0; }); - - return page; } - public async Task MigrationAsync(GainfoBalanceMigrationRequest request) + + //*********************************************************************************************************************************************************************** + + + public async Task> GetWalletFactoriesAsync(string factory) { - //只能针对离职人员 - var gainfoLeave = await _dbContext.Queryable().Where(x => x.Code == request.BeforeCode).SingleAsync(); - gainfoLeave.ThrowIfNull("需要对已离职人员迁移"); + var wallets = await _dbContext.Queryable().ToListAsync(); + var code = wallets.First(x => x.Factory == factory).Code; - var beforeBalance = await _dbContext.Queryable().Where(x => x.UserCode == request.BeforeCode).Select(x => x.Balance).SingleAsync(); - (beforeBalance <= 0).ThrowIfTrue("不可操作,余额不足"); - - var now = DateTime.Now; - - await _dbContext.InsertAsync(new BalanceRecordEntity - { - No = SnowFlakeHelper.Instance.NextId().ToString(), - UserNo = gainfoLeave.No, - Name = gainfoLeave.Name, - UserCode = gainfoLeave.Code, - DepartmentNo = gainfoLeave.DepartmentNo, - Source = BalanceRecordSourceEnumType.BalanceMigration, - Type = BalanceRecordTypeEnumType.In, - Amount = -Math.Abs(beforeBalance), - BusinessPlaceCode = null, - BusinessShiftCode = null, - ShiftType = null, - CostDate = now.Date, - DeviceUniqueNo = null, - IP = null, - CostId = gainfoLeave.CostId, - OriginTenantId = gainfoLeave.Factory, - IsCostCrossTenant = false, - Status = BalanceRecordStatusEnumType.Valid, - CostDateTime = DateTime.Now, - Remark = $"{gainfoLeave.Code}:余额转出", - ApplicationType = CanteenApplicationTypeEnumType.BalanceMigration, - TenantId = gainfoLeave.Factory, - Werks = gainfoLeave.Werks, - IsDeleted = false, - }); - - var beforeBalanceAmount = await _dbContext.Queryable() - .Where(x => x.UserCode == gainfoLeave.Code) - .Where(x => x.Status == BalanceRecordStatusEnumType.Valid) - .SumAsync(x => x.Amount); - - await _dbContext.Updateable() - .SetColumns(x => x.Balance == beforeBalanceAmount) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .SetColumns(x => x.Version == x.Version + 1) - .Where(x => x.UserCode == request.BeforeCode) //不需要判断version - .ExecuteCommandAsync(); - - //转移到现有人员 - var gainfo = await _dbContext.Queryable().Where(x => x.Code == request.NowCode).SingleAsync(); - gainfo.ThrowIfNull("目标人员不存在"); - - await _dbContext.InsertAsync(new BalanceRecordEntity - { - No = SnowFlakeHelper.Instance.NextId().ToString(), - UserNo = gainfo.No, - Name = gainfo.Name, - UserCode = gainfo.Code, - DepartmentNo = gainfo.DepartmentNo, - Source = BalanceRecordSourceEnumType.BalanceMigration, - Type = BalanceRecordTypeEnumType.In, - Amount = beforeBalance, - BusinessPlaceCode = null, - BusinessShiftCode = null, - ShiftType = null, - CostDate = now.Date, - DeviceUniqueNo = null, - IP = null, - CostId = gainfo.CostId, - OriginTenantId = gainfo.Factory, - IsCostCrossTenant = false, - Status = BalanceRecordStatusEnumType.Valid, - CostDateTime = DateTime.Now, - Remark = $"{gainfoLeave.Code}:余额转入", - ApplicationType = CanteenApplicationTypeEnumType.MealSupplement, - TenantId = gainfo.Factory, - Werks = gainfo.Werks, - IsDeleted = false, - }); - - var nowGainfoMeal = await _dbContext.Queryable().Where(x => x.UserCode == request.NowCode).SingleAsync(); - var nowBalance = nowGainfoMeal.Balance + beforeBalance; - - await _dbContext.Updateable() - .SetColumns(x => x.Balance == nowBalance) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .SetColumns(x => x.Version == x.Version + 1) - .Where(x => x.UserCode == request.NowCode && x.Version == nowGainfoMeal.Version) - .ExecuteCommandThrowVersionAsync("转入失败"); + return wallets.Where(x => x.Code == code).Select(x => x.Factory).ToList(); } } \ No newline at end of file diff --git a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs index 63317086..ad8f6cfa 100644 --- a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs +++ b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs @@ -197,10 +197,9 @@ public class GainfoService : IGainfoService var info = await _gainfoStore.Queryable(GainfoStatusEnumType.Working) .LeftJoin((ga, department) => ga.DepartmentNo == department.No) - .LeftJoin((ga, department, mealUser) => ga.Code == mealUser.UserCode) - .LeftJoin((ga, department, mealUser, type) => type.Code == ga.CardType.ToString() && type.Type == "timeperiod") + .LeftJoin((ga, department, type) => type.Code == ga.CardType.ToString() && type.Type == "timeperiod") .Where((ga, department) => ga.Id == gainfo.Id) - .Select((ga, department, mealUser, type) => new GainfoCurrentResponse + .Select((ga, department, type) => new GainfoCurrentResponse { UserNo = ga.No, Birthday = ga.Birthday, @@ -210,10 +209,6 @@ public class GainfoService : IGainfoService UserCode = ga.Code, UserName = ga.Name, Factory = ga.Factory, - MealInfo = new GainfoCurrentMealInfoResponse - { - Balance = mealUser.Balance - }, CardType = ga.CardType, CardTypeName = type.Display }).SingleAsync(); @@ -225,7 +220,7 @@ public class GainfoService : IGainfoService var tenantResult = await _tenantApi.GetTenantByCodeAsync(info.Factory); if (tenantResult is { Data: not null }) { - info.Factory = tenantResult.Data.Name; + info.FactoryName = tenantResult.Data.Name; } } catch (Exception) diff --git a/Src/Dlhis.Canteen/MealSupplementRecord/Services/Implements/MealSupplementRecordService.cs b/Src/Dlhis.Canteen/MealSupplementRecord/Services/Implements/MealSupplementRecordService.cs index c334c0cc..ddeb8fca 100644 --- a/Src/Dlhis.Canteen/MealSupplementRecord/Services/Implements/MealSupplementRecordService.cs +++ b/Src/Dlhis.Canteen/MealSupplementRecord/Services/Implements/MealSupplementRecordService.cs @@ -187,37 +187,6 @@ public class MealSupplementRecordService : IMealSupplementRecordService DepartmentNo = gainfo.DepartmentNo, DeviceUniqueNo = null }); - - await CommonHelper.RetryAsync(async () => - { - var mealUser = await _dbContext.Queryable().Where(x => x.UserCode == request.Code).FirstAsync(); - if (mealUser == null) - { - mealUser = new GainfoMealEntity - { - UserCode = gainfo.Code, - Balance = 0, - BalanceOfRecharge = 0, - BalanceOfSubsidies = 0, - Version = 0 - }; - - await _dbContext.InsertAsync(mealUser); - } - - //重新核算 - var balance = await _dbContext.Queryable() - .Where(x => x.UserCode == request.Code) - .Where(x => x.Status == BalanceRecordStatusEnumType.Valid) - .SumAsync(x => x.Amount); - - await _dbContext.Updateable() - .SetColumns(x => x.Version == x.Version + 1) - .SetColumns(x => x.Balance == balance) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .Where(x => x.UserCode == mealUser.UserCode && x.Version == mealUser.Version) - .ExecuteCommandThrowVersionAsync(); - }, 5); } } } \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs b/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs index dcf8df4c..00b9eae8 100644 --- a/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs +++ b/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs @@ -1,16 +1,11 @@ -using System.ComponentModel.DataAnnotations; -using System.Data; +using System.Data; using Dlhis.Canteen.Order.Request; using Dlhis.Canteen.Order.Response; using Dlhis.Canteen.Order.Services; -using Dlhis.Entity.Canteen.Entities; -using Kurisu.AspNetCore; using Kurisu.AspNetCore.DataAccess.Dto; using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; -using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using SqlSugar; namespace Dlhis.Canteen.Order.Controllers; @@ -167,36 +162,4 @@ public class BalancesController : ControllerBase { await _balanceService.ManuallyImportBalanceAndReCalcAsync(requests); } - - - /// - /// 重算 - /// - /// - [Authorize(Roles = "admin")] - [HttpPost("recalc")] - [IgnoreTenant] - [Transactional(IsolationLevel.ReadCommitted)] - public async Task ReCalcAsync(BalanceReCalcRequest request) - { - await _balanceService.ReCalcAsync(request.UserCode); - } - - [HttpPost("recalc1")] - [Transactional(IsolationLevel.ReadCommitted)] - [IgnoreTenant] - public async Task ReC5() - { - var sp = App.GetServiceProvider(); - var db = sp.GetService(); - var middleService = sp.GetService(); - var others = await db.Queryable().AS("LocalBalanceRecordEntity") - .Where(x => SqlFunc.Subqueryable().Where(p => p.No == x.No).NotAny()) - .ToListAsync(); - - foreach (var item in others) - { - await middleService.MiddleUploadAsync(item); - } - } } \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Controllers/OrdersController.cs b/Src/Dlhis.Canteen/Order/Controllers/OrdersController.cs index 59ab64b3..ba3482cd 100644 --- a/Src/Dlhis.Canteen/Order/Controllers/OrdersController.cs +++ b/Src/Dlhis.Canteen/Order/Controllers/OrdersController.cs @@ -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.Services; using Kurisu.AspNetCore.DataAccess.Dto; @@ -19,12 +18,10 @@ namespace Dlhis.Canteen.Order.Controllers; public class OrdersController : ControllerBase { private readonly IOrderService _orderService; - private readonly IBookingOrderService _bookingOrderService; - public OrdersController(IOrderService orderService, IBookingOrderService bookingOrderService) + public OrdersController(IOrderService orderService) { _orderService = orderService; - _bookingOrderService = bookingOrderService; } /// @@ -37,8 +34,7 @@ public class OrdersController : ControllerBase [IgnoreTenant] public async Task> AddAsync(OrderAddRequest request) { - var result = await _orderService.AddAsync(request); - return result.Response; + return await _orderService.AddAsync(request); } /// @@ -51,8 +47,7 @@ public class OrdersController : ControllerBase [IgnoreTenant] public async Task> DeviceConsume(OrderAddRequest request) { - var result = await _orderService.AddAsync(request); - return result.Response; + return await _orderService.AddAsync(request); } /// @@ -66,56 +61,56 @@ public class OrdersController : ControllerBase return await _orderService.GetOrdersAggregationAsync(page); } - /// - /// 取消订单 - /// - /// - /// - [HttpPut("user-cancel")] - [Transactional] - [IgnoreTenant] - public async Task CancelOrderAsync(OrderCancelRequest request) - { - await _bookingOrderService.UserCancelOrderAsync(request); - } + // /// + // /// 取消订单 + // /// + // /// + // /// + // [HttpPut("user-cancel")] + // [Transactional] + // [IgnoreTenant] + // public async Task CancelOrderAsync(OrderCancelRequest request) + // { + // await _bookingOrderService.UserCancelOrderAsync(request); + // } - - /// - /// 取消订单 - /// - /// - /// - [HttpPut("manager-cancel")] - [Transactional] - [IgnoreTenant] - public async Task ManagerCancelOrderAsync(OrderCancelRequest request) - { - await _bookingOrderService.ManagerCancelOrderAsync(request); - } - - //************************************************************** 订餐功能 ********************************************************************************** - - /// - /// 取消订单 - /// - /// - /// - [HttpPost("booking")] - [Transactional] - [IgnoreTenant] - public async Task Booking(BookingMealRequest request) - { - return await _bookingOrderService.BookingAsync(request); - } - - /// - /// 订单分页 - /// - /// - /// - [HttpGet("booking/page")] - public async Task> GetBookingPageAsync([FromQuery] OrderBookingPageRequest request) - { - return await _bookingOrderService.GetBookingPageAsync(request); - } + // + // /// + // /// 取消订单 + // /// + // /// + // /// + // [HttpPut("manager-cancel")] + // [Transactional] + // [IgnoreTenant] + // public async Task ManagerCancelOrderAsync(OrderCancelRequest request) + // { + // await _bookingOrderService.ManagerCancelOrderAsync(request); + // } + // + // //************************************************************** 订餐功能 ********************************************************************************** + // + // /// + // /// 取消订单 + // /// + // /// + // /// + // [HttpPost("booking")] + // [Transactional] + // [IgnoreTenant] + // public async Task Booking(BookingMealRequest request) + // { + // return await _bookingOrderService.BookingAsync(request); + // } + // + // /// + // /// 订单分页 + // /// + // /// + // /// + // [HttpGet("booking/page")] + // public async Task> GetBookingPageAsync([FromQuery] OrderBookingPageRequest request) + // { + // return await _bookingOrderService.GetBookingPageAsync(request); + // } } \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Dto/OrderPreCheckDto.cs b/Src/Dlhis.Canteen/Order/Dto/OrderPreCheckDto.cs index 6667755e..c4f9c9dc 100644 --- a/Src/Dlhis.Canteen/Order/Dto/OrderPreCheckDto.cs +++ b/Src/Dlhis.Canteen/Order/Dto/OrderPreCheckDto.cs @@ -1,16 +1,16 @@ -using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Canteen.Enums; -namespace Dlhis.Canteen.Order.Dto +namespace Dlhis.Canteen.Order.Dto; + +public class OrderPreCheckDto { - public class OrderPreCheckDto - { - public DateTime CostDate { get; set; } - public BusinessShiftMealRelationshipEntity Shift { get; set; } + public string Factory { get; set; } - public string BusinessPlaceTenantId { get; set; } + public DateTime CostDate { get; set; } - public Guid BusinessPlaceCode { get; set; } + public ShiftEnumType ShiftType { get; set; } - public Guid BusinessShiftCode { get; set; } - } + public Guid BusinessPlaceCode { get; set; } + + public Guid BusinessShiftCode { get; set; } } \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Repository/IBalanceRepository.cs b/Src/Dlhis.Canteen/Order/Repository/IBalanceRepository.cs new file mode 100644 index 00000000..0c1e321c --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Repository/IBalanceRepository.cs @@ -0,0 +1,40 @@ +using Dlhis.Entity.Canteen.Entities; + +namespace Dlhis.Canteen.Order.Repository; + +public interface IBalanceRepository : IScopeDependency +{ + /// + /// 获取余额 + /// + /// + /// + /// + Task GetBalanceAsync(string userCode, string factory); + + /// + /// 获取消费明细 + /// + /// + /// + /// + /// + /// + Task> GetBalanceRecordsAsync(string userCode, string factory, DateTime startTime, DateTime endTime); + + /// + /// 获取钱包厂区 + /// + /// + /// + Task> GetWalletFactoriesAsync(string factory); + + /// + /// 检测余额是否足够 + /// + /// + /// + /// + /// + Task CheckConsumableAsync(string userCode, string factory, decimal cost); +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Repository/IOrderRepository.cs b/Src/Dlhis.Canteen/Order/Repository/IOrderRepository.cs new file mode 100644 index 00000000..d9e904c4 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Repository/IOrderRepository.cs @@ -0,0 +1,11 @@ +using Dlhis.Canteen.Order.Dto; +using Dlhis.Canteen.Order.Request; +using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Canteen.Entities; + +namespace Dlhis.Canteen.Order.Repository; + +public interface IOrderRepository : IScopeDependency +{ + Task CreateDeviceConsumeOrderAsync(OrderPreCheckDto checkData, ConsumeRequest request, GainfoEntity gainfo); +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Repository/Implements/BalanceRepository.cs b/Src/Dlhis.Canteen/Order/Repository/Implements/BalanceRepository.cs new file mode 100644 index 00000000..f1d60d9e --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Repository/Implements/BalanceRepository.cs @@ -0,0 +1,60 @@ +using Dlhis.Entity.Canteen.Entities; +using Dlhis.Entity.Canteen.Enums; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +using Kurisu.AspNetCore.Utils.Extensions; + +namespace Dlhis.Canteen.Order.Repository.Implements; + +public class BalanceRepository : IBalanceRepository +{ + private readonly IDbContext _dbContext; + + public BalanceRepository(IDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task GetBalanceAsync(string userCode, string factory) + { + var queryable = _dbContext.Queryable() + .Where(x => x.UserCode == userCode && x.Status == BalanceRecordStatusEnumType.Valid) + .Where(x => x.Source != BalanceRecordSourceEnumType.EMealVoucher); + + 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> GetBalanceRecordsAsync(string userCode, string factory, DateTime startTime, DateTime endTime) + { + var factories = await GetWalletFactoriesAsync(factory); + + return await _dbContext.Queryable() + .Where(x => x.UserCode == userCode) + .Where(x => factories.Contains(x.TenantId)) + .Where(x => x.Type == BalanceRecordTypeEnumType.Out) + .Where(x => x.Status == BalanceRecordStatusEnumType.Valid) + .Where(x => x.CostDateTime >= startTime && x.CostDateTime <= endTime) + .ToListAsync(); + } + + public async Task> GetWalletFactoriesAsync(string factory) + { + var wallets = await _dbContext.Queryable().ToListAsync(); + var code = wallets.First(x => x.Factory == factory).Code; + + return wallets.Where(x => x.Code == code).Select(x => x.Factory).ToList(); + } + + + public async Task CheckConsumableAsync(string userCode, string factory, decimal cost) + { + var balance = await GetBalanceAsync(userCode, factory); + var isEnough = balance - Math.Abs(cost) >= 0; + isEnough.ThrowIfFalse($"钱包余额不足({balance}元)"); + } +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Repository/Implements/OrderRepository.cs b/Src/Dlhis.Canteen/Order/Repository/Implements/OrderRepository.cs new file mode 100644 index 00000000..a9e31997 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Repository/Implements/OrderRepository.cs @@ -0,0 +1,68 @@ +using Dlhis.Canteen.Order.Dto; +using Dlhis.Canteen.Order.Request; +using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Canteen.Entities; +using Dlhis.Entity.Canteen.Enums; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +using Kurisu.AspNetCore.Utils; +using Kurisu.AspNetCore.Utils.Extensions; + +namespace Dlhis.Canteen.Order.Repository.Implements; + +public class OrderRepository : IOrderRepository +{ + private readonly IDbContext _dbContext; + + public OrderRepository(IDbContext dbContext) + { + _dbContext = dbContext; + } + + /// + public async Task CreateDeviceConsumeOrderAsync(OrderPreCheckDto checkData, ConsumeRequest request, GainfoEntity gainfo) + { + var order = new CanteenOrderEntity + { + RequestJson = request.ToJson(), + IsOffline = request.IsOffline, + No = SnowFlakeHelper.Instance.NextId().ToString(), + ThirdNo = request.ThirdNo, + Type = BalanceRecordTypeEnumType.Out, + ApplicationType = CanteenApplicationTypeEnumType.Device, + Name = gainfo.Name, + Source = request.Source, + UserNo = gainfo.No, + UserCode = gainfo.Code, + Amount = request.Amount, + BusinessPlaceCode = checkData.BusinessPlaceCode, + BusinessShiftCode = checkData.BusinessShiftCode, + ShiftType = checkData.ShiftType, + CostDate = checkData.CostDate, + CostDateTime = request.CostDateTime, + CostId = gainfo.CostId, + DepartmentNo = gainfo.DepartmentNo, + DeviceUniqueNo = request.DeviceUniqueNo, + + Remark = null, + DeliveryAddress = null, + Result = null, + IP = null, + + IsCostCrossTenant = gainfo.Factory != checkData.Factory, + + Werks = gainfo.Werks, + OriginTenantId = gainfo.Factory, + //标识当前消费的基地 + TenantId = checkData.Factory, + + Status = BalanceRecordStatusEnumType.Valid, + OrderStatus = CanteenOrderStatusEnumType.Wait, + + IsDeleted = false, + }; + + var orderId = await _dbContext.InsertReturnIdentityAsync(order); + order.Id = orderId; + return order; + } +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Request/BalanceRecordUserPageRequest.cs b/Src/Dlhis.Canteen/Order/Request/BalanceRecordUserPageRequest.cs index 32f30543..44f78456 100644 --- a/Src/Dlhis.Canteen/Order/Request/BalanceRecordUserPageRequest.cs +++ b/Src/Dlhis.Canteen/Order/Request/BalanceRecordUserPageRequest.cs @@ -17,6 +17,11 @@ public class BalanceRecordUserPageRequest : PageDto, IValidatableObject /// public DateTimePeriod SearchDateTimePeriod { get; set; } + /// + /// 钱包code + /// + [Required(ErrorMessage = "请选择钱包")] + public string WalletCode { get; set; } public IEnumerable Validate(ValidationContext validationContext) { @@ -27,4 +32,4 @@ public class BalanceRecordUserPageRequest : PageDto, IValidatableObject yield break; } -} +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Response/OrderResultResponse.cs b/Src/Dlhis.Canteen/Order/Response/OrderResultResponse.cs index d564e96b..a1472e19 100644 --- a/Src/Dlhis.Canteen/Order/Response/OrderResultResponse.cs +++ b/Src/Dlhis.Canteen/Order/Response/OrderResultResponse.cs @@ -32,4 +32,13 @@ public class OrderResultResponse /// 余额 /// public string Balance => CurrentBalance.ToString("f2"); -} + + + [JsonIgnore] + public decimal CurrentTotalBalance { get; set; } + + /// + /// 总余额 + /// + public string TotalBalance => CurrentTotalBalance.ToString("f2"); +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Services/BaseOrderService.cs b/Src/Dlhis.Canteen/Order/Services/BaseOrderService.cs index 16ed5fee..df521389 100644 --- a/Src/Dlhis.Canteen/Order/Services/BaseOrderService.cs +++ b/Src/Dlhis.Canteen/Order/Services/BaseOrderService.cs @@ -1,16 +1,12 @@ using Dlhis.Canteen.Setting.Services; -using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Canteen.Enums; using Kurisu.AspNetCore.Authentication.Abstractions; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; -using Kurisu.AspNetCore.Utils.Extensions; -using Mapster; namespace Dlhis.Canteen.Order.Services; public abstract class BaseOrderService { - private readonly ICanteenSettingService _canteenSettingService; private readonly IDbContext _dbContext; private readonly ICurrentUser _currentUser; private readonly ILogger _logger; @@ -21,7 +17,6 @@ public abstract class BaseOrderService ICurrentUser currentUser, ILogger logger) { - _canteenSettingService = canteenSettingService; _dbContext = dbContext; _currentUser = currentUser; _logger = logger; @@ -32,37 +27,4 @@ public abstract class BaseOrderService protected IDbContext DbContext => _dbContext; protected ICurrentUser CurrentUser => _currentUser; - - /// - /// 获取参数配置 - /// - /// - /// - /// - /// - /// - /// - protected async Task GetSettingAsync(string userNo, string tenantId, CanteenSettingItemEnumType item, T defaultValue = default) - { - _settings ??= await _canteenSettingService.GetUserSettingAsync(userNo, tenantId); - - if (_settings.TryGetValue(item, out var v)) - { - try - { - if (int.TryParse(v, out var n)) - { - return n.Adapt(); - } - - return v.Adapt(); - } - catch (Exception) - { - return defaultValue; - } - } - - return defaultValue; - } } \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Services/IBalanceService.cs b/Src/Dlhis.Canteen/Order/Services/IBalanceService.cs index bedc7a72..4efc0902 100644 --- a/Src/Dlhis.Canteen/Order/Services/IBalanceService.cs +++ b/Src/Dlhis.Canteen/Order/Services/IBalanceService.cs @@ -14,7 +14,7 @@ public interface IBalanceService : IScopeDependency Task> GetUserBalanceGroupAsync(BalanceRecordUserGroupPageRequest request); - + Task SupplementBalanceAsync(BalanceSupplementRequest request); @@ -40,6 +40,4 @@ public interface IBalanceService : IScopeDependency Task ManuallyImportBalanceAndReCalcAsync(List requests); - - Task ReCalcAsync(string userCode); } \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Services/IOrderService.cs b/Src/Dlhis.Canteen/Order/Services/IOrderService.cs index a956a361..a738db3f 100644 --- a/Src/Dlhis.Canteen/Order/Services/IOrderService.cs +++ b/Src/Dlhis.Canteen/Order/Services/IOrderService.cs @@ -1,19 +1,20 @@ using Dlhis.Canteen.Order.Request; using Dlhis.Canteen.Order.Response; using Kurisu.AspNetCore.DataAccess.Dto; +using Kurisu.AspNetCore.UnifyResultAndValidation; namespace Dlhis.Canteen.Order.Services; public interface IOrderService : IScopeDependency { - Task AddAsync(OrderAddRequest request); + Task> AddAsync(OrderAddRequest request); /// /// 设备消费 /// /// /// - Task DeviceConsume(OrderAddRequest request); + Task> DeviceConsume(OrderAddRequest request); Task> GetOrdersAggregationAsync(PageDto page); } \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs index 64bfb9e4..88601a8a 100644 --- a/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs +++ b/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs @@ -1,4 +1,5 @@ using Dlhis.Basic.Api; +using Dlhis.Canteen.Order.Repository; using Dlhis.Canteen.Order.Request; using Dlhis.Canteen.Order.Response; using Dlhis.Canteen.Setting.Services; @@ -29,6 +30,7 @@ public class BalanceService : IBalanceService private readonly ITenantApi _tenantApi; private readonly ICanteenSettingService _canteenSettingService; private readonly RedisCache _redisCache; + private readonly IBalanceRepository _balanceRepository; private readonly IBusinessPlacePermissionsApi _businessPlacePermissionsApi; public BalanceService(IDbContext dbContext, @@ -36,6 +38,7 @@ public class BalanceService : IBalanceService ITenantApi tenantApi, ICanteenSettingService canteenSettingService, RedisCache redisCache, + IBalanceRepository balanceRepository, IBusinessPlacePermissionsApi businessPlacePermissionsApi) { _dbContext = dbContext; @@ -43,6 +46,7 @@ public class BalanceService : IBalanceService _tenantApi = tenantApi; _canteenSettingService = canteenSettingService; _redisCache = redisCache; + _balanceRepository = balanceRepository; _businessPlacePermissionsApi = businessPlacePermissionsApi; } @@ -123,11 +127,7 @@ public class BalanceService : IBalanceService balanceRecord.Remark = "退款"; await _dbContext.UpdateAsync(balanceRecord); - if (balanceRecord.Source is BalanceRecordSourceEnumType.Face or BalanceRecordSourceEnumType.QrCode) - { - await ReCalcAsync(balanceRecord.UserCode); - } - else if (balanceRecord.Source == BalanceRecordSourceEnumType.EMealVoucher) + if (balanceRecord.Source == BalanceRecordSourceEnumType.EMealVoucher) { var originData = balanceRecord.UserNo.Split(":"); var eNo = originData[0]; @@ -324,39 +324,29 @@ public class BalanceService : IBalanceService await _dbContext.Client.Ado.RollbackTranAsync(); throw; } - - await CommonHelper.RetryAsync(async () => - { - var gainfoMeal = await _dbContext.Queryable().Where(x => x.UserCode == balanceInfo.UserCode).SingleAsync(); - var balance = gainfoMeal.Balance + Math.Abs(balanceInfo.Amount) + request.Amount; - - await _dbContext.Updateable() - .SetColumns(x => x.Balance == balance) - .SetColumns(x => x.Version == x.Version + 1) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .Where(x => x.UserCode == balanceInfo.UserCode && x.Version == gainfoMeal.Version) - .ExecuteCommandThrowVersionAsync(); - }, 10); } public async Task GetPageByUserAsync(BalanceRecordUserPageRequest request) { - var jinkoCode = _currentUser.GetUserClaim("jinko-code") ?? string.Empty; + var userCode = _currentUser.GetUserClaim("jinko-code") ?? string.Empty; + + var factory = await _dbContext.Queryable().Where(x => x.Code == request.WalletCode).Select(x => x.Factory).FirstAsync(); + var factories = await _balanceRepository.GetWalletFactoriesAsync(factory); var page = await _dbContext.Queryable() .LeftJoin((record, ga) => record.UserNo == ga.No) .LeftJoin((record, ga, department) => ga.DepartmentNo == department.No) .LeftJoin((record, ga, department, bPlace) => record.BusinessPlaceCode == bPlace.Code) - .Where((record, ga) => record.UserCode == jinkoCode) - .Where(record => record.Type == BalanceRecordTypeEnumType.Out && (record.Status == BalanceRecordStatusEnumType.Valid || record.Status == BalanceRecordStatusEnumType.Obsolute)) - .Where(record => record.Source != BalanceRecordSourceEnumType.EMealVoucher) + .Where((record, ga) => record.UserCode == userCode && factories.Contains(record.TenantId)) + .Where(record => record.Status == BalanceRecordStatusEnumType.Valid || record.Status == BalanceRecordStatusEnumType.Obsolute) + .Where(record => record.Source != BalanceRecordSourceEnumType.EMealVoucher) //不展示电子餐券信息 .WhereIF(request.SearchDateTimePeriod.HasValue(), record => record.CostDate >= request.SearchDateTimePeriod.Start && record.CostDate <= request.SearchDateTimePeriod.End) .WhereIF(request.ApplicationType.HasValue, record => record.ApplicationType == request.ApplicationType) .OrderBy(record => record.CostDateTime) .Select((record, ga, department, bPlace) => new BalanceRecordUserPageResponse { Id = record.Id, - CurrentAmount = SqlFunc.Abs(record.Amount), + CurrentAmount = record.Amount, Source = record.Source, Status = record.Status, No = SqlFunc.IIF(record.Status == BalanceRecordStatusEnumType.Obsolute, "(已退)" + record.No, record.No), @@ -372,11 +362,11 @@ public class BalanceService : IBalanceService var monthEnd = monthStart.AddMonths(1).AddSeconds(-1); var monthSum = await _dbContext.Queryable() - .Where(x => x.CostDateTime >= monthStart && x.CostDateTime <= monthEnd) - .Where(x => x.Type == BalanceRecordTypeEnumType.Out && x.Status == BalanceRecordStatusEnumType.Valid) + .Where(x => x.UserCode == userCode && factories.Contains(x.TenantId)) + .Where(x => x.CostDate >= monthStart && x.CostDate <= monthEnd) + .Where(x => x.Status == BalanceRecordStatusEnumType.Valid) .Where(x => x.Source != BalanceRecordSourceEnumType.EMealVoucher) - .Where(x => x.UserCode == jinkoCode) - .SumAsync(x => SqlFunc.Abs(x.Amount)); + .SumAsync(x => x.Amount); return new BalanceRecordUserResultResponse { @@ -544,82 +534,10 @@ public class BalanceService : IBalanceService Werks = gainfo.Werks, IsDeleted = false }); - - await CommonHelper.RetryAsync(async () => - { - var gainfoMeal = await _dbContext.Queryable().Where(x => x.UserCode == gainfo.Code).FirstAsync(); - if (gainfoMeal == null) - { - gainfoMeal = new GainfoMealEntity - { - UserCode = gainfo.Code, - Balance = 0, - BalanceOfRecharge = 0, - BalanceOfSubsidies = 0, - Version = 0 - }; - - await _dbContext.InsertAsync(gainfoMeal); - } - - //重新核算 - var balance = await _dbContext.Queryable() - .Where(x => x.UserCode == gainfo.Code) - .Where(x => x.Status == BalanceRecordStatusEnumType.Valid) - .SumAsync(x => x.Amount); - - await _dbContext.Updateable() - .SetColumns(x => x.Version == x.Version + 1) - .SetColumns(x => x.Balance == balance) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .Where(x => x.UserCode == gainfoMeal.UserCode && x.Version == gainfoMeal.Version) - .ExecuteCommandThrowVersionAsync(); - }, 5); } } - public async Task ReCalcAsync(string userCode) - { - await CommonHelper.RetryAsync(async () => - { - var gainfo = await _dbContext.Queryable().Where(x => x.Code == userCode).SingleAsync() - ?? await _dbContext.Queryable().Where(x => x.Code == userCode).SingleAsync(); - - gainfo.ThrowIfNull("档案不存在"); - - var gainfoMeal = await _dbContext.Queryable().Where(x => x.UserCode == gainfo.Code).FirstAsync(); - if (gainfoMeal == null) - { - gainfoMeal = new GainfoMealEntity - { - UserCode = gainfo.Code, - Balance = 0, - BalanceOfRecharge = 0, - BalanceOfSubsidies = 0, - Version = 0 - }; - - await _dbContext.InsertAsync(gainfoMeal); - } - - //重新核算 - var balance = await _dbContext.Queryable() - .Where(x => x.UserCode == gainfo.Code) - .Where(x => x.Source != BalanceRecordSourceEnumType.EMealVoucher) - .Where(x => x.Status == BalanceRecordStatusEnumType.Valid) - .SumAsync(x => x.Amount); - - await _dbContext.Updateable() - .SetColumns(x => x.Version == x.Version + 1) - .SetColumns(x => x.Balance == balance) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .Where(x => x.UserCode == gainfoMeal.UserCode && x.Version == gainfoMeal.Version) - .ExecuteCommandThrowVersionAsync(); - }, 5); - } - - /// /// 获取离线设备 /// diff --git a/Src/Dlhis.Canteen/Order/Services/Implements/BookingOrderService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/BookingOrderService.cs index e149c83b..e71257ed 100644 --- a/Src/Dlhis.Canteen/Order/Services/Implements/BookingOrderService.cs +++ b/Src/Dlhis.Canteen/Order/Services/Implements/BookingOrderService.cs @@ -1,300 +1,302 @@ -using Dlhis.Canteen.Dishes.Request; -using Dlhis.Canteen.Order.Request; -using Dlhis.Canteen.Order.Response; -using Dlhis.Entity.Basic.Entity; -using Dlhis.Entity.Canteen.Entities; -using Dlhis.Entity.Canteen.Enums; -using Kurisu.AspNetCore.Utils.Extensions; -using Kurisu.AspNetCore.Utils; -using Dlhis.Canteen.Setting.Services; -using Dlhis.Basic.Api; -using Kurisu.AspNetCore.Authentication.Abstractions; -using Kurisu.AspNetCore.DataAccess.Dto; -using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions; -using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; -using SqlSugar; -using Mapster; - -namespace Dlhis.Canteen.Order.Services.Implements; - -public class BookingOrderService : BaseOrderService, IBookingOrderService -{ - private readonly IDishesMenuApi _dishesMenuApi; - private readonly IBusinessPlacePermissionsApi _businessPlacePermissionsApi; - - public BookingOrderService(ICanteenSettingService canteenSettingService, IDbContext dbContext, - IDishesMenuApi dishesMenuApi, - IBusinessPlacePermissionsApi businessPlacePermissionsApi, - ICurrentUser currentUser, ILogger logger) - : base(canteenSettingService, dbContext, currentUser, logger) - { - _dishesMenuApi = dishesMenuApi; - _businessPlacePermissionsApi = businessPlacePermissionsApi; - } - - public async Task BookingAsync(BookingMealRequest request) - { - var gainfo = await CheckExistsMyBookingsAsync(request.BookingDate, request.ShiftType); - - var businessPlace = await DbContext.Queryable().Where(x => x.Code == request.BusinessPlaceCode).FirstAsync(); - - //预定时间 - var now = DateTime.Now; - - var bookingDeadline = await GetSettingAsync(gainfo.No, businessPlace.TenantId, CanteenSettingItemEnumType.BookingDeadline) ?? new TimeOnly(20, 30, 00); - var dealLine = DateTime.Today.Add(bookingDeadline.ToTimeSpan()); - (now >= dealLine).ThrowIfTrue("超出订餐时间"); - - //菜单存在 - var menuDetailResult = await _dishesMenuApi.GetBookingDishesDetailAsync(request.BookingDate, request.BusinessPlaceCode, (int)request.ShiftType, request.DishesCode); - var menuDetail = menuDetailResult.Data; - menuDetail.ThrowIfNull("预定失败,菜品已经下架"); - - var order = new CanteenOrderEntity - { - RequestJson = request.ToJson(), - ThirdNo = string.Empty, - Type = BalanceRecordTypeEnumType.Out, - ApplicationType = CanteenApplicationTypeEnumType.Booking, - Name = gainfo.Name, - Source = request.Source, - UserNo = gainfo.No, - UserCode = gainfo.Code, - Amount = -Math.Abs(menuDetail.Price * request.Number), - BusinessPlaceCode = request.BusinessPlaceCode, - BusinessShiftCode = null, - ShiftType = request.ShiftType, - CostDate = now.Date, - CostId = gainfo.CostId, - DepartmentNo = gainfo.DepartmentNo, - DeviceUniqueNo = string.Empty, - DeliveryAddress = request.DeliveryAddress, - - No = SnowFlakeHelper.Instance.NextId().ToString(), - IsOffline = false, - IsCostCrossTenant = await GetSettingAsync(gainfo.No, businessPlace.TenantId, CanteenSettingItemEnumType.AllowCrossTenant), - - OriginTenantId = gainfo.Factory, //人员可能跨基地消费 - - Status = BalanceRecordStatusEnumType.Valid, - OrderStatus = CanteenOrderStatusEnumType.Success, - - CostDateTime = now, - - //标识当前消费的基地 - TenantId = businessPlace.TenantId, - }; - - await DbContext.InsertAsync(order); - await CommonHelper.RetryAsync(async () => - { - await _dishesMenuApi.GetBookingDishesDetailAsync(request.BookingDate, request.BusinessPlaceCode, (int)request.ShiftType, request.DishesCode); - menuDetail = menuDetailResult.Data; - menuDetail.ThrowIfNull("预定失败,菜品已经下架"); - menuDetail.Number -= request.Number; - - (menuDetail.Number < 0).ThrowIfTrue($"{menuDetail.DishesName}剩余数量不足"); - - //餐补 - if (request.Source == BalanceRecordSourceEnumType.LocalTenantSubsidies) - { - var mealInfo = await DbContext.Queryable().Where(x => x.UserCode == order.UserCode).FirstAsync(); - mealInfo.ThrowIfNull("余额不足,无法消费"); - - //金额+- - mealInfo.Balance += order.Amount; - (mealInfo.Balance < 0).ThrowIfTrue("余额不足,无法消费"); - - await DbContext.Updateable() - .SetColumns(x => x.Balance == mealInfo.Balance) - .SetColumns(x => x.Version == x.Version + 1) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .Where(x => x.UserCode == order.UserCode && x.Version == mealInfo.Version) //必须与查询时的version一致 - .ExecuteCommandThrowVersionAsync(); - } - - await DbContext.Updateable() - .SetColumns(x => x.Number == menuDetail.Number) - .SetColumns(x => x.Version == x.Version + 1) - .SetColumns(x => x.ModifiedTime == now) - .Where(x => x.StartDate <= menuDetail.Date && menuDetail.Date <= x.EndDate - && x.ShiftType == request.ShiftType - && x.BusinessPlaceCode == request.BusinessPlaceCode - && x.DishesCode == request.DishesCode - && x.Version == menuDetail.Version - ) - .ExecuteCommandThrowVersionAsync(); - - //写入记录 - var balanceRecord = order.Adapt(); - await DbContext.InsertAsync(balanceRecord); - - await DbContext.InsertAsync(new CanteenOrderDetailEntity - { - OrderNo = order.No, - BookingDate = request.BookingDate, - DishesCode = request.DishesCode, - Price = menuDetail.Price, - Unit = request.Unit, - Number = request.Number, - TenantId = order.TenantId, - }); - }, 10); - - - return new BookingResponse - { - No = order.No, - CreateTime = order.CreateTime, - Source = order.Source, - Amount = Math.Abs(order.Amount) - }; - } - - - public async Task> GetBookingPageAsync(OrderBookingPageRequest request) - { - var myCodes = (await _businessPlacePermissionsApi.GetMyAsync()).Data.Select(x => (Guid?)x.Code).ToList(); - - var page = await DbContext.Queryable() - .LeftJoin((x, ga) => x.UserNo == ga.No) - .LeftJoin((x, ga, dept) => dept.No == ga.DepartmentNo) - .Where(x => myCodes.Contains(x.BusinessPlaceCode)) - .WhereIF(request.OrderStatus.HasValue, x => x.OrderStatus == request.OrderStatus) - .WhereIF(request.Source.HasValue, x => x.Source == request.Source) - .WhereIF(request.Search.IsPresent(), (x, ga) => x.UserCode == request.Search || ga.Name.Contains(request.Search)) - .WhereIF(request.BookingPeriod?.Start.HasValue == true && request.BookingPeriod?.End.HasValue == true, x => - SqlFunc.Subqueryable() - .Where(p => p.OrderNo == x.No) - .Where(p => p.BookingDate >= request.BookingPeriod.Start) - .Where(p => p.BookingDate <= request.BookingPeriod.End) - .Any() - ) - .WhereIF(request.ShiftType.HasValue, x => x.ShiftType == request.ShiftType) - .Select((x, ga, dept) => new OrderBookingResponse - { - No = x.No, - DeliveryAddress = x.DeliveryAddress, - Remark = x.Remark, - Amount = SqlFunc.Abs(x.Amount), - CreateTime = x.CreateTime, - ShiftType = x.ShiftType, - Status = x.OrderStatus, - UserCode = x.UserCode, - UserName = ga.Name, - Source = x.Source, - DepartmentName = dept.Name, - }) - .ToPageListAsync(request); - - var nos = page.Data.Select(x => x.No).ToList(); - var details = await DbContext.Queryable() - .LeftJoin((x, dishes) => x.DishesCode == dishes.CustomCode) - .Where(x => nos.Contains(x.OrderNo)) - .Select((x, dishes) => new { x.OrderNo, x.BookingDate, x.Number, dishes.Name }).ToListAsync(); - - page.Data.ForEach(x => - { - x.BookingDate = details.First(item => item.OrderNo == x.No).BookingDate; - x.TotalNumber = details.Where(item => item.OrderNo == x.No).Sum(item => item.Number); - x.DishesInfos = details.Where(item => item.OrderNo == x.No).Select(item => new - { - item.Name - }) - .ToList(); - }); - - return page; - } - - public async Task UserCancelOrderAsync(OrderCancelRequest request) - { - var order = await DbContext.Queryable().Where(x => x.No == request.No).FirstAsync(); - order.ThrowIfNull("订单不存在"); - - var bookingDeadline = await GetSettingAsync(order.No, order.TenantId, CanteenSettingItemEnumType.BookingDeadline) ?? new TimeOnly(20, 30, 00); - var dealLine = DateTime.Today.Add(bookingDeadline.ToTimeSpan()); - (DateTime.Now >= dealLine).ThrowIfTrue("不在订单取消允许范围内,不可操作"); - - await ManagerCancelOrderAsync(new OrderCancelRequest - { - No = request.No, - Reason = request.Reason, - }); - } - - - public async Task ManagerCancelOrderAsync(OrderCancelRequest request) - { - var now = DateTime.Now; - var userId = CurrentUser.GetIntSubjectId(); - - var order = await DbContext.Queryable().Where(x => x.No == request.No).FirstAsync(); - order.ThrowIfNull("订单不存在"); - - await DbContext.Updateable() - .SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Cancel) - .SetColumns(x => x.Remark == request.Reason) - .SetColumns(x => x.ModifiedTime == now) - .SetColumns(x => x.ModifiedBy == userId) - .Where(x => x.No == request.No) - .ExecuteCommandAsync(); - - await DbContext.Updateable() - .SetColumns(x => x.Status == BalanceRecordStatusEnumType.Obsolute) - .SetColumns(x => x.Remark == request.Reason) - .SetColumns(x => x.ModifiedTime == now) - .SetColumns(x => x.ModifiedBy == userId) - .Where(x => x.No == request.No) - .ExecuteCommandAsync(); - - if (order.Source == BalanceRecordSourceEnumType.LocalTenantSubsidies) - { - await CommonHelper.RetryAsync(async () => - { - var mealInfo = await DbContext.Queryable().Where(x => x.UserCode == order.UserCode).FirstAsync(); - var setBalance = mealInfo.Balance + Math.Abs(order.Amount); - await DbContext.Updateable() - .SetColumns(x => x.Version == x.Version + 1) - .SetColumns(x => x.Balance == setBalance) - .SetColumns(x => x.ModifiedTime == now) - .SetColumns(x => x.ModifiedBy == userId) - .Where(x => x.Id == mealInfo.Id && x.Version == mealInfo.Version) - .ExecuteCommandThrowVersionAsync(); - }, 5); - } - } - - - /// - /// 检查我的订餐是否存在,存在抛出异常 - /// - /// - /// - /// - private async Task CheckExistsMyBookingsAsync(DateTime bookingDate, ShiftEnumType shiftType) - { - var subjectId = CurrentUser.GetIntSubjectId(); - var gainfo = await DbContext.Queryable().Where(x => x.IdentityUserId == subjectId).FirstAsync(); - var jinkoCode = CurrentUser.GetUserClaim("jinko-code") ?? string.Empty; - if (jinkoCode.IsPresent()) - { - gainfo = await DbContext.Queryable().Where(x => x.Code == jinkoCode).FirstAsync(); - } - - var start = bookingDate.Date.AddDays(-1); - var end = start.AddDays(1).AddSeconds(-1); - - var exists = await DbContext.Queryable() - .Where(x => x.ApplicationType == CanteenApplicationTypeEnumType.Booking) - .Where(x => x.OrderStatus == CanteenOrderStatusEnumType.Success) - .Where(x => x.ShiftType == shiftType) - .Where(x => x.UserNo == gainfo.No) - .Where(x => x.CreateTime >= start && x.CreateTime <= end) - .AnyAsync(); - - exists.ThrowIfTrue("不可重复订餐"); - - return gainfo; - } -} \ No newline at end of file +// using Dlhis.Canteen.Dishes.Request; +// using Dlhis.Canteen.Order.Request; +// using Dlhis.Canteen.Order.Response; +// using Dlhis.Entity.Basic.Entity; +// using Dlhis.Entity.Canteen.Entities; +// using Dlhis.Entity.Canteen.Enums; +// using Kurisu.AspNetCore.Utils.Extensions; +// using Kurisu.AspNetCore.Utils; +// using Dlhis.Canteen.Setting.Services; +// using Dlhis.Basic.Api; +// using Kurisu.AspNetCore.Authentication.Abstractions; +// using Kurisu.AspNetCore.DataAccess.Dto; +// using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions; +// using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +// using SqlSugar; +// using Mapster; +// +// namespace Dlhis.Canteen.Order.Services.Implements; +// +// public class BookingOrderService : BaseOrderService, IBookingOrderService +// { +// private readonly ICanteenSettingService _canteenSettingService; +// private readonly IDishesMenuApi _dishesMenuApi; +// private readonly IBusinessPlacePermissionsApi _businessPlacePermissionsApi; +// +// public BookingOrderService(ICanteenSettingService canteenSettingService, IDbContext dbContext, +// IDishesMenuApi dishesMenuApi, +// IBusinessPlacePermissionsApi businessPlacePermissionsApi, +// ICurrentUser currentUser, ILogger logger) +// : base(canteenSettingService, dbContext, currentUser, logger) +// { +// _canteenSettingService = canteenSettingService; +// _dishesMenuApi = dishesMenuApi; +// _businessPlacePermissionsApi = businessPlacePermissionsApi; +// } +// +// public async Task BookingAsync(BookingMealRequest request) +// { +// var gainfo = await CheckExistsMyBookingsAsync(request.BookingDate, request.ShiftType); +// +// var businessPlace = await DbContext.Queryable().Where(x => x.Code == request.BusinessPlaceCode).FirstAsync(); +// +// //预定时间 +// var now = DateTime.Now; +// +// var bookingDeadline = await _canteenSettingService.GetSettingAsync(CanteenSettingItemEnumType.BookingDeadline, businessPlace.TenantId, gainfo.No, new TimeOnly(20, 30, 00)); +// var dealLine = DateTime.Today.Add(bookingDeadline.ToTimeSpan()); +// (now >= dealLine).ThrowIfTrue("超出订餐时间"); +// +// //菜单存在 +// var menuDetailResult = await _dishesMenuApi.GetBookingDishesDetailAsync(request.BookingDate, request.BusinessPlaceCode, (int)request.ShiftType, request.DishesCode); +// var menuDetail = menuDetailResult.Data; +// menuDetail.ThrowIfNull("预定失败,菜品已经下架"); +// +// var order = new CanteenOrderEntity +// { +// RequestJson = request.ToJson(), +// ThirdNo = string.Empty, +// Type = BalanceRecordTypeEnumType.Out, +// ApplicationType = CanteenApplicationTypeEnumType.Booking, +// Name = gainfo.Name, +// Source = request.Source, +// UserNo = gainfo.No, +// UserCode = gainfo.Code, +// Amount = -Math.Abs(menuDetail.Price * request.Number), +// BusinessPlaceCode = request.BusinessPlaceCode, +// BusinessShiftCode = null, +// ShiftType = request.ShiftType, +// CostDate = now.Date, +// CostId = gainfo.CostId, +// DepartmentNo = gainfo.DepartmentNo, +// DeviceUniqueNo = string.Empty, +// DeliveryAddress = request.DeliveryAddress, +// +// No = SnowFlakeHelper.Instance.NextId().ToString(), +// IsOffline = false, +// IsCostCrossTenant = await _canteenSettingService.GetSettingAsync(CanteenSettingItemEnumType.AllowCrossTenant, businessPlace.TenantId, gainfo.No), +// +// OriginTenantId = gainfo.Factory, //人员可能跨基地消费 +// +// Status = BalanceRecordStatusEnumType.Valid, +// OrderStatus = CanteenOrderStatusEnumType.Success, +// +// CostDateTime = now, +// +// //标识当前消费的基地 +// TenantId = businessPlace.TenantId, +// }; +// +// await DbContext.InsertAsync(order); +// await CommonHelper.RetryAsync(async () => +// { +// await _dishesMenuApi.GetBookingDishesDetailAsync(request.BookingDate, request.BusinessPlaceCode, (int)request.ShiftType, request.DishesCode); +// menuDetail = menuDetailResult.Data; +// menuDetail.ThrowIfNull("预定失败,菜品已经下架"); +// menuDetail.Number -= request.Number; +// +// (menuDetail.Number < 0).ThrowIfTrue($"{menuDetail.DishesName}剩余数量不足"); +// +// //餐补 +// if (request.Source == BalanceRecordSourceEnumType.LocalTenantSubsidies) +// { +// var mealInfo = await DbContext.Queryable().Where(x => x.UserCode == order.UserCode).FirstAsync(); +// mealInfo.ThrowIfNull("余额不足,无法消费"); +// +// //金额+- +// mealInfo.Balance += order.Amount; +// (mealInfo.Balance < 0).ThrowIfTrue("余额不足,无法消费"); +// +// await DbContext.Updateable() +// .SetColumns(x => x.Balance == mealInfo.Balance) +// .SetColumns(x => x.Version == x.Version + 1) +// .SetColumns(x => x.ModifiedTime == DateTime.Now) +// .Where(x => x.UserCode == order.UserCode && x.Version == mealInfo.Version) //必须与查询时的version一致 +// .ExecuteCommandThrowVersionAsync(); +// } +// +// await DbContext.Updateable() +// .SetColumns(x => x.Number == menuDetail.Number) +// .SetColumns(x => x.Version == x.Version + 1) +// .SetColumns(x => x.ModifiedTime == now) +// .Where(x => x.StartDate <= menuDetail.Date && menuDetail.Date <= x.EndDate +// && x.ShiftType == request.ShiftType +// && x.BusinessPlaceCode == request.BusinessPlaceCode +// && x.DishesCode == request.DishesCode +// && x.Version == menuDetail.Version +// ) +// .ExecuteCommandThrowVersionAsync(); +// +// //写入记录 +// var balanceRecord = order.Adapt(); +// await DbContext.InsertAsync(balanceRecord); +// +// await DbContext.InsertAsync(new CanteenOrderDetailEntity +// { +// OrderNo = order.No, +// BookingDate = request.BookingDate, +// DishesCode = request.DishesCode, +// Price = menuDetail.Price, +// Unit = request.Unit, +// Number = request.Number, +// TenantId = order.TenantId, +// }); +// }, 10); +// +// +// return new BookingResponse +// { +// No = order.No, +// CreateTime = order.CreateTime, +// Source = order.Source, +// Amount = Math.Abs(order.Amount) +// }; +// } +// +// +// public async Task> GetBookingPageAsync(OrderBookingPageRequest request) +// { +// var myCodes = (await _businessPlacePermissionsApi.GetMyAsync()).Data.Select(x => (Guid?)x.Code).ToList(); +// +// var page = await DbContext.Queryable() +// .LeftJoin((x, ga) => x.UserNo == ga.No) +// .LeftJoin((x, ga, dept) => dept.No == ga.DepartmentNo) +// .Where(x => myCodes.Contains(x.BusinessPlaceCode)) +// .WhereIF(request.OrderStatus.HasValue, x => x.OrderStatus == request.OrderStatus) +// .WhereIF(request.Source.HasValue, x => x.Source == request.Source) +// .WhereIF(request.Search.IsPresent(), (x, ga) => x.UserCode == request.Search || ga.Name.Contains(request.Search)) +// .WhereIF(request.BookingPeriod?.Start.HasValue == true && request.BookingPeriod?.End.HasValue == true, x => +// SqlFunc.Subqueryable() +// .Where(p => p.OrderNo == x.No) +// .Where(p => p.BookingDate >= request.BookingPeriod.Start) +// .Where(p => p.BookingDate <= request.BookingPeriod.End) +// .Any() +// ) +// .WhereIF(request.ShiftType.HasValue, x => x.ShiftType == request.ShiftType) +// .Select((x, ga, dept) => new OrderBookingResponse +// { +// No = x.No, +// DeliveryAddress = x.DeliveryAddress, +// Remark = x.Remark, +// Amount = SqlFunc.Abs(x.Amount), +// CreateTime = x.CreateTime, +// ShiftType = x.ShiftType, +// Status = x.OrderStatus, +// UserCode = x.UserCode, +// UserName = ga.Name, +// Source = x.Source, +// DepartmentName = dept.Name, +// }) +// .ToPageListAsync(request); +// +// var nos = page.Data.Select(x => x.No).ToList(); +// var details = await DbContext.Queryable() +// .LeftJoin((x, dishes) => x.DishesCode == dishes.CustomCode) +// .Where(x => nos.Contains(x.OrderNo)) +// .Select((x, dishes) => new { x.OrderNo, x.BookingDate, x.Number, dishes.Name }).ToListAsync(); +// +// page.Data.ForEach(x => +// { +// x.BookingDate = details.First(item => item.OrderNo == x.No).BookingDate; +// x.TotalNumber = details.Where(item => item.OrderNo == x.No).Sum(item => item.Number); +// x.DishesInfos = details.Where(item => item.OrderNo == x.No).Select(item => new +// { +// item.Name +// }) +// .ToList(); +// }); +// +// return page; +// } +// +// public async Task UserCancelOrderAsync(OrderCancelRequest request) +// { +// var order = await DbContext.Queryable().Where(x => x.No == request.No).FirstAsync(); +// order.ThrowIfNull("订单不存在"); +// +// var bookingDeadline = await _canteenSettingService.GetSettingAsync(CanteenSettingItemEnumType.BookingDeadline, order.TenantId, order.No, new TimeOnly(20, 30, 00)); +// var dealLine = DateTime.Today.Add(bookingDeadline.ToTimeSpan()); +// (DateTime.Now >= dealLine).ThrowIfTrue("不在订单取消允许范围内,不可操作"); +// +// await ManagerCancelOrderAsync(new OrderCancelRequest +// { +// No = request.No, +// Reason = request.Reason, +// }); +// } +// +// +// public async Task ManagerCancelOrderAsync(OrderCancelRequest request) +// { +// var now = DateTime.Now; +// var userId = CurrentUser.GetIntSubjectId(); +// +// var order = await DbContext.Queryable().Where(x => x.No == request.No).FirstAsync(); +// order.ThrowIfNull("订单不存在"); +// +// await DbContext.Updateable() +// .SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Cancel) +// .SetColumns(x => x.Remark == request.Reason) +// .SetColumns(x => x.ModifiedTime == now) +// .SetColumns(x => x.ModifiedBy == userId) +// .Where(x => x.No == request.No) +// .ExecuteCommandAsync(); +// +// await DbContext.Updateable() +// .SetColumns(x => x.Status == BalanceRecordStatusEnumType.Obsolute) +// .SetColumns(x => x.Remark == request.Reason) +// .SetColumns(x => x.ModifiedTime == now) +// .SetColumns(x => x.ModifiedBy == userId) +// .Where(x => x.No == request.No) +// .ExecuteCommandAsync(); +// +// if (order.Source == BalanceRecordSourceEnumType.LocalTenantSubsidies) +// { +// await CommonHelper.RetryAsync(async () => +// { +// var mealInfo = await DbContext.Queryable().Where(x => x.UserCode == order.UserCode).FirstAsync(); +// var setBalance = mealInfo.Balance + Math.Abs(order.Amount); +// await DbContext.Updateable() +// .SetColumns(x => x.Version == x.Version + 1) +// .SetColumns(x => x.Balance == setBalance) +// .SetColumns(x => x.ModifiedTime == now) +// .SetColumns(x => x.ModifiedBy == userId) +// .Where(x => x.Id == mealInfo.Id && x.Version == mealInfo.Version) +// .ExecuteCommandThrowVersionAsync(); +// }, 5); +// } +// } +// +// +// /// +// /// 检查我的订餐是否存在,存在抛出异常 +// /// +// /// +// /// +// /// +// private async Task CheckExistsMyBookingsAsync(DateTime bookingDate, ShiftEnumType shiftType) +// { +// var subjectId = CurrentUser.GetIntSubjectId(); +// var gainfo = await DbContext.Queryable().Where(x => x.IdentityUserId == subjectId).FirstAsync(); +// var jinkoCode = CurrentUser.GetUserClaim("jinko-code") ?? string.Empty; +// if (jinkoCode.IsPresent()) +// { +// gainfo = await DbContext.Queryable().Where(x => x.Code == jinkoCode).FirstAsync(); +// } +// +// var start = bookingDate.Date.AddDays(-1); +// var end = start.AddDays(1).AddSeconds(-1); +// +// var exists = await DbContext.Queryable() +// .Where(x => x.ApplicationType == CanteenApplicationTypeEnumType.Booking) +// .Where(x => x.OrderStatus == CanteenOrderStatusEnumType.Success) +// .Where(x => x.ShiftType == shiftType) +// .Where(x => x.UserNo == gainfo.No) +// .Where(x => x.CreateTime >= start && x.CreateTime <= end) +// .AnyAsync(); +// +// exists.ThrowIfTrue("不可重复订餐"); +// +// return gainfo; +// } +// } \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Services/Implements/MiddleService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/MiddleService.cs index 213b4c83..f0a1bb2f 100644 --- a/Src/Dlhis.Canteen/Order/Services/Implements/MiddleService.cs +++ b/Src/Dlhis.Canteen/Order/Services/Implements/MiddleService.cs @@ -1,3 +1,4 @@ +using Dlhis.Canteen.Order.Repository; using Dlhis.Canteen.Order.Response; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Canteen.Entities; @@ -8,12 +9,12 @@ namespace Dlhis.Canteen.Order.Services.Implements; public class MiddleService : IMiddleService { private readonly IDbContext _dbContext; - private readonly IBalanceService _balanceService; + private readonly IBalanceRepository _balanceRepository; - public MiddleService(IDbContext dbContext, IBalanceService balanceService) + public MiddleService(IDbContext dbContext, IBalanceRepository balanceRepository) { _dbContext = dbContext; - _balanceService = balanceService; + _balanceRepository = balanceRepository; } /// @@ -25,21 +26,21 @@ public class MiddleService : IMiddleService if (!await _dbContext.Queryable().AnyAsync(x => x.No == entity.No)) { await _dbContext.InsertAsync(entity); - await _balanceService.ReCalcAsync(entity.UserCode); } + var balance = await _balanceRepository.GetBalanceAsync(entity.UserCode, string.Empty); + //获取人员余额 - return await _dbContext.Queryable() - .LeftJoin((x, ga) => x.UserCode == ga.Code) - .LeftJoin((x, ga, department) => ga.DepartmentNo == department.No) - .Where(x => x.UserCode == entity.UserCode) - .Select((x, ga, department) => new OrderResultResponse + return await _dbContext.Queryable() + .LeftJoin((x, department) => x.DepartmentNo == department.No) + .Where(x => x.Code == entity.UserCode) + .Select((x, department) => new OrderResultResponse { - UserCode = ga.Code, - UserName = ga.Name, - DepartmentNo = ga.DepartmentNo, + UserCode = x.Code, + UserName = x.Name, + DepartmentNo = x.DepartmentNo, DepartmentName = department.Name, - CurrentBalance = x.Balance + CurrentBalance = balance }) .SingleAsync(); } diff --git a/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs index 59585d0b..ff0362d8 100644 --- a/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs +++ b/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs @@ -1,4 +1,5 @@ using Dlhis.Canteen.Order.Dto; +using Dlhis.Canteen.Order.Repository; using Dlhis.Canteen.Setting.Services; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Canteen.Entities; @@ -11,6 +12,7 @@ using SqlSugar; using Dlhis.Canteen.Order.Response; using Dlhis.Canteen.Order.Request; using Dlhis.Entity.Device; +using Dlhis.Entity.Device.Enums; using Kurisu.AspNetCore.Authentication.Abstractions; using Kurisu.AspNetCore.CustomClass; using Kurisu.AspNetCore.DataAccess.Dto; @@ -20,21 +22,34 @@ using Kurisu.AspNetCore.UnifyResultAndValidation; namespace Dlhis.Canteen.Order.Services.Implements; -public class OrderService : BaseOrderService, IOrderService +public class OrderService : IOrderService { - private readonly IServiceProvider _serviceProvider; + private readonly ICanteenSettingService _canteenSettingService; + private readonly IDbContext _dbContext; + private readonly ICurrentUser _currentUser; private readonly IHttpContextAccessor _httpContextAccessor; + private readonly IBalanceRepository _balanceRepository; + private readonly IOrderRepository _orderRepository; + private readonly RedisCache _redisCache; + private readonly ILogger _logger; public OrderService(ICanteenSettingService canteenSettingService, IDbContext dbContext, ICurrentUser currentUser, - IServiceProvider serviceProvider, IHttpContextAccessor httpContextAccessor, + IBalanceRepository balanceRepository, + IOrderRepository orderRepository, + RedisCache redisCache, ILogger logger) - : base(canteenSettingService, dbContext, currentUser, logger) { - _serviceProvider = serviceProvider; + _canteenSettingService = canteenSettingService; + _dbContext = dbContext; + _currentUser = currentUser; _httpContextAccessor = httpContextAccessor; + _balanceRepository = balanceRepository; + _orderRepository = orderRepository; + _redisCache = redisCache; + _logger = logger; } /// @@ -42,7 +57,7 @@ public class OrderService : BaseOrderService, IOrderService /// /// /// - public async Task DeviceConsume(OrderAddRequest request) + public async Task> DeviceConsume(OrderAddRequest request) { return await AddAsync(request); } @@ -52,48 +67,29 @@ public class OrderService : BaseOrderService, IOrderService /// /// /// - public async Task AddAsync(OrderAddRequest request) + public async Task> AddAsync(OrderAddRequest request) { - try - { - if (_httpContextAccessor.HttpContext!.Request.Headers.TryGetValue("X-Requested-DeviceIp", out var ip)) - { - var device = await DbContext.Queryable().Where(x => x.UniqueNo == request.DeviceUniqueNo).SingleAsync(); - device.ConnectionInfo = new - { - ip = ip.ToString() - }.ToJson(); + await SetDeviceIpInfoAsync(request.DeviceUniqueNo); - await DbContext.UpdateAsync(device); - } - - Logger.LogInformation("设备IP获取:{deviceNo} {ip}", request.DeviceUniqueNo, ip); - } - catch (Exception) - { - //ignore - } + await using var handler = await _redisCache.LockAsync($"consume:device:{request.UserUniqueNo}"); + handler.Acquired.ThrowIfFalse("请勿频繁消费"); var gainfo = await GetUserAsync(request); - - return new OrderResultResponseWithLogs - { - Response = await ToDoAsync(request, gainfo) - }; + return await ToDoAsync(request, gainfo); } public async Task> GetOrdersAggregationAsync(PageDto page) { - var subjectId = CurrentUser.GetIntSubjectId(); - var gainfo = await DbContext.Queryable().Where(x => x.IdentityUserId == subjectId).FirstAsync(); - var jinkoCode = CurrentUser.GetUserClaim("jinko-code") ?? string.Empty; + var subjectId = _currentUser.GetIntSubjectId(); + var gainfo = await _dbContext.Queryable().Where(x => x.IdentityUserId == subjectId).FirstAsync(); + var jinkoCode = _currentUser.GetUserClaim("jinko-code") ?? string.Empty; if (jinkoCode.IsPresent()) { - gainfo = await DbContext.Queryable().Where(x => x.Code == jinkoCode).FirstAsync(); + gainfo = await _dbContext.Queryable().Where(x => x.Code == jinkoCode).FirstAsync(); } - var pageResult = await DbContext.Queryable().Where(x => x.UserNo == gainfo.No) + var pageResult = await _dbContext.Queryable().Where(x => x.UserNo == gainfo.No) .LeftJoin((x, bPlace) => x.BusinessPlaceCode == bPlace.Code) .Where(x => x.ApplicationType == CanteenApplicationTypeEnumType.Booking) .OrderBy(x => x.CreateTime, OrderByType.Desc) @@ -108,7 +104,7 @@ public class OrderService : BaseOrderService, IOrderService .ToPageListAsync(page); var nos = pageResult.Data.Select(x => x.No).ToList(); - var dtos = await DbContext.Queryable() + var dtos = await _dbContext.Queryable() .LeftJoin((x, d) => x.DishesCode == d.CustomCode) .Where(x => nos.Contains(x.OrderNo)) .Select((x, d) => new DishesDto @@ -150,10 +146,10 @@ public class OrderService : BaseOrderService, IOrderService else if (request.UserUniqueNo.StartsWith("02")) { request.Source = BalanceRecordSourceEnumType.EMealVoucher; - var eMealVoucherQrCode = await DbContext.Queryable().Where(x => x.QrCode == request.UserUniqueNo).SingleAsync(); + var eMealVoucherQrCode = await _dbContext.Queryable().Where(x => x.QrCode == request.UserUniqueNo).SingleAsync(); eMealVoucherQrCode.ThrowIfNull("无法识别"); var code = eMealVoucherQrCode.OriginData.Split(':')[2]; - gainfo = await DbContext.Queryable().Where(x => x.Code == code).SingleAsync(); + gainfo = await _dbContext.Queryable().Where(x => x.Code == code).SingleAsync(); } else { @@ -163,7 +159,7 @@ public class OrderService : BaseOrderService, IOrderService else { //根据档案号 - gainfo = await DbContext.Queryable() + gainfo = await _dbContext.Queryable() .WhereIF(request.Source == BalanceRecordSourceEnumType.Card, x => x.CardNo == request.UserUniqueNo) .WhereIF(request.Source != BalanceRecordSourceEnumType.Card && !request.IsUserId, x => x.Code == request.UserUniqueNo) .WhereIF(request.Source != BalanceRecordSourceEnumType.Card && request.IsUserId && s, x => x.Id == userId) @@ -179,7 +175,7 @@ public class OrderService : BaseOrderService, IOrderService else { //兼容hpt尚未删除人员后产生的消费数据 - gainfo ??= await DbContext.Queryable().AS("GainfoLeave") + gainfo ??= await _dbContext.Queryable().AS("GainfoLeave") .WhereIF(request.Source == BalanceRecordSourceEnumType.Card, x => x.CardNo == request.UserUniqueNo) .WhereIF(request.Source != BalanceRecordSourceEnumType.Card && !request.IsUserId, x => x.Code == request.UserUniqueNo) .WhereIF(request.Source != BalanceRecordSourceEnumType.Card && request.IsUserId && s, x => x.IdentityUserId == userId) @@ -190,33 +186,6 @@ public class OrderService : BaseOrderService, IOrderService } - /// - /// 获取成本日期 - /// - /// - /// - /// - private async Task GetCostDateAsync(BusinessShiftMealRelationshipEntity shift, DateTime costDateTime) - { - var switchTime = await GetSettingAsync(string.Empty, shift.TenantId, CanteenSettingItemEnumType.BusinessSwitchTime); - - var costDate = costDateTime.Date; - - if (!switchTime.HasValue) - { - return costDate; - } - - var beginOfDay = costDateTime.Date; - - var setTime = beginOfDay.Add(switchTime.Value.ToTimeSpan()); - if (costDateTime < setTime) - costDate = costDateTime.Date.AddDays(-1); - - return costDate; - } - - /// /// 验证参数 /// @@ -229,21 +198,16 @@ public class OrderService : BaseOrderService, IOrderService var endOfDay = beginOfDay.AddDays(1).AddSeconds(-1); //今天所有消费记录 - var todayAllBalanceRecords = await DbContext.Queryable() - .Where(x => x.UserNo == order.UserNo) - .Where(x => x.Type == BalanceRecordTypeEnumType.Out) - .Where(x => x.Status == BalanceRecordStatusEnumType.Valid) - .Where(x => x.CreateTime >= beginOfDay && x.CreateTime <= endOfDay) - .ToListAsync(); + var todayAllBalanceRecords = await _balanceRepository.GetBalanceRecordsAsync(order.UserCode, order.TenantId, beginOfDay, endOfDay); //超市营业点 - var supermarketCodes = await DbContext.Queryable() + var supermarketCodes = await _dbContext.Queryable() .Where(x => x.Catalog == CanteenBusinessPlaceCatalogEnumType.Supermarket) .Select(x => x.Code) .ToListAsync(); //一天消费金额 - var dayCostMax = await GetSettingAsync(order.UserNo, order.TenantId, CanteenSettingItemEnumType.DayCostMax); + var dayCostMax = await _canteenSettingService.GetSettingAsync(CanteenSettingItemEnumType.DayCostMax, order.TenantId, order.UserNo); if (dayCostMax != default) { //消费记录一定会有营业点代码!.Value是安全用法 @@ -255,7 +219,7 @@ public class OrderService : BaseOrderService, IOrderService } //一次消费金额 - var onceCostMax = await GetSettingAsync(order.UserNo, order.TenantId, CanteenSettingItemEnumType.OnceCostMax); + var onceCostMax = await _canteenSettingService.GetSettingAsync(CanteenSettingItemEnumType.OnceCostMax, order.TenantId, order.UserNo); if (onceCostMax != default) { (Math.Abs(order.Amount) > onceCostMax).ThrowIfTrue($"单次消费金额不能超出{onceCostMax}元"); @@ -264,7 +228,7 @@ public class OrderService : BaseOrderService, IOrderService //超市消费限额 if (order.BusinessPlaceCode.HasValue && supermarketCodes.Contains(order.BusinessPlaceCode.Value)) { - var dayMarketCostMax = await GetSettingAsync(order.UserNo, order.TenantId, CanteenSettingItemEnumType.DayMarketCostMax); + var dayMarketCostMax = await _canteenSettingService.GetSettingAsync(CanteenSettingItemEnumType.DayMarketCostMax, order.TenantId, order.UserNo); if (dayMarketCostMax != default) { var cost = todayAllBalanceRecords.Where(x => x.BusinessPlaceCode.HasValue) @@ -276,7 +240,7 @@ public class OrderService : BaseOrderService, IOrderService } //重复餐次消费 - var allowRepeatInOneShift = await GetSettingAsync(order.UserNo, order.TenantId, CanteenSettingItemEnumType.AllowRepeatInOneShift, true); + var allowRepeatInOneShift = await _canteenSettingService.GetSettingAsync(CanteenSettingItemEnumType.AllowRepeatInOneShift, order.TenantId, order.UserNo, true); if (!allowRepeatInOneShift) { var costCount = todayAllBalanceRecords.Count(x => x.ShiftType == order.ShiftType); @@ -284,77 +248,44 @@ public class OrderService : BaseOrderService, IOrderService } } - - /// - /// 生成充值记录 - /// - /// - /// - /// - /// - private async Task GenerateConsumeRecordAsync(long orderId) - { - var order = await DbContext.Queryable().Where(x => x.Id == orderId).SingleAsync(); - //校验 - await Verify(order); - - await CommonHelper.RetryAsync(async () => - { - var gainfoMeal = await DbContext.Queryable().Where(x => x.UserCode == order.UserCode).SingleAsync(); - //金额+- - gainfoMeal.Balance += order.Amount; - (gainfoMeal.Balance < 0).ThrowIfTrue("余额不足,无法消费"); - - await DbContext.Updateable() - .SetColumns(x => x.Balance == gainfoMeal.Balance) - .SetColumns(x => x.Version == x.Version + 1) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .Where(x => x.UserCode == order.UserCode && x.Version == gainfoMeal.Version) //必须与查询时的version一致 - .ExecuteCommandThrowVersionAsync(); - - //写入记录 - var balanceRecord = order.Adapt(); - await DbContext.InsertAsync(balanceRecord); - }, 5); - } - /// /// 离线消费处理 /// /// /// - private async Task HandleOfflineAsync(ConsumeRequest request, GainfoEntity gainfo) + private async Task OfflineHandleAsync(ConsumeRequest request, GainfoEntity gainfo) { - var registry = await DbContext.Queryable() - .Where(x => x.DeviceUniqueNo == request.DeviceUniqueNo) - .FirstAsync(); - - var businessPlaceInfo = await DbContext.Queryable() - .LeftJoin((x, sr) => x.Code == sr.PlaceCode) - .Where(x => x.Code == registry.BusinessPlaceCode) - .Select((x, sr) => new + var bindingInfo = await _dbContext.Queryable() + .LeftJoin((x, b) => x.BusinessPlaceCode == b.Code) + .LeftJoin((x, b, bs) => bs.PlaceCode == b.Code) + .Where(x => x.DeviceUniqueNo == request.DeviceUniqueNo && x.BusinessPlaceCode.HasValue) + .Select((x, b, bs) => new { - x.Code, - sr.ShiftCode, - x.TenantId + b.Code, bs.ShiftCode, Factory = x.TenantId }) - .FirstAsync(); + .SingleAsync(); + //获取当前消费餐次 - var shifts = await DbContext.Queryable().Where(x => x.ShiftCode == businessPlaceInfo.ShiftCode).ToListAsync(); + var shifts = await _dbContext.Queryable() + .Where(x => x.ShiftCode == bindingInfo.ShiftCode).ToListAsync(); - var shift = shifts.FirstOrDefault(x => new TimePeriod() - { - Start = x.Start, - End = x.End - }.IsPresent(request.CostDateTime)); + var shift = shifts.FirstOrDefault(x => + new TimePeriod + { + Start = x.Start, + End = x.End + }.IsPresent(request.CostDateTime) + ); - var costDate = await GetCostDateAsync(shift, request.CostDateTime); + var costDate = await _canteenSettingService.GetCostDateAsync(bindingInfo.Factory, request.CostDateTime); var order = new CanteenOrderEntity { RequestJson = request.ToJson(), + No = SnowFlakeHelper.Instance.NextId().ToString(), ThirdNo = request.ThirdNo, + IsOffline = request.IsOffline, Type = BalanceRecordTypeEnumType.Out, ApplicationType = CanteenApplicationTypeEnumType.Device, Name = gainfo.Name, @@ -362,7 +293,7 @@ public class OrderService : BaseOrderService, IOrderService UserNo = gainfo.No, UserCode = gainfo.Code, Amount = request.Amount, - BusinessPlaceCode = businessPlaceInfo?.Code, + BusinessPlaceCode = bindingInfo.Code, BusinessShiftCode = shift?.ShiftCode, ShiftType = shift?.ShiftType, CostDate = costDate.Date, @@ -370,9 +301,7 @@ public class OrderService : BaseOrderService, IOrderService DepartmentNo = gainfo.DepartmentNo, DeviceUniqueNo = request.DeviceUniqueNo, - No = SnowFlakeHelper.Instance.NextId().ToString(), - IsOffline = request.IsOffline, - IsCostCrossTenant = businessPlaceInfo!.TenantId != gainfo.Factory, + IsCostCrossTenant = bindingInfo.Factory != gainfo.Factory, OriginTenantId = gainfo.Factory, Werks = gainfo.Werks, @@ -384,48 +313,12 @@ public class OrderService : BaseOrderService, IOrderService CostDateTime = request.CostDateTime, //标识当前消费的基地 - TenantId = businessPlaceInfo!.TenantId, + TenantId = bindingInfo.Factory, }; - - await DbContext.InsertReturnIdentityAsync(order); - - await CommonHelper.RetryAsync(async () => - { - var redisCache = _serviceProvider.GetService(); - await using var handler = await redisCache.LockAsync($"gainfo:meal:{order.UserNo}", retryInterval: TimeSpan.FromSeconds(1)); - if (handler.Acquired) - { - var gainfoMeal = await DbContext.Queryable().Where(x => x.UserCode == order.UserCode).FirstAsync(); - if (gainfoMeal == null) - { - gainfoMeal = new GainfoMealEntity - { - Balance = 0, - BalanceOfRecharge = 0, - BalanceOfSubsidies = 0, - Version = 0, - UserCode = order.UserCode, - }; - - await DbContext.InsertAsync(gainfoMeal); - } - - //金额+- - gainfoMeal.Balance += order.Amount; - - await DbContext.Updateable() - .SetColumns(x => x.Balance == gainfoMeal.Balance) - .SetColumns(x => x.Version == x.Version + 1) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .Where(x => x.UserCode == order.UserCode && x.Version == gainfoMeal.Version) //必须与查询时的version一致 - .ExecuteCommandThrowVersionAsync(); - - //写入记录 - var balanceRecord = order.Adapt(); - await DbContext.InsertAsync(balanceRecord); - } - }, 5); + await _dbContext.InsertAsync(order); + var balanceRecord = order.Adapt(); + await _dbContext.InsertAsync(balanceRecord); } /// @@ -437,80 +330,128 @@ public class OrderService : BaseOrderService, IOrderService private async Task ConsumeAsync(ConsumeRequest request, GainfoEntity gainfo) { var checkData = await PreCheckAsync(request.DeviceUniqueNo, request.CostDateTime); + var order = await _orderRepository.CreateDeviceConsumeOrderAsync(checkData, request, gainfo); - //(request.Source == BalanceRecordSourceEnumType.QrCode && checkData.BusinessPlaceTenantId != gainfo.Factory).ThrowIfTrue("禁止跨厂区消费"); - - var order = new CanteenOrderEntity + if (request.Image.IsPresent()) { - RequestJson = request.ToJson(), - ThirdNo = request.ThirdNo, - Type = BalanceRecordTypeEnumType.Out, - ApplicationType = CanteenApplicationTypeEnumType.Device, - Name = gainfo.Name, - Source = request.Source, - UserNo = gainfo.No, - UserCode = gainfo.Code, - Amount = request.Amount, - BusinessPlaceCode = checkData.BusinessPlaceCode, - BusinessShiftCode = checkData.BusinessShiftCode, - ShiftType = checkData.Shift.ShiftType, - CostDate = checkData.CostDate, - CostId = gainfo.CostId, - DepartmentNo = gainfo.DepartmentNo, - DeviceUniqueNo = request.DeviceUniqueNo, + await _dbContext.InsertAsync(new ImageRecordEntity + { + No = order.No, + TableName = "B", + Image = request.Image + }); + } - No = SnowFlakeHelper.Instance.NextId().ToString(), - IsOffline = request.IsOffline, - IsCostCrossTenant = gainfo.Factory != checkData.BusinessPlaceTenantId, + await TryConsumeAsync(order.Id, async () => + { + //校验 + await Verify(order); - OriginTenantId = gainfo.Factory, - Werks = gainfo.Werks, - Status = BalanceRecordStatusEnumType.Valid, + await _balanceRepository.CheckConsumableAsync(order.UserCode, order.TenantId, order.Amount); - OrderStatus = CanteenOrderStatusEnumType.Wait, + //写入记录 + var balanceRecord = order.Adapt(); + await _dbContext.InsertAsync(balanceRecord); + }); + } - CostDateTime = request.CostDateTime, + private async Task> EMealVoucherConsumeAsync(OrderAddRequest request, GainfoEntity gainfo) + { + var msg = string.Empty; + var originData = await _dbContext.Queryable() + .Where(x => x.QrCode == request.UserUniqueNo) + .Select(x => x.OriginData) + .SingleAsync(); - //标识当前消费的基地 - TenantId = checkData.BusinessPlaceTenantId - }; + if (originData.IsEmpty()) + { + msg = "无法识别"; + return new DefaultApiResult + { + Code = ApiStateCode.Error, + Data = null, + Msg = msg + }; + } - var orderId = await DbContext.InsertReturnIdentityAsync(order); + var originDataItems = originData.Split(":"); + var no = originDataItems[0]; + var idNo = originDataItems[3]; try { - await GenerateConsumeRecordAsync(orderId); - - if (request.Image.IsPresent()) - { - await DbContext.InsertAsync(new ImageRecordEntity - { - No = order.No, - TableName = "B", - Image = request.Image - }); - } - - await DbContext.Updateable() - .SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Success) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .Where(x => x.Id == orderId) - .ExecuteCommandAsync(); + await EMealVoucherConsumeAsync(request.Adapt(), gainfo, originData, no, idNo); } catch (Exception ex) { - await DbContext.Updateable() - .SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Failure) - .SetColumns(x => x.Result == ex.Message) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .Where(x => x.Id == orderId) - .ExecuteCommandAsync(); - - throw; + msg = ex.Message; } + + //获取餐券余额 + var data = await _dbContext.Queryable() + .LeftJoin((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) + { + //ignore + } + + return new DefaultApiResult + { + Code = msg.IsPresent() ? ApiStateCode.Error : ApiStateCode.Success, + Data = data, + Msg = msg + }; } + 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(), gainfo); + } + } + else + { + await OfflineHandleAsync(request.Adapt(), gainfo); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "离线入库错误: 订单号:{no} .{message}", request.ThirdNo, ex.Message); + } + } + /// /// 分支处理在线/离线消费 /// @@ -522,158 +463,55 @@ public class OrderService : BaseOrderService, IOrderService var message = string.Empty; if (request.Source == BalanceRecordSourceEnumType.EMealVoucher) + return await EMealVoucherConsumeAsync(request, gainfo); + + if (request.IsOffline) + await OfflineConsumeAsync(request, gainfo); + else { - var eMealVoucherQrCode = await DbContext.Queryable().Where(x => x.QrCode == request.UserUniqueNo).SingleAsync(); - if (eMealVoucherQrCode == null) - { - message = "无法识别"; - return new DefaultApiResult - { - Code = ApiStateCode.Error, - Data = null, - Msg = message - }; - } - - var originData = eMealVoucherQrCode.OriginData.Split(':'); - var no = originData[0]; - var idNo = originData[3]; - try { - await EMealVoucherConsumeAsync(request.Adapt(), gainfo, eMealVoucherQrCode.OriginData, no, idNo); + await ConsumeAsync(request.Adapt(), gainfo); } catch (Exception ex) { message = ex.Message; } - - //获取餐券余额 - var data = await DbContext.Queryable() - .LeftJoin((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 - { - Code = message.IsPresent() ? ApiStateCode.Error : ApiStateCode.Success, - Data = data, - Msg = message - }; } - else + + //获取人员信息 + var currentFactory = await _dbContext.Queryable() + .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() + .LeftJoin((x, department) => x.DepartmentNo == department.No) + .Where(x => x.Code == gainfo.Code) + .Select((x, department) => new OrderResultResponse + { + UserCode = x.Code, + UserName = x.Name, + DepartmentNo = x.DepartmentNo, + DepartmentName = department.Name, + + CurrentBalance = balance, + CurrentTotalBalance = totalBalance + }) + .SingleAsync(); + + return new DefaultApiResult { - if (request.IsOffline) - { - if (request.ThirdNo.IsPresent()) - { - var redisCache = _serviceProvider.GetService(); - - await using var handler = await redisCache.LockAsync($"consume:device:{request.ThirdNo}"); - if (handler.Acquired && await ToCheckAbleOfflineConsumeAsync(request.ThirdNo)) - { - try - { - await HandleOfflineAsync(request.Adapt(), gainfo); - } - catch (Exception ex) - { - Logger.LogError(ex, "离线入库错误: 订单号:{no} .{message}", request.ThirdNo, ex.Message); - } - } - } - else - { - try - { - await HandleOfflineAsync(request.Adapt(), gainfo); - } - catch (Exception ex) - { - Logger.LogError(ex, "离线入库错误: 订单号:{no} .{message}", request.ThirdNo, ex.Message); - } - } - } - else - { - try - { - (await DbContext.Queryable().Where(x => x.UserCode == gainfo.Code).AnyAsync()).ThrowIfFalse("就餐余额不足"); - await ConsumeAsync(request.Adapt(), gainfo); - } - catch (Exception ex) - { - message = ex.Message; - } - } - - //获取人员余额 - var data = await DbContext.Queryable() - .LeftJoin((x, ga) => x.UserCode == ga.Code) - .LeftJoin((x, ga, department) => ga.DepartmentNo == department.No) - .Where(x => x.UserCode == gainfo.Code) - .Select((x, ga, department) => new OrderResultResponse - { - UserCode = ga.Code, - UserName = ga.Name, - DepartmentNo = ga.DepartmentNo, - DepartmentName = department.Name, - CurrentBalance = x.Balance - }) - .SingleAsync(); - - return new DefaultApiResult - { - Code = message.IsPresent() ? ApiStateCode.Error : ApiStateCode.Success, - Data = data, - Msg = message - }; - } - - async Task ToCheckAbleOfflineConsumeAsync(string thirdNo) - { - var order = await DbContext.Queryable() - .Where(x => x.ThirdNo == thirdNo) - .OrderBy(x => x.CreateTime, OrderByType.Desc) - .FirstAsync(); - - if (order == null) - { - return true; - } - - //存在在线order数据,但是状态未成功 - return !order.IsOffline && order.OrderStatus != CanteenOrderStatusEnumType.Success; - } + Code = message.IsPresent() ? ApiStateCode.Error : ApiStateCode.Success, + Data = data, + Msg = message + }; } + /// /// 根据二维码获取消费用户 /// @@ -681,74 +519,57 @@ public class OrderService : BaseOrderService, IOrderService /// private async Task GetUserByQrCodeAsync(string qrCode) { - var gainfoQrCode = await DbContext.Queryable().Where(x => x.QrCode == qrCode).SingleAsync(); + var gainfoQrCode = await _dbContext.Queryable().Where(x => x.QrCode == qrCode).SingleAsync(); gainfoQrCode.ThrowIfNull("消费码已过期"); (gainfoQrCode.ModifiedTime.AddMinutes(60) < DateTime.Now).ThrowIfTrue("消费码已过期"); - return await DbContext.Queryable().Where(x => x.Code == gainfoQrCode.UserCode).SingleAsync(); + return await _dbContext.Queryable().Where(x => x.Code == gainfoQrCode.UserCode).SingleAsync(); } + /// + /// 电子餐券消费 + /// + /// + /// + /// + /// + /// private async Task EMealVoucherConsumeAsync(ConsumeRequest request, GainfoEntity gainfo, string originData, string no, string idNo) { - var now = DateTime.Now; - var current = await DbContext.Queryable() - .Where(x => x.No == no && x.IdNo == idNo).SingleAsync(); - (now >= current.Start && now <= current.End).ThrowIfFalse("餐券已过期"); + var nowDate = DateTime.Now.Date; - var eMealVoucher = await DbContext.Queryable().Where(x => x.No == no).SingleAsync(); + var current = await _dbContext.Queryable() + .Where(x => x.No == no && x.IdNo == idNo) + .Select(x => new { x.Start, x.End }) + .SingleAsync(); + + (nowDate >= current.Start && nowDate <= current.End).ThrowIfFalse("餐券已过期"); + + + var eMealVoucher = await _dbContext.Queryable().Where(x => x.No == no).SingleAsync(); var checkData = await PreCheckAsync(request.DeviceUniqueNo, request.CostDateTime); - var isSupermarket = await DbContext.Queryable().Where(x => x.Code == checkData.BusinessPlaceCode) + var isSupermarket = await _dbContext.Queryable().Where(x => x.Code == checkData.BusinessPlaceCode) .Select(x => x.Catalog == CanteenBusinessPlaceCatalogEnumType.Supermarket) .SingleAsync(); + isSupermarket.ThrowIfTrue("禁止在超市消费"); - eMealVoucher.Factories.Contains(checkData.BusinessPlaceTenantId).ThrowIfFalse("禁止在当前厂区消费"); + eMealVoucher.Factories.Contains(checkData.Factory).ThrowIfFalse("禁止在当前厂区消费"); - var order = new CanteenOrderEntity - { - RequestJson = request.ToJson(), - ThirdNo = request.ThirdNo, - Type = BalanceRecordTypeEnumType.Out, - ApplicationType = CanteenApplicationTypeEnumType.Device, - Name = gainfo.Name, - Source = request.Source, - UserNo = originData, //记录为当前消费券 - UserCode = gainfo.Code, - Amount = request.Amount, - BusinessPlaceCode = checkData.BusinessPlaceCode, - BusinessShiftCode = checkData.BusinessShiftCode, - ShiftType = checkData.Shift.ShiftType, - CostDate = checkData.CostDate, - CostId = gainfo.CostId, - DepartmentNo = gainfo.DepartmentNo, - DeviceUniqueNo = request.DeviceUniqueNo, + gainfo.No = originData; + var order = await _orderRepository.CreateDeviceConsumeOrderAsync(checkData, request, gainfo); - No = SnowFlakeHelper.Instance.NextId().ToString(), - IsOffline = false, - IsCostCrossTenant = gainfo.Factory != checkData.BusinessPlaceTenantId, - - OriginTenantId = gainfo.Factory, - Werks = gainfo.Werks, - Status = BalanceRecordStatusEnumType.Valid, - OrderStatus = CanteenOrderStatusEnumType.Wait, - CostDateTime = request.CostDateTime, - //标识当前消费的基地 - TenantId = checkData.BusinessPlaceTenantId, - }; - - var orderId = await DbContext.InsertReturnIdentityAsync(order); - - try + await TryConsumeAsync(order.Id, async () => { await CommonHelper.RetryAsync(async () => { - var detail = await DbContext.Queryable().Where(x => x.No == no && x.IdNo == idNo).SingleAsync(); + var detail = await _dbContext.Queryable().Where(x => x.No == no && x.IdNo == idNo).SingleAsync(); //金额+- detail.Balance += order.Amount; (detail.Balance < 0).ThrowIfTrue("余额不足,无法消费"); - await DbContext.Updateable() + await _dbContext.Updateable() .SetColumns(x => x.Balance == detail.Balance) .SetColumns(x => x.Version == x.Version + 1) .SetColumns(x => x.ModifiedTime == DateTime.Now) @@ -757,26 +578,9 @@ public class OrderService : BaseOrderService, IOrderService //写入记录 var balanceRecord = order.Adapt(); - await DbContext.InsertAsync(balanceRecord); + await _dbContext.InsertAsync(balanceRecord); }, 5); - - await DbContext.Updateable() - .SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Success) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .Where(x => x.Id == orderId) - .ExecuteCommandAsync(); - } - catch (Exception ex) - { - await DbContext.Updateable() - .SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Failure) - .SetColumns(x => x.Result == ex.Message) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .Where(x => x.Id == orderId) - .ExecuteCommandAsync(); - - throw; - } + }); } /// @@ -787,28 +591,28 @@ public class OrderService : BaseOrderService, IOrderService /// private async Task PreCheckAsync(string deviceUniqueNo, DateTime costDateTime) { - var registry = await DbContext.Queryable().Where(x => x.DeviceUniqueNo == deviceUniqueNo).SingleAsync(); - registry.ThrowIfNull("设备尚未登记"); - - var device = await DbContext.Queryable().Where(x => x.UniqueNo == deviceUniqueNo).SingleAsync(); + var device = await _dbContext.Queryable().Where(x => x.UniqueNo == deviceUniqueNo).SingleAsync(); device.ThrowIfNull("设备尚未登记"); device.Enable.ThrowIfFalse("设备已被停用"); + (device.Type != DeviceTypeEnumType.Meal).ThrowIfTrue("设备类型有误"); - var businessPlaceInfo = await DbContext.Queryable() - .LeftJoin((x, sr) => x.Code == sr.PlaceCode) - .Where(x => x.Code == registry.BusinessPlaceCode) - .Select((x, sr) => new + var bindingInfo = await _dbContext.Queryable() + .LeftJoin((x, b) => x.BusinessPlaceCode == b.Code) + .LeftJoin((x, b, bs) => bs.PlaceCode == b.Code) + .Where(x => x.DeviceUniqueNo == deviceUniqueNo && x.BusinessPlaceCode.HasValue) + .Select((x, b, bs) => new { - x.Code, - sr.ShiftCode, - x.TenantId + b.Code, bs.ShiftCode, Factory = x.TenantId }) .SingleAsync(); - businessPlaceInfo.ThrowIfNull("设备尚未登记营业点"); + bindingInfo.ThrowIfNull("设备尚未登记营业点"); //获取当前消费餐次 - var shifts = await DbContext.Queryable().Where(x => x.ShiftCode == businessPlaceInfo.ShiftCode).ToListAsync(); + var shifts = await _dbContext.Queryable() + .Where(x => x.ShiftCode == bindingInfo.ShiftCode) + .Select(x => new { x.ShiftType, x.Start, x.End }) + .ToListAsync(); shifts.ThrowIfEmpty("营业点餐次尚未设置"); var shift = shifts.FirstOrDefault(x => new TimePeriod @@ -816,17 +620,88 @@ public class OrderService : BaseOrderService, IOrderService Start = x.Start, End = x.End }.IsPresent(costDateTime)); - shift.ThrowIfNull("非就餐时间,无法消费"); + shift.ThrowIfNull("非就餐时间"); - var costDate = await GetCostDateAsync(shift, costDateTime); + var costDate = await _canteenSettingService.GetCostDateAsync(bindingInfo.Factory, costDateTime); return new OrderPreCheckDto { + Factory = bindingInfo.Factory, CostDate = costDate, - Shift = shift, - BusinessPlaceTenantId = businessPlaceInfo.TenantId, - BusinessPlaceCode = businessPlaceInfo.Code, - BusinessShiftCode = businessPlaceInfo.ShiftCode + ShiftType = shift!.ShiftType, + BusinessPlaceCode = bindingInfo.Code, + BusinessShiftCode = bindingInfo.ShiftCode }; } + + /// + /// 设置设备ip + /// + /// + private async Task SetDeviceIpInfoAsync(string deviceNo) + { + try + { + if (_httpContextAccessor.HttpContext!.Request.Headers.TryGetValue("X-Requested-DeviceIp", out var ip)) + { + var device = await _dbContext.Queryable().Where(x => x.UniqueNo == deviceNo).SingleAsync(); + device.ConnectionInfo = new + { + ip = ip.ToString() + }.ToJson(); + + await _dbContext.UpdateAsync(device); + } + + _logger.LogInformation("设备ip获取:{deviceNo} {ip}", deviceNo, ip); + } + catch (Exception) + { + //ignore + } + } + + /// + /// 检查是否能够进行离线消费,处理hpt重复提交 + /// + /// + /// + private async Task ToCheckAbleOfflineConsumeAsync(string thirdNo) + { + var order = await _dbContext.Queryable() + .Where(x => x.ThirdNo == thirdNo) + .OrderBy(x => x.CreateTime, OrderByType.Desc) + .SingleAsync(); + + if (order == null) + return true; + + //存在在线order数据,但是状态未成功 + return !order.IsOffline && order.OrderStatus != CanteenOrderStatusEnumType.Success; + } + + private async Task TryConsumeAsync(long orderId, Func action) + { + try + { + await action(); + + await _dbContext.Updateable() + .SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Success) + .SetColumns(x => x.ModifiedTime == DateTime.Now) + .Where(x => x.Id == orderId) + .ExecuteCommandAsync(); + } + catch (Exception ex) + { + await _dbContext.Updateable() + .SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Failure) + .SetColumns(x => x.ModifiedTime == DateTime.Now) + .SetColumns(x => x.Result == ex.Message) + .Where(x => x.Id == orderId) + .ExecuteCommandAsync(); + + throw; + } + } } \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Setting/Controllers/WalletsController.cs b/Src/Dlhis.Canteen/Setting/Controllers/WalletsController.cs new file mode 100644 index 00000000..36fd7142 --- /dev/null +++ b/Src/Dlhis.Canteen/Setting/Controllers/WalletsController.cs @@ -0,0 +1,46 @@ +using System.ComponentModel.DataAnnotations; +using Dlhis.Canteen.Setting.Response; +using Dlhis.Canteen.Setting.Services; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Dlhis.Canteen.Setting.Controllers; + +/// +/// 钱包 +/// +[Authorize] +[Route("api/[controller]")] +[ApiController] +public class WalletsController : ControllerBase +{ + private readonly IWalletService _walletService; + + public WalletsController(IWalletService walletService) + { + _walletService = walletService; + } + + /// + /// 钱包列表 + /// + /// + [HttpGet("list")] + public async Task> GetListAsync() + { + return await _walletService.GetListAsync(); + } + + /// + /// 余额 + /// + /// 钱包code + /// + [HttpGet("balance")] + [IgnoreTenant] + public async Task GetBalanceAsync([Required] string walletCode) + { + return await _walletService.GetBalanceAsync(walletCode); + } +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Setting/Response/WalletResponse.cs b/Src/Dlhis.Canteen/Setting/Response/WalletResponse.cs new file mode 100644 index 00000000..6e4879a3 --- /dev/null +++ b/Src/Dlhis.Canteen/Setting/Response/WalletResponse.cs @@ -0,0 +1,19 @@ +namespace Dlhis.Canteen.Setting.Response; + +public class WalletResponse +{ + /// + /// 钱包编码 + /// + public string Code { get; set; } + + /// + /// 钱包名称 + /// + public string Name { get; set; } + + /// + /// 厂区 + /// + public List Factories { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Setting/Services/ICanteenSettingService.cs b/Src/Dlhis.Canteen/Setting/Services/ICanteenSettingService.cs index ecd8d69d..9c4c6b36 100644 --- a/Src/Dlhis.Canteen/Setting/Services/ICanteenSettingService.cs +++ b/Src/Dlhis.Canteen/Setting/Services/ICanteenSettingService.cs @@ -7,10 +7,25 @@ namespace Dlhis.Canteen.Setting.Services; public interface ICanteenSettingService : IScopeDependency { + /// + /// 获取配置分页数据 + /// + /// + /// Task> GetPageAsync(CanteenSettingPageRequest request); + /// + /// 保存配置 + /// + /// + /// Task SaveSettingAsync(CanteenSettingSaveRequest request); + /// + /// 设置配置启用开关 + /// + /// + /// Task SetEnableAsync(CanteenSettingSetEnableRequest request); /// @@ -21,7 +36,21 @@ public interface ICanteenSettingService : IScopeDependency /// Task> GetUserSettingAsync(string userNo, string tenantId); - + /// + /// 获取营业点切换时间 + /// + /// + /// Task GetBusinessSwitchTimeAsync(string tenantId); -} + + Task GetSettingAsync(CanteenSettingItemEnumType item, string factory, string userNo, T defaultValue = default); + + /// + /// 获取消费日期 + /// + /// + /// + /// + Task GetCostDateAsync(string factory, DateTime costDateTime); +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Setting/Services/IWalletService.cs b/Src/Dlhis.Canteen/Setting/Services/IWalletService.cs new file mode 100644 index 00000000..38aa63f8 --- /dev/null +++ b/Src/Dlhis.Canteen/Setting/Services/IWalletService.cs @@ -0,0 +1,10 @@ +using Dlhis.Canteen.Setting.Response; + +namespace Dlhis.Canteen.Setting.Services; + +public interface IWalletService : IScopeDependency +{ + Task> GetListAsync(); + + Task GetBalanceAsync(string walletCode); +} \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Setting/Services/Implements/CanteenSettingService.cs b/Src/Dlhis.Canteen/Setting/Services/Implements/CanteenSettingService.cs index 39300afe..c5654f0d 100644 --- a/Src/Dlhis.Canteen/Setting/Services/Implements/CanteenSettingService.cs +++ b/Src/Dlhis.Canteen/Setting/Services/Implements/CanteenSettingService.cs @@ -114,6 +114,49 @@ public class CanteenSettingService : ICanteenSettingService } } + /// + public async Task GetSettingAsync(CanteenSettingItemEnumType item, string factory, string userNo, T defaultValue = default) + { + var settings = await GetUserSettingAsync(userNo, factory); + + if (settings.TryGetValue(item, out var v)) + { + try + { + if (int.TryParse(v, out var n)) + { + return n.Adapt(); + } + + return v.Adapt(); + } + catch (Exception) + { + return defaultValue; + } + } + + return defaultValue; + } + + /// + public async Task GetCostDateAsync(string factory, DateTime costDateTime) + { + var switchTime = await GetSettingAsync(CanteenSettingItemEnumType.BusinessSwitchTime, factory, string.Empty); + + var costDate = costDateTime.Date; + + if (!switchTime.HasValue) return costDate; + + var beginOfDay = costDateTime.Date; + + var setTime = beginOfDay.Add(switchTime.Value.ToTimeSpan()); + if (costDateTime < setTime) + costDate = costDateTime.Date.AddDays(-1); + + return costDate; + } + //*********************************************** help methods ***************************************************// private async Task CheckDuplicateSettingAsync(CanteenSettingSaveRequest request) diff --git a/Src/Dlhis.Canteen/Setting/Services/Implements/WalletService.cs b/Src/Dlhis.Canteen/Setting/Services/Implements/WalletService.cs new file mode 100644 index 00000000..1ef8e9c5 --- /dev/null +++ b/Src/Dlhis.Canteen/Setting/Services/Implements/WalletService.cs @@ -0,0 +1,45 @@ +using Dlhis.Canteen.Order.Repository; +using Dlhis.Canteen.Setting.Response; +using Dlhis.Entity.Canteen.Entities; +using Kurisu.AspNetCore.Authentication.Abstractions; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +using SqlSugar; + +namespace Dlhis.Canteen.Setting.Services.Implements; + +public class WalletService : IWalletService +{ + private readonly IDbContext _dbContext; + private readonly ICurrentUser _currentUser; + private readonly IBalanceRepository _balanceRepository; + + public WalletService(IDbContext dbContext, ICurrentUser currentUser, IBalanceRepository balanceRepository) + { + _dbContext = dbContext; + _currentUser = currentUser; + _balanceRepository = balanceRepository; + } + + /// + public async Task> GetListAsync() + { + return await _dbContext.Queryable() + .GroupBy(x => new { x.Code, x.Name }) + .Select(x => new WalletResponse + { + Code = x.Code, + Name = x.Name + "钱包", + Factories = SqlFunc.Subqueryable().Where(p => p.Code == x.Code).ToList(p => p.Factory) + }) + .ToListAsync(); + } + + /// + public async Task GetBalanceAsync(string walletCode) + { + var userCode = _currentUser.GetUserClaim("jinko-code"); + var factory = await _dbContext.Queryable().Where(x => x.Code == walletCode).Select(x => x.Factory).FirstAsync(); + + return await _balanceRepository.GetBalanceAsync(userCode, factory); + } +} \ No newline at end of file diff --git a/Src/Dlhis.Entity/Basic/Entity/BusinessShiftMealRelationshipEntity.cs b/Src/Dlhis.Entity/Basic/Entity/BusinessShiftMealRelationshipEntity.cs index d2cc2328..0928de41 100644 --- a/Src/Dlhis.Entity/Basic/Entity/BusinessShiftMealRelationshipEntity.cs +++ b/Src/Dlhis.Entity/Basic/Entity/BusinessShiftMealRelationshipEntity.cs @@ -10,37 +10,37 @@ namespace Dlhis.Entity.Basic.Entity; public class BusinessShiftMealRelationshipEntity : SugarBaseEntity, ITenantId { /// - /// 餐别Code + /// 餐别Code /// [SugarColumn(ColumnDescription = "餐别Code", UniqueGroupNameList = new[] { "idx_CanteenBusinessShiftRelationship_shiftcode_shifttype" })] public Guid ShiftCode { get; set; } /// - /// 餐别手工号 + /// 餐别手工号 /// [SugarColumn(ColumnDescription = "餐别编码")] public string ShiftCustomCode { get; set; } /// - /// 餐次 + /// 餐次 /// [SugarColumn(ColumnDescription = "餐次", ColumnDataType = "varchar(50)", SqlParameterDbType = typeof(EnumToStringConvert), UniqueGroupNameList = new[] { "idx_CanteenBusinessShiftRelationship_shiftcode_shifttype" })] public ShiftEnumType ShiftType { get; set; } /// - /// 经营开始时间 + /// 经营开始时间 /// [SugarColumn(ColumnDescription = "经营开始时间")] public TimeOnly Start { get; set; } /// - /// 经营结束时间 + /// 经营结束时间 /// [SugarColumn(ColumnDescription = "经营结束时间")] public TimeOnly End { get; set; } /// - /// 序号 + /// 序号 /// [SugarColumn(ColumnDescription = "序号")] public int Sequence { get; set; } diff --git a/Src/Dlhis.Entity/Basic/Entity/GainfoMealEntity.cs b/Src/Dlhis.Entity/Basic/Entity/GainfoMealEntity.cs index 1d311bc6..5e779cd9 100644 --- a/Src/Dlhis.Entity/Basic/Entity/GainfoMealEntity.cs +++ b/Src/Dlhis.Entity/Basic/Entity/GainfoMealEntity.cs @@ -1,43 +1,43 @@ -using Kurisu.AspNetCore.DataAccess.Entity; -using Kurisu.AspNetCore.DataAccess.SqlSugar; -using SqlSugar; - -namespace Dlhis.Entity.Basic.Entity -{ - /// - /// 就餐人员 - /// - [SugarTable(TableName = "GainfoMeal", TableDescription = "就餐人员")] - public class GainfoMealEntity : SugarBaseEntity, IVersion - { - /// - /// 工号 - /// - [SugarColumn(IsNullable = false, ColumnDescription = "工号", UniqueGroupNameList = new[] { "idx_CanteenUser_UserCode" })] - public string UserCode { get; set; } - - /// - /// 余额 - /// - [SugarColumn(ColumnDescription = "余额")] - public decimal Balance { get; set; } - - /// - /// 余额(充值) - /// - [SugarColumn(ColumnDescription = "余额(充值)")] - public decimal BalanceOfRecharge { get; set; } - - /// - /// 余额(餐补) - /// - [SugarColumn(ColumnDescription = "余额(餐补)")] - public decimal BalanceOfSubsidies { get; set; } - - /// - /// 版本号 - /// - [SugarColumn(ColumnDescription = "版本号")] - public int Version { get; set; } - } -} \ No newline at end of file +// using Kurisu.AspNetCore.DataAccess.Entity; +// using Kurisu.AspNetCore.DataAccess.SqlSugar; +// using SqlSugar; +// +// namespace Dlhis.Entity.Basic.Entity +// { +// /// +// /// 就餐人员 +// /// +// [SugarTable(TableName = "GainfoMeal", TableDescription = "就餐人员")] +// public class GainfoMealEntity : SugarBaseEntity, IVersion +// { +// /// +// /// 工号 +// /// +// [SugarColumn(IsNullable = false, ColumnDescription = "工号", UniqueGroupNameList = new[] { "idx_CanteenUser_UserCode" })] +// public string UserCode { get; set; } +// +// /// +// /// 余额 +// /// +// [SugarColumn(ColumnDescription = "余额")] +// public decimal Balance { get; set; } +// +// /// +// /// 余额(充值) +// /// +// [SugarColumn(ColumnDescription = "余额(充值)")] +// public decimal BalanceOfRecharge { get; set; } +// +// /// +// /// 余额(餐补) +// /// +// [SugarColumn(ColumnDescription = "余额(餐补)")] +// public decimal BalanceOfSubsidies { get; set; } +// +// /// +// /// 版本号 +// /// +// [SugarColumn(ColumnDescription = "版本号")] +// public int Version { get; set; } +// } +// } \ No newline at end of file diff --git a/Src/Dlhis.Entity/Canteen/Entities/WalletEntity.cs b/Src/Dlhis.Entity/Canteen/Entities/WalletEntity.cs new file mode 100644 index 00000000..f396b88a --- /dev/null +++ b/Src/Dlhis.Entity/Canteen/Entities/WalletEntity.cs @@ -0,0 +1,29 @@ +using Kurisu.AspNetCore.DataAccess.SqlSugar; +using SqlSugar; + +namespace Dlhis.Entity.Canteen.Entities; + +/// +/// 钱包 +/// +[SugarTable(TableName = "Wallets", TableDescription = "钱包")] +public class WalletEntity : SugarBaseEntity +{ + /// + /// 钱包编码 + /// + [SugarColumn(IsNullable = false)] + public string Code { get; set; } + + /// + /// 钱包名称 + /// + [SugarColumn(IsNullable = false)] + public string Name { get; set; } + + /// + /// 厂区 + /// + [SugarColumn(IsNullable = false, UniqueGroupNameList = new[] { "idx_wallets_factry" })] + public string Factory { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Entity/Canteen/Enums/CanteenApplicationTypeEnumType.cs b/Src/Dlhis.Entity/Canteen/Enums/CanteenApplicationTypeEnumType.cs index 54cc684e..d3ab0bd7 100644 --- a/Src/Dlhis.Entity/Canteen/Enums/CanteenApplicationTypeEnumType.cs +++ b/Src/Dlhis.Entity/Canteen/Enums/CanteenApplicationTypeEnumType.cs @@ -8,6 +8,9 @@ namespace Dlhis.Entity.Canteen.Enums; [Description("餐饮应用类型")] public enum CanteenApplicationTypeEnumType { + /// + /// 设备消费 + /// [Description("设备消费")] Device = 100, diff --git a/Src/Dlhis.Middle/Api/FaceBearerTokenHandler.cs b/Src/Dlhis.Middle/Api/FaceBearerTokenHandler.cs deleted file mode 100644 index a0e806ff..00000000 --- a/Src/Dlhis.Middle/Api/FaceBearerTokenHandler.cs +++ /dev/null @@ -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 - { - /// - public string GetToken(IServiceProvider serviceProvider) - { - var cache = serviceProvider.GetService(); - if (cache.TryGetValue("token", out string token)) return "Bearer " + token; - - var httpClient = serviceProvider.GetService().CreateClient("face"); - var hostName = serviceProvider.GetService().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>(tokenJson); - - token = tokenInfo.Data.AccessToken; - - cache.Set("token", token, TimeSpan.FromSeconds(tokenInfo.Data.Expires)); - - return token; - } - } -} \ No newline at end of file diff --git a/Src/Dlhis.Middle/Apis/Handlers/ClientHandler.cs b/Src/Dlhis.Middle/Apis/Handlers/ClientHandler.cs new file mode 100644 index 00000000..fa3b1694 --- /dev/null +++ b/Src/Dlhis.Middle/Apis/Handlers/ClientHandler.cs @@ -0,0 +1,23 @@ +using Kurisu.RemoteCall.Abstractions; + +namespace Dlhis.Middle.Apis.Handlers; + +public class ClientHandler : IHttpRemoteCallPolicyHandler +{ + /// + 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; + } +} \ No newline at end of file diff --git a/Src/Dlhis.Middle/Apis/Handlers/FaceBearerTokenHandler.cs b/Src/Dlhis.Middle/Apis/Handlers/FaceBearerTokenHandler.cs new file mode 100644 index 00000000..37c409a2 --- /dev/null +++ b/Src/Dlhis.Middle/Apis/Handlers/FaceBearerTokenHandler.cs @@ -0,0 +1,14 @@ +using Dlhis.Middle.Services; +using Kurisu.RemoteCall.Abstractions; + +namespace Dlhis.Middle.Apis.Handlers; + +public class FaceBearerTokenHandler : IAsyncAuthTokenHandler +{ + /// + public async Task GetTokenAsync(IServiceProvider serviceProvider) + { + var tokenService = serviceProvider.GetService(); + return await tokenService.AuthAsync(); + } +} \ No newline at end of file diff --git a/Src/Dlhis.Middle/Api/IAssignmentDeviceTaskApi.cs b/Src/Dlhis.Middle/Apis/IAssignmentDeviceTaskApi.cs similarity index 69% rename from Src/Dlhis.Middle/Api/IAssignmentDeviceTaskApi.cs rename to Src/Dlhis.Middle/Apis/IAssignmentDeviceTaskApi.cs index 8c856a82..d15f6f0f 100644 --- a/Src/Dlhis.Middle/Api/IAssignmentDeviceTaskApi.cs +++ b/Src/Dlhis.Middle/Apis/IAssignmentDeviceTaskApi.cs @@ -1,11 +1,13 @@ +using Dlhis.Middle.Apis.Handlers; using Dlhis.Middle.Request; using Dlhis.Middle.Response; using Kurisu.AspNetCore.UnifyResultAndValidation; 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 { [Auth(TokenHandler = typeof(FaceBearerTokenHandler))] diff --git a/Src/Dlhis.Middle/Api/ICanteenApi.cs b/Src/Dlhis.Middle/Apis/ICanteenApi.cs similarity index 67% rename from Src/Dlhis.Middle/Api/ICanteenApi.cs rename to Src/Dlhis.Middle/Apis/ICanteenApi.cs index 84a4d7e9..bbc2c1eb 100644 --- a/Src/Dlhis.Middle/Api/ICanteenApi.cs +++ b/Src/Dlhis.Middle/Apis/ICanteenApi.cs @@ -1,26 +1,29 @@ +using Dlhis.Middle.Apis.Handlers; +using Dlhis.Middle.Entities; using Dlhis.Middle.Request; using Dlhis.Middle.Response; using Kurisu.AspNetCore.DataAccess.Dto; using Kurisu.AspNetCore.UnifyResultAndValidation; 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 { - [Auth(TokenHandler = typeof(FaceBearerTokenHandler))] [Post("/api/canteen/orders")] Task> AddAsync(OrderAddRequest request); - - - + + [Auth(TokenHandler = typeof(FaceBearerTokenHandler))] + [Post("/api/canteen/middle/upload")] + Task> UploadAsync(LocalBalanceRecordEntity entity); + [Auth(TokenHandler = typeof(FaceBearerTokenHandler))] [Post("/api/canteen/balances/device/page")] Task>> GetPageByDeviceUniqueNoAsync(BalanceRecordPageByDeviceUniqueNoRequest request); - - + [Auth(TokenHandler = typeof(FaceBearerTokenHandler))] [Post("/api/canteen/balances/refund")] Task> RefundAsync(BalanceRecordRefundRequest request); diff --git a/Src/Dlhis.Middle/Apis/ICommonApi.cs b/Src/Dlhis.Middle/Apis/ICommonApi.cs new file mode 100644 index 00000000..b44214fc --- /dev/null +++ b/Src/Dlhis.Middle/Apis/ICommonApi.cs @@ -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> AuthAsync(object request); + + [Auth(TokenHandler = typeof(FaceBearerTokenHandler))] + [Post("/api/common/middles/healthz")] + public Task CheckHealthAsync(HealthCheckRequest request); + + [Auth(TokenHandler = typeof(FaceBearerTokenHandler))] + [Get("/api/common/SysData/download")] + public Task>> DownloadAsync(string factory, string tableName); +} \ No newline at end of file diff --git a/Src/Dlhis.Middle/BackgroundServices/BalanceRecordUploadBackgroundService.cs b/Src/Dlhis.Middle/BackgroundServices/BalanceRecordUploadBackgroundService.cs new file mode 100644 index 00000000..9984ac59 --- /dev/null +++ b/Src/Dlhis.Middle/BackgroundServices/BalanceRecordUploadBackgroundService.cs @@ -0,0 +1,131 @@ +using Dlhis.Middle.Apis; +using Dlhis.Middle.Entities; +using Dlhis.Middle.Services; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +using Kurisu.AspNetCore.UnifyResultAndValidation; + +namespace Dlhis.Middle.BackgroundServices; + +public class BalanceRecordUploadBackgroundService : BackgroundService +{ + private readonly IServiceProvider _serviceProvider; + private readonly ILogger _logger; + private readonly IWebHostEnvironment _environment; + private readonly ISettingService _settingService; + private readonly ICanteenApi _canteenApi; + + private readonly IHealthCheckService _healthCheckService; + + public BalanceRecordUploadBackgroundService(IServiceProvider serviceProvider, + ILogger logger, + IWebHostEnvironment environment, + ISettingService settingService, + ICanteenApi canteenApi, + IHealthCheckService healthCheckService) + { + _serviceProvider = serviceProvider; + _logger = logger; + _environment = environment; + _settingService = settingService; + _canteenApi = canteenApi; + _healthCheckService = healthCheckService; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested && _environment.IsProduction()) + { + if (!_healthCheckService.GetIsOffline()) + { + var now = DateTime.Now; + + using var scope = _serviceProvider.CreateScope(); + var db = scope.ServiceProvider.GetService(); + var ds = scope.ServiceProvider.GetService(); + + var factories = _settingService.GetFactories(); + foreach (var factory in factories) + { + try + { + var balanceRecords = await db.Queryable().Where(x => x.TenantId == factory).Where(x => !x.IsSync).ToListAsync(stoppingToken); + if (balanceRecords.Any()) + { + _logger.LogInformation("上传离线订单"); + foreach (var balanceRecord in balanceRecords) + { + //事务 + await db.Client.Ado.BeginTranAsync(); + try + { + var r = await _canteenApi.UploadAsync(balanceRecord); + if (r.Code != ApiStateCode.Success) + { + throw new Exception(r.Msg); + } + + var d = r.Data; + //更新本地余额表 + await db.Updateable() + .SetColumns(x => x.Balance == d.Balance) + .Where(x => x.UserCode == d.UserCode) + .ExecuteCommandAsync(stoppingToken); + + //eg:一定要在此处更新为已同步 + balanceRecord.IsSync = true; + await db.UpdateAsync(balanceRecord); + + await db.Client.Ado.CommitTranAsync(); + } + catch (Exception e) + { + _logger.LogError(e, "{factory}上传离线消费失败:{message}", factory, e.Message); + await db.Client.Ado.RollbackTranAsync(); + } + } + + balanceRecords = await db.Queryable().Where(x => x.TenantId == factory).Where(x => !x.IsSync).ToListAsync(stoppingToken); + if (!balanceRecords.Any()) + { + _logger.LogInformation("上传离线订单完成"); + } + } + } + catch (Exception e) + { + _logger.LogError(e, "{factory}同步离线数据失败:{message}", factory, e.Message); + } + } + + + try + { + if (now is { Hour: 4, Minute: 1 } + && !_healthCheckService.GetIsOffline() + && !await db.Queryable().Where(x => !x.IsSync).AnyAsync()) + { + await ds.InitAsync(factories); + } + else if (now is { Hour: 15, Minute: 1 } + && !_healthCheckService.GetIsOffline() + && !await db.Queryable().Where(x => !x.IsSync).AnyAsync()) + { + await ds.InitAsync(factories); + } + else if (now is { Hour: 20, Minute: 1 } + && !_healthCheckService.GetIsOffline() + && !await db.Queryable().Where(x => !x.IsSync).AnyAsync()) + { + await ds.InitAsync(factories); + } + } + catch (Exception e) + { + _logger.LogError(e, "定时同步离线数据失败"); + } + } + + await Task.Delay(60 * 1000, stoppingToken); + } + } +} \ No newline at end of file diff --git a/Src/Dlhis.Middle/BackgroundServices/HealthCheckBackgroundService.cs b/Src/Dlhis.Middle/BackgroundServices/HealthCheckBackgroundService.cs new file mode 100644 index 00000000..44320032 --- /dev/null +++ b/Src/Dlhis.Middle/BackgroundServices/HealthCheckBackgroundService.cs @@ -0,0 +1,35 @@ +using Dlhis.Middle.Services; + +namespace Dlhis.Middle.BackgroundServices; + +public class HealthCheckBackgroundService : BackgroundService +{ + private readonly IHealthCheckService _healthCheckService; + private readonly IServiceProvider _serviceProvider; + private bool _init; + + public HealthCheckBackgroundService(IHealthCheckService healthCheckService, IServiceProvider serviceProvider) + { + _healthCheckService = healthCheckService; + _serviceProvider = serviceProvider; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + if (await _healthCheckService.DoHealthCheckAsync()) + { + if (!_init) + { + using var scope = _serviceProvider.CreateScope(); + var factories = scope.ServiceProvider.GetService().GetFactories(); + var ds = scope.ServiceProvider.GetService(); + await ds.InitAsync(factories); + } + } + + _init = true; + } + } +} \ No newline at end of file diff --git a/Src/Dlhis.Middle/Controllers/AssignmentsController.cs b/Src/Dlhis.Middle/Controllers/AssignmentsController.cs index 5dfaa784..8238041f 100644 --- a/Src/Dlhis.Middle/Controllers/AssignmentsController.cs +++ b/Src/Dlhis.Middle/Controllers/AssignmentsController.cs @@ -1,5 +1,5 @@ using Dlhis.Entity.Device.Enums; -using Dlhis.Middle.Api; +using Dlhis.Middle.Apis; using Dlhis.Middle.Request; using Dlhis.Middle.Response; using Dlhis.Middle.Services; diff --git a/Src/Dlhis.Middle/Controllers/BalancesController.cs b/Src/Dlhis.Middle/Controllers/BalancesController.cs index 04749614..31444388 100644 --- a/Src/Dlhis.Middle/Controllers/BalancesController.cs +++ b/Src/Dlhis.Middle/Controllers/BalancesController.cs @@ -1,4 +1,4 @@ -using Dlhis.Middle.Api; +using Dlhis.Middle.Apis; using Dlhis.Middle.Request; using Dlhis.Middle.Response; using Dlhis.Middle.Services; diff --git a/Src/Dlhis.Middle/Controllers/GainfosController.cs b/Src/Dlhis.Middle/Controllers/GainfosController.cs index a8f72fa6..6fc33430 100644 --- a/Src/Dlhis.Middle/Controllers/GainfosController.cs +++ b/Src/Dlhis.Middle/Controllers/GainfosController.cs @@ -1,4 +1,5 @@ using Dlhis.Middle.Filters; +using Dlhis.Middle.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -12,14 +13,13 @@ namespace Dlhis.Middle.Controllers; public class GainfosController : ControllerBase { private readonly IHttpClientFactory _httpClientFactory; - private readonly IConfiguration _configuration; - private readonly ILogger _logger; + private readonly ISettingService _settingService; - public GainfosController(IHttpClientFactory httpClientFactory, IConfiguration configuration, ILogger logger) + public GainfosController(IHttpClientFactory httpClientFactory, + ISettingService settingService) { _httpClientFactory = httpClientFactory; - _configuration = configuration; - _logger = logger; + _settingService = settingService; } /// @@ -32,14 +32,11 @@ public class GainfosController : ControllerBase [HttpGet("face")] public async Task GetFaceAsync(string token) { - var hostName = _configuration.GetSection("HostName").Value; - var httpClient = _httpClientFactory.CreateClient("face"); - - _logger.LogInformation("token:{token}", token); + var httpClient = _httpClientFactory.CreateClient(); 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) { diff --git a/Src/Dlhis.Middle/Controllers/OAuth2Controller.cs b/Src/Dlhis.Middle/Controllers/OAuth2Controller.cs index 5b448c0d..f6b8624e 100644 --- a/Src/Dlhis.Middle/Controllers/OAuth2Controller.cs +++ b/Src/Dlhis.Middle/Controllers/OAuth2Controller.cs @@ -1,4 +1,5 @@ using System.Text; +using Dlhis.Middle.Apis; using Dlhis.Middle.Request; using Dlhis.Middle.Services; using Microsoft.AspNetCore.Mvc; @@ -13,23 +14,20 @@ namespace Dlhis.Middle.Controllers; [ApiController] public class OAuth2Controller : ControllerBase { - private readonly IHttpClientFactory _httpClientFactory; - private readonly IConfiguration _configuration; + private readonly ICommonApi _commonApi; private readonly IHealthCheckService _healthCheckService; - public OAuth2Controller(IHttpClientFactory httpClientFactory, IConfiguration configuration, IHealthCheckService healthCheckService) + public OAuth2Controller( + ICommonApi commonApi, + IHealthCheckService healthCheckService) { - _httpClientFactory = httpClientFactory; - _configuration = configuration; + _commonApi = commonApi; _healthCheckService = healthCheckService; } [HttpPost("clientgrant")] public async Task ClientGrant(ClientGrantRequest request) { - var authority = _configuration.GetSection("Authority").Value; - var httpClient = _httpClientFactory.CreateClient("face"); - if (_healthCheckService.GetIsOffline()) { 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 response = await httpClient.PostAsync($"{authority}/oauth2/connect/token", content); - var result = await response.Content.ReadAsStringAsync(); - return JObject.Parse(result); + var r = await _commonApi.AuthAsync(new + { + request.ClientId, + request.ClientSecret + }); + + return r.Data; } } \ No newline at end of file diff --git a/Src/Dlhis.Middle/Dlhis.Middle.csproj b/Src/Dlhis.Middle/Dlhis.Middle.csproj index accf19a9..07f7413e 100644 --- a/Src/Dlhis.Middle/Dlhis.Middle.csproj +++ b/Src/Dlhis.Middle/Dlhis.Middle.csproj @@ -17,10 +17,23 @@ - + + + + appsettings.json + + + + + + + + + + diff --git a/Src/Dlhis.Middle/Entities/LocalGainfoMealEntity.cs b/Src/Dlhis.Middle/Entities/LocalGainfoMealEntity.cs index e21572d2..322272aa 100644 --- a/Src/Dlhis.Middle/Entities/LocalGainfoMealEntity.cs +++ b/Src/Dlhis.Middle/Entities/LocalGainfoMealEntity.cs @@ -1,4 +1,6 @@ -using Dlhis.Entity.Basic.Entity; +using Kurisu.AspNetCore.DataAccess.Entity; +using Kurisu.AspNetCore.DataAccess.SqlSugar; +using SqlSugar; namespace Dlhis.Middle.Entities { @@ -6,4 +8,30 @@ namespace Dlhis.Middle.Entities { public new int Id { get; set; } } + + + /// + /// 就餐人员 + /// + [SugarTable(TableName = "GainfoMeal", TableDescription = "就餐人员")] + public class GainfoMealEntity : SugarBaseEntity, IVersion + { + /// + /// 工号 + /// + [SugarColumn(IsNullable = false, ColumnDescription = "工号", UniqueGroupNameList = new[] { "idx_CanteenUser_UserCode" })] + public string UserCode { get; set; } + + /// + /// 余额 + /// + [SugarColumn(ColumnDescription = "余额")] + public decimal Balance { get; set; } + + /// + /// 版本号 + /// + [SugarColumn(ColumnDescription = "版本号")] + public int Version { get; set; } + } } \ No newline at end of file diff --git a/Src/Dlhis.Middle/Packs/SqlSugarPack.cs b/Src/Dlhis.Middle/Packs/SqlSugarPack.cs index 89e24c61..b47ad750 100644 --- a/Src/Dlhis.Middle/Packs/SqlSugarPack.cs +++ b/Src/Dlhis.Middle/Packs/SqlSugarPack.cs @@ -1,7 +1,4 @@ -using Dlhis.Entity.Basic.Entity; -using Dlhis.Entity.Canteen.Entities; -using Dlhis.Entity.Device.Entities; -using Dlhis.Middle.Entities; +using Dlhis.Middle.Entities; using Kurisu.AspNetCore.Authentication.Abstractions; using Kurisu.AspNetCore.DataAccess.Entity; using Kurisu.AspNetCore.DataAccess.SqlSugar.Attributes; @@ -141,7 +138,7 @@ public class SqlSugarPack : BaseAppPack { using var scope = app.ApplicationServices.CreateScope(); var db = scope.ServiceProvider.GetService(); - + db.Client.DbMaintenance.CreateDatabase(); db.Client.CodeFirst.InitTables(); db.Client.CodeFirst.InitTables(); diff --git a/Src/Dlhis.Middle/Program.cs b/Src/Dlhis.Middle/Program.cs index 99f6c4a6..f139b0fd 100644 --- a/Src/Dlhis.Middle/Program.cs +++ b/Src/Dlhis.Middle/Program.cs @@ -1,5 +1,4 @@ -using Dlhis.Middle.Services; -using Dlhis.Middle.Services.Implements; +using Dlhis.Middle.BackgroundServices; using Kurisu.AspNetCore; using Kurisu.AspNetCore.Startup; using Microsoft.IdentityModel.Logging; @@ -24,35 +23,11 @@ public class Startup : DefaultStartup public override void ConfigureServices(IServiceCollection services) { base.ConfigureServices(services); + + services.AddHostedService(); + services.AddHostedService(); services.AddRemoteCall(App.ActiveTypes); - services.AddHttpClient("face", client => { client.Timeout = TimeSpan.FromSeconds(30); }).ConfigurePrimaryHttpMessageHandler(() => - { - var handler = new HttpClientHandler - { - ClientCertificateOptions = ClientCertificateOption.Manual, - ServerCertificateCustomValidationCallback = (_, _, _, _) => true - }; - - return handler; - }); - services.AddHostedService(); - services.AddHostedService(); services.AddMemoryCache(); } - - /// - public override void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - base.Configure(app, env); - - if (Configuration.GetSection("Download").Value == "1") - { - using var scope = app.ApplicationServices.CreateScope(); - var factory = Configuration.GetSection("Factory").Value; - - var ds = scope.ServiceProvider.GetService(); - ds.InitAsync(factory).Wait(); - } - } } \ No newline at end of file diff --git a/Src/Dlhis.Middle/Request/HealthCheckRequest.cs b/Src/Dlhis.Middle/Request/HealthCheckRequest.cs new file mode 100644 index 00000000..e783dc81 --- /dev/null +++ b/Src/Dlhis.Middle/Request/HealthCheckRequest.cs @@ -0,0 +1,6 @@ +namespace Dlhis.Middle.Request; + +public class HealthCheckRequest +{ + public List Factories { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Middle/Response/OrderResultResponse.cs b/Src/Dlhis.Middle/Response/OrderResultResponse.cs index 65b07959..eaf00498 100644 --- a/Src/Dlhis.Middle/Response/OrderResultResponse.cs +++ b/Src/Dlhis.Middle/Response/OrderResultResponse.cs @@ -28,4 +28,7 @@ public class OrderResultResponse /// 余额 /// public decimal Balance { get; set; } + + + public decimal TotalBalance { get; set; } } \ No newline at end of file diff --git a/Src/Dlhis.Middle/Services/IDataDownloadService.cs b/Src/Dlhis.Middle/Services/IDataDownloadService.cs index 0efb64d4..eccedb1c 100644 --- a/Src/Dlhis.Middle/Services/IDataDownloadService.cs +++ b/Src/Dlhis.Middle/Services/IDataDownloadService.cs @@ -2,9 +2,7 @@ namespace Dlhis.Middle.Services; public interface IDataDownloadService : IScopeDependency { - Task InitAsync(string factory); + Task InitAsync(string[] factories); Task DownloadAsync(string factory) where T : class, new(); - - Task TruncateAsync(); } \ No newline at end of file diff --git a/Src/Dlhis.Middle/Services/IHealthCheckService.cs b/Src/Dlhis.Middle/Services/IHealthCheckService.cs index e8c05049..b3f8fd9a 100644 --- a/Src/Dlhis.Middle/Services/IHealthCheckService.cs +++ b/Src/Dlhis.Middle/Services/IHealthCheckService.cs @@ -4,5 +4,5 @@ public interface IHealthCheckService : ISingletonDependency { bool GetIsOffline(); - Task DoHealthCheckAsync(); + Task DoHealthCheckAsync(); } \ No newline at end of file diff --git a/Src/Dlhis.Middle/Services/IRemoteOrderService.cs b/Src/Dlhis.Middle/Services/IRemoteOrderService.cs index e161d9ad..e5f6dfc0 100644 --- a/Src/Dlhis.Middle/Services/IRemoteOrderService.cs +++ b/Src/Dlhis.Middle/Services/IRemoteOrderService.cs @@ -1,12 +1,8 @@ -using Dlhis.Middle.Request; -using Dlhis.Middle.Response; -using Kurisu.AspNetCore.UnifyResultAndValidation; - namespace Dlhis.Middle.Services; public interface IRemoteOrderService : IScopeDependency { - Task> AddAsync(OrderAddRequest request); + //Task> AddAsync(OrderAddRequest request); Task AbleRemoteCallAsync(); } \ No newline at end of file diff --git a/Src/Dlhis.Middle/Services/ISettingService.cs b/Src/Dlhis.Middle/Services/ISettingService.cs new file mode 100644 index 00000000..b7946df7 --- /dev/null +++ b/Src/Dlhis.Middle/Services/ISettingService.cs @@ -0,0 +1,8 @@ +namespace Dlhis.Middle.Services; + +public interface ISettingService : ISingletonDependency +{ + string[] GetFactories(); + + string GetBaseUrl(); +} \ No newline at end of file diff --git a/Src/Dlhis.Middle/Services/ITokenService.cs b/Src/Dlhis.Middle/Services/ITokenService.cs new file mode 100644 index 00000000..d4dffabe --- /dev/null +++ b/Src/Dlhis.Middle/Services/ITokenService.cs @@ -0,0 +1,6 @@ +namespace Dlhis.Middle.Services; + +public interface ITokenService : ISingletonDependency +{ + Task AuthAsync(); +} \ No newline at end of file diff --git a/Src/Dlhis.Middle/Services/Implements/BalanceRecordCheckBackgroundService.cs b/Src/Dlhis.Middle/Services/Implements/BalanceRecordCheckBackgroundService.cs deleted file mode 100644 index a117b24b..00000000 --- a/Src/Dlhis.Middle/Services/Implements/BalanceRecordCheckBackgroundService.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System.Net.Http.Headers; -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.Extensions.Caching.Memory; -using Newtonsoft.Json; - -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(); - var db = scope.ServiceProvider.GetService(); - var ds = scope.ServiceProvider.GetService(); - - foreach (var factory in _factory.Split(',')) - { - try - { - var balanceRecords = await db.Queryable().Where(x => x.TenantId == factory).Where(x => !x.IsSync).ToListAsync(stoppingToken); - if (balanceRecords.Any()) - { - _logger.LogInformation("上传离线订单"); - foreach (var balanceRecord in balanceRecords) - { - //事务 - await db.Client.Ado.BeginTranAsync(); - try - { - var r = await UploadAsync(balanceRecord); - if (r.Code != ApiStateCode.Success) - { - throw new Exception(r.Msg); - } - - var d = r.Data; - //更新本地余额表 - await db.Updateable() - .SetColumns(x => x.Balance == d.Balance) - .Where(x => x.UserCode == d.UserCode) - .ExecuteCommandAsync(stoppingToken); - - //eg:一定要在此处更新为已同步 - balanceRecord.IsSync = true; - await db.UpdateAsync(balanceRecord); - - await db.Client.Ado.CommitTranAsync(); - } - catch (Exception e) - { - _logger.LogError(e, "{factory}上传离线消费失败:{message}", factory, e.Message); - await db.Client.Ado.RollbackTranAsync(); - } - } - - balanceRecords = await db.Queryable().Where(x => x.TenantId == factory).Where(x => !x.IsSync).ToListAsync(stoppingToken); - if (!balanceRecords.Any()) - { - _logger.LogInformation("上传离线订单完成"); - } - } - } - catch (Exception e) - { - _logger.LogError(e, "{factory}同步离线数据失败:{message}", factory, e.Message); - } - } - - - if (now is { Hour: 4, Minute: 1 } - && !_healthCheckService.GetIsOffline() - && !await db.Queryable().Where(x => !x.IsSync).AnyAsync()) - { - await ds.InitAsync(_factory); - } - else if (now is { Hour: 15, Minute: 1 } - && !_healthCheckService.GetIsOffline() - && !await db.Queryable().Where(x => !x.IsSync).AnyAsync()) - { - await ds.InitAsync(_factory); - } - else if (now is { Hour: 20, Minute: 1 } - && !_healthCheckService.GetIsOffline() - && !await db.Queryable().Where(x => !x.IsSync).AnyAsync()) - { - await ds.InitAsync(_factory); - } - } - - 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 = "e509c2aaa98e300c", - 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 c9498000..9f1e83bd 100644 --- a/Src/Dlhis.Middle/Services/Implements/DataDownloadService.cs +++ b/Src/Dlhis.Middle/Services/Implements/DataDownloadService.cs @@ -1,43 +1,32 @@ -using System.Net.Http.Headers; using System.Reflection; -using System.Text; +using Dlhis.Middle.Apis; 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 DataDownloadService : IDataDownloadService { - private readonly IHttpClientFactory _httpClientFactory; - private readonly IMemoryCache _memoryCache; private readonly IDbContext _dbContext; + private readonly ICommonApi _commonApi; private readonly ILogger _logger; - private readonly string _hostName; - public DataDownloadService(IHttpClientFactory httpClientFactory, IMemoryCache memoryCache, IConfiguration configuration, IDbContext dbContext, ILogger logger) + public DataDownloadService( + IDbContext dbContext, + ICommonApi commonApi, + ILogger logger) { - _httpClientFactory = httpClientFactory; - _memoryCache = memoryCache; _dbContext = dbContext; + _commonApi = commonApi; _logger = logger; - _hostName = configuration.GetSection("HostName").Value; } - public async Task InitAsync(string factory) + public async Task InitAsync(string[] factories) { + //需要上传完记录才可下载 if (await _dbContext.Queryable().Where(x => x.IsSync == false).AnyAsync()) - { return; - } - - var factories = factory.Split(','); await _dbContext.Client.Ado.BeginTranAsync(); try @@ -84,9 +73,10 @@ public class DataDownloadService : IDataDownloadService _logger.LogInformation("档案{count}", await _dbContext.Queryable().CountAsync()); _logger.LogInformation("余额{count}", await _dbContext.Queryable().CountAsync()); _logger.LogInformation("下载完成"); + await _dbContext.Client.Ado.CommitTranAsync(); } - catch (Exception e) + catch (Exception) { await _dbContext.Client.Ado.RollbackTranAsync(); throw; @@ -95,27 +85,11 @@ public class DataDownloadService : IDataDownloadService public async Task DownloadAsync(string factory) where T : class, new() { - var token = await AuthAsync(); - - 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 - { - ["factory"] = factory, - ["tableName"] = typeof(T).GetCustomAttribute()!.TableName - }); - - var response = await httpClient.GetStringAsync(requestUrl); - var pack = response.ToObject>>(); - + var pack = await _commonApi.DownloadAsync(factory, typeof(T).GetCustomAttribute()!.TableName); await _dbContext.InsertAsync(pack.Data); } - /// - public async Task TruncateAsync() + private async Task TruncateAsync() { await _dbContext.Client.Deleteable().ExecuteCommandAsync(); await _dbContext.Client.Deleteable().ExecuteCommandAsync(); @@ -128,26 +102,4 @@ public class DataDownloadService : IDataDownloadService await _dbContext.Client.Deleteable().ExecuteCommandAsync(); await _dbContext.Client.Deleteable().ExecuteCommandAsync(); } - - 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 = "e509c2aaa98e300c", - 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/HealthCheckBackgroundService.cs b/Src/Dlhis.Middle/Services/Implements/HealthCheckBackgroundService.cs deleted file mode 100644 index bc658f64..00000000 --- a/Src/Dlhis.Middle/Services/Implements/HealthCheckBackgroundService.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Dlhis.Middle.Services.Implements; - -public class HealthCheckBackgroundService : BackgroundService -{ - private readonly IHealthCheckService _healthCheckService; - - public HealthCheckBackgroundService(IHealthCheckService healthCheckService) - { - _healthCheckService = healthCheckService; - } - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - while (!stoppingToken.IsCancellationRequested) - { - await _healthCheckService.DoHealthCheckAsync(); - } - } -} \ No newline at end of file diff --git a/Src/Dlhis.Middle/Services/Implements/HealthcheckService.cs b/Src/Dlhis.Middle/Services/Implements/HealthcheckService.cs index 2c01192a..3b44aa94 100644 --- a/Src/Dlhis.Middle/Services/Implements/HealthcheckService.cs +++ b/Src/Dlhis.Middle/Services/Implements/HealthcheckService.cs @@ -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; public class HealthCheckService : IHealthCheckService { private bool _isOffline; - private readonly string _hostName; private readonly ILogger _logger; - private int _successRetry; - private int _retry; + private readonly ISettingService _settingService; + private readonly ICommonApi _healthCheckApi; + private int _attempted; - public HealthCheckService(ILogger logger, IConfiguration configuration) + private bool _isBegin; + + public HealthCheckService(ILogger logger, + ISettingService settingService, + ICommonApi healthCheckApi) { _logger = logger; - _hostName = configuration.GetSection("HostName").Value; + _settingService = settingService; + _healthCheckApi = healthCheckApi; } public bool GetIsOffline() @@ -21,46 +28,49 @@ public class HealthCheckService : IHealthCheckService return _isOffline; } - public async Task DoHealthCheckAsync() + public async Task DoHealthCheckAsync() { - var cancellationTokenSource = new CancellationTokenSource(5000); - var tcpClient = new TcpClient(); - try { - _logger.LogDebug("tcp connecting {host} {port}", _hostName, 443); - await tcpClient.ConnectAsync(_hostName, 443, cancellationTokenSource.Token); - _retry++; - _logger.LogDebug("retry {retry}", _retry); - if (tcpClient.Connected) + var factories = _settingService.GetFactories(); + + var response = await _healthCheckApi.CheckHealthAsync(new HealthCheckRequest { Factories = factories.ToList() }); + if (response.Code == ApiStateCode.Success) { - _successRetry++; - _logger.LogDebug("success connected {count}", _successRetry); - if (_successRetry == 4) + _attempted++; + if (_attempted == 5) { _isOffline = false; - _successRetry = 0; - _retry = 0; - _logger.LogInformation("current is {status}", "healthy"); + _attempted = 0; } - else + + if (!_isBegin) { - _logger.LogDebug("wait next check in {waitTime}", _successRetry * 1000); - await Task.Delay(_successRetry * 1000, cancellationTokenSource.Token); + _isOffline = false; + _isBegin = true; + } + + await Task.Delay(_attempted * 1000); + + if (_isOffline == false) + { + _logger.LogInformation("Health"); + return true; } } else { - _successRetry = 0; _isOffline = true; - _logger.LogDebug("current is {status}", "unavailable"); - await Task.Delay(500); + _logger.LogInformation("Error {message}", response.Msg); } } catch (Exception ex) { - _logger.LogError("fail cause :{reason}", ex.Message); + _logger.LogError("Failed cause :{reason}", ex.Message); _isOffline = true; + await Task.Delay(5000); } + + return false; } } \ No newline at end of file diff --git a/Src/Dlhis.Middle/Services/Implements/OrderService.cs b/Src/Dlhis.Middle/Services/Implements/OrderService.cs index 919663c6..fadb172f 100644 --- a/Src/Dlhis.Middle/Services/Implements/OrderService.cs +++ b/Src/Dlhis.Middle/Services/Implements/OrderService.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using Dlhis.Entity.Canteen.Enums; +using Dlhis.Middle.Apis; using Dlhis.Middle.Entities; using Kurisu.AspNetCore.Utils; using Kurisu.AspNetCore.Utils.Extensions; @@ -17,6 +18,7 @@ public class OrderService : IOrderService { private readonly ICanteenSettingService _canteenSettingService; private readonly IRemoteOrderService _remoteOrderService; + private readonly ICanteenApi _canteenApi; private readonly ILogger _logger; private readonly string _factory; private readonly IDbContext _dbContext; @@ -24,14 +26,16 @@ public class OrderService : IOrderService public OrderService(ICanteenSettingService canteenSettingService, IRemoteOrderService remoteOrderService, - IConfiguration configuration, + ISettingService settingService, + ICanteenApi canteenApi, ILogger logger, IDbContext dbContext) { _canteenSettingService = canteenSettingService; _remoteOrderService = remoteOrderService; + _canteenApi = canteenApi; _logger = logger; - _factory = configuration.GetSection("Factory").Value; + _factory = settingService.GetFactories().Join(","); _dbContext = dbContext; } @@ -43,12 +47,12 @@ public class OrderService : IOrderService { if (await _remoteOrderService.AbleRemoteCallAsync()) { - var remoteResult = await _remoteOrderService.AddAsync(request); + var remoteResult = await _canteenApi.AddAsync(request); if (remoteResult.Code == ApiStateCode.Success) { var d = remoteResult.Data; await _dbContext.Updateable() - .SetColumns(x => x.Balance == d.Balance) + .SetColumns(x => x.Balance == d.TotalBalance) .Where(x => x.UserCode == d.UserCode) .ExecuteCommandAsync(); } diff --git a/Src/Dlhis.Middle/Services/Implements/RemoteOrderService.cs b/Src/Dlhis.Middle/Services/Implements/RemoteOrderService.cs index 05d84b14..707e7338 100644 --- a/Src/Dlhis.Middle/Services/Implements/RemoteOrderService.cs +++ b/Src/Dlhis.Middle/Services/Implements/RemoteOrderService.cs @@ -1,54 +1,37 @@ -using System.Net.Http.Headers; -using System.Text; using Dlhis.Middle.Entities; -using Dlhis.Middle.Request; -using Dlhis.Middle.Response; 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; public class RemoteOrderService : IRemoteOrderService { - private readonly string _hostName; - private readonly IHttpClientFactory _httpClientFactory; - private readonly IMemoryCache _memoryCache; private readonly IHealthCheckService _healthCheckService; private readonly IDbContext _dbContext; - public RemoteOrderService(IHttpClientFactory httpClientFactory, - IMemoryCache memoryCache, - IConfiguration configuration, + public RemoteOrderService( IHealthCheckService healthCheckService, IDbContext dbContext) { - _httpClientFactory = httpClientFactory; - _memoryCache = memoryCache; - _hostName = configuration.GetSection("hostName").Value; _healthCheckService = healthCheckService; _dbContext = dbContext; } - - /// - public async Task> AddAsync(OrderAddRequest request) - { - var token = await AuthAsync(); - - var httpClient = _httpClientFactory.CreateClient("face"); - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); - - var content = new StringContent(request.ToJson(), Encoding.UTF8, "application/json"); - - var response = await httpClient.PostAsync($"https://{_hostName}/api/canteen/orders", content); - - var responseJson = await response.Content.ReadAsStringAsync(); - return responseJson.ToObject>(); - } + ///// + // public async Task> AddAsync(OrderAddRequest request) + // { + // var token = await _tokenService.AuthAsync(); + // + // var httpClient = _httpClientFactory.CreateClient("face"); + // httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); + // + // var content = new StringContent(request.ToJson(), Encoding.UTF8, "application/json"); + // + // var response = await httpClient.PostAsync($"{_settingService.GetBaseUrl()}/api/canteen/orders", content); + // + // var responseJson = await response.Content.ReadAsStringAsync(); + // return responseJson.ToObject>(); + // } /// public async Task AbleRemoteCallAsync() @@ -60,28 +43,4 @@ public class RemoteOrderService : IRemoteOrderService return !_healthCheckService.GetIsOffline(); } - - - - 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 = "e509c2aaa98e300c", - 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/SettingService.cs b/Src/Dlhis.Middle/Services/Implements/SettingService.cs new file mode 100644 index 00000000..b583f924 --- /dev/null +++ b/Src/Dlhis.Middle/Services/Implements/SettingService.cs @@ -0,0 +1,23 @@ +namespace Dlhis.Middle.Services.Implements; + +public class SettingService : ISettingService +{ + private readonly IConfiguration _configuration; + + public SettingService(IConfiguration configuration) + { + _configuration = configuration; + } + + /// + public string[] GetFactories() + { + return _configuration.GetSection("Factory").Value.Split(","); + } + + /// + public string GetBaseUrl() + { + return _configuration.GetSection("BaseUrl").Value; + } +} \ No newline at end of file diff --git a/Src/Dlhis.Middle/Services/Implements/TokenService.cs b/Src/Dlhis.Middle/Services/Implements/TokenService.cs new file mode 100644 index 00000000..226a9de0 --- /dev/null +++ b/Src/Dlhis.Middle/Services/Implements/TokenService.cs @@ -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; + } + + /// + public async Task 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; + } +} \ No newline at end of file diff --git a/Src/Dlhis.Middle/appsettings.Development.json b/Src/Dlhis.Middle/appsettings.Development.json index efb527ae..4a755b60 100644 --- a/Src/Dlhis.Middle/appsettings.Development.json +++ b/Src/Dlhis.Middle/appsettings.Development.json @@ -5,8 +5,7 @@ "Microsoft.AspNetCore": "Warning" } }, - "Authority": "https://face.jinkosolar.com", - "Factory": "1201", - "HostName": "face.jinkosolar.com", - "Download":1 + "BaseUrl": "https://yikatong.isawesome.cn", + "Factory": "1234", + "InternalIP":"" } diff --git a/Src/Dlhis.Middle/appsettings.json b/Src/Dlhis.Middle/appsettings.json index f85ae1ef..b6e736e0 100644 --- a/Src/Dlhis.Middle/appsettings.json +++ b/Src/Dlhis.Middle/appsettings.json @@ -12,9 +12,6 @@ "SlowSqlTime": 1, "EnableSqlLog": false }, - "Authority": "https://face.jinkosolar.com", - "Factory": "", - "HostName": "face.jinkosolar.com", "AllowedHosts": "*", "SwaggerOptions": { "Enable": true, diff --git a/Src/Dlhis.ScheduledTask/Common/Services/IJinkoJIPService.cs b/Src/Dlhis.ScheduledTask/Common/Services/IJinkoJIPService.cs index 5533267d..6b91adbc 100644 --- a/Src/Dlhis.ScheduledTask/Common/Services/IJinkoJIPService.cs +++ b/Src/Dlhis.ScheduledTask/Common/Services/IJinkoJIPService.cs @@ -3,7 +3,7 @@ using Dlhis.ScheduledTask.Common.Response; namespace Dlhis.ScheduledTask.Common.Services; -public interface IJinkoJIPService : ISingletonDependency +public interface IJinkoJipService : ISingletonDependency { Task UploadAttendancesAsync(List requests); diff --git a/Src/Dlhis.ScheduledTask/Common/Services/IJinkoJipService.cs b/Src/Dlhis.ScheduledTask/Common/Services/IJinkoJipService.cs new file mode 100644 index 00000000..6b91adbc --- /dev/null +++ b/Src/Dlhis.ScheduledTask/Common/Services/IJinkoJipService.cs @@ -0,0 +1,36 @@ +using Dlhis.ScheduledTask.Common.Request; +using Dlhis.ScheduledTask.Common.Response; + +namespace Dlhis.ScheduledTask.Common.Services; + +public interface IJinkoJipService : ISingletonDependency +{ + Task UploadAttendancesAsync(List requests); + + Task SyncMealSupplementRecordsAsync(List requests); + + + /// + /// 获取出差信息 + /// + /// + /// + /// + /// + Task GetTravelInfosByFactoryAsync(DateTime begin, DateTime end, string fatory); + + /// + /// 获取出差信息 + /// + /// + /// + /// + /// + Task GetTravelInfosByUserCodeAsync(DateTime begin, DateTime end, string userCode); + + + Task SyncFaceToSapAsync(string userCode, string base64); + + + string IssueToken(); +} \ No newline at end of file diff --git a/Src/Dlhis.ScheduledTask/Common/Services/Implements/HangfireService.cs b/Src/Dlhis.ScheduledTask/Common/Services/Implements/HangfireService.cs index 6f177b4e..51d56928 100644 --- a/Src/Dlhis.ScheduledTask/Common/Services/Implements/HangfireService.cs +++ b/Src/Dlhis.ScheduledTask/Common/Services/Implements/HangfireService.cs @@ -12,9 +12,9 @@ public class HangfireService : IHangfireService { private readonly RedisCache _redisCache; private readonly IDbContext _dbContext; - private readonly IJinkoJIPService _jinkoJipService; + private readonly IJinkoJipService _jinkoJipService; - public HangfireService(RedisCache redisCache, IDbContext dbContext, IJinkoJIPService jinkoJipService) + public HangfireService(RedisCache redisCache, IDbContext dbContext, IJinkoJipService jinkoJipService) { _redisCache = redisCache; _dbContext = dbContext; diff --git a/Src/Dlhis.ScheduledTask/Common/Services/Implements/JinkoJIPService.cs b/Src/Dlhis.ScheduledTask/Common/Services/Implements/JinkoJIPService.cs index 9ed40b41..88d207b2 100644 --- a/Src/Dlhis.ScheduledTask/Common/Services/Implements/JinkoJIPService.cs +++ b/Src/Dlhis.ScheduledTask/Common/Services/Implements/JinkoJIPService.cs @@ -10,13 +10,13 @@ using Newtonsoft.Json.Linq; namespace Dlhis.ScheduledTask.Common.Services.Implements; -public class JinkoJIPService : IJinkoJIPService +public class JinkoJipService : IJinkoJipService { private readonly IHttpClientFactory _httpClientFactory; - private readonly ILogger _logger; + private readonly ILogger _logger; private readonly IConfiguration _configuration; - public JinkoJIPService(IHttpClientFactory httpClientFactory, ILogger logger, IConfiguration configuration) + public JinkoJipService(IHttpClientFactory httpClientFactory, ILogger logger, IConfiguration configuration) { _httpClientFactory = httpClientFactory; _logger = logger; diff --git a/Src/Dlhis.ScheduledTask/Common/Services/Implements/JinkoJipService.cs b/Src/Dlhis.ScheduledTask/Common/Services/Implements/JinkoJipService.cs new file mode 100644 index 00000000..88d207b2 --- /dev/null +++ b/Src/Dlhis.ScheduledTask/Common/Services/Implements/JinkoJipService.cs @@ -0,0 +1,213 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Net.Http.Headers; +using System.Security.Claims; +using System.Text; +using Dlhis.ScheduledTask.Common.Request; +using Dlhis.ScheduledTask.Common.Response; +using Kurisu.AspNetCore.Utils.Extensions; +using Microsoft.IdentityModel.Tokens; +using Newtonsoft.Json.Linq; + +namespace Dlhis.ScheduledTask.Common.Services.Implements; + +public class JinkoJipService : IJinkoJipService +{ + private readonly IHttpClientFactory _httpClientFactory; + private readonly ILogger _logger; + private readonly IConfiguration _configuration; + + public JinkoJipService(IHttpClientFactory httpClientFactory, ILogger logger, IConfiguration configuration) + { + _httpClientFactory = httpClientFactory; + _logger = logger; + _configuration = configuration; + } + + private HttpClient GetHttpClient() + { + var httpClient = _httpClientFactory.CreateClient(); + httpClient.BaseAddress = new Uri(_configuration.GetSection("JinkoOptions:Jip:BaseUrl").Value); + httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", IssueToken()); + + return httpClient; + } + + public async Task UploadAttendancesAsync(List requests) + { + var requestUrl = "/JIP/HCM/HCM_SWIPE_DATA_SAVE"; + + var body = new + { + T_DATA = requests, + }; + + var json = body.ToJson(); + + //_logger.LogInformation("上传考勤记录请求:{json}", json); + + var content = new StringContent(json, Encoding.UTF8, "application/json"); + var response = await GetHttpClient().PostAsync(requestUrl, content); + var responseJson = await response.Content.ReadAsStringAsync(); + + _logger.LogInformation("上传考勤记录响应:{json}", responseJson); + + try + { + return responseJson.ToObject(); + } + catch (Exception) + { + return new UploadAttendanceResponse + { + OLX = "99", + OMS = "接口响应数据异常,无法反序列化" + }; + } + } + + + public async Task SyncMealSupplementRecordsAsync(List requests) + { + var requestUrl = "/JIP/DHR/DHR_DL_CALLBACK"; + + var json = requests.ToJson(Kurisu.AspNetCore.Utils.Extensions.JsonExtensions.DefaultSetting); + + var content = new StringContent(json, Encoding.UTF8, "application/json"); + var response = await GetHttpClient().PostAsync(requestUrl, content); + var responseJson = await response.Content.ReadAsStringAsync(); + + _logger.LogInformation("餐补状态同步响应:{json}", responseJson); + + var jobj = JObject.Parse(responseJson); + if (jobj.TryGetValue("exp", out var exp)) + { + true.ThrowIfTrue(exp.Value()); + } + + if (jobj.TryGetValue("message", out var message) && !jobj.ContainsKey("code")) + { + true.ThrowIfTrue(message.Value()); + } + + (jobj.GetValue("code").Value() != 200).ThrowIfTrue(jobj.GetValue("message").Value()); + + return responseJson; + } + + + public async Task GetTravelInfosByFactoryAsync(DateTime begin, DateTime end, string factory) + { + const string apiUrl = "/JIP/HCM/HCM_TRAVELINFO"; + + var beginDay = begin.ToString("yyyyMMdd"); + var endDay = end.ToString("yyyyMMdd"); + + var obj = new + { + I_PAGE = 1, + I_LINE = 500, + IV_BEGDA = beginDay, + IV_ENDDA = endDay, + IT_CCMDD = new List + { + new + { + IV_CCMDD = factory + } + } + }; + + var body = obj.ToJson(); + + var content = new StringContent(body, Encoding.UTF8, "application/json"); + var response = await GetHttpClient().PostAsync(apiUrl, content); + response.EnsureSuccessStatusCode(); + + var jsonResponse = await response.Content.ReadAsStringAsync(); + return jsonResponse.ToObject(); + } + + public async Task GetTravelInfosByUserCodeAsync(DateTime begin, DateTime end, string userCode) + { + const string apiUrl = "/JIP/HCM/HCM_TRAVELINFO"; + + var beginDay = begin.ToString("yyyyMMdd"); + var endDay = end.ToString("yyyyMMdd"); + + //IV_GONGH IV_ORGID + //IT_2002GH + var obj = new + { + IV_BEGDA = beginDay, + IV_ENDDA = endDay, + IT_2002GH = new List + { + new + { + IV_GONGH = userCode + } + } + }; + + var body = obj.ToJson(); + + var content = new StringContent(body, Encoding.UTF8, "application/json"); + var response = await GetHttpClient().PostAsync(apiUrl, content); + response.EnsureSuccessStatusCode(); + + var jsonResponse = await response.Content.ReadAsStringAsync(); + return jsonResponse.ToObject(); + } + + public async Task SyncFaceToSapAsync(string userCode, string base64) + { + const string apiUrl = "/JIP/HCM/HCM_I_PHOTO"; + var obj = new + { + I_WORKCODE = userCode, + I_PHOTO_STR = base64 + }; + + var body = obj.ToJson(); + + var content = new StringContent(body, Encoding.UTF8, "application/json"); + var response = await GetHttpClient().PostAsync(apiUrl, content); + response.EnsureSuccessStatusCode(); + + var jsonResponse = await response.Content.ReadAsStringAsync(); + return jsonResponse.ToObject(); + } + + + public string IssueToken() + { + var jwtKey = _configuration.GetSection("JinkoOptions:Jip:Key").Value; + var jwtSecret = _configuration.GetSection("JinkoOptions:Jip:Secret").Value; + const int min = 5; + + var now = DateTime.Now.AddMinutes(-min); + var expires = DateTime.Now.AddMinutes(min); + + var claims = new List + { + new(JwtRegisteredClaimNames.Nbf, $"{new DateTimeOffset(now).ToUnixTimeSeconds()}"), + new(JwtRegisteredClaimNames.Exp, $"{new DateTimeOffset(expires).ToUnixTimeSeconds()}"), + new(JwtRegisteredClaimNames.Iss, jwtKey), + }; + + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecret)); + var descriptor = new SecurityTokenDescriptor + { + Subject = new ClaimsIdentity(claims), + Issuer = jwtKey, + IssuedAt = now, + NotBefore = now, + Expires = expires, + SigningCredentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256) + }; + + var jwtHandler = new JwtSecurityTokenHandler(); + var securityToken = jwtHandler.CreateToken(descriptor); + return jwtHandler.WriteToken(securityToken); + } +} \ No newline at end of file diff --git a/Src/Dlhis.ScheduledTask/Common/Services/Implements/SapBusinessTripService.cs b/Src/Dlhis.ScheduledTask/Common/Services/Implements/SapBusinessTripService.cs index e4282e7d..e3ec2c19 100644 --- a/Src/Dlhis.ScheduledTask/Common/Services/Implements/SapBusinessTripService.cs +++ b/Src/Dlhis.ScheduledTask/Common/Services/Implements/SapBusinessTripService.cs @@ -15,12 +15,12 @@ namespace Dlhis.ScheduledTask.Common.Services.Implements; public class SapBusinessTripService : ISapBusinessTripService { - private readonly IJinkoJIPService _jinkoJipService; + private readonly IJinkoJipService _jinkoJipService; private readonly IDbContext _dbContext; private readonly IDevicePermissionApi _devicePermissionApi; private readonly ILogger _logger; - public SapBusinessTripService(IJinkoJIPService jinkoJipService, IDbContext dbContext, IDevicePermissionApi devicePermissionApi, ILogger logger) + public SapBusinessTripService(IJinkoJipService jinkoJipService, IDbContext dbContext, IDevicePermissionApi devicePermissionApi, ILogger logger) { _jinkoJipService = jinkoJipService; _dbContext = dbContext; diff --git a/Src/Dlhis.ScheduledTask/Jobs/MealSupplementHandlerJob.cs b/Src/Dlhis.ScheduledTask/Jobs/MealSupplementHandlerJob.cs index 8a67cd70..d81ca771 100644 --- a/Src/Dlhis.ScheduledTask/Jobs/MealSupplementHandlerJob.cs +++ b/Src/Dlhis.ScheduledTask/Jobs/MealSupplementHandlerJob.cs @@ -6,8 +6,6 @@ using Dlhis.ScheduledTask.Attributes; using Dlhis.ScheduledTask.Common.Request; using Dlhis.ScheduledTask.Common.Services; using Hangfire; -using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions; -using Kurisu.AspNetCore.Utils; namespace Dlhis.ScheduledTask.Jobs; @@ -17,10 +15,10 @@ namespace Dlhis.ScheduledTask.Jobs; [RecurringJob(nameof(MealSupplementHandlerJob), "0 0/30 * * * ?", Name = "餐补核算")] public class MealSupplementHandlerJob : BaseJob { - private readonly IJinkoJIPService _jinkoJIPService; + private readonly IJinkoJipService _jinkoJIPService; public MealSupplementHandlerJob(IServiceProvider serviceProvider, - IJinkoJIPService jinkoJIPService) : base(serviceProvider) + IJinkoJipService jinkoJIPService) : base(serviceProvider) { _jinkoJIPService = jinkoJIPService; } @@ -85,23 +83,6 @@ public class MealSupplementHandlerJob : BaseJob } } - - //创建余额数据 - var mealUser = await DbContext.Queryable().Where(x => x.UserCode == gainfo.Code).SingleAsync(); - if (mealUser == null) - { - mealUser = new GainfoMealEntity - { - Balance = 0, - BalanceOfRecharge = 0, - BalanceOfSubsidies = 0, - UserCode = gainfo.Code, - Version = 0, - }; - - await DbContext.InsertAsync(mealUser); - } - await SetMealSupplementAsync(gainfo, item); } } @@ -111,19 +92,6 @@ public class MealSupplementHandlerJob : BaseJob await DbContext.Client.Ado.BeginTranAsync(); try { - await CommonHelper.RetryAsync(async () => - { - var mealUser = await DbContext.Queryable().Where(x => x.UserCode == mealSupplement.Code).FirstAsync(); - var b = mealUser.Balance += mealSupplement.Amount; - - await DbContext.Updateable() - .SetColumns(x => x.Version == x.Version + 1) - .SetColumns(x => x.Balance == b) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .Where(x => x.UserCode == mealUser.UserCode && x.Version == mealUser.Version) - .ExecuteCommandThrowVersionAsync(); - }, 5); - await DbContext.InsertAsync(new BalanceRecordEntity { Amount = mealSupplement.Amount, diff --git a/Src/Dlhis.ScheduledTask/Jobs/SyncFaceJob.cs b/Src/Dlhis.ScheduledTask/Jobs/SyncFaceJob.cs index 843c2531..73af6cd2 100644 --- a/Src/Dlhis.ScheduledTask/Jobs/SyncFaceJob.cs +++ b/Src/Dlhis.ScheduledTask/Jobs/SyncFaceJob.cs @@ -7,10 +7,10 @@ namespace Dlhis.ScheduledTask.Jobs; [RecurringJob(nameof(SyncFaceJob), "0 0/15 * * * ?", Name = "face数据同步sap")] public class SyncFaceJob : BaseJob { - private readonly IJinkoJIPService _jinkoJipService; + private readonly IJinkoJipService _jinkoJipService; /// - public SyncFaceJob(IServiceProvider serviceProvider, IJinkoJIPService jinkoJipService) : base(serviceProvider) + public SyncFaceJob(IServiceProvider serviceProvider, IJinkoJipService jinkoJipService) : base(serviceProvider) { _jinkoJipService = jinkoJipService; } diff --git a/Src/Dlhis.ScheduledTask/Jobs/SyncOriginCardInfoJob.cs b/Src/Dlhis.ScheduledTask/Jobs/SyncOriginCardInfoJob.cs index 35b71940..12e6e305 100644 --- a/Src/Dlhis.ScheduledTask/Jobs/SyncOriginCardInfoJob.cs +++ b/Src/Dlhis.ScheduledTask/Jobs/SyncOriginCardInfoJob.cs @@ -14,9 +14,9 @@ namespace Dlhis.ScheduledTask.Jobs; [RecurringJob(nameof(SyncOriginCardInfoJob), "0 0/1 * * * ?", Name = "上传考勤记录")] public class SyncOriginCardInfoJob : BaseJob { - private readonly IJinkoJIPService _jinkoJIPService; + private readonly IJinkoJipService _jinkoJIPService; - public SyncOriginCardInfoJob(IJinkoJIPService jinkoJIPService, IServiceProvider serviceProvider) : base(serviceProvider) + public SyncOriginCardInfoJob(IJinkoJipService jinkoJIPService, IServiceProvider serviceProvider) : base(serviceProvider) { _jinkoJIPService = jinkoJIPService; } diff --git a/Src/Dlhis.ScheduledTask/Program.cs b/Src/Dlhis.ScheduledTask/Program.cs index 999976ce..5289eb45 100644 --- a/Src/Dlhis.ScheduledTask/Program.cs +++ b/Src/Dlhis.ScheduledTask/Program.cs @@ -26,6 +26,6 @@ public class Startup : DefaultStartup { base.Configure(app, env); - var token = app.ApplicationServices.GetService().IssueToken(); + var token = app.ApplicationServices.GetService().IssueToken(); } } \ No newline at end of file