diff --git a/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs b/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs index 67503ab2..a97ba073 100644 --- a/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs +++ b/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs @@ -19,10 +19,12 @@ namespace Dlhis.Canteen.Order.Controllers; public class BalancesController : ControllerBase { private readonly IBalanceService _balanceService; + private readonly IHainingBalanceService _hainingBalanceService; - public BalancesController(IBalanceService balanceService) + public BalancesController(IBalanceService balanceService, IHainingBalanceService hainingBalanceService) { _balanceService = balanceService; + _hainingBalanceService = hainingBalanceService; } /// @@ -47,6 +49,17 @@ public class BalancesController : ControllerBase await _balanceService.SupplementBalanceAsync(request); } + /// + /// 退款 + /// + /// + [HttpPost("refund")] + [Transactional(IsolationLevel.ReadCommitted)] + public async Task RefundAsync(BalanceRecordRefundRequest request) + { + return await _balanceService.RefundAsync(request.No); + } + /// /// 营业点消费明细 @@ -135,7 +148,7 @@ public class BalancesController : ControllerBase [HttpPost("haining/import")] public async Task Import(BalanceHainingImportRequest request) { - await _balanceService.HainingImportAsync(request); + await _hainingBalanceService.HainingImportAsync(request); } /// @@ -147,7 +160,7 @@ public class BalancesController : ControllerBase [HttpGet("haining/list")] public async Task> HainingDownloadAsync([Required(ErrorMessage = "工号不能为空")] string userCode) { - return await _balanceService.HainingDownloadAsync(userCode); + return await _hainingBalanceService.HainingDownloadAsync(userCode); } /// @@ -174,6 +187,6 @@ public class BalancesController : ControllerBase [Transactional(IsolationLevel.ReadCommitted)] public async Task ReCalcAsync(BalanceReCalcRequest request) { - await _balanceService.ReCalcAsync(request); + await _balanceService.ReCalcAsync(request.UserCode); } } \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Request/BalanceRecordRefundRequest.cs b/Src/Dlhis.Canteen/Order/Request/BalanceRecordRefundRequest.cs new file mode 100644 index 00000000..d80f569d --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Request/BalanceRecordRefundRequest.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Canteen.Order.Request; + +public class BalanceRecordRefundRequest +{ + /// + /// no + /// + [Required(ErrorMessage = "消费流水号不能为空")] + public string No { get; set; } +} \ 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 34cfce93..32dea145 100644 --- a/Src/Dlhis.Canteen/Order/Services/IBalanceService.cs +++ b/Src/Dlhis.Canteen/Order/Services/IBalanceService.cs @@ -1,5 +1,4 @@ -using Dlhis.Canteen.Dishes.Request; -using Dlhis.Canteen.Order.Request; +using Dlhis.Canteen.Order.Request; using Dlhis.Canteen.Order.Response; using Kurisu.AspNetCore.DataAccess.Dto; @@ -15,6 +14,13 @@ public interface IBalanceService : IScopeDependency Task SupplementBalanceAsync(BalanceSupplementRequest request); + /// + /// 退款 + /// + /// + /// + Task RefundAsync(string no); + /// /// 营业点消费明细 /// @@ -29,11 +35,7 @@ public interface IBalanceService : IScopeDependency Task> AverageDailyAsync(AverageDailyRequest request); - Task HainingImportAsync(BalanceHainingImportRequest request); - - Task> HainingDownloadAsync(string userCode); - Task ManuallyImportBalanceAndReCalcAsync(List requests); - Task ReCalcAsync(BalanceReCalcRequest request); + Task ReCalcAsync(string userCode); } \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Order/Services/IHainingBalanceService.cs b/Src/Dlhis.Canteen/Order/Services/IHainingBalanceService.cs new file mode 100644 index 00000000..b693db8d --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Services/IHainingBalanceService.cs @@ -0,0 +1,11 @@ +using Dlhis.Canteen.Order.Request; +using Dlhis.Canteen.Order.Response; + +namespace Dlhis.Canteen.Order.Services; + +public interface IHainingBalanceService : IScopeDependency +{ + Task HainingImportAsync(BalanceHainingImportRequest request); + + Task> HainingDownloadAsync(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 6dc81b3d..d50287a8 100644 --- a/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs +++ b/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs @@ -3,13 +3,13 @@ using Dlhis.Canteen.Order.Request; using Dlhis.Canteen.Order.Response; using Dlhis.Canteen.Setting.Services; using Dlhis.Entity.Basic.Entity; -using Dlhis.Entity.Basic.Enums; using Dlhis.Entity.Canteen.Entities; using Dlhis.Entity.Canteen.Enums; using Dlhis.Entity.Device; using Dlhis.Entity.Device.Entities; using Dlhis.Entity.Device.Enums; using Dlhis.Sys.Api; +using Kurisu.AspNetCore; using Kurisu.AspNetCore.Authentication.Abstractions; using Kurisu.AspNetCore.CustomClass; using Kurisu.AspNetCore.DataAccess.Dto; @@ -108,6 +108,42 @@ public class BalanceService : IBalanceService return page; } + public async Task RefundAsync(string no) + { + var cache = App.GetServiceProvider().GetService(); + var balanceRecord = await _dbContext.Queryable().Where(x => x.No == no).SingleAsync(); + await using var handler = await cache.LockAsync($"balance:refund:{no}", TimeSpan.FromSeconds(9)); + if (handler.Acquired) + { + if (balanceRecord is { Status: BalanceRecordStatusEnumType.Valid, Type: BalanceRecordTypeEnumType.Out }) + { + balanceRecord.Status = BalanceRecordStatusEnumType.Obsolute; + await _dbContext.UpdateAsync(balanceRecord); + + if (balanceRecord.Source is BalanceRecordSourceEnumType.Face or BalanceRecordSourceEnumType.QrCode) + { + await ReCalcAsync(balanceRecord.UserCode); + } + } + } + + var userNo = balanceRecord?.UserNo; + //获取人员余额 + return await _dbContext.Queryable() + .LeftJoin((x, ga) => x.UserNo == ga.No) + .LeftJoin((x, ga, department) => ga.DepartmentNo == department.No) + .Where(x => x.UserNo == userNo) + .Select((x, ga, department) => new OrderResultResponse + { + UserCode = ga.Code, + UserName = ga.Name, + DepartmentNo = ga.DepartmentNo, + DepartmentName = department.Name, + CurrentBalance = x.Balance + }) + .SingleAsync(); + } + public async Task> GetPageByDeviceUniqueNoAsync(BalanceRecordPageByDeviceUniqueNoRequest request) { @@ -413,114 +449,6 @@ public class BalanceService : IBalanceService .ToPageListAsync(request); } - public async Task HainingImportAsync(BalanceHainingImportRequest request) - { - var gainfo = await _dbContext.Queryable().Where(x => x.Code == request.UserCode).SingleAsync() - ?? await _dbContext.Queryable().Where(x => x.Code == request.UserCode).SingleAsync(); - - gainfo.ThrowIfNull($"工号{request.UserCode}不存在"); - var gainfoMeal = await _dbContext.Queryable().Where(x => x.UserCode == request.UserCode).SingleAsync(); - if (gainfoMeal == null) - { - gainfoMeal = new GainfoMealEntity - { - UserNo = gainfo.No, - UserCode = gainfo.Code, - Balance = 0, - BalanceOfRecharge = 0, - BalanceOfSubsidies = 0, - Version = 0 - }; - - await _dbContext.InsertAsync(gainfoMeal); - } - - var businessPlaceInfo = await _dbContext.Queryable() - .LeftJoin((a, b) => a.Code == b.PlaceCode) - .LeftJoin((a, b, c) => b.ShiftCode == c.Code) - .Where((a, b, c) => a.CustomCode == request.BusinessPlaceCode) - .Select((a, b, c) => new - { - a.Code, - ShiftCode = c.Code, - a.TenantId - }) - .SingleAsync(); - - if (request.Type == BalanceRecordTypeEnumType.Out) - { - businessPlaceInfo.ThrowIfNull($"营业点{request.BusinessPlaceCode}不存在"); - } - - await _dbContext.InsertAsync(new BalanceRecordEntity - { - Amount = request.Amount, - ApplicationType = request.ApplicationType, - Type = request.Type, - BusinessPlaceCode = businessPlaceInfo?.Code, - BusinessShiftCode = businessPlaceInfo?.ShiftCode, - ShiftType = request.ShiftType, - CostDate = request.CostDate, - CostDateTime = request.CostDateTime, - UserNo = gainfo.No, - No = request.No, - UserCode = gainfo.Code, - Status = BalanceRecordStatusEnumType.Valid, - 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, - Name = gainfo.Name, - IsCostCrossTenant = false, - DepartmentNo = gainfo.DepartmentNo, - DeviceUniqueNo = request.DeviceUniqueNo, - - IsDeleted = false, - }); - - var newBalance = gainfoMeal.Balance += request.Amount; - - await _dbContext.Updateable() - .SetColumns(x => x.Balance == newBalance) - .SetColumns(x => x.ModifiedBy == 1) - .SetColumns(x => x.ModifiedTime == DateTime.Now) - .SetColumns(x => x.Version == x.Version + 1) - .Where(x => x.UserCode == request.UserCode && x.Version == gainfoMeal.Version) - .ExecuteCommandThrowVersionAsync("写入失败,正在消费中"); - } - - /// - public async Task> HainingDownloadAsync(string userCode) - { - return await _dbContext.Queryable() - .LeftJoin((record, ga) => record.UserNo == ga.No) - .LeftJoin((record, ga, department) => ga.DepartmentNo == department.No) - .LeftJoin((record, ga, department, bPlace) => record.BusinessPlaceCode == bPlace.Code) - .Where(record => record.Status == BalanceRecordStatusEnumType.Valid) - .Where((record, ga) => ga.Code == userCode) - .OrderBy(record => record.CreateTime, OrderByType.Desc) - .Select((record, ga, department, bPlace) => new BalanceHainingDownloadResponse - { - No = record.No, - Factory = record.TenantId, - OriginFactory = record.OriginTenantId, - Werks = record.Werks, - DepartmentNo = record.DepartmentNo, - DepartmentName = department.Name, - CostDate = record.CostDate, - CostDateTime = record.CostDateTime, - Remark = record.Remark, - Amount = SqlFunc.Abs(record.Amount), - Type = record.Type, - Source = record.Source, - BusinessPlaceCode = bPlace.CustomCode, - DeviceUniqueNo = record.DeviceUniqueNo - }) - .ToListAsync(); - } /// public async Task ManuallyImportBalanceAndReCalcAsync(List requests) @@ -600,11 +528,11 @@ public class BalanceService : IBalanceService } - public async Task ReCalcAsync(BalanceReCalcRequest request) + public async Task ReCalcAsync(string userCode) { await CommonHelper.RetryAsync(async () => { - var gainfo = await _dbContext.Queryable().Where(x => x.Code == request.UserCode).SingleAsync(); + var gainfo = await _dbContext.Queryable().Where(x => x.Code == userCode).SingleAsync(); gainfo.ThrowIfNull("档案不存在"); var gainfoMeal = await _dbContext.Queryable().Where(x => x.UserCode == gainfo.Code).FirstAsync(); diff --git a/Src/Dlhis.Canteen/Order/Services/Implements/HainingBalanceService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/HainingBalanceService.cs new file mode 100644 index 00000000..fd0e9fea --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Services/Implements/HainingBalanceService.cs @@ -0,0 +1,129 @@ +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.DataAccess.SqlSugar.Extensions; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; +using Kurisu.AspNetCore.Utils.Extensions; +using SqlSugar; + +namespace Dlhis.Canteen.Order.Services.Implements; + +public class HainingBalanceService : IHainingBalanceService +{ + private readonly IDbContext _dbContext; + + public HainingBalanceService(IDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task HainingImportAsync(BalanceHainingImportRequest request) + { + var gainfo = await _dbContext.Queryable().Where(x => x.Code == request.UserCode).SingleAsync() + ?? await _dbContext.Queryable().Where(x => x.Code == request.UserCode).SingleAsync(); + + gainfo.ThrowIfNull($"工号{request.UserCode}不存在"); + var gainfoMeal = await _dbContext.Queryable().Where(x => x.UserCode == request.UserCode).SingleAsync(); + if (gainfoMeal == null) + { + gainfoMeal = new GainfoMealEntity + { + UserNo = gainfo.No, + UserCode = gainfo.Code, + Balance = 0, + BalanceOfRecharge = 0, + BalanceOfSubsidies = 0, + Version = 0 + }; + + await _dbContext.InsertAsync(gainfoMeal); + } + + var businessPlaceInfo = await _dbContext.Queryable() + .LeftJoin((a, b) => a.Code == b.PlaceCode) + .LeftJoin((a, b, c) => b.ShiftCode == c.Code) + .Where((a, b, c) => a.CustomCode == request.BusinessPlaceCode) + .Select((a, b, c) => new + { + a.Code, + ShiftCode = c.Code, + a.TenantId + }) + .SingleAsync(); + + if (request.Type == BalanceRecordTypeEnumType.Out) + { + businessPlaceInfo.ThrowIfNull($"营业点{request.BusinessPlaceCode}不存在"); + } + + await _dbContext.InsertAsync(new BalanceRecordEntity + { + Amount = request.Amount, + ApplicationType = request.ApplicationType, + Type = request.Type, + BusinessPlaceCode = businessPlaceInfo?.Code, + BusinessShiftCode = businessPlaceInfo?.ShiftCode, + ShiftType = request.ShiftType, + CostDate = request.CostDate, + CostDateTime = request.CostDateTime, + UserNo = gainfo.No, + No = request.No, + UserCode = gainfo.Code, + Status = BalanceRecordStatusEnumType.Valid, + 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, + Name = gainfo.Name, + IsCostCrossTenant = false, + DepartmentNo = gainfo.DepartmentNo, + DeviceUniqueNo = request.DeviceUniqueNo, + + IsDeleted = false, + }); + + var newBalance = gainfoMeal.Balance += request.Amount; + + await _dbContext.Updateable() + .SetColumns(x => x.Balance == newBalance) + .SetColumns(x => x.ModifiedBy == 1) + .SetColumns(x => x.ModifiedTime == DateTime.Now) + .SetColumns(x => x.Version == x.Version + 1) + .Where(x => x.UserCode == request.UserCode && x.Version == gainfoMeal.Version) + .ExecuteCommandThrowVersionAsync("写入失败,正在消费中"); + } + + public async Task> HainingDownloadAsync(string userCode) + { + return await _dbContext.Queryable() + .LeftJoin((record, ga) => record.UserNo == ga.No) + .LeftJoin((record, ga, department) => ga.DepartmentNo == department.No) + .LeftJoin((record, ga, department, bPlace) => record.BusinessPlaceCode == bPlace.Code) + .Where(record => record.Status == BalanceRecordStatusEnumType.Valid) + .Where((record, ga) => ga.Code == userCode) + .OrderBy(record => record.CreateTime, OrderByType.Desc) + .Select((record, ga, department, bPlace) => new BalanceHainingDownloadResponse + { + No = record.No, + Factory = record.TenantId, + OriginFactory = record.OriginTenantId, + Werks = record.Werks, + DepartmentNo = record.DepartmentNo, + DepartmentName = department.Name, + CostDate = record.CostDate, + CostDateTime = record.CostDateTime, + Remark = record.Remark, + Amount = SqlFunc.Abs(record.Amount), + Type = record.Type, + Source = record.Source, + BusinessPlaceCode = bPlace.CustomCode, + DeviceUniqueNo = record.DeviceUniqueNo + }) + .ToListAsync(); + } +} \ No newline at end of file diff --git a/Src/Dlhis.Sys/Identity/Controllers/UsersController.cs b/Src/Dlhis.Sys/Identity/Controllers/UsersController.cs index 5232aa00..119c0983 100644 --- a/Src/Dlhis.Sys/Identity/Controllers/UsersController.cs +++ b/Src/Dlhis.Sys/Identity/Controllers/UsersController.cs @@ -103,6 +103,7 @@ public class UsersController : ControllerBase /// 密码重置 /// /// + [Authorize(Roles = "admin")] [HttpPost("password/reset")] public async Task ResetPasswordAsync(UserResetPasswordRequest request) {