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 Dlhis.Constants; using Kurisu.AspNetCore.Authentication.Abstractions; using Kurisu.AspNetCore.DataAccess.Dto; using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; using SqlSugar; namespace Dlhis.Canteen.Order.Services.Implements; public class BookingOrderService : IBookingOrderService { private readonly ICanteenSettingService _canteenSettingService; private readonly IDbContext _dbContext; private readonly IBusinessPlacePermissionsApi _businessPlacePermissionsApi; private readonly ICurrentUser _currentUser; public BookingOrderService(ICanteenSettingService canteenSettingService, IDbContext dbContext, IBusinessPlacePermissionsApi businessPlacePermissionsApi, ICurrentUser currentUser) { _canteenSettingService = canteenSettingService; _dbContext = dbContext; _businessPlacePermissionsApi = businessPlacePermissionsApi; _currentUser = currentUser; } 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 menuDetail = await GetBookingDishesDetailAsync(request.BookingDate, request.BusinessPlaceCode, request.ShiftType, request.DishesCode); 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 () => { menuDetail = await GetBookingDishesDetailAsync(request.BookingDate, request.BusinessPlaceCode, request.ShiftType, request.DishesCode); menuDetail.ThrowIfNull("预定失败,菜品已经下架"); menuDetail.Number -= request.Number; (menuDetail.Number < 0).ThrowIfTrue($"{menuDetail.DishesName}剩余数量不足"); 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(); 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 GetBookingDishesDetailAsync(DateTime date, Guid businessPlaceCode, ShiftEnumType shiftType, string dishesCode) { var detail = await _dbContext.Queryable() .LeftJoin((x, d) => x.DishesCode == d.CustomCode) .Where(x => x.StartDate <= date && date <= x.EndDate) .Where(x => x.BusinessPlaceCode == businessPlaceCode) .Where(x => x.ShiftType == shiftType) .Where(x => x.DishesCode == dishesCode) .Where((x, d) => d.Enable) .Select((x, d) => new DishesMenuDetailDto { Date = date, MenuId = x.Id, DishesId = d.Id, DishesCode = d.CustomCode, DishesName = d.Name, Price = x.Price == null || x.Price == 0 ? d.Price : x.Price.Value, Number = x.Number, Version = x.Version, }) .SingleAsync(); return detail; } public async Task> GetBookingPageAsync(OrderBookingPageRequest request) { var myCodes = (await _businessPlacePermissionsApi.GetMyAsync()).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) .Where(p => p.IsDeleted == false) .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) { return; 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) { return; 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(); } /// /// 检查我的订餐是否存在,存在抛出异常 /// /// /// /// 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(UserClaimConstants.JinkoCode) ?? 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; } } public class DishesMenuDetailDto { public DateTime Date { get; set; } public long MenuId { get; set; } public long DishesId { get; set; } public string DishesCode { get; set; } public string DishesName { get; set; } public decimal Price { get; set; } public int Number { get; set; } public int Version { get; set; } }