using Dlhis.Basic.Api; 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 Dlhis.Entity.Device; using Dlhis.Entity.Device.Entities; using Dlhis.Entity.Device.Enums; using Dlhis.Sys.Api; 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; using Kurisu.AspNetCore.Utils.Extensions; using Mapster; using SqlSugar; namespace Dlhis.Canteen.Order.Services.Implements; public class BalanceService : IBalanceService { private readonly IDbContext _dbContext; private readonly ICurrentUser _currentUser; private readonly ITenantApi _tenantApi; private readonly IBusinessPlacePermissionsApi _businessPlacePermissionsApi; public BalanceService(IDbContext dbContext, ICurrentUser currentUser, ITenantApi tenantApi, IBusinessPlacePermissionsApi businessPlacePermissionsApi) { _dbContext = dbContext; _currentUser = currentUser; _tenantApi = tenantApi; _businessPlacePermissionsApi = businessPlacePermissionsApi; } 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, order) => record.No == order.No) .WhereIF(request.SearchDateTimePeriod?.Start.HasValue == true, record => record.CostDate >= request.SearchDateTimePeriod.Start) .WhereIF(request.SearchDateTimePeriod?.End.HasValue == true, record => record.CostDate <= request.SearchDateTimePeriod.End) .WhereIF(request.ShiftTypes.IsPresent(), record => request.ShiftTypes.Contains(record.ShiftType)) .WhereIF(request.Type.HasValue, record => record.Type == request.Type) .WhereIF(request.Factory.IsPresent(), record => record.TenantId == request.Factory) .WhereIF(request.Search.IsPresent(), (record, ga) => ga.Name == request.Search || ga.Code == request.Search ) .OrderBy(record => record.CreateTime, OrderByType.Desc) .Select((record, ga, department, bPlace, order) => new BalanceRecordResponse { Id = record.Id, No = record.No, IsOffline = order.IsOffline, Amount = record.Amount, CostDate = record.CostDate, CostId = record.CostId, DepartmentNo = record.DepartmentNo, DeviceInfo = record.DeviceUniqueNo, UserNo = record.UserNo, UserCode = record.UserCode, Type = record.Type, Status = record.Status, Source = record.Source, OriginTenantId = record.OriginTenantId, Name = record.Name, DepartmentName = department.Name, BusinessPlaceName = bPlace.Name, CostDateTime = record.CostDateTime, Factory = record.TenantId, ShiftType = record.ShiftType, Remark = record.Remark, Werks = ga.Werks }) .ToPageListAsync(request); try { var tenantResult = await _tenantApi.GetTenantsInternalAsync(); var tenants = tenantResult.Data; page.Data.ForEach(item => { item.OriginTenantId = tenants.FirstOrDefault(x => x.Code == item.OriginTenantId)?.Name; item.Factory = tenants.FirstOrDefault(x => x.Code == item.Factory)?.Name; }); } catch (Exception) { //ignore } return page; } public async Task> GetPageByDeviceUniqueNoAsync(BalanceRecordPageByDeviceUniqueNoRequest request) { 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() }; } if (request.SearchDate.HasValue) { request.Start = request.SearchDate; request.End = request.SearchDate.Value.AddDays(1); } // var switchTime = await _canteenSettingService.GetBusinessSwitchTimeAsync(device.TenantId); // if (switchTime.HasValue) // { // var setTime = date.Add(switchTime.Value.ToTimeSpan()); // if (DateTime.Now < setTime) // date = date.AddDays(-1); // } 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, imageRecord) => imageRecord.No == record.No) .WhereIF(request.UserCode.IsPresent(), record => record.UserCode == request.UserCode) .Where(record => record.CostDate >= request.Start && record.CostDate <= request.End) .Where(record => record.DeviceUniqueNo == device.UniqueNo || record.DeviceUniqueNo == device.Code.ToString()) .Where(record => record.Type == BalanceRecordTypeEnumType.Out && record.Status == BalanceRecordStatusEnumType.Valid) .OrderBy(record => record.CreateTime, OrderByType.Desc) .Select((record, ga, department, bPlace, imageRecord) => new BalanceRecordDeviceResponse { Id = record.Id, Amount = SqlFunc.Abs(record.Amount), CostDate = record.CostDate, DepartmentNo = record.DepartmentNo, UserNo = record.UserNo, UserCode = record.UserCode, Type = record.Type, Status = record.Status, Source = record.Source, No = record.No, Name = record.Name, DepartmentName = department.Name, BusinessPlaceName = bPlace.Name, ShiftType = record.ShiftType, CostDateTime = record.CostDateTime, TenantId = record.TenantId, Image = imageRecord.Image }) .ToPageListAsync(request); //消费总金额 var sum = await _dbContext.Queryable() .Where(record => record.CostDate >= request.Start && record.CostDate <= request.End) .Where(record => record.DeviceUniqueNo == device.UniqueNo || record.DeviceUniqueNo == device.Code.ToString()) .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 RecordPageDetailsAsync(BalanceRecordPageDetailRequest request) { var myCodes = (await _businessPlacePermissionsApi.GetMyAsync()).Data.Select(x => (Guid?)x.Code).ToList(); if (request.BusinessplaceCodes.IsPresent()) { myCodes = myCodes.Where(x => request.BusinessplaceCodes.Contains(x)).ToList(); } var queryable = _dbContext.Queryable() .LeftJoin((record, department) => record.DepartmentNo == department.No) .LeftJoin((record, department, bPlace) => record.BusinessPlaceCode == bPlace.Code) .LeftJoin((record, department, bPlace, gainfo) => record.UserNo == gainfo.No) .Where((record) => record.Type == BalanceRecordTypeEnumType.Out && record.Status == BalanceRecordStatusEnumType.Valid) .Where(record => myCodes.Contains(record.BusinessPlaceCode)) .WhereIF(request.SearchDateTimePeriod?.Start.HasValue == true, record => record.CostDate >= request.SearchDateTimePeriod.Start) .WhereIF(request.SearchDateTimePeriod?.End.HasValue == true, record => record.CostDate <= request.SearchDateTimePeriod.End) .WhereIF(request.ShiftType.HasValue, record => record.ShiftType == request.ShiftType) .WhereIF(request.Search.IsPresent(), record => record.UserCode == request.Search || record.Name == request.Search); var page = await queryable .OrderBy(record => record.CostDateTime, OrderByType.Desc) .Select((record, department, bPlace, gainfo) => new BalanceRecordDetailPageResponse { Id = record.Id, No = record.No, CurrentAmount = SqlFunc.Abs(record.Amount), CostDate = record.CostDate, UserNo = record.UserNo, UserCode = record.UserCode, Source = record.Source, UserName = record.Name, DepartmentName = department.Name, BusinessPlaceName = bPlace.Name, CostDateTime = record.CostDateTime, ShiftType = record.ShiftType, Werks = gainfo.Werks, Factory = record.TenantId, Remark = record.Remark, }) .ToPageListAsync(request); try { var tenantResult = await _tenantApi.GetTenantsInternalAsync(); var tenants = tenantResult.Data; page.Data.ForEach(item => { item.Factory = tenants.FirstOrDefault(x => x.Code == item.Factory)?.Name; }); } catch (Exception) { //ignore } var amount = await queryable.SumAsync(record => SqlFunc.Abs(record.Amount)); var total = await queryable.GroupBy(record => record.UserNo).Select(record => record.UserNo).CountAsync(); return new BalanceRecordByBusinessPlaceResponse { Amount = amount, Total = total, Pagination = page, }; } public async Task SupplementBalanceAsync(BalanceSupplementRequest request) { var balanceInfo = await _dbContext.Queryable().Where(x => x.No == request.No).SingleAsync(); balanceInfo.ThrowIfNull("明细数据不存在"); (balanceInfo.Status != BalanceRecordStatusEnumType.Valid || balanceInfo.Type != BalanceRecordTypeEnumType.Out).ThrowIfTrue("消费明细状态有误"); balanceInfo.Status = BalanceRecordStatusEnumType.Obsolute; balanceInfo.Remark += request.Reason; await _dbContext.Client.Ado.BeginTranAsync(); try { await _dbContext.UpdateAsync(balanceInfo); //不为空则插入记录 if (request.Amount != 0) { var newBalanceRecord = balanceInfo.Adapt(); newBalanceRecord.Id = 0; newBalanceRecord.No = SnowFlakeHelper.Instance.NextId().ToString(); newBalanceRecord.Amount = request.Amount; newBalanceRecord.Remark = balanceInfo.No + ":" + request.Reason; newBalanceRecord.Status = BalanceRecordStatusEnumType.Valid; await _dbContext.InsertAsync(newBalanceRecord); } await _dbContext.Client.Ado.CommitTranAsync(); } catch (Exception) { await _dbContext.Client.Ado.RollbackTranAsync(); throw; } await CommonHelper.RetryAsync(async () => { var gainfoMeal = await _dbContext.Queryable().Where(x => x.UserCode == balanceInfo.UserCode).SingleAsync(); var balance = gainfoMeal.Balance + Math.Abs(balanceInfo.Amount) + request.Amount; await _dbContext.Updateable() .SetColumns(x => x.Balance == balance) .SetColumns(x => x.Version == x.Version + 1) .SetColumns(x => x.ModifiedTime == DateTime.Now) .Where(x => x.UserCode == balanceInfo.UserCode && x.Version == gainfoMeal.Version) .ExecuteCommandThrowVersionAsync(); }, 10); } public async Task GetPageByUserAsync(BalanceRecordUserPageRequest request) { var jinkoCode = _currentUser.GetUserClaim("jinko-code") ?? string.Empty; 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) .Where(record => record.Type == BalanceRecordTypeEnumType.Out) .Where(record => record.Status == BalanceRecordStatusEnumType.Valid) .WhereIF(request.SearchDateTimePeriod?.Start.HasValue == true, record => record.CostDate >= request.SearchDateTimePeriod.Start) .WhereIF(request.SearchDateTimePeriod?.End.HasValue == true, record => record.CostDate <= request.SearchDateTimePeriod.End) .WhereIF(request.ApplicationType.HasValue, record => record.ApplicationType == request.ApplicationType) .Where((record, ga) => ga.Code == jinkoCode) .OrderBy(record => record.CostDateTime) .Select((record, ga, department, bPlace) => new BalanceRecordUserPageResponse { Id = record.Id, CurrentAmount = SqlFunc.Abs(record.Amount), Source = record.Source, No = record.No, BusinessPlaceName = bPlace.Name, ShiftType = record.ShiftType, CostDateTime = record.CostDateTime, ApplicationType = record.ApplicationType, }) .ToPageListAsync(request); var now = DateTime.Now; var monthStart = new DateTime(now.Year, now.Month, 1); var monthEnd = monthStart.AddMonths(1).AddSeconds(-1); var monthSum = await _dbContext.Queryable() .Where(x => x.CostDateTime >= monthStart && x.CostDateTime <= monthEnd) .Where(x => x.Type == BalanceRecordTypeEnumType.Out) .Where(x => x.Status == BalanceRecordStatusEnumType.Valid) .Where(x => x.UserCode == jinkoCode) .SumAsync(x => SqlFunc.Abs(x.Amount)); return new BalanceRecordUserResultResponse { CurrentMonthSum = monthSum, Items = page }; } public async Task GetConsumeNumberAsync() { var devices = await _dbContext.Queryable() .Where(x => x.Type == DeviceTypeEnumType.Meal) .ToListAsync(); var offCount = await GetOfflineDevicesCount(devices); var places = await _dbContext.Queryable() .Where(x => x.Status == CanteenBusinessPlaceStatusEnumType.Opened) .ToListAsync(); //所有消费数据 var bs = await _dbContext.Queryable() .Where(x => x.Type == BalanceRecordTypeEnumType.Out) .ToListAsync(); //可消费人数 var consumerable = bs.Select(x => x.UserCode).Distinct().Count(); var dailyNumber = SameDay(bs, ""); //消费走势 var months = await GetConsumptionTrendsAsync(ConsumptionTrendEnumType.Month); var days = await GetConsumptionTrendsAsync(ConsumptionTrendEnumType.Day); var res = new ConsumeNumberResponse { Total = devices.Count, OffNumber = offCount.Count, PlaceNumber = places.Count, ConsumerableNumber = consumerable, DailyNumber = dailyNumber, Days = days, Months = months }; return res; } /// /// 获取单日消费数据 /// /// /// /// private static int SameDay(List bs, string userCode) { var time = DateTime.Now.ToString("yyyy-MM-dd"); var b = userCode.IsEmpty(); //当日消费数据 var monthbs = bs .Where(x => x.CostDateTime.ToString("yyyy-MM-dd") == time) .WhereIF(!userCode.IsEmpty(), x => x.UserCode == userCode); //当日消费额 var monthlySpending = monthbs.Sum(x => x.Amount); //当日消费人数 var monthlyCount = monthbs.Select(x => x.UserCode).Distinct().Count(); var dailyNumber = monthlyCount != 0 ? Math.Abs((int)Math.Ceiling(monthlySpending / monthlyCount)) : 0; return dailyNumber; } public async Task> GetSpendingDetailsAsync(SpendingDetailRequest request) { var brs = await _dbContext .Queryable() .Where(x => x.Type == BalanceRecordTypeEnumType.Out) .ToListAsync(); var codes = brs.Select(x => x.UserCode).ToList().Distinct().ToList(); var lst = new List(); foreach (var code in codes) { var br = brs.Where(x => x.UserCode == code).FirstOrDefault(); var res = br.Adapt(); lst.Add(res); } // 分页操作 var startIndex = (request.PageIndex - 1) * request.PageSize; var pagedData = lst.Skip(startIndex).Take(request.PageSize).ToList(); var pagination = new Pagination { PageIndex = request.PageIndex, PageSize = request.PageSize, Total = lst.Count, // 使用原始列表的总数进行分页 Data = pagedData }; return pagination; } public async Task> AverageDailyAsync(AverageDailyRequest request) { //所有消费数据 var brs = await _dbContext.Queryable() .Where(x => x.Type == BalanceRecordTypeEnumType.Out) .ToListAsync(); var codes = brs.Select(x => x.UserCode).ToList().Distinct().ToList(); var lst = new List(); foreach (var code in codes) { var br = brs.FirstOrDefault(x => x.UserCode == code); var res = br.Adapt(); var dailyNumber = SameDay(brs, code); res.AverageAmount = dailyNumber; lst.Add(res); } // 分页操作 var startIndex = (request.PageIndex - 1) * request.PageSize; var pagedData = lst.Skip(startIndex).Take(request.PageSize).ToList(); var pagination = new Pagination { PageIndex = request.PageIndex, PageSize = request.PageSize, Total = lst.Count, // 使用原始列表的总数进行分页 Data = pagedData }; return pagination; } public async Task> GetConsumeDeviceAsync(GetConsumeDeviceRequest request) { if (request.IsOff) { //消费机离线设备 var devices = await _dbContext.Queryable() .Where(x => x.Type == DeviceTypeEnumType.Meal) .ToListAsync(); var offs = await GetOfflineDevicesCount(devices); var offUniqueNos = offs.Select(x => x.UniqueNo).ToList(); var offpage = await _dbContext.Queryable() .LeftJoin((d, dt) => d.TemplateCode == dt.Code) .Where(d => offUniqueNos.Contains(d.UniqueNo)) .Select((d, dt) => new DevicePageResponse { FuncsStr = d.Funcs.ToString(), Brand = dt.Brand, Type = d.Type, }, true) .ToPageListAsync(request); return offpage; } //消费机总数 var page = await _dbContext.Queryable() .LeftJoin((d, dt) => d.TemplateCode == dt.Code) .Where(d => d.Type == DeviceTypeEnumType.Meal) .Select((d, dt) => new DevicePageResponse { FuncsStr = d.Funcs.ToString(), Brand = dt.Brand, Type = d.Type, }, true) .ToPageListAsync(request); return page; } 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) { foreach (var request in requests) { var gainfo = await _dbContext.Queryable() .Where(x => x.Code == request.UserCode) .SingleAsync() ?? await _dbContext.Queryable().AS("GainfoLeave") .Where(x => x.Code == request.UserCode) .SingleAsync(); gainfo.ThrowIfNull($"工号{request.UserCode}不存在"); await _dbContext.InsertAsync(new BalanceRecordEntity { No = "M" + SnowFlakeHelper.Instance.NextId(), UserNo = gainfo.No, Name = gainfo.Name, UserCode = gainfo.Code, DepartmentNo = gainfo.DepartmentNo, Source = BalanceRecordSourceEnumType.OtherTenantSubsidies, Type = BalanceRecordTypeEnumType.In, Amount = request.Balance, BusinessPlaceCode = null, BusinessShiftCode = null, ShiftType = null, CostDate = DateTime.Now.Date, DeviceUniqueNo = null, SubsidiesFromMonth = DateTime.Now.ToString("yyyyMM"), IP = null, CostId = gainfo.CostId, OriginTenantId = gainfo.Factory, IsCostCrossTenant = false, Status = BalanceRecordStatusEnumType.Valid, CostDateTime = DateTime.Now, Remark = "手动写入余额", ApplicationType = CanteenApplicationTypeEnumType.Manually, TenantId = gainfo.Factory, Werks = gainfo.Werks, IsDeleted = false }); await CommonHelper.RetryAsync(async () => { var gainfoMeal = await _dbContext.Queryable().Where(x => x.UserCode == gainfo.Code).FirstAsync(); 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 balance = await _dbContext.Queryable() .Where(x => x.UserCode == gainfo.Code) .Where(x => x.Status == BalanceRecordStatusEnumType.Valid) .SumAsync(x => x.Amount); await _dbContext.Updateable() .SetColumns(x => x.Version == x.Version + 1) .SetColumns(x => x.Balance == balance) .SetColumns(x => x.ModifiedTime == DateTime.Now) .Where(x => x.UserCode == gainfoMeal.UserCode && x.Version == gainfoMeal.Version) .ExecuteCommandThrowVersionAsync(); }, 5); } } public async Task ReCalcAsync(BalanceReCalcRequest request) { await CommonHelper.RetryAsync(async () => { var gainfo = await _dbContext.Queryable().Where(x => x.Code == request.UserCode).SingleAsync(); gainfo.ThrowIfNull("档案不存在"); var gainfoMeal = await _dbContext.Queryable().Where(x => x.UserCode == gainfo.Code).FirstAsync(); 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 balance = await _dbContext.Queryable() .Where(x => x.UserCode == gainfo.Code) .Where(x => x.Status == BalanceRecordStatusEnumType.Valid) .SumAsync(x => x.Amount); await _dbContext.Updateable() .SetColumns(x => x.Version == x.Version + 1) .SetColumns(x => x.Balance == balance) .SetColumns(x => x.ModifiedTime == DateTime.Now) .Where(x => x.UserCode == gainfoMeal.UserCode && x.Version == gainfoMeal.Version) .ExecuteCommandThrowVersionAsync(); }, 5); } /// /// 获取离线设备 /// /// /// private async Task> GetOfflineDevicesCount(List devices) { var now = DateTime.Now; var deviceUniqueNos = devices.Select(x => x.UniqueNo).ToList(); var deviceRegistries = await _dbContext.Queryable() .LeftJoin((x, d) => x.DeviceUniqueNo == d.UniqueNo) .Where(x => deviceUniqueNos.Contains(x.DeviceUniqueNo)) .Select((x, d) => new { d.UniqueNo, d.HeartbeatInterval, x.LastHeartbeatTime }) .ToListAsync(); var onlineDevices = deviceRegistries .Where(x => { if (x.HeartbeatInterval <= 0) { return true; } return x.LastHeartbeatTime.AddSeconds(x.HeartbeatInterval * 3) >= now; }) .Select(x => new { x.UniqueNo, }) .ToList(); var onlineUniqueNos = new List(); onlineDevices.ForEach(x => onlineUniqueNos.Add(x.UniqueNo)); var offUniqueNos = deviceUniqueNos.Except(onlineUniqueNos).ToList(); var list = devices.Where(x => offUniqueNos.Contains(x.UniqueNo)).ToList(); return list; } private async Task> GetConsumptionTrendsAsync(ConsumptionTrendEnumType type) { var bs = await _dbContext.Queryable() .Where(x => x.Type == BalanceRecordTypeEnumType.Out) .ToListAsync(); //消费总额 var totalAmount = Math.Round(Math.Abs(bs.Sum(x => x.Amount)), 2); var list = new List(); if (type == ConsumptionTrendEnumType.Month) { //月与总额对比 var months = GetMonthsOfYear(); foreach (var month in months) { var currentAmount = Math.Round(Math.Abs(bs.Where(x => x.CostDateTime.ToString("yyyy-MM") == month) .Sum(x => x.Amount)), 2); var res = new ConsumptionTrendResponse { DateTime = month, CurrentAmount = currentAmount, TotalAmount = totalAmount }; list.Add(res); } } else { //日与总额对比 var days = GetDaysOfMonth(); foreach (var day in days) { var currentAmount = Math.Round(Math.Abs(bs.Where(x => x.CostDateTime.ToString("yyyy-MM-dd") == day) .Sum(x => x.Amount)), 2); var res = new ConsumptionTrendResponse { DateTime = day, CurrentAmount = currentAmount, TotalAmount = totalAmount }; list.Add(res); } } return list; } /// /// 获取当前年所有月 /// /// private List GetMonthsOfYear() { var months = new List(); // 获取当前年份 int year = DateTime.Now.Year; // 遍历12个月份并添加到列表中 for (int month = 1; month <= 12; month++) { var monthDate = new DateTime(year, month, 1); months.Add(monthDate.ToString("yyyy-MM")); } return months; } /// /// 获取当前月所有天 /// /// private List GetDaysOfMonth() { var days = new List(); // 获取当前年份和月份 int year = DateTime.Now.Year; int month = DateTime.Now.Month; // 获取当前月份的第一天和最后一天 var firstDayOfMonth = new DateTime(year, month, 1); var lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1); // 遍历当前月的所有日期并添加到列表中 for (DateTime date = firstDayOfMonth; date <= lastDayOfMonth; date = date.AddDays(1)) { days.Add(date.ToString("yyyy-MM-dd")); } return days; } }