diff --git a/Src/Dlhis.Basic/Device/Services/Implements/DeviceConnectService.cs b/Src/Dlhis.Basic/Device/Services/Implements/DeviceConnectService.cs
index 4b767cc6..30671f82 100644
--- a/Src/Dlhis.Basic/Device/Services/Implements/DeviceConnectService.cs
+++ b/Src/Dlhis.Basic/Device/Services/Implements/DeviceConnectService.cs
@@ -17,10 +17,12 @@ namespace Dlhis.Basic.Device.Services.Implements;
public class DeviceConnectService : IDeviceConnectService
{
private readonly IDbContext _dbContext;
+ private readonly IHttpContextAccessor _httpContextAccessor;
- public DeviceConnectService(IDbContext dbContext)
+ public DeviceConnectService(IDbContext dbContext, IHttpContextAccessor httpContextAccessor)
{
_dbContext = dbContext;
+ _httpContextAccessor = httpContextAccessor;
}
diff --git a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoMealService.cs b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoMealService.cs
index 93249cff..587c3fb2 100644
--- a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoMealService.cs
+++ b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoMealService.cs
@@ -96,7 +96,6 @@ public class GainfoMealService : IGainfoMealService
ShiftType = null,
CostDate = now.Date,
DeviceUniqueNo = null,
- SubsidiesFromMonth = now.ToString("yyyyMM"),
IP = null,
CostId = gainfoLeave.CostId,
OriginTenantId = gainfoLeave.Factory,
@@ -141,7 +140,6 @@ public class GainfoMealService : IGainfoMealService
ShiftType = null,
CostDate = now.Date,
DeviceUniqueNo = null,
- SubsidiesFromMonth = now.ToString("yyyyMM"),
IP = null,
CostId = gainfo.CostId,
OriginTenantId = gainfo.Factory,
diff --git a/Src/Dlhis.Basic/Program.cs b/Src/Dlhis.Basic/Program.cs
index 882db25c..6bd00925 100644
--- a/Src/Dlhis.Basic/Program.cs
+++ b/Src/Dlhis.Basic/Program.cs
@@ -22,6 +22,7 @@ public class Startup : DefaultStartup
public override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
+ services.AddHttpContextAccessor();
services.AddRedis();
}
}
\ No newline at end of file
diff --git a/Src/Dlhis.Canteen/Dlhis.Canteen.csproj b/Src/Dlhis.Canteen/Dlhis.Canteen.csproj
index fc8473bf..0c6bb0f3 100644
--- a/Src/Dlhis.Canteen/Dlhis.Canteen.csproj
+++ b/Src/Dlhis.Canteen/Dlhis.Canteen.csproj
@@ -28,10 +28,4 @@
-
-
-
-
-
-
diff --git a/Src/Dlhis.Canteen/EMealVoucher/Controllers/EMealVoucherController.cs b/Src/Dlhis.Canteen/EMealVoucher/Controllers/EMealVoucherController.cs
index 98e2f3ed..40b9483d 100644
--- a/Src/Dlhis.Canteen/EMealVoucher/Controllers/EMealVoucherController.cs
+++ b/Src/Dlhis.Canteen/EMealVoucher/Controllers/EMealVoucherController.cs
@@ -1,3 +1,7 @@
+using Dlhis.Canteen.EMealVoucher.Request;
+using Dlhis.Canteen.EMealVoucher.Response;
+using Dlhis.Canteen.EMealVoucher.Services;
+using Kurisu.AspNetCore.DataAccess.Dto;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -8,4 +12,65 @@ namespace Dlhis.Canteen.EMealVoucher.Controllers;
[ApiController]
public class EMealVoucherController : ControllerBase
{
+ private readonly IEMealVoucherService _eMealVoucherService;
+
+ public EMealVoucherController(IEMealVoucherService eMealVoucherService)
+ {
+ _eMealVoucherService = eMealVoucherService;
+ }
+
+ ///
+ /// 获取我的电子餐券申请
+ ///
+ ///
+ ///
+ [HttpGet("my")]
+ public async Task> GetMyEMealVoucherPageAsync([FromQuery] EMealVoucherPageRequest request)
+ {
+ return await _eMealVoucherService.GetMyEMealVoucherPageAsync(request);
+ }
+
+ ///
+ /// 查看电子餐券
+ ///
+ ///
+ ///
+ [HttpGet("no")]
+ public async Task> GetDetailsByNoAsync(string no)
+ {
+ return await _eMealVoucherService.GetDetailsByNoAsync(no);
+ }
+
+ ///
+ /// 获取url签名
+ ///
+ ///
+ ///
+ [HttpGet("signature")]
+ public async Task GetQrCodeSignatureAsync(long detailId)
+ {
+ return await _eMealVoucherService.GetQrCodeSignatureAsync(detailId);
+ }
+
+ ///
+ /// 刷新url签名
+ ///
+ ///
+ [HttpPost("signature/refresh")]
+ public async Task RefreshSignatureAsync(long detailId)
+ {
+ await _eMealVoucherService.RefreshSignatureAsync(detailId);
+ }
+
+ ///
+ /// 获取二维码
+ ///
+ ///
+ ///
+ [AllowAnonymous]
+ [HttpGet("qrcode")]
+ public async Task GetQrCodeAsync(string signature)
+ {
+ return await _eMealVoucherService.GetQrCodeAsync(signature);
+ }
}
\ No newline at end of file
diff --git a/Src/Dlhis.Canteen/EMealVoucher/Request/EMealVoucherPageRequest.cs b/Src/Dlhis.Canteen/EMealVoucher/Request/EMealVoucherPageRequest.cs
new file mode 100644
index 00000000..d23ddad0
--- /dev/null
+++ b/Src/Dlhis.Canteen/EMealVoucher/Request/EMealVoucherPageRequest.cs
@@ -0,0 +1,7 @@
+using Kurisu.AspNetCore.DataAccess.Dto;
+
+namespace Dlhis.Canteen.EMealVoucher.Request;
+
+public class EMealVoucherPageRequest : PageDto
+{
+}
\ No newline at end of file
diff --git a/Src/Dlhis.Canteen/EMealVoucher/Response/EMealVoucherDetailResponse.cs b/Src/Dlhis.Canteen/EMealVoucher/Response/EMealVoucherDetailResponse.cs
new file mode 100644
index 00000000..03656071
--- /dev/null
+++ b/Src/Dlhis.Canteen/EMealVoucher/Response/EMealVoucherDetailResponse.cs
@@ -0,0 +1,31 @@
+namespace Dlhis.Canteen.EMealVoucher.Response;
+
+public class EMealVoucherDetailResponse
+{
+ public long Id { get; set; }
+
+ ///
+ /// 用餐人
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// 手机号
+ ///
+ public string Phone { get; set; }
+
+ ///
+ /// 就餐开始
+ ///
+ public DateTime Start { get; set; }
+
+ ///
+ /// 就餐结束
+ ///
+ public DateTime End { get; set; }
+
+ ///
+ /// 金额
+ ///
+ public decimal Balance { get; set; }
+}
\ No newline at end of file
diff --git a/Src/Dlhis.Canteen/EMealVoucher/Response/EMealVoucherPageResponse.cs b/Src/Dlhis.Canteen/EMealVoucher/Response/EMealVoucherPageResponse.cs
new file mode 100644
index 00000000..c35caac3
--- /dev/null
+++ b/Src/Dlhis.Canteen/EMealVoucher/Response/EMealVoucherPageResponse.cs
@@ -0,0 +1,12 @@
+namespace Dlhis.Canteen.EMealVoucher.Response;
+
+public class EMealVoucherPageResponse
+{
+ public long Id { get; set; }
+
+ public string No { get; set; }
+
+ public string Title { get; set; }
+
+ public DateTime Date { get; set; }
+}
\ No newline at end of file
diff --git a/Src/Dlhis.Canteen/EMealVoucher/Services/IEMealVoucherService.cs b/Src/Dlhis.Canteen/EMealVoucher/Services/IEMealVoucherService.cs
new file mode 100644
index 00000000..ef41f70a
--- /dev/null
+++ b/Src/Dlhis.Canteen/EMealVoucher/Services/IEMealVoucherService.cs
@@ -0,0 +1,18 @@
+using Dlhis.Canteen.EMealVoucher.Request;
+using Dlhis.Canteen.EMealVoucher.Response;
+using Kurisu.AspNetCore.DataAccess.Dto;
+
+namespace Dlhis.Canteen.EMealVoucher.Services;
+
+public interface IEMealVoucherService : IScopeDependency
+{
+ Task> GetMyEMealVoucherPageAsync(EMealVoucherPageRequest request);
+
+ Task> GetDetailsByNoAsync(string no);
+
+ Task GetQrCodeSignatureAsync(long detailId);
+
+ Task RefreshSignatureAsync(long detailId);
+
+ Task GetQrCodeAsync(string signature);
+}
\ No newline at end of file
diff --git a/Src/Dlhis.Canteen/EMealVoucher/Services/Implements/EMealVoucherService.cs b/Src/Dlhis.Canteen/EMealVoucher/Services/Implements/EMealVoucherService.cs
new file mode 100644
index 00000000..c00c2fa1
--- /dev/null
+++ b/Src/Dlhis.Canteen/EMealVoucher/Services/Implements/EMealVoucherService.cs
@@ -0,0 +1,106 @@
+using System.Text;
+using Dlhis.Canteen.EMealVoucher.Request;
+using Dlhis.Canteen.EMealVoucher.Response;
+using Dlhis.Entity.Canteen.Entities;
+using Kurisu.AspNetCore.Authentication.Abstractions;
+using Kurisu.AspNetCore.DataAccess.Dto;
+using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions;
+using Kurisu.AspNetCore.DataAccess.SqlSugar.Services;
+using Kurisu.AspNetCore.Utils.Extensions;
+using SqlSugar;
+
+namespace Dlhis.Canteen.EMealVoucher.Services.Implements;
+
+public class EMealVoucherService : IEMealVoucherService
+{
+ private readonly IDbContext _dbContext;
+ private readonly ICurrentUser _currentUser;
+
+ public EMealVoucherService(IDbContext dbContext, ICurrentUser currentUser)
+ {
+ _dbContext = dbContext;
+ _currentUser = currentUser;
+ }
+
+
+ public async Task> GetMyEMealVoucherPageAsync(EMealVoucherPageRequest request)
+ {
+ var code = _currentUser.GetUserClaim("jinko-code");
+ code = "0123456";
+ return await _dbContext.Queryable()
+ .Where(x => x.Code == code)
+ .OrderBy(x => x.Id, OrderByType.Desc)
+ .Select()
+ .ToPageListAsync(request);
+ }
+
+
+ public async Task> GetDetailsByNoAsync(string no)
+ {
+ return await _dbContext.Queryable()
+ .Where(x => x.No == no)
+ .Select()
+ .ToListAsync();
+ }
+
+
+ public async Task GetQrCodeSignatureAsync(long detailId)
+ {
+ return await GenerateSignatureAsync(detailId, false);
+ }
+
+ public async Task RefreshSignatureAsync(long detailId)
+ {
+ await GenerateSignatureAsync(detailId, true);
+ }
+
+ private async Task GenerateSignatureAsync(long detailId, bool isRefresh)
+ {
+ var eMealVoucherDetail = await _dbContext.Queryable().Where(x => x.Id == detailId).SingleAsync();
+ eMealVoucherDetail.ThrowIfNull("数据不存在");
+ if (eMealVoucherDetail.Signature.IsPresent())
+ {
+ if (isRefresh)
+ {
+ await _dbContext.Deleteable().Where(x => x.Signature == eMealVoucherDetail.Signature).ExecuteCommandAsync();
+ }
+ else
+ {
+ return eMealVoucherDetail.Signature;
+ }
+ }
+
+ eMealVoucherDetail.Signature = Guid.NewGuid().ToString().Replace("-", "") + DateTimeOffset.Now.ToUnixTimeMilliseconds();
+ await _dbContext.UpdateAsync(eMealVoucherDetail);
+
+ return eMealVoucherDetail.Signature;
+ }
+
+ ///
+ public async Task GetQrCodeAsync(string signature)
+ {
+ var eMealVoucherDetail = await _dbContext.Queryable().Where(x => x.Signature == signature).SingleAsync();
+ eMealVoucherDetail.ThrowIfNull("无效地址");
+ var eMealVoucher = await _dbContext.Queryable().Where(x => x.No == eMealVoucherDetail.No).SingleAsync();
+
+ var ts = DateTimeOffset.Now.ToUnixTimeMilliseconds().ToString().Reverse().Join(string.Empty);
+ var originData = string.Concat(eMealVoucher.No, ":", ts, ":", eMealVoucher.Code, ":", eMealVoucherDetail.IdNo);
+
+ var qrCode = "02" + originData.ToArray().Select(x =>
+ {
+ var b = Encoding.ASCII.GetBytes(x.ToString());
+ int i = b[0];
+ return Convert.ToString(i);
+ }).Join(string.Empty);
+
+
+ await _dbContext.InsertAsync(new EMealVoucherDetailQrCodeEntity
+ {
+ Signature = signature,
+ OriginData = originData,
+ QrCode = qrCode
+ });
+
+ return qrCode;
+ }
+}
\ No newline at end of file
diff --git a/Src/Dlhis.Canteen/MealSupplementRecord/Services/Implements/MealSupplementRecordService.cs b/Src/Dlhis.Canteen/MealSupplementRecord/Services/Implements/MealSupplementRecordService.cs
index 70e94176..6bb57af2 100644
--- a/Src/Dlhis.Canteen/MealSupplementRecord/Services/Implements/MealSupplementRecordService.cs
+++ b/Src/Dlhis.Canteen/MealSupplementRecord/Services/Implements/MealSupplementRecordService.cs
@@ -174,7 +174,6 @@ public class MealSupplementRecordService : IMealSupplementRecordService
Source = BalanceRecordSourceEnumType.LocalTenantSubsidies,
Remark = "期初导入",
CostId = gainfo.CostId,
- SubsidiesFromMonth = today.ToString("yyyyMM"),
OriginTenantId = gainfo.Factory,
Werks = gainfo.Werks,
Name = gainfo.Name,
diff --git a/Src/Dlhis.Canteen/Order/Dto/OrderPreCheckDto.cs b/Src/Dlhis.Canteen/Order/Dto/OrderPreCheckDto.cs
new file mode 100644
index 00000000..6667755e
--- /dev/null
+++ b/Src/Dlhis.Canteen/Order/Dto/OrderPreCheckDto.cs
@@ -0,0 +1,16 @@
+using Dlhis.Entity.Basic.Entity;
+
+namespace Dlhis.Canteen.Order.Dto
+{
+ public class OrderPreCheckDto
+ {
+ public DateTime CostDate { get; set; }
+ public BusinessShiftMealRelationshipEntity Shift { get; set; }
+
+ public string BusinessPlaceTenantId { 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/Services/Implements/BalanceService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs
index cd1cc6a6..a83c3107 100644
--- a/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs
+++ b/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs
@@ -482,7 +482,6 @@ public class BalanceService : IBalanceService
ShiftType = null,
CostDate = DateTime.Now.Date,
DeviceUniqueNo = null,
- SubsidiesFromMonth = DateTime.Now.ToString("yyyyMM"),
IP = null,
CostId = gainfo.CostId,
OriginTenantId = gainfo.Factory,
diff --git a/Src/Dlhis.Canteen/Order/Services/Implements/BookingOrderService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/BookingOrderService.cs
index 17054b7b..afff04cd 100644
--- a/Src/Dlhis.Canteen/Order/Services/Implements/BookingOrderService.cs
+++ b/Src/Dlhis.Canteen/Order/Services/Implements/BookingOrderService.cs
@@ -71,7 +71,6 @@ public class BookingOrderService : BaseOrderService, IBookingOrderService
DeliveryAddress = request.DeliveryAddress,
No = SnowFlakeHelper.Instance.NextId().ToString(),
- SubsidiesFromMonth = now.ToString("yyyyMM"),
IsOffline = false,
IsCostCrossTenant = await GetSettingAsync(gainfo.No, businessPlace.TenantId, CanteenSettingItemEnumType.AllowCrossTenant),
diff --git a/Src/Dlhis.Canteen/Order/Services/Implements/HainingBalanceService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/HainingBalanceService.cs
index fd0e9fea..46ac4779 100644
--- a/Src/Dlhis.Canteen/Order/Services/Implements/HainingBalanceService.cs
+++ b/Src/Dlhis.Canteen/Order/Services/Implements/HainingBalanceService.cs
@@ -75,7 +75,6 @@ public class HainingBalanceService : IHainingBalanceService
Source = request.Source,
Remark = request.Remark,
CostId = gainfo.CostId,
- SubsidiesFromMonth = request.CostDate.ToString("yyyyMM"),
TenantId = businessPlaceInfo?.TenantId ?? request.Factory,
OriginTenantId = gainfo.Factory,
Werks = gainfo.Werks,
diff --git a/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs
index 8edc15fa..e94823f6 100644
--- a/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs
+++ b/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs
@@ -1,4 +1,5 @@
using System.Diagnostics;
+using Dlhis.Canteen.Order.Dto;
using Dlhis.Canteen.Setting.Services;
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Canteen.Entities;
@@ -161,7 +162,11 @@ public class OrderService : BaseOrderService, IOrderService
}
else if (request.UserUniqueNo.StartsWith("02"))
{
- request.Source = BalanceRecordSourceEnumType.EVoucher;
+ request.Source = BalanceRecordSourceEnumType.EMealVoucher;
+ 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();
}
}
else
@@ -180,8 +185,7 @@ public class OrderService : BaseOrderService, IOrderService
{
gainfo.ThrowIfNull("无法识别,无权消费");
}
-
- if (request.IsOffline)
+ else
{
//兼容hpt尚未删除人员后产生的消费数据
gainfo ??= await DbContext.Queryable().AS("GainfoLeave")
@@ -305,7 +309,6 @@ public class OrderService : BaseOrderService, IOrderService
_logs.Add("消费请求校验:" + _sw.ElapsedMilliseconds);
_sw.Restart();
-
await CommonHelper.RetryAsync(async () =>
{
var gainfoMeal = await DbContext.Queryable().Where(x => x.UserNo == order.UserNo).SingleAsync();
@@ -329,42 +332,6 @@ public class OrderService : BaseOrderService, IOrderService
_sw.Restart();
}
- ///
- /// 根据二维码获取消费用户
- ///
- ///
- ///
- private async Task GetUserByQrCodeAsync(string qrCode)
- {
- 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();
- }
-
-
- ///
- /// 检查是否可以离线消费
- ///
- ///
- ///
- private 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;
- }
-
///
/// 离线消费处理
///
@@ -418,7 +385,6 @@ public class OrderService : BaseOrderService, IOrderService
DeviceUniqueNo = request.DeviceUniqueNo,
No = SnowFlakeHelper.Instance.NextId().ToString(),
- SubsidiesFromMonth = request.CostDateTime.ToString("yyyyMM"),
IsOffline = request.IsOffline,
IsCostCrossTenant = businessPlaceInfo!.TenantId != gainfo.Factory,
@@ -492,8 +458,6 @@ public class OrderService : BaseOrderService, IOrderService
device.ThrowIfNull("设备尚未登记");
device.Enable.ThrowIfFalse("设备已被停用");
- (request.Source == BalanceRecordSourceEnumType.QrCode && device.TenantId != gainfo.Factory).ThrowIfTrue("二维码在此设备禁止消费");
-
var businessPlaceInfo = await DbContext.Queryable()
.LeftJoin((x, sr) => x.Code == sr.PlaceCode)
.Where(x => x.Code == registry.BusinessPlaceCode)
@@ -523,6 +487,8 @@ public class OrderService : BaseOrderService, IOrderService
var costDate = await GetCostDateAsync(shift, request.CostDateTime);
+ (request.Source == BalanceRecordSourceEnumType.QrCode && businessPlaceInfo.TenantId != gainfo.Factory).ThrowIfTrue("禁止跨厂区消费");
+
var order = new CanteenOrderEntity
{
RequestJson = request.ToJson(),
@@ -543,9 +509,8 @@ public class OrderService : BaseOrderService, IOrderService
DeviceUniqueNo = request.DeviceUniqueNo,
No = SnowFlakeHelper.Instance.NextId().ToString(),
- SubsidiesFromMonth = request.CostDateTime.ToString("yyyyMM"),
IsOffline = request.IsOffline,
- IsCostCrossTenant = await GetSettingAsync(gainfo.No, businessPlaceInfo!.TenantId, CanteenSettingItemEnumType.AllowCrossTenant),
+ IsCostCrossTenant = gainfo.Factory != businessPlaceInfo!.TenantId,
OriginTenantId = gainfo.Factory,
Werks = gainfo.Werks,
@@ -610,17 +575,29 @@ public class OrderService : BaseOrderService, IOrderService
///
private async Task ToDoAsync(OrderAddRequest request, GainfoEntity gainfo)
{
- var message = "";
- if (request.IsOffline)
- {
- if (request.ThirdNo.IsPresent())
- {
- var redisCache = _serviceProvider.GetService();
+ var message = string.Empty;
- await using var handler = await redisCache.LockAsync($"consume:device:{request.ThirdNo}");
- if (handler.Acquired)
+ if (request.Source == BalanceRecordSourceEnumType.EMealVoucher)
+ {
+ try
+ {
+ await EMealVoucherConsumeAsync(request.Adapt(), gainfo);
+ }
+ catch (Exception ex)
+ {
+ message = ex.Message;
+ }
+ }
+ else
+ {
+ if (request.IsOffline)
+ {
+ if (request.ThirdNo.IsPresent())
{
- if (await ToCheckAbleOfflineConsumeAsync(request.ThirdNo))
+ var redisCache = _serviceProvider.GetService();
+
+ await using var handler = await redisCache.LockAsync($"consume:device:{request.ThirdNo}");
+ if (handler.Acquired && await ToCheckAbleOfflineConsumeAsync(request.ThirdNo))
{
try
{
@@ -632,32 +609,219 @@ public class OrderService : BaseOrderService, IOrderService
}
}
}
+ else
+ {
+ 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);
+ (await DbContext.Queryable().Where(x => x.UserCode == gainfo.Code).AnyAsync()).ThrowIfFalse("就餐余额不足");
+ await ConsumeAsync(request.Adapt(), gainfo);
}
catch (Exception ex)
{
- Logger.LogError(ex, "离线入库错误: 订单号:{no} .{message}", request.ThirdNo, ex.Message);
+ message = 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;
- }
- }
return 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;
+ }
+ }
+
+ ///
+ /// 根据二维码获取消费用户
+ ///
+ ///
+ ///
+ private async Task GetUserByQrCodeAsync(string qrCode)
+ {
+ 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();
+ }
+
+
+ private async Task EMealVoucherConsumeAsync(ConsumeRequest request, GainfoEntity gainfo)
+ {
+ var eMealVoucherQrCode = await DbContext.Queryable().Where(x => x.QrCode == request.UserUniqueNo).SingleAsync();
+ var originData = eMealVoucherQrCode.OriginData.Split(':');
+
+ var no = originData[0];
+ var idNo = originData[3];
+ var nowDate = DateTime.Now.Date;
+ var current = await DbContext.Queryable()
+ .Where(x => x.No == no && x.IdNo == idNo).SingleAsync();
+ (nowDate >= current.Start && nowDate <= current.End).ThrowIfFalse("餐券已过期");
+
+ var time = DateTimeOffset.FromUnixTimeMilliseconds(Convert.ToInt64(originData[1].Reverse().Join(string.Empty))).ToLocalTime().DateTime;
+ (time.AddMinutes(60) >= DateTime.Now).ThrowIfFalse("二维码已过期");
+
+ var eMealVoucher = await DbContext.Queryable().Where(x => x.No == no).SingleAsync();
+
+ var checkData = await PreCheckAsync(request.DeviceUniqueNo, request.CostDateTime);
+ eMealVoucher.Factories.Contains(checkData.BusinessPlaceTenantId).ThrowIfFalse("禁止在当前厂区消费");
+
+ _logs.Add("检验设备和营业点参数:" + _sw.ElapsedMilliseconds);
+ _sw.Restart();
+
+ var order = new CanteenOrderEntity
+ {
+ RequestJson = request.ToJson(),
+ ThirdNo = request.ThirdNo,
+ Type = BalanceRecordTypeEnumType.Out,
+ ApplicationType = CanteenApplicationTypeEnumType.Device,
+ Name = gainfo.Name,
+ Source = request.Source,
+ UserNo = eMealVoucherQrCode.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,
+
+ 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);
+ _logs.Add("临时order写入:" + _sw.ElapsedMilliseconds);
+ _sw.Restart();
+
+ try
+ {
+ await CommonHelper.RetryAsync(async () =>
+ {
+ 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()
+ .SetColumns(x => x.Balance == detail.Balance)
+ .SetColumns(x => x.Version == x.Version + 1)
+ .SetColumns(x => x.ModifiedTime == DateTime.Now)
+ .Where(x => x.No == no && x.IdNo == idNo && x.Version == detail.Version) //必须与查询时的version一致
+ .ExecuteCommandThrowVersionAsync();
+
+ //写入记录
+ var balanceRecord = order.Adapt();
+ await DbContext.InsertAsync(balanceRecord);
+ }, 5);
+
+ _logs.Add("消费写入:" + _sw.ElapsedMilliseconds);
+ _sw.Restart();
+
+ await DbContext.Updateable()
+ .SetColumns(x => x.OrderStatus == CanteenOrderStatusEnumType.Success)
+ .SetColumns(x => x.ModifiedTime == DateTime.Now)
+ .Where(x => x.Id == orderId)
+ .ExecuteCommandAsync();
+
+ _logs.Add("更新image和订单信息:" + _sw.ElapsedMilliseconds);
+ _sw.Restart();
+ }
+ 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;
+ }
+ }
+
+ ///
+ /// 消费前预检
+ ///
+ ///
+ ///
+ ///
+ 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();
+ device.ThrowIfNull("设备尚未登记");
+ device.Enable.ThrowIfFalse("设备已被停用");
+
+ var businessPlaceInfo = await DbContext.Queryable()
+ .LeftJoin((x, sr) => x.Code == sr.PlaceCode)
+ .Where(x => x.Code == registry.BusinessPlaceCode)
+ .Select((x, sr) => new
+ {
+ x.Code,
+ sr.ShiftCode,
+ x.TenantId
+ })
+ .SingleAsync();
+
+ businessPlaceInfo.ThrowIfNull("设备尚未登记营业点");
+
+ //获取当前消费餐次
+ var shifts = await DbContext.Queryable().Where(x => x.ShiftCode == businessPlaceInfo.ShiftCode).ToListAsync();
+ shifts.ThrowIfEmpty("营业点餐次尚未设置");
+
+ var shift = shifts.FirstOrDefault(x => new TimePeriod
+ {
+ Start = x.Start,
+ End = x.End
+ }.IsPresent(costDateTime));
+ shift.ThrowIfNull("非就餐时间,无法消费");
+
+ var costDate = await GetCostDateAsync(shift, costDateTime);
+
+ return new OrderPreCheckDto
+ {
+ CostDate = costDate,
+ Shift = shift,
+ BusinessPlaceTenantId = businessPlaceInfo.TenantId,
+ BusinessPlaceCode = businessPlaceInfo.Code,
+ BusinessShiftCode = businessPlaceInfo.ShiftCode
+ };
}
}
\ No newline at end of file
diff --git a/Src/Dlhis.Entity/Canteen/Entities/BalanceRecordEntity.cs b/Src/Dlhis.Entity/Canteen/Entities/BalanceRecordEntity.cs
index b5874947..a3fdedda 100644
--- a/Src/Dlhis.Entity/Canteen/Entities/BalanceRecordEntity.cs
+++ b/Src/Dlhis.Entity/Canteen/Entities/BalanceRecordEntity.cs
@@ -91,12 +91,6 @@ public class BalanceRecordEntity : SugarBaseEntity, ISoftDeleted, ITenantId
[SugarColumn(ColumnDescription = "消费机器唯一信息", IsNullable = true)]
public string DeviceUniqueNo { get; set; }
- ///
- /// 餐补月份
- ///
- [SugarColumn(ColumnDescription = "餐补月份")]
- public string SubsidiesFromMonth { get; set; }
-
///
/// Ip
///
diff --git a/Src/Dlhis.Entity/Canteen/Entities/EMealVoucherDetailEntity.cs b/Src/Dlhis.Entity/Canteen/Entities/EMealVoucherDetailEntity.cs
index b41f0533..35a963b2 100644
--- a/Src/Dlhis.Entity/Canteen/Entities/EMealVoucherDetailEntity.cs
+++ b/Src/Dlhis.Entity/Canteen/Entities/EMealVoucherDetailEntity.cs
@@ -1,4 +1,5 @@
-using Kurisu.AspNetCore.DataAccess.SqlSugar;
+using Kurisu.AspNetCore.DataAccess.Entity;
+using Kurisu.AspNetCore.DataAccess.SqlSugar;
using SqlSugar;
namespace Dlhis.Entity.Canteen.Entities;
@@ -7,7 +8,7 @@ namespace Dlhis.Entity.Canteen.Entities;
/// 电子餐券明细
///
[SugarTable(TableName = "EMealVoucherDetail", TableDescription = "电子餐券明细")]
-public class EMealVoucherDetailEntity : SugarBaseEntity
+public class EMealVoucherDetailEntity : SugarBaseEntity, IVersion
{
///
/// 主表id
@@ -18,7 +19,7 @@ public class EMealVoucherDetailEntity : SugarBaseEntity
///
/// 流水号
///
- [SugarColumn(ColumnDescription = "流水号")]
+ [SugarColumn(ColumnDescription = "流水号", UniqueGroupNameList = new[] { "idx_EMealVoucherDetail_no_idno" })]
public string No { get; set; }
///
@@ -30,7 +31,7 @@ public class EMealVoucherDetailEntity : SugarBaseEntity
///
/// 身份证
///
- [SugarColumn(ColumnDescription = "序号")]
+ [SugarColumn(ColumnDescription = "证件号", UniqueGroupNameList = new[] { "idx_EMealVoucherDetail_no_idno" })]
public string IdNo { get; set; }
///
@@ -60,6 +61,16 @@ public class EMealVoucherDetailEntity : SugarBaseEntity
///
/// 金额
///
- [SugarColumn(ColumnDescription = "金额", ColumnDataType = "decimal(16,2)")]
- public decimal Amount { get; set; }
+ [SugarColumn(ColumnDescription = "余额", ColumnDataType = "decimal(16,2)")]
+ public decimal Balance { get; set; }
+
+ ///
+ /// 版本
+ ///
+ [SugarColumn(ColumnDescription = "版本")]
+ public int Version { get; set; }
+
+
+ [SugarColumn(ColumnDataType = "varchar(255)")]
+ public string Signature { get; set; }
}
\ No newline at end of file
diff --git a/Src/Dlhis.Entity/Canteen/Entities/EMealVoucherDetailQrCodeEntity.cs b/Src/Dlhis.Entity/Canteen/Entities/EMealVoucherDetailQrCodeEntity.cs
new file mode 100644
index 00000000..b1a6b505
--- /dev/null
+++ b/Src/Dlhis.Entity/Canteen/Entities/EMealVoucherDetailQrCodeEntity.cs
@@ -0,0 +1,29 @@
+using Kurisu.AspNetCore.DataAccess.SqlSugar;
+using SqlSugar;
+
+namespace Dlhis.Entity.Canteen.Entities;
+
+///
+/// 电子餐券明细QrCode
+///
+[SugarTable(TableName = "EMealVoucherDetailQrCode", TableDescription = "电子餐券明细QrCode")]
+public class EMealVoucherDetailQrCodeEntity : SugarBaseEntity
+{
+ ///
+ /// url签名
+ ///
+ [SugarColumn(ColumnDataType = "varchar(255)", IndexGroupNameList = new[] { "idx_EMealVoucherDetailQrCode_signature" })]
+ public string Signature { get; set; }
+
+ ///
+ /// 原始数据
+ ///
+ [SugarColumn(ColumnDescription = "原始数据")]
+ public string OriginData { get; set; }
+
+ ///
+ /// 二维码
+ ///
+ [SugarColumn(ColumnDescription = "二维码", UniqueGroupNameList = new[] { "idx_EMealVoucherDetailQrCode_qrcode" })]
+ public string QrCode { get; set; }
+}
\ No newline at end of file
diff --git a/Src/Dlhis.Entity/Canteen/Enums/BalanceRecordSourceEnumType.cs b/Src/Dlhis.Entity/Canteen/Enums/BalanceRecordSourceEnumType.cs
index 587169b8..79d86f43 100644
--- a/Src/Dlhis.Entity/Canteen/Enums/BalanceRecordSourceEnumType.cs
+++ b/Src/Dlhis.Entity/Canteen/Enums/BalanceRecordSourceEnumType.cs
@@ -36,7 +36,7 @@ public enum BalanceRecordSourceEnumType
/// 电子餐券
///
[Description("电子餐券")]
- EVoucher,
+ EMealVoucher,
//*********************************************************************************************************
///
diff --git a/Src/Dlhis.External/EMealVoucher/Request/EMealVoucherReceiveRequest.cs b/Src/Dlhis.External/EMealVoucher/Request/EMealVoucherReceiveRequest.cs
index 86f3aa3f..76597552 100644
--- a/Src/Dlhis.External/EMealVoucher/Request/EMealVoucherReceiveRequest.cs
+++ b/Src/Dlhis.External/EMealVoucher/Request/EMealVoucherReceiveRequest.cs
@@ -82,6 +82,11 @@ public class EMealVoucherReceiveRequest : IValidatableObject
{
yield return new ValidationResult("来宾数量不匹配", new[] { nameof(Number) });
}
+
+ if (Details.DistinctBy(x => x.IdNo).Count() != Details.Count)
+ {
+ yield return new ValidationResult("证件信息重复", new[] { "IdNo" });
+ }
}
}
diff --git a/Src/Dlhis.External/EMealVoucher/Services/Implements/EMealVoucherService.cs b/Src/Dlhis.External/EMealVoucher/Services/Implements/EMealVoucherService.cs
index 9e12372a..a956a612 100644
--- a/Src/Dlhis.External/EMealVoucher/Services/Implements/EMealVoucherService.cs
+++ b/Src/Dlhis.External/EMealVoucher/Services/Implements/EMealVoucherService.cs
@@ -43,7 +43,7 @@ public class EMealVoucherService : IEMealVoucherService
Phone = x.Phone,
Start = x.Start,
End = x.End,
- Amount = x.Amount
+ Balance = x.Amount
}).ToList();
var id = await _dbContext.InsertReturnIdentityAsync(voucher);
diff --git a/Src/Dlhis.Middle/Services/Implements/OrderService.cs b/Src/Dlhis.Middle/Services/Implements/OrderService.cs
index 98126066..a632b1c5 100644
--- a/Src/Dlhis.Middle/Services/Implements/OrderService.cs
+++ b/Src/Dlhis.Middle/Services/Implements/OrderService.cs
@@ -171,7 +171,6 @@ public class OrderService : IOrderService
DeviceUniqueNo = request.DeviceUniqueNo,
No = _factory + "-" + SnowFlakeHelper.Instance.NextId(),
- SubsidiesFromMonth = request.CostDateTime.ToString("yyyyMM"),
// IsOffline = request.IsOffline,
IsCostCrossTenant = await GetSettingAsync(businessPlaceInfo!.TenantId, CanteenSettingItemEnumType.AllowCrossTenant),
diff --git a/Src/Dlhis.ScheduledTask/Jobs/MealSupplementHandlerJob.cs b/Src/Dlhis.ScheduledTask/Jobs/MealSupplementHandlerJob.cs
index 0f288bd5..1a3698ca 100644
--- a/Src/Dlhis.ScheduledTask/Jobs/MealSupplementHandlerJob.cs
+++ b/Src/Dlhis.ScheduledTask/Jobs/MealSupplementHandlerJob.cs
@@ -156,7 +156,6 @@ public class MealSupplementHandlerJob : BaseJob
Source = BalanceRecordSourceEnumType.LocalTenantSubsidies,
Remark = mealSupplement.UniqueId + ":" + mealSupplement.Remark,
CostId = gainfo.CostId,
- SubsidiesFromMonth = mealSupplement.Date.ToString("yyyyMM"),
OriginTenantId = gainfo.Factory,
Werks = gainfo.Werks,
Name = gainfo.Name,
diff --git a/Src/Gateway/Program.cs b/Src/Gateway/Program.cs
index e96a2582..c8fd2e8b 100644
--- a/Src/Gateway/Program.cs
+++ b/Src/Gateway/Program.cs
@@ -1,4 +1,3 @@
-
using Gateway;
public class Program
@@ -8,13 +7,14 @@ public class Program
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, builder) =>
{
- builder.AddJsonFile("ocelot.json", true, true);
+ if (context.HostingEnvironment.IsProduction())
+ {
+ builder.AddJsonFile("ocelot.json", true, true);
+ }
+
builder.AddJsonFile($"ocelot.{context.HostingEnvironment.EnvironmentName}.json", true, true);
})
- .ConfigureWebHostDefaults(webBuilder =>
- {
- webBuilder.UseStartup();
- })
+ .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); })
.Build().Run();
}
-}
+}
\ No newline at end of file
diff --git a/Src/Gateway/appsettings.json b/Src/Gateway/appsettings.json
index 508d6f2a..d0e733e1 100644
--- a/Src/Gateway/appsettings.json
+++ b/Src/Gateway/appsettings.json
@@ -11,7 +11,6 @@
"Common",
"Canteen",
"Scheduled",
- "User",
"Sys",
"Visitor",
"Message"
diff --git a/Src/Gateway/ocelot.Development.json b/Src/Gateway/ocelot.Development.json
index e1aaacf0..45f461c0 100644
--- a/Src/Gateway/ocelot.Development.json
+++ b/Src/Gateway/ocelot.Development.json
@@ -72,7 +72,7 @@
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
- "Host": "localhost",
+ "Host": "192.168.199.115",
"Port": 6000
}
]