diff --git a/Src/Dlhis.Basic/Common/Holiday/Controller/HolidaysController.cs b/Src/Dlhis.Basic/Common/Holiday/Controller/HolidaysController.cs index 75cd31b0..eb6f589b 100644 --- a/Src/Dlhis.Basic/Common/Holiday/Controller/HolidaysController.cs +++ b/Src/Dlhis.Basic/Common/Holiday/Controller/HolidaysController.cs @@ -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(); } } \ No newline at end of file diff --git a/Src/Dlhis.Basic/Common/Message/MessagesController.cs b/Src/Dlhis.Basic/Common/Message/Controller/MessagesController.cs similarity index 61% rename from Src/Dlhis.Basic/Common/Message/MessagesController.cs rename to Src/Dlhis.Basic/Common/Message/Controller/MessagesController.cs index ffb95678..dd3ce314 100644 --- a/Src/Dlhis.Basic/Common/Message/MessagesController.cs +++ b/Src/Dlhis.Basic/Common/Message/Controller/MessagesController.cs @@ -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); } diff --git a/Src/Dlhis.Basic/Common/Message/IJinkoApiService.cs b/Src/Dlhis.Basic/Common/Message/IJinkoApiService.cs deleted file mode 100644 index 7b1f9485..00000000 --- a/Src/Dlhis.Basic/Common/Message/IJinkoApiService.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Dlhis.Basic.Common.Message; - -public interface IJinkoApiService : ISingletonDependency -{ - Task SendWorkMessageAsync(JinkoSendWorkMessageRequest request); -} \ No newline at end of file diff --git a/Src/Dlhis.Basic/Common/Message/JinkoSendWorkMessageRequest.cs b/Src/Dlhis.Basic/Common/Message/JinkoSendWorkMessageRequest.cs deleted file mode 100644 index 5353e28a..00000000 --- a/Src/Dlhis.Basic/Common/Message/JinkoSendWorkMessageRequest.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Dlhis.Basic.Common.Message; - -public class JinkoSendWorkMessageRequest -{ - /// - /// 发送内容 - /// - public string Content { get; set; } - - /// - /// 工号 - /// - public List WorkCodes { get; set; } -} \ No newline at end of file diff --git a/Src/Dlhis.Basic/Common/Message/Requests/DingTalkWorkMessageRequest.cs b/Src/Dlhis.Basic/Common/Message/Requests/DingTalkWorkMessageRequest.cs new file mode 100644 index 00000000..ce96b358 --- /dev/null +++ b/Src/Dlhis.Basic/Common/Message/Requests/DingTalkWorkMessageRequest.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Basic.Common.Message.Requests; + +public class DingTalkWorkMessageRequest +{ + /// + /// 工号 + /// + [MinLength(1, ErrorMessage = "{0}不能为空")] + public List WorkCodes { get; set; } + + /// + /// 发送内容 + /// + [Required(ErrorMessage = "{0}不能为空")] + public string Content { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/Common/Message/JinkoOaResponse.cs b/Src/Dlhis.Basic/Common/Message/Responses/JinkoOaResponse.cs similarity index 89% rename from Src/Dlhis.Basic/Common/Message/JinkoOaResponse.cs rename to Src/Dlhis.Basic/Common/Message/Responses/JinkoOaResponse.cs index 301c100f..8f4fb9ed 100644 --- a/Src/Dlhis.Basic/Common/Message/JinkoOaResponse.cs +++ b/Src/Dlhis.Basic/Common/Message/Responses/JinkoOaResponse.cs @@ -1,4 +1,4 @@ -namespace Dlhis.Basic.Common.Message +namespace Dlhis.Basic.Common.Message.Responses { public class JinkoOaResponse { @@ -12,11 +12,10 @@ namespace Dlhis.Basic.Common.Message public T Data { get; set; } } - public class JinkoOaInfo { public int Id { get; set; } public string Email { get; set; } public string Lastname { get; set; } } -} +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/Common/Message/Services/IJinkoApiService.cs b/Src/Dlhis.Basic/Common/Message/Services/IJinkoApiService.cs new file mode 100644 index 00000000..2efc0314 --- /dev/null +++ b/Src/Dlhis.Basic/Common/Message/Services/IJinkoApiService.cs @@ -0,0 +1,8 @@ +using Dlhis.Basic.Common.Message.Requests; + +namespace Dlhis.Basic.Common.Message.Services; + +public interface IJinkoApiService : ISingletonDependency +{ + Task SendWorkMessageAsync(DingTalkWorkMessageRequest request); +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/Common/Message/JinkoApiService.cs b/Src/Dlhis.Basic/Common/Message/Services/Implements/JinkoApiService.cs similarity index 69% rename from Src/Dlhis.Basic/Common/Message/JinkoApiService.cs rename to Src/Dlhis.Basic/Common/Message/Services/Implements/JinkoApiService.cs index 07390ff7..d8bec022 100644 --- a/Src/Dlhis.Basic/Common/Message/JinkoApiService.cs +++ b/Src/Dlhis.Basic/Common/Message/Services/Implements/JinkoApiService.cs @@ -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 GetTokenAsync() { var token = string.Empty; - if (string.IsNullOrEmpty(token)) + if (!token.IsEmpty()) { - var httpClient = _httpClientFactory.CreateClient("jinko.httpclient"); - - var form = new FormUrlEncodedContent(new Dictionary - { - { "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("errcode") == "0").ThrowIfFalse(jObj.Value("errmsg")); - - token = jObj.Value("access_token"); + return token; } + var httpClient = _httpClientFactory.CreateClient("jinko.httpclient"); + + var form = new FormUrlEncodedContent(new Dictionary + { + { "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("errcode") == "0").ThrowIfFalse(jObj.Value("errmsg")); + + token = jObj.Value("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("errcode") == "0").ThrowIfFalse(jObj.Value("errmsg")); - + var jObject = JObject.Parse(jsonResponse); + (jObject.Value("errcode") == "0").ThrowIfFalse(jObject.Value("errmsg")); using var scope = _serviceProvider.CreateScope(); var ol = scope.ServiceProvider.GetService(); diff --git a/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionAnalyzeService.cs b/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionAnalyzeService.cs index 827020d1..f6da70f8 100644 --- a/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionAnalyzeService.cs +++ b/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionAnalyzeService.cs @@ -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 { analyzePack.UserFactory }, - AssignTypeEnumType.Role => throw new ArgumentOutOfRangeException(), + AssignTypeEnumType.Role => throw new NotSupportedException("不支持角色分配"), _ => throw new ArgumentOutOfRangeException() }; diff --git a/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionService.cs b/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionService.cs index d6fabd88..10050f1a 100644 --- a/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionService.cs +++ b/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionService.cs @@ -517,6 +517,7 @@ public class DevicePermissionService : IDevicePermissionService { var now = DateTime.Now; + // 获取与指定设备关联的分组编码 var groupCodes = await _dbContext.Queryable() .Where(x => SqlFunc.Subqueryable() .Where(p => p.GroupCode == x.Code) @@ -525,25 +526,30 @@ public class DevicePermissionService : IDevicePermissionService .Select(x => x.Code) .ToListAsync(); + // 获取与分组相关联的区域编码及类型 var areaCodes = await _dbContext.Queryable() .LeftJoin((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() .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(); } } \ No newline at end of file diff --git a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoUpdateService.cs b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoUpdateService.cs index 69c9db9e..b19bb912 100644 --- a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoUpdateService.cs +++ b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoUpdateService.cs @@ -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().Where(x => x.UserCode == gainfo.Code) + var workGridSchedules = await _dbContext.Queryable() + .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().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() + .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 { workGrid.LeaderUserCode } @@ -364,30 +369,39 @@ public class GainfoUpdateService : IGainfoUpdateService await _dbContext.Client.Insertable(gainfo).AS("GainfoLeave").ExecuteCommandAsync(); } - await _dbContext.Deleteable().Where(x => x.UserNo == gainfo.No).ExecuteCommandAsync(); - await _dbContext.Deleteable().Where(x => x.AssignType == AssignTypeEnumType.User && x.AssignValue == gainfo.No).ExecuteCommandAsync(); + await _dbContext.Deleteable() + .Where(x => x.UserNo == gainfo.No) + .ExecuteCommandAsync(); + await _dbContext.Deleteable() + .Where(x => x.AssignType == AssignTypeEnumType.User && x.AssignValue == gainfo.No) + .ExecuteCommandAsync(); - var workGridUser = await _dbContext.Queryable().Where(x => x.UserCode == gainfo.Code).SingleAsync(); + var workGridUser = await _dbContext.Queryable() + .Where(x => x.UserCode == gainfo.Code) + .SingleAsync(); if (workGridUser != null) { try { var resignationDate = DateOnly.FromDateTime(gainfo.ResignationDate!.Value); - var workGridSchedules = await _dbContext.Queryable().Where(x => x.UserCode == gainfo.Code) + var workGridSchedules = await _dbContext.Queryable() + .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().Where(x => x.Code == workGridUser.WorkGridCode).Select(x => new - { - x.LeaderUserCode, x.Name - }).SingleAsync(); + var workGrid = await _dbContext.Queryable() + .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 { workGrid.LeaderUserCode } diff --git a/Src/Dlhis.Basic/Program.cs b/Src/Dlhis.Basic/Program.cs index 6bca2ab1..93573d15 100644 --- a/Src/Dlhis.Basic/Program.cs +++ b/Src/Dlhis.Basic/Program.cs @@ -7,9 +7,7 @@ class Program { static void Main(string[] args) { - KurisuHost.Builder(args) - //.EnableAppSettingsReload() - .RunKurisu(); + KurisuHost.Builder(args).RunKurisu(); } } @@ -29,7 +27,7 @@ public class Startup : DefaultStartup options.KnownNetworks.Clear(); options.KnownProxies.Clear(); }); - + base.ConfigureServices(services); services.AddRedis(); diff --git a/Src/Dlhis.Basic/WorkGrids/Repositories/IWorkGridScheduleRepository.cs b/Src/Dlhis.Basic/WorkGrids/Repositories/IWorkGridScheduleRepository.cs new file mode 100644 index 00000000..84fcaa64 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Repositories/IWorkGridScheduleRepository.cs @@ -0,0 +1,15 @@ +using Dlhis.Basic.WorkGrids.Response; + +namespace Dlhis.Basic.WorkGrids.Repositories; + +public interface IWorkGridScheduleRepository : IScopeDependency +{ + /// + /// 获取用户在指定天数内最大连续排班信息 + /// + /// 用户编码列表 + /// 天数s + /// 日期 + /// 最大连续排班信息列表 + Task> MaximumConsecutiveUserCodesAsync(List userCodes, int days, DateOnly date); +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Repositories/Implements/WorkGridScheduleRepository.cs b/Src/Dlhis.Basic/WorkGrids/Repositories/Implements/WorkGridScheduleRepository.cs new file mode 100644 index 00000000..6175ca55 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Repositories/Implements/WorkGridScheduleRepository.cs @@ -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> MaximumConsecutiveUserCodesAsync(List 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(sql, parameters); + } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Request/WorkGridPageRequest.cs b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridPageRequest.cs index eb7bd538..811a3d55 100644 --- a/Src/Dlhis.Basic/WorkGrids/Request/WorkGridPageRequest.cs +++ b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridPageRequest.cs @@ -5,7 +5,7 @@ namespace Dlhis.Basic.WorkGrids.Request; public class WorkGridPageRequest : PageDto { /// - /// 网格code,不传则取我的网格 + /// 网格code,默认为当前用户网格 /// public Guid? WorkGridCode { get; set; } diff --git a/Src/Dlhis.Basic/WorkGrids/Request/WorkGridScheduleSaveRequest.cs b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridScheduleSaveRequest.cs index 359ace8b..b4d3dae5 100644 --- a/Src/Dlhis.Basic/WorkGrids/Request/WorkGridScheduleSaveRequest.cs +++ b/Src/Dlhis.Basic/WorkGrids/Request/WorkGridScheduleSaveRequest.cs @@ -18,7 +18,7 @@ public class WorkGridScheduleSaveRequest : IValidatableObject /// /// 班次 /// - [EnumDataType(typeof(WorkGridTimePeriodEnumType))] + [EnumDataType(typeof(WorkGridTimePeriodEnumType), ErrorMessage = "班次类型错误")] public WorkGridTimePeriodEnumType PeriodType { get; set; } /// @@ -27,7 +27,7 @@ public class WorkGridScheduleSaveRequest : IValidatableObject [MinLength(1, ErrorMessage = "人员不可为空")] public List UserCodes { get; set; } - /// + public IEnumerable Validate(ValidationContext validationContext) { var now = DateTime.Now; diff --git a/Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleMaximumConsecutiveDto.cs b/Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleMaximumConsecutiveDto.cs index 4301c336..2454dac1 100644 --- a/Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleMaximumConsecutiveDto.cs +++ b/Src/Dlhis.Basic/WorkGrids/Response/WorkGridScheduleMaximumConsecutiveDto.cs @@ -1,5 +1,11 @@ +using Dlhis.Basic.WorkGrids.Repositories; + namespace Dlhis.Basic.WorkGrids.Response; +/// +/// 连续排班信息 +/// 的方法返回类型 +/// public class WorkGridScheduleMaximumConsecutiveDto { public string UserCode { get; set; } diff --git a/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridScheduleService.cs b/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridScheduleService.cs index d32bd128..1c31dcb3 100644 --- a/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridScheduleService.cs +++ b/Src/Dlhis.Basic/WorkGrids/Services/IWorkGridScheduleService.cs @@ -10,21 +10,60 @@ namespace Dlhis.Basic.WorkGrids.Services; /// public interface IWorkGridScheduleService : IScopeDependency { + /// + /// 保存网格排班信息 + /// + /// 排班保存请求参数 Task SaveAsync(WorkGridScheduleSaveRequest request); + /// + /// 移除网格排班信息 + /// + /// 排班移除请求参数 Task RemoveAsync(WorkGridScheduleSaveRequest request); + /// + /// 获取指定网格、日期和时段的排班列表 + /// + /// 网格编码 + /// 日期 + /// 时段类型 + /// 排班列表 Task> GetListAsync(Guid workGridCode, DateOnly date, WorkGridTimePeriodEnumType? periodType); + /// + /// 获取指定网格和日期范围的排班汇总列表 + /// + /// 网格编码 + /// 开始日期 + /// 结束日期 + /// 排班汇总列表 Task> GetListSummaryAsync(Guid workGridCode, DateOnly start, DateOnly end); + /// + /// 获取排班考勤分页数据 + /// + /// 考勤分页请求参数 + /// 排班考勤分页数据 Task> GetAttendancePageAsync(WorkGridScheduleAttendancePageRequest request); + /// + /// 获取所有排班考勤分页数据 + /// + /// 考勤分页请求参数 + /// 排班考勤分页数据 Task> GetAllAttendancePageAsync(WorkGridScheduleAttendancePageRequest request); + /// + /// 手动上传考勤日志 + /// + /// 考勤日志ID列表 Task ManuallyUploadAsync(List logIds); - Task> MaximumConsecutiveUserCodesAsync(List userCodes, int days, DateOnly date); - + /// + /// 测试接口 + /// + /// 排班保存请求参数 + /// 测试结果 Task TestAsync(WorkGridScheduleSaveRequest request); } \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridScheduleService.cs b/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridScheduleService.cs index d5cd9941..6a568669 100644 --- a/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridScheduleService.cs +++ b/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridScheduleService.cs @@ -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 _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 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().Where(x => x.Type == request.PeriodType).Select(x => new { x.Start, x.Before }).SingleAsync(); + 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))); @@ -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().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().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 { 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 { gainfo.Code } @@ -432,44 +444,4 @@ public class WorkGridScheduleService : IWorkGridScheduleService } } } - - public async Task> MaximumConsecutiveUserCodesAsync(List 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(sql, values); - return data; - } } \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridService.cs b/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridService.cs index 35b9eb15..02a60ee1 100644 --- a/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridService.cs +++ b/Src/Dlhis.Basic/WorkGrids/Services/Implements/WorkGridService.cs @@ -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() + var pagination = await _dbContext.Queryable() .LeftJoin((x, dept) => x.DepartmentNo == dept.No) .LeftJoin((x, dept, wgu) => x.Code == wgu.UserCode) .LeftJoin((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; } - /// public async Task SaveAsync(List requests) { - var factory = _currentUser.GetStringTenantId(); + await _workGridValidateService.ValidateSaveAsync(requests); var insertGrids = new List(); var updateGrids = new List(); - var leaderUserCodes = requests.Select(x => x.LeaderUserCode).Distinct().ToList(); - var existsUserInfos = await _dbContext.Queryable() - .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().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() @@ -145,7 +129,6 @@ public class WorkGridService : IWorkGridService await _operateLog.LogAsync(OperateLogType.WorkGrid, "修改网格:" + updateGrids.Join("\r\n")); } - /// public async Task> GetPageAsync(WorkGridPageRequest request) { var role = _currentUser.GetRole(); @@ -190,7 +173,6 @@ public class WorkGridService : IWorkGridService .ToPageListAsync(request); } - /// public async Task ChangeAsync(string from, string to) { if (await _dbContext.Queryable().Where(x => x.LeaderUserCode == to).AnyAsync()) @@ -216,7 +198,6 @@ public class WorkGridService : IWorkGridService await _operateLog.LogAsync(OperateLogType.WorkGrid, $"网格({workGrid.Name})修改负责人{from}->{to}"); } - /// public async Task 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; } - /// public async Task AssignAsync(WorkGridUserAssignRequest request) { var currentFactory = _currentUser.GetStringTenantId(); diff --git a/Src/Dlhis.Basic/WorkGrids/Validations/IWorkGridValidateService.cs b/Src/Dlhis.Basic/WorkGrids/Validations/IWorkGridValidateService.cs new file mode 100644 index 00000000..06f48f67 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Validations/IWorkGridValidateService.cs @@ -0,0 +1,8 @@ +using Dlhis.Basic.WorkGrids.Request; + +namespace Dlhis.Basic.WorkGrids.Validations; + +public interface IWorkGridValidateService : IScopeDependency +{ + Task ValidateSaveAsync(List requests); +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/WorkGrids/Validations/Implements/WorkGridValidateService.cs b/Src/Dlhis.Basic/WorkGrids/Validations/Implements/WorkGridValidateService.cs new file mode 100644 index 00000000..f2804439 --- /dev/null +++ b/Src/Dlhis.Basic/WorkGrids/Validations/Implements/WorkGridValidateService.cs @@ -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; + } + + /// + /// 验证保存请求 + /// + /// + public async Task ValidateSaveAsync(List 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() + .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(); + 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().Where(where).Select(x => x.Name).ToListAsync(); + if (existsNames.Any()) + { + true.ThrowIfTrue($"网格区域名称[{existsNames.Join(",")}]重复"); + } + } + } +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/appsettings.Development.json b/Src/Dlhis.Basic/appsettings.Development.json index e384ae4e..c002b122 100644 --- a/Src/Dlhis.Basic/appsettings.Development.json +++ b/Src/Dlhis.Basic/appsettings.Development.json @@ -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": "", diff --git a/Src/Dlhis.Basic/appsettings.json b/Src/Dlhis.Basic/appsettings.json index c3a6b57f..f5e258ee 100644 --- a/Src/Dlhis.Basic/appsettings.json +++ b/Src/Dlhis.Basic/appsettings.json @@ -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 } } ]