refactor 优化命名和结构,注释

This commit is contained in:
2025-06-30 14:20:26 +08:00
parent 6fd0a2b1ef
commit 221d985850
24 changed files with 368 additions and 205 deletions

View File

@@ -52,18 +52,6 @@ public class HolidaysController : ControllerBase
[HttpGet("remote-ip")]
public string GetRemoteIp()
{
// 检查是否在Kubernetes环境中
var isKubernetes = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("KUBERNETES_SERVICE_HOST"));
if (isKubernetes)
{
var forwardedFor = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault();
if (!string.IsNullOrEmpty(forwardedFor))
{
return forwardedFor.Split(',').First().Trim();
}
}
return HttpContext.Connection.RemoteIpAddress?.ToString();
}
}

View File

@@ -1,6 +1,8 @@
using Microsoft.AspNetCore.Mvc;
using Dlhis.Basic.Common.Message.Requests;
using Dlhis.Basic.Common.Message.Services;
using Microsoft.AspNetCore.Mvc;
namespace Dlhis.Basic.Common.Message;
namespace Dlhis.Basic.Common.Message.Controller;
[Route("api/[controller]")]
@@ -16,7 +18,7 @@ public class MessagesController : ControllerBase
[HttpPost("dingtalk/workmessage")]
public async Task SendWorkMessageAsync(JinkoSendWorkMessageRequest request)
public async Task SendWorkMessageAsync(DingTalkWorkMessageRequest request)
{
await _jinkoApiService.SendWorkMessageAsync(request);
}

View File

@@ -1,6 +0,0 @@
namespace Dlhis.Basic.Common.Message;
public interface IJinkoApiService : ISingletonDependency
{
Task SendWorkMessageAsync(JinkoSendWorkMessageRequest request);
}

View File

@@ -1,14 +0,0 @@
namespace Dlhis.Basic.Common.Message;
public class JinkoSendWorkMessageRequest
{
/// <summary>
/// 发送内容
/// </summary>
public string Content { get; set; }
/// <summary>
/// 工号
/// </summary>
public List<string> WorkCodes { get; set; }
}

View File

@@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
namespace Dlhis.Basic.Common.Message.Requests;
public class DingTalkWorkMessageRequest
{
/// <summary>
/// 工号
/// </summary>
[MinLength(1, ErrorMessage = "{0}不能为空")]
public List<string> WorkCodes { get; set; }
/// <summary>
/// 发送内容
/// </summary>
[Required(ErrorMessage = "{0}不能为空")]
public string Content { get; set; }
}

View File

@@ -1,4 +1,4 @@
namespace Dlhis.Basic.Common.Message
namespace Dlhis.Basic.Common.Message.Responses
{
public class JinkoOaResponse<T>
{
@@ -12,7 +12,6 @@ namespace Dlhis.Basic.Common.Message
public T Data { get; set; }
}
public class JinkoOaInfo
{
public int Id { get; set; }

View File

@@ -0,0 +1,8 @@
using Dlhis.Basic.Common.Message.Requests;
namespace Dlhis.Basic.Common.Message.Services;
public interface IJinkoApiService : ISingletonDependency
{
Task SendWorkMessageAsync(DingTalkWorkMessageRequest request);
}

View File

@@ -1,11 +1,13 @@
using System.Text;
using Dlhis.Basic.Common.ApiLog.Services;
using Dlhis.Basic.Common.Message.Requests;
using Dlhis.Basic.Common.Message.Responses;
using Dlhis.Entity.Common.Enums;
using Kurisu.AspNetCore.Utils.Extensions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Dlhis.Basic.Common.Message;
namespace Dlhis.Basic.Common.Message.Services.Implements;
public class JinkoApiService : IJinkoApiService
{
@@ -24,49 +26,51 @@ public class JinkoApiService : IJinkoApiService
private async Task<string> GetTokenAsync()
{
var token = string.Empty;
if (string.IsNullOrEmpty(token))
if (!token.IsEmpty())
{
var httpClient = _httpClientFactory.CreateClient("jinko.httpclient");
var form = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "appkey", _appKey },
{ "appsecret", _appSecret }
});
//appkey=dingumelppews6rah7x9&appsecret=nsWfJTVjYesHVzrLiP-t1TQqMQvC1L_kZNfXfnIyzcXK4pCTfKprkjZcxAbsDR_l
var response = await httpClient.GetStringAsync($"/gettoken?{await form.ReadAsStringAsync()}");
var jObj = JObject.Parse(response);
(jObj.Value<string>("errcode") == "0").ThrowIfFalse(jObj.Value<string>("errmsg"));
token = jObj.Value<string>("access_token");
return token;
}
var httpClient = _httpClientFactory.CreateClient("jinko.httpclient");
var form = new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "appkey", _appKey },
{ "appsecret", _appSecret }
});
//eg: appkey=dingumelppews6rah7x9&appsecret=nsWfJTVjYesHVzrLiP-t1TQqMQvC1L_kZNfXfnIyzcXK4pCTfKprkjZcxAbsDR_l
var response = await httpClient.GetStringAsync($"/gettoken?{await form.ReadAsStringAsync()}");
var jObj = JObject.Parse(response);
(jObj.Value<string>("errcode") == "0").ThrowIfFalse(jObj.Value<string>("errmsg"));
token = jObj.Value<string>("access_token");
return token;
}
public async Task SendWorkMessageAsync(JinkoSendWorkMessageRequest request)
public async Task SendWorkMessageAsync(DingTalkWorkMessageRequest request)
{
request.Content.IsEmpty().ThrowIfTrue("发送内容不能为空");
var token = await GetTokenAsync();
var users = await GetJinkoOaInfoByWorkCodes(request.WorkCodes);
var userStringIds = string.Join(",", users.Select(x => x.Id));
(userStringIds.Length > 100).ThrowIfTrue("超出消息接受人数,length > 100");
var userIds = users.Select(x => x.Id).Join(",");
(userIds.Length > 100).ThrowIfTrue("超出消息接受人数,length > 100");
var body = new
{
agent_id = _agentId,
userid_list = userStringIds,
userid_list = userIds,
msg = new
{
msgtype = "text",
text = new
{
content = request.Content
},
},
}
}
}.ToJson();
var httpClient = _httpClientFactory.CreateClient("jinko.httpclient");
@@ -76,11 +80,8 @@ public class JinkoApiService : IJinkoApiService
var response = await httpClient.PostAsync(url, content);
var jsonResponse = await response.Content.ReadAsStringAsync();
//_logger.LogInformation("请求参数:{Request} \r\n 钉钉请求参数:{DingTalkRequest} \r\n 钉钉响应:{DingTalkResponse}", JsonConvert.SerializeObject(request), body, jsonResponse);
var jObj = JObject.Parse(jsonResponse);
(jObj.Value<string>("errcode") == "0").ThrowIfFalse(jObj.Value<string>("errmsg"));
var jObject = JObject.Parse(jsonResponse);
(jObject.Value<string>("errcode") == "0").ThrowIfFalse(jObject.Value<string>("errmsg"));
using var scope = _serviceProvider.CreateScope();
var ol = scope.ServiceProvider.GetService<IOperateLog>();

View File

@@ -196,7 +196,7 @@ public class DevicePermissionAnalyzeService : IDevicePermissionAnalyzeService
AssignTypeEnumType.Group => analyzePack.UserGainfoGroupCodes.Select(x => x.ToString()),
AssignTypeEnumType.Department => RecursionHelper.HandlerDepartmentPreviousCodes(analyzePack.UserDepartment, departments).Select(x => analyzePack.UserFactory + ":" + x).ToList(),
AssignTypeEnumType.Tenant => new List<string> { analyzePack.UserFactory },
AssignTypeEnumType.Role => throw new ArgumentOutOfRangeException(),
AssignTypeEnumType.Role => throw new NotSupportedException("不支持角色分配"),
_ => throw new ArgumentOutOfRangeException()
};

View File

@@ -517,6 +517,7 @@ public class DevicePermissionService : IDevicePermissionService
{
var now = DateTime.Now;
// 获取与指定设备关联的分组编码
var groupCodes = await _dbContext.Queryable<DeviceGroupEntity>()
.Where(x => SqlFunc.Subqueryable<DeviceGroupRelationshipEntity>()
.Where(p => p.GroupCode == x.Code)
@@ -525,25 +526,30 @@ public class DevicePermissionService : IDevicePermissionService
.Select(x => x.Code)
.ToListAsync();
// 获取与分组相关联的区域编码及类型
var areaCodes = await _dbContext.Queryable<AppointmentAreaAssignmentEntity>()
.LeftJoin<AppointmentAreaEntity>((x, y) => x.CustomCode == y.CustomCode)
.Where(x => groupCodes.Contains(x.DeviceGroupCode))
.Select((x, y) => new { x.CustomCode, y.Type })
.ToListAsync();
// 根据区域类型筛选出门区和厂区的区域编码
var doorAreaCodes = areaCodes.Where(p => p.Type == AppointmentAreaEnumType.Door).Select(p => p.CustomCode).ToList();
var workplaceAreaCodes = areaCodes.Where(p => p.Type == AppointmentAreaEnumType.Workplace).Select(p => p.CustomCode).ToList();
// 查询当前时间段内已到访的访客预约明细
var details = await _dbContext.Queryable<VisitorAppointmentDetailEntity>()
.Where(x => (x.IsEnterDoorArea && now >= x.DoorStart && now <= x.DoorEnd && x.DoorStatus == VisitorAppointmentDetailStatusEnumType.Arrived)
|| x.IsEnterWorkplace && now >= x.WorkplaceStart && now <= x.WorkplaceEnd && x.WorkplaceStatus == VisitorAppointmentDetailStatusEnumType.Arrived)
.ToListAsync();
//到访门区或厂区的访客编号列表
return details.Where(x =>
(x.IsEnterDoorArea && doorAreaCodes.Contains(x.ApplyDoorArea))
|| (x.IsEnterWorkplace && workplaceAreaCodes.Any(p => x.ApplyWorkplaceAreas.Contains(p)))
)
.Select(x => x.UserNo)
.Distinct()
.ToList();
}
}

View File

@@ -1,4 +1,5 @@
using Dlhis.Basic.Common.Message;
using Dlhis.Basic.Common.Message.Requests;
using Dlhis.Basic.Common.Message.Services;
using Dlhis.Basic.Organization.Comparers;
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Basic.Enums;
@@ -186,7 +187,7 @@ public class GainfoUpdateService : IGainfoUpdateService
});
}
private GainfoEntity GainfoSapToGainfo(GainfoSapEntity item)
private static GainfoEntity GainfoSapToGainfo(GainfoSapEntity item)
{
var info = new GainfoEntity
{
@@ -319,20 +320,24 @@ public class GainfoUpdateService : IGainfoUpdateService
{
var resignationDate = DateOnly.FromDateTime(gainfo.ModifiedTime);
var workGridSchedules = await _dbContext.Queryable<WorkGridScheduleEntity>().Where(x => x.UserCode == gainfo.Code)
var workGridSchedules = await _dbContext.Queryable<WorkGridScheduleEntity>()
.Where(x => x.UserCode == gainfo.Code)
.Where(x => x.Date >= resignationDate)
.ToListAsync();
if (workGridSchedules.Any())
{
var dates = workGridSchedules.Select(x => $"{x.Date.ToString("yyyy年-MM月-dd日")}的{x.PeriodType.GetDescription()}").Join(",");
var workGrid = await _dbContext.Queryable<WorkGridEntity>().Where(x => x.Code == workGridUser.WorkGridCode).Select(x => new
{
x.LeaderUserCode, x.Name
}).SingleAsync();
var content = $"{workGrid.Name}的网格长,你好,检测到你网格中 {gainfo.Name}({gainfo.Code}),因发生离职或者异动,现已经删除该员工{dates}的排班信息,请前往系统处理!";
var workGrid = await _dbContext.Queryable<WorkGridEntity>()
.Where(x => x.Code == workGridUser.WorkGridCode)
.Select(x => new
{
x.LeaderUserCode, x.Name
}).SingleAsync();
await _dingTalkApi.SendWorkMessageAsync(new JinkoSendWorkMessageRequest
var content = $"{workGrid.Name}的网格长,你好,检测到你网格中 {gainfo.Name}({gainfo.Code}),因发生离职或者异动,现已经删除该员工{dates}的排班信息,请前往系统处理!";
await _dingTalkApi.SendWorkMessageAsync(new DingTalkWorkMessageRequest
{
Content = content,
WorkCodes = new List<string> { workGrid.LeaderUserCode }
@@ -364,30 +369,39 @@ public class GainfoUpdateService : IGainfoUpdateService
await _dbContext.Client.Insertable(gainfo).AS("GainfoLeave").ExecuteCommandAsync();
}
await _dbContext.Deleteable<GainfoGroupRelationshipEntity>().Where(x => x.UserNo == gainfo.No).ExecuteCommandAsync();
await _dbContext.Deleteable<DevicePermissionEntity>().Where(x => x.AssignType == AssignTypeEnumType.User && x.AssignValue == gainfo.No).ExecuteCommandAsync();
await _dbContext.Deleteable<GainfoGroupRelationshipEntity>()
.Where(x => x.UserNo == gainfo.No)
.ExecuteCommandAsync();
await _dbContext.Deleteable<DevicePermissionEntity>()
.Where(x => x.AssignType == AssignTypeEnumType.User && x.AssignValue == gainfo.No)
.ExecuteCommandAsync();
var workGridUser = await _dbContext.Queryable<WorkGridUserEntity>().Where(x => x.UserCode == gainfo.Code).SingleAsync();
var workGridUser = await _dbContext.Queryable<WorkGridUserEntity>()
.Where(x => x.UserCode == gainfo.Code)
.SingleAsync();
if (workGridUser != null)
{
try
{
var resignationDate = DateOnly.FromDateTime(gainfo.ResignationDate!.Value);
var workGridSchedules = await _dbContext.Queryable<WorkGridScheduleEntity>().Where(x => x.UserCode == gainfo.Code)
var workGridSchedules = await _dbContext.Queryable<WorkGridScheduleEntity>()
.Where(x => x.UserCode == gainfo.Code)
.Where(x => x.Date >= resignationDate)
.ToListAsync();
if (workGridSchedules.Any())
{
var dates = workGridSchedules.Select(x => $"{x.Date.ToString("yyyy年-MM月-dd日")}的{x.PeriodType.GetDescription()}").Join(",");
var workGrid = await _dbContext.Queryable<WorkGridEntity>().Where(x => x.Code == workGridUser.WorkGridCode).Select(x => new
{
x.LeaderUserCode, x.Name
}).SingleAsync();
var workGrid = await _dbContext.Queryable<WorkGridEntity>()
.Where(x => x.Code == workGridUser.WorkGridCode)
.Select(x => new
{
x.LeaderUserCode, x.Name
}).SingleAsync();
var content = $"{workGrid.Name}的网格长,你好,检测到你网格中 {gainfo.Name}({gainfo.Code}),因发生离职或者异动,现已经删除该员工{dates}的排班信息,请前往系统处理!";
await _dingTalkApi.SendWorkMessageAsync(new JinkoSendWorkMessageRequest
await _dingTalkApi.SendWorkMessageAsync(new DingTalkWorkMessageRequest
{
Content = content,
WorkCodes = new List<string> { workGrid.LeaderUserCode }

View File

@@ -7,9 +7,7 @@ class Program
{
static void Main(string[] args)
{
KurisuHost.Builder(args)
//.EnableAppSettingsReload()
.RunKurisu<Startup>();
KurisuHost.Builder(args).RunKurisu<Startup>();
}
}

View File

@@ -0,0 +1,15 @@
using Dlhis.Basic.WorkGrids.Response;
namespace Dlhis.Basic.WorkGrids.Repositories;
public interface IWorkGridScheduleRepository : IScopeDependency
{
/// <summary>
/// 获取用户在指定天数内最大连续排班信息
/// </summary>
/// <param name="userCodes">用户编码列表</param>
/// <param name="days">天数</param>s
/// <param name="date">日期</param>
/// <returns>最大连续排班信息列表</returns>
Task<List<WorkGridScheduleMaximumConsecutiveDto>> MaximumConsecutiveUserCodesAsync(List<string> userCodes, int days, DateOnly date);
}

View File

@@ -0,0 +1,62 @@
using Dlhis.Basic.WorkGrids.Response;
using Kurisu.AspNetCore.DataAccess.SqlSugar.Services;
using Kurisu.AspNetCore.Utils.Extensions;
namespace Dlhis.Basic.WorkGrids.Repositories.Implements;
public class WorkGridScheduleRepository : IWorkGridScheduleRepository
{
private readonly IDbContext _dbContext;
public WorkGridScheduleRepository(IDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<List<WorkGridScheduleMaximumConsecutiveDto>> MaximumConsecutiveUserCodesAsync(List<string> userCodes, int days, DateOnly date)
{
var userCodesStr = userCodes.Select(x => $"@param{x.Replace("-", "A_")}").Join(",");
var parameters = userCodes.ToDictionary(x => $"param{x.Replace("-", "A_")}", x => x);
parameters.Add("dateBefore", date.AddDays(-1).ToString("yyyy-MM-dd"));
parameters.Add("dateAfter", date.AddDays(1).ToString("yyyy-MM-dd"));
parameters.Add("days", days.ToString());
/* 1.用户按日期排序,标记序号
* 2.将日期减去序号,需要得出结果一致 == 数据连续 == 连续排班
* 3.根据减去序号后的日期分组,统计连续排班的天数
* 4.数据过滤,取当前排班的前后一天
*
* 根据以上结果,获取连续排班天数大于等于指定天数的用户
*/
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 ({userCodesStr})
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)
";
return await _dbContext.Client.Ado.SqlQueryAsync<WorkGridScheduleMaximumConsecutiveDto>(sql, parameters);
}
}

View File

@@ -5,7 +5,7 @@ namespace Dlhis.Basic.WorkGrids.Request;
public class WorkGridPageRequest : PageDto
{
/// <summary>
/// 网格code,不传则取我的网格
/// 网格code,默认为当前用户网格
/// </summary>
public Guid? WorkGridCode { get; set; }

View File

@@ -18,7 +18,7 @@ public class WorkGridScheduleSaveRequest : IValidatableObject
/// <summary>
/// 班次
/// </summary>
[EnumDataType(typeof(WorkGridTimePeriodEnumType))]
[EnumDataType(typeof(WorkGridTimePeriodEnumType), ErrorMessage = "班次类型错误")]
public WorkGridTimePeriodEnumType PeriodType { get; set; }
/// <summary>
@@ -27,7 +27,7 @@ public class WorkGridScheduleSaveRequest : IValidatableObject
[MinLength(1, ErrorMessage = "人员不可为空")]
public List<string> UserCodes { get; set; }
/// <inheritdoc />
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var now = DateTime.Now;

View File

@@ -1,5 +1,11 @@
using Dlhis.Basic.WorkGrids.Repositories;
namespace Dlhis.Basic.WorkGrids.Response;
/// <summary>
/// 连续排班信息
/// <see cref="IWorkGridScheduleRepository.MaximumConsecutiveUserCodesAsync"/>的方法返回类型
/// </summary>
public class WorkGridScheduleMaximumConsecutiveDto
{
public string UserCode { get; set; }

View File

@@ -10,21 +10,60 @@ namespace Dlhis.Basic.WorkGrids.Services;
/// </summary>
public interface IWorkGridScheduleService : IScopeDependency
{
/// <summary>
/// 保存网格排班信息
/// </summary>
/// <param name="request">排班保存请求参数</param>
Task SaveAsync(WorkGridScheduleSaveRequest request);
/// <summary>
/// 移除网格排班信息
/// </summary>
/// <param name="request">排班移除请求参数</param>
Task RemoveAsync(WorkGridScheduleSaveRequest request);
/// <summary>
/// 获取指定网格、日期和时段的排班列表
/// </summary>
/// <param name="workGridCode">网格编码</param>
/// <param name="date">日期</param>
/// <param name="periodType">时段类型</param>
/// <returns>排班列表</returns>
Task<List<WorkGridScheduleResponse>> GetListAsync(Guid workGridCode, DateOnly date, WorkGridTimePeriodEnumType? periodType);
/// <summary>
/// 获取指定网格和日期范围的排班汇总列表
/// </summary>
/// <param name="workGridCode">网格编码</param>
/// <param name="start">开始日期</param>
/// <param name="end">结束日期</param>
/// <returns>排班汇总列表</returns>
Task<IEnumerable<WorkGridScheduleSummaryResponse>> GetListSummaryAsync(Guid workGridCode, DateOnly start, DateOnly end);
/// <summary>
/// 获取排班考勤分页数据
/// </summary>
/// <param name="request">考勤分页请求参数</param>
/// <returns>排班考勤分页数据</returns>
Task<Pagination<WorkGridScheduleAttendancePageResponse>> GetAttendancePageAsync(WorkGridScheduleAttendancePageRequest request);
/// <summary>
/// 获取所有排班考勤分页数据
/// </summary>
/// <param name="request">考勤分页请求参数</param>
/// <returns>排班考勤分页数据</returns>
Task<Pagination<WorkGridScheduleAttendancePageResponse>> GetAllAttendancePageAsync(WorkGridScheduleAttendancePageRequest request);
/// <summary>
/// 手动上传考勤日志
/// </summary>
/// <param name="logIds">考勤日志ID列表</param>
Task ManuallyUploadAsync(List<long> logIds);
Task<List<WorkGridScheduleMaximumConsecutiveDto>> MaximumConsecutiveUserCodesAsync(List<string> userCodes, int days, DateOnly date);
/// <summary>
/// 测试接口
/// </summary>
/// <param name="request">排班保存请求参数</param>
/// <returns>测试结果</returns>
Task<object> TestAsync(WorkGridScheduleSaveRequest request);
}

View File

@@ -1,5 +1,7 @@
using Dlhis.Basic.Common.ApiLog.Services;
using Dlhis.Basic.Common.Message;
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;
@@ -27,6 +29,7 @@ public class WorkGridScheduleService : IWorkGridScheduleService
private readonly IOperateLog _operateLog;
private readonly ILogger<WorkGridScheduleService> _logger;
private readonly IJinkoApiService _jinkoApiService;
private readonly IWorkGridScheduleRepository _workGridScheduleRepository;
private readonly ICurrentUser _currentUser;
public WorkGridScheduleService(IDbContext dbContext,
@@ -34,6 +37,7 @@ public class WorkGridScheduleService : IWorkGridScheduleService
IOperateLog operateLog,
ILogger<WorkGridScheduleService> logger,
IJinkoApiService jinkoApiService,
IWorkGridScheduleRepository workGridScheduleRepository,
ICurrentUser currentUser)
{
_dbContext = dbContext;
@@ -41,6 +45,7 @@ public class WorkGridScheduleService : IWorkGridScheduleService
_operateLog = operateLog;
_logger = logger;
_jinkoApiService = jinkoApiService;
_workGridScheduleRepository = workGridScheduleRepository;
_currentUser = currentUser;
}
@@ -61,7 +66,11 @@ public class WorkGridScheduleService : IWorkGridScheduleService
.SingleAsync();
workGrid.ThrowIfNull("工作网格不存在");
var timeSetting = await _dbContext.Queryable<WorkGridTimePeriodEntity>().Where(x => x.Type == request.PeriodType).Select(x => new { x.Start, x.Before }).SingleAsync();
var timeSetting = await _dbContext.Queryable<WorkGridTimePeriodEntity>()
.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)));
@@ -103,8 +112,8 @@ public class WorkGridScheduleService : IWorkGridScheduleService
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 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
{
@@ -120,12 +129,15 @@ public class WorkGridScheduleService : IWorkGridScheduleService
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)
var codeAndNameList = await _dbContext.Queryable<GainfoEntity>().Where(x => request.UserCodes.Contains(x.Code))
.Select(x => new { x.Code, x.Name })
.ToListAsync();
foreach (var gainfo in codeAndNameList)
{
try
{
await _jinkoApiService.SendWorkMessageAsync(new JinkoSendWorkMessageRequest
await _jinkoApiService.SendWorkMessageAsync(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<string> { gainfo.Code }
@@ -210,7 +222,7 @@ public class WorkGridScheduleService : IWorkGridScheduleService
{
try
{
await _jinkoApiService.SendWorkMessageAsync(new JinkoSendWorkMessageRequest
await _jinkoApiService.SendWorkMessageAsync(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<string> { gainfo.Code }
@@ -432,44 +444,4 @@ public class WorkGridScheduleService : IWorkGridScheduleService
}
}
}
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;
}
}

View File

@@ -1,6 +1,8 @@
using Dlhis.Basic.Common.ApiLog.Services;
using Dlhis.Basic.WorkGrids.Repositories;
using Dlhis.Basic.WorkGrids.Request;
using Dlhis.Basic.WorkGrids.Response;
using Dlhis.Basic.WorkGrids.Validations;
using Dlhis.Constants;
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Basic.Enums;
@@ -19,17 +21,20 @@ public class WorkGridService : IWorkGridService
{
private readonly IDbContext _dbContext;
private readonly ICurrentUser _currentUser;
private readonly IWorkGridScheduleService _workGridScheduleService;
private readonly IWorkGridValidateService _workGridValidateService;
private readonly IWorkGridScheduleRepository _workGridScheduleRepository;
private readonly IOperateLog _operateLog;
public WorkGridService(IDbContext dbContext,
ICurrentUser currentUser,
IWorkGridScheduleService workGridScheduleService,
IWorkGridValidateService workGridValidateService,
IWorkGridScheduleRepository workGridScheduleRepository,
IOperateLog operateLog)
{
_dbContext = dbContext;
_currentUser = currentUser;
_workGridScheduleService = workGridScheduleService;
_workGridValidateService = workGridValidateService;
_workGridScheduleRepository = workGridScheduleRepository;
_operateLog = operateLog;
}
@@ -38,7 +43,7 @@ public class WorkGridService : IWorkGridService
{
var factory = _currentUser.GetStringTenantId();
var r = await _dbContext.Queryable<GainfoEntity>()
var pagination = await _dbContext.Queryable<GainfoEntity>()
.LeftJoin<DepartmentEntity>((x, dept) => x.DepartmentNo == dept.No)
.LeftJoin<WorkGridUserEntity>((x, dept, wgu) => x.Code == wgu.UserCode)
.LeftJoin<WorkGridEntity>((x, dept, wgu, wg) => wgu.WorkGridCode == wg.Code)
@@ -59,50 +64,29 @@ public class WorkGridService : IWorkGridService
})
.ToPageListAsync(request);
var userCodes = r.Data.Select(x => x.Code).ToList();
if (userCodes.Any())
var userCodeList = pagination.Data.Select(x => x.Code).ToList();
if (userCodeList.Any())
{
var maximumConsecutiveUserCodes = await _workGridScheduleService.MaximumConsecutiveUserCodesAsync(userCodes, 1, DateOnly.FromDateTime(request.Date));
var maximumConsecutiveUserCodeList = await _workGridScheduleRepository.MaximumConsecutiveUserCodesAsync(userCodeList, 1, DateOnly.FromDateTime(request.Date));
foreach (var item in r.Data)
foreach (var item in pagination.Data)
{
item.MaximumConsecutiveSchedules = maximumConsecutiveUserCodes.FirstOrDefault(x => x.UserCode == item.Code);
item.MaximumConsecutiveSchedules = maximumConsecutiveUserCodeList.FirstOrDefault(x => x.UserCode == item.Code);
}
}
return r;
return pagination;
}
/// <inheritdoc />
public async Task SaveAsync(List<WorkGridSaveRequest> requests)
{
var factory = _currentUser.GetStringTenantId();
await _workGridValidateService.ValidateSaveAsync(requests);
var insertGrids = new List<string>();
var updateGrids = new List<string>();
var leaderUserCodes = requests.Select(x => x.LeaderUserCode).Distinct().ToList();
var existsUserInfos = await _dbContext.Queryable<GainfoEntity>()
.Where(x => leaderUserCodes.Contains(x.Code))
.Select(x => new { x.Code, x.Factory })
.ToListAsync();
var excludeCodes = existsUserInfos.Where(x => x.Factory != factory).Select(x => x.Code).ToList();
excludeCodes.ThrowIfExists($"工号:{excludeCodes.Join(",")}不属于当前厂区");
var existsUserCodes = existsUserInfos.Select(x => x.Code).ToList();
var notExistsUserCodes = leaderUserCodes.Except(existsUserCodes).ToList();
notExistsUserCodes.ThrowIfExists($"工号:{notExistsUserCodes.Join(",")}不存在");
foreach (var request in requests)
{
if (await _dbContext.Queryable<WorkGridEntity>().Where(x => x.Name == request.Name)
.WhereIF(request.Id.HasValue, x => x.Id != request.Id)
.AnyAsync())
{
true.ThrowIfTrue($"网格区域名称[{request.Name}]重复");
}
if (!request.Id.HasValue)
{
var exists = await _dbContext.Queryable<WorkGridEntity>()
@@ -145,7 +129,6 @@ public class WorkGridService : IWorkGridService
await _operateLog.LogAsync(OperateLogType.WorkGrid, "修改网格:" + updateGrids.Join("\r\n"));
}
/// <inheritdoc />
public async Task<Pagination<WorkGridPageResponse>> GetPageAsync(WorkGridPageRequest request)
{
var role = _currentUser.GetRole();
@@ -190,7 +173,6 @@ public class WorkGridService : IWorkGridService
.ToPageListAsync(request);
}
/// <inheritdoc />
public async Task ChangeAsync(string from, string to)
{
if (await _dbContext.Queryable<WorkGridEntity>().Where(x => x.LeaderUserCode == to).AnyAsync())
@@ -216,7 +198,6 @@ public class WorkGridService : IWorkGridService
await _operateLog.LogAsync(OperateLogType.WorkGrid, $"网格({workGrid.Name})修改负责人{from}->{to}");
}
/// <inheritdoc />
public async Task<WorkGridUserPageResponse> GetMyWorkGridAsync(WorkGridPageRequest request)
{
var currentUserCode = _currentUser.GetUserClaim(UserClaimConstants.JinkoCode);
@@ -253,11 +234,11 @@ public class WorkGridService : IWorkGridService
var userCodes = myWorkGrid.UserInfos.Select(x => x.Code).ToList();
if (userCodes.Any())
{
var maximumConsecutiveUserCodes = await _workGridScheduleService.MaximumConsecutiveUserCodesAsync(userCodes, 1, DateOnly.FromDateTime(request.Date));
var maximumConsecutiveUserCodeList = await _workGridScheduleRepository.MaximumConsecutiveUserCodesAsync(userCodes, 1, DateOnly.FromDateTime(request.Date));
foreach (var item in myWorkGrid.UserInfos)
{
item.MaximumConsecutiveSchedules = maximumConsecutiveUserCodes.FirstOrDefault(x => x.UserCode == item.Code);
item.MaximumConsecutiveSchedules = maximumConsecutiveUserCodeList.FirstOrDefault(x => x.UserCode == item.Code);
}
}
}
@@ -265,7 +246,6 @@ public class WorkGridService : IWorkGridService
return myWorkGrid;
}
/// <inheritdoc />
public async Task AssignAsync(WorkGridUserAssignRequest request)
{
var currentFactory = _currentUser.GetStringTenantId();

View File

@@ -0,0 +1,8 @@
using Dlhis.Basic.WorkGrids.Request;
namespace Dlhis.Basic.WorkGrids.Validations;
public interface IWorkGridValidateService : IScopeDependency
{
Task ValidateSaveAsync(List<WorkGridSaveRequest> requests);
}

View File

@@ -0,0 +1,74 @@
using Dlhis.Basic.WorkGrids.Request;
using Dlhis.Entity.Basic.Entity;
using Kurisu.AspNetCore.Authentication.Abstractions;
using Kurisu.AspNetCore.DataAccess.SqlSugar.Services;
using Kurisu.AspNetCore.Utils.Extensions;
namespace Dlhis.Basic.WorkGrids.Validations.Implements
{
public class WorkGridValidateService : IWorkGridValidateService
{
private readonly ICurrentUser _currentUser;
private readonly IDbContext _dbContext;
public WorkGridValidateService(ICurrentUser currentUser, IDbContext dbContext)
{
_currentUser = currentUser;
_dbContext = dbContext;
}
/// <summary>
/// 验证保存请求
/// </summary>
/// <param name="requests"></param>
public async Task ValidateSaveAsync(List<WorkGridSaveRequest> requests)
{
var duplicateUserCodes = requests.GroupBy(x => x.LeaderUserCode)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();
duplicateUserCodes.ThrowIfExists($"请求中存在重复工号:{duplicateUserCodes.Join(",")}");
var duplicateNames = requests.GroupBy(x => x.Name)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();
duplicateNames.ThrowIfExists($"请求中存在重复网格名称:{duplicateNames.Join(",")}");
var factory = _currentUser.GetStringTenantId();
var leaderUserCodes = requests.Select(x => x.LeaderUserCode).Distinct().ToList();
var userCodeWithFactories = await _dbContext.Queryable<GainfoEntity>()
.Where(x => leaderUserCodes.Contains(x.Code))
.Select(x => new { x.Code, x.Factory })
.ToListAsync();
//检查工号是否存在
var existsUserCodes = userCodeWithFactories.Select(x => x.Code).ToList();
var notExistsUserCodes = leaderUserCodes.Except(existsUserCodes).ToList();
notExistsUserCodes.ThrowIfExists($"工号:{notExistsUserCodes.Join(",")}不存在");
//检查负责人工号是否存在
var excludeCodes = userCodeWithFactories
.Where(x => x.Factory != factory)
.Select(x => x.Code)
.ToList();
excludeCodes.ThrowIfExists($"工号:{excludeCodes.Join(",")}不属于当前厂区");
//检查网格名称是否重复
var where = LinqExpressionExtensions.False<WorkGridEntity>();
foreach (var req in requests)
{
where = req.Id.HasValue
? where.Or(x => x.Name == req.Name && x.Id != req.Id.Value)
: where.Or(x => x.Name == req.Name);
}
var existsNames = await _dbContext.Queryable<WorkGridEntity>().Where(where).Select(x => x.Name).ToListAsync();
if (existsNames.Any())
{
true.ThrowIfTrue($"网格区域名称[{existsNames.Join(",")}]重复");
}
}
}
}

View File

@@ -27,7 +27,7 @@
},
"SwaggerOptions": {
"Enable": true,
"Authority": "http://localhost:5000",
"Authority": "https://park.yaens.com/oauth2",
"ClientId": "98958aa485d38412",
"ClientSecret": "52589e011f02fe19",
"Scopes": {
@@ -37,7 +37,7 @@
}
},
"IdentityServerOptions": {
"Authority": "http://localhost:5000",
"Authority": "https://park.yaens.com/oauth2",
"RequireHttpsMetadata": false,
"Issuer": "identity.isawesome.cn",
"Audience": "",

View File

@@ -34,9 +34,9 @@
},
"SwaggerOptions": {
"Enable": true,
"Authority": "http://identity.oauth.svc.cluster.local",
"ClientId": "4fb184324eac4180",
"ClientSecret": "8ac8f235152f1524",
"Authority": "https://192.168.199.124/oauth2",
"ClientId": "98958aa485d38412",
"ClientSecret": "52589e011f02fe19",
"Scopes": {
"openid": "openid",
"profile": "profile",
@@ -47,10 +47,10 @@
"ConnectionString": "192.168.199.124:6379,defaultDatabase=1,password=dlhis123"
},
"IdentityServerOptions": {
"Authority": "http://identity.oauth.svc.cluster.local",
"Authority": "https://192.168.199.124/oauth2",
"RequireHttpsMetadata": false,
"Issuer": "identity.isawesome.cn",
"Audience": "",
"Audience": "yikatong",
"Pat": {
"Enable": false,
"Scheme": "token",
@@ -60,8 +60,8 @@
},
"MinIO": {
"Endpoint": "http://192.168.199.124:9000",
"AccessKey": "bmQOlK0COe5nL56kzGd3",
"SecretKey": "Udc5uBrGcT2yukfGXQtHKpqLQ27L1FmbrLyYunBr",
"AccessKey": "7z9Aeat9givpNbnAt36T",
"SecretKey": "tW3WqS1wQZMbIrXKZokyxfF0CDL4vqrthTUNVMdB",
"DefaultBucket": "apk"
},
"Serilog": {
@@ -75,24 +75,17 @@
},
"WriteTo": [
{
"Name": "Async",
"Name": "Console",
"Args": {
"configure": [
{
"Name": "Console",
"Args": {
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "MySQL",
"Args": {
"connectionString": "server=192.168.199.124;port=3306;userid=root;password=dlhis123;database=log;charset=utf8mb4;",
"tableName": "Logs",
"autoCreateSqlTable": true
}
}
]
"outputTemplate": "[{Timestamp:HH:mm:ss} {Level:u3}] [{SourceContext}] {Message:lj}{NewLine}{Exception}"
}
},
{
"Name": "MySQL",
"Args": {
"connectionString": "server=192.168.199.124;port=3306;userid=root;password=dlhis123;database=log;charset=utf8mb4;",
"tableName": "Logs",
"autoCreateSqlTable": true
}
}
]