416 lines
19 KiB
C#
416 lines
19 KiB
C#
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,
|
|
x.MaxConsecutiveLimitCount
|
|
})
|
|
.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, workGrid.MaxConsecutiveLimitCount, request.Date);
|
|
maximumConsecutiveDtos.ThrowIfExists(maximumConsecutiveDtos.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(",")}");
|
|
|
|
|
|
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>()
|
|
.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<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);
|
|
}
|
|
|
|
public async Task<Pagination<WorkGridScheduleAttendancePageResponse>> GetAllAttendancePageAsync(WorkGridScheduleAttendancePageRequest request)
|
|
{
|
|
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)
|
|
.Where(log => SqlFunc.Subqueryable<WorkGridUserEntity>()
|
|
.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);
|
|
}
|
|
|
|
|
|
/// <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, $"重提上传考勤{DateTime.Now:yyyy-MM-dd HH:mm:ss}:{items.Select(x => x.ZWORKCODE + $"打卡时间{x.RaiseDateTime:yyyy-MM-dd HH:mm:ss}").Join("\r\n")}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Task<List<WorkGridScheduleMaximumConsecutiveDto>> MaximumConsecutiveUserCodesAsync(List<string> userCodes, int days, DateOnly date)
|
|
{
|
|
var p = userCodes.Select(x => $"@param{x.Replace("-", "A_")}").Join(",");
|
|
var values = userCodes.ToDictionary(x => $"param{x.Replace("-", "A_")}", 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;
|
|
}
|
|
} |