From 7d322ef5f60b39d28ee1c267c9461070ed83728c Mon Sep 17 00:00:00 2001 From: liguoyang Date: Thu, 24 Oct 2024 16:51:17 +0800 Subject: [PATCH 1/6] =?UTF-8?q?fix=20=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Order/Services/BaseOrderService.cs | 38 ------------------- .../Implements/BookingOrderService.cs | 8 ++-- .../Order/Services/Implements/OrderService.cs | 12 +++--- .../Services/ICanteenSettingService.cs | 25 +++++++++++- .../Implements/CanteenSettingService.cs | 25 ++++++++++++ 5 files changed, 60 insertions(+), 48 deletions(-) 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/Implements/BookingOrderService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/BookingOrderService.cs index e149c83b..a39482e4 100644 --- a/Src/Dlhis.Canteen/Order/Services/Implements/BookingOrderService.cs +++ b/Src/Dlhis.Canteen/Order/Services/Implements/BookingOrderService.cs @@ -19,6 +19,7 @@ namespace Dlhis.Canteen.Order.Services.Implements; public class BookingOrderService : BaseOrderService, IBookingOrderService { + private readonly ICanteenSettingService _canteenSettingService; private readonly IDishesMenuApi _dishesMenuApi; private readonly IBusinessPlacePermissionsApi _businessPlacePermissionsApi; @@ -28,6 +29,7 @@ public class BookingOrderService : BaseOrderService, IBookingOrderService ICurrentUser currentUser, ILogger logger) : base(canteenSettingService, dbContext, currentUser, logger) { + _canteenSettingService = canteenSettingService; _dishesMenuApi = dishesMenuApi; _businessPlacePermissionsApi = businessPlacePermissionsApi; } @@ -41,7 +43,7 @@ public class BookingOrderService : BaseOrderService, IBookingOrderService //预定时间 var now = DateTime.Now; - var bookingDeadline = await GetSettingAsync(gainfo.No, businessPlace.TenantId, CanteenSettingItemEnumType.BookingDeadline) ?? new TimeOnly(20, 30, 00); + 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("超出订餐时间"); @@ -72,7 +74,7 @@ public class BookingOrderService : BaseOrderService, IBookingOrderService No = SnowFlakeHelper.Instance.NextId().ToString(), IsOffline = false, - IsCostCrossTenant = await GetSettingAsync(gainfo.No, businessPlace.TenantId, CanteenSettingItemEnumType.AllowCrossTenant), + IsCostCrossTenant = await _canteenSettingService.GetSettingAsync(CanteenSettingItemEnumType.AllowCrossTenant, businessPlace.TenantId, gainfo.No), OriginTenantId = gainfo.Factory, //人员可能跨基地消费 @@ -212,7 +214,7 @@ public class BookingOrderService : BaseOrderService, IBookingOrderService 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 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("不在订单取消允许范围内,不可操作"); diff --git a/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs index 98e9356c..4b9f43bc 100644 --- a/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs +++ b/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs @@ -22,6 +22,7 @@ namespace Dlhis.Canteen.Order.Services.Implements; public class OrderService : BaseOrderService, IOrderService { + private readonly ICanteenSettingService _canteenSettingService; private readonly IServiceProvider _serviceProvider; private readonly IHttpContextAccessor _httpContextAccessor; @@ -33,6 +34,7 @@ public class OrderService : BaseOrderService, IOrderService ILogger logger) : base(canteenSettingService, dbContext, currentUser, logger) { + _canteenSettingService = canteenSettingService; _serviceProvider = serviceProvider; _httpContextAccessor = httpContextAccessor; } @@ -198,7 +200,7 @@ public class OrderService : BaseOrderService, IOrderService /// private async Task GetCostDateAsync(BusinessShiftMealRelationshipEntity shift, DateTime costDateTime) { - var switchTime = await GetSettingAsync(string.Empty, shift.TenantId, CanteenSettingItemEnumType.BusinessSwitchTime); + var switchTime = await _canteenSettingService.GetSettingAsync(CanteenSettingItemEnumType.BusinessSwitchTime, shift.TenantId, string.Empty); var costDate = costDateTime.Date; @@ -243,7 +245,7 @@ public class OrderService : BaseOrderService, IOrderService .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 +257,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 +266,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 +278,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); diff --git a/Src/Dlhis.Canteen/Setting/Services/ICanteenSettingService.cs b/Src/Dlhis.Canteen/Setting/Services/ICanteenSettingService.cs index ecd8d69d..9872d134 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,13 @@ 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); +} \ 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..41f3791b 100644 --- a/Src/Dlhis.Canteen/Setting/Services/Implements/CanteenSettingService.cs +++ b/Src/Dlhis.Canteen/Setting/Services/Implements/CanteenSettingService.cs @@ -114,6 +114,31 @@ 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; + } + //*********************************************** help methods ***************************************************// private async Task CheckDuplicateSettingAsync(CanteenSettingSaveRequest request) From 5cf11e51287395ad4d0fa5d2bf963fa62dbfd3c0 Mon Sep 17 00:00:00 2001 From: liguoyang Date: Fri, 25 Oct 2024 17:10:18 +0800 Subject: [PATCH 2/6] =?UTF-8?q?add=20=E6=B6=88=E8=B4=B9=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/Implements/SysDataService.cs | 4 +- .../Controllers/GainfoMealController.cs | 12 - .../Response/GainfoCurrentMealInfoResponse.cs | 9 - .../Response/GainfoCurrentResponse.cs | 5 - .../Response/GainfoMealResponse.cs | 2 +- .../Services/IGainfoMealService.cs | 2 - .../Services/Implements/GainfoMealService.cs | 160 +--- .../Services/Implements/GainfoService.cs | 9 +- .../Implements/MealSupplementRecordService.cs | 31 - .../Order/Controllers/BalancesController.cs | 32 - .../Order/Controllers/OrdersController.cs | 106 ++- .../Order/Dto/OrderPreCheckDto.cs | 20 +- .../Order/Repository/IBalanceRepository.cs | 40 + .../Order/Repository/IOrderRepository.cs | 11 + .../Implements/BalanceRepository.cs | 57 ++ .../Repository/Implements/OrderRepository.cs | 68 ++ .../Order/Services/IBalanceService.cs | 4 +- .../Services/Implements/BalanceService.cs | 91 +-- .../Implements/BookingOrderService.cs | 604 +++++++-------- .../Services/Implements/MiddleService.cs | 18 +- .../Order/Services/Implements/OrderService.cs | 697 +++++++----------- .../Services/ICanteenSettingService.cs | 2 + .../Implements/CanteenSettingService.cs | 18 + .../BusinessShiftMealRelationshipEntity.cs | 12 +- .../Basic/Entity/GainfoMealEntity.cs | 86 +-- .../Canteen/Entities/WalletEntity.cs | 29 + .../Enums/CanteenApplicationTypeEnumType.cs | 3 + .../Entities/LocalGainfoMealEntity.cs | 41 ++ .../Common/Services/IJinkoJipService.cs | 36 + .../Services/Implements/HangfireService.cs | 4 +- .../Services/Implements/JinkoJipService.cs | 213 ++++++ .../Implements/SapBusinessTripService.cs | 4 +- .../Jobs/MealSupplementHandlerJob.cs | 36 +- Src/Dlhis.ScheduledTask/Jobs/SyncFaceJob.cs | 4 +- .../Jobs/SyncOriginCardInfoJob.cs | 4 +- Src/Dlhis.ScheduledTask/Program.cs | 2 +- 36 files changed, 1273 insertions(+), 1203 deletions(-) delete mode 100644 Src/Dlhis.Basic/Organization/Response/GainfoCurrentMealInfoResponse.cs create mode 100644 Src/Dlhis.Canteen/Order/Repository/IBalanceRepository.cs create mode 100644 Src/Dlhis.Canteen/Order/Repository/IOrderRepository.cs create mode 100644 Src/Dlhis.Canteen/Order/Repository/Implements/BalanceRepository.cs create mode 100644 Src/Dlhis.Canteen/Order/Repository/Implements/OrderRepository.cs create mode 100644 Src/Dlhis.Entity/Canteen/Entities/WalletEntity.cs create mode 100644 Src/Dlhis.ScheduledTask/Common/Services/IJinkoJipService.cs create mode 100644 Src/Dlhis.ScheduledTask/Common/Services/Implements/JinkoJipService.cs diff --git a/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs b/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs index 53ec663d..3b35e692 100644 --- a/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs +++ b/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs @@ -39,7 +39,9 @@ public class SysDataService : ISysDataService 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(); + return null; + //todo 余额 + ///return await _dbContext.Queryable().Where(x => codes.Contains(x.UserCode)).Select(x => (object)x).ToListAsync(); } default: return await _dbContext.Client.SqlQueryable(sql).ToListAsync(); 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..b2fb54b8 100644 --- a/Src/Dlhis.Basic/Organization/Response/GainfoCurrentResponse.cs +++ b/Src/Dlhis.Basic/Organization/Response/GainfoCurrentResponse.cs @@ -62,9 +62,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..f2bc5509 100644 --- a/Src/Dlhis.Basic/Organization/Response/GainfoMealResponse.cs +++ b/Src/Dlhis.Basic/Organization/Response/GainfoMealResponse.cs @@ -41,7 +41,7 @@ public class GainfoMealResponse /// /// 餐补总额 /// - public decimal? MealSupplementAmount { get; set; } + public decimal MealSupplementAmount { 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..f9a354e7 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(_currentUser.GetStringTenantId()); - 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 => x.Factory == factory) + .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, + MealSupplementAmount = 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), + Balance = 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..b76ef920 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(); 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..baa4deb1 100644 --- a/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs +++ b/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs @@ -167,36 +167,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..1949cb5b 100644 --- a/Src/Dlhis.Canteen/Order/Controllers/OrdersController.cs +++ b/Src/Dlhis.Canteen/Order/Controllers/OrdersController.cs @@ -19,12 +19,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; } /// @@ -66,56 +64,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..5de3e2f9 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Repository/Implements/BalanceRepository.cs @@ -0,0 +1,57 @@ +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 factories = await GetWalletFactoriesAsync(factory); + + return await _dbContext.Queryable() + .Where(x => x.UserCode == userCode && x.Status == BalanceRecordStatusEnumType.Valid) + .Where(x => factories.Contains(x.TenantId)) + .Where(x => x.Source != BalanceRecordSourceEnumType.EMealVoucher) + .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/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/Implements/BalanceService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs index 24e9fb11..3bedcca8 100644 --- a/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs +++ b/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs @@ -122,11 +122,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]; @@ -323,19 +319,6 @@ 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) @@ -542,82 +525,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 a39482e4..e71257ed 100644 --- a/Src/Dlhis.Canteen/Order/Services/Implements/BookingOrderService.cs +++ b/Src/Dlhis.Canteen/Order/Services/Implements/BookingOrderService.cs @@ -1,302 +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 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 +// 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..55530c2f 100644 --- a/Src/Dlhis.Canteen/Order/Services/Implements/MiddleService.cs +++ b/Src/Dlhis.Canteen/Order/Services/Implements/MiddleService.cs @@ -25,21 +25,19 @@ public class MiddleService : IMiddleService if (!await _dbContext.Queryable().AnyAsync(x => x.No == entity.No)) { await _dbContext.InsertAsync(entity); - await _balanceService.ReCalcAsync(entity.UserCode); } //获取人员余额 - 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 = x.Balance todo 余额 }) .SingleAsync(); } diff --git a/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs index 4b9f43bc..791dac7f 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,23 +22,34 @@ using Kurisu.AspNetCore.UnifyResultAndValidation; namespace Dlhis.Canteen.Order.Services.Implements; -public class OrderService : BaseOrderService, IOrderService +public class OrderService : IOrderService { private readonly ICanteenSettingService _canteenSettingService; - private readonly IServiceProvider _serviceProvider; + 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) { _canteenSettingService = canteenSettingService; - _serviceProvider = serviceProvider; + _dbContext = dbContext; + _currentUser = currentUser; _httpContextAccessor = httpContextAccessor; + _balanceRepository = balanceRepository; + _orderRepository = orderRepository; + _redisCache = redisCache; + _logger = logger; } /// @@ -56,28 +69,12 @@ public class OrderService : BaseOrderService, IOrderService /// 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) @@ -87,15 +84,15 @@ public class OrderService : BaseOrderService, IOrderService 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) @@ -110,7 +107,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 @@ -152,10 +149,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 { @@ -165,7 +162,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) @@ -181,7 +178,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) @@ -192,33 +189,6 @@ public class OrderService : BaseOrderService, IOrderService } - /// - /// 获取成本日期 - /// - /// - /// - /// - private async Task GetCostDateAsync(BusinessShiftMealRelationshipEntity shift, DateTime costDateTime) - { - var switchTime = await _canteenSettingService.GetSettingAsync(CanteenSettingItemEnumType.BusinessSwitchTime, shift.TenantId, 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; - } - - /// /// 验证参数 /// @@ -231,15 +201,10 @@ 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(); @@ -286,40 +251,6 @@ 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); - } - /// /// 离线消费处理 /// @@ -327,36 +258,35 @@ public class OrderService : BaseOrderService, IOrderService /// private async Task HandleOfflineAsync(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() + 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, @@ -364,7 +294,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, @@ -372,9 +302,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, @@ -386,48 +314,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.InsertReturnIdentityAsync(order); + var balanceRecord = order.Adapt(); + await _dbContext.InsertAsync(balanceRecord); } /// @@ -439,79 +331,83 @@ 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) + .LeftJoin((x, d, u) => u.IdNo == d.IdNo) + .Where((x, d) => x.No == no && d.IdNo == idNo) + .Select((x, d, u) => new OrderResultResponse + { + UserCode = u.Code, + UserName = u.Name, + CurrentBalance = d.Balance + }) + .SingleAsync(); + + return new DefaultApiResult + { + Code = msg.IsPresent() ? ApiStateCode.Error : ApiStateCode.Success, + Data = data, + Msg = msg + }; + } /// /// 分支处理在线/离线消费 @@ -524,135 +420,68 @@ public class OrderService : BaseOrderService, IOrderService var message = string.Empty; if (request.Source == BalanceRecordSourceEnumType.EMealVoucher) + return await EMealVoucherConsumeAsync(request, gainfo); + + if (request.IsOffline) { - 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); + if (request.ThirdNo.IsPresent() && await ToCheckAbleOfflineConsumeAsync(request.ThirdNo)) + { + await using var handler = await _redisCache.LockAsync($"consume:device-offline:{request.ThirdNo}"); + if (handler.Acquired) + { + await HandleOfflineAsync(request.Adapt(), gainfo); + } + } + else + { + await HandleOfflineAsync(request.Adapt(), gainfo); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "离线入库错误: 订单号:{no} .{message}", request.ThirdNo, ex.Message); + } + } + else + { + try + { + 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 - { - CurrentBalance = d.Balance - }) - .SingleAsync(); - - 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 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 + }) + .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 + }; } + /// /// 根据二维码获取消费用户 /// @@ -660,74 +489,56 @@ 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 nowDate = DateTime.Now.Date; - var current = await DbContext.Queryable() - .Where(x => x.No == no && x.IdNo == idNo).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 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) @@ -736,26 +547,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; - } + }); } /// @@ -766,28 +560,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 @@ -795,17 +589,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/Services/ICanteenSettingService.cs b/Src/Dlhis.Canteen/Setting/Services/ICanteenSettingService.cs index 9872d134..2376b87c 100644 --- a/Src/Dlhis.Canteen/Setting/Services/ICanteenSettingService.cs +++ b/Src/Dlhis.Canteen/Setting/Services/ICanteenSettingService.cs @@ -45,4 +45,6 @@ public interface ICanteenSettingService : IScopeDependency 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/Implements/CanteenSettingService.cs b/Src/Dlhis.Canteen/Setting/Services/Implements/CanteenSettingService.cs index 41f3791b..c5654f0d 100644 --- a/Src/Dlhis.Canteen/Setting/Services/Implements/CanteenSettingService.cs +++ b/Src/Dlhis.Canteen/Setting/Services/Implements/CanteenSettingService.cs @@ -139,6 +139,24 @@ public class CanteenSettingService : ICanteenSettingService 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.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..8f698195 --- /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_facotry" })] + 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/Entities/LocalGainfoMealEntity.cs b/Src/Dlhis.Middle/Entities/LocalGainfoMealEntity.cs index e21572d2..bb9809ff 100644 --- a/Src/Dlhis.Middle/Entities/LocalGainfoMealEntity.cs +++ b/Src/Dlhis.Middle/Entities/LocalGainfoMealEntity.cs @@ -1,4 +1,7 @@ using Dlhis.Entity.Basic.Entity; +using Kurisu.AspNetCore.DataAccess.Entity; +using Kurisu.AspNetCore.DataAccess.SqlSugar; +using SqlSugar; namespace Dlhis.Middle.Entities { @@ -6,4 +9,42 @@ 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 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.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 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 0475ec33..d71c18be 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 173ada85..1d7412b9 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 From 71b563546c27c69bc7a2b3e803cd3f9bd1283645 Mon Sep 17 00:00:00 2001 From: liguoyang Date: Mon, 28 Oct 2024 10:27:16 +0800 Subject: [PATCH 3/6] rename . --- Src/Dlhis.ScheduledTask/Common/Services/IJinkoJIPService.cs | 2 +- .../Common/Services/Implements/JinkoJIPService.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) 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/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; From 727e49f72e128aa19155ac31c51d8dfb0a58a23e Mon Sep 17 00:00:00 2001 From: liguoyang Date: Mon, 28 Oct 2024 16:28:31 +0800 Subject: [PATCH 4/6] feat middleHealthz --- .../BusinessPlaceShiftTinyResponse.cs | 2 +- .../SysData/Controller/MiddlesController.cs | 29 +++++++++++++++++++ .../SysData/Request/HealthCheckRequest.cs | 9 ++++++ .../Request/MiddleHealthCheckRequest.cs | 6 ++++ .../SysData/Services/IHealthCheckService.cs | 6 ++++ .../Services/Implements/HealthCheckService.cs | 10 +++++++ .../Services/Implements/SysDataService.cs | 25 ++++++++++++++-- 7 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 Src/Dlhis.Basic/Common/SysData/Controller/MiddlesController.cs create mode 100644 Src/Dlhis.Basic/Common/SysData/Request/HealthCheckRequest.cs create mode 100644 Src/Dlhis.Basic/Common/SysData/Request/MiddleHealthCheckRequest.cs create mode 100644 Src/Dlhis.Basic/Common/SysData/Services/IHealthCheckService.cs create mode 100644 Src/Dlhis.Basic/Common/SysData/Services/Implements/HealthCheckService.cs 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 3b35e692..f413900e 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; @@ -34,17 +36,34 @@ public class SysDataService : ISysDataService switch (tableName) { 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 factories = await GetWalletFactoriesAsync(factory); + 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) && factories.Contains(x.TenantId)) + // .Where(x => x.Source != BalanceRecordSourceEnumType.EMealVoucher) + // .GroupBy(x => x.UserCode) + // .Select(x => new + // { + // x.UserCode, + // Balance = SqlFunc.AggregateSum(x.Amount) + // }) + // .ToListAsync(); return null; - //todo 余额 - ///return await _dbContext.Queryable().Where(x => codes.Contains(x.UserCode)).Select(x => (object)x).ToListAsync(); } 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 From 43189e1e573e4ead40fc54e4d94827c299c7ebd1 Mon Sep 17 00:00:00 2001 From: liguoyang Date: Mon, 28 Oct 2024 17:37:13 +0800 Subject: [PATCH 5/6] =?UTF-8?q?fix=20gainfomeal=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/Implements/SysDataService.cs | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs b/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs index f413900e..1650fa30 100644 --- a/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs +++ b/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs @@ -24,35 +24,42 @@ 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": return await _dbContext.Queryable().Where(x => x.Factory == factory).Select(x => (object)x).ToListAsync(); case "GainfoMeal": { - var factories = await GetWalletFactoriesAsync(factory); + var r = await _dbContext.Queryable() + .LeftJoin((x, b) => x.Code == b.UserCode) + .Where(x => x.Factory == factory) + .Where((x, b) => b.Source != BalanceRecordSourceEnumType.EMealVoucher && b.Status == BalanceRecordStatusEnumType.Valid && b.IsDeleted == false) + .GroupBy(x => x.Code) + .Select((x, b) => new + { + UserCode = x.Code, + Balance = SqlFunc.AggregateSum(b.Amount) + }) + .ToListAsync(); - 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) && factories.Contains(x.TenantId)) - // .Where(x => x.Source != BalanceRecordSourceEnumType.EMealVoucher) - // .GroupBy(x => x.UserCode) - // .Select(x => new - // { - // x.UserCode, - // Balance = SqlFunc.AggregateSum(x.Amount) - // }) - // .ToListAsync(); - return null; + return r.Select(x => (object)x).ToList(); } default: return await _dbContext.Client.SqlQueryable(sql).ToListAsync(); From 7ab7c61a293ebe23a4778297b2b5bb92108e7ca3 Mon Sep 17 00:00:00 2001 From: liguoyang Date: Tue, 29 Oct 2024 12:03:19 +0800 Subject: [PATCH 6/6] =?UTF-8?q?fix=20=E6=9F=A5=E8=AF=A2=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Common/SysData/Services/Implements/SysDataService.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs b/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs index 1650fa30..976e0244 100644 --- a/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs +++ b/Src/Dlhis.Basic/Common/SysData/Services/Implements/SysDataService.cs @@ -48,14 +48,14 @@ public class SysDataService : ISysDataService case "GainfoMeal": { var r = await _dbContext.Queryable() - .LeftJoin((x, b) => x.Code == b.UserCode) .Where(x => x.Factory == factory) - .Where((x, b) => b.Source != BalanceRecordSourceEnumType.EMealVoucher && b.Status == BalanceRecordStatusEnumType.Valid && b.IsDeleted == false) .GroupBy(x => x.Code) - .Select((x, b) => new + .Select(x => new { UserCode = x.Code, - Balance = SqlFunc.AggregateSum(b.Amount) + 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();