Merge branch 'development' of https://git.dlhis.com/liguoyang/jinko.yikatong into development

This commit is contained in:
cheche
2024-03-20 15:27:39 +08:00
35 changed files with 675 additions and 452 deletions

View File

@@ -6,7 +6,6 @@ using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop;
using Kurisu.Core.DataAccess.Dto;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Org.BouncyCastle.Asn1.Ocsp;
namespace Dlhis.Canteen.Dishes.Controllers;
@@ -19,21 +18,12 @@ namespace Dlhis.Canteen.Dishes.Controllers;
public class OrdersController : ControllerBase
{
private readonly IOrderService _orderService;
private readonly IBookingOrderService _bookingOrderService;
public OrdersController(IOrderService orderService)
public OrdersController(IOrderService orderService, IBookingOrderService bookingOrderService)
{
_orderService = orderService;
}
/// <summary>
/// 订单分页
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpGet("page")]
public async Task<Pagination<OrderResponse>> GetPageAsync([FromQuery] OrderPageRequest request)
{
return await _orderService.GetPageAsync(request);
_bookingOrderService = bookingOrderService;
}
/// <summary>
@@ -60,39 +50,6 @@ public class OrdersController : ControllerBase
return await _orderService.AddAsync(request);
}
/// <summary>
/// 订餐
/// </summary>
/// <returns></returns>
[HttpPost("booking")]
[Transactional]
public async Task Booking(BookingMealRequest request)
{
await _orderService.BookingAsync(request);
}
///// <summary>
///// 获取详情
///// </summary>
///// <param name="no"></param>
///// <returns></returns>
//[HttpGet("details")]
//public async Task<List<OrderDetailResponse>> GetDetailsAsync(string no)
//{
// return await _orderService.GetDetailsAsync(no);
//}
/// <summary>
/// 充值
/// </summary>
/// <returns></returns>
[HttpPost("recharge")]
public async Task ReCharge()
{
}
/// <summary>
/// 订单聚合数据
/// </summary>
@@ -102,4 +59,28 @@ public class OrdersController : ControllerBase
{
return await _orderService.GetOrdersAggregationAsync(page);
}
//************************************************************** 订餐功能 **********************************************************************************
/// <summary>
/// 订餐
/// </summary>
/// <returns></returns>
[HttpPost("booking")]
[Transactional]
public async Task Booking(BookingMealRequest request)
{
await _bookingOrderService.BookingAsync(request);
}
/// <summary>
/// 订单分页
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpGet("booking/page")]
public async Task<Pagination<OrderBookingResponse>> GetBookingPageAsync([FromQuery] OrderBookingPageRequest request)
{
return await _bookingOrderService.GetBookingPageAsync(request);
}
}

View File

@@ -2,7 +2,7 @@
namespace Dlhis.Canteen.Order.Request;
public class OrderPageRequest : PageDto
public class OrderBookingPageRequest : PageDto
{
}

View File

@@ -0,0 +1,34 @@
namespace Dlhis.Canteen.Order.Response;
public class OrderBookingResponse
{
/// <summary>
/// 订单号
/// </summary>
public string No { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
/// <summary>
/// 配送地址
/// </summary>
public string DeliveryAddress { get; set; }
/// <summary>
/// 订单金额
/// </summary>
public decimal Amount { get; set; }
/// <summary>
/// 工号
/// </summary>
public string UserCode { get; set; }
/// <summary>
///
/// </summary>
public string UserNo { get; set; }
}

View File

@@ -1,5 +0,0 @@
namespace Dlhis.Canteen.Order.Response;
public class OrderResponse
{
}

View File

@@ -0,0 +1,21 @@
using Dlhis.Canteen.Dishes.Request;
using Dlhis.Canteen.Order.Request;
using Dlhis.Canteen.Order.Response;
using Kurisu.Core.DataAccess.Dto;
namespace Dlhis.Canteen.Order.Services;
public interface IBookingOrderService
{
/// <summary>
/// 订餐
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
Task BookingAsync(BookingMealRequest request);
Task<Pagination<OrderBookingResponse>> GetBookingPageAsync(OrderBookingPageRequest request);
Task CancelOrderAsync();
}

View File

@@ -1,5 +1,4 @@
using Dlhis.Canteen.Dishes.Request;
using Dlhis.Canteen.Order.Request;
using Dlhis.Canteen.Order.Request;
using Dlhis.Canteen.Order.Response;
using Kurisu.Core.DataAccess.Dto;
@@ -7,7 +6,7 @@ namespace Dlhis.Canteen.Order.Services;
public interface IOrderService : IScopeDependency
{
Task<Pagination<OrderResponse>> GetPageAsync(OrderPageRequest request);
Task<OrderResultResponse> AddAsync(OrderAddRequest request);
@@ -19,20 +18,5 @@ public interface IOrderService : IScopeDependency
/// <returns></returns>
Task<OrderResultResponse> DeviceConsume(OrderAddRequest request);
/// <summary>
/// 订餐
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
Task BookingAsync(BookingMealRequest request);
///// <summary>
///// 获取订单详情
///// </summary>
///// <param name="no"></param>
///// <returns></returns>
//Task<List<OrderDetailResponse>> GetDetailsAsync(string no);
Task<Pagination<OrderAggregationResponse>> GetOrdersAggregationAsync(PageDto page);
}

View File

@@ -0,0 +1,133 @@
using Dlhis.Basic.Api;
using Dlhis.Canteen.Setting.Services;
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Canteen.Enums;
using Kurisu.AspNetCore.UnifyResultAndValidation;
using Kurisu.AspNetCore.Utils.Extensions;
using Kurisu.Core.User.Abstractions;
using Kurisu.SqlSugar.Services;
using Mapster;
using SqlSugar;
namespace Dlhis.Canteen.Order.Services.Implements;
public abstract class BaseOrderService
{
private readonly ICanteenSettingService _canteenSettingService;
private readonly IDbContext _dbContext;
private readonly ICurrentUser _currentUser;
private readonly IDevicePermissionApi _devicePermissionApi;
private readonly ILogger _logger;
private Dictionary<CanteenSettingItemEnumType, string> _settings;
public BaseOrderService(ICanteenSettingService canteenSettingService,
IDbContext dbContext,
ICurrentUser currentUser,
IDevicePermissionApi devicePermissionApi,
ILogger logger)
{
_canteenSettingService = canteenSettingService;
_dbContext = dbContext;
_currentUser = currentUser;
_devicePermissionApi = devicePermissionApi;
_logger = logger;
}
protected BaseOrderService(ICanteenSettingService canteenSettingService)
{
_canteenSettingService = canteenSettingService;
}
protected IDbContext DbContext => _dbContext;
protected ICurrentUser CurrentUser => _currentUser;
/// <summary>
/// 检查权限
/// </summary>
/// <param name="userNo"></param>
/// <param name="deviceUniqueNo"></param>
/// <returns></returns>
protected async Task CheckPermissionAsync(string userNo, string deviceUniqueNo)
{
var uniqueNosResult = await _devicePermissionApi.GetCanteenDeviceUniqueNosByUserNoAsync(userNo);
var uniqueNos = uniqueNosResult.Data;
uniqueNos.Contains(deviceUniqueNo).ThrowIfFalse("无消费权限");
}
/// <summary>
/// 设备并发更新 需要抛出VersionExceptions
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <param name="retry">并发冲突处理次数</param>
/// <param name="errorMessage"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
protected async Task ContinueUpdateAsync(Func<Task> func, int retry, string errorMessage = null)
{
for (int i = 0; i < retry; i++)
{
try
{
await func();
return;
}
catch (Exception ex) when (ex is VersionExceptions)
{
_logger.LogWarning(ex, ex.Message);
}
catch (Exception)
{
throw;
}
}
throw new UserFriendlyException(errorMessage ?? "操作失败,请重试");
}
/// <summary>
/// 获取参数配置
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="item"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
protected async Task<T> GetSettingAsync<T>(CanteenSettingItemEnumType item, T defaultValue = default)
{
if (_settings == null)
{
var subjectId = _currentUser.GetIntSubjectId();
var gainfo = await _dbContext.Queryable<GainfoEntity>().Where(x => x.IdentityUserId == subjectId).FirstAsync();
var jinkoCode = _currentUser.GetUserClaim("jinko-code") ?? string.Empty;
if (jinkoCode.IsPresent())
{
gainfo = await _dbContext.Queryable<GainfoEntity>().Where(x => x.Code == jinkoCode).FirstAsync();
}
_settings = await _canteenSettingService.GetUserSettingAsync(gainfo.No);
}
if (_settings.TryGetValue(item, out var v))
{
try
{
if (int.TryParse(v, out var n))
{
return n.Adapt<T>();
}
return v.Adapt<T>();
}
catch (Exception)
{
return defaultValue;
}
}
return defaultValue;
}
}

View File

@@ -0,0 +1,210 @@
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 Kurisu.Core.DataAccess.Dto;
using Kurisu.SqlSugar.Services;
using Kurisu.Core.User.Abstractions;
using Dlhis.Canteen.Setting.Services;
using Dlhis.Basic.Api;
using Kurisu.SqlSugar.Extensions;
using SqlSugar;
using Mapster;
namespace Dlhis.Canteen.Order.Services.Implements;
public class BookingOrderService : BaseOrderService, IBookingOrderService
{
private readonly IDbContext _dbContext;
private readonly ICurrentUser _currentUser;
private readonly IDisheMenuApi _disheMenuApi;
public BookingOrderService(ICanteenSettingService canteenSettingService, IDisheMenuApi disheMenuApi) : base(canteenSettingService)
{
_disheMenuApi = disheMenuApi;
}
public async Task BookingAsync(BookingMealRequest request)
{
var gainfo = await CheckExistsMyBookingsAsync(request.BookingDate, request.ShiftType);
var businessPlace = await _dbContext.Queryable<BusinessPlaceEntity>().Where(x => x.Code == request.BusinessPlaceCode).FirstAsync();
//预定时间
var now = DateTime.Now;
var bookingDeadline = await GetSettingAsync<TimeOnly?>(CanteenSettingItemEnumType.BookingDeadline) ?? new TimeOnly(20, 30, 00);
var dealLine = DateTime.Today.Add(bookingDeadline.ToTimeSpan());
(now >= dealLine).ThrowIfTrue("超出订餐时间");
//菜单存在
var menuDetailResult = await _disheMenuApi.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 = CanteenApplicaitonTypeEnumType.Booking,
Name = gainfo.Name,
Source = request.Source,
UserNo = gainfo.No,
UserCode = gainfo.Code,
Amount = -Math.Abs(menuDetail.Price),
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(),
SubsidiesFromMonth = now.ToString("yyyyMM"),
IsOffline = false,
IsCostCrossTenant = await GetSettingAsync<bool>(CanteenSettingItemEnumType.AllowCrossTenant),
OriginTenantId = gainfo.Factory,//人员可能跨基地消费
Status = BalanceRecordStatusEnumType.Valid,
OrderStatus = CanteenOrderStatusEnumType.Success,
CostDateTime = now,
//标识当前消费的基地
TenantId = businessPlace.TenantId,
};
var orderId = await _dbContext.InsertReturnIdentityAsync(order);
await ContinueUpdateAsync(async () =>
{
await _disheMenuApi.GetBookingDishesDetailAsync(request.BookingDate, request.BusinessPlaceCode, (int)request.ShiftType, request.DishesCode);
var menuDetail = menuDetailResult.Data;
menuDetail.ThrowIfNull("预定失败,菜品已经下架");
menuDetail.Number -= 1;
(menuDetail.Number < 0).ThrowIfTrue($"{menuDetail.DishesName}已售罄");
var mealInfo = await _dbContext.Queryable<GainfoMealEntity>().Where(x => x.UserNo == order.UserNo).FirstAsync();
if (mealInfo == null)
{
await _dbContext.InsertAsync(new GainfoMealEntity
{
Balance = 0,
BalanceOfRecharge = 0,
BalanceOfSubsidies = 0,
UserCode = gainfo.Code,
UserNo = gainfo.No,
Version = 0,
});
(mealInfo == null).ThrowIfTrue("余额不足,无法消费");
}
//金额+-
mealInfo.Balance += order.Amount;
(mealInfo.Balance < 0).ThrowIfTrue("余额不足,无法消费");
var effect = await _dbContext.Updateable<GainfoMealEntity>()
.SetColumns(x => x.Balance == mealInfo.Balance)
.SetColumns(x => x.Version == x.Version + 1)
.Where(x => x.UserNo == order.UserNo && x.Version == mealInfo.Version)//必须与查询时的version一致
.ExecuteCommandAsync();
if (effect <= 0) throw new VersionExceptions("更新版本冲突");
effect = await _dbContext.Updateable<DishesMenuEntity>()
.SetColumns(x => x.Number == menuDetail.Number)
.SetColumns(x => x.Version == x.Version + 1)
.SetColumns(x => x.ModifiedTime == now)
.Where(x => x.Date == menuDetail.Date
&& x.ShiftType == request.ShiftType
&& x.BusinessPlaceCode == request.BusinessPlaceCode
&& x.DishesCode == request.DishesCode
&& x.Version == menuDetail.Version
)
.ExecuteCommandAsync();
if (effect <= 0) throw new VersionExceptions("更新版本冲突");
//写入记录
var balanceRecord = order.Adapt<BalanceRecordEntity>();
await _dbContext.InsertAsync(balanceRecord);
await _dbContext.InsertAsync(new CanteenOrderDetailEntity
{
OrderNo = order.No,
DishesCode = request.DishesCode,
Price = menuDetail.Price,
//Unit
Number = 1,
});
}, 10);
}
public async Task<Pagination<OrderBookingResponse>> GetBookingPageAsync(OrderBookingPageRequest request)
{
var page = await _dbContext.Queryable<CanteenOrderEntity>()
.LeftJoin<GainfoEntity>((x, ga) => x.UserNo == ga.No)
.Select(x => new OrderBookingResponse
{
No = x.No,
DeliveryAddress = x.DeliveryAddress,
Remark = x.Remark,
Amount = x.Amount
})
.ToPageListAsync(request);
return page;
}
public async Task CancelOrderAsync()
{
throw new NotImplementedException();
}
/// <summary>
/// 检查我的订餐是否存在,存在抛出异常
/// </summary>
/// <param name="bookingDate"></param>
/// <param name="shiftType"></param>
/// <returns></returns>
private async Task<GainfoEntity> CheckExistsMyBookingsAsync(DateTime bookingDate, ShiftEnumType shiftType)
{
var subjectId = _currentUser.GetIntSubjectId();
var gainfo = await _dbContext.Queryable<GainfoEntity>().Where(x => x.IdentityUserId == subjectId).FirstAsync();
var jinkoCode = _currentUser.GetUserClaim("jinko-code") ?? string.Empty;
if (jinkoCode.IsPresent())
{
gainfo = await _dbContext.Queryable<GainfoEntity>().Where(x => x.Code == jinkoCode).FirstAsync();
}
var start = bookingDate.Date.AddDays(-1);
var end = start.AddDays(1).AddSeconds(-1);
var exists = await _dbContext.Queryable<CanteenOrderEntity>()
.Where(x => x.ApplicationType == CanteenApplicaitonTypeEnumType.Booking)
.Where(x => x.ShiftType == shiftType)
.Where(x => x.UserNo == gainfo.No)
.Where(x => x.CreateTime >= start && x.CreateTime <= end)
.AnyAsync();
exists.ThrowIfTrue("不可重复订餐");
return gainfo;
}
}

View File

@@ -18,193 +18,18 @@ using SqlSugar;
using Dlhis.Canteen.Order.Response;
using Dlhis.Canteen.Dishes;
using Dlhis.Canteen.Order.Request;
using System.Numerics;
using MySqlX.XDevAPI.Relational;
namespace Dlhis.Canteen.Order.Services.Implements;
public class OrderService : IOrderService
public class OrderService : BaseOrderService, IOrderService
{
private readonly IDbContext _dbContext;
private readonly ICanteenSettingService _canteenSettingService;
private readonly IDevicePermissionApi _devicePermissionApi;
private readonly ICurrentUser _currentUser;
private readonly IDisheMenuApi _disheMenuApi;
private readonly ILogger<OrderService> _logger;
public OrderService(IDbContext dbContext,
ICanteenSettingService canteenSettingService,
IDevicePermissionApi devicePermissionApi,
ICurrentUser currentUser,
IDisheMenuApi disheMenuApi,
public OrderService(ICanteenSettingService canteenSettingService, IDbContext dbContext,
ICurrentUser currentUser, IDevicePermissionApi devicePermissionApi,
ILogger<OrderService> logger)
: base(canteenSettingService, dbContext, currentUser, devicePermissionApi, logger)
{
_dbContext = dbContext;
_canteenSettingService = canteenSettingService;
_devicePermissionApi = devicePermissionApi;
_currentUser = currentUser;
_disheMenuApi = disheMenuApi;
_logger = logger;
}
public async Task<Pagination<OrderResponse>> GetPageAsync(OrderPageRequest request)
{
var page = await _dbContext.Queryable<CanteenOrderEntity>()
.Select<OrderResponse>()
.ToPageListAsync(request);
return page;
}
private async Task CheckPAsync(string userNo, string deviceUniqueNo)
{
var uniqueNosResult = await _devicePermissionApi.GetCanteenDeviceUniqueNosByUserNoAsync(userNo);
var uniqueNos = uniqueNosResult.Data;
uniqueNos.Contains(deviceUniqueNo).ThrowIfFalse("无消费权限");
}
public async Task BookingAsync(BookingMealRequest request)
{
var gainfo = await CheckExistsMyBookingsAsync(request.BookingDate, request.ShiftType);
var businessPlace = await _dbContext.Queryable<BusinessPlaceEntity>().Where(x => x.Code == request.BusinessPlaceCode).FirstAsync();
//预定时间
var now = DateTime.Now;
var settings = await _canteenSettingService.GetUserSettingAsync(gainfo.Code);
var bookingDeadline = GetSetting<TimeOnly?>(settings, CanteenSettingItemEnumType.BookingDeadline) ?? new TimeOnly(20, 30, 00);
var dealLine = DateTime.Today.Add(bookingDeadline.ToTimeSpan());
(now >= dealLine).ThrowIfTrue("超出订餐时间");
//菜单存在
var menuDetailResult = await _disheMenuApi.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 = CanteenApplicaitonTypeEnumType.Booking,
Name = gainfo.Name,
Source = request.Source,
UserNo = gainfo.No,
UserCode = gainfo.Code,
Amount = -Math.Abs(menuDetail.Price),
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(),
SubsidiesFromMonth = now.ToString("yyyyMM"),
IsOffline = false,
IsCostCrossTenant = GetSetting<bool>(settings, CanteenSettingItemEnumType.AllowCrossTenant),
OriginTenantId = gainfo.Factory,//人员可能跨基地消费
Status = BalanceRecordStatusEnumType.Valid,
OrderStatus = CanteenOrderStatusEnumType.Successful,
CostDateTime = now,
//标识当前消费的基地
TenantId = businessPlace.TenantId,
};
var orderId = await _dbContext.InsertReturnIdentityAsync(order);
await ContinueUpdateAsync(async () =>
{
await _disheMenuApi.GetBookingDishesDetailAsync(request.BookingDate, request.BusinessPlaceCode, (int)request.ShiftType, request.DishesCode);
var menuDetail = menuDetailResult.Data;
menuDetail.ThrowIfNull("预定失败,菜品已经下架");
menuDetail.Number -= 1;
(menuDetail.Number < 0).ThrowIfTrue($"{menuDetail.DishesName}已售罄");
var mealInfo = await _dbContext.Queryable<GainfoMealEntity>().Where(x => x.UserNo == order.UserNo).FirstAsync();
if (mealInfo == null)
{
await _dbContext.InsertAsync(new GainfoMealEntity
{
Balance = 0,
BalanceOfRecharge = 0,
BalanceOfSubsidies = 0,
UserCode = gainfo.Code,
UserNo = gainfo.No,
Version = 0,
});
(mealInfo == null).ThrowIfTrue("余额不足,无法消费");
}
//金额+-
mealInfo.Balance += order.Amount;
(mealInfo.Balance < 0).ThrowIfTrue("余额不足,无法消费");
var effect = await _dbContext.Updateable<GainfoMealEntity>()
.SetColumns(x => x.Balance == mealInfo.Balance)
.SetColumns(x => x.Version == x.Version + 1)
.Where(x => x.UserNo == order.UserNo && x.Version == mealInfo.Version)//必须与查询时的version一致
.ExecuteCommandAsync();
if (effect <= 0) throw new VersionExceptions("更新版本冲突");
effect = await _dbContext.Updateable<DishesMenuEntity>()
.SetColumns(x => x.Number == menuDetail.Number)
.SetColumns(x => x.Version == x.Version + 1)
.SetColumns(x => x.ModifiedTime == now)
.Where(x => x.Date == menuDetail.Date
&& x.ShiftType == request.ShiftType
&& x.BusinessPlaceCode == request.BusinessPlaceCode
&& x.DishesCode == request.DishesCode
&& x.Version == menuDetail.Version
)
.ExecuteCommandAsync();
if (effect <= 0) throw new VersionExceptions("更新版本冲突");
//写入记录
var balanceRecord = order.Adapt<BalanceRecordEntity>();
await _dbContext.InsertAsync(balanceRecord);
await _dbContext.InsertAsync(new CanteenOrderDetailEntity
{
OrderNo = order.No,
DishesCode = request.DishesCode,
Price = menuDetail.Price,
//Unit
Number = 1,
});
}, 10);
}
//public async Task<List<OrderDetailResponse>> GetDetailsAsync(string no)
//{
// return await _dbContext.Queryable<CanteenOrderDetailEntity>()
// .LeftJoin<DishesEntity>((x, d) => x.DishesCode == d.CustomCode)
// .Where(x => x.OrderNo == no)
// .Select((x, d) => new OrderDetailResponse
// {
// DishesCode = x.DishesCode,
// Price = x.Price,
// Name = d.Name,
// Number = x.Number,
// Unit = x.Unit,
// ImageUrl = d.ImageUrl,
// })
// .ToListAsync();
//}
public async Task<OrderResultResponse> DeviceConsume(OrderAddRequest request)
{
return await AddAsync(request);
@@ -216,21 +41,21 @@ public class OrderService : IOrderService
_ = long.TryParse(request.UserUniqueNo, out var userId);
//根据档案号
var gainfo = await _dbContext.Queryable<GainfoEntity>()
var gainfo = await DbContext.Queryable<GainfoEntity>()
.Where(x => x.No == request.UserUniqueNo || x.Code == request.UserUniqueNo || x.Id == userId || x.CardNo == request.UserUniqueNo)
.FirstAsync();
gainfo.ThrowIfNull($"无消费权限");
//消费权限控制
await CheckPAsync(gainfo.No, request.DeviceUniqueNo);
await CheckPermissionAsync(gainfo.No, request.DeviceUniqueNo);
var canteenUserExists = await _dbContext.Queryable<GainfoMealEntity>()
var canteenUserExists = await DbContext.Queryable<GainfoMealEntity>()
.Where(x => x.UserNo == gainfo.No)
.AnyAsync();
if (!canteenUserExists)
{
await _dbContext.InsertAsync(new GainfoMealEntity
await DbContext.InsertAsync(new GainfoMealEntity
{
UserNo = gainfo.No,
Balance = 0,
@@ -258,7 +83,7 @@ public class OrderService : IOrderService
}
//获取人员余额
return await _dbContext.Queryable<GainfoEntity>()
return await DbContext.Queryable<GainfoEntity>()
.LeftJoin<DepartmentEntity>((ga, department) => ga.DepartmentNo == department.No)
.LeftJoin<GainfoMealEntity>((ga, department, mealUser) => ga.No == mealUser.UserNo)
.Where(ga => ga.Id == gainfo.Id)
@@ -281,11 +106,11 @@ public class OrderService : IOrderService
/// <returns></returns>
private async Task ConsumeAsync(ConsumeRequest request, GainfoEntity gainfo)
{
var device = await _dbContext.Queryable<DeviceEntity>().Where(x => x.UniqueNo == request.DeviceUniqueNo).Where(x => x.Enable).FirstAsync();
var device = await DbContext.Queryable<DeviceEntity>().Where(x => x.UniqueNo == request.DeviceUniqueNo).Where(x => x.Enable).FirstAsync();
device.ThrowIfNull("设备不存在或者尚未启用");
var registry = await _dbContext.Queryable<DeviceRegistryEntity>().Where(x => x.DeviceUniqueNo == request.DeviceUniqueNo).FirstAsync();
var registry = await DbContext.Queryable<DeviceRegistryEntity>().Where(x => x.DeviceUniqueNo == request.DeviceUniqueNo).FirstAsync();
registry.ThrowIfNull("设备尚未注册");
var businessPlaceInfo = await _dbContext.Queryable<BusinessPlaceEntity>()
var businessPlaceInfo = await DbContext.Queryable<BusinessPlaceEntity>()
.LeftJoin<BusinessPlaceShiftRelationshipEntity>((x, sr) => x.Code == sr.PlaceCode)
.Where(x => x.Code == registry.BusinessPlaceCode)
.Select((x, sr) => new
@@ -298,7 +123,7 @@ public class OrderService : IOrderService
businessPlaceInfo.ThrowIfNull("营业点尚未设置");
//获取当前消费餐次
var shifts = await _dbContext.Queryable<BusinessShiftMealRelationshipEntity>().Where(x => x.ShiftCode == businessPlaceInfo.ShiftCode).ToListAsync();
var shifts = await DbContext.Queryable<BusinessShiftMealRelationshipEntity>().Where(x => x.ShiftCode == businessPlaceInfo.ShiftCode).ToListAsync();
var shift = shifts.FirstOrDefault(x => new TimePeriod()
{
@@ -307,8 +132,7 @@ public class OrderService : IOrderService
}.IsPresent1(request.CostDateTime));
var settings = await _canteenSettingService.GetUserSettingAsync(request.UserUniqueNo);
var switchTime = GetSetting<TimeOnly?>(settings, CanteenSettingItemEnumType.BusinessSwitchTime);
var switchTime = await GetSettingAsync<TimeOnly?>(CanteenSettingItemEnumType.BusinessSwitchTime);
var shiftTimePeriod = new TimePeriod()
{
@@ -356,7 +180,7 @@ public class OrderService : IOrderService
No = SnowFlakeHelper.Instance.NextId().ToString(),
SubsidiesFromMonth = request.CostDateTime.ToString("yyyyMM"),
IsOffline = request.IsOffline,
IsCostCrossTenant = GetSetting<bool>(settings, CanteenSettingItemEnumType.AllowCrossTenant),
IsCostCrossTenant = await GetSettingAsync<bool>(CanteenSettingItemEnumType.AllowCrossTenant),
OriginTenantId = gainfo.Factory,//人员可能跨基地消费
@@ -371,14 +195,14 @@ public class OrderService : IOrderService
TenantId = businessPlaceInfo.TenantId,
};
var orderId = await _dbContext.InsertReturnIdentityAsync(order);
var orderId = await DbContext.InsertReturnIdentityAsync(order);
try
{
await GenerateConsumeRecordAsync(orderId, settings, shifts, request.CostDateTime);
await GenerateConsumeRecordAsync(orderId, shifts, request.CostDateTime);
}
catch (Exception ex)
{
await _dbContext.Updateable<CanteenOrderEntity>()
await DbContext.Updateable<CanteenOrderEntity>()
.SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Failure)
.SetColumns(x => x.Result == ex.Message)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
@@ -388,8 +212,8 @@ public class OrderService : IOrderService
throw;
}
await _dbContext.Updateable<CanteenOrderEntity>()
.SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Successful)
await DbContext.Updateable<CanteenOrderEntity>()
.SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Success)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(x => x.Id == orderId)
.ExecuteCommandAsync();
@@ -403,8 +227,6 @@ public class OrderService : IOrderService
/// <returns></returns>
private async Task RechargeAsync(RechargeRequest request, GainfoEntity gainfo)
{
var settings = await _canteenSettingService.GetUserSettingAsync(request.UserUniqueNo);
var order = new CanteenOrderEntity
{
RequestJson = request.ToJson(),
@@ -425,7 +247,7 @@ public class OrderService : IOrderService
No = SnowFlakeHelper.Instance.NextId().ToString(),
SubsidiesFromMonth = request.CostDateTime.ToString("yyyyMM"),
IsCostCrossTenant = GetSetting<bool>(settings, CanteenSettingItemEnumType.AllowCrossTenant),
IsCostCrossTenant = await GetSettingAsync<bool>(CanteenSettingItemEnumType.AllowCrossTenant),
OriginTenantId = gainfo.Factory,//人员可能跨基地消费
@@ -439,14 +261,14 @@ public class OrderService : IOrderService
TenantId = gainfo.Factory,
};
var orderId = await _dbContext.InsertReturnIdentityAsync(order);
var orderId = await DbContext.InsertReturnIdentityAsync(order);
try
{
await GenerateRechargeRecordAsync(orderId);
}
catch (Exception ex)
{
await _dbContext.Updateable<CanteenOrderEntity>()
await DbContext.Updateable<CanteenOrderEntity>()
.SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Failure)
.SetColumns(x => x.Result == ex.Message)
.Where(x => x.Id == orderId)
@@ -455,8 +277,8 @@ public class OrderService : IOrderService
throw;
}
await _dbContext.Updateable<CanteenOrderEntity>()
.SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Successful)
await DbContext.Updateable<CanteenOrderEntity>()
.SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Success)
.Where(x => x.Id == orderId)
.ExecuteCommandAsync();
}
@@ -464,15 +286,15 @@ public class OrderService : IOrderService
public async Task<Pagination<OrderAggregationResponse>> GetOrdersAggregationAsync(PageDto page)
{
var subjectId = _currentUser.GetIntSubjectId();
var gainfo = await _dbContext.Queryable<GainfoEntity>().Where(x => x.IdentityUserId == subjectId).FirstAsync();
var jinkoCode = _currentUser.GetUserClaim("jinko-code") ?? string.Empty;
var subjectId = CurrentUser.GetIntSubjectId();
var gainfo = await DbContext.Queryable<GainfoEntity>().Where(x => x.IdentityUserId == subjectId).FirstAsync();
var jinkoCode = CurrentUser.GetUserClaim("jinko-code") ?? string.Empty;
if (jinkoCode.IsPresent())
{
gainfo = await _dbContext.Queryable<GainfoEntity>().Where(x => x.Code == jinkoCode).FirstAsync();
gainfo = await DbContext.Queryable<GainfoEntity>().Where(x => x.Code == jinkoCode).FirstAsync();
}
var pageResult = await _dbContext.Queryable<CanteenOrderEntity>().Where(x => x.UserNo == gainfo.No)
var pageResult = await DbContext.Queryable<CanteenOrderEntity>().Where(x => x.UserNo == gainfo.No)
.LeftJoin<BusinessPlaceEntity>((x, bplace) => x.BusinessPlaceCode == bplace.Code)
.Where(x => x.ApplicationType == CanteenApplicaitonTypeEnumType.Booking)
.Select((x, bplace) => new OrderAggregationResponse
@@ -485,7 +307,7 @@ public class OrderService : IOrderService
//.LeftJoin<CanteenOrderDetailEntity>((x, bplace, detail) => x.No == detail.OrderNo)
var nos = pageResult.Data.Select(x => x.No).ToList();
var dtos = await _dbContext.Queryable<CanteenOrderDetailEntity>()
var dtos = await DbContext.Queryable<CanteenOrderDetailEntity>()
.LeftJoin<DishesEntity>((x, d) => x.DishesCode == d.CustomCode)
.Where(x => nos.Contains(x.OrderNo))
.Select((x, d) => new DishesDto
@@ -512,78 +334,17 @@ public class OrderService : IOrderService
//******************************************************* help methods ************************************************************/
/// <summary>
/// 设备并发更新 需要抛出VersionExceptions
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <param name="retry">并发冲突处理次数</param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
private async Task ContinueUpdateAsync(Func<Task> func, int retry)
{
for (int i = 0; i < retry; i++)
{
try
{
await func();
return;
}
catch (Exception ex) when (ex is VersionExceptions)
{
_logger.LogWarning(ex, ex.Message);
}
catch (Exception)
{
throw;
}
}
throw new UserFriendlyException("操作失败,请重试");
}
/// <summary>
/// 获取参数配置
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="settings"></param>
/// <param name="item"></param>
/// <param name="defaultValue"></param>
/// <returns></returns>
private static T GetSetting<T>(Dictionary<CanteenSettingItemEnumType, string> settings, CanteenSettingItemEnumType item, T defaultValue = default)
{
if (settings.TryGetValue(item, out var v))
{
try
{
if (int.TryParse(v, out var n))
{
return n.Adapt<T>();
}
return v.Adapt<T>();
}
catch (Exception)
{
return defaultValue;
}
}
return defaultValue;
}
/// <summary>
/// 验证参数
/// </summary>
/// <param name="settings"></param>
/// <param name="shifts"></param>
/// <param name="costDateTime"></param>
/// <param name="order"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
private async Task Verify(Dictionary<CanteenSettingItemEnumType, string> settings, List<BusinessShiftMealRelationshipEntity> shifts, DateTime costDateTime, CanteenOrderEntity order)
private async Task Verify(List<BusinessShiftMealRelationshipEntity> shifts, DateTime costDateTime, CanteenOrderEntity order)
{
//在线
if (!order.IsOffline)
@@ -599,15 +360,15 @@ public class OrderService : IOrderService
var beginOfDay = DateTime.Today;
var endOfDay = beginOfDay.AddHours(23).AddMinutes(59).AddSeconds(59);
var balanceRecords = await _dbContext.Queryable<BalanceRecordEntity>()
var balanceRecords = await DbContext.Queryable<BalanceRecordEntity>()
.Where(x => x.UserNo == order.UserNo)
.Where(x => x.CreateTime >= beginOfDay && x.CreateTime <= endOfDay) //今天消费记录
.ToListAsync();
var dayMarketCostMax = GetSetting<decimal>(settings, CanteenSettingItemEnumType.DayMarketCostMax);
var dayMarketCostMax = await GetSettingAsync<decimal>(CanteenSettingItemEnumType.DayMarketCostMax);
if (dayMarketCostMax != default)
{
var businessPlaceCodes = await _dbContext.Queryable<BusinessPlaceEntity>()
var businessPlaceCodes = await DbContext.Queryable<BusinessPlaceEntity>()
.Where(x => x.Catalog == CanteenBusinessPlaceCatalogEnumType.Supermarket)
.Select(x => x.Code).ToListAsync();
@@ -618,7 +379,7 @@ public class OrderService : IOrderService
}
}
var allowRepeatInOneShift = GetSetting(settings, CanteenSettingItemEnumType.AllowRepeatInOneShift, true);
var allowRepeatInOneShift = await GetSettingAsync(CanteenSettingItemEnumType.AllowRepeatInOneShift, true);
if (!allowRepeatInOneShift)
{
//todo 餐次问题
@@ -629,7 +390,7 @@ public class OrderService : IOrderService
}
}
var onceCostMax = GetSetting<decimal>(settings, CanteenSettingItemEnumType.OnceCostMax);
var onceCostMax = await GetSettingAsync<decimal>(CanteenSettingItemEnumType.OnceCostMax);
if (onceCostMax != default)
{
if (Math.Abs(order.Amount) > onceCostMax)
@@ -638,7 +399,7 @@ public class OrderService : IOrderService
}
}
var dayCostMax = GetSetting<decimal>(settings, CanteenSettingItemEnumType.DayCostMax);
var dayCostMax = await GetSettingAsync<decimal>(CanteenSettingItemEnumType.DayCostMax);
if (dayCostMax != default)
{
var cost = balanceRecords.Where(x => x.Type == BalanceRecordTypeEnumType.Out).Sum(x => Math.Abs(x.Amount));
@@ -659,15 +420,15 @@ public class OrderService : IOrderService
/// <exception cref="VersionExceptions"></exception>
private async Task GenerateRechargeRecordAsync(long orderId)
{
var order = await _dbContext.Queryable<CanteenOrderEntity>().Where(x => x.Id == orderId).FirstAsync();
var order = await DbContext.Queryable<CanteenOrderEntity>().Where(x => x.Id == orderId).FirstAsync();
await ContinueUpdateAsync(async () =>
{
var canteenUser = await _dbContext.Queryable<GainfoMealEntity>().Where(x => x.UserNo == order.UserNo).FirstAsync();
var canteenUser = await DbContext.Queryable<GainfoMealEntity>().Where(x => x.UserNo == order.UserNo).FirstAsync();
//金额+-
canteenUser.Balance += order.Amount;
var effect = await _dbContext.Updateable<GainfoMealEntity>()
var effect = await DbContext.Updateable<GainfoMealEntity>()
.SetColumns(x => x.Balance == canteenUser.Balance)
.SetColumns(x => x.Version == x.Version + 1)
.Where(x => x.UserNo == order.UserNo && x.Version == canteenUser.Version)
@@ -677,7 +438,7 @@ public class OrderService : IOrderService
//写入记录
var balanceRecord = order.Adapt<BalanceRecordEntity>();
await _dbContext.InsertAsync(balanceRecord);
await DbContext.InsertAsync(balanceRecord);
}, 5);
}
@@ -686,25 +447,24 @@ public class OrderService : IOrderService
/// 生成充值记录
/// </summary>
/// <param name="orderId"></param>
/// <param name="settings"></param>
/// <param name="shifts"></param>
/// <param name="costDateTime"></param>
/// <returns></returns>
/// <exception cref="UserFriendlyException"></exception>
/// <exception cref="VersionExceptions"></exception>
private async Task GenerateConsumeRecordAsync(long orderId, Dictionary<CanteenSettingItemEnumType, string> settings, List<BusinessShiftMealRelationshipEntity> shifts, DateTime costDateTime)
private async Task GenerateConsumeRecordAsync(long orderId, List<BusinessShiftMealRelationshipEntity> shifts, DateTime costDateTime)
{
var order = await _dbContext.Queryable<CanteenOrderEntity>().Where(x => x.Id == orderId).FirstAsync();
var order = await DbContext.Queryable<CanteenOrderEntity>().Where(x => x.Id == orderId).FirstAsync();
//校验
await Verify(settings, shifts, costDateTime, order);
await Verify(shifts, costDateTime, order);
var allowNoBalance = GetSetting<bool>(settings, CanteenSettingItemEnumType.AllowNoBalance);
var allowNoBalance = await GetSettingAsync<bool>(CanteenSettingItemEnumType.AllowNoBalance);
await ContinueUpdateAsync(async () =>
{
if (!allowNoBalance)
{
var canteenUser = await _dbContext.Queryable<GainfoMealEntity>().Where(x => x.UserNo == order.UserNo).FirstAsync();
var canteenUser = await DbContext.Queryable<GainfoMealEntity>().Where(x => x.UserNo == order.UserNo).FirstAsync();
//金额+-
canteenUser.Balance += order.Amount;
if (canteenUser.Balance < 0)
@@ -712,7 +472,7 @@ public class OrderService : IOrderService
throw new UserFriendlyException("余额不足,无法消费");
}
var effect = await _dbContext.Updateable<GainfoMealEntity>()
var effect = await DbContext.Updateable<GainfoMealEntity>()
.SetColumns(x => x.Balance == canteenUser.Balance)
.SetColumns(x => x.Version == x.Version + 1)
.Where(x => x.UserNo == order.UserNo && x.Version == canteenUser.Version)//必须与查询时的version一致
@@ -723,39 +483,10 @@ public class OrderService : IOrderService
//写入记录
var balanceRecord = order.Adapt<BalanceRecordEntity>();
await _dbContext.InsertAsync(balanceRecord);
await DbContext.InsertAsync(balanceRecord);
}, 5);
}
/// <summary>
/// 检查我的订餐是否存在,存在抛出异常
/// </summary>
/// <param name="bookingDate"></param>
/// <param name="shiftType"></param>
/// <returns></returns>
private async Task<GainfoEntity> CheckExistsMyBookingsAsync(DateTime bookingDate, ShiftEnumType shiftType)
{
var subjectId = _currentUser.GetIntSubjectId();
var gainfo = await _dbContext.Queryable<GainfoEntity>().Where(x => x.IdentityUserId == subjectId).FirstAsync();
var jinkoCode = _currentUser.GetUserClaim("jinko-code") ?? string.Empty;
if (jinkoCode.IsPresent())
{
gainfo = await _dbContext.Queryable<GainfoEntity>().Where(x => x.Code == jinkoCode).FirstAsync();
}
var start = bookingDate.Date.AddDays(-1);
var end = start.AddDays(1).AddSeconds(-1);
var exists = await _dbContext.Queryable<CanteenOrderEntity>()
.Where(x => x.ApplicationType == CanteenApplicaitonTypeEnumType.Booking)
.Where(x => x.ShiftType == shiftType)
.Where(x => x.UserNo == gainfo.No)
.Where(x => x.CreateTime >= start && x.CreateTime <= end)
.AnyAsync();
exists.ThrowIfTrue("不可重复订餐");
return gainfo;
}
}