using Dlhis.Basic.Common.ApiLog.Services; using Dlhis.Basic.Common.Message.Requests; using Dlhis.Basic.Common.Message.Services; using Dlhis.Basic.WorkGrids.Repositories; using Dlhis.Basic.WorkGrids.Request; using Dlhis.Basic.WorkGrids.Response; using Dlhis.Constants; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Basic.Enums; using Dlhis.Entity.Common.Enums; using Dlhis.Entity.Device.Entities; using Dlhis.Entity.Device.Enums; using Dlhis.Jip.Api; using Dlhis.Jip.Api.Requests; using Kurisu.AspNetCore.Authentication.Abstractions; using Kurisu.AspNetCore.CustomClass; 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.Basic.WorkGrids.Services.Implements; public class WorkGridScheduleService : IWorkGridScheduleService { private readonly IDbContext _dbContext; private readonly IJinkoJipApi _jinkoJipApi; private readonly IOperateLog _operateLog; private readonly ILogger _logger; private readonly IJinkoApiService _jinkoApiService; private readonly IWorkGridScheduleRepository _workGridScheduleRepository; private readonly ICurrentUser _currentUser; public WorkGridScheduleService(IDbContext dbContext, IJinkoJipApi jinkoJipApi, IOperateLog operateLog, ILogger logger, IJinkoApiService jinkoApiService, IWorkGridScheduleRepository workGridScheduleRepository, ICurrentUser currentUser) { _dbContext = dbContext; _jinkoJipApi = jinkoJipApi; _operateLog = operateLog; _logger = logger; _jinkoApiService = jinkoApiService; _workGridScheduleRepository = workGridScheduleRepository; _currentUser = currentUser; } public async Task SaveAsync(WorkGridScheduleSaveRequest request) { request.UserCodes = request.UserCodes.Distinct().ToList(); var workGrid = await _dbContext.Queryable() .LeftJoin((x, ga) => x.LeaderUserCode == ga.Code) .Where(x => x.Code == request.WorkGridCode) .Select((x, ga) => new { x.Code, x.Name, x.LeaderUserCode, LeaderUserName = ga.Name, x.MaxConsecutiveLimitCount }) .SingleAsync(); workGrid.ThrowIfNull("工作网格不存在"); var timeSetting = await _dbContext.Queryable() .Where(x => x.Type == request.PeriodType) .Select(x => new { x.Start, x.Before }) .SingleAsync(); timeSetting.ThrowIfNull("工作网格时间尚未配置,无法操作"); //班次开始前一个小时不可操作 var currentLimit = request.Date.ToDateTime(timeSetting.Start.Add(-TimeSpan.FromHours(1))); (DateTime.Now >= currentLimit).ThrowIfTrue("当前时间不可修改排班信息"); var workGridUserCodes = await _dbContext.Queryable() .Where(x => x.WorkGridCode == workGrid.Code && request.UserCodes.Contains(x.UserCode)) .Select(x => x.UserCode) .ToListAsync(); var others = request.UserCodes.Except(workGridUserCodes).ToList(); others.ThrowIfExists($"{others.Join(",")}非当前网格人员"); //处理排班冲突问题 var dateBefore = request.Date.AddDays(-1); var dateAfter = request.Date.AddDays(1); var scheduleRange = await _dbContext.Queryable() .Where(x => x.WorkGridCode == request.WorkGridCode && x.Date >= dateBefore && x.Date <= dateAfter && request.UserCodes.Contains(x.UserCode) ) .ToListAsync(); //重复排班 var existsUserCodes = scheduleRange.Where(x => x.Date == request.Date && request.UserCodes.Contains(x.UserCode)).Select(x => x.UserCode).ToList(); existsUserCodes.ThrowIfExists(existsUserCodes.Join(",") + "当天已存在排班"); //当前排班为白班,那么上一天不能存在晚班 if (request.PeriodType == WorkGridTimePeriodEnumType.Day) { existsUserCodes = scheduleRange .Where(x => x.Date == dateBefore && x.PeriodType == WorkGridTimePeriodEnumType.Night && request.UserCodes.Contains(x.UserCode)) .Select(x => x.UserCode).ToList(); existsUserCodes.ThrowIfExists(existsUserCodes.Join(",") + $"在{dateBefore.ToString("yyyy-MM-dd")}已存在{WorkGridTimePeriodEnumType.Night.GetDescription()}"); } //当前排班为晚班,那么下一天不能存在白班 else if (request.PeriodType == WorkGridTimePeriodEnumType.Night) { existsUserCodes = scheduleRange .Where(x => x.Date == dateAfter && x.PeriodType == WorkGridTimePeriodEnumType.Day && request.UserCodes.Contains(x.UserCode)) .Select(x => x.UserCode).ToList(); existsUserCodes.ThrowIfExists(existsUserCodes.Join(",") + $"在{dateAfter.ToString("yyyy-MM-dd")}已存在{WorkGridTimePeriodEnumType.Day.GetDescription()}"); } else { throw new ArgumentException("排班类型错误,请检查"); } //连续排班限制 var maximumConsecutiveListDto = await _workGridScheduleRepository.MaximumConsecutiveUserCodesAsync( request.UserCodes, workGrid.MaxConsecutiveLimitCount, request.Date ); maximumConsecutiveListDto.ThrowIfExists(maximumConsecutiveListDto.Select(x => $"{x.UserCode}已经连续排班{workGrid.MaxConsecutiveLimitCount}天({x.Start}-{x.End}),禁止排班").Join("\r\n")); //数据保存 var toInserts = request.UserCodes.Select(x => new WorkGridScheduleEntity { WorkGridCode = request.WorkGridCode, Date = request.Date, PeriodType = request.PeriodType, UserCode = x }).ToList(); await _dbContext.InsertAsync(toInserts); await _operateLog.LogAsync(OperateLogType.WorkGrid, $"新增网格({workGrid.Name}){request.Date.ToString("yyyy-M-d")}的{request.PeriodType.GetDescription()}人员:{request.UserCodes.Join(",")}"); await PublishScheduleToSapAsync(workGrid.Code, toInserts, request.PeriodType); await NoticeScheduleMessageAsync(request.UserCodes, workGrid.Name, workGrid.LeaderUserName, workGrid.LeaderUserCode, request.Date, request.PeriodType); } private async Task PublishScheduleToSapAsync(Guid workGridCode, List toInserts, WorkGridTimePeriodEnumType periodType) { //仅部分网格推送SAP var workGridCodes = new List { "769ace3b-28fa-4c4e-ba46-fc399a25085f", //"90e79f00-d203-4a4d-a08e-fd129ea02734", //"9901a303-c537-4db4-9b83-0bc5b3e713ab", //"6e37be58-75a3-44d8-bb6e-4faf9d6dbc39", //"cdea5dd2-42d6-44a5-bd0d-9a339f788174", //"c983e6d8-0626-4171-8313-4cd4379975a2", //"a4bad4f4-a041-47e8-b7ee-8c480babf7d6", "5f6bf4ae-f7d1-47ce-9dc0-82f5f0cadff0", "47416294-724f-4a63-b422-bb6c43466554", "33fb2070-f8ec-4762-b7f7-4073eb68a41d", "ee509a29-c040-4e49-964d-d7e3a9094a0f", "25aac70d-73ca-465a-bd70-fcd5999d1476", "aa1e4052-c1e8-4213-bfb5-aacf00eb480f", "6fd4df7e-6278-4622-8d9c-77e29f7fad1d", "5ea25d26-1ce2-4760-9bee-c21a8ff78ff6", "fe61c0d7-dda8-4570-afd4-be2b28def6b3", "67ccd75f-7743-48a0-b89e-3d90fd208c8a", "375976e4-b4ca-424c-bfb9-a81f5ff02c5d", "af588a53-7757-48d2-a078-280be0b471e4", "a54365db-624d-4d24-8840-465a7cd0d760" }; if (!workGridCodes.Contains(workGridCode.ToString())) { return; } //推送到SAP var items = toInserts.Select(x => new SyncWorkShiftDayItem { UserCode = x.UserCode, Begin = x.Date.ToString("yyyyMMdd"), End = x.Date.ToString("yyyyMMdd"), Plan = periodType == WorkGridTimePeriodEnumType.Day ? "JK03" : "JK04", Operate = "INS" }).ToList(); var response = await _jinkoJipApi.SyncWorkShiftAsync(new SyncWorkShiftRequest { Items = items }); response.Message.IsPresent().ThrowIfTrue("JIP接口返回错误:" + response.Message); if (response == null || response.Items == null || response.Items.Count == 0) { throw new UserFriendlyException("JIP接口返回数据异常,请联系管理员"); } if (response.Items.Any(x => x.Result != "S")) { var errorMessages = response.Items.Where(x => x.Result != "S") .Select(x => $"{x.UserCode}排班新增失败:{x.Message}").Join("\r\n"); throw new UserFriendlyException(errorMessages); } foreach (var workGridSchedule in toInserts) { var msg = response.Items.FirstOrDefault(x => x.UserCode == workGridSchedule.UserCode)?.Message; await _dbContext.Updateable() .SetColumns(x => x.SyncResult == msg) .Where(x => x.WorkGridCode == workGridCode) .Where(x => x.UserCode == workGridSchedule.UserCode) .Where(x => x.Date == workGridSchedule.Date) .Where(x => x.PeriodType == workGridSchedule.PeriodType) .ExecuteCommandAsync(); } } private async Task NoticeScheduleMessageAsync(List userCodes, string workGridName, string workGridLeaderName, string workGridLeaderCode, DateOnly date, WorkGridTimePeriodEnumType periodType) { //todo sub pub //推送排班通知 try { var codeAndNameList = await _dbContext.Queryable().Where(x => userCodes.Contains(x.Code)) .Select(x => new { x.Code, x.Name }) .ToListAsync(); foreach (var gainfo in codeAndNameList) { try { await _jinkoApiService.SendWorkMessageByWorkGridAsync(new DingTalkWorkMessageRequest { Content = $"{gainfo.Name}({gainfo.Code})你好,系统检测到你被{workGridName}网格的网格长{workGridLeaderName}({workGridLeaderCode})安排了{date.ToString("yyyy年MM月dd日")}{periodType.GetDescription()},请按时上班", WorkCodes = new List { gainfo.Code } }); } catch (Exception e) { _logger.LogError("推送信息错误{code}:{error}", gainfo.Code, e.Message); } } } catch (Exception e) { _logger.LogError("推送信息错误:{error}", e.Message); } } public async Task RemoveAsync(WorkGridScheduleSaveRequest request) { var workGrid = await _dbContext.Queryable() .LeftJoin((x, ga) => x.LeaderUserCode == ga.Code) .Where(x => x.Code == request.WorkGridCode) .Select((x, ga) => new { x.Code, x.Name, x.LeaderUserCode, LeaderUserName = ga.Name }) .SingleAsync(); workGrid.ThrowIfNull("工作网格不存在"); var timeSetting = await _dbContext.Queryable() .Where(x => x.Type == request.PeriodType) .Select(x => new { x.Start, x.Before }) .SingleAsync(); //班次识别区间内禁止操作移除 var currentLimit = request.Date.ToDateTime(timeSetting.Start.Add(-TimeSpan.FromSeconds(timeSetting.Before))); (DateTime.Now >= currentLimit).ThrowIfTrue("当前时间不可修改排班信息"); //获取需要移除的班次数据 var data = await _dbContext.Queryable() .Where(x => x.WorkGridCode == request.WorkGridCode) .Where(x => x.Date == request.Date) .Where(x => x.PeriodType == request.PeriodType) .Where(x => request.UserCodes.Contains(x.UserCode)) .ToListAsync(); if (data.Any()) { if (workGrid.Code.ToString() == "769ace3b-28fa-4c4e-ba46-fc399a25085f") { //推送到SAP var items = data.Select(x => new SyncWorkShiftDayItem { UserCode = x.UserCode, Begin = x.Date.ToString("yyyyMMdd"), End = x.Date.ToString("yyyyMMdd"), Plan = request.PeriodType == WorkGridTimePeriodEnumType.Day ? "JK03" : "JK04", Operate = "DEL" }).ToList(); var response = await _jinkoJipApi.SyncWorkShiftAsync(new SyncWorkShiftRequest { Items = items }); response.Message.IsPresent().ThrowIfTrue("JIP接口返回错误:" + response.Message); if (response == null || response.Items == null || response.Items.Count == 0) { throw new UserFriendlyException("JIP接口返回数据异常,请联系管理员"); } if (response.Items.Any(x => x.Result != "S")) { var errorMessages = response.Items.Where(x => x.Result != "S") .Select(x => $"{x.UserCode}排班删除失败:{x.Message}").Join("\r\n"); throw new UserFriendlyException(errorMessages); } foreach (var workGridSchedule in data) { workGridSchedule.SyncResult = response.Items.FirstOrDefault(x => x.UserCode == workGridSchedule.UserCode)?.Message; } await _dbContext.UpdateAsync(data); } await _dbContext.DeleteAsync(data); await _operateLog.LogAsync(OperateLogType.WorkGrid, $"移除网格({workGrid.Name}){request.Date.ToString("yyyy-M-d")}的{request.PeriodType.GetDescription()}人员:{request.UserCodes.Join(",")}"); //todo sub pub //推送移除通知 try { var userCodes = data.Select(x => x.UserCode).Distinct().ToList(); var gainfos = await _dbContext.Queryable().Where(x => userCodes.Contains(x.Code)).Select(x => new { x.Code, x.Name }).ToListAsync(); foreach (var gainfo in gainfos) { try { await _jinkoApiService.SendWorkMessageByWorkGridAsync(new DingTalkWorkMessageRequest { Content = $"{gainfo.Name}({gainfo.Code})你好,系统检测到你被{workGrid.Name}网格的网格长{workGrid.LeaderUserName}({workGrid.LeaderUserCode})删除了{request.Date.ToString("yyyy年MM月dd日")}{request.PeriodType.GetDescription()},如有疑问,请联系网格长!", WorkCodes = new List { gainfo.Code } }); } catch (Exception e) { _logger.LogError("推送信息错误{code}:{error}", gainfo.Code, e.Message); } } } catch (Exception e) { _logger.LogError("推送信息错误remove,{error}", e.Message); } } } public async Task ManuallyCancelAsync(long id, string reason) { var currentDate = DateOnly.FromDateTime(DateTime.Today); var workGridSchedule = await _dbContext.Queryable() .Where(x => x.Date < currentDate) .SingleAsync(x => x.Id == id); workGridSchedule.ThrowIfNull("排班信息不存在或不可操作"); workGridSchedule.IsCancel.ThrowIfTrue("排班信息已被取消,请勿重复操作"); //获取班次的时间配置 var timeSetting = await _dbContext.Queryable() .Where(x => x.Type == workGridSchedule.PeriodType) .Select(x => new { x.Start, x.Before }) .SingleAsync(); timeSetting.ThrowIfNull("工作网格时间尚未配置,无法操作"); var start = workGridSchedule.Date.ToDateTime(timeSetting.Start); var end = workGridSchedule.Date.ToDateTime(TimeOnly.MaxValue); //const string filterString = "{\"IsError\":true"; var exists = await _dbContext.Queryable() .Where(x => x.UserUniqueNo == workGridSchedule.UserCode) .Where(x => x.RaiseDateTime >= start && x.RaiseDateTime <= end) .Where(x => x.Type == DeviceUploadLogTypeEnumType.Attendance) .AnyAsync(); exists.ThrowIfTrue("排班信息已存在打卡记录,请勿取消"); await _workGridScheduleRepository.CancelAsync(id, reason); } public async Task> GetListAsync(Guid workGridCode, DateOnly date, WorkGridTimePeriodEnumType? periodType) { var data = await _dbContext.Queryable() .LeftJoin((x, ga) => x.UserCode == ga.Code) .Where(x => x.WorkGridCode == workGridCode && x.Date == date) .WhereIF(periodType.HasValue, x => x.PeriodType == periodType) .Select((x, ga) => new WorkGridScheduleResponse { Id = x.Id, Date = x.Date, PeriodType = x.PeriodType, UserCode = x.UserCode, UserName = ga.Name, IsCancel = x.IsCancel }) .ToListAsync(); return data; } public async Task> GetListSummaryAsync(Guid workGridCode, DateTimePeriod dateTimePeriod) { var start = dateTimePeriod.Start!.Value; var end = dateTimePeriod.End!.Value; return await _workGridScheduleRepository.GetListSummaryAsync(workGridCode, new DateOnly(start.Year, start.Month, start.Day), new DateOnly(end.Year, end.Month, end.Day) ); } public async Task> GetAttendancePageAsync(WorkGridScheduleAttendancePageRequest request) { var jinkoCode = _currentUser.GetUserClaim(UserClaimConstants.JinkoCode); var workGrid = await _dbContext.Queryable() .WhereIF(request.WorkGridCode.HasValue, x => x.Code == request.WorkGridCode) .WhereIF(request.WorkGridCode.HasValue == false, x => x.LeaderUserCode == jinkoCode) .SingleAsync(); if (workGrid == null) { return new Pagination { PageIndex = request.PageIndex, PageSize = request.PageSize, Total = 0, Data = null }; } var t = request.IsError.ToString().ToLower(); var filterString = $"{{\"IsError\":{t}"; var r = await _dbContext.Queryable() .LeftJoin((log, d) => log.DeviceUniqueNo == d.UniqueNo) .LeftJoin((log, d, ga) => log.UserUniqueNo == ga.Code) .WhereIF(request.DateTimePeriod.HasValue(), log => log.RaiseDateTime >= request.DateTimePeriod.Start && log.RaiseDateTime <= request.DateTimePeriod.End) .WhereIF(request.UserCode.IsPresent(), log => log.UserUniqueNo == request.UserCode) .WhereIF(request.UserCode.IsEmpty(), log => SqlFunc.Subqueryable() .Where(p => p.WorkGridCode == workGrid.Code && p.UserCode == log.UserUniqueNo && p.IsDeleted == false) .Any() ) .Where(log => log.WorkGridInfo.StartsWith(filterString)) .Where((log, d) => d.Type == DeviceTypeEnumType.Attendance || (d.Type == DeviceTypeEnumType.Door && d.AsAttendance)) .OrderBy(log => log.RaiseDateTime) .Select((log, d, ga) => new WorkGridScheduleAttendancePageResponse { Id = log.Id, OriginUserUniqueNo = log.UserUniqueNo, DeviceUniqueNo = log.DeviceUniqueNo, RaiseDateTime = log.RaiseDateTime, CreateTime = log.CreateTime, SerialNo = log.SerialNo, DeployPlace = d.DeployPlace, UserCode = log.UserUniqueNo, UserName = ga.Name, Sync = log.Sync, NeedUpload = log.NeedUpload, Status = log.Status, WorkGridInfo = log.WorkGridInfo, }) .ToPageListAsync(request.PageIndex, request.PageSize); return new Pagination { PageIndex = request.PageIndex, PageSize = request.PageSize, Total = 0, Data = r }; } public async Task> GetAllAttendancePageAsync(WorkGridScheduleAttendancePageRequest request) { var t = request.IsError.ToString().ToLower(); var filterString = $"{{\"IsError\":{t}"; if (request.UserCode.IsEmpty()) { request.UserCode = request.Search; } var page = await _dbContext.Queryable() .LeftJoin((log, d) => log.DeviceUniqueNo == d.UniqueNo) .LeftJoin((log, d, ga) => log.UserUniqueNo == ga.Code) .WhereIF(request.DateTimePeriod.HasValue(), log => log.RaiseDateTime >= request.DateTimePeriod.Start && log.RaiseDateTime <= request.DateTimePeriod.End) .Where(log => SqlFunc.Subqueryable() .Where(p => p.UserCode == log.UserUniqueNo && p.IsDeleted == false) .WhereIF(request.WorkGridCode.HasValue, p => p.WorkGridCode == request.WorkGridCode) .WhereIF(request.UserCode.IsPresent(), p => p.UserCode == request.UserCode) .Any() ) .Where(log => log.WorkGridInfo.StartsWith(filterString)) .Where((log, d) => d.Type == DeviceTypeEnumType.Attendance || (d.Type == DeviceTypeEnumType.Door && d.AsAttendance)) .OrderBy(log => log.RaiseDateTime, OrderByType.Desc) .Select((log, d, ga) => new WorkGridScheduleAttendancePageResponse { Id = log.Id, OriginUserUniqueNo = log.UserUniqueNo, DeviceUniqueNo = log.DeviceUniqueNo, RaiseDateTime = log.RaiseDateTime, CreateTime = log.CreateTime, SerialNo = log.SerialNo, DeployPlace = d.DeployPlace, UserCode = log.UserUniqueNo, UserName = ga.Name, Sync = log.Sync, NeedUpload = log.NeedUpload, Status = log.Status, WorkGridInfo = log.WorkGridInfo, }) .ToPageListAsync(request); var leaderUserCodes = page.Data.Select(x => x.LeaderUserCode).Distinct().ToList(); var gainfos = await _dbContext.Queryable().Where(x => leaderUserCodes.Contains(x.Code)).Select(x => new { x.Code, x.Name }).ToListAsync(); page.Data.ForEach(x => { var gainfo = gainfos.FirstOrDefault(p => p.Code == x.LeaderUserCode); if (gainfo != null) { x.LeaderUserName = gainfo.Name; } }); return page; } public async Task ManuallyUploadAsync(List logIds) { var sub = _currentUser.GetIntSubjectId(); const string filterString = "{\"IsError\":true"; var requests = await _dbContext.Queryable() .LeftJoin((x, device) => x.DeviceUniqueNo == device.UniqueNo) .Where(x => logIds.Contains(x.Id) && x.Sync == false) .Where(x => x.WorkGridInfo.StartsWith(filterString)) .Select((x, device) => new UploadAttendanceRequestItem { Id = x.Id, RaiseDateTime = x.RaiseDateTime, ZNOTE = device.DeployPlace, ZWORKCODE = x.UserUniqueNo }) .ToListAsync(); if (requests.Any()) { foreach (var items in requests.Chunk(500)) { var ids = items.Select(x => x.Id).ToList(); var r = await _jinkoJipApi.UploadAttendancesAsync(new UploadAttendanceRequest { Data = items.ToList() }); if (r.OLX == "0") { await _dbContext.Updateable() .SetColumns(x => x.Sync == true) .SetColumns(x => x.ModifiedTime == DateTime.Now) .SetColumns(x => x.ModifiedBy == sub) .Where(x => ids.Contains(x.Id)) .ExecuteCommandAsync(); await _operateLog.LogAsync(OperateLogType.WorkGrid, $"重提上传考勤{DateTime.Now:yyyy-MM-dd HH:mm:ss}:{items.Select(x => x.ZWORKCODE + $"打卡时间{x.RaiseDateTime:yyyy-MM-dd HH:mm:ss}").Join("\r\n")}"); } } } } }