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.UnifyResultAndValidation; using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Core.DataAccess.Dto; using Kurisu.Core.User.Abstractions; using Kurisu.SqlSugar.Attributes; using Kurisu.SqlSugar.Extensions; using Kurisu.SqlSugar.Services; using Mapster; using Microsoft.AspNetCore.Mvc; using SqlSugar; using System.Linq; using System.Numerics; namespace Dlhis.Canteen.Order.Services.Implements; public class BalanceService : IBalanceService { private readonly IDbContext _dbContext; private readonly ICurrentUser _currentUser; private readonly ILogger _logger; public BalanceService(IDbContext dbContext, ICurrentUser currentUser, ILogger logger) { _dbContext = dbContext; _currentUser = currentUser; _logger = logger; } public async Task> GetPageAsync(BalanceRecordPageRequest request) { var page = 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) .LeftJoin((record, ga, department, bplace, bshift) => record.BusinessShiftCode == bshift.Code) .LeftJoin((record, ga, department, bplace, bshift, type) => record.BusinessShiftCode.ToString() == type.Code) .WhereIF(request.SearchDateTimePeriod?.Start.HasValue == true, record => record.CostDate >= request.SearchDateTimePeriod.Start) .WhereIF(request.SearchDateTimePeriod?.End.HasValue == true, record => record.CostDate <= request.SearchDateTimePeriod.End) .OrderBy(record => record.CreateTime, OrderByType.Desc) .Select((record, ga, department, bplace, bshift,type) => new BalanceRecordResponse { Id = record.Id, Amount = record.Amount, BusinessPlaceCode = record.BusinessPlaceCode, BusinessShiftCode = record.BusinessShiftCode, CostDate = record.CostDate, CostId = record.CostId, DepartmentNo = record.DepartmentNo, DeviceInfo = record.DeviceInfo, IP = record.IP, UserNo = record.UserNo, UserCode = record.UserCode, Type = record.Type, SubsidiesFromMonth = record.SubsidiesFromMonth, Status = record.Status, Source = record.Source, No = record.No, IsCostCrossTenant = record.IsCostCrossTenant, OriginTenantId = record.OriginTenantId, Name = record.Name, DepartmentName = department.Name, BusinessPlaceName = bplace.Name, BusinessShiftName = type.Display, CostDateTime = record.CreateTime // todo }) .ToPageListAsync(request); return page; } /// /// 根据成本中心 /// /// /// public async Task> RecordByCostIdAsync(BalanceRecordPageByCostIdRequest request) { var pageinfo = await _dbContext.Queryable() .LeftJoin((record, department) => record.DepartmentNo == department.No) .LeftJoin((record, department, bplace) => record.BusinessPlaceCode == bplace.Code) .LeftJoin((record, department, bplace, bshift) => record.BusinessShiftCode == bshift.Code) .LeftJoin((record, department, bplace, bshift,type) =>record.BusinessShiftCode.ToString() == type.Code) .Where(record => record.CostId == request.CostId) .OrderBy(record => record.CreateTime, OrderByType.Desc) .Select((record, department, bplace, bshift,type) => new BalanceRecordResponse { Id = record.Id, Amount = record.Amount, BusinessPlaceCode = record.BusinessPlaceCode, BusinessShiftCode = record.BusinessShiftCode, CostDate = record.CostDate, CostId = record.CostId, DepartmentNo = record.DepartmentNo, DeviceInfo = record.DeviceInfo, IP = record.IP, UserNo = record.UserNo, UserCode = record.UserCode, Type = record.Type, SubsidiesFromMonth = record.SubsidiesFromMonth, Status = record.Status, Source = record.Source, No = record.No, IsCostCrossTenant = record.IsCostCrossTenant, OriginTenantId = record.OriginTenantId, Name = record.Name, DepartmentName = department.Name, BusinessPlaceName = bplace.Name, BusinessShiftName = type.Display, CostDateTime = record.CreateTime // todo }) .ToPageListAsync(request); return pageinfo; } /// /// 消费明细 /// /// /// public async Task> RecordPageDetailsAsync(BalanceRecordPageDetailsRequest request) { var paeginfo = await _dbContext.Queryable() .LeftJoin((record, department) => record.DepartmentNo == department.No) .LeftJoin((record, department, bplace) => record.BusinessPlaceCode == bplace.Code) .LeftJoin((record, department, bplace, bshift) => record.BusinessShiftCode == bshift.Code) .LeftJoin((record, department, bplace, bshift, type) => record.BusinessShiftCode.ToString() == type.Code) .WhereIF(!request.Name.IsEmpty(), record => record.Name == request.Name) .WhereIF(!request.DepartmentNo.IsEmpty(), record => record.DepartmentNo == request.DepartmentNo) .WhereIF(!request.UserCode.IsEmpty(), record => record.UserCode == request.UserCode) .WhereIF(request.Type.HasValue, record => record.Type == request.Type) .WhereIF(request.BusinessShiftCode.HasValue, record => record.BusinessShiftCode == request.BusinessShiftCode) .WhereIF(request.BusinessPlaceCode.HasValue, record => record.BusinessPlaceCode == request.BusinessPlaceCode) .WhereIF(request.StartDatetime.HasValue, record => record.CostDateTime >= request.StartDatetime) .WhereIF(request.EndDatetime.HasValue, record => record.CostDateTime <= request.EndDatetime) .OrderBy(record => record.CreateTime, OrderByType.Desc) .Select((record, department, bplace, bshift,type) => new BalanceRecordResponse { Id = record.Id, Amount = record.Amount, BusinessPlaceCode = record.BusinessPlaceCode, BusinessShiftCode = record.BusinessShiftCode, CostDate = record.CostDate, CostId = record.CostId, DepartmentNo = record.DepartmentNo, DeviceInfo = record.DeviceInfo, IP = record.IP, UserNo = record.UserNo, UserCode = record.UserCode, Type = record.Type, SubsidiesFromMonth = record.SubsidiesFromMonth, Status = record.Status, Source = record.Source, No = record.No, IsCostCrossTenant = record.IsCostCrossTenant, OriginTenantId = record.OriginTenantId, Name = record.Name, DepartmentName = department.Name, BusinessPlaceName = bplace.Name, BusinessShiftName = type.Display, CostDateTime = record.CreateTime // todo }) .ToPageListAsync(request); return paeginfo; } /// /// 消费汇总 根据部门号 /// /// /// public async Task> BalanceRecordSummaryByNoAsync(BalanceRecordSummaryRequest request) { var shiftCodeDict = await GetShiftCode(); var paeginfos = await _dbContext.Queryable() .WhereIF(!request.DepartmentNo.IsEmpty(), record => record.DepartmentNo == request.DepartmentNo) .WhereIF(!request.UserCode.IsEmpty(), record => record.UserCode == request.UserCode) .WhereIF(!request.OriginTenantId.IsEmpty(), record => record.TenantId == request.OriginTenantId) .WhereIF(request.StartDatetime.HasValue, record => record.CostDateTime >= request.StartDatetime) .WhereIF(request.EndDatetime.HasValue, record => record.CostDateTime <= request.EndDatetime) .Where(record => record.CostId == request.CostId) .OrderBy(record => record.CreateTime, OrderByType.Desc) .ToListAsync(); var lst = new List(); foreach (var departmentGroup in paeginfos.GroupBy(record => record.DepartmentNo)) { // 每个部门 var obj = new BalanceRecordSummaryRespones { DepartmentNo = departmentGroup.Key, CostId = request.CostId }; // 每个餐次人数 obj.BreakfastNumberOfPeople = departmentGroup.Count(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Breakfast"]); obj.LunchNumberOfPeople = departmentGroup.Count(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Lunch"]); obj.DinnerNumberOfPeople = departmentGroup.Count(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Dinner"]); obj.LateNightNumberOfPeople = departmentGroup.Count(x => x.BusinessShiftCode.ToString() == shiftCodeDict["LateNight"]); //餐次金额 obj.BreakfastAmount = departmentGroup.Where(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Breakfast"]).Sum(x => x.Amount); obj.LunchAmount = departmentGroup.Where(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Lunch"]).Sum(x => x.Amount); obj.DinnerAmount = departmentGroup.Where(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Dinner"]).Sum(x => x.Amount); obj.LateNightAmount = departmentGroup.Where(x => x.BusinessShiftCode.ToString() == shiftCodeDict["LateNight"]).Sum(x => x.Amount); // 每个餐次单价 obj.BreakfastPrice = obj.BreakfastAmount / obj.BreakfastNumberOfPeople; obj.LunchPrice = obj.LunchAmount / obj.LunchNumberOfPeople; obj.DinnerPrice = obj.DinnerAmount / obj.DinnerNumberOfPeople; obj.LateNightPrice = obj.LateNightAmount / obj.LateNightNumberOfPeople; // 每个部门餐次中的 OriginTenantId obj.OriginTenantId = departmentGroup.FirstOrDefault()?.OriginTenantId; // 总人数 总金额 obj.TotalNumberOfPeople = obj.BreakfastNumberOfPeople + obj.LunchNumberOfPeople + obj.DinnerNumberOfPeople + obj.LateNightNumberOfPeople; obj.TotalAmount = obj.BreakfastAmount + obj.LunchAmount + obj.DinnerAmount + obj.LateNightAmount; lst.Add(obj); } return lst; } /// /// 消费汇总 根据人事范围 /// /// /// public async Task> BalanceRecordSummaryByTidAsync(BalanceRecordSummaryRequest request) { var shiftCodeDict = await GetShiftCode(); var paeginfos = await _dbContext.Queryable() .WhereIF(!request.DepartmentNo.IsEmpty(), record => record.DepartmentNo == request.DepartmentNo) .WhereIF(!request.UserCode.IsEmpty(), record => record.UserCode == request.UserCode) .WhereIF(!request.OriginTenantId.IsEmpty(), record => record.TenantId == request.OriginTenantId) .WhereIF(request.StartDatetime.HasValue, record => record.CostDateTime >= request.StartDatetime) .WhereIF(request.EndDatetime.HasValue, record => record.CostDateTime <= request.EndDatetime) .Where(record => record.CostId == request.CostId) .OrderBy(record => record.CreateTime, OrderByType.Desc) .ToListAsync(); var lst = new List(); foreach (var originTenantIdGroup in paeginfos.GroupBy(record => record.OriginTenantId)) { // 每个人事范围 var obj = new BalanceRecordSummaryRespones { OriginTenantId = originTenantIdGroup.Key, CostId = request.CostId, DepartmentNo = originTenantIdGroup.First().DepartmentNo }; // 每个餐次人数 obj.BreakfastNumberOfPeople = originTenantIdGroup.Count(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Breakfast"]); obj.LunchNumberOfPeople = originTenantIdGroup.Count(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Lunch"]); obj.DinnerNumberOfPeople = originTenantIdGroup.Count(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Dinner"]); obj.LateNightNumberOfPeople = originTenantIdGroup.Count(x => x.BusinessShiftCode.ToString() == shiftCodeDict["LateNight"]); //餐次金额 obj.BreakfastAmount = originTenantIdGroup.Where(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Breakfast"]).Sum(x => x.Amount); obj.LunchAmount = originTenantIdGroup.Where(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Lunch"]).Sum(x => x.Amount); obj.DinnerAmount = originTenantIdGroup.Where(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Dinner"]).Sum(x => x.Amount); obj.LateNightAmount = originTenantIdGroup.Where(x => x.BusinessShiftCode.ToString() == shiftCodeDict["LateNight"]).Sum(x => x.Amount); // 每个餐次单价 obj.BreakfastPrice = obj.BreakfastAmount / obj.BreakfastNumberOfPeople; obj.LunchPrice = obj.LunchAmount / obj.LunchNumberOfPeople; obj.DinnerPrice = obj.DinnerAmount / obj.DinnerNumberOfPeople; obj.LateNightPrice = obj.LateNightAmount / obj.LateNightNumberOfPeople; // 总人数 总金额 obj.TotalNumberOfPeople = obj.BreakfastNumberOfPeople + obj.LunchNumberOfPeople + obj.DinnerNumberOfPeople + obj.LateNightNumberOfPeople; obj.TotalAmount = obj.BreakfastAmount + obj.LunchAmount + obj.DinnerAmount + obj.LateNightAmount; lst.Add(obj); } return lst; } private async Task> GetShiftCode() { var list = await _dbContext.Queryable().ToListAsync(); var shiftCodeDict = new Dictionary(); var breakfastCode = list.FirstOrDefault(x => x.Display == "早餐")?.Code; var lunchCode = list.FirstOrDefault(x => x.Display == "午餐")?.Code; var dinnerCode = list.FirstOrDefault(x => x.Display == "晚餐")?.Code; var lateNightCode = list.FirstOrDefault(x => x.Display == "宵夜")?.Code; shiftCodeDict.Add("Breakfast", breakfastCode); shiftCodeDict.Add("Lunch", lunchCode); shiftCodeDict.Add("Dinner", dinnerCode); shiftCodeDict.Add("LateNight", lateNightCode); return shiftCodeDict; } /// /// 用餐分析 /// /// /// public async Task> BalanceRecordAnalysisAsync(BalanceRecordAnalysisRequest request) { var shiftCodeDict = await GetShiftCode(); var pageinfo = await _dbContext.Queryable() .WhereIF(request.StartDatetime.HasValue, record => record.CostDateTime >= request.StartDatetime) .WhereIF(request.EndDatetime.HasValue, record => record.CostDateTime <= request.EndDatetime) .WhereIF(request.BusinessPlaceCode.HasValue, record => record.BusinessPlaceCode == request.BusinessPlaceCode).ToListAsync(); var lst = new List(); foreach (var first in pageinfo.GroupBy(record => record.CostDateTime.ToString("yyyy-MM-dd"))) { var obj = new BalanceRecordAnalysisRespones() { CostDate = DateOnly.FromDateTime(Convert.ToDateTime(first.Key)), }; obj.BreakfastNumberOfPeople= first.Count(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Breakfast"]); obj.LunchNumberOfPeople = first.Count(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Lunch"]); obj.DinnerNumberOfPeople = first.Count(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Dinner"]); obj.LateNightNumberOfPeople = first.Count(x => x.BusinessShiftCode.ToString() == shiftCodeDict["LateNight"]); obj.TotalNumberOfPeople = obj.BreakfastNumberOfPeople + obj.LunchNumberOfPeople + obj.DinnerNumberOfPeople + obj.LateNightNumberOfPeople; lst.Add(obj); } return lst; } [IgnoreTenant] public async Task> GetPageByDeviceUniqueNoAsync(BalanceRecordPageByDeviceUniqueNoRequest request) { var now = DateTime.Now; var start = DateTime.Today; var end = new DateTime(now.Year, now.Month, now.Day, 23, 59, 59); if (request.SearchDate.HasValue) { now = request.SearchDate.Value; start = new DateTime(now.Year, now.Month, now.Day); end = new DateTime(now.Year, now.Month, now.Day, 23, 59, 59); } var device = await _dbContext.Queryable() .Where(x => x.UniqueNo == request.DeviceUniqueNo || x.Code == Guid.Parse(request.DeviceUniqueNo)) .FirstAsync(); if (device == null) { return new Pagination() { Data = new List() }; } var page = 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) .LeftJoin((record, ga, department, bplace, bshift) => record.BusinessShiftCode == bshift.Code) .LeftJoin((record, ga, department, bplace, bshift, type) => record.BusinessShiftCode.ToString() == type.Code) .WhereIF(request.SearchDate.HasValue, record => record.CostDate >= start) .WhereIF(request.SearchDate.HasValue, record => record.CostDate <= end) .Where(record => record.TenantId == device.TenantId || (record.DeviceInfo == device.Code.ToString() || record.DeviceInfo == device.UniqueNo)) .Where(record => record.Type == BalanceRecordTypeEnumType.Out && record.Status == BalanceRecordStatusEnumType.Valid) .OrderBy(record => record.CreateTime, OrderByType.Desc) .Select((record, ga, department, bplace, bshift,type) => new BalanceRecordResponse { Id = record.Id, Amount = record.Amount, BusinessPlaceCode = record.BusinessPlaceCode, BusinessShiftCode = record.BusinessShiftCode, CostDate = record.CostDate, CostId = record.CostId, DepartmentNo = record.DepartmentNo, DeviceInfo = record.DeviceInfo, IP = record.IP, UserNo = record.UserNo, UserCode = record.UserCode, Type = record.Type, SubsidiesFromMonth = record.SubsidiesFromMonth, Status = record.Status, Source = record.Source, No = record.No, IsCostCrossTenant = record.IsCostCrossTenant, OriginTenantId = record.OriginTenantId, Name = record.Name, DepartmentName = department.Name, BusinessPlaceName = bplace.Name, BusinessShiftName = type.Display, CostDateTime = record.CostDateTime }).ToPageListAsync(request); //消费总金额 var sum = await _dbContext.Queryable() .WhereIF(request.SearchDate.HasValue, record => record.CostDate >= start) .WhereIF(request.SearchDate.HasValue, record => record.CostDate <= end) .Where(record => record.TenantId == device.TenantId || (record.DeviceInfo == device.Code.ToString() || record.DeviceInfo == device.UniqueNo)) .Where(record => record.Type == BalanceRecordTypeEnumType.Out && record.Status == BalanceRecordStatusEnumType.Valid) .SumAsync(record => SqlFunc.Abs(SqlFunc.IsNull(record.Amount, 0))); page.Data.ForEach(x => x.Sum = sum); return page; } public async Task> GetPageByUserAsync(BalanceRecordUserPageRequest request) { //当前用户 var userId = _currentUser.GetIntSubjectId(); var page = 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) .LeftJoin((record, ga, department, bplace, bshift) => record.BusinessShiftCode == bshift.Code) .LeftJoin((record, ga, department, bplace, bshift,type) =>record.BusinessShiftCode.ToString() == type.Code) .WhereIF(request.SearchDateTimePeriod?.Start.HasValue == true, record => record.CostDate >= request.SearchDateTimePeriod.Start) .WhereIF(request.SearchDateTimePeriod?.End.HasValue == true, record => record.CostDate <= request.SearchDateTimePeriod.End) .Where((record, ga) => ga.IdentityUserId == userId) .OrderBy(record => record.CreateTime, OrderByType.Desc) .Select((record, ga, department, bplace, bshift,type) => new BalanceRecordUserResponse { Id = record.Id, Amount = record.Amount, Type = record.Type, Status = record.Status, Source = record.Source, No = record.No, BusinessPlaceName = bplace.Name, BusinessShiftName = type.Display, CostDateTime = record.CreateTime // todo }) .ToPageListAsync(request); return page; } [Transactional] public async Task ObsoluteAsync(BalanceRecordObsoluteRequest request) { var balanceRecord = await _dbContext.Queryable().Where(x => x.Id == request.Id).SingleAsync(); balanceRecord.ThrowIfNull("记录不存在"); (balanceRecord.Type != BalanceRecordTypeEnumType.Out).ThrowIfTrue("非消费记录,无法操作"); balanceRecord.Status = BalanceRecordStatusEnumType.Obsolute; balanceRecord.Remark = request.Reason; await ContinueUpdateAsync(async () => { //重算金额 var canteenUser = await _dbContext.Queryable().Where(x => x.UserNo == balanceRecord.UserNo).SingleAsync(); //金额 + canteenUser.Balance += Math.Abs(balanceRecord.Amount); var effect = await _dbContext.Updateable() .SetColumns(x => x.Balance == canteenUser.Balance) .SetColumns(x => x.Version == x.Version + 1) .Where(x => x.UserNo == balanceRecord.UserNo && x.Version == canteenUser.Version) .ExecuteCommandAsync(); if (effect <= 0) throw new VersionExceptions("更新版本冲突"); await _dbContext.UpdateAsync(balanceRecord); }, 5); } //************************************************************ help methods ****************************************************************** private async Task ContinueUpdateAsync(Func 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("操作失败,请重试"); } }