Files
jinko.yikatong/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridScheduleService.cs
2025-03-24 10:54:06 +08:00

369 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Dlhis.Basic.Common.ApiLog.Services;
using Dlhis.Basic.Common.Message;
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<WorkGridScheduleService> _logger;
private readonly IJinkoApiService _jinkoApiService;
private readonly ICurrentUser _currentUser;
public WorkGridScheduleService(IDbContext dbContext,
IJinkoJipApi jinkoJipApi,
IOperateLog operateLog,
ILogger<WorkGridScheduleService> logger,
IJinkoApiService jinkoApiService,
ICurrentUser currentUser)
{
_dbContext = dbContext;
_jinkoJipApi = jinkoJipApi;
_operateLog = operateLog;
_logger = logger;
_jinkoApiService = jinkoApiService;
_currentUser = currentUser;
}
public async Task SaveAsync(WorkGridScheduleSaveRequest request)
{
request.UserCodes = request.UserCodes.Distinct().ToList();
var workGrid = await _dbContext.Queryable<WorkGridEntity>()
.LeftJoin<GainfoEntity>((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<WorkGridTimePeriodEntity>().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 workGridUserCodes = await _dbContext.Queryable<WorkGridUserEntity>()
.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<WorkGridScheduleEntity>()
.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()}");
}
var maximumConsecutiveDtos = await MaximumConsecutiveUserCodesAsync(request.UserCodes, 10, request.Date);
//张三(工号)已连续排班10天,超10休1规定,禁止排班,请处理
maximumConsecutiveDtos.ThrowIfExists(maximumConsecutiveDtos.Select(x => $"{x.UserCode}已经连续排班10天({x.Start}-{x.End}),超10休1规定,禁止排班").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(",")}");
try
{
var gainfos = await _dbContext.Queryable<GainfoEntity>().Where(x => request.UserCodes.Contains(x.Code)).Select(x => new { x.Code, x.Name }).ToListAsync();
foreach (var gainfo in gainfos)
{
try
{
await _jinkoApiService.SendWorkMessageAsync(new JinkoSendWorkMessageRequest
{
Content = $"{gainfo.Name}({gainfo.Code})你好,系统检测到你被{workGrid.Name}网格的网格长{workGrid.LeaderUserName}({workGrid.LeaderUserCode})安排了{request.Date.ToString("yyyy年MM月dd日")}{request.PeriodType.GetDescription()},请按时上班",
WorkCodes = new List<string> { gainfo.Code }
});
}
catch (Exception e)
{
_logger.LogError("推送信息错误{code}:{error}", gainfo.Code, e.Message);
}
}
}
catch (Exception e)
{
_logger.LogError("推送信息错误save,{error}", e.Message);
}
}
/// <inheritdoc />
public async Task RemoveAsync(WorkGridScheduleSaveRequest request)
{
var workGrid = await _dbContext.Queryable<WorkGridEntity>()
.LeftJoin<GainfoEntity>((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<WorkGridTimePeriodEntity>().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<WorkGridScheduleEntity>()
.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())
{
await _dbContext.DeleteAsync(data);
await _operateLog.LogAsync(OperateLogType.WorkGrid, $"移除网格({workGrid.Name}){request.Date.ToString("yyyy-M-d")}的{request.PeriodType.GetDescription()}人员:{request.UserCodes.Join(",")}");
try
{
var userCodes = data.Select(x => x.UserCode).Distinct().ToList();
var gainfos = await _dbContext.Queryable<GainfoEntity>().Where(x => userCodes.Contains(x.Code)).Select(x => new { x.Code, x.Name }).ToListAsync();
foreach (var gainfo in gainfos)
{
try
{
await _jinkoApiService.SendWorkMessageAsync(new JinkoSendWorkMessageRequest
{
Content = $"{gainfo.Name}({gainfo.Code})你好,系统检测到你被{workGrid.Name}网格的网格长{workGrid.LeaderUserName}({workGrid.LeaderUserCode})删除了{request.Date.ToString("yyyy年MM月dd日")}{request.PeriodType.GetDescription()},如有疑问,请联系网格长!",
WorkCodes = new List<string> { gainfo.Code }
});
}
catch (Exception e)
{
_logger.LogError("推送信息错误{code}:{error}", gainfo.Code, e.Message);
}
}
}
catch (Exception e)
{
_logger.LogError("推送信息错误remove,{error}", e.Message);
}
}
}
/// <inheritdoc />
public async Task<List<WorkGridScheduleResponse>> GetListAsync(Guid workGridCode, DateOnly date, WorkGridTimePeriodEnumType? periodType)
{
var data = await _dbContext.Queryable<WorkGridScheduleEntity>()
.LeftJoin<GainfoEntity>((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
{
Date = x.Date,
PeriodType = x.PeriodType,
UserCode = x.UserCode,
UserName = ga.Name
})
.ToListAsync();
return data;
}
/// <inheritdoc />
public async Task<IEnumerable<WorkGridScheduleSummaryResponse>> GetListSummaryAsync(Guid workGridCode, DateOnly start, DateOnly end)
{
var workGridSchedules = await _dbContext.Queryable<WorkGridScheduleEntity>()
.Where(x => x.WorkGridCode == workGridCode && x.Date >= start && x.Date <= end)
.ToListAsync();
return workGridSchedules.GroupBy(x => x.Date).Select(x =>
{
return new WorkGridScheduleSummaryResponse
{
Date = x.Key,
Details = x.GroupBy(p => p.PeriodType).Select(p => new WorkGridScheduleSummaryDetailResponse
{
PeriodType = p.Key,
Count = p.Count()
})
};
});
}
/// <inheritdoc />
public async Task<Pagination<WorkGridScheduleAttendancePageResponse>> GetAttendancePageAsync(WorkGridScheduleAttendancePageRequest request)
{
var jinkoCode = _currentUser.GetUserClaim(UserClaimConstants.JinkoCode);
var workGrid = await _dbContext.Queryable<WorkGridEntity>().Where(x => x.LeaderUserCode == jinkoCode).SingleAsync();
if (workGrid == null)
{
return new Pagination<WorkGridScheduleAttendancePageResponse>
{
PageIndex = request.PageIndex,
PageSize = request.PageSize,
Total = 0,
Data = null
};
}
var t = request.IsError.ToString().ToLower();
var filterString = $"{{\"IsError\":{t}";
return await _dbContext.Queryable<DeviceUploadLogEntity>()
.LeftJoin<DeviceEntity>((log, d) => log.DeviceUniqueNo == d.UniqueNo)
.LeftJoin<GainfoEntity>((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<WorkGridUserEntity>().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);
}
/// <inheritdoc />
public async Task ManuallyUploadAsync(List<long> logIds)
{
var sub = _currentUser.GetIntSubjectId();
const string filterString = "{\"IsError\":true";
var requests = await _dbContext.Queryable<DeviceUploadLogEntity>()
.LeftJoin<DeviceEntity>((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<DeviceUploadLogEntity>()
.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, $"重提上传考勤:{requests.Select(x => x.Id).Join(",")}");
}
public async Task<List<WorkGridScheduleMaximumConsecutiveDto>> MaximumConsecutiveUserCodesAsync(List<string> userCodes, int days, DateOnly date)
{
var p = userCodes.Select(x => $"@param{x}").Join(",");
var values = userCodes.ToDictionary(x => $"param{x}", x => x);
values.Add("dateBefore", date.AddDays(-1).ToString("yyyy-MM-dd"));
values.Add("dateAfter", date.AddDays(1).ToString("yyyy-MM-dd"));
values.Add("days", days.ToString());
var sql = $@"
WITH ranked_dates AS (SELECT UserCode,
date,
ROW_NUMBER() OVER (PARTITION BY UserCode ORDER BY date) AS rn
FROM WorkGridSchedules where IsDeleted = 0 and UserCode in ({p})
GROUP BY UserCode, date),
grouped AS (SELECT UserCode,
date,
DATE_SUB(date, INTERVAL rn DAY) AS grp
FROM ranked_dates),
streaks AS (SELECT UserCode,
grp,
COUNT(*) AS streak_days,
MIN(date) AS start_date,
MAX(date) AS end_date
FROM grouped
GROUP BY UserCode, grp
HAVING streak_days >= @days)
SELECT UserCode, streak_days as Count, start_date as Start, end_date as End
FROM streaks
where (@dateBefore >= start_date
and @dateBefore <= end_date)
or (@dateAfter >= start_date
and @dateAfter <= end_date);
";
var data = await _dbContext.Client.Ado.SqlQueryAsync<WorkGridScheduleMaximumConsecutiveDto>(sql, values);
return data;
}
}