diff --git a/Dlis.Jinko.Yikatong.sln b/Dlhis.Jinko.Face.sln similarity index 91% rename from Dlis.Jinko.Yikatong.sln rename to Dlhis.Jinko.Face.sln index 254bd420..0f893f5a 100644 --- a/Dlis.Jinko.Yikatong.sln +++ b/Dlhis.Jinko.Face.sln @@ -29,6 +29,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dlhis.Canteen", "Src\Dlhis. EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dlhis.Sys.Api", "Src\Dlhis.Sys.Api\Dlhis.Sys.Api.csproj", "{85A345C7-913E-4C1B-BE1C-600DE408A02F}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dlhis.Basic.Api", "Src\Dlhis.Basic.Api\Dlhis.Basic.Api.csproj", "{1F337648-8A22-4601-A842-0F634FBA2825}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -71,6 +73,10 @@ Global {85A345C7-913E-4C1B-BE1C-600DE408A02F}.Debug|Any CPU.Build.0 = Debug|Any CPU {85A345C7-913E-4C1B-BE1C-600DE408A02F}.Release|Any CPU.ActiveCfg = Release|Any CPU {85A345C7-913E-4C1B-BE1C-600DE408A02F}.Release|Any CPU.Build.0 = Release|Any CPU + {1F337648-8A22-4601-A842-0F634FBA2825}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1F337648-8A22-4601-A842-0F634FBA2825}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F337648-8A22-4601-A842-0F634FBA2825}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1F337648-8A22-4601-A842-0F634FBA2825}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -84,6 +90,7 @@ Global {925048C9-5940-4B51-8480-4DC908C754A1} = {57074FD8-1A2F-40C7-8476-001778A3F65D} {8317C526-3523-4195-9B19-2F17CFD71927} = {3D8A98C1-83C9-4375-AEAB-4F72AFAF365B} {85A345C7-913E-4C1B-BE1C-600DE408A02F} = {57074FD8-1A2F-40C7-8476-001778A3F65D} + {1F337648-8A22-4601-A842-0F634FBA2825} = {4EBCE46B-CE3A-4A47-9432-82A1854CA58A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7439494A-E2FD-4FEA-8ABF-74AE57A4A5A0} diff --git a/Src/Dlhis.Basic.Api/Dlhis.Basic.Api.csproj b/Src/Dlhis.Basic.Api/Dlhis.Basic.Api.csproj new file mode 100644 index 00000000..e41324f5 --- /dev/null +++ b/Src/Dlhis.Basic.Api/Dlhis.Basic.Api.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + + + + + + + diff --git a/Src/Dlhis.Basic.Api/IDevicePermissionApi.cs b/Src/Dlhis.Basic.Api/IDevicePermissionApi.cs new file mode 100644 index 00000000..feeb452e --- /dev/null +++ b/Src/Dlhis.Basic.Api/IDevicePermissionApi.cs @@ -0,0 +1,12 @@ +using Kurisu.Core.Result; +using Kurisu.RemoteCall.Attributes; + +namespace Dlhis.Basic.Api; + +[EnableRemoteClient(Name = "basic-service", BaseUrl = "${InternalRequest:BasicRequestUrl}")] +public interface IDevicePermissionApi +{ + [Auth] + [Get("/api/devicepermissions/canteen/uniqueNos")] + Task>> GetCanteenDeviceUniqueNosByUserNoAsync(string userNo); +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/BusinessPlace/Controllers/BusinessPlacesController.cs b/Src/Dlhis.Basic/BusinessPlace/Controllers/BusinessPlacesController.cs index 9649c414..887dfe09 100644 --- a/Src/Dlhis.Basic/BusinessPlace/Controllers/BusinessPlacesController.cs +++ b/Src/Dlhis.Basic/BusinessPlace/Controllers/BusinessPlacesController.cs @@ -2,6 +2,7 @@ using Dlhis.Basic.BusinessPlace.Response; using Dlhis.Basic.BusinessPlace.Services; using Dlhis.Basic.BusinessShift.Services; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; using Kurisu.Core.DataAccess.Dto; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -58,12 +59,24 @@ public class BusinessPlacesController : ControllerBase await _businessPlaceService.SaveAsync(request); } + /// + /// 营业点保存餐次 + /// + /// + /// + [HttpPost("shift/save")] + public async Task SaveShiftAsync(BusinessPlaceSaveShiftRequest request) + { + await _businessPlaceService.SaveShiftAsync(request); + } + /// /// 根据手工号获取餐别 /// /// /// [HttpPost("shifts")] + [IgnoreTenant] public async Task GetShiftsByPlaceCustomCodeHttpPostAsync(BusinessPlaceGetShiftsRequest request) { return await _businessPlaceShiftService.GetShiftsByPlaceCustomCodeAsync(request.CustomCode); @@ -75,8 +88,20 @@ public class BusinessPlacesController : ControllerBase /// /// [HttpGet("shifts")] + [IgnoreTenant] public async Task GetShiftsByPlaceCustomCodeHttpGetAsync([FromQuery] BusinessPlaceGetShiftsRequest request) { return await _businessPlaceShiftService.GetShiftsByPlaceCustomCodeAsync(request.CustomCode); } + + /// + /// 根据营业点获取下面的设备 + /// + /// + /// + [HttpGet("devicenos")] + public async Task> GetDevicesByCode([FromQuery]BusinessPlaceGetByCodeRequest request) + { + return await _businessPlaceShiftService.GetDevicesByCode(request); + } } diff --git a/Src/Dlhis.Basic/BusinessPlace/Request/BusinessPlaceGetByCodeRequest.cs b/Src/Dlhis.Basic/BusinessPlace/Request/BusinessPlaceGetByCodeRequest.cs new file mode 100644 index 00000000..5177dd0c --- /dev/null +++ b/Src/Dlhis.Basic/BusinessPlace/Request/BusinessPlaceGetByCodeRequest.cs @@ -0,0 +1,14 @@ +using Kurisu.Core.DataAccess.Dto; +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Basic.BusinessPlace.Request +{ + public class BusinessPlaceGetByCodeRequest : PageDto + { + /// + /// 营业点号 + /// + [Required(ErrorMessage = "营业点号不能为空")] + public string Code { get; set; } + } +} diff --git a/Src/Dlhis.Basic/BusinessPlace/Request/BusinessPlaceSaveShiftRequest.cs b/Src/Dlhis.Basic/BusinessPlace/Request/BusinessPlaceSaveShiftRequest.cs new file mode 100644 index 00000000..23fc1140 --- /dev/null +++ b/Src/Dlhis.Basic/BusinessPlace/Request/BusinessPlaceSaveShiftRequest.cs @@ -0,0 +1,14 @@ +namespace Dlhis.Basic.BusinessPlace.Request; + +public class BusinessPlaceSaveShiftRequest +{ + /// + /// 营业点id + /// + public long Id { get; set; } + + /// + /// 餐次code + /// + public Guid ShiftCode { get; set; } +} diff --git a/Src/Dlhis.Basic/BusinessPlace/Response/BusinessPlaceResponse.cs b/Src/Dlhis.Basic/BusinessPlace/Response/BusinessPlaceResponse.cs index 9793da56..c954462a 100644 --- a/Src/Dlhis.Basic/BusinessPlace/Response/BusinessPlaceResponse.cs +++ b/Src/Dlhis.Basic/BusinessPlace/Response/BusinessPlaceResponse.cs @@ -35,9 +35,21 @@ public class BusinessPlaceResponse public CanteenBusinessPlaceSourceEnumType Source { get; set; } public string SourceDesc => Source.GetDescription(); + /// /// 分类,超市/餐厅/食堂 /// public CanteenBusinessPlaceCatalogEnumType Catalog { get; set; } public string CatalogDesc => Catalog.GetDescription(); + + + /// + /// 餐次code + /// + public Guid? ShiftCode { get; set; } + + /// + /// 餐次名称 + /// + public string ShiftName { get; set; } } diff --git a/Src/Dlhis.Basic/BusinessPlace/Response/DeviceCodeNameDetail.cs b/Src/Dlhis.Basic/BusinessPlace/Response/DeviceCodeNameDetail.cs new file mode 100644 index 00000000..0841c315 --- /dev/null +++ b/Src/Dlhis.Basic/BusinessPlace/Response/DeviceCodeNameDetail.cs @@ -0,0 +1,8 @@ +namespace Dlhis.Basic.BusinessPlace.Response +{ + public class DeviceCodeNameDetail + { + public string UniqueNo { get; set; } + public string UniqueName { get; set; } + } +} diff --git a/Src/Dlhis.Basic/BusinessPlace/Services/IBusinessPlaceService.cs b/Src/Dlhis.Basic/BusinessPlace/Services/IBusinessPlaceService.cs index e012ce37..2ada296c 100644 --- a/Src/Dlhis.Basic/BusinessPlace/Services/IBusinessPlaceService.cs +++ b/Src/Dlhis.Basic/BusinessPlace/Services/IBusinessPlaceService.cs @@ -1,12 +1,16 @@ using Dlhis.Basic.BusinessPlace.Request; using Dlhis.Basic.BusinessPlace.Response; using Kurisu.Core.DataAccess.Dto; -using Kurisu.SqlSugar.Attributes; namespace Dlhis.Basic.BusinessPlace.Services; public interface IBusinessPlaceService : IScopeDependency { + /// + /// 营业点分页 + /// + /// + /// Task> GetPageAsync(BusinessPlacePageRequest request); /// @@ -15,6 +19,17 @@ public interface IBusinessPlaceService : IScopeDependency /// Task> GetOpenedListAsync(); - + /// + /// 保存营业点 + /// + /// + /// Task SaveAsync(BusinessPlaceSaveRequest request); + + /// + /// 营业点保存餐次 + /// + /// + /// + Task SaveShiftAsync(BusinessPlaceSaveShiftRequest request); } diff --git a/Src/Dlhis.Basic/BusinessPlace/Services/Implements/BusinessPlaceService.cs b/Src/Dlhis.Basic/BusinessPlace/Services/Implements/BusinessPlaceService.cs index 6e60570a..a49ecf44 100644 --- a/Src/Dlhis.Basic/BusinessPlace/Services/Implements/BusinessPlaceService.cs +++ b/Src/Dlhis.Basic/BusinessPlace/Services/Implements/BusinessPlaceService.cs @@ -22,46 +22,83 @@ public class BusinessPlaceService : IBusinessPlaceService public async Task> GetPageAsync(BusinessPlacePageRequest request) { - var page = await _dbContext.Queryable() - .WhereIF(request.Search.IsPresent(), x => x.CustomCode.Contains(request.Search)) + var page = await _dbContext.Queryable() + .LeftJoin((x, ps) => x.Code == ps.PlaceCode) + .LeftJoin((x, ps, s) => s.Code == ps.ShiftCode) + .WhereIF(request.Search.IsPresent(), x => x.CustomCode.Contains(request.Search) + || x.Code.ToString().Contains(request.Search) || x.Name.Contains(request.Search)) .WhereIF(request.Catalog.HasValue, x => x.Catalog == request.Catalog) .WhereIF(request.Status.HasValue, x => x.Status == request.Status) .WhereIF(request.Source.HasValue, x => x.Source == request.Source) - .Select() + .Select((x, ps, s) => new BusinessPlaceResponse + { + Id = x.Id, + Code = x.Code, + Status = x.Status, + Name = x.Name, + Catalog = x.Catalog, + CustomCode = x.CustomCode, + Source = x.Source, + + ShiftCode = s.Code, + ShiftName = s.Name + }) .ToPageListAsync(request); - + return page; } public async Task> GetOpenedListAsync() { - return await _dbContext.Queryable() + return await _dbContext.Queryable() .Where(x => x.Status == CanteenBusinessPlaceStatusEnumType.Opened) - .Select() - .ToListAsync(); + .Select() + .ToListAsync(); } - + public async Task SaveAsync(BusinessPlaceSaveRequest request) { await CheckExistsCustomCodeAsync(request); if (!request.Id.HasValue) { - var entity = request.Adapt(); + var entity = request.Adapt(); entity.Code = Guid.NewGuid(); await _dbContext.InsertAsync(entity); } else { - var entity = await _dbContext.Queryable().FirstAsync(x => x.Id == request.Id); + var entity = await _dbContext.Queryable().FirstAsync(x => x.Id == request.Id); request.Adapt(entity); await _dbContext.UpdateAsync(entity); } } + + public async Task SaveShiftAsync(BusinessPlaceSaveShiftRequest request) + { + var place = await _dbContext.Queryable().Where(x => x.Id == request.Id).FirstAsync(); + place.ThrowIfNull("营业点不存在"); + + var relationship = await _dbContext.Queryable().Where(x => x.PlaceCode == place.Code).FirstAsync(); + if (relationship == null) + { + await _dbContext.InsertAsync(new BusinessPlaceShiftRelationshipEntity + { + PlaceCode = place.Code, + ShiftCode = request.ShiftCode, + }); + } + else + { + relationship.ShiftCode = request.ShiftCode; + await _dbContext.UpdateAsync(relationship); + } + } + //*********************************************** help methods ***************************************************// /// @@ -71,7 +108,7 @@ public class BusinessPlaceService : IBusinessPlaceService /// private async Task CheckExistsCustomCodeAsync(BusinessPlaceSaveRequest request) { - var existsSameCustomCode = await _dbContext.Queryable() + var existsSameCustomCode = await _dbContext.Queryable() .Where(x => x.CustomCode == request.CustomCode) .WhereIF(request.Id.HasValue, x => x.Id != request.Id) .AnyAsync(); diff --git a/Src/Dlhis.Basic/BusinessShift/Controllers/BusinessShiftsController.cs b/Src/Dlhis.Basic/BusinessShift/Controllers/BusinessShiftsController.cs index 154bb984..981c650b 100644 --- a/Src/Dlhis.Basic/BusinessShift/Controllers/BusinessShiftsController.cs +++ b/Src/Dlhis.Basic/BusinessShift/Controllers/BusinessShiftsController.cs @@ -1,6 +1,7 @@ using Dlhis.Basic.BusinessShift.Request; using Dlhis.Basic.BusinessShift.Response; using Dlhis.Basic.BusinessShift.Services; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; using Kurisu.Core.DataAccess.Dto; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -33,14 +34,36 @@ public class BusinessShiftsController : ControllerBase return await _businessPlaceShiftService.GetPageAsync(request); } + /// + /// 餐次列表 + /// + /// + [HttpGet("list")] + public async Task> GetListAsync() + { + return await _businessPlaceShiftService.GetListAsync(); + } + /// /// 餐次保存 /// /// /// [HttpPost("save")] - public async Task SaveAsync(BusinessPlaceShiftSaveRequest request) + public async Task SaveAsync(BusinessPlaceShiftSaveRequest request) { - await _businessPlaceShiftService.SaveAsync(request); + return await _businessPlaceShiftService.SaveAsync(request); + } + + /// + /// 保存餐次段 + /// + /// + /// + [HttpPost("meal/save")] + [Transactional] + public async Task SaveMealShiftsAsync(List requests) + { + await _businessPlaceShiftService.SaveMealShiftsAsync(requests); } } diff --git a/Src/Dlhis.Basic/BusinessShift/Request/BusinessPlaceShiftPageRequest.cs b/Src/Dlhis.Basic/BusinessShift/Request/BusinessPlaceShiftPageRequest.cs index ed518c13..5ed2e3c2 100644 --- a/Src/Dlhis.Basic/BusinessShift/Request/BusinessPlaceShiftPageRequest.cs +++ b/Src/Dlhis.Basic/BusinessShift/Request/BusinessPlaceShiftPageRequest.cs @@ -4,5 +4,5 @@ namespace Dlhis.Basic.BusinessShift.Request; public class BusinessPlaceShiftPageRequest : PageDto { - public Guid? BusinessPlaceCode { get; set; } + } diff --git a/Src/Dlhis.Basic/BusinessShift/Request/BusinessPlaceShiftSaveMealShiftRequest.cs b/Src/Dlhis.Basic/BusinessShift/Request/BusinessPlaceShiftSaveMealShiftRequest.cs new file mode 100644 index 00000000..98e63fae --- /dev/null +++ b/Src/Dlhis.Basic/BusinessShift/Request/BusinessPlaceShiftSaveMealShiftRequest.cs @@ -0,0 +1,38 @@ +using System.ComponentModel.DataAnnotations; +using Kurisu.Core.CustomClass; + +namespace Dlhis.Basic.BusinessShift.Request; + +public class BusinessPlaceShiftSaveMealShiftRequest : IValidatableObject +{ + public long? Id { get; set; } + + /// + /// 餐次Code + /// + public Guid ShiftCode { get; set; } + + /// + /// mealshift + /// + public string ShiftType { get; set; } + + /// + /// 序号 + /// + public int Sequence { get; set; } = 100; + + /// + /// 时间段 + /// + public TimePeriod TimePeriod { get; set; } + + + public IEnumerable Validate(ValidationContext validationContext) + { + if (TimePeriod.Start == TimePeriod.End) + { + yield return new ValidationResult("时间段设置区间有误,开始时段不能等于结束时段.", new[] { nameof(TimePeriod) }); + } + } +} diff --git a/Src/Dlhis.Basic/BusinessShift/Request/BusinessPlaceShiftSaveRequest.cs b/Src/Dlhis.Basic/BusinessShift/Request/BusinessPlaceShiftSaveRequest.cs index 8353c298..82f1c25c 100644 --- a/Src/Dlhis.Basic/BusinessShift/Request/BusinessPlaceShiftSaveRequest.cs +++ b/Src/Dlhis.Basic/BusinessShift/Request/BusinessPlaceShiftSaveRequest.cs @@ -1,39 +1,20 @@ using System.ComponentModel.DataAnnotations; -using Kurisu.Core.CustomClass; namespace Dlhis.Basic.BusinessShift.Request; -public class BusinessPlaceShiftSaveRequest : IValidatableObject +public class BusinessPlaceShiftSaveRequest { public long? Id { get; set; } - /// - /// 营业点代号 - /// - [Required(ErrorMessage = "营业点不能为空")] - public Guid BusinessPlaceCode { get; set; } - /// /// 自定义代号 mealshift /// - [Required(ErrorMessage = "餐次编号不能为空")] + [Required(ErrorMessage = "餐次手工号不能为空")] public string CustomCode { get; set; } /// - /// 序号 + /// 餐次名称 /// - public int Sequence { get; set; } = 100; - - /// - /// 时间段 - /// - public TimePeriod TimePeriod { get; set; } - - public IEnumerable Validate(ValidationContext validationContext) - { - if (TimePeriod.Start == TimePeriod.End) - { - yield return new ValidationResult("时间段设置区间有误,开始时段不能等于结束时段.", new[] { nameof(TimePeriod) }); - } - } + [Required(ErrorMessage = "餐次名称")] + public string Name { get; set; } } diff --git a/Src/Dlhis.Basic/BusinessShift/Response/BusinessPlaceShiftResponse.cs b/Src/Dlhis.Basic/BusinessShift/Response/BusinessPlaceShiftResponse.cs index 5c6848a6..82a3aab3 100644 --- a/Src/Dlhis.Basic/BusinessShift/Response/BusinessPlaceShiftResponse.cs +++ b/Src/Dlhis.Basic/BusinessShift/Response/BusinessPlaceShiftResponse.cs @@ -4,21 +4,6 @@ public class BusinessPlaceShiftResponse { public long Id { get; set; } - /// - /// 营业点代号 - /// - public Guid BusinessPlaceCode { get; set; } - - /// - /// 营业点手工号 - /// - public string BusinessPlaceCustomCode { get; set; } - - /// - /// 营业点 - /// - public string BusinessPlace { get; set; } - /// /// 代号 /// @@ -35,12 +20,7 @@ public class BusinessPlaceShiftResponse public string Name { get; set; } /// - /// 经营开始时间 + /// 就餐时间段 /// - public TimeOnly Start { get; set; } - - /// - /// 经营结束时间 - /// - public TimeOnly End { get; set; } + public List MealShifts { get; set; } } diff --git a/Src/Dlhis.Basic/BusinessShift/Response/BusinessPlaceShiftTinyResponse.cs b/Src/Dlhis.Basic/BusinessShift/Response/BusinessPlaceShiftTinyResponse.cs index cf2576ab..908425b3 100644 --- a/Src/Dlhis.Basic/BusinessShift/Response/BusinessPlaceShiftTinyResponse.cs +++ b/Src/Dlhis.Basic/BusinessShift/Response/BusinessPlaceShiftTinyResponse.cs @@ -3,7 +3,7 @@ public class BusinessPlaceShiftTinyResponse { /// - /// 自定义代号 + /// 无用字段,暂时不删除 //todo /// public string CustomCode { get; set; } diff --git a/Src/Dlhis.Basic/BusinessShift/Response/MealShiftResponse.cs b/Src/Dlhis.Basic/BusinessShift/Response/MealShiftResponse.cs new file mode 100644 index 00000000..bf99cb6a --- /dev/null +++ b/Src/Dlhis.Basic/BusinessShift/Response/MealShiftResponse.cs @@ -0,0 +1,17 @@ +namespace Dlhis.Basic.BusinessShift.Response +{ + public class MealShiftResponse + { + public long Id { get; set; } + + public Guid ShiftCode { get; set; } + + public string ShiftType { get; set; } + + public string Name { get; set; } + + public TimeOnly Start { get; set; } + + public TimeOnly End { get; set; } + } +} diff --git a/Src/Dlhis.Basic/BusinessShift/Services/IBusinessPlaceShiftService.cs b/Src/Dlhis.Basic/BusinessShift/Services/IBusinessPlaceShiftService.cs index 3993951a..ba4e1114 100644 --- a/Src/Dlhis.Basic/BusinessShift/Services/IBusinessPlaceShiftService.cs +++ b/Src/Dlhis.Basic/BusinessShift/Services/IBusinessPlaceShiftService.cs @@ -1,4 +1,5 @@ -using Dlhis.Basic.BusinessPlace.Response; +using Dlhis.Basic.BusinessPlace.Request; +using Dlhis.Basic.BusinessPlace.Response; using Dlhis.Basic.BusinessShift.Request; using Dlhis.Basic.BusinessShift.Response; using Kurisu.Core.DataAccess.Dto; @@ -9,7 +10,13 @@ public interface IBusinessPlaceShiftService : IScopeDependency { Task> GetPageAsync(BusinessPlaceShiftPageRequest request); - Task SaveAsync(BusinessPlaceShiftSaveRequest request); + Task> GetListAsync(); + + Task SaveAsync(BusinessPlaceShiftSaveRequest request); + + Task SaveMealShiftsAsync(List requests); Task GetShiftsByPlaceCustomCodeAsync(string placeCustomCode); + + Task> GetDevicesByCode(BusinessPlaceGetByCodeRequest request); } diff --git a/Src/Dlhis.Basic/BusinessShift/Services/Implements/BusinessPlaceShiftService.cs b/Src/Dlhis.Basic/BusinessShift/Services/Implements/BusinessPlaceShiftService.cs index aa263e38..6df8324d 100644 --- a/Src/Dlhis.Basic/BusinessShift/Services/Implements/BusinessPlaceShiftService.cs +++ b/Src/Dlhis.Basic/BusinessShift/Services/Implements/BusinessPlaceShiftService.cs @@ -1,16 +1,18 @@ -using Dlhis.Basic.BusinessPlace.Response; +using Dlhis.Basic.BusinessPlace.Request; +using Dlhis.Basic.BusinessPlace.Response; using Dlhis.Basic.BusinessShift.Request; using Dlhis.Basic.BusinessShift.Response; using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Device.Entities; using Dlhis.Sys.Api; using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Core.CustomClass; using Kurisu.Core.DataAccess.Dto; using Kurisu.Core.Result; -using Kurisu.SqlSugar.Attributes; using Kurisu.SqlSugar.Extensions; using Kurisu.SqlSugar.Services; using Mapster; +using System.Collections.Generic; namespace Dlhis.Basic.BusinessShift.Services.Implements; @@ -27,58 +29,109 @@ public class BusinessPlaceShiftService : IBusinessPlaceShiftService public async Task> GetPageAsync(BusinessPlaceShiftPageRequest request) { - var page = await _dbContext.Queryable() - .LeftJoin((shift, place) => shift.BusinessPlaceCode == place.Code) - .LeftJoin((shift, place, type) => shift.Code.ToString() == type.Code) - .WhereIF(request.Search.IsPresent(), (shift, place) => shift.CustomCode.Contains(request.Search)) - .WhereIF(request.BusinessPlaceCode.HasValue, (shift, place) => shift.BusinessPlaceCode == request.BusinessPlaceCode) - .Select((shift, place, type) => new BusinessPlaceShiftResponse - { - Id = shift.Id, - BusinessPlaceCode = place.Code, - BusinessPlaceCustomCode = place.CustomCode, - BusinessPlace = place.Name, - Code = shift.Code, - CustomCode = shift.CustomCode, - Name = type.Display, - Start = shift.Start, - End = shift.End - }) + var page = await _dbContext.Queryable() + .WhereIF(request.Search.IsPresent(), (shift) => shift.CustomCode.Contains(request.Search) || shift.Name.Contains(request.Search)) + .Select() .ToPageListAsync(request); + var shiftCodes = page.Data.Select(x => x.Code).ToList(); + + //下级就餐时间段 + var mealShifts = await _dbContext.Queryable() + .LeftJoin((x, bt) => x.ShiftType == bt.Code) + .Where(x => shiftCodes.Contains(x.ShiftCode)) + .Select((x, bt) => new MealShiftResponse + { + Id = x.Id, + ShiftCode = x.ShiftCode, + ShiftType = bt.Code, + Name = bt.Display, + Start = x.Start, + End = x.End, + }) + .ToListAsync(); + + page.Data.ForEach(x => + { + x.MealShifts = mealShifts.Where(item => item.ShiftCode == x.Code).ToList(); + }); + return page; } - public async Task SaveAsync(BusinessPlaceShiftSaveRequest request) + public async Task> GetListAsync() { - await CheckExistsCustomCodeAsync(request); - await DuplicateTimePeriodAsync(request); - await CheckPlaceExistsAsync(request.BusinessPlaceCode); + var result = await _dbContext.Queryable() + .Select().ToListAsync(); + return result; + } + public async Task SaveAsync(BusinessPlaceShiftSaveRequest request) + { + await CheckExistsAsync(request); + + Guid code = Guid.Empty; if (!request.Id.HasValue) { - var entity = request.Adapt(); + var entity = request.Adapt(); entity.Code = Guid.NewGuid(); - entity.Start = request.TimePeriod.Start.Value; - entity.End = request.TimePeriod.End.Value; - await _dbContext.InsertAsync(entity); + + code = entity.Code; } else { - var entity = await _dbContext.Queryable().FirstAsync(x => x.Id == request.Id); - entity.Adapt(request); + var entity = await _dbContext.Queryable().FirstAsync(x => x.Id == request.Id); + entity.ThrowIfNull("餐次不存在"); + request.Adapt(entity); await _dbContext.UpdateAsync(entity); + + code = entity.Code; + } + + return code; + } + + public async Task SaveMealShiftsAsync(List requests) + { + var mealShifts = await GetMealShiftAsync(); + + foreach (var request in requests) + { + var shift = await CheckExistsShiftAsync(request.ShiftCode); + await CheckExistsMealShiftAsync(request); + await DuplicateTimePeriodAsync(request); + mealShifts.Contains(request.ShiftType).ThrowIfFalse($"{request.ShiftType}不存在"); + + if (!request.Id.HasValue) + { + var entity = request.Adapt(); + + entity.ShiftCode = shift.Code; + entity.ShiftCustomCode = shift.CustomCode; + entity.Start = request.TimePeriod.Start.Value; + entity.End = request.TimePeriod.End.Value; + + await _dbContext.InsertAsync(entity); + } + else + { + var entity = await _dbContext.Queryable().FirstAsync(x => x.Id == request.Id); + entity.ThrowIfNull("餐次段不存在"); + + request.Adapt(entity); + await _dbContext.UpdateAsync(entity); + } } } - [IgnoreTenant] + public async Task GetShiftsByPlaceCustomCodeAsync(string placeCustomCode) { - var place = await _dbContext.Queryable().Where(x => x.CustomCode == placeCustomCode).FirstAsync(); + var place = await _dbContext.Queryable().Where(x => x.CustomCode == placeCustomCode).FirstAsync(); if (place == null) { return new BusinessPlaceWithShiftResponse() @@ -87,29 +140,45 @@ public class BusinessPlaceShiftService : IBusinessPlaceShiftService }; } - var shifts = await _dbContext.Queryable() - .Where(x => x.BusinessPlaceCode == place.Code) - .OrderBy(x => x.Sequence) //排序 - .ToListAsync(); - var tenantResult = await _tenantApi.GetTenantByCodeAsync(place.TenantId); (tenantResult.Code == ApiStateCode.Success).ThrowIfFalse(tenantResult.Msg); var tenantInfo = tenantResult.Data; + var relationship = await _dbContext.Queryable().Where(x => x.PlaceCode == place.Code).FirstAsync(); + if (relationship == null) + { + return new BusinessPlaceWithShiftResponse() + { + CustomCode = placeCustomCode, + Name = place.Name, + TenantCode = tenantInfo.Code, + TenantName = tenantInfo.Name, + Shifts = new List() + }; + } + + var shifts = await _dbContext.Queryable() + .Where(x => x.ShiftCode == relationship.ShiftCode) + .OrderBy(x => x.Sequence) //排序 + .ToListAsync(); + var list = new List(); - foreach (CanteenBusinessShiftEntity shiftinfo in shifts) + + foreach (BusinessShiftMealRelationshipEntity shiftinfo in shifts) { var name = _dbContext.Queryable() - .Where(x => x.Code == shiftinfo.CustomCode.ToString()) + .Where(x => x.Code == shiftinfo.ShiftType.ToString()) .Select(x => x.Display) .First(); + var shift = new BusinessPlaceShiftTinyResponse { - CustomCode = shiftinfo.CustomCode, + CustomCode = shiftinfo.Id.ToString(), Name = name, Start = shiftinfo.Start, End = shiftinfo.End }; + list.Add(shift); } return new BusinessPlaceWithShiftResponse @@ -122,24 +191,58 @@ public class BusinessPlaceShiftService : IBusinessPlaceShiftService }; } - //*********************************************** help methods ***************************************************// - - - private async Task CheckPlaceExistsAsync(Guid code) + public async Task> GetDevicesByCode(BusinessPlaceGetByCodeRequest request) { - var existsPlace = await _dbContext.Queryable().AnyAsync(x => x.Code == code); - existsPlace.ThrowIfFalse("营业点不存在,请检查."); + var regiss = await _dbContext.Queryable() + .Where(x => x.BusinessPlaceCode.ToString() == request.Code) + .ToListAsync(); + + var devices = await _dbContext.Queryable().ToListAsync(); + + var list = new List(); + if (regiss.Count > 0) + { + foreach (var regis in regiss) + { + var device = devices.Where(x => x.UniqueNo == regis.DeviceUniqueNo) + .FirstOrDefault(); + if (device != null) + { + var detail = new DeviceCodeNameDetail + { + UniqueNo = regis.DeviceUniqueNo, + UniqueName = device.Supplier + }; + list.Add(detail); + } + } + } + //分页 + var startIndex = (request.PageIndex - 1) * request.PageSize; + var pagedData = list.Skip(startIndex).Take(request.PageSize).ToList(); + + var pagination = new Pagination + { + PageIndex = request.PageIndex, + PageSize = request.PageSize, + Total = list.Count, // 使用原始列表的总数进行分页 + Data = pagedData + }; + + return pagination; } + //*********************************************** help methods ***************************************************// + /// /// 时间段重合检查 /// /// /// - private async Task DuplicateTimePeriodAsync(BusinessPlaceShiftSaveRequest request) + private async Task DuplicateTimePeriodAsync(BusinessPlaceShiftSaveMealShiftRequest request) { - var periods = await _dbContext.Queryable() - .Where(x => x.BusinessPlaceCode == request.BusinessPlaceCode) + var periods = await _dbContext.Queryable() + .Where(x => x.ShiftCode == request.ShiftCode) .WhereIF(request.Id.HasValue, x => x.Id != request.Id) .Select(x => new TimePeriod { @@ -150,24 +253,47 @@ public class BusinessPlaceShiftService : IBusinessPlaceShiftService foreach (var period in periods) { - period.IsDuplicate(request.TimePeriod).ThrowIfTrue($"存在时间段重合[{period.Start}-{period.End}]"); + period.IsDuplicate1(request.TimePeriod).ThrowIfTrue($"存在时间段重合[{period.Start}-{period.End}]"); } } /// - /// 检查重复手工号 + /// 检查重复 /// /// /// - private async Task CheckExistsCustomCodeAsync(BusinessPlaceShiftSaveRequest request) + private async Task CheckExistsAsync(BusinessPlaceShiftSaveRequest request) { - var existsSameCustomCode = await _dbContext.Queryable() - .Where(x => x.CustomCode == request.CustomCode) + var existsSameCustomCode = await _dbContext.Queryable() + .Where(x => x.CustomCode == request.CustomCode || x.Name == request.Name) .WhereIF(request.Id.HasValue, x => x.Id != request.Id) .AnyAsync(); - existsSameCustomCode.ThrowIfTrue($"自定义手工号[{request.CustomCode}]存在重复,请检查."); + existsSameCustomCode.ThrowIfTrue($"自定义手工号或者名称存在重复,请检查."); } + private async Task CheckExistsShiftAsync(Guid shiftCode) + { + var shift = await _dbContext.Queryable().Where(x => x.Code == shiftCode).FirstAsync(); + shift.ThrowIfNull("餐次不存在"); + + return shift; + } + + private async Task> GetMealShiftAsync() + { + return await _dbContext.Queryable().Where(x => x.Type == "mealshift").Select(x => x.Code).ToListAsync(); + } + + private async Task CheckExistsMealShiftAsync(BusinessPlaceShiftSaveMealShiftRequest request) + { + var exists = await _dbContext.Queryable() + .Where(x => x.ShiftCode == request.ShiftCode) + .Where(x => x.ShiftType == request.ShiftType) + .WhereIF(request.Id.HasValue, x => x.Id != request.Id) + .AnyAsync(); + + exists.ThrowIfTrue("重复设置!"); + } } diff --git a/Src/Dlhis.Basic/BusinessShift/TimePeriodExtensions.cs b/Src/Dlhis.Basic/BusinessShift/TimePeriodExtensions.cs new file mode 100644 index 00000000..15d4c69d --- /dev/null +++ b/Src/Dlhis.Basic/BusinessShift/TimePeriodExtensions.cs @@ -0,0 +1,73 @@ +using Kurisu.Core.CustomClass; + +namespace Dlhis.Basic.BusinessShift; + + +/// +/// 临时的扩展类 todo: 嵌入框架 +/// +public static class TimePeriodExtensions +{ + public static bool IsDuplicate1(this TimePeriod current, TimePeriod period) + { + return !Check(); + + bool Check() + { + if (current.IsCrossDay()) + { + if (period.IsCrossDay()) + { + return true; + } + else + { + return period.Start > current.End && period.End < current.Start; + } + } + else + { + if (period.IsCrossDay()) + { + return period.Start > current.End && period.End < current.Start; + } + else + { + return period.Start > current.End || period.End < current.Start; + } + } + } + } + + + public static bool IsPresent1(this TimePeriod period, DateTime dateTime) + { + DateTime start; + DateTime end; + + if (period.IsCrossDay()) + { + var currentStart = DateTime.Today.Add(period.Start.Value.ToTimeSpan()); + var endOfDay = DateTime.Today.AddHours(23).AddMinutes(59).AddSeconds(59); + + if (dateTime >= currentStart && dateTime <= endOfDay) + { + start = DateTime.Today.Add(period.Start.Value.ToTimeSpan()); + end = DateTime.Today.AddDays(1).Add(period.End.Value.ToTimeSpan()); + } + else + { + //第二天 + start = DateTime.Today.AddDays(-1).Add(period.Start.Value.ToTimeSpan()); + end = DateTime.Today.Add(period.End.Value.ToTimeSpan()); + } + } + else + { + start = DateTime.Today.Add(period.Start.Value.ToTimeSpan()); + end = DateTime.Today.Add(period.End.Value.ToTimeSpan()); + } + + return dateTime >= start && dateTime <= end; + } +} diff --git a/Src/Dlhis.Basic/Device/Controllers/DeviceGroupsController.cs b/Src/Dlhis.Basic/Device/Controllers/DeviceGroupsController.cs index bba3ae80..b3b849cd 100644 --- a/Src/Dlhis.Basic/Device/Controllers/DeviceGroupsController.cs +++ b/Src/Dlhis.Basic/Device/Controllers/DeviceGroupsController.cs @@ -1,6 +1,8 @@ using Dlhis.Basic.Device.Request; using Dlhis.Basic.Device.Response; using Dlhis.Basic.Device.Services; +using Dlhis.Entity.Device.Enums; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; using Kurisu.Core.DataAccess.Dto; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -22,26 +24,17 @@ public class DeviceGroupsController : ControllerBase _deviceGroupService = deviceGroupService; } - /// - /// 设备组分页 - /// - /// - /// - [HttpPost("page")] - public async Task> GetPageAsync([FromQuery] DeviceGroupPageRequest request) - { - return await _deviceGroupService.GetPageAsync(request); - } - /// /// 获取设备组树 /// /// 是否显示设备 + /// 分组类型 /// [HttpGet("tree")] - public async Task> GetTreeAsync(bool showDevices) + [IgnoreTenant] + public async Task> GetTreeAsync(bool showDevices, DeviceTypeEnumType? type) { - return await _deviceGroupService.GetTreeAsync(showDevices); + return await _deviceGroupService.GetTreeAsync(showDevices, type); } /// @@ -50,6 +43,7 @@ public class DeviceGroupsController : ControllerBase /// /// [HttpPost("save")] + [IgnoreTenant] public async Task SaveAsync(DeviceGroupSaveRequest request) { await _deviceGroupService.SaveAsync(request); @@ -72,6 +66,8 @@ public class DeviceGroupsController : ControllerBase /// /// [HttpPost("assign")] + [IgnoreTenant] + [Transactional] public async Task AssignDevicesAsync(DeviceAssignRequest request) { await _deviceGroupService.AssignDevicesAsync(request); diff --git a/Src/Dlhis.Basic/Device/Controllers/DevicePermissionsController.cs b/Src/Dlhis.Basic/Device/Controllers/DevicePermissionsController.cs index b0ecaf24..736902c0 100644 --- a/Src/Dlhis.Basic/Device/Controllers/DevicePermissionsController.cs +++ b/Src/Dlhis.Basic/Device/Controllers/DevicePermissionsController.cs @@ -1,6 +1,7 @@ using Dlhis.Basic.Device.Request; using Dlhis.Basic.Device.Response; using Dlhis.Basic.Device.Services; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; using Kurisu.Core.DataAccess.Dto; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -25,6 +26,8 @@ public class DevicePermissionsController : ControllerBase /// /// [HttpPost("assign")] + [Transactional] + [IgnoreTenant] public async Task AssignAsync(DevicePermissionSaveReuqest request) { await _devicePermissionService.AssignAsync(request); @@ -51,4 +54,17 @@ public class DevicePermissionsController : ControllerBase { return await _devicePermissionService.GetAssigmentByCodeAsync(request); } + + + /// + /// 就餐设备唯一号权限 + /// + /// + /// + [HttpGet("canteen/uniqueNos")] + [IgnoreTenant] + public async Task> GetCanteenDeviceUniqueNosByUserNoAsync(string userNo) + { + return await _devicePermissionService.GetCanteenDeviceUniqueNosByUserNoAsync(userNo); + } } diff --git a/Src/Dlhis.Basic/Device/Controllers/DevicesController.cs b/Src/Dlhis.Basic/Device/Controllers/DevicesController.cs index 74e3ca8b..400cff09 100644 --- a/Src/Dlhis.Basic/Device/Controllers/DevicesController.cs +++ b/Src/Dlhis.Basic/Device/Controllers/DevicesController.cs @@ -1,6 +1,7 @@ using Dlhis.Basic.Device.Request; using Dlhis.Basic.Device.Response; using Dlhis.Basic.Device.Services; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; using Kurisu.Core.DataAccess.Dto; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -70,6 +71,7 @@ public class DevicesController : ControllerBase /// /// [HttpPost("save")] + [Transactional] public async Task SaveAsync(DeviceSaveRequest request) { await _deviceService.SaveAsync(request); @@ -81,6 +83,7 @@ public class DevicesController : ControllerBase /// /// [HttpPost("resigter")] + [IgnoreTenant] public async Task ResigterAsync(DeviceResigterRequest request) { return await _deviceConnectService.ResigterAsync(request); @@ -128,6 +131,7 @@ public class DevicesController : ControllerBase /// /// [HttpPost("heartbeat")] + [IgnoreTenant] public async Task HeartbeatAsync(DeviceHeartbeatRequest request) { await _deviceConnectService.HeartbeatAsync(request); @@ -142,6 +146,7 @@ public class DevicesController : ControllerBase { return await _deviceService.CheckExistsAsync(deviceUniqueNo); } + /// /// 获取设备 /// @@ -194,12 +199,34 @@ public class DevicesController : ControllerBase } /// - /// 获取近期30天新增设备数 + /// 获取门禁在线/离线设备详情列表 + /// + /// + [HttpGet("dooroffline")] + public async Task> GetDoorOfflineAsync([FromQuery] GetDoorOfflineRequest request) + { + return await _deviceService.GetDoorOfflineAsync(request); + } + + /// + /// 获取近期30天考勤新增设备数 /// /// [HttpGet("recentnumbers")] - public async Task> GetRecentNumbersAsync() + public async Task> GetRecentNumbersAsync() { return await _deviceService.GetRecentNumbersAsync(); } + + /// + /// 批量导入设备信息 + /// + /// + /// + [HttpPost("bulkUploads")] + public async Task BulkUploadsAsync([FromForm] BulkUploadsRequest request) + { + await _deviceService.BulkUploadsAsync(request); + } + } diff --git a/Src/Dlhis.Basic/Device/Dtos/UserTenantDeviceCodeDto.cs b/Src/Dlhis.Basic/Device/Dtos/UserTenantDeviceCodeDto.cs new file mode 100644 index 00000000..1d1b3b82 --- /dev/null +++ b/Src/Dlhis.Basic/Device/Dtos/UserTenantDeviceCodeDto.cs @@ -0,0 +1,14 @@ +namespace Dlhis.Basic.Device.Dtos; + +public class UserTenantDeviceCodeDto +{ + /// + /// 租户 + /// + public string Tenant { get; set; } + + /// + /// 设备权限 + /// + public List Codes { get; set; } +} diff --git a/Src/Dlhis.Basic/Device/Request/BulkUploadsRequest.cs b/Src/Dlhis.Basic/Device/Request/BulkUploadsRequest.cs new file mode 100644 index 00000000..a0ff5fb0 --- /dev/null +++ b/Src/Dlhis.Basic/Device/Request/BulkUploadsRequest.cs @@ -0,0 +1,10 @@ +namespace Dlhis.Basic.Device.Request +{ + public class BulkUploadsRequest + { + /// + /// 设备信息文件 + /// + public IFormFile FormFile { get; set; } + } +} diff --git a/Src/Dlhis.Basic/Device/Request/DeviceGroupPageRequest.cs b/Src/Dlhis.Basic/Device/Request/DeviceGroupPageRequest.cs deleted file mode 100644 index 419810f2..00000000 --- a/Src/Dlhis.Basic/Device/Request/DeviceGroupPageRequest.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Kurisu.Core.DataAccess.Dto; - -namespace Dlhis.Basic.Device.Request; - -public class DeviceGroupPageRequest : PageDto -{ -} diff --git a/Src/Dlhis.Basic/Device/Request/DeviceGroupSaveRequest.cs b/Src/Dlhis.Basic/Device/Request/DeviceGroupSaveRequest.cs index 35240dac..73bf5281 100644 --- a/Src/Dlhis.Basic/Device/Request/DeviceGroupSaveRequest.cs +++ b/Src/Dlhis.Basic/Device/Request/DeviceGroupSaveRequest.cs @@ -10,7 +10,7 @@ public class DeviceGroupSaveRequest /// /// 上级分组 /// - public Guid? PCode { get; set; } + public Guid PCode { get; set; } /// /// 设备组名称 @@ -21,7 +21,11 @@ public class DeviceGroupSaveRequest /// /// 分类类型 DeviceTypeEnumType /// - [Required(ErrorMessage = "分类不能为空")] [EnumDataType(typeof(DeviceTypeEnumType))] - public DeviceTypeEnumType Type { get; set; } + public DeviceTypeEnumType? Type { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } } diff --git a/Src/Dlhis.Basic/Device/Request/DeviceOnlineAndofflineRequest.cs b/Src/Dlhis.Basic/Device/Request/DeviceOnlineAndofflineRequest.cs index 14b9b1b5..0100289f 100644 --- a/Src/Dlhis.Basic/Device/Request/DeviceOnlineAndofflineRequest.cs +++ b/Src/Dlhis.Basic/Device/Request/DeviceOnlineAndofflineRequest.cs @@ -2,7 +2,7 @@ namespace Dlhis.Basic.Device.Request { - public class DeviceOnlineAndofflineRequest :PageDto + public class DeviceOnlineAndofflineRequest : PageDto { public bool IsOnAndOff { get; set; } } diff --git a/Src/Dlhis.Basic/Device/Request/DevicePermissionSaveReuqest.cs b/Src/Dlhis.Basic/Device/Request/DevicePermissionSaveReuqest.cs index 5bca130a..85c3a613 100644 --- a/Src/Dlhis.Basic/Device/Request/DevicePermissionSaveReuqest.cs +++ b/Src/Dlhis.Basic/Device/Request/DevicePermissionSaveReuqest.cs @@ -1,9 +1,10 @@ using System.ComponentModel.DataAnnotations; using Dlhis.Entity.Sys.Enums; +using Kurisu.AspNetCore.Utils.Extensions; namespace Dlhis.Basic.Device.Request; -public class DevicePermissionSaveReuqest +public class DevicePermissionSaveReuqest : IValidatableObject { /// /// 分配类型 AssignTypeEnumType @@ -12,9 +13,12 @@ public class DevicePermissionSaveReuqest public AssignTypeEnumType AssignType { get; set; } /// - /// 人员档案号 + /// 分配值 /// - [Required(ErrorMessage = "人员档案号不能为空")] + /// + /// 如果为用户,那么AssignValue=UserNo + /// + [Required(ErrorMessage = "分配值不能为空")] public string AssignValue { get; set; } /// @@ -24,7 +28,23 @@ public class DevicePermissionSaveReuqest public DeviceAssignmentTypeEnumType UniqueType { get; set; } /// - /// 分配权限值不能为空 + /// 分配权限值 /// - public List Codes { get; set; } = new List(); + public List Codes { get; set; } + + /// + /// AssignType == AssignTypeEnumType.Department 需要传入 + /// + public string TenantId { get; set; } + + public IEnumerable Validate(ValidationContext validationContext) + { + if (AssignType == AssignTypeEnumType.Department) + { + if (TenantId.IsEmpty()) + { + yield return new ValidationResult("请明确具体的厂区", new[] { nameof(TenantId) }); + } + } + } } diff --git a/Src/Dlhis.Basic/Device/Request/DeviceSaveRequest.cs b/Src/Dlhis.Basic/Device/Request/DeviceSaveRequest.cs index 10b3a65b..f7e4a84c 100644 --- a/Src/Dlhis.Basic/Device/Request/DeviceSaveRequest.cs +++ b/Src/Dlhis.Basic/Device/Request/DeviceSaveRequest.cs @@ -1,9 +1,10 @@ using System.ComponentModel.DataAnnotations; using Dlhis.Entity.Device.Enums; +using Kurisu.AspNetCore.Utils.Extensions; namespace Dlhis.Basic.Device.Request; -public class DeviceSaveRequest +public class DeviceSaveRequest : IValidatableObject { public long? Id { get; set; } @@ -42,7 +43,6 @@ public class DeviceSaveRequest /// /// 心跳间隔 /// - [Range(0, int.MaxValue)] public int HeartbeatInterval { get; set; } /// @@ -70,4 +70,22 @@ public class DeviceSaveRequest /// 是否启用 /// public bool Enable { get; set; } + + /// + /// 是否作为考勤设备 + /// + public bool AsAttendance { get; set; } + + public IEnumerable Validate(ValidationContext validationContext) + { + if (Type != DeviceTypeEnumType.Door && AsAttendance) + { + yield return new ValidationResult($"{DeviceTypeEnumType.Door.GetDescription()}才可设置作为考勤设备", new[] { nameof(AsAttendance) }); + } + + if (HeartbeatInterval != 0 && HeartbeatInterval < 30) + { + yield return new ValidationResult($"心跳间隔需要设置>=30秒", new[] { nameof(HeartbeatInterval) }); + } + } } diff --git a/Src/Dlhis.Basic/Device/Request/DeviceSetResigterBusinessPlaceRequest.cs b/Src/Dlhis.Basic/Device/Request/DeviceSetResigterBusinessPlaceRequest.cs index ef87f679..fb750ea1 100644 --- a/Src/Dlhis.Basic/Device/Request/DeviceSetResigterBusinessPlaceRequest.cs +++ b/Src/Dlhis.Basic/Device/Request/DeviceSetResigterBusinessPlaceRequest.cs @@ -10,6 +10,6 @@ /// /// 营业点 /// - public Guid BusinessPlaceCode { get; set; } + public Guid? BusinessPlaceCode { get; set; } } } diff --git a/Src/Dlhis.Basic/Device/Request/ImportDeviceSaveRequest.cs b/Src/Dlhis.Basic/Device/Request/ImportDeviceSaveRequest.cs new file mode 100644 index 00000000..eddbcaed --- /dev/null +++ b/Src/Dlhis.Basic/Device/Request/ImportDeviceSaveRequest.cs @@ -0,0 +1,76 @@ +using Dlhis.Entity.Device.Enums; +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Basic.Device.Request +{ + public class ImportDeviceSaveRequest + { + + public long? Id { get; set; } + + /// + /// 设备唯一号 + /// + public string UniqueNo { get; set; } + + /// + /// 模板code + /// + public Guid TemplateCode { get; set; } + + /// + /// 供应商信息 + /// + public string Supplier { get; set; } + + /// + /// 设备功能 + /// + public List Funcs { get; set; } + + /// + /// 设备类型 DeviceTypeEnumType + /// + public DeviceTypeEnumType Type { get; set; } + + /// + /// 心跳间隔 + /// + public int HeartbeatInterval { get; set; } + + /// + /// 部署点 + /// + public string DeployPlace { get; set; } + + /// + /// 部署坐标 + /// + public string DeployPosition { get; set; } + + /// + /// 部署方向 + /// + public string DeployDirection { get; set; } + + /// + /// 连接信息 + /// + public string ConnectionInfo { get; set; } + + /// + /// 是否启用 + /// + public bool Enable { get; set; } + + /// + /// 是否作为考勤设备 + /// + public bool AsAttendance { get; set; } + + /// + /// 营业点code + /// + public Guid? PlaceeCode { get; set; } + } +} diff --git a/Src/Dlhis.Basic/Device/Response/DeviceGroupPageResponse.cs b/Src/Dlhis.Basic/Device/Response/DeviceGroupPageResponse.cs index 446557ed..941a4487 100644 --- a/Src/Dlhis.Basic/Device/Response/DeviceGroupPageResponse.cs +++ b/Src/Dlhis.Basic/Device/Response/DeviceGroupPageResponse.cs @@ -1,4 +1,7 @@ -namespace Dlhis.Basic.Device.Response; +using Dlhis.Entity.Device.Enums; +using Kurisu.AspNetCore.Utils.Extensions; + +namespace Dlhis.Basic.Device.Response; public class DeviceGroupPageResponse { @@ -11,4 +14,30 @@ public class DeviceGroupPageResponse /// 设备组名称 /// public string Name { get; set; } + + /// + /// 分组类型 + /// + public DeviceTypeEnumType? Type { get; set; } + + /// + /// 是否可编辑 + /// + public bool Editable { get; set; } + + /// + /// 分组类型描述 + /// + public string TypeDesc + { + get + { + if (!Type.HasValue) + { + return string.Empty; + } + + return Type.Value.GetDescription(); + } + } } diff --git a/Src/Dlhis.Basic/Device/Response/DeviceGroupTreeResponse.cs b/Src/Dlhis.Basic/Device/Response/DeviceGroupTreeResponse.cs index 6e9588ea..b1b919d9 100644 --- a/Src/Dlhis.Basic/Device/Response/DeviceGroupTreeResponse.cs +++ b/Src/Dlhis.Basic/Device/Response/DeviceGroupTreeResponse.cs @@ -1,7 +1,14 @@ -namespace Dlhis.Basic.Device.Response; +using Dlhis.Entity.Device.Enums; +using Kurisu.AspNetCore.Utils.Extensions; +using Newtonsoft.Json; + +namespace Dlhis.Basic.Device.Response; public class DeviceGroupTreeResponse { + public long Id { get; set; } + + /// /// 上级分组code /// @@ -17,6 +24,42 @@ public class DeviceGroupTreeResponse /// public string Name { get; set; } + /// + /// 设备组类型 + /// + public DeviceTypeEnumType? Type { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 分组类型描述 + /// + public string TypeDesc + { + get + { + if (!Type.HasValue) + { + return string.Empty; + } + + return Type.Value.GetDescription(); + } + } + + public bool Editable { get; set; } + + /// + /// 是否允许设置下级 + /// + public bool AbleSetNext { get; set; } + + [JsonIgnore] + public string TenantId { get; set; } + /// /// 下级 /// diff --git a/Src/Dlhis.Basic/Device/Response/DevicePageResponse.cs b/Src/Dlhis.Basic/Device/Response/DevicePageResponse.cs index 448e34e9..5543715e 100644 --- a/Src/Dlhis.Basic/Device/Response/DevicePageResponse.cs +++ b/Src/Dlhis.Basic/Device/Response/DevicePageResponse.cs @@ -84,4 +84,51 @@ public class DevicePageResponse /// 是否启用 /// public bool Enable { get; set; } + + + //*********************************************************附加信息**************************************************************/ + + /// + /// 最后心跳时间 + /// + public DateTime? LastHeartbeatTime { get; set; } + + /// + /// 是否在线 + /// + public bool IsOnline + { + get + { + if (!LastHeartbeatTime.HasValue) + return false; + else + { + if (HeartbeatInterval <= 0) + return true; + else + return LastHeartbeatTime.Value.AddSeconds(HeartbeatInterval * 3) >= DateTime.Now; + } + } + } + + /// + /// 消费总额 + /// + public decimal Amount { get; set; } + + /// + /// 消费人次 + /// + public int UserCount { get; set; } + + /// + /// 营业点code + /// + public Guid? BusinessPlaceCode { get; set; } + + /// + /// 营业点名称 + /// + public string BusinessPlaceName { get; set; } } diff --git a/Src/Dlhis.Basic/Device/Response/DeviceResponse.cs b/Src/Dlhis.Basic/Device/Response/DeviceResponse.cs index d28b8650..642a250a 100644 --- a/Src/Dlhis.Basic/Device/Response/DeviceResponse.cs +++ b/Src/Dlhis.Basic/Device/Response/DeviceResponse.cs @@ -88,6 +88,9 @@ public class DeviceResponse } } + public DeviceTypeEnumType Type { get; set; } + public string TypeDesc => Type.GetDescription(); + /// /// 连接信息 /// diff --git a/Src/Dlhis.Basic/Device/Response/DeviceUnderGroupResponse.cs b/Src/Dlhis.Basic/Device/Response/DeviceUnderGroupResponse.cs index 806a7d9b..c9d59404 100644 --- a/Src/Dlhis.Basic/Device/Response/DeviceUnderGroupResponse.cs +++ b/Src/Dlhis.Basic/Device/Response/DeviceUnderGroupResponse.cs @@ -1,4 +1,6 @@ -namespace Dlhis.Basic.Device.Response; +using Dlhis.Entity.Device.Enums; + +namespace Dlhis.Basic.Device.Response; public class DeviceUnderGroupResponse { @@ -14,6 +16,12 @@ public class DeviceUnderGroupResponse /// public string Brand { get; set; } + /// + /// 设备类型 + /// + public DeviceTypeEnumType Type { get; set; } + + /// /// 是否启用 /// diff --git a/Src/Dlhis.Basic/Device/Response/GetDoorOfflineRequest.cs b/Src/Dlhis.Basic/Device/Response/GetDoorOfflineRequest.cs new file mode 100644 index 00000000..89d4eff4 --- /dev/null +++ b/Src/Dlhis.Basic/Device/Response/GetDoorOfflineRequest.cs @@ -0,0 +1,9 @@ +using Kurisu.Core.DataAccess.Dto; + +namespace Dlhis.Basic.Device.Response +{ + public class GetDoorOfflineRequest : PageDto + { + public bool IsOnAndOff { get; set; } + } +} diff --git a/Src/Dlhis.Basic/Device/Response/RecentNumberResponse.cs b/Src/Dlhis.Basic/Device/Response/RecentNumberResponse.cs new file mode 100644 index 00000000..ce0de9ea --- /dev/null +++ b/Src/Dlhis.Basic/Device/Response/RecentNumberResponse.cs @@ -0,0 +1,9 @@ +namespace Dlhis.Basic.Device.Response +{ + public class RecentNumberResponse + { + public string Date { get; set; } + + public int Num { get; set; } + } +} diff --git a/Src/Dlhis.Basic/Device/Services/IDeviceGroupService.cs b/Src/Dlhis.Basic/Device/Services/IDeviceGroupService.cs index e759376e..4335fce8 100644 --- a/Src/Dlhis.Basic/Device/Services/IDeviceGroupService.cs +++ b/Src/Dlhis.Basic/Device/Services/IDeviceGroupService.cs @@ -1,14 +1,12 @@ using Dlhis.Basic.Device.Request; using Dlhis.Basic.Device.Response; -using Kurisu.Core.DataAccess.Dto; +using Dlhis.Entity.Device.Enums; namespace Dlhis.Basic.Device.Services; public interface IDeviceGroupService : IScopeDependency { - Task> GetPageAsync(DeviceGroupPageRequest request); - - Task> GetTreeAsync(bool showDevices); + Task> GetTreeAsync(bool showDevices, DeviceTypeEnumType? type); Task SaveAsync(DeviceGroupSaveRequest request); diff --git a/Src/Dlhis.Basic/Device/Services/IDevicePermissionService.cs b/Src/Dlhis.Basic/Device/Services/IDevicePermissionService.cs index 040d00ac..ef313da0 100644 --- a/Src/Dlhis.Basic/Device/Services/IDevicePermissionService.cs +++ b/Src/Dlhis.Basic/Device/Services/IDevicePermissionService.cs @@ -50,6 +50,14 @@ public interface IDevicePermissionService : IScopeDependency /// Task> GetAssignDeviceCodesAsync(string userNo); + + /// + /// 获取就餐权限唯一号 + /// + /// + /// + Task> GetCanteenDeviceUniqueNosByUserNoAsync(string userNo); + /// /// 获取用户权限设备 /// diff --git a/Src/Dlhis.Basic/Device/Services/IDeviceService.cs b/Src/Dlhis.Basic/Device/Services/IDeviceService.cs index 745702d4..a5549121 100644 --- a/Src/Dlhis.Basic/Device/Services/IDeviceService.cs +++ b/Src/Dlhis.Basic/Device/Services/IDeviceService.cs @@ -23,5 +23,11 @@ public interface IDeviceService : IScopeDependency Task> GetAttendanceOfflineAsync(DeviceOnlineAndofflineRequest request); - Task> GetRecentNumbersAsync(); + Task> GetDoorOfflineAsync(GetDoorOfflineRequest request); + + Task> GetRecentNumbersAsync(); + + Task BulkUploadsAsync(BulkUploadsRequest request); + + } diff --git a/Src/Dlhis.Basic/Device/Services/Implements/DeviceConnectService.cs b/Src/Dlhis.Basic/Device/Services/Implements/DeviceConnectService.cs index e507e5e5..810c377d 100644 --- a/Src/Dlhis.Basic/Device/Services/Implements/DeviceConnectService.cs +++ b/Src/Dlhis.Basic/Device/Services/Implements/DeviceConnectService.cs @@ -3,9 +3,7 @@ using Dlhis.Basic.Device.Response; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Device.Entities; using Dlhis.Entity.Device.Enums; -using Kurisu.AspNetCore.UnifyResultAndValidation; using Kurisu.AspNetCore.Utils.Extensions; -using Kurisu.SqlSugar.Attributes; using Kurisu.SqlSugar.Services; using Mapster; @@ -20,10 +18,10 @@ public class DeviceConnectService : IDeviceConnectService _dbContext = dbContext; } - [IgnoreTenant] + public async Task ResigterAsync(DeviceResigterRequest request) { - var businessPlace = await _dbContext.Queryable().Where(x => x.CustomCode == request.BusinessCustomCode).FirstAsync(); + var businessPlace = await _dbContext.Queryable().Where(x => x.CustomCode == request.BusinessCustomCode).FirstAsync(); businessPlace.ThrowIfNull("营业点不存在"); var device = await GetEnableDeviceAsync(request.UniqueNo, false, request.DeployPlace, businessPlace.TenantId); @@ -55,7 +53,7 @@ public class DeviceConnectService : IDeviceConnectService public async Task> GetNotSetBusinessPlaceDevicesAsync() { var devices = await _dbContext.Queryable().Where(x => x.Enable).ToListAsync(); - devices = devices.Where(x => x.Funcs.Any(item => item == DeviceFuncEnumType.Meal)).ToList(); + devices = devices.Where(x => x.Type == DeviceTypeEnumType.Meal).ToList(); return devices.Adapt>(); } @@ -63,26 +61,31 @@ public class DeviceConnectService : IDeviceConnectService public async Task SetResigterBusinessPlaceAsync(DeviceSetResigterBusinessPlaceRequest request) { var device = await GetEnableDeviceAsync(request.UniqueNo); - if (device.Funcs?.Any(x => x == DeviceFuncEnumType.Meal) != true) - { - throw new UserFriendlyException("当前设备无用餐功能,无法设置"); - } + (device.Type != DeviceTypeEnumType.Meal).ThrowIfTrue("非就餐类型设备,无法设置"); var registry = await _dbContext.Queryable().Where(x => x.DeviceUniqueNo == request.UniqueNo).FirstAsync(); registry.ThrowIfNull($"{request.UniqueNo}的设备注册信息不存在,无法设置营业点"); - var businessPlace = await _dbContext.Queryable().Where(x => x.Code == request.BusinessPlaceCode).FirstAsync(); - businessPlace.ThrowIfNull("营业点不存在,无法设置到设备注册信息中"); + if (request.BusinessPlaceCode.HasValue) + { + var businessPlace = await _dbContext.Queryable().Where(x => x.Code == request.BusinessPlaceCode).FirstAsync(); + businessPlace.ThrowIfNull("营业点不存在,无法设置到设备注册信息中"); - //更新营业点信息 - registry.BusinessPlaceCode = businessPlace.Code; - registry.BusinessPlaceCustomCode = businessPlace.CustomCode; + //更新营业点信息 + registry.BusinessPlaceCode = businessPlace.Code; + registry.BusinessPlaceCustomCode = businessPlace.CustomCode; + } + else + { + //更新营业点信息 + registry.BusinessPlaceCode = null; + registry.BusinessPlaceCustomCode = null; + } await _dbContext.UpdateAsync(registry); - //todo 通知设备重新获取 } - [IgnoreTenant] + public async Task HeartbeatAsync(DeviceHeartbeatRequest request) { var registry = await _dbContext.Queryable().Where(x => x.DeviceUniqueNo == request.UniqueNo).FirstAsync(); diff --git a/Src/Dlhis.Basic/Device/Services/Implements/DeviceGroupService.cs b/Src/Dlhis.Basic/Device/Services/Implements/DeviceGroupService.cs index 3047dc21..3c8797d6 100644 --- a/Src/Dlhis.Basic/Device/Services/Implements/DeviceGroupService.cs +++ b/Src/Dlhis.Basic/Device/Services/Implements/DeviceGroupService.cs @@ -2,10 +2,9 @@ using Dlhis.Basic.Device.Response; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Device.Entities; +using Dlhis.Entity.Device.Enums; using Kurisu.AspNetCore.Utils.Extensions; -using Kurisu.Core.DataAccess.Dto; -using Kurisu.SqlSugar.Attributes; -using Kurisu.SqlSugar.Extensions; +using Kurisu.Core.User.Abstractions; using Kurisu.SqlSugar.Services; using Mapster; @@ -15,24 +14,20 @@ public class DeviceGroupService : IDeviceGroupService { private readonly IDbContext _dbContext; private readonly IDevicePermissionService _devicePermissionService; + private readonly ICurrentUser _currentUser; - public DeviceGroupService(IDbContext dbContext, IDevicePermissionService devicePermissionService) + public DeviceGroupService(IDbContext dbContext, IDevicePermissionService devicePermissionService, ICurrentUser currentUser) { _dbContext = dbContext; _devicePermissionService = devicePermissionService; + _currentUser = currentUser; } - public async Task> GetPageAsync(DeviceGroupPageRequest request) - { - return await _dbContext.Queryable() - .WhereIF(request.Search.IsPresent(), x => x.Name.Contains(request.Search)) - .Select() - .ToPageListAsync(request); - } - public async Task> GetTreeAsync(bool showDevices) + public async Task> GetTreeAsync(bool showDevices, DeviceTypeEnumType? type) { var deviceGroups = await _dbContext.Queryable() + .WhereIF(type.HasValue, x => x.Type == type) .Select() .ToListAsync(); @@ -48,19 +43,37 @@ public class DeviceGroupService : IDeviceGroupService public async Task SaveAsync(DeviceGroupSaveRequest request) { //检查父级节点 - await CheckPCodeExistsDeviceAsync(request.PCode); + var pGrp = await CheckPCodeAsync(request); if (!request.Id.HasValue) { var entity = request.Adapt(); - entity.Code = Guid.NewGuid(); + entity.TenantId = _currentUser.GetStringTenantId(); + await _dbContext.InsertAsync(entity); } else { var entity = await _dbContext.Queryable().Where(x => x.Id == request.Id).FirstAsync(); - entity.ThrowIfNull("设备组不存在,无法更新"); + entity.ThrowIfNull("设备分组不存在,无法更新"); + entity.Editable.ThrowIfFalse("不可编辑当前分组"); + + if (request.Type != entity.Type) + { + //检查下级分组type不一致 + var existsDiffNextGroups = await _dbContext.Queryable() + .Where(x => x.PCode == entity.Code) + .Where(x => x.Type != request.Type) + .AnyAsync(); + + existsDiffNextGroups.ThrowIfTrue("存在下级分组类型不一致,无法修改"); + + var nextDevices = await GetDevicesAsync(entity.Code); + nextDevices.Any(x => x.Type != request.Type).ThrowIfTrue("分组设备类型不匹配,无法修改类型"); + } + + request.Adapt(entity); await _dbContext.UpdateAsync(entity); } } @@ -68,16 +81,16 @@ public class DeviceGroupService : IDeviceGroupService public async Task DeleteAsync(long id) { var deviceGroup = await _dbContext.Queryable().Where(x => x.Id == id).FirstAsync(); - deviceGroup.ThrowIfNull("设备组不存在,无需删除"); + deviceGroup.ThrowIfNull("设备分组不存在,无需删除"); await _dbContext.DeleteAsync(deviceGroup); } - [Transactional] + public async Task AssignDevicesAsync(DeviceAssignRequest request) { var group = await _dbContext.Queryable().Where(x => x.Code == request.GroupCode).FirstAsync(); - group.ThrowIfNull("设备组不存在"); + group.ThrowIfNull("设备分组不存在,无法进行分配"); await CheckNoNextGroupAsync(group.Code); await CheckGroupTypeMatchAsync(request, group); @@ -86,13 +99,14 @@ public class DeviceGroupService : IDeviceGroupService await _devicePermissionService.GenerateDeviceTasksAsync(async () => { var relationships = await _dbContext.Queryable().Where(x => x.GroupCode == request.GroupCode).ToListAsync(); - await _dbContext.DeleteAsync(relationships); - + await _dbContext.DeleteReallyAsync(relationships); var entities = request.DeviceCodes.Select(x => new DeviceGroupRelationshipEntity { GroupCode = request.GroupCode, - DeviceCode = x + DeviceCode = x, + + TenantId = _currentUser.GetStringTenantId(), }).ToList(); @@ -116,28 +130,33 @@ public class DeviceGroupService : IDeviceGroupService /****************************************************** help methods *********************************************************/ /// - /// 检查父级分组存在设备 + /// 检查父级分组 /// - /// + /// /// - private async Task CheckPCodeExistsDeviceAsync(Guid? pCode) + private async Task CheckPCodeAsync(DeviceGroupSaveRequest request) { - if (pCode.HasValue) + var pGrp = await _dbContext.Queryable().Where(x => x.Code == request.PCode).FirstAsync(); + if (pGrp != null) { - var exists = await _dbContext.Queryable().Where(x => x.GroupCode == pCode).AnyAsync(); - exists.ThrowIfTrue("父级分组存在设备"); + (!pGrp.AbleSetNext && !request.Id.HasValue).ThrowIfTrue($"{pGrp.Name}不可添加下级"); + var exists = await _dbContext.Queryable().Where(x => x.GroupCode == request.PCode).AnyAsync(); + exists.ThrowIfTrue("上级分组存在设备"); } + + (pGrp != null && pGrp.Type != request.Type).ThrowIfTrue("分组类型必须与上级类型保持一致"); + return pGrp; } /// - /// + /// 检查分组是否存在下级 /// /// /// private async Task CheckNoNextGroupAsync(Guid currentCode) { var exists = await _dbContext.Queryable().Where(x => x.PCode == currentCode).AnyAsync(); - exists.ThrowIfTrue("存在下级设备分组"); + exists.ThrowIfTrue("存在下级设备分组,无法分配设备"); } /// @@ -150,10 +169,7 @@ public class DeviceGroupService : IDeviceGroupService { var devices = await _dbContext.Queryable().Where(x => request.DeviceCodes.Contains(x.Code)).ToListAsync(); //检查分组的分类控制 - if (group.Type.HasValue) - { - devices.Any(x => x.Type != group.Type).ThrowIfTrue("存在设备类型有误,无法分配到当前分组"); - } + devices.Any(x => x.Type != group.Type).ThrowIfTrue($"存在设备类型不一致,无法分配到当前{group.Type.GetDescription()}分组"); //重新赋值到请求参数中 request.DeviceCodes = devices.Select(x => x.Code).ToList(); @@ -164,15 +180,32 @@ public class DeviceGroupService : IDeviceGroupService /// 递归获取树 /// /// - /// + /// /// - private void RecursionGetNext(DeviceGroupTreeResponse current, List departments, bool showDevices) + private void RecursionGetNext(DeviceGroupTreeResponse current, List deviceGroups, bool showDevices) { - var next = departments.Where(x => x.PCode == current.Code).ToList(); + var currentTenantId = _currentUser.GetStringTenantId(); + var next = deviceGroups.Where(x => + { + if (x.PCode == current.Code) + { + if (x.TenantId.IsEmpty()) + { + return true; + } + else + { + return x.TenantId == currentTenantId; + } + } + + return false; + + }).ToList(); current.Next = next; foreach (var item in next) { - RecursionGetNext(item, departments, showDevices); + RecursionGetNext(item, deviceGroups, showDevices); } //最后层级附加设备信息 diff --git a/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionService.cs b/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionService.cs index 1fbc6c88..d46ac987 100644 --- a/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionService.cs +++ b/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionService.cs @@ -1,4 +1,5 @@ -using Dlhis.Basic.Device.Request; +using Dlhis.Basic.Device.Dtos; +using Dlhis.Basic.Device.Request; using Dlhis.Basic.Device.Response; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Device.Entities; @@ -7,159 +8,156 @@ using Dlhis.Entity.Sys.Entities; using Dlhis.Entity.Sys.Enums; using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Core.DataAccess.Dto; -using Kurisu.SqlSugar.Attributes; +using Kurisu.Core.DataAccess.Entity; +using Kurisu.Core.User.Abstractions; using Kurisu.SqlSugar.Services; -using System.Collections.Generic; namespace Dlhis.Basic.Device.Services.Implements; public class DevicePermissionService : IDevicePermissionService { private readonly IDbContext _dbContext; + private readonly ICurrentUser _currentUser; - public DevicePermissionService(IDbContext dbContext) + public DevicePermissionService(IDbContext dbContext, ICurrentUser currentUser) { _dbContext = dbContext; + _currentUser = currentUser; } - [Transactional] public async Task AssignAsync(DevicePermissionSaveReuqest reuqest) { + var tenantId = reuqest.AssignType == AssignTypeEnumType.Department ? reuqest.TenantId : _currentUser.GetStringTenantId(); + + //删除当前匹配的基地权限 + var p = await _dbContext.Queryable() + .Where(x => x.AssignType == reuqest.AssignType) + .Where(x => x.AssignValue == reuqest.AssignValue) + .Where(x => x.UniqueType == reuqest.UniqueType) + .Where(x => x.TenantId == tenantId) + .ToListAsync(); + + await _dbContext.DeleteReallyAsync(p); + await GenerateDeviceTasksAsync(async () => { - - var p = await _dbContext.Queryable() - .Where(x => x.AssignType == reuqest.AssignType) - .Where(x => x.AssignValue == reuqest.AssignValue) - .Where(x => x.UniqueType == reuqest.UniqueType) - .ToListAsync(); - - await _dbContext.DeleteReallyAsync(p); - - var devicePermissions = reuqest.Codes.Select(x => new DevicePermissionEntity + //保存配置的权限 + if (reuqest.Codes.IsPresent()) { - AssignType = reuqest.AssignType, - AssignValue = reuqest.AssignValue, - UniqueType = reuqest.UniqueType, - UniqueValue = x, + var devicePermissions = reuqest.Codes.Select(code => new DevicePermissionEntity + { + AssignType = reuqest.AssignType, + AssignValue = reuqest.AssignValue, + UniqueType = reuqest.UniqueType, + UniqueValue = code, + TenantId = tenantId + }) + .ToList(); - }).ToList(); - - await _dbContext.InsertAsync(devicePermissions); + await _dbContext.InsertAsync(devicePermissions); + } }, null); } public async Task> GetAssignmentsAsync(DeviceAssignmentTypeEnumType uniqueType, AssignTypeEnumType assignType, string assignValue) { - var devicePermissions = await _dbContext.Queryable() - .Where(x => x.UniqueType == uniqueType) - .Where(x => x.AssignType == assignType) - .Where(x => x.AssignValue == assignValue) - .Select(x => x.UniqueValue) - .ToListAsync(); + var devicePermissions = await _dbContext.Queryable().ToListAsync(); + var deviceGroups = await _dbContext.Queryable().ToListAsync(); - if (uniqueType == DeviceAssignmentTypeEnumType.Group) - { - var deviceGroups = await _dbContext.Queryable().ToListAsync(); - var tops = deviceGroups.Where(x => !x.PCode.HasValue).ToList(); - tops.ForEach(x => - { - RecursionGetNext(x, deviceGroups, devicePermissions, devicePermissions.Contains(x.Code)); - }); - } - - return devicePermissions.Distinct().ToList(); - - void RecursionGetNext(DeviceGroupEntity current, List deviceGroups, List result, bool add) - { - var next = deviceGroups.Where(x => x.PCode == current.Code).ToList(); - foreach (var item in next) - { - if (add) result.Add(item.Code); - RecursionGetNext(item, deviceGroups, result, result.Contains(item.Code)); - } - } + var result = GetAssignCodesInternal(devicePermissions, deviceGroups, uniqueType, assignType, assignValue); + return result.FirstOrDefault(x => x.Tenant == _currentUser.GetStringTenantId())?.Codes; } public async Task GenerateDeviceTasksAsync(Func func, List userNos) { - if (userNos.IsEmpty()) + _dbContext.Client.QueryFilter.ClearAndBackup(); + try { - userNos = await GetPermissionUserNosAsync(); - } + //获取所有人的权限 + if (userNos.IsEmpty()) + userNos = await GetPermissionUserNosAsync(); - var beforeDic = await GetAssignDeviceCodesAsync(userNos); + var before = await GetAssignDeviceCodesInternalAsync(userNos); - //获取处理 - await func(); + _dbContext.Client.QueryFilter.Restore(); + //业务处理 + await func(); + _dbContext.Client.QueryFilter.ClearAndBackup(); - //获取当前的权限code + //获取业务处理后所有人的权限 + var nextUserNos = await GetPermissionUserNosAsync(); + if (nextUserNos.Any()) + userNos = userNos.Concat(nextUserNos).Distinct().ToList(); - var nextUserNos = await GetPermissionUserNosAsync(); - if (nextUserNos.Any()) - { - userNos = nextUserNos; - } + var current = await GetAssignDeviceCodesInternalAsync(userNos); - var currentDic = await GetAssignDeviceCodesAsync(userNos); + var deivces = await _dbContext.Queryable().ToListAsync(); + var waitTasks = new List(); - var deivces = await _dbContext.Queryable().ToListAsync(); - foreach (var userNo in userNos) - { - beforeDic.TryGetValue(userNo, out var beforeCodes); - beforeCodes ??= new List(); - - currentDic.TryGetValue(userNo, out var currentCodes); - currentCodes ??= new List(); - - //被删除 - var deletedItems = beforeCodes.Where(x => !currentCodes.Contains(x)).ToList(); - //新增 - var addItems = currentCodes.Where(x => !beforeCodes.Contains(x)).ToList(); - - if (!deletedItems.Any() && !addItems.Any()) + foreach (var userNo in userNos) { - continue; + before.TryGetValue(userNo, out var beforeTenantDeviceCodes); + beforeTenantDeviceCodes ??= new(); + + current.TryGetValue(userNo, out var currentTenantDeviceCodes); + currentTenantDeviceCodes ??= new(); + + var tenants = beforeTenantDeviceCodes.Select(x => x.Tenant).Concat(currentTenantDeviceCodes.Select(x => x.Tenant)).Distinct().ToList(); + + foreach (var tenant in tenants) + { + var currentCodes = currentTenantDeviceCodes.FirstOrDefault(x => x.Tenant == tenant)?.Codes ?? new(); + var beforeCodes = beforeTenantDeviceCodes.FirstOrDefault(x => x.Tenant == tenant)?.Codes ?? new(); + + //被删除 + var deletedItems = beforeCodes.Where(x => !currentCodes.Contains(x)).ToList(); + //新增 + var addItems = currentCodes.Where(x => !beforeCodes.Contains(x)).ToList(); + + if (!deletedItems.Any() && !addItems.Any()) + { + continue; + } + + var deviceCodes = deivces.Where(x => deletedItems.Contains(x.Code) || addItems.Contains(x.Code)).Select(x => x.Code).ToList(); + deletedItems = deletedItems.Where(x => deviceCodes.Contains(x)).ToList(); + addItems = addItems.Where(x => deviceCodes.Contains(x)).ToList(); + + //下发任务创建 + var toDeleted = deletedItems.Select(x => new DeviceTaskEntity + { + DeviceUniqueNo = deivces.First(item => item.Code == x).UniqueNo, + Status = DeviceTaskStatusEnumType.Wait, + Type = DeviceTaskEnumType.Delete, + UserNo = userNo, + Version = 0, + TenantId = tenant, + }).ToList(); + + var toAdd = addItems.Select(x => new DeviceTaskEntity + { + DeviceUniqueNo = deivces.First(item => item.Code == x).UniqueNo, + Status = DeviceTaskStatusEnumType.Wait, + Type = DeviceTaskEnumType.Add, + UserNo = userNo, + Version = 0, + TenantId = tenant, + }).ToList(); + + waitTasks.AddRange(toDeleted.Concat(toAdd).ToList()); + } } - var deviceCodes = deivces.Where(x => deletedItems.Contains(x.Code) || addItems.Contains(x.Code)).Select(x => x.Code).ToList(); - deletedItems = deletedItems.Where(x => deviceCodes.Contains(x)).ToList(); - addItems = addItems.Where(x => deviceCodes.Contains(x)).ToList(); - - var userInfo = await _dbContext.Queryable() - .LeftJoin((ga, department) => department.No == ga.DepartmentNo) - .Where(ga => ga.No == userNo) - .Select(ga => new - { - ga.Code - }) - .SingleAsync(); - - //下发任务创建 - var toDeleted = deletedItems.Select(x => new DeviceTaskEntity + if (waitTasks.Any()) { - DeviceUniqueNo = deivces.First(item => item.Code == x).UniqueNo, - Status = DeviceTaskStatusEnumType.Wait, - Type = DeviceTaskEnumType.Delete, - UserNo = userNo, - Version = 0, - UserCode = userInfo.Code, - }).ToList(); - - var toAdd = addItems.Select(x => new DeviceTaskEntity - { - DeviceUniqueNo = deivces.First(item => item.Code == x).UniqueNo, - Status = DeviceTaskStatusEnumType.Wait, - Type = DeviceTaskEnumType.Add, - UserNo = userNo, - Version = 0, - UserCode = userInfo.Code, - }).ToList(); - - var tasks = toDeleted.Concat(toAdd).ToList(); - await _dbContext.InsertAsync(tasks); + await _dbContext.InsertAsync(waitTasks); + } + } + finally + { + _dbContext.Client.QueryFilter.Restore(); } } @@ -183,7 +181,6 @@ public class DevicePermissionService : IDevicePermissionService }) .SingleAsync(); - var tasks = deviceCodes.Select(x => new DeviceTaskEntity { DeviceUniqueNo = deivces.First(item => item.Code == x).UniqueNo, @@ -191,13 +188,23 @@ public class DevicePermissionService : IDevicePermissionService Type = taskType, UserNo = userNo, Version = 0, - UserCode = userInfo.Code, }).ToList(); await _dbContext.InsertAsync(tasks); } + public async Task> GetCanteenDeviceUniqueNosByUserNoAsync(string userNo) + { + var all = await GetAssignDeviceCodesAsync(userNo); + + return await _dbContext.Queryable().Where(x => x.Type == DeviceTypeEnumType.Meal) + .Where(x => all.Contains(x.Code)) + .Select(x => x.UniqueNo) + .ToListAsync(); + } + + /// /// 获取用户所有设备权限 /// @@ -206,125 +213,17 @@ public class DevicePermissionService : IDevicePermissionService public async Task> GetAssignDeviceCodesAsync(string userNo) { var gainfo = await _dbContext.Queryable().Where(x => x.No == userNo).SingleAsync(); - var gainfoGroups = await _dbContext.Queryable().Where(x => x.UserNo == userNo).Select(x => x.GroupCode).ToListAsync(); + var gainfoGroups = await _dbContext.Queryable().ToListAsync(); - var deviceCodes = new List(); - var deviceGroupCodes = new List(); - - var du = await GetAssignmentsAsync(DeviceAssignmentTypeEnumType.Device, AssignTypeEnumType.User, userNo); - var gu = await GetAssignmentsAsync(DeviceAssignmentTypeEnumType.Group, AssignTypeEnumType.User, userNo); - deviceCodes.AddRange(du); - deviceGroupCodes.AddRange(gu); - - foreach (var group in gainfoGroups) - { - var dg = await GetAssignmentsAsync(DeviceAssignmentTypeEnumType.Device, AssignTypeEnumType.Group, group.ToString()); - var gg = await GetAssignmentsAsync(DeviceAssignmentTypeEnumType.Group, AssignTypeEnumType.Group, group.ToString()); - deviceCodes.AddRange(dg); - deviceGroupCodes.AddRange(gg); - } - - var dd = await GetAssignmentsAsync(DeviceAssignmentTypeEnumType.Device, AssignTypeEnumType.Department, gainfo.DepartmentNo); - var gd = await GetAssignmentsAsync(DeviceAssignmentTypeEnumType.Group, AssignTypeEnumType.Department, gainfo.DepartmentNo); - deviceCodes.AddRange(dd); - deviceGroupCodes.AddRange(gd); - - var dt = await GetAssignmentsAsync(DeviceAssignmentTypeEnumType.Device, AssignTypeEnumType.Tenant, gainfo.Werks); - var gt = await GetAssignmentsAsync(DeviceAssignmentTypeEnumType.Group, AssignTypeEnumType.Tenant, gainfo.Werks); - deviceCodes.AddRange(du); - deviceGroupCodes.AddRange(gu); - - var deveiceCodesByGroup = await _dbContext.Queryable().Where(x => deviceGroupCodes.Contains(x.GroupCode)).Select(x => x.DeviceCode).ToListAsync(); - deviceCodes.AddRange(deveiceCodesByGroup); - - return deviceCodes.Distinct().ToList(); - } - - private async Task>> GetAssignDeviceCodesAsync(List userNos) - { - var gainfos = await _dbContext.Queryable().Where(x => userNos.Contains(x.No)).ToListAsync(); - if (!gainfos.Any()) - { - return new Dictionary>(); - } - var gainfoGroupRelationships = await _dbContext.Queryable().ToListAsync(); var devicePermissions = await _dbContext.Queryable().ToListAsync(); var deviceGroups = await _dbContext.Queryable().ToListAsync(); + var deviceRelationships = await _dbContext.Queryable().ToListAsync(); - var result = new Dictionary>(); - - foreach (var gainfo in gainfos) - { - var userNo = gainfo.No; - - var gainfoGroups = gainfoGroupRelationships.Where(x => x.UserNo == userNo).ToList(); - - var deviceCodes = new List(); - var deviceGroupCodes = new List(); - - var du = GetAssignmentsInternal(DeviceAssignmentTypeEnumType.Device, AssignTypeEnumType.User, userNo); - var gu = GetAssignmentsInternal(DeviceAssignmentTypeEnumType.Group, AssignTypeEnumType.User, userNo); - deviceCodes.AddRange(du); - deviceGroupCodes.AddRange(gu); - - foreach (var group in gainfoGroups) - { - var dg = GetAssignmentsInternal(DeviceAssignmentTypeEnumType.Device, AssignTypeEnumType.Group, group.ToString()); - var gg = GetAssignmentsInternal(DeviceAssignmentTypeEnumType.Group, AssignTypeEnumType.Group, group.ToString()); - deviceCodes.AddRange(dg); - deviceGroupCodes.AddRange(gg); - } - - var dd = GetAssignmentsInternal(DeviceAssignmentTypeEnumType.Device, AssignTypeEnumType.Department, gainfo.DepartmentNo); - var gd = GetAssignmentsInternal(DeviceAssignmentTypeEnumType.Group, AssignTypeEnumType.Department, gainfo.DepartmentNo); - deviceCodes.AddRange(dd); - deviceGroupCodes.AddRange(gd); - - var dt = GetAssignmentsInternal(DeviceAssignmentTypeEnumType.Device, AssignTypeEnumType.Tenant, gainfo.Werks); - var gt = GetAssignmentsInternal(DeviceAssignmentTypeEnumType.Group, AssignTypeEnumType.Tenant, gainfo.Werks); - deviceCodes.AddRange(du); - deviceGroupCodes.AddRange(gu); - - var deveiceCodesByGroup = await _dbContext.Queryable().Where(x => deviceGroupCodes.Contains(x.GroupCode)).Select(x => x.DeviceCode).ToListAsync(); - deviceCodes.AddRange(deveiceCodesByGroup); - - result.Add(userNo, deviceCodes.Distinct().ToList()); - } - - return result; - - - List GetAssignmentsInternal(DeviceAssignmentTypeEnumType uniqueTypeInternal, AssignTypeEnumType assignTypeInternal, string assignValueInternal) - { - var devicePermissionsInternal = devicePermissions.Where(x => x.UniqueType == uniqueTypeInternal) - .Where(x => x.AssignType == assignTypeInternal) - .Where(x => x.AssignValue == assignValueInternal) - .Select(x => x.UniqueValue) - .ToList(); - - if (uniqueTypeInternal == DeviceAssignmentTypeEnumType.Group) - { - var tops = deviceGroups.Where(x => !x.PCode.HasValue).ToList(); - tops.ForEach(x => - { - RecursionGetNext(x, deviceGroups, devicePermissionsInternal, devicePermissionsInternal.Contains(x.Code)); - }); - } - - return devicePermissionsInternal.Distinct().ToList(); - - void RecursionGetNext(DeviceGroupEntity current, List deviceGroups, List result, bool add) - { - var next = deviceGroups.Where(x => x.PCode == current.Code).ToList(); - foreach (var item in next) - { - if (add) result.Add(item.Code); - RecursionGetNext(item, deviceGroups, result, result.Contains(item.Code)); - } - } - } + var result = GetAssignDeviceCodesInternal(gainfo, gainfoGroups, devicePermissions, deviceGroups, deviceRelationships); + return result.SelectMany(x => x.Codes).ToList(); } + /// /// 获取用户权限设备 /// @@ -340,7 +239,7 @@ public class DevicePermissionService : IDevicePermissionService var devices = await _dbContext.Queryable().ToListAsync(); - if(ggs != null && ggs.Count > 0) + if (ggs != null && ggs.Count > 0) { foreach (var gid in ggs) { @@ -351,7 +250,7 @@ public class DevicePermissionService : IDevicePermissionService gds.AddRange(ds); } } - + var dds = gds.Distinct().ToList(); var lst = devices.Where(x => dds.Contains(x.Code)) .Select(x => new AssigmentByCodeResponse @@ -403,8 +302,169 @@ public class DevicePermissionService : IDevicePermissionService var tenants = permissions.Where(x => x.AssignType == AssignTypeEnumType.Tenant).Select(x => x.AssignValue).ToList(); var userNosByWerk = await _dbContext.Queryable().Where(x => tenants.Contains(x.Werks)).Select(x => x.No).ToListAsync(); - - return userNos.Concat(userNosByGroup).Concat(userNosByDepartment).Concat(userNosByWerk).ToList(); + return userNos.Concat(userNosByGroup).Concat(userNosByDepartment).Concat(userNosByWerk).Distinct().ToList(); } + + private static List GetAssignmentsInternal( + List devicePermissions, List deviceGroups, + DeviceAssignmentTypeEnumType uniqueType, AssignTypeEnumType assignType, string assignValue) + { + var devicePermissionCodes = devicePermissions + .Where(x => x.UniqueType == uniqueType) + .Where(x => x.AssignType == assignType) + .Where(x => x.AssignValue == assignValue) + .Select(x => x.UniqueValue).ToList(); + + //处理获取设备组的情况,递归到最下级获取设备code + if (uniqueType == DeviceAssignmentTypeEnumType.Group) + { + var tops = deviceGroups.Where(x => !x.PCode.HasValue).ToList(); + tops.ForEach(x => + { + RecursionGetNext(x, deviceGroups, devicePermissionCodes, devicePermissionCodes.Contains(x.Code)); + }); + } + + return devicePermissionCodes.Distinct().ToList(); + + void RecursionGetNext(DeviceGroupEntity current, List deviceGroups, List result, bool add) + { + var next = deviceGroups.Where(x => x.PCode == current.Code).ToList(); + foreach (var item in next) + { + if (add) result.Add(item.Code); + RecursionGetNext(item, deviceGroups, result, result.Contains(item.Code)); + } + } + } + + private static List GetAssignCodesInternal( + List devicePermissions, List deviceGroups, + DeviceAssignmentTypeEnumType uniqueType, AssignTypeEnumType assignType, string assignValue) + { + var tenantDevicePermissions = devicePermissions + .Where(x => x.UniqueType == uniqueType) + .Where(x => x.AssignType == assignType) + .Where(x => x.AssignValue == assignValue) + .GroupBy(x => x.TenantId) + .Select(x => new UserTenantDeviceCodeDto { Tenant = x.Key, Codes = x.Select(x => x.UniqueValue).ToList() }) + .ToList(); + + //处理获取设备分组的情况 + if (uniqueType == DeviceAssignmentTypeEnumType.Group) + { + //开始递归处理 + var tops = deviceGroups.Where(x => !x.PCode.HasValue).ToList(); + tops.ForEach(x => + { + RecursionGetNext(x, deviceGroups, tenantDevicePermissions, tenantDevicePermissions.SelectMany(x => x.Codes).Contains(x.Code)); + }); + } + + return tenantDevicePermissions.GroupBy(x => x.Tenant).Select(x => + { + var tenant = x.Key; + + return new UserTenantDeviceCodeDto + { + Tenant = tenant, + Codes = x.SelectMany(item => item.Codes).ToList() + }; + }).ToList(); + + void RecursionGetNext(DeviceGroupEntity current, List deviceGroups, List result, bool add) + { + var next = deviceGroups.Where(x => x.PCode == current.Code).ToList(); + foreach (var item in next) + { + if (add) + { + result.Add(new UserTenantDeviceCodeDto { Tenant = item.TenantId, Codes = new List { item.Code } }); + } + RecursionGetNext(item, deviceGroups, result, result.SelectMany(x => x.Codes).Contains(item.Code)); + } + } + } + + private async Task>> GetAssignDeviceCodesInternalAsync(List userNos) + { + var gainfos = await _dbContext.Queryable().Where(x => userNos.Contains(x.No)).ToListAsync(); + if (!gainfos.Any()) + { + return new Dictionary>(); + } + + var gainfoGroupRelationships = await _dbContext.Queryable().Where(x => userNos.Contains(x.UserNo)).ToListAsync(); + var devicePermissions = await _dbContext.Queryable().ToListAsync(); + var deviceGroups = await _dbContext.Queryable().ToListAsync(); + var deviceRelationships = await _dbContext.Queryable().ToListAsync(); + + var result = new Dictionary>(gainfos.Count); + + foreach (var gainfo in gainfos) + { + var codes = GetAssignDeviceCodesInternal(gainfo, gainfoGroupRelationships, devicePermissions, deviceGroups, deviceRelationships); + result.Add(gainfo.No, codes); + } + + return result; + } + + private static List GetAssignDeviceCodesInternal( + GainfoEntity gainfo, + List gainfoGroupRelationships, + List devicePermissions, + List deviceGroups, + List deviceGroupRelationships) + { + var userNo = gainfo.No; + var deviceCodes = new List(); + var deviceGroupCodes = new List(); + + //获取人员的分组,跨基地 + var gainfoGroups = gainfoGroupRelationships.Where(x => x.UserNo == userNo).Select(x => x.GroupCode).ToList(); + + //按User获取权限 + var du = GetAssignCodesInternal(devicePermissions, deviceGroups, DeviceAssignmentTypeEnumType.Device, AssignTypeEnumType.User, userNo); + var gu = GetAssignCodesInternal(devicePermissions, deviceGroups, DeviceAssignmentTypeEnumType.Group, AssignTypeEnumType.User, userNo); + deviceCodes.AddRange(du); + deviceGroupCodes.AddRange(gu); + + foreach (var groupCode in gainfoGroups) + { + var dg = GetAssignCodesInternal(devicePermissions, deviceGroups, DeviceAssignmentTypeEnumType.Device, AssignTypeEnumType.Group, groupCode.ToString()); + var gg = GetAssignCodesInternal(devicePermissions, deviceGroups, DeviceAssignmentTypeEnumType.Group, AssignTypeEnumType.Group, groupCode.ToString()); + deviceCodes.AddRange(dg); + deviceGroupCodes.AddRange(gg); + } + + var gainfoFactoryDevicePermissions = devicePermissions.Where(x => x.TenantId == gainfo.Factory).ToList(); + var dd = GetAssignCodesInternal(gainfoFactoryDevicePermissions, deviceGroups, DeviceAssignmentTypeEnumType.Device, AssignTypeEnumType.Department, gainfo.DepartmentNo); + var gd = GetAssignCodesInternal(gainfoFactoryDevicePermissions, deviceGroups, DeviceAssignmentTypeEnumType.Group, AssignTypeEnumType.Department, gainfo.DepartmentNo); + deviceCodes.AddRange(dd); + deviceGroupCodes.AddRange(gd); + + var dt = GetAssignCodesInternal(devicePermissions, deviceGroups, DeviceAssignmentTypeEnumType.Device, AssignTypeEnumType.Tenant, gainfo.Factory); + var gt = GetAssignCodesInternal(devicePermissions, deviceGroups, DeviceAssignmentTypeEnumType.Group, AssignTypeEnumType.Tenant, gainfo.Factory); + deviceCodes.AddRange(du); + deviceGroupCodes.AddRange(gu); + + var groupCodes = deviceGroupCodes.SelectMany(x => x.Codes).Distinct().ToList(); + var deveiceCodesByGroup = deviceGroupRelationships.Where(x => groupCodes.Contains(x.GroupCode)).GroupBy(x => x.TenantId) + .Select(x => new UserTenantDeviceCodeDto + { + Tenant = x.Key, + Codes = x.Select(item => item.DeviceCode).Distinct().ToList() + }); + + deviceCodes.AddRange(deveiceCodesByGroup); + + return deviceCodes.GroupBy(x => x.Tenant).Select(x => new UserTenantDeviceCodeDto + { + Tenant = x.Key, + Codes = x.SelectMany(item => item.Codes).Distinct().ToList() + + }).ToList(); + } } diff --git a/Src/Dlhis.Basic/Device/Services/Implements/DeviceService.cs b/Src/Dlhis.Basic/Device/Services/Implements/DeviceService.cs index 28b2f66c..05184d1b 100644 --- a/Src/Dlhis.Basic/Device/Services/Implements/DeviceService.cs +++ b/Src/Dlhis.Basic/Device/Services/Implements/DeviceService.cs @@ -1,18 +1,24 @@ using Dlhis.Basic.Device.Request; using Dlhis.Basic.Device.Response; using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Canteen.Entities; +using Dlhis.Entity.Canteen.Enums; using Dlhis.Entity.Device.Entities; using Dlhis.Entity.Device.Enums; using Kurisu.AspNetCore.UnifyResultAndValidation; using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Core.DataAccess.Dto; -using Kurisu.SqlSugar.Attributes; using Kurisu.SqlSugar.Extensions; using Kurisu.SqlSugar.Services; using Mapster; using Microsoft.AspNetCore.Mvc; +using MySqlX.XDevAPI.Relational; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using NPOI.OpenXmlFormats; +using NPOI.SS.UserModel; +using NPOI.XSSF.UserModel; +using SqlSugar; namespace Dlhis.Basic.Device.Services.Implements; @@ -29,29 +35,57 @@ public class DeviceService : IDeviceService { var page = await _dbContext.Queryable() .LeftJoin((d, dt) => d.TemplateCode == dt.Code) + .LeftJoin((d, dt, dr) => d.UniqueNo == dr.DeviceUniqueNo) + .LeftJoin((d, dt, dr, bp) => dr.BusinessPlaceCode == bp.Code) .WhereIF(request.Type.HasValue, (d, dt) => d.Type == request.Type) - .Select((d, dt) => new DevicePageResponse + .Select((d, dt, dr, bp) => new DevicePageResponse { FuncsStr = d.Funcs.ToString(), Brand = dt.Brand, Type = d.Type, - + BusinessPlaceCode = bp.Code, + BusinessPlaceName = bp.Name, + LastHeartbeatTime = dr.LastHeartbeatTime }, true) .ToPageListAsync(request); + var deviceUniqueNos = page.Data.Select(x => x.UniqueNo).ToList(); + + var balanceInfos = await _dbContext.Queryable().Where(x => x.Type == BalanceRecordTypeEnumType.Out) + .Where(x => deviceUniqueNos.Contains(x.DeviceInfo)) + .Select(x => new + { + x.DeviceInfo, + Amount = SqlFunc.AggregateSum(x.Amount), + UserCount = SqlFunc.AggregateDistinctCount(x.UserCode) + }) + .GroupBy(x => x.DeviceInfo) + .ToListAsync(); + + page.Data.ForEach(device => + { + var info = balanceInfos.FirstOrDefault(x => x.DeviceInfo == device.UniqueNo); + if (info != null) + { + device.Amount = info.Amount; + device.UserCount = info.UserCount; + } + }); + return page; } - [Transactional] + public async Task SaveAsync(DeviceSaveRequest request) { await CheckUniqueNoExistsAsync(request); var template = await CheckTemplateExistsAsync(request.TemplateCode); await CheckTemplateFieldAsync(request.TemplateCode, request.ConnectionInfo); + DeviceEntity entity = null; if (!request.Id.HasValue) { - var entity = request.Adapt(); + entity = request.Adapt(); entity.Code = Guid.NewGuid(); await _dbContext.InsertAsync(entity); //同时保存注册信息 @@ -72,7 +106,7 @@ public class DeviceService : IDeviceService } else { - var entity = await _dbContext.Queryable().Where(x => x.Id == request.Id).FirstAsync(); + entity = await _dbContext.Queryable().Where(x => x.Id == request.Id).FirstAsync(); entity.ThrowIfNull("设备不存在,无法更新"); //分类发生变化,检查是否存在分组 @@ -81,21 +115,94 @@ public class DeviceService : IDeviceService var existsG = await _dbContext.Queryable() .LeftJoin((dgr, dg) => dgr.GroupCode == dg.Code) .Where((dgr, dg) => dgr.DeviceCode == entity.Code) - .Where((dgr, dg) => dg.Type != null) .AnyAsync(); existsG.ThrowIfTrue("存在设备分组,不可修改设备分类"); } - var uniqueNo = entity.UniqueNo; - request.Adapt(entity).UniqueNo = uniqueNo; - + request.Adapt(entity); await _dbContext.UpdateAsync(entity); } + + await GenerateUploadRecordLogForBeginTimeAsync(entity); + } + + /// + /// 批量导入 + /// + /// + /// + public async Task BulkUploadsAsync(BulkUploadsRequest request) + { + var lst = ImportFromExcel(request.FormFile); + var places = await _dbContext.Queryable().ToListAsync(); + + foreach (var form in lst) + { + if (form.PlaceeCode != null) + { + var place = places.Where(x => x.Code == form.PlaceeCode).FirstOrDefault(); + place.ThrowIfNull($"该设备{form.UniqueNo}配置的营业点营业点不存在!"); + } + //插入 + var exists = await _dbContext.Queryable().Where(x => x.UniqueNo == form.UniqueNo) + .AnyAsync(); + exists.ThrowIfTrue("唯一号已经存在"); + var template = await CheckTemplateExistsAsync(form.TemplateCode); + await CheckTemplateFieldAsync(form.TemplateCode, form.ConnectionInfo); + + var saveEntity = form.Adapt(); + saveEntity.Code = Guid.NewGuid(); + await _dbContext.InsertAsync(saveEntity); + //保存注册信息 + var regis = await _dbContext.Queryable() + .Where(x => x.DeviceUniqueNo == form.UniqueNo) + .FirstAsync(); + if(regis == null) + { + var custCode = places.Where(x => x.Code == form.PlaceeCode).Select(x => x.CustomCode).FirstOrDefault(); + regis = new DeviceRegistryEntity + { + DeviceUniqueNo = form.UniqueNo, + LastHeartbeatTime = DateTime.Now, + RegisterTime = DateTime.Now, + BusinessPlaceCode = form.PlaceeCode, + BusinessPlaceCustomCode = custCode + }; + await _dbContext.InsertAsync(regis); + } + } } //******************************************************** help methods ********************************************************** + private async Task GenerateUploadRecordLogForBeginTimeAsync(DeviceEntity entity) + { + if (entity.Type == DeviceTypeEnumType.Door || entity.Type == DeviceTypeEnumType.Attendance) + { + var exists = await _dbContext.Queryable() + .Where(x => x.DeviceUniqueNo == entity.UniqueNo) + .Where(x => x.Type == DeviceUploadLogTypeEnumType.Raise) + .AnyAsync(); + + if (!exists) + { + await _dbContext.InsertAsync(new DeviceUploadLogEntity + { + DeviceUniqueNo = entity.UniqueNo, + Type = DeviceUploadLogTypeEnumType.Raise, + UserUniqueNo = string.Empty, + RaiseDateTime = entity.CreateTime, + + CreatedBy = 1, + CreateTime = DateTime.Now, + ModifiedBy = 1, + ModifiedTime = DateTime.Now, + }); + } + } + } + /// /// 检查设备模板是否存在 /// @@ -148,14 +255,15 @@ public class DeviceService : IDeviceService /// private async Task CheckUniqueNoExistsAsync(DeviceSaveRequest request) { - var exists = await _dbContext.Queryable().Where(x => x.UniqueNo == request.UniqueNo) + var exists = await _dbContext.Queryable() + .Where(x => x.UniqueNo == request.UniqueNo) .WhereIF(request.Id.HasValue, x => x.Id != request.Id) .AnyAsync(); exists.ThrowIfTrue("唯一号已经存在"); } - [IgnoreTenant] + public async Task CheckExistsAsync(string deviceUniqueNo) { return await _dbContext.Queryable() @@ -187,36 +295,81 @@ public class DeviceService : IDeviceService { var devices = await _dbContext.Queryable() .Where(x => x.Enable) + .Where(x => x.Type == DeviceTypeEnumType.Attendance || (x.Type == DeviceTypeEnumType.Door && x.AsAttendance)) .Select(x => new DeviceResponse { FuncsStr = x.Funcs.ToString() }, true) .ToListAsync(); - return devices.Where(x => x.Funcs.Contains(DeviceFuncEnumType.Attendance)).ToList(); + return devices.Where(x => x.Type == DeviceTypeEnumType.Attendance).ToList(); } public async Task GetDeviceNumberAsync() { - var devices = await _dbContext.Queryable() - .Where(x => x.Type == DeviceTypeEnumType.Attendance) - .ToListAsync(); - var onlineNumber = devices.Where(x => x.Enable).Count(); - var offlineNumber = devices.Where(x => x.Enable == false).Count(); + var devices = await _dbContext.Queryable().Where(x => x.Type == DeviceTypeEnumType.Attendance || (x.Type == DeviceTypeEnumType.Door && x.AsAttendance)).ToListAsync(); + + var offlineNumber = await GetOfflineDevicesCount(devices); + var res = new DeviceNumberResponse { - OnlineNumber = onlineNumber, - OfflineNumber = offlineNumber, - TotalNumber = devices.Count() + OfflineNumber = offlineNumber.Count, + TotalNumber = devices.Count, + OnlineNumber = devices.Count - offlineNumber.Count }; return res; } + /// + /// 获取离线设备 + /// + /// + private async Task> GetOfflineDevicesCount(List devices) + { + var now = DateTime.Now; + + var deviceUniqueNos = devices.Select(x => x.UniqueNo).ToList(); + + var deviceRegistries = await _dbContext.Queryable() + .LeftJoin((x, d) => x.DeviceUniqueNo == d.UniqueNo) + .Where(x => deviceUniqueNos.Contains(x.DeviceUniqueNo)) + .Select((x, d) => new + { + UniqueNo = d.UniqueNo, + d.HeartbeatInterval, + x.LastHeartbeatTime + }) + .ToListAsync(); + + var OnlineDevices = deviceRegistries + .Where(x => + { + if (x.HeartbeatInterval <= 0) + { + return true; + } + + return x.LastHeartbeatTime.AddSeconds(x.HeartbeatInterval * 3) >= now; + + }) + .Select(x => new + { + UniqueNo = x.UniqueNo, + }) + .ToList(); + var onlineUniqueNos = new List(); + OnlineDevices.ForEach(x => onlineUniqueNos.Add(x.UniqueNo)); + + var offUniqueNos = deviceUniqueNos.Except(onlineUniqueNos).ToList(); + var list = devices.Where(x => offUniqueNos.Contains(x.UniqueNo)).ToList(); + return list; + } + public async Task> GetAttendanceTotalAsync(AttendanceTotalRequest request) { var page = await _dbContext.Queryable() .LeftJoin((d, dt) => d.TemplateCode == dt.Code) - .Where(d => d.Type == DeviceTypeEnumType.Attendance) + .Where(d => d.Type == DeviceTypeEnumType.Attendance || (d.Type == DeviceTypeEnumType.Door && d.AsAttendance)) .Select((d, dt) => new DevicePageResponse { FuncsStr = d.Funcs.ToString(), @@ -231,9 +384,37 @@ public class DeviceService : IDeviceService public async Task> GetAttendanceOfflineAsync(DeviceOnlineAndofflineRequest request) { + var devices = await _dbContext.Queryable().Where(x => x.Type == DeviceTypeEnumType.Attendance || (x.Type == DeviceTypeEnumType.Door && x.AsAttendance)).ToListAsync(); + + //离线 + var offDevices = await GetOfflineDevicesCount(devices); + var offids = offDevices.Select(x => x.Id).ToList(); + + if (request.IsOnAndOff) + { + //离线 + var offpage = await _dbContext.Queryable() + .LeftJoin((d, dt) => d.TemplateCode == dt.Code) + .Where(d => d.Type == DeviceTypeEnumType.Attendance) + .Where(d => offids.Contains(d.Id)) + .Select((d, dt) => new DevicePageResponse + { + FuncsStr = d.Funcs.ToString(), + Brand = dt.Brand, + Type = d.Type, + + }, true) + .ToPageListAsync(request); + return offpage; + } + //在线 + var ids = devices.Select(x => x.Id).ToList(); + List onids = ids.Except(offids).ToList(); + var page = await _dbContext.Queryable() .LeftJoin((d, dt) => d.TemplateCode == dt.Code) - .Where(d => d.Type == DeviceTypeEnumType.Attendance && d.Enable == request.IsOnAndOff) + .Where(d => d.Type == DeviceTypeEnumType.Attendance) + .Where(d => onids.Contains(d.Id)) .Select((d, dt) => new DevicePageResponse { FuncsStr = d.Funcs.ToString(), @@ -246,7 +427,52 @@ public class DeviceService : IDeviceService return page; } - public async Task> GetRecentNumbersAsync() + public async Task> GetDoorOfflineAsync(GetDoorOfflineRequest request) + { + var devices = await _dbContext.Queryable().Where(x => x.Type == DeviceTypeEnumType.Door).ToListAsync(); + + var offDevices = await GetOfflineDevicesCount(devices); + var offids = offDevices.Select(x => x.Id).ToList(); + + if (request.IsOnAndOff) + { + //离线 + var offpage = await _dbContext.Queryable() + .LeftJoin((d, dt) => d.TemplateCode == dt.Code) + .Where(d => d.Type == DeviceTypeEnumType.Door) + .Where(d => offids.Contains(d.Id)) + .Select((d, dt) => new DevicePageResponse + { + FuncsStr = d.Funcs.ToString(), + Brand = dt.Brand, + Type = d.Type, + + }, true) + .ToPageListAsync(request); + return offpage; + } + //在线 + var ids = devices.Select(x => x.Id).ToList(); + List onids = ids.Except(offids).ToList(); + + var page = await _dbContext.Queryable() + .LeftJoin((d, dt) => d.TemplateCode == dt.Code) + .Where(d => d.Type == DeviceTypeEnumType.Door) + .Where(d => onids.Contains(d.Id)) + .Select((d, dt) => new DevicePageResponse + { + FuncsStr = d.Funcs.ToString(), + Brand = dt.Brand, + Type = d.Type, + + }, true) + .ToPageListAsync(request); + + return page; + + } + + public async Task> GetRecentNumbersAsync() { //获取近30天并转化为"yyyy-MM-dd"集合 DateTime currentDate = DateTime.Now; @@ -258,15 +484,84 @@ public class DeviceService : IDeviceService times.Add(dateString); } - var devices = await _dbContext.Queryable().ToListAsync(); + var devices = await _dbContext.Queryable().Where(x => x.Type == DeviceTypeEnumType.Attendance || (x.Type == DeviceTypeEnumType.Door && x.AsAttendance)).ToListAsync(); - var dic = new Dictionary(); + var list = new List(); times.ForEach(time => { var count = devices.Where(x => x.CreateTime.ToString("yyyy-MM-dd") == time).Count(); - dic[time] = count; + var res = new RecentNumberResponse + { + Date = time, + Num = count, + }; + list.Add(res); }); - return dic; + + return list; + } + private List ImportFromExcel(IFormFile formFile) + { + var requests = new List(); + + using (var stream = formFile.OpenReadStream()) + { + XSSFWorkbook workbook = new XSSFWorkbook(stream); + ISheet sheet = workbook.GetSheetAt(0); + + for (int rowIdx = 1; rowIdx <= sheet.LastRowNum; rowIdx++) + { + IRow row = sheet.GetRow(rowIdx); + if (row == null) continue; + + var request = new ImportDeviceSaveRequest(); + + request.UniqueNo = row.GetCell(0).ToString(); + request.TemplateCode = Guid.Parse(row.GetCell(1).ToString()); + request.Supplier = row.GetCell(2).ToString(); + + var funs = new List(); + var fs = row.GetCell(3).ToString().Split(','); + foreach (string str in fs) + { + switch (str) + { + case "1": + funs.Add(DeviceFuncEnumType.Face); + break; + case "2": + funs.Add(DeviceFuncEnumType.Card); + break; + case "3": + funs.Add(DeviceFuncEnumType.QrCode); + break; + case "4": + funs.Add(DeviceFuncEnumType.Control); + break; + default: + break; + } + } + request.Funcs = funs; + request.Type = (DeviceTypeEnumType)Enum.Parse(typeof(DeviceTypeEnumType), row.GetCell(4).ToString()); + request.HeartbeatInterval = int.Parse(row.GetCell(5).ToString()); + request.DeployPlace = row.GetCell(6).ToString(); + request.DeployPosition = row.GetCell(7).ToString(); + request.DeployDirection = row.GetCell(8).ToString(); + request.ConnectionInfo = row.GetCell(9).ToString(); + request.Enable = Convert.ToBoolean(Convert.ToInt32(row.GetCell(10).ToString())); + request.AsAttendance = Convert.ToBoolean(Convert.ToInt32(row.GetCell(11).ToString())); + if (row.GetCell(12) != null) + { + request.PlaceeCode = Guid.Parse(row.GetCell(12)?.ToString()); + } + requests.Add(request); + } + } + + return requests; } } + + diff --git a/Src/Dlhis.Basic/Device/Services/Implements/DeviceTemplateService.cs b/Src/Dlhis.Basic/Device/Services/Implements/DeviceTemplateService.cs index 4f60c45d..c9c7accb 100644 --- a/Src/Dlhis.Basic/Device/Services/Implements/DeviceTemplateService.cs +++ b/Src/Dlhis.Basic/Device/Services/Implements/DeviceTemplateService.cs @@ -46,6 +46,7 @@ public class DeviceTemplateService : IDeviceTemplateService { var entity = await _dbContext.Queryable().Where(x => x.Id == request.Id).FirstAsync(); await ExistsDeviceUsingTemplateAsync(entity.Code); + request.Adapt(entity); await _dbContext.UpdateAsync(entity); } } diff --git a/Src/Dlhis.Basic/Dlhis.Basic.csproj b/Src/Dlhis.Basic/Dlhis.Basic.csproj index 6dc23042..d0e8b356 100644 --- a/Src/Dlhis.Basic/Dlhis.Basic.csproj +++ b/Src/Dlhis.Basic/Dlhis.Basic.csproj @@ -17,9 +17,8 @@ - - - + + @@ -28,4 +27,8 @@ + + + + diff --git a/Src/Dlhis.Basic/Organization/Controllers/DepartmentsController.cs b/Src/Dlhis.Basic/Organization/Controllers/DepartmentsController.cs index 678e2e0a..55fa4c2b 100644 --- a/Src/Dlhis.Basic/Organization/Controllers/DepartmentsController.cs +++ b/Src/Dlhis.Basic/Organization/Controllers/DepartmentsController.cs @@ -1,8 +1,6 @@ -using Dlhis.Basic.Organization.Request; -using Dlhis.Basic.Organization.Response; +using Dlhis.Basic.Organization.Response; using Dlhis.Basic.Organization.Services; using Kurisu.AspNetCore.MVC; -using Kurisu.Core.DataAccess.Dto; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -33,14 +31,4 @@ public class DepartmentsController : ControllerBase { return await _departmentService.GetTreeAsync(); } - - /// - /// 获取部门树分页 - /// - /// - [HttpGet("treepage")] - public async Task> GetTreePageAsync([FromQuery]DepartmentPageRequest request) - { - return await _departmentService.GetTreePageAsync(request); - } } diff --git a/Src/Dlhis.Basic/Organization/Controllers/GainfoCardsController.cs b/Src/Dlhis.Basic/Organization/Controllers/GainfoCardsController.cs new file mode 100644 index 00000000..3e04aa6d --- /dev/null +++ b/Src/Dlhis.Basic/Organization/Controllers/GainfoCardsController.cs @@ -0,0 +1,47 @@ +using Dlhis.Basic.Organization.Request; +using Dlhis.Basic.Organization.Response; +using Dlhis.Basic.Organization.Services; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Dlhis.Basic.Organization.Controllers; + +/// +/// 档案卡记录 +/// +[Authorize] +[Route("api/[controller]")] +[ApiController] +public class GainfoCardsController : ControllerBase +{ + private readonly IGainfoCardService _gainfoCardService; + + public GainfoCardsController(IGainfoCardService gainfoCardService) + { + _gainfoCardService = gainfoCardService; + } + + /// + /// 换卡 + /// + /// + /// + [HttpPost("change")] + [Transactional] + public async Task ChangeCardAsync(ChangeCardRequest request) + { + await _gainfoCardService.ChangeCardAsync(request); + } + + /// + /// 换卡记录 + /// + /// + /// + [HttpGet("records")] + public async Task> GetChangeRecordsAsync(string userNo) + { + return await _gainfoCardService.GetChangeRecordsAsync(userNo); + } +} diff --git a/Src/Dlhis.Basic/Organization/Controllers/GainfoGroupsController.cs b/Src/Dlhis.Basic/Organization/Controllers/GainfoGroupsController.cs index b578cf1a..8fc33da5 100644 --- a/Src/Dlhis.Basic/Organization/Controllers/GainfoGroupsController.cs +++ b/Src/Dlhis.Basic/Organization/Controllers/GainfoGroupsController.cs @@ -1,8 +1,8 @@ using Dlhis.Basic.Organization.Request; using Dlhis.Basic.Organization.Response; using Dlhis.Basic.Organization.Services; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; using Kurisu.AspNetCore.MVC; -using Kurisu.Core.DataAccess.Dto; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -25,24 +25,14 @@ public class GainfoGroupsController : ControllerBase } /// - /// 获取档案组分页 - /// - /// - /// - [HttpGet("page")] - public async Task> GetPageAsync([FromQuery] GainfoGroupPageRequest request) - { - return await _gainfoGroupService.GetPageAsync(request); - } - - /// - /// 获取所有档案组 + /// 获取档案分组树 /// /// - [HttpGet("list")] - public async Task> GetListAsync() + [HttpGet("tree")] + [IgnoreTenant] + public async Task> GetTreeAsync() { - return await _gainfoGroupService.GetListAsync(); + return await _gainfoGroupService.GetTreeAsync(); } /// @@ -50,7 +40,7 @@ public class GainfoGroupsController : ControllerBase /// /// /// - [HttpPost] + [HttpPost("save")] public async Task SaveAsync(GainfoGroupSaveRequest request) { await _gainfoGroupService.SaveAsync(request); @@ -73,6 +63,8 @@ public class GainfoGroupsController : ControllerBase /// /// [HttpPost("assign")] + [IgnoreTenant] + [Transactional] public async Task AssignGainfosAsync(GainfoAssignRequest request) { await _gainfoGroupService.AssignGainfosAsync(request); diff --git a/Src/Dlhis.Basic/Organization/Controllers/GainfosController.cs b/Src/Dlhis.Basic/Organization/Controllers/GainfosController.cs index b6c09d43..e51d85a5 100644 --- a/Src/Dlhis.Basic/Organization/Controllers/GainfosController.cs +++ b/Src/Dlhis.Basic/Organization/Controllers/GainfosController.cs @@ -2,12 +2,10 @@ using Dlhis.Basic.Organization.Request; using Dlhis.Basic.Organization.Response; using Dlhis.Basic.Organization.Services; -using Dlhis.Entity.Basic.Entity; using Kurisu.AspNetCore.MVC; using Kurisu.Core.DataAccess.Dto; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Org.BouncyCastle.Asn1.Ocsp; namespace Dlhis.Basic.MealUser.Controllers; @@ -43,8 +41,8 @@ public class GainfosController : ControllerBase /// /// /// - [HttpGet("page")] - public async Task> GetPageAsync([FromQuery] GainfoPageRequest request) + [HttpPost("page")] + public async Task> GetPageAsync(GainfoPageRequest request) { return await _gainfoService.GetPageAsync(request); } @@ -102,6 +100,17 @@ public class GainfosController : ControllerBase await _gainfoService.UploadFaceAsync(request); } + /// + /// 批量上传人脸照片 + /// + /// + /// + [HttpPost("batchface")] + public async Task UploadFaceAsync(List request) + { + await _gainfoService.UploadFacesAsync(request); + } + /// /// 通过token获取人脸照片,失效或者找不到返回HttpStatus404状态码 /// @@ -142,72 +151,21 @@ public class GainfosController : ControllerBase /// /// /// - [HttpGet("gainfolist")] - public async Task> GetGainfoListTaskAsync([FromQuery]GainfoListRequest request) + [HttpGet("list")] + public async Task> GetGainfoListTaskAsync([FromQuery] GainfoListRequest request) { - return await _gainfoService.GetGainfoListTaskAsync(request); + return await _gainfoService.GetGainfoListAsync(request); } + /// /// 档案详情 /// /// /// - [HttpGet("gainfodetail")] - public async Task GetGainfoDetailTaskAsync([FromQuery] GainfoDetailRequest request) + [HttpGet("detail")] + public async Task GetGainfoDetailAsync([FromQuery] GainfoDetailRequest request) { - return await _gainfoService.GetGainfoDetailTaskAsync(request); - } - - /// - /// 获取近七天入职人数及列表 - /// - /// - [HttpGet("weeknumber")] - public async Task>> GetWeekNumberAsync([FromQuery] WeekNumberRequest request) - { - // 从请求头中获取 Token - string token = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", ""); - - return await _gainfoService.GetWeekNumberAsync(request,token); - } - - /// - /// 获取近七天离职人数及列表 - /// - /// - [HttpGet("weeknumberdimission")] - public async Task>> GetDimissionWeekNumberAsync([FromQuery] WeekNumberRequest request) - { - // 从请求头中获取 Token - string token = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", ""); - - return await _gainfoService.GetDimissionWeekNumberAsync(request,token); - } - - /// - /// 获取基地在职、离职、考勤数 - /// - /// - [HttpGet("gainfonumbers")] - public async Task GetGainfoNumbersAsync() - { - // 从请求头中获取 Token - string token = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", ""); - - return await _gainfoService.GetGainfoNumbersAsync(token); - } - - /// - /// 获取本基地管理员列表 - /// - /// - /// - [HttpGet("administrator")] - public async Task> GetAdministratorAsync([FromQuery] WeekNumberRequest request) - { - // 从请求头中获取 Token - string token = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", ""); - return await _gainfoService.GetAdministratorAsync(request,token); + return await _gainfoService.GetGainfoDetailAsync(request); } } diff --git a/Src/Dlhis.Basic/Organization/Controllers/WerksController.cs b/Src/Dlhis.Basic/Organization/Controllers/WerksController.cs index 5f7522dd..bbbb7904 100644 --- a/Src/Dlhis.Basic/Organization/Controllers/WerksController.cs +++ b/Src/Dlhis.Basic/Organization/Controllers/WerksController.cs @@ -1,6 +1,5 @@ -using Dlhis.Basic.Organization.Response; -using Dlhis.Basic.Organization.Services; -using Dlhis.User.Api.Response; +using Dlhis.Basic.Organization.Services; +using Dlhis.Sys.Api.Response; using Kurisu.AspNetCore.MVC; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; diff --git a/Src/Dlhis.Basic/Organization/Request/ChangeCardRequest.cs b/Src/Dlhis.Basic/Organization/Request/ChangeCardRequest.cs new file mode 100644 index 00000000..d470812f --- /dev/null +++ b/Src/Dlhis.Basic/Organization/Request/ChangeCardRequest.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Basic.Organization.Request; + +public class ChangeCardRequest +{ + [Required] + public string UserNo { get; set; } + + [Required] + public string CardNo { get; set; } + + [Required] + public string Reason { get; set; } + + public string Remark { get; set; } +} diff --git a/Src/Dlhis.Basic/Organization/Request/DepartmentPageRequest.cs b/Src/Dlhis.Basic/Organization/Request/DepartmentPageRequest.cs deleted file mode 100644 index cdba378c..00000000 --- a/Src/Dlhis.Basic/Organization/Request/DepartmentPageRequest.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Kurisu.Core.DataAccess.Dto; - -namespace Dlhis.Basic.Organization.Request -{ - public class DepartmentPageRequest : PageDto - { - } -} diff --git a/Src/Dlhis.Basic/Organization/Request/GainfoDetailRequest.cs b/Src/Dlhis.Basic/Organization/Request/GainfoDetailRequest.cs index dde7384a..8de96bae 100644 --- a/Src/Dlhis.Basic/Organization/Request/GainfoDetailRequest.cs +++ b/Src/Dlhis.Basic/Organization/Request/GainfoDetailRequest.cs @@ -1,23 +1,25 @@ using Dlhis.Entity.Basic.Enums; -using Kurisu.Core.DataAccess.Dto; -using SqlSugar; using System.ComponentModel.DataAnnotations; -namespace Dlhis.Basic.Organization.Request -{ - public class GainfoDetailRequest : PageDto - { - /// - /// 档案类型 - /// - [Required] - [EnumDataType(typeof(GainfoStatusEnumType))] - public GainfoStatusEnumType Status { get; set; } +namespace Dlhis.Basic.Organization.Request; - /// - /// 档案号 - /// - [Required] - public string No { get; set; } - } +public class GainfoDetailRequest +{ + /// + /// 档案类型 + /// + [Required] + [EnumDataType(typeof(GainfoStatusEnumType))] + public GainfoStatusEnumType Status { get; set; } + + /// + /// 档案号 + /// + [Required] + public string No { get; set; } + + /// + /// 是否需要人脸信息 + /// + public bool WithFace { get; set; } } diff --git a/Src/Dlhis.Basic/Organization/Request/GainfoGroupPageRequest.cs b/Src/Dlhis.Basic/Organization/Request/GainfoGroupPageRequest.cs deleted file mode 100644 index b38a161d..00000000 --- a/Src/Dlhis.Basic/Organization/Request/GainfoGroupPageRequest.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Kurisu.Core.DataAccess.Dto; - -namespace Dlhis.Basic.Organization.Request; - -public class GainfoGroupPageRequest: PageDto -{ -} diff --git a/Src/Dlhis.Basic/Organization/Request/GainfoGroupSaveRequest.cs b/Src/Dlhis.Basic/Organization/Request/GainfoGroupSaveRequest.cs index bb5173e4..eb4bfe9a 100644 --- a/Src/Dlhis.Basic/Organization/Request/GainfoGroupSaveRequest.cs +++ b/Src/Dlhis.Basic/Organization/Request/GainfoGroupSaveRequest.cs @@ -4,6 +4,11 @@ public class GainfoGroupSaveRequest { public long? Id { get; set; } + /// + /// 上级 + /// + public Guid? PCode { get; set; } + /// /// 名称 /// diff --git a/Src/Dlhis.Basic/Organization/Request/GainfoPageRequest.cs b/Src/Dlhis.Basic/Organization/Request/GainfoPageRequest.cs index 0bfb339b..c9a13210 100644 --- a/Src/Dlhis.Basic/Organization/Request/GainfoPageRequest.cs +++ b/Src/Dlhis.Basic/Organization/Request/GainfoPageRequest.cs @@ -10,10 +10,10 @@ public class GainfoPageRequest : PageDto /// 档案类型 /// [EnumDataType(typeof(GainfoStatusEnumType))] - public GainfoStatusEnumType Status { get; set; } = GainfoStatusEnumType.Working; + public GainfoStatusEnumType? Status { get; set; } = GainfoStatusEnumType.Working; /// - /// 部门号(部门树传入) + /// 部门选择 /// - public List DepartmentNos { get; set; } + public List DepartmentSelections { get; set; } = new List(); } diff --git a/Src/Dlhis.Basic/Organization/Request/GainfoSaveRequest.cs b/Src/Dlhis.Basic/Organization/Request/GainfoSaveRequest.cs index 54f41fe3..838f621c 100644 --- a/Src/Dlhis.Basic/Organization/Request/GainfoSaveRequest.cs +++ b/Src/Dlhis.Basic/Organization/Request/GainfoSaveRequest.cs @@ -110,11 +110,6 @@ public class GainfoSaveRequest : IValidatableObject /// public string Werks { get; set; } - /// - /// 在职状态 - /// - public string Employed { get; set; } - public IEnumerable Validate(ValidationContext validationContext) { if (GainfoStatus.HasValue && GainfoStatus == GainfoStatusEnumType.Leave) diff --git a/Src/Dlhis.Basic/Organization/Request/TenantDepartmentRequest.cs b/Src/Dlhis.Basic/Organization/Request/TenantDepartmentRequest.cs new file mode 100644 index 00000000..b2bff024 --- /dev/null +++ b/Src/Dlhis.Basic/Organization/Request/TenantDepartmentRequest.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Basic.Organization.Request; + +public class TenantDepartmentRequest +{ + [Required] + public string Tenant { get; set; } + + /// + /// 部门号(部门树传入) + /// + public List DepartmentNos { get; set; } +} diff --git a/Src/Dlhis.Basic/Organization/Response/ChangeCardResponse.cs b/Src/Dlhis.Basic/Organization/Response/ChangeCardResponse.cs new file mode 100644 index 00000000..47943f0e --- /dev/null +++ b/Src/Dlhis.Basic/Organization/Response/ChangeCardResponse.cs @@ -0,0 +1,15 @@ +namespace Dlhis.Basic.Organization.Response; + +public class ChangeCardResponse +{ + public string UserNo { get; set; } + public string UserCode { get; set; } + public string UserName { get; set; } + public string CardNo { get; set; } + + public string Reason { get; set; } + + public string Remark { get; set; } + + public DateTime CreateTime { get; set; } +} diff --git a/Src/Dlhis.Basic/Organization/Response/DepartmentTreeResponse.cs b/Src/Dlhis.Basic/Organization/Response/DepartmentTreeResponse.cs index c30d68b1..88aba019 100644 --- a/Src/Dlhis.Basic/Organization/Response/DepartmentTreeResponse.cs +++ b/Src/Dlhis.Basic/Organization/Response/DepartmentTreeResponse.cs @@ -1,7 +1,14 @@ -namespace Dlhis.Basic.Organization.Response; +using Newtonsoft.Json; + +namespace Dlhis.Basic.Organization.Response; public class DepartmentTreeResponse { + /// + /// 上级 + /// + public string PNo { get; set; } + /// /// 部门号 /// @@ -12,8 +19,24 @@ public class DepartmentTreeResponse /// public string Name { get; set; } + + public string Tenant { get; set; } + /// /// 下级部门 /// - public List Next { get; set; } = new List(); + [JsonIgnore] + public List _Next { get; set; } = new List(); + + /// + /// 下级部门 + /// + public List Next + { + get + { + _Next.ForEach(x => x.Tenant = Tenant); + return _Next; + } + } } diff --git a/Src/Dlhis.Basic/Organization/Response/GainfoDetailResponse.cs b/Src/Dlhis.Basic/Organization/Response/GainfoDetailResponse.cs index a2472cfe..6ffc95a7 100644 --- a/Src/Dlhis.Basic/Organization/Response/GainfoDetailResponse.cs +++ b/Src/Dlhis.Basic/Organization/Response/GainfoDetailResponse.cs @@ -1,114 +1,125 @@ using Dlhis.Entity.Basic.Enums; -using SqlSugar; -using SqlSugar.DbConvert; +using Kurisu.AspNetCore.Utils.Extensions; -namespace Dlhis.Basic.Organization.Response +namespace Dlhis.Basic.Organization.Response; + +public class GainfoDetailResponse { - public class GainfoDetailResponse - { - /// - /// 档案号 - /// - public string No { get; set; } + /// + /// 档案号 + /// + public string No { get; set; } - /// - /// SAP内部编号 - /// - public string SapNo { get; set; } + /// + /// SAP内部编号 + /// + public string SapNo { get; set; } - /// - /// 姓名 - /// - public string Name { get; set; } + /// + /// 姓名 + /// + public string Name { get; set; } - /// - /// 学工号 - /// - public string Code { get; set; } + /// + /// 学工号 + /// + public string Code { get; set; } - /// - /// 部门名称 - /// - public string DepartmentName { get; set; } + /// + /// 部门名称 + /// + public string DepartmentName { get; set; } - /// - /// 证件类型代码 - /// - public string IdClass { get; set; } + /// + /// 证件类型代码 + /// + public string IdClass { get; set; } - /// - /// 身份证号 - /// - public string IdNo { get; set; } + /// + /// 证件类型名称 + /// + public string ClassName { get; set; } - /// - /// 性别 - /// - public GenderEnumType Gender { get; set; } + /// + /// 身份证号 + /// + public string IdNo { get; set; } - /// - /// 身份证籍贯 - /// - public string NativeName { get; set; } + /// + /// 性别 + /// + public GenderEnumType Gender { get; set; } - /// - /// 身份证地址 - /// - public string Address { get; set; } + /// + /// 性别描述 + /// + public string GenderCN => Gender.GetDescription(); - /// - /// 生日 - /// - public DateTime Birthday { get; set; } + /// + /// 身份证籍贯 + /// + public string NativeName { get; set; } - /// - /// 手机号码 - /// - public string Phone { get; set; } + /// + /// 身份证地址 + /// + public string Address { get; set; } - /// - /// 岗位 - /// - public string Position { get; set; } + /// + /// 生日 + /// + public DateTime Birthday { get; set; } - /// - /// 职等 - /// - public string Grade { get; set; } + /// + /// 手机号码 + /// + public string Phone { get; set; } - /// - /// 学历 - /// - public string Degree { get; set; } + /// + /// 岗位 + /// + public string Position { get; set; } - /// - /// 人事范围 - /// - public string Werks { get; set; } + /// + /// 职等 + /// + public string Grade { get; set; } - /// - /// 成本中心 - /// - public string CostId { get; set; } + /// + /// 学历 + /// + public string Degree { get; set; } - /// - /// 已婚/未婚 - /// - public string Marital { get; set; } + /// + /// 人事范围 + /// + public string Werks { get; set; } - /// - /// 国家 - /// - public string CountryName { get; set; } + /// + /// 成本中心 + /// + public string CostId { get; set; } + + /// + /// 已婚/未婚 + /// + public string Marital { get; set; } + + /// + /// 国家 + /// + public string CountryName { get; set; } + + /// + /// 民族代码 + /// + public string NationName { get; set; } + + + /// + /// 人脸base64 + /// + public string Face { get; set; } - /// - /// 民族代码 - /// - public string NationName { get; set; } - /// - /// 是否在职 - /// - public bool IsActive { get; set; } - } } diff --git a/Src/Dlhis.Basic/Organization/Response/GainfoGroupResponse.cs b/Src/Dlhis.Basic/Organization/Response/GainfoGroupResponse.cs deleted file mode 100644 index f7f397b4..00000000 --- a/Src/Dlhis.Basic/Organization/Response/GainfoGroupResponse.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Dlhis.Basic.Organization.Response; - -public class GainfoGroupResponse -{ - public long Id { get; set; } - - /// - /// 分组code - /// - public Guid Code { get; set; } - - /// - /// 名称 - /// - public string Name { get; set; } - - /// - /// 备注 - /// - public string Remark { get; set; } -} diff --git a/Src/Dlhis.Basic/Organization/Response/GainfoGroupTreeResponse.cs b/Src/Dlhis.Basic/Organization/Response/GainfoGroupTreeResponse.cs new file mode 100644 index 00000000..5b5000fd --- /dev/null +++ b/Src/Dlhis.Basic/Organization/Response/GainfoGroupTreeResponse.cs @@ -0,0 +1,49 @@ +using Newtonsoft.Json; + +namespace Dlhis.Basic.Organization.Response; + +public class GainfoGroupTreeResponse +{ + public long Id { get; set; } + + public Guid? PCode { get; set; } + + /// + /// 分组code + /// + public Guid Code { get; set; } + + /// + /// 名称 + /// + public string Name { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + /// + /// 是否可编辑 + /// + public bool Editable { get; set; } + + /// + /// 是否为业务默认分组 + /// + public bool IsDefault { get; set; } + + /// + /// 是否允许设置下级 + /// + public bool AbleSetNext => IsDefault; + + + [JsonIgnore] + public string TenantId { get; set; } + + /// + /// 下级数据 + /// + public List Next { get; set; } +} diff --git a/Src/Dlhis.Basic/Organization/Response/GainfoListResponse.cs b/Src/Dlhis.Basic/Organization/Response/GainfoListResponse.cs index a826eec0..8e7c6789 100644 --- a/Src/Dlhis.Basic/Organization/Response/GainfoListResponse.cs +++ b/Src/Dlhis.Basic/Organization/Response/GainfoListResponse.cs @@ -1,60 +1,51 @@ using Dlhis.Entity.Basic.Enums; -using SqlSugar; -using SqlSugar.DbConvert; -namespace Dlhis.Basic.Organization.Response +namespace Dlhis.Basic.Organization.Response; + +public class GainfoListResponse { - public class GainfoListResponse - { - /// - /// 档案号 - /// - public string No { get; set; } + /// + /// 档案号 + /// + public string No { get; set; } - /// - /// 姓名 - /// - public string Name { get; set; } + /// + /// 姓名 + /// + public string Name { get; set; } - /// - /// 学工号 - /// - public string Code { get; set; } + /// + /// 学工号 + /// + public string Code { get; set; } - /// - /// 身份证号 - /// - public string IdNo { get; set; } + /// + /// 身份证号 + /// + public string IdNo { get; set; } - /// - /// 性别 - /// - public GenderEnumType Gender { get; set; } + /// + /// 性别 + /// + public GenderEnumType Gender { get; set; } - /// - /// 手机号码 - /// - public string Phone { get; set; } + /// + /// 手机号码 + /// + public string Phone { get; set; } - /// - /// 部门编码 - /// - public string DepartmentNo { get; set; } + /// + /// 部门编码 + /// + public string DepartmentNo { get; set; } - /// - /// 部门名称 - /// - public string DepartmentName { get; set; } + /// + /// 部门名称 + /// + public string DepartmentName { get; set; } - /// - /// 身份证地址 - /// - public string Address { get; set; } - - /// - /// 在职状态 - /// - public string Employed { get; set; } - - } + /// + /// 身份证地址 + /// + public string Address { get; set; } } diff --git a/Src/Dlhis.Basic/Organization/Response/GainfoPageResponse.cs b/Src/Dlhis.Basic/Organization/Response/GainfoPageResponse.cs index df112cee..4b8faadd 100644 --- a/Src/Dlhis.Basic/Organization/Response/GainfoPageResponse.cs +++ b/Src/Dlhis.Basic/Organization/Response/GainfoPageResponse.cs @@ -1,4 +1,7 @@ -namespace Dlhis.Basic.Organization.Response; +using Dlhis.Entity.Basic.Enums; +using Kurisu.AspNetCore.Utils.Extensions; + +namespace Dlhis.Basic.Organization.Response; public class GainfoPageResponse { @@ -52,7 +55,8 @@ public class GainfoPageResponse /// /// 性别 /// - public string Gender { get; set; } + public GenderEnumType Gender { get; set; } + public string GenderDesc => Gender.GetDescription(); /// /// 生日 @@ -108,9 +112,4 @@ public class GainfoPageResponse /// 人事范围 /// public string Werks { get; set; } - - /// - /// 在职状态 - /// - public string Employed { get; set; } } diff --git a/Src/Dlhis.Basic/Organization/Services/IDepartmentService.cs b/Src/Dlhis.Basic/Organization/Services/IDepartmentService.cs index cfd0b9d2..5e3784c1 100644 --- a/Src/Dlhis.Basic/Organization/Services/IDepartmentService.cs +++ b/Src/Dlhis.Basic/Organization/Services/IDepartmentService.cs @@ -1,12 +1,8 @@ -using Dlhis.Basic.Organization.Request; -using Dlhis.Basic.Organization.Response; -using Kurisu.Core.DataAccess.Dto; +using Dlhis.Basic.Organization.Response; namespace Dlhis.Basic.Organization.Services; public interface IDepartmentService : IScopeDependency { Task> GetTreeAsync(); - - Task> GetTreePageAsync(DepartmentPageRequest request); } diff --git a/Src/Dlhis.Basic/Organization/Services/IGainfoCardService.cs b/Src/Dlhis.Basic/Organization/Services/IGainfoCardService.cs new file mode 100644 index 00000000..8bbce4c7 --- /dev/null +++ b/Src/Dlhis.Basic/Organization/Services/IGainfoCardService.cs @@ -0,0 +1,11 @@ +using Dlhis.Basic.Organization.Request; +using Dlhis.Basic.Organization.Response; + +namespace Dlhis.Basic.Organization.Services; + +public interface IGainfoCardService +{ + Task ChangeCardAsync(ChangeCardRequest request); + + Task> GetChangeRecordsAsync(string userNo); +} diff --git a/Src/Dlhis.Basic/Organization/Services/IGainfoGroupService.cs b/Src/Dlhis.Basic/Organization/Services/IGainfoGroupService.cs index 344f4dd1..d9b2801e 100644 --- a/Src/Dlhis.Basic/Organization/Services/IGainfoGroupService.cs +++ b/Src/Dlhis.Basic/Organization/Services/IGainfoGroupService.cs @@ -1,14 +1,11 @@ using Dlhis.Basic.Organization.Request; using Dlhis.Basic.Organization.Response; -using Kurisu.Core.DataAccess.Dto; namespace Dlhis.Basic.Organization.Services; public interface IGainfoGroupService : IScopeDependency { - Task> GetPageAsync(GainfoGroupPageRequest request); - - Task> GetListAsync(); + Task> GetTreeAsync(); Task SaveAsync(GainfoGroupSaveRequest request); diff --git a/Src/Dlhis.Basic/Organization/Services/IGainfoService.cs b/Src/Dlhis.Basic/Organization/Services/IGainfoService.cs index 7bb8f0fc..b40f3678 100644 --- a/Src/Dlhis.Basic/Organization/Services/IGainfoService.cs +++ b/Src/Dlhis.Basic/Organization/Services/IGainfoService.cs @@ -1,6 +1,5 @@ using Dlhis.Basic.Organization.Request; using Dlhis.Basic.Organization.Response; -using Dlhis.Entity.Basic.Entity; using Kurisu.Core.DataAccess.Dto; using Microsoft.AspNetCore.Mvc; @@ -22,21 +21,15 @@ public interface IGainfoService : IScopeDependency Task UploadFaceAsync(GainfoUploadFaceRequest request); + Task UploadFacesAsync(List request); + Task GetFaceAsync(string token); Task GetFaceByUserCodeAsync(string userCode); Task> GetFaceByUserCodesAsync(GetFaceByUserCodesRequest request); - Task> GetGainfoListTaskAsync(GainfoListRequest request); + Task> GetGainfoListAsync(GainfoListRequest request); - Task GetGainfoDetailTaskAsync(GainfoDetailRequest request); - - Task>> GetWeekNumberAsync(WeekNumberRequest request, string token); - - Task>> GetDimissionWeekNumberAsync([FromQuery] WeekNumberRequest request, string token); - - Task GetGainfoNumbersAsync(string token); - - Task> GetAdministratorAsync(WeekNumberRequest request,string token); + Task GetGainfoDetailAsync(GainfoDetailRequest request); } diff --git a/Src/Dlhis.Basic/Organization/Services/IGainfoStore.cs b/Src/Dlhis.Basic/Organization/Services/IGainfoStore.cs index 3204fb74..8af56343 100644 --- a/Src/Dlhis.Basic/Organization/Services/IGainfoStore.cs +++ b/Src/Dlhis.Basic/Organization/Services/IGainfoStore.cs @@ -9,7 +9,7 @@ namespace Dlhis.Basic.Organization.Services; /// public interface IGainfoStore : IScopeDependency { - ISugarQueryable Queryable(GainfoStatusEnumType? gainfoStatus); + ISugarQueryable Queryable(GainfoStatusEnumType? gainfoStatus = null); diff --git a/Src/Dlhis.Basic/Organization/Services/IWerkService.cs b/Src/Dlhis.Basic/Organization/Services/IWerkService.cs index 8f8ecc4b..42b1a90e 100644 --- a/Src/Dlhis.Basic/Organization/Services/IWerkService.cs +++ b/Src/Dlhis.Basic/Organization/Services/IWerkService.cs @@ -1,4 +1,4 @@ -using Dlhis.User.Api.Response; +using Dlhis.Sys.Api.Response; namespace Dlhis.Basic.Organization.Services; diff --git a/Src/Dlhis.Basic/Organization/Services/Implements/DepartmentService.cs b/Src/Dlhis.Basic/Organization/Services/Implements/DepartmentService.cs index 77f42f9a..fd4f8e74 100644 --- a/Src/Dlhis.Basic/Organization/Services/Implements/DepartmentService.cs +++ b/Src/Dlhis.Basic/Organization/Services/Implements/DepartmentService.cs @@ -1,73 +1,60 @@ -using Dlhis.Basic.Organization.Request; -using Dlhis.Basic.Organization.Response; +using Dlhis.Basic.Organization.Response; using Dlhis.Entity.Basic.Entity; -using Kurisu.AspNetCore.Utils.Extensions; -using Kurisu.Core.DataAccess.Dto; +using Dlhis.Sys.Api; using Kurisu.SqlSugar.Services; using Mapster; -using System.Collections.Generic; namespace Dlhis.Basic.Organization.Services.Implements; public class DepartmentService : IDepartmentService { private readonly IDbContext _dbContext; + private readonly ITenantApi _tenantApi; - public DepartmentService(IDbContext dbContext) + public DepartmentService(IDbContext dbContext, ITenantApi tenantApi) { _dbContext = dbContext; + _tenantApi = tenantApi; } public async Task> GetTreeAsync() { - var departments = await _dbContext.Queryable().ToListAsync(); + var departments = await _dbContext.Queryable().Select().ToListAsync(); //00001000 一定是这个开头的 - var tops = departments.Where(x => x.PNo == "00001000").Adapt>(); + var tops = departments.Where(x => x.PNo == "00001000").ToList(); tops.ForEach(x => { RecursionGetNext(x, departments); }); - return tops; + var myTenantResult = await _tenantApi.GetMyTenantsAsync(); + var tenants = myTenantResult.Data; + + return tenants.Select(x => new DepartmentTreeResponse + { + Name = x.Name, + No = x.Code, + PNo = x.PCode, + Tenant = x.Code, + _Next = tops.Select(item => + { + var i = item.Adapt(); + i.PNo = x.Code; + return i; + + }).ToList() + }) + .ToList(); } - public async Task> GetTreePageAsync(DepartmentPageRequest request) + private void RecursionGetNext(DepartmentTreeResponse current, List data) { - var departments = await _dbContext - .Queryable() - .WhereIF(!request.Search.IsEmpty(), x => x.Name.Contains(request.Search) || x.No.Contains(request.Search)).ToListAsync(); - - var tops = departments.Where(x => x.PNo.IsEmpty()).Adapt>(); - - tops.ForEach(x => - { - RecursionGetNext(x, departments); - }); - - // 分页操作 - var startIndex = (request.PageIndex - 1) * request.PageSize; - var pagedData = tops.Skip(startIndex).Take(request.PageSize).ToList(); - - var pagination = new Pagination - { - PageIndex = request.PageIndex, - PageSize = request.PageSize, - Total = tops.Count, // 使用原始列表的总数进行分页 - Data = pagedData - }; - - return pagination; - - } - - private void RecursionGetNext(DepartmentTreeResponse current, List departments) - { - var next = departments.Where(x => x.PNo == current.No).ToList(); - current.Next = next.Adapt>(); + var next = data.Where(x => x.PNo == current.No).ToList(); + current._Next = next; foreach (var item in next) { - RecursionGetNext(item.Adapt(), departments); + RecursionGetNext(item, data); } } } diff --git a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoCardService.cs b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoCardService.cs new file mode 100644 index 00000000..c9d3213e --- /dev/null +++ b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoCardService.cs @@ -0,0 +1,54 @@ +using Dlhis.Basic.Organization.Request; +using Dlhis.Basic.Organization.Response; +using Dlhis.Entity.Basic.Entity; +using Kurisu.AspNetCore.Utils.Extensions; +using Kurisu.SqlSugar.Services; + +namespace Dlhis.Basic.Organization.Services.Implements; + +public class GainfoCardService : IGainfoCardService +{ + private readonly IDbContext _dbContext; + private readonly IGainfoStore _gainfoStore; + + public GainfoCardService(IDbContext dbContext, IGainfoStore gainfoStore) + { + _dbContext = dbContext; + _gainfoStore = gainfoStore; + } + + public async Task ChangeCardAsync(ChangeCardRequest request) + { + var gainfo = await _gainfoStore.Queryable().Where(x => x.No == request.UserNo).FirstAsync(); + gainfo.ThrowIfNull("档案不存在"); + + gainfo.CardNo = request.CardNo; + + await _gainfoStore.UpdateAsync(gainfo); + await _dbContext.InsertAsync(new GainfoCardRecordEntity + { + UserNo = request.UserNo, + CardNo = request.CardNo, + Reason = request.Reason, + Remark = request.Remark, + }); + } + + public async Task> GetChangeRecordsAsync(string userNo) + { + return await _dbContext.Queryable() + .LeftJoin((x, ga) => x.UserNo == ga.No) + .Where(x => x.UserNo == userNo) + .Select((x, ga) => new ChangeCardResponse + { + UserNo = x.UserNo, + UserCode = ga.Code, + UserName = ga.Name, + CardNo = x.CardNo, + Reason = x.Reason, + Remark = x.Remark, + CreateTime = x.CreateTime, + }) + .ToListAsync(); + } +} diff --git a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoGroupService.cs b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoGroupService.cs index ae598cf1..3c00eeca 100644 --- a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoGroupService.cs +++ b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoGroupService.cs @@ -1,9 +1,10 @@ -using Dlhis.Basic.Organization.Request; +using Dlhis.Basic.Device.Services; +using Dlhis.Basic.Organization.Request; using Dlhis.Basic.Organization.Response; using Dlhis.Entity.Basic.Entity; using Kurisu.AspNetCore.Utils.Extensions; -using Kurisu.Core.DataAccess.Dto; -using Kurisu.SqlSugar.Extensions; +using Kurisu.Core.DataAccess.Entity; +using Kurisu.Core.User.Abstractions; using Kurisu.SqlSugar.Services; using Mapster; @@ -12,30 +13,50 @@ namespace Dlhis.Basic.Organization.Services.Implements; public class GainfoGroupService : IGainfoGroupService { private readonly IDbContext _dbContext; + private readonly ICurrentUser _currentUser; + private readonly IDevicePermissionService _devicePermissionService; - public GainfoGroupService(IDbContext dbContext) + public GainfoGroupService(IDbContext dbContext, ICurrentUser currentUser, IDevicePermissionService devicePermissionService) { _dbContext = dbContext; + _currentUser = currentUser; + _devicePermissionService = devicePermissionService; } - public async Task> GetPageAsync(GainfoGroupPageRequest request) + public async Task> GetTreeAsync() { - return await _dbContext.Queryable() - .Select() - .ToPageListAsync(request); - } + var gainfoGroups = await _dbContext.Queryable() + .Select() + .ToListAsync(); + var tops = gainfoGroups.Where(x => !x.PCode.HasValue).ToList(); + tops.ForEach(x => + { + RecursionGetNext(x, gainfoGroups); + }); - public async Task> GetListAsync() - { - return await _dbContext.Queryable() - .Select() - .ToListAsync(); + return tops; } public async Task SaveAsync(GainfoGroupSaveRequest request) { await CheckDuplicateNameAsync(request); + try + { + //todo + _dbContext.Client.QueryFilter.ClearAndBackup(); + var defaultGroup = await _dbContext.Queryable().Where(x => x.IsDefault).FirstAsync(); + + if (!request.PCode.HasValue) + { + request.PCode = defaultGroup.Code; + } + } + finally + { + _dbContext.Client.QueryFilter.Restore(); + } + if (!request.Id.HasValue) { @@ -47,7 +68,6 @@ public class GainfoGroupService : IGainfoGroupService { var entity = await _dbContext.Queryable().Where(x => x.Id == request.Id).SingleAsync(); request.Adapt(entity); - await _dbContext.UpdateAsync(entity); } } @@ -69,18 +89,32 @@ public class GainfoGroupService : IGainfoGroupService public async Task AssignGainfosAsync(GainfoAssignRequest request) { - var relations = await _dbContext.Queryable().Where(x => x.GroupCode == request.GroupCode).ToListAsync(); - await _dbContext.DeleteReallyAsync(relations); - - request.UserNos = await _dbContext.Queryable().Where(x => request.UserNos.Contains(x.No)).Select(x => x.No).ToListAsync(); - var entities = request.UserNos.Select(x => new GainfoGroupRelationshipEntity + if (request.UserNos.IsPresent()) { - GroupCode = request.GroupCode, - UserNo = x + var pGrp = await _dbContext.Queryable().Where(x => x.Code == request.GroupCode).FirstAsync(); + (pGrp?.Editable == false && pGrp?.IsDefault == true).ThrowIfTrue("不可分配人员到当前分组"); + } - }).ToList(); + var existsNextGroup = await _dbContext.Queryable().Where(x => x.PCode == request.GroupCode).AnyAsync(); + existsNextGroup.ThrowIfTrue("存在下级分组,无法分配人员"); - await _dbContext.InsertAsync(entities); + await _devicePermissionService.GenerateDeviceTasksAsync(async () => + { + var relations = await _dbContext.Queryable().Where(x => x.GroupCode == request.GroupCode).ToListAsync(); + await _dbContext.DeleteReallyAsync(relations); + + request.UserNos = await _dbContext.Queryable().Where(x => request.UserNos.Contains(x.No)).Select(x => x.No).ToListAsync(); + var entities = request.UserNos.Select(x => new GainfoGroupRelationshipEntity + { + GroupCode = request.GroupCode, + UserNo = x, + + + }).ToList(); + + await _dbContext.InsertAsync(entities); + + }, null); } //******************************************************* help methods *********************************************************** @@ -99,4 +133,32 @@ public class GainfoGroupService : IGainfoGroupService exists.ThrowIfTrue($"重复组名{request.Name}"); } + + + private void RecursionGetNext(GainfoGroupTreeResponse current, List gainfoGroups) + { + var currentTenantId = _currentUser.GetStringTenantId(); + var next = gainfoGroups.Where(x => + { + if (x.PCode == current.Code) + { + if (x.TenantId.IsEmpty()) + { + return true; + } + else + { + return x.TenantId == currentTenantId; + } + } + + return false; + + }).ToList(); + current.Next = next; + foreach (var item in next) + { + RecursionGetNext(item, gainfoGroups); + } + } } diff --git a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs index ce14552e..c7ceda84 100644 --- a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs +++ b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs @@ -4,19 +4,17 @@ using Dlhis.Basic.Organization.Response; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Basic.Enums; using Dlhis.Entity.Device.Enums; -using Kurisu.AspNetCore.Scope; -using Kurisu.AspNetCore.UnifyResultAndValidation; +using Kurisu.AspNetCore.Utils; using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Core.DataAccess.Dto; using Kurisu.Core.User.Abstractions; -using Kurisu.SqlSugar.Attributes; using Kurisu.SqlSugar.Extensions; -using Kurisu.SqlSugar.Options; using Kurisu.SqlSugar.Services; using Mapster; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Mvc; using System.IdentityModel.Tokens.Jwt; +using System.Linq.Expressions; namespace Dlhis.Basic.Organization.Services.Implements; @@ -26,18 +24,27 @@ public class GainfoService : IGainfoService private readonly IDbContext _dbContext; private readonly ICurrentUser _currentUser; private readonly IDevicePermissionService _devicePermissionService; + private readonly RedisCache _redisCache; + private readonly IServiceProvider _serviceProvider; + private readonly IConfiguration _configuration; private readonly ILogger _logger; public GainfoService(IGainfoStore gainfoStore, IDbContext dbContext, ICurrentUser currentUser, IDevicePermissionService devicePermissionService, + RedisCache redisCache, + IServiceProvider serviceProvider, + IConfiguration configuration, ILogger logger) { _gainfoStore = gainfoStore; _dbContext = dbContext; _currentUser = currentUser; _devicePermissionService = devicePermissionService; + _redisCache = redisCache; + _serviceProvider = serviceProvider; + _configuration = configuration; _logger = logger; } @@ -48,16 +55,28 @@ public class GainfoService : IGainfoService /// public async Task> GetPageAsync(GainfoPageRequest request) { - request.DepartmentNos = await GetRecursionDepartmentNosAsync(request.DepartmentNos); + var currentTenantId = _currentUser.GetStringTenantId(); - var tenant = _currentUser.GetStringTenantId(); + //根据组织架构搜索档案 + Expression> where = LinqExpressionExtension.False(); + foreach (var item in request.DepartmentSelections) + { + var depts = await GetRecursionDepartmentNosAsync(item.DepartmentNos); + if (depts.Any()) + where = where.Or(gainfo => gainfo.Factory == item.Tenant && depts.Contains(gainfo.DepartmentNo)); + else + where = where.Or(gainfo => gainfo.Factory == item.Tenant); + } var page = await _gainfoStore.Queryable(request.Status) - .WhereIF(request.DepartmentNos?.Any() == true, gainfo => request.DepartmentNos.Contains(gainfo.DepartmentNo)) + .WhereIF(request.DepartmentSelections.IsPresent(), where) + .WhereIF(request.DepartmentSelections.IsEmpty(), gainfo => gainfo.Factory == currentTenantId) .WhereIF(request.Search.IsPresent(), gainfo => - gainfo.Name.Contains(request.Search) - || gainfo.Code.Contains(request.Search)) - .Where(gainfo => gainfo.Werks == tenant) + gainfo.Name.Contains(request.Search) + || gainfo.Code.Contains(request.Search) + || gainfo.IdNo.Contains(request.Search) + || gainfo.Werks.Contains(request.Search) + ) .Select() .ToPageListAsync(request); @@ -104,7 +123,6 @@ public class GainfoService : IGainfoService /// /// /// - [Transactional] public async Task DimissionAsync(GainfoDimissionRequest request) { var gainfo = await _gainfoStore.Queryable(GainfoStatusEnumType.Working) @@ -140,11 +158,19 @@ public class GainfoService : IGainfoService public async Task CurrentUserInfoAsync() { var identityId = _currentUser.GetIntSubjectId(); + var code = _currentUser.GetUserClaim("jinko-code"); + var gainfo = await _gainfoStore.Queryable().Where(x => x.Code == code).FirstAsync() + ?? await _gainfoStore.Queryable().Where(x => x.IdentityUserId == identityId).FirstAsync(); - var gainfo = await _gainfoStore.Queryable(GainfoStatusEnumType.Working) + if (gainfo == null) + { + return null; + } + + var info = await _gainfoStore.Queryable(GainfoStatusEnumType.Working) .LeftJoin((ga, department) => ga.DepartmentNo == department.No) .LeftJoin((ga, department, mealUser) => ga.No == mealUser.UserNo) - .Where((ga, department) => ga.IdentityUserId == identityId) + .Where((ga, department) => ga.Id == gainfo.Id) .Select((ga, department, mealUser) => new GainfoCurrentResponse { UserNo = ga.No, @@ -161,49 +187,43 @@ public class GainfoService : IGainfoService }) .FirstAsync(); - return gainfo; + return info; } public async Task GetFaceAsync(string token) { - return await Scoped.Request.Value.CreateAsync(async (sp) => + var timeLimitedDataProtector = _serviceProvider.GetService() + .CreateProtector("down:face") + .ToTimeLimitedDataProtector(); + + try { - var timeLimitedDataProtector = sp.GetService() - .CreateProtector("down:face") - .ToTimeLimitedDataProtector(); + var password = timeLimitedDataProtector.Unprotect(token); + var userCode = password.Split(':')[1]; - try + var faceCache = await _redisCache.HashGetAsync("faces", userCode); + if (faceCache.HasValue) { - var password = timeLimitedDataProtector.Unprotect(token); - var userCode = password.Split(':')[1]; - - var configuration = sp.GetService(); - var faceConnectionString = configuration.GetSection("SqlSugarOptions:FaceConnectionString").Value; - if (faceConnectionString.IsEmpty()) - { - _logger.LogWarning("暂不支持获取人脸"); - return new NotFoundResult(); - } - - //todo 切换连接字符串 - var dbContext = sp.GetService(); - dbContext.Client.CurrentConnectionConfig.ConnectionString = faceConnectionString; - var face = await dbContext.Queryable().Where(x => x.UserCode == userCode).SingleAsync(); - - if (face == null) - { - _logger.LogWarning("学工号:{userCode}不存在人脸照片", userCode); - return new NotFoundResult(); - } - - return new FileContentResult(Convert.FromBase64String(face.Face), "image/png"); + return new FileContentResult(Convert.FromBase64String(faceCache), "image/png"); } - catch (Exception ex) + + var dbContext = _dbContext.ChangeDb("face"); + var face = await dbContext.Queryable().Where(x => x.UserCode == userCode).SingleAsync(); + + if (face == null) { - _logger.LogError(ex, "获取人脸失败:" + ex.Message); + _logger.LogWarning("学工号:{userCode}不存在人脸照片", userCode); return new NotFoundResult(); } - }); + + await _redisCache.HashSetAsync("faces", userCode, face.Face); + return new FileContentResult(Convert.FromBase64String(face.Face), "image/png"); + } + catch (Exception ex) + { + _logger.LogError(ex, "获取人脸失败:{message}", ex.Message); + return new NotFoundResult(); + } } public async Task UploadFaceAsync(GainfoUploadFaceRequest request) @@ -213,21 +233,53 @@ public class GainfoService : IGainfoService gainfo.ThrowIfNull("人员档案信息不存在"); - await Scoped.Request.Value.CreateAsync(async (sp) => + var dbContext = _dbContext.ChangeDb("face"); + var face = await dbContext.Queryable().Where(x => x.UserCode == gainfo.Code).SingleAsync(); + + var taskType = face == null ? DeviceTaskEnumType.Add : DeviceTaskEnumType.Update; + + await _redisCache.HashSetAsync("faces", gainfo.Code, request.Face); + if (face == null) { - var configuration = sp.GetService(); - var faceConnectionString = configuration.GetSection("SqlSugarOptions:FaceConnectionString").Value; - if (faceConnectionString.IsEmpty()) + face = new GainfoFaceEntity { - throw new UserFriendlyException("暂不支持人脸上传"); + UserCode = gainfo.Code, + Face = request.Face + }; + + await dbContext.InsertAsync(face); + } + else + { + if (face.Face == request.Face) + { + _logger.LogWarning("相同的人脸照片更新忽略"); + return; } - var dbContext = sp.GetService(); - //todo 切换连接字符串 - dbContext.Client.CurrentConnectionConfig.ConnectionString = faceConnectionString; + face.Face = request.Face; + await dbContext.UpdateAsync(face); + } + + //生成任务 + await _devicePermissionService.GenerateDeviceTasksAsync(gainfo.No, taskType); + } + + public async Task UploadFacesAsync(List requests) + { + foreach (var request in requests) + { + var gainfo = await _gainfoStore.Queryable(GainfoStatusEnumType.Working).Where(x => x.No == request.UserNo).FirstAsync(); + gainfo ??= await _gainfoStore.Queryable(GainfoStatusEnumType.External).Where(x => x.No == request.UserNo).FirstAsync(); + + gainfo.ThrowIfNull("人员档案信息不存在"); + + var dbContext = _dbContext.ChangeDb("face"); var face = await dbContext.Queryable().Where(x => x.UserCode == gainfo.Code).SingleAsync(); var taskType = face == null ? DeviceTaskEnumType.Add : DeviceTaskEnumType.Update; + + await _redisCache.HashSetAsync("faces", gainfo.Code, request.Face); if (face == null) { face = new GainfoFaceEntity @@ -252,49 +304,33 @@ public class GainfoService : IGainfoService //生成任务 await _devicePermissionService.GenerateDeviceTasksAsync(gainfo.No, taskType); - }); + } } - - public async Task GetFaceByUserCodeAsync(string userCode) { var gainfo = await _dbContext.Queryable().Where(x => x.Code == userCode).SingleAsync(); gainfo.ThrowIfNull($"{userCode}人员信息不存在"); - return await Scoped.Request.Value.CreateAsync(async (sp) => - { - var configuration = sp.GetService(); + var dbContext = _dbContext.ChangeDb("face"); - var faceConnectionString = configuration.GetSection($"{nameof(SqlSugarOptions)}:FaceConnectionString").Value; - if (faceConnectionString.IsEmpty()) - { - throw new UserFriendlyException("暂不支持获取人脸"); - } + var face = await dbContext.Queryable() + .Where(x => x.UserCode == userCode) + .SingleAsync(); - var dbContext = sp.GetService(); - //todo 切换连接字符串 - dbContext.Client.CurrentConnectionConfig.ConnectionString = faceConnectionString; + face.ThrowIfNull("人脸照片尚未录入"); - var face = await dbContext.Queryable() - .Where(x => x.UserCode == userCode) - .SingleAsync(); + var timeLimitedDataProtector = _serviceProvider.GetService() + .CreateProtector("down:face") + .ToTimeLimitedDataProtector(); - face.ThrowIfNull("人脸照片尚未录入"); + var faceUrl = _configuration.GetSection("FaceUrlOptions:Url").Value; + var limit = _configuration.GetSection("FaceUrlOptions:Limit").Get(); - var timeLimitedDataProtector = sp.GetService() - .CreateProtector("down:face") - .ToTimeLimitedDataProtector(); + var key = $"face:{userCode}"; + var token = timeLimitedDataProtector.Protect(key, TimeSpan.FromSeconds(limit)); - var faceUrl = configuration.GetSection("FaceUrlOptions:Url").Value; - var limit = configuration.GetSection("FaceUrlOptions:Limit").Get(); - var dataProtectionProvider = sp.GetService(); - - var key = $"face:{userCode}"; - var token = timeLimitedDataProtector.Protect(key, TimeSpan.FromSeconds(limit)); - - return faceUrl + token; - }); + return faceUrl + token; } public async Task> GetFaceByUserCodesAsync(GetFaceByUserCodesRequest request) @@ -314,7 +350,7 @@ public class GainfoService : IGainfoService /// /// /// - public async Task> GetGainfoListTaskAsync(GainfoListRequest request) + public async Task> GetGainfoListAsync(GainfoListRequest request) { var page = await _gainfoStore.Queryable(request.Status) .LeftJoin((ga, de) => ga.DepartmentNo == de.No) @@ -333,25 +369,26 @@ public class GainfoService : IGainfoService DepartmentNo = de.No, DepartmentName = de.Name, Address = ga.Address, - Employed = ga.Employed }).ToPageListAsync(request); return page; } + /// /// 档案详情 /// /// /// - public async Task GetGainfoDetailTaskAsync(GainfoDetailRequest request) + public async Task GetGainfoDetailAsync(GainfoDetailRequest request) { var info = await _gainfoStore.Queryable(request.Status) .LeftJoin((ga, de) => ga.DepartmentNo == de.No) .LeftJoin((ga, de, type) => ga.Native == type.Code && type.Type == "native") .LeftJoin((ga, de, type, posi) => ga.Position == posi.Name) .LeftJoin((ga, de, type, posi, type2) => ga.CountryNo == type2.Code && type2.Type == "country") - .LeftJoin((ga, de, type, posi, type2, type3) => ga.Nation == type3.Code && type3.Type == "nation") + .LeftJoin((ga, de, type, posi, type2, type3) => ga.Nation == type3.Code && type3.Type == "nation") + .LeftJoin((ga, de, type, posi, type2, type3, type4) => ga.IdClass == type4.Code && type4.Type == "cls") .Where(ga => ga.No == request.No) - .Select((ga, de, type, posi, type2, type3) => new GainfoDetailResponse + .Select((ga, de, type, posi, type2, type3, type4) => new GainfoDetailResponse { No = ga.No, SapNo = ga.SapNo, @@ -370,11 +407,21 @@ public class GainfoService : IGainfoService Degree = ga.Degree, Werks = ga.Werks, CostId = ga.CostId, - Marital = ga.Marital, + Marital = ga.Marital == "Single" ? "未婚" : "已婚", CountryName = type2.Display, NationName = type3.Display, - IsActive = ga.Employed == "Active" ? true : false, - }).FirstAsync(); + ClassName = type4.Display, + }) + .FirstAsync(); + + if (info != null) + { + if (request.WithFace) + { + info.Face = await _dbContext.ChangeDb("face").Queryable().Where(x => x.UserCode == info.Code).Select(x => x.Face).SingleAsync(); + } + } + return info; } @@ -425,11 +472,8 @@ public class GainfoService : IGainfoService Marital = ga.Marital, CountryName = type2.Display, NationName = type3.Display, - IsActive = ga.Employed == "Active" ? true : false, }).ToPageListAsync(request); - //var infos = await _gainfoStore.Queryable(GainfoStatusEnumType.Working) - // .Where(ga => ga.CreateTime.ToString("yyyy-MM-dd") == time) - // .ToPageListAsync(request); + dic[time] = infos; } return dic; @@ -481,7 +525,6 @@ public class GainfoService : IGainfoService Marital = ga.Marital, CountryName = type2.Display, NationName = type3.Display, - IsActive = ga.Employed == "Active" ? true : false, }).ToPageListAsync(request); dic[time] = infos; } @@ -514,44 +557,6 @@ public class GainfoService : IGainfoService return res; } - public async Task> GetAdministratorAsync( WeekNumberRequest request, string token) - { - var werks = GetClaimValueFromToken(token, "tenant"); - var info = await _gainfoStore.Queryable(GainfoStatusEnumType.Working) - .LeftJoin((ga, de) => ga.DepartmentNo == de.No) - .LeftJoin((ga, de, type) => ga.Native == type.Code && type.Type == "native") - .LeftJoin((ga, de, type, posi) => ga.Position == posi.Name) - .LeftJoin((ga, de, type, posi, type2) => ga.CountryNo == type2.Code && type2.Type == "country") - .LeftJoin((ga, de, type, posi, type2, type3) => ga.Nation == type3.Code && type3.Type == "nation") - .Where(ga => ga.Werks == werks && ga.IdentityUserId != 0) - .Select((ga, de, type, posi, type2, type3) => new GainfoDetailResponse - { - No = ga.No, - SapNo = ga.SapNo, - Name = ga.Name, - Code = ga.Code, - DepartmentName = de.Name, - IdClass = ga.IdClass, - IdNo = ga.IdNo, - Gender = ga.Gender, - NativeName = type.Display, - Address = ga.Address, - Birthday = ga.Birthday, - Phone = ga.Phone, - Position = ga.Position, - Grade = posi.Grade, - Degree = ga.Degree, - Werks = ga.Werks, - CostId = ga.CostId, - Marital = ga.Marital, - CountryName = type2.Display, - NationName = type3.Display, - IsActive = ga.Employed == "Active" ? true : false, - }).ToPageListAsync(request); - - return info; - } - //-------------------------------------------help methods ------------------------------------------------ /// @@ -561,7 +566,7 @@ public class GainfoService : IGainfoService /// private async Task> GetRecursionDepartmentNosAsync(List departmentNos) { - if (departmentNos == null || departmentNos.Count == 0) + if (departmentNos.IsEmpty()) { return new List(); } @@ -579,13 +584,10 @@ public class GainfoService : IGainfoService void RecursionGetNext(string currentCode, List departments, List result) { var next = departments.Where(x => x.PNo == currentCode).ToList(); - foreach (var item in next) - { - result.Add(item.No); - } foreach (var item in next) { + result.Add(item.No); RecursionGetNext(item.No, departments, result); } } diff --git a/Src/Dlhis.Basic/Organization/Services/Implements/WerkService.cs b/Src/Dlhis.Basic/Organization/Services/Implements/WerkService.cs index 718bd6c1..99517d8a 100644 --- a/Src/Dlhis.Basic/Organization/Services/Implements/WerkService.cs +++ b/Src/Dlhis.Basic/Organization/Services/Implements/WerkService.cs @@ -1,5 +1,5 @@ using Dlhis.Sys.Api; -using Dlhis.User.Api.Response; +using Dlhis.Sys.Api.Response; using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Core.Result; using Kurisu.Core.User.Abstractions; diff --git a/Src/Dlhis.Basic/Packs/SqlSugarPack.cs b/Src/Dlhis.Basic/Packs/SqlSugarPack.cs index f481ce85..64d5b2aa 100644 --- a/Src/Dlhis.Basic/Packs/SqlSugarPack.cs +++ b/Src/Dlhis.Basic/Packs/SqlSugarPack.cs @@ -1,7 +1,10 @@ using Dlhis.Entity.Basic.Entity; using Kurisu.AspNetCore; using Kurisu.AspNetCore.Startup; +using Kurisu.Core.DataAccess.Entity; using Kurisu.SqlSugar.Extensions; +using Kurisu.SqlSugar.Services; +using Microsoft.AspNetCore.Mvc.Filters; using SqlSugar; namespace Dlhis.Basic.Packs; @@ -12,25 +15,57 @@ public class SqlSugarPack : BaseAppPack public override void ConfigureServices(IServiceCollection services) { - services.AddSqlSugar(); + services.AddSqlSugar(sp => + { + var configuration = sp.GetService(); + + return new List + { + new ConnectionConfig + { + ConfigId = "face", + + ConnectionString = configuration.GetSection("SqlSugarOptions:FaceConnectionString").Value, + DbType = DbType.MySql, + InitKeyType = InitKeyType.Attribute, + IsAutoCloseConnection = true, + + MoreSettings = new ConnMoreSettings + { + DisableNvarchar = true, + } + }, + new ConnectionConfig + { + ConfigId = "log", + + ConnectionString = configuration.GetSection("SqlSugarOptions:LogConnectionString").Value, + DbType = DbType.MySql, + InitKeyType = InitKeyType.Attribute, + IsAutoCloseConnection = true, + + MoreSettings = new ConnMoreSettings + { + DisableNvarchar = true, + } + }, + }; + }); } public override void Configure(IApplicationBuilder app, IWebHostEnvironment env) { - //using var scope = app.ApplicationServices.CreateScope(); - //var db = scope.ServiceProvider.GetService(); - //var tables = App.ActiveTyps.Where(x => x.IsDefined(typeof(SugarTable), false)) - // .Where(x => !x.IsDefined(typeof(SkipScanAttribute), false)) - // .ToArray(); + using var scope = app.ApplicationServices.CreateScope(); + var db = scope.ServiceProvider.GetService(); + var tables = App.ActiveTyps.Where(x => x.IsDefined(typeof(SugarTable), false)) + .Where(x => !x.IsDefined(typeof(SkipScanAttribute), false)) + .ToArray(); - //db.DbMaintenance.CreateDatabase(); - //db.CodeFirst.InitTables(tables); + db.Client.DbMaintenance.CreateDatabase(); + db.Client.CodeFirst.InitTables(tables); - //using var scopeFace = app.ApplicationServices.CreateScope(); - //var dbFace = scopeFace.ServiceProvider.GetService(); - //var faceConnectionString = Configuration.GetSection("SqlSugarOptions:FaceConnectionString").Value; - //dbFace.CurrentConnectionConfig.ConnectionString = faceConnectionString; - //dbFace.DbMaintenance.CreateDatabase(); - //dbFace.CodeFirst.InitTables(); + var faceDb = db.ChangeDb("face"); + faceDb.Client.DbMaintenance.CreateDatabase(); + faceDb.Client.CodeFirst.InitTables(); } -} +} \ No newline at end of file diff --git a/Src/Dlhis.Basic/Program.cs b/Src/Dlhis.Basic/Program.cs index 48192332..953eea17 100644 --- a/Src/Dlhis.Basic/Program.cs +++ b/Src/Dlhis.Basic/Program.cs @@ -1,8 +1,7 @@ using Kurisu.AspNetCore.Startup; +using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Startup; -using Microsoft.AspNetCore.DataProtection; using Microsoft.IdentityModel.Logging; -using StackExchange.Redis; class Program { @@ -22,11 +21,6 @@ public class Startup : DefaultStartup public override void ConfigureServices(IServiceCollection services) { base.ConfigureServices(services); - - //todo - var redis = ConnectionMultiplexer.Connect($"10.27.21.15:30050,defaultDatabase:1,password=dlhis123"); - services.AddDataProtection() - .SetApplicationName("basic") - .PersistKeysToStackExchangeRedis(redis, "DataProtection-Keys"); + services.AddRedis(); } } \ No newline at end of file diff --git a/Src/Dlhis.Basic/Summary/Controllers/SummariesController.cs b/Src/Dlhis.Basic/Summary/Controllers/SummariesController.cs new file mode 100644 index 00000000..2acddc4e --- /dev/null +++ b/Src/Dlhis.Basic/Summary/Controllers/SummariesController.cs @@ -0,0 +1,40 @@ +using Dlhis.Basic.Device.Response; +using Dlhis.Basic.Summary.Response; +using Dlhis.Basic.Summary.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Dlhis.Basic.Summary.Controllers; + +[Authorize] +[Route("api/[controller]")] +[ApiController] +public class SummariesController : ControllerBase +{ + private readonly ISummaryService _summaryService; + + public SummariesController(ISummaryService summaryService) + { + _summaryService = summaryService; + } + + /// + /// 考勤首页 + /// + /// + [HttpGet("attendance-summary")] + public async Task GetKaoQingSummary() + { + return await _summaryService.GetAttendanceSummaryAsync(); + } + + /// + /// 门禁首页数据 + /// + /// + [HttpGet("door-summary")] + public async Task GetDoorHomeDataAsync() + { + return await _summaryService.GetDoorHomeDataAsync(); + } +} diff --git a/Src/Dlhis.Basic/Summary/Dto/TrendDto.cs b/Src/Dlhis.Basic/Summary/Dto/TrendDto.cs new file mode 100644 index 00000000..2d2d046d --- /dev/null +++ b/Src/Dlhis.Basic/Summary/Dto/TrendDto.cs @@ -0,0 +1,8 @@ +namespace Dlhis.Basic.Summary.Dto; + +public class TrendDto +{ + public DateTime Date { get; set; } + + public int Count { get; set; } +} diff --git a/Src/Dlhis.Basic/Summary/Response/AttendanceDetailSummary.cs b/Src/Dlhis.Basic/Summary/Response/AttendanceDetailSummary.cs new file mode 100644 index 00000000..a013bb6a --- /dev/null +++ b/Src/Dlhis.Basic/Summary/Response/AttendanceDetailSummary.cs @@ -0,0 +1,24 @@ +namespace Dlhis.Basic.Summary.Response; + +public class AttendanceDetailSummary +{ + /// + /// 考勤总人数 + /// + public int Total => Employed + Unemployed; + + /// + /// 在职人数 + /// + public int Employed { get; set; } + + /// + /// 非在职人数 + /// + public int Unemployed { get; set; } + + /// + /// 人员趋势 7天 + /// + public GainfoTrendResponse GainfoTrend { get; set; } +} diff --git a/Src/Dlhis.Basic/Summary/Response/AttendanceDeviceSummary.cs b/Src/Dlhis.Basic/Summary/Response/AttendanceDeviceSummary.cs new file mode 100644 index 00000000..77a92955 --- /dev/null +++ b/Src/Dlhis.Basic/Summary/Response/AttendanceDeviceSummary.cs @@ -0,0 +1,31 @@ +using Dlhis.Basic.Summary.Dto; + +namespace Dlhis.Basic.Summary.Response; + +public class AttendanceDeviceSummary +{ + /// + /// 总数 + /// + public int Total { get; set; } + + /// + /// 在线数 + /// + public int OnlineCount { get; set; } + + /// + /// 启用数 + /// + public int EnableCount { get; set; } + + /// + /// 离线数 + /// + public int OfflineCount => Total - OnlineCount; + + /// + /// 新增设备趋势 30天内 + /// + public List Trend { get; set; } +} diff --git a/Src/Dlhis.Basic/Summary/Response/AttendanceSummaryResponse.cs b/Src/Dlhis.Basic/Summary/Response/AttendanceSummaryResponse.cs new file mode 100644 index 00000000..ab930d60 --- /dev/null +++ b/Src/Dlhis.Basic/Summary/Response/AttendanceSummaryResponse.cs @@ -0,0 +1,9 @@ +namespace Dlhis.Basic.Summary.Response +{ + public class AttendanceSummaryResponse + { + public AttendanceDeviceSummary DeviceSummary { get; set; } + + public AttendanceDetailSummary AttendanceDetailSummary { get; set; } + } +} diff --git a/Src/Dlhis.Basic/Summary/Response/DoorHomeDataResponse.cs b/Src/Dlhis.Basic/Summary/Response/DoorHomeDataResponse.cs new file mode 100644 index 00000000..163a35fe --- /dev/null +++ b/Src/Dlhis.Basic/Summary/Response/DoorHomeDataResponse.cs @@ -0,0 +1,11 @@ +using Dlhis.Basic.Summary.Dto; + +namespace Dlhis.Basic.Summary.Response +{ + public class DoorHomeDataResponse + { + public int OffNum { get; set; } + public int TotalNum { get; set; } + public List Trend { get; set; } + } +} diff --git a/Src/Dlhis.Basic/Summary/Response/GainfoTrendResponse.cs b/Src/Dlhis.Basic/Summary/Response/GainfoTrendResponse.cs new file mode 100644 index 00000000..4050c983 --- /dev/null +++ b/Src/Dlhis.Basic/Summary/Response/GainfoTrendResponse.cs @@ -0,0 +1,10 @@ +using Dlhis.Basic.Summary.Dto; + +namespace Dlhis.Basic.Summary.Response +{ + public class GainfoTrendResponse + { + public List EmployedTrend { get; set; } + public List UnemployedTrend { get; set; } + } +} diff --git a/Src/Dlhis.Basic/Summary/Services/ISummaryService.cs b/Src/Dlhis.Basic/Summary/Services/ISummaryService.cs new file mode 100644 index 00000000..9c92e218 --- /dev/null +++ b/Src/Dlhis.Basic/Summary/Services/ISummaryService.cs @@ -0,0 +1,11 @@ +using Dlhis.Basic.Device.Response; +using Dlhis.Basic.Summary.Response; + +namespace Dlhis.Basic.Summary.Services; + +public interface ISummaryService : IScopeDependency +{ + Task GetAttendanceSummaryAsync(); + + Task GetDoorHomeDataAsync(); +} diff --git a/Src/Dlhis.Basic/Summary/Services/Implements/SummaryService.cs b/Src/Dlhis.Basic/Summary/Services/Implements/SummaryService.cs new file mode 100644 index 00000000..4feeb179 --- /dev/null +++ b/Src/Dlhis.Basic/Summary/Services/Implements/SummaryService.cs @@ -0,0 +1,189 @@ +using Dlhis.Basic.Device.Response; +using Dlhis.Basic.Organization.Services; +using Dlhis.Basic.Summary.Dto; +using Dlhis.Basic.Summary.Response; +using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Basic.Enums; +using Dlhis.Entity.Device.Entities; +using Dlhis.Entity.Device.Enums; +using Kurisu.SqlSugar.Services; + +namespace Dlhis.Basic.Summary.Services.Implements; + +public class SummaryService : ISummaryService +{ + private readonly IDbContext _dbContext; + private readonly IGainfoStore _gainfoStore; + + public SummaryService(IDbContext dbContext, IGainfoStore gainfoStore) + { + _dbContext = dbContext; + _gainfoStore = gainfoStore; + } + + public async Task GetAttendanceSummaryAsync() + { + var now = DateTime.Now; + + var currentDay = DateTime.Today.AddDays(1).AddSeconds(-1); + var before30Day = DateTime.Today.AddDays(-29); + var before7Day = DateTime.Today.AddDays(-6); + + var devices = await _dbContext.Queryable().Where(x => x.Type == DeviceTypeEnumType.Attendance || (x.Type == DeviceTypeEnumType.Door && x.AsAttendance)).ToListAsync(); + var deviceUniqueNos = devices.Select(x => x.UniqueNo).ToList(); + + var deviceRegistries = await _dbContext.Queryable() + .LeftJoin((x, d) => x.DeviceUniqueNo == d.UniqueNo) + .Where(x => deviceUniqueNos.Contains(x.DeviceUniqueNo)) + .Select((x, d) => new + { + d.HeartbeatInterval, + x.LastHeartbeatTime + }) + .ToListAsync(); + + var workingGainfos = await _gainfoStore.Queryable(GainfoStatusEnumType.Working).ToListAsync(); + var leaveGainfos = await _gainfoStore.Queryable(GainfoStatusEnumType.Leave).ToListAsync(); + + return new AttendanceSummaryResponse + { + DeviceSummary = new AttendanceDeviceSummary + { + Total = devices.Count, + EnableCount = devices.Where(x => x.Enable).Count(), + OnlineCount = deviceRegistries.Where(x => + { + if (x.HeartbeatInterval <= 0) + { + return true; + } + + return x.LastHeartbeatTime.AddSeconds(x.HeartbeatInterval * 3) >= now; + + }).Count(), + Trend = FixTrend(devices.Where(x => x.CreateTime >= before30Day && x.CreateTime <= currentDay) + .GroupBy(x => x.CreateTime.Date) + .Select(x => new TrendDto { Date = x.Key, Count = x.Count() }), before30Day, currentDay) + }, + AttendanceDetailSummary = new AttendanceDetailSummary + { + Employed = workingGainfos.Count, + Unemployed = leaveGainfos.Count, + GainfoTrend = new GainfoTrendResponse + { + EmployedTrend = FixTrend(workingGainfos.Where(x => x.CreateTime >= before7Day && x.CreateTime <= currentDay) + .GroupBy(x => x.CreateTime.Date) + .Select(x => new TrendDto { Date = x.Key, Count = x.Count() }), before7Day, currentDay), + UnemployedTrend = FixTrend(leaveGainfos.Where(x => x.CreateTime >= before7Day && x.CreateTime <= currentDay) + .GroupBy(x => x.CreateTime.Date) + .Select(x => new TrendDto { Date = x.Key, Count = x.Count() }), before7Day, currentDay), + } + } + }; + } + + public async Task GetDoorHomeDataAsync() + { + var devices = await _dbContext.Queryable().Where(x => x.Type == DeviceTypeEnumType.Door).ToListAsync(); + + var offDevices = await GetOfflineDevicesCount(devices); + + //获取近30天并转化为"yyyy-MM-dd"集合 + DateTime currentDate = DateTime.Now; + List times = new List(); + for (int i = 0; i < 30; i++) + { + DateTime date = currentDate.AddDays(-i); + string dateString = date.ToString("yyyy-MM-dd"); + times.Add(dateString); + } + + var list = new List(); + times.ForEach(time => + { + var count = devices.Where(x => x.CreateTime.ToString("yyyy-MM-dd") == time).Count(); + var trend = new TrendDto + { + Date = DateTime.ParseExact(time, "yyyy-MM-dd", null), + Count = count, + }; + list.Add(trend); + }); + var res = new DoorHomeDataResponse + { + OffNum = offDevices.Count, + TotalNum = devices.Count, + Trend = list + }; + return res; + } + + private static List FixTrend(IEnumerable dtos, DateTime start, DateTime end) + { + var result = dtos.ToList(); + + start = start.Date; + end = end.Date; + + var currentDate = start; + while (currentDate <= end) + { + if (result.Any(x => x.Date == currentDate)) + { + currentDate = currentDate.AddDays(1); + continue; + } + + result.Add(new TrendDto { Date = currentDate, Count = 0 }); + + currentDate = currentDate.AddDays(1); + } + + return result.OrderBy(x => x.Date).ToList(); + } + + /// + /// 获取离线设备 + /// + /// + private async Task> GetOfflineDevicesCount(List devices) + { + var now = DateTime.Now; + + var deviceUniqueNos = devices.Select(x => x.UniqueNo).ToList(); + + var deviceRegistries = await _dbContext.Queryable() + .LeftJoin((x, d) => x.DeviceUniqueNo == d.UniqueNo) + .Where(x => deviceUniqueNos.Contains(x.DeviceUniqueNo)) + .Select((x, d) => new + { + UniqueNo = d.UniqueNo, + d.HeartbeatInterval, + x.LastHeartbeatTime + }) + .ToListAsync(); + + var OnlineDevices = deviceRegistries + .Where(x => + { + if (x.HeartbeatInterval <= 0) + { + return true; + } + + return x.LastHeartbeatTime.AddSeconds(x.HeartbeatInterval * 3) >= now; + + }) + .Select(x => new + { + UniqueNo = x.UniqueNo, + }) + .ToList(); + var onlineUniqueNos = new List(); + OnlineDevices.ForEach(x => onlineUniqueNos.Add(x.UniqueNo)); + + var offUniqueNos = deviceUniqueNos.Except(onlineUniqueNos).ToList(); + var list = devices.Where(x => offUniqueNos.Contains(x.UniqueNo)).ToList(); + return list; + } +} diff --git a/Src/Dlhis.Basic/appsettings.Development.json b/Src/Dlhis.Basic/appsettings.Development.json index ae653872..7b943fd9 100644 --- a/Src/Dlhis.Basic/appsettings.Development.json +++ b/Src/Dlhis.Basic/appsettings.Development.json @@ -9,6 +9,10 @@ "CommonRequestUrl": "http://localhost:5001", "SysRequestUrl": "http://localhost:5003" }, + "FaceUrlOptions": { + "Url": "http://localhost:5002/api/gainfos/face?token=", + "Limit": 7200 + }, "SwaggerOptions": { "Enable": true, "Authority": "https://yikatong.isawesome.cn/oauth2", diff --git a/Src/Dlhis.Basic/appsettings.json b/Src/Dlhis.Basic/appsettings.json index 8df2fbfb..906fdee8 100644 --- a/Src/Dlhis.Basic/appsettings.json +++ b/Src/Dlhis.Basic/appsettings.json @@ -11,13 +11,18 @@ "SysRequestUrl": "http://sys" }, "FaceUrlOptions": { - "Url": "https://face.jinkosolar.com/api/basic/gainfos/face?token=", + "Url": "https://yikatong.isawesome.cn/api/basic/gainfos/face?token=", "Limit": 7200 }, + "DataProtectionOptions": { + "Enable": true, + "AppName": "yiliantong" + }, "SqlSugarOptions": { "DefaultConnectionString": "server=10.27.21.22;port=3306;userid=root;password=dlhis123;database=yikatong;charset=utf8mb4;", "SlaveConnectionString": [], - "FaceConnectionString": "server=10.27.21.20;port=3306;userid=root;password=dlhis123;database=face;charset=utf8mb4;", + "FaceConnectionString": "server=192.168.199.124;port=3306;userid=root;password=dlhis123;database=face;charset=utf8mb4;", + "LogConnectionString": "server=192.168.199.124;port=3306;userid=root;password=dlhis123;database=log;charset=utf8mb4;", "Timeout": 30, "SlowSqlTime": 1, "EnableSqlLog": false @@ -33,6 +38,9 @@ "yikatong": "yikatong" } }, + "RedisOptions": { + "ConnectionString": "192.168.199.30:30115,defaultDatabase:1,password=dlhis123" + }, "IdentityServerOptions": { "Authority": "http://identity.oauth.svc.cluster.local", "RequireHttpsMetadata": false, diff --git a/Src/Dlhis.Canteen/Dlhis.Canteen.csproj b/Src/Dlhis.Canteen/Dlhis.Canteen.csproj index 36fc7e42..a17298ed 100644 --- a/Src/Dlhis.Canteen/Dlhis.Canteen.csproj +++ b/Src/Dlhis.Canteen/Dlhis.Canteen.csproj @@ -17,8 +17,9 @@ - + + @@ -35,7 +36,8 @@ - + + diff --git a/Src/Dlhis.Canteen/Dockerfile b/Src/Dlhis.Canteen/Dockerfile index b148099c..f9585740 100644 --- a/Src/Dlhis.Canteen/Dockerfile +++ b/Src/Dlhis.Canteen/Dockerfile @@ -7,8 +7,9 @@ EXPOSE 80 FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /src COPY ["Src/Dlhis.Canteen/Dlhis.Canteen.csproj", "Src/Dlhis.Canteen/"] -COPY ["Src/Dlhis.Common.Api/Dlhis.Common.Api.csproj", "Src/Dlhis.Common.Api/"] +COPY ["Src/Dlhis.Basic.Api/Dlhis.Basic.Api.csproj", "Src/Dlhis.Basic.Api/"] COPY ["Src/Dlhis.Entity/Dlhis.Entity.csproj", "Src/Dlhis.Entity/"] +COPY ["Src/Dlhis.Sys.Api/Dlhis.Sys.Api.csproj", "Src/Dlhis.Sys.Api/"] RUN dotnet restore "Src/Dlhis.Canteen/Dlhis.Canteen.csproj" COPY . . WORKDIR "/src/Src/Dlhis.Canteen" diff --git a/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs b/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs index c2fbc53b..e114ea36 100644 --- a/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs +++ b/Src/Dlhis.Canteen/Order/Controllers/BalancesController.cs @@ -1,9 +1,11 @@ using Dlhis.Canteen.Order.Request; using Dlhis.Canteen.Order.Response; using Dlhis.Canteen.Order.Services; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; using Kurisu.Core.DataAccess.Dto; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Org.BouncyCastle.Asn1.Ocsp; namespace Dlhis.Canteen.Order.Controllers; @@ -27,6 +29,7 @@ public class BalancesController : ControllerBase /// /// /// + [DataPermission] [HttpGet] public async Task> GetPageAsync([FromQuery] BalanceRecordPageRequest request) { @@ -39,6 +42,7 @@ public class BalancesController : ControllerBase /// /// [HttpGet("device/page")] + [IgnoreTenant] public async Task> GetPageByDeviceUniqueNoGetAsync([FromQuery] BalanceRecordPageByDeviceUniqueNoRequest request) { return await _balanceService.GetPageByDeviceUniqueNoAsync(request); @@ -50,6 +54,7 @@ public class BalancesController : ControllerBase /// /// [HttpPost("device/page")] + [IgnoreTenant] public async Task> GetPageByDeviceUniqueNoPostAsync(BalanceRecordPageByDeviceUniqueNoRequest request) { return await _balanceService.GetPageByDeviceUniqueNoAsync(request); @@ -72,6 +77,7 @@ public class BalancesController : ControllerBase /// /// [HttpPost("obsoluteOutRecord")] + [Transactional] public async Task ObsoluteAsync(BalanceRecordObsoluteRequest request) { await _balanceService.ObsoluteAsync(request); @@ -85,7 +91,7 @@ public class BalancesController : ControllerBase [HttpGet("recordByCostId")] public async Task> RecordByCostIdAsync([FromQuery] BalanceRecordPageByCostIdRequest request) { - return await _balanceService.RecordByCostIdAsync(request); + return await _balanceService.RecordByCostIdAsync(request); } /// @@ -132,4 +138,81 @@ public class BalancesController : ControllerBase { return await _balanceService.BalanceRecordAnalysisAsync(request); } + + /// + /// 获取消费机总数、离线设备数量、营业点数量、可消费人数、人均日消费额 + /// + /// + [HttpGet("consumenumbers")] + public async Task GetConsumeNumberAsync() + { + return await _balanceService.GetConsumeNumberAsync(); + } + + /// + /// 消费人数详情 + /// + /// + [HttpGet("spendingDetails")] + public async Task> GetSpendingDetailsAsync([FromQuery]SpendingDetailRequest request) + { + return await _balanceService.GetSpendingDetailsAsync(request); + } + + /// + /// 日消费均额 + /// + /// + /// + [HttpGet("averageDaily")] + public async Task> AverageDailyAsync([FromQuery] AverageDailyRequest request) + { + return await _balanceService.AverageDailyAsync(request); + } + + /// + /// 获取消费机详情 + /// + /// + /// + [HttpGet("consumeDevices")] + public async Task> GetConsumeDeviceAsync([FromQuery] GetConsumeDeviceRequest request) + { + return await _balanceService.GetConsumeDeviceAsync(request); + } + + + /// + /// 营业点增加配置设备接口 + /// + /// + /// + [HttpPost("disposition")] + public async Task DispositionDeviceAsync([FromQuery] DispositionDeviceRequest request) + { + await _balanceService.DispositionDeviceAsync(request); + } + + + /// + /// 电子餐券申请记录查询 + /// + /// + [HttpGet("oaemealvoucher")] + public async Task> GetOaEMealVoucherAsync([FromQuery] OaEMealVoucherRequest request) + { + return await _balanceService.GetOaEMealVoucherAsync(request); + } + + /// + /// 电子餐券使用记录查询 + /// + /// + /// + [HttpGet("oaemealvoucherdetail")] + public async Task> GetOaEMealVoucherDetailAsync([FromQuery] OaEMealVoucherDetailRequest request) + { + return await _balanceService.GetOaEMealVoucherDetailAsync(request); + } + } diff --git a/Src/Dlhis.Canteen/Order/Controllers/OrdersController.cs b/Src/Dlhis.Canteen/Order/Controllers/OrdersController.cs index 3e8f30e5..da3d8d38 100644 --- a/Src/Dlhis.Canteen/Order/Controllers/OrdersController.cs +++ b/Src/Dlhis.Canteen/Order/Controllers/OrdersController.cs @@ -39,9 +39,9 @@ public class OrdersController : ControllerBase /// /// [HttpPost] + [IgnoreTenant] public async Task AddAsync(OrderAddRequest request) { return await _orderService.AddAsync(request); } - } diff --git a/Src/Dlhis.Canteen/Order/Request/AverageDailyRequest.cs b/Src/Dlhis.Canteen/Order/Request/AverageDailyRequest.cs new file mode 100644 index 00000000..01a91d70 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Request/AverageDailyRequest.cs @@ -0,0 +1,8 @@ +using Kurisu.Core.DataAccess.Dto; + +namespace Dlhis.Canteen.Order.Request +{ + public class AverageDailyRequest : PageDto + { + } +} diff --git a/Src/Dlhis.Canteen/Order/Request/BalanceRecordPageRequest.cs b/Src/Dlhis.Canteen/Order/Request/BalanceRecordPageRequest.cs index 34ead05f..b30c502c 100644 --- a/Src/Dlhis.Canteen/Order/Request/BalanceRecordPageRequest.cs +++ b/Src/Dlhis.Canteen/Order/Request/BalanceRecordPageRequest.cs @@ -7,6 +7,17 @@ namespace Dlhis.Canteen.Order.Request; public class BalanceRecordPageRequest : PageDto, IValidatableObject { + /// + /// 营业点codes + /// + public List BusinessplaceCodes { get; set; } + + /// + /// 餐次 + /// + public List ShiftTypes { get; set; } + + /// /// 查询日期范围类型 DateTimeSearchEnumType /// diff --git a/Src/Dlhis.Canteen/Order/Request/ConsumptionTrendRequest.cs b/Src/Dlhis.Canteen/Order/Request/ConsumptionTrendRequest.cs new file mode 100644 index 00000000..7f223506 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Request/ConsumptionTrendRequest.cs @@ -0,0 +1,12 @@ +using Dlhis.Entity.Canteen.Enums; +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Canteen.Order.Request +{ + public class ConsumptionTrendRequest + { + [Required] + [EnumDataType(typeof(ConsumptionTrendEnumType))] + public ConsumptionTrendEnumType Type { get; set; } + } +} diff --git a/Src/Dlhis.Canteen/Order/Request/DispositionDeviceRequest.cs b/Src/Dlhis.Canteen/Order/Request/DispositionDeviceRequest.cs new file mode 100644 index 00000000..aa76e8c5 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Request/DispositionDeviceRequest.cs @@ -0,0 +1,9 @@ +namespace Dlhis.Canteen.Order.Request +{ + public class DispositionDeviceRequest + { + public string PlaceCode { get; set; } + + public List UniqueNos { get; set; } + } +} diff --git a/Src/Dlhis.Canteen/Order/Request/GetConsumeDeviceRequest.cs b/Src/Dlhis.Canteen/Order/Request/GetConsumeDeviceRequest.cs new file mode 100644 index 00000000..ee74d2fb --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Request/GetConsumeDeviceRequest.cs @@ -0,0 +1,9 @@ +using Kurisu.Core.DataAccess.Dto; + +namespace Dlhis.Canteen.Order.Request +{ + public class GetConsumeDeviceRequest : PageDto + { + public bool IsOff { get; set; } + } +} diff --git a/Src/Dlhis.Canteen/Order/Request/OaEMealVoucherDetailRequest.cs b/Src/Dlhis.Canteen/Order/Request/OaEMealVoucherDetailRequest.cs new file mode 100644 index 00000000..1a4d504b --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Request/OaEMealVoucherDetailRequest.cs @@ -0,0 +1,12 @@ +using Kurisu.Core.DataAccess.Dto; + +namespace Dlhis.Canteen.Order.Request +{ + public class OaEMealVoucherDetailRequest : PageDto + { + /// + /// 餐券id + /// + public long OaEMealVoucherId { get; set; } + } +} diff --git a/Src/Dlhis.Canteen/Order/Request/OaEMealVoucherRequest.cs b/Src/Dlhis.Canteen/Order/Request/OaEMealVoucherRequest.cs new file mode 100644 index 00000000..a77865a7 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Request/OaEMealVoucherRequest.cs @@ -0,0 +1,14 @@ +using Dlhis.Entity.Canteen.Enums; +using Kurisu.Core.DataAccess.Dto; +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Canteen.Order.Request +{ + public class OaEMealVoucherRequest : PageDto + { + /// + /// 用户学工号 + /// + public string UserCode { get; set; } + } +} diff --git a/Src/Dlhis.Canteen/Order/Request/OrderPageRequest.cs b/Src/Dlhis.Canteen/Order/Request/OrderPageRequest.cs index cd82d190..95374cfa 100644 --- a/Src/Dlhis.Canteen/Order/Request/OrderPageRequest.cs +++ b/Src/Dlhis.Canteen/Order/Request/OrderPageRequest.cs @@ -4,4 +4,5 @@ namespace Dlhis.Canteen.Order.Request; public class OrderPageRequest : PageDto { + } diff --git a/Src/Dlhis.Canteen/Order/Request/SpendingDetailRequest.cs b/Src/Dlhis.Canteen/Order/Request/SpendingDetailRequest.cs new file mode 100644 index 00000000..fcdcff5f --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Request/SpendingDetailRequest.cs @@ -0,0 +1,8 @@ +using Kurisu.Core.DataAccess.Dto; + +namespace Dlhis.Canteen.Order.Request +{ + public class SpendingDetailRequest:PageDto + { + } +} diff --git a/Src/Dlhis.Canteen/Order/Response/AverageDailyResponse.cs b/Src/Dlhis.Canteen/Order/Response/AverageDailyResponse.cs new file mode 100644 index 00000000..92541825 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Response/AverageDailyResponse.cs @@ -0,0 +1,25 @@ +namespace Dlhis.Canteen.Order.Response +{ + public class AverageDailyResponse + { + /// + /// 档案号/QRCode + /// + public string UserNo { get; set; } + + /// + /// 姓名 + /// + public string Name { get; set; } + + /// + /// 学工号 + /// + public string UserCode { get; set; } + + /// + /// 日消费均额 + /// + public int AverageAmount { get; set; } + } +} diff --git a/Src/Dlhis.Canteen/Order/Response/BalanceRecordResponse.cs b/Src/Dlhis.Canteen/Order/Response/BalanceRecordResponse.cs index d8ffb6ad..dcb63bab 100644 --- a/Src/Dlhis.Canteen/Order/Response/BalanceRecordResponse.cs +++ b/Src/Dlhis.Canteen/Order/Response/BalanceRecordResponse.cs @@ -119,4 +119,14 @@ public class BalanceRecordResponse /// 汇总金额 /// public decimal Sum { get; set; } + + /// + /// 厂区id + /// + public string TenantId { get; set; } + + /// + /// 厂区名称 + /// + public string TenantName { get; set; } } diff --git a/Src/Dlhis.Canteen/Order/Response/ConsumeNumberResponse.cs b/Src/Dlhis.Canteen/Order/Response/ConsumeNumberResponse.cs new file mode 100644 index 00000000..130bd99b --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Response/ConsumeNumberResponse.cs @@ -0,0 +1,40 @@ +namespace Dlhis.Canteen.Order.Response +{ + public class ConsumeNumberResponse + { + /// + /// 消费机总数 + /// + public int Total { get; set; } + + /// + /// 消费机离线数 + /// + public int OffNumber { get; set; } + + /// + /// 营业点总数 + /// + public int PlaceNumber { get; set; } + + /// + /// 可消费人数 + /// + public int ConsumerableNumber { get; set; } + + /// + /// 人均日消费额 + /// + public int DailyNumber { get; set; } + + /// + /// 日消费额走势图 + /// + public List Days { get; set; } + + /// + /// 月消费额走势图 + /// + public List Months { get; set; } + } +} diff --git a/Src/Dlhis.Canteen/Order/Response/ConsumptionTrendResponse.cs b/Src/Dlhis.Canteen/Order/Response/ConsumptionTrendResponse.cs new file mode 100644 index 00000000..b490dd19 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Response/ConsumptionTrendResponse.cs @@ -0,0 +1,20 @@ +namespace Dlhis.Canteen.Order.Response +{ + public class ConsumptionTrendResponse + { + /// + /// 时间 + /// + public string DateTime { get; set; } + + /// + /// 当日/月消费总额 + /// + public decimal CurrentAmount { get; set; } + + /// + /// 消费总额 + /// + public decimal TotalAmount { get; set; } + } +} diff --git a/Src/Dlhis.Canteen/Order/Response/DevicePageResponseResponse.cs b/Src/Dlhis.Canteen/Order/Response/DevicePageResponseResponse.cs new file mode 100644 index 00000000..1b3724d1 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Response/DevicePageResponseResponse.cs @@ -0,0 +1,104 @@ +using Dlhis.Entity.Device.Enums; +using Kurisu.AspNetCore.Utils.Extensions; + +namespace Dlhis.Canteen.Order.Response +{ + public class DevicePageResponse + { + public long Id { get; set; } + + /// + /// 设备code + /// + public Guid Code { get; set; } + + /// + /// 设备唯一码,IP/MAC/SNO + /// + public string UniqueNo { get; set; } + + /// + /// 心跳间隔 + /// + public int HeartbeatInterval { get; set; } + + /// + /// 品牌 + /// + public string Brand { get; set; } + + /// + /// 供应商 + /// + public string Supplier { get; set; } + + /// + /// 设备功能 DeviceFuncEnumType + /// + public List Funcs + { + get + { + if (FuncsStr.IsEmpty()) FuncsStr = "[]"; + + return FuncsStr.ToObject>(); + } + } + + public string FuncsStr { get; set; } + + /// + /// 设备机功能类型 + /// + public DeviceTypeEnumType Type { get; set; } + + public string TypeDescription => Type.GetDescription(); + + /// + /// 部署点 + /// + public string DeployPlace { get; set; } + + /// + /// 部署坐标 + /// + public string DeployPosition { get; set; } + + /// + /// 部署方向 + /// + public string DeployDirection { get; set; } + + /// + /// 模板code + /// + public Guid TemplateCode { get; set; } + + /// + /// 连接信息 + /// + public string ConnectionInfo { get; set; } + + + /// + /// 是否启用 + /// + public bool Enable { get; set; } + + /// + /// 是否在线 + /// + public bool IsOnline { get; set; } + + /// + /// 消费总额 + /// + public decimal Amount { get; set; } + + /// + /// 消费人次 + /// + public int UserCount { get; set; } + + } +} diff --git a/Src/Dlhis.Canteen/Order/Response/OaEMealVoucherDetailResponse.cs b/Src/Dlhis.Canteen/Order/Response/OaEMealVoucherDetailResponse.cs new file mode 100644 index 00000000..d57c1e7e --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Response/OaEMealVoucherDetailResponse.cs @@ -0,0 +1,65 @@ +using SqlSugar; + +namespace Dlhis.Canteen.Order.Response +{ + public class OaEMealVoucherDetailResponse + { + public long Id { get; set; } + + public long VoucherId { get; set; } + + public int Sequence { get; set; } + + public string DinnerType { get; set; } + + /// + /// 身份证 + /// + public string IdNo { get; set; } + + /// + /// 用餐人 + /// + public string Name { get; set; } + + /// + /// 手机号 + /// + public string Phone { get; set; } + + /// + /// 就餐开始 + /// + public DateTime Start { get; set; } + + /// + /// 就餐结束 + /// + public DateTime End { get; set; } + + /// + /// 就餐天数 + /// + public int Days { get; set; } + + /// + /// 金额 + /// + public decimal Amount { get; set; } + + /// + /// 二维码 + /// + public string QrCode { get; set; } + + /// + /// 状态 + /// + public string Status { get; set; } + + /// + /// 信息 + /// + public string Message { get; set; } + } +} diff --git a/Src/Dlhis.Canteen/Order/Response/OaEMealVoucherResponse.cs b/Src/Dlhis.Canteen/Order/Response/OaEMealVoucherResponse.cs new file mode 100644 index 00000000..32ceaeb3 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Response/OaEMealVoucherResponse.cs @@ -0,0 +1,100 @@ +using SqlSugar; + +namespace Dlhis.Canteen.Order.Response +{ + public class OaEMealVoucherResponse + { + public long Id { get; set; } + + /// + /// 标题 + /// + public string Title { get; set; } + + /// + /// 申请人工号 + /// + public string UserCode { get; set; } + + /// + /// 申请人名称 + /// + public string Name { get; set; } + + /// + /// 基地代码 + /// + public string TenantId { get; set; } + + /// + /// 基地名称 + /// + public string TenantName { get; set; } + + /// + /// 成本中心 + /// + public string CostId { get; set; } + + /// + /// 成本中心名称 + /// + public string CostName { get; set; } + + /// + /// 来宾公司 + /// + public string Company { get; set; } + + /// + /// 联系人 + /// + public string Phone { get; set; } + + /// + /// 来宾人数 + /// + public int Num { get; set; } + + /// + /// 就餐食堂 + /// + public string Canteen { get; set; } + + /// + /// 就餐事由 + /// + public string Reason { get; set; } + + /// + /// 就餐次 + /// + public string CanteenShift { get; set; } + + /// + /// oa流程id + /// + public string OaId { get; set; } + + /// + /// 部门号 + /// + public string DepartmentNo { get; set; } + + /// + /// 部门名称 + /// + public string DepartmentName { get; set; } + + + /// + /// 申请日期 + /// + public DateTime LogDate { get; set; } + + /// + /// 数据创建时间 + /// + public DateTime CreateTime { get; set; } + } +} diff --git a/Src/Dlhis.Canteen/Order/Response/OrderResultResponse.cs b/Src/Dlhis.Canteen/Order/Response/OrderResultResponse.cs index 69418e4d..98d4b96b 100644 --- a/Src/Dlhis.Canteen/Order/Response/OrderResultResponse.cs +++ b/Src/Dlhis.Canteen/Order/Response/OrderResultResponse.cs @@ -1,4 +1,6 @@ -namespace Dlhis.Canteen.Order.Response; +using Newtonsoft.Json; + +namespace Dlhis.Canteen.Order.Response; public class OrderResultResponse { @@ -22,8 +24,11 @@ public class OrderResultResponse /// public string DepartmentName { get; set; } + [JsonIgnore] + public decimal CurrentBalance { get; set; } + /// /// 余额 /// - public decimal Balance { get; set; } + public string Balance => CurrentBalance.ToString("f2"); } diff --git a/Src/Dlhis.Canteen/Order/Response/SpendingDetailResponse.cs b/Src/Dlhis.Canteen/Order/Response/SpendingDetailResponse.cs new file mode 100644 index 00000000..8ad95ac5 --- /dev/null +++ b/Src/Dlhis.Canteen/Order/Response/SpendingDetailResponse.cs @@ -0,0 +1,38 @@ +using SqlSugar; + +namespace Dlhis.Canteen.Order.Response +{ + public class SpendingDetailResponse + { + /// + /// 档案号/QRCode + /// + public string UserNo { get; set; } + + /// + /// 姓名 + /// + public string Name { get; set; } + + /// + /// 学工号 + /// + public string UserCode { get; set; } + + /// + /// 部门号 + /// + public string DepartmentNo { get; set; } + + /// + /// 成本中心 + /// + public string CostId { get; set; } + + /// + /// 消费者所在基地 + /// + public string OriginTenantId { get; set; } + + } +} diff --git a/Src/Dlhis.Canteen/Order/Services/IBalanceService.cs b/Src/Dlhis.Canteen/Order/Services/IBalanceService.cs index 8a12f2c4..8302795d 100644 --- a/Src/Dlhis.Canteen/Order/Services/IBalanceService.cs +++ b/Src/Dlhis.Canteen/Order/Services/IBalanceService.cs @@ -1,6 +1,7 @@ using Dlhis.Canteen.Order.Request; using Dlhis.Canteen.Order.Response; using Kurisu.Core.DataAccess.Dto; +using Microsoft.AspNetCore.Mvc; namespace Dlhis.Canteen.Order.Services; @@ -23,4 +24,19 @@ public interface IBalanceService : IScopeDependency Task> BalanceRecordSummaryByTidAsync(BalanceRecordSummaryRequest request); Task> BalanceRecordAnalysisAsync(BalanceRecordAnalysisRequest request); + + Task GetConsumeNumberAsync(); + + Task> GetSpendingDetailsAsync(SpendingDetailRequest request); + + Task> AverageDailyAsync(AverageDailyRequest request); + + Task> GetConsumeDeviceAsync(GetConsumeDeviceRequest request); + + Task DispositionDeviceAsync(DispositionDeviceRequest request); + + + Task> GetOaEMealVoucherAsync([FromQuery] OaEMealVoucherRequest request); + + Task> GetOaEMealVoucherDetailAsync([FromQuery] OaEMealVoucherDetailRequest request); } diff --git a/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs index eb6d36e4..9cddd2da 100644 --- a/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs +++ b/Src/Dlhis.Canteen/Order/Services/Implements/BalanceService.cs @@ -5,14 +5,16 @@ using Dlhis.Entity.Canteen.Entities; using Dlhis.Entity.Canteen.Enums; using Dlhis.Entity.Device.Entities; using Dlhis.Entity.Device.Enums; +using Dlhis.Sys.Api; using Kurisu.AspNetCore.UnifyResultAndValidation; using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Core.DataAccess.Dto; using Kurisu.Core.DataAccess.Entity; using Kurisu.Core.User.Abstractions; -using Kurisu.SqlSugar.Attributes; using Kurisu.SqlSugar.Extensions; using Kurisu.SqlSugar.Services; +using Mapster; +using Microsoft.AspNetCore.Mvc; using SqlSugar; namespace Dlhis.Canteen.Order.Services.Implements; @@ -22,12 +24,14 @@ public class BalanceService : IBalanceService private readonly IDbContext _dbContext; private readonly ICurrentUser _currentUser; private readonly ILogger _logger; + private readonly ITenantApi _tenantApi; - public BalanceService(IDbContext dbContext, ICurrentUser currentUser, ILogger logger) + public BalanceService(IDbContext dbContext, ICurrentUser currentUser, ILogger logger ,ITenantApi tenantApi) { _dbContext = dbContext; _currentUser = currentUser; _logger = logger; + _tenantApi = tenantApi; } public async Task> GetPageAsync(BalanceRecordPageRequest request) @@ -35,11 +39,17 @@ public class BalanceService : IBalanceService var page = await _dbContext.Queryable() .LeftJoin((record, ga) => record.UserNo == ga.No) .LeftJoin((record, ga, department) => ga.DepartmentNo == department.No) - .LeftJoin((record, ga, department, bplace) => record.BusinessPlaceCode == bplace.Code) - .LeftJoin((record, ga, department, bplace, bshift) => record.BusinessShiftCode == bshift.Code) - .LeftJoin((record, ga, department, bplace, bshift, type) => record.BusinessShiftCode.ToString() == type.Code) + .LeftJoin((record, ga, department, bplace) => record.BusinessPlaceCode == bplace.Code) + .LeftJoin((record, ga, department, bplace, bshift) => record.BusinessShiftCode == bshift.Code) + .LeftJoin((record, ga, department, bplace, bshift, type) => record.ShiftType == type.Code && type.Type == "mealshift") .WhereIF(request.SearchDateTimePeriod?.Start.HasValue == true, record => record.CostDate >= request.SearchDateTimePeriod.Start) .WhereIF(request.SearchDateTimePeriod?.End.HasValue == true, record => record.CostDate <= request.SearchDateTimePeriod.End) + .WhereIF(request.BusinessplaceCodes.IsPresent(), record => request.BusinessplaceCodes.Contains(record.BusinessPlaceCode)) + .WhereIF(request.ShiftTypes.IsPresent(), record => request.ShiftTypes.Contains(record.ShiftType)) + .WhereIF(request.Search.IsPresent(), (record, ga) => + ga.Name.Contains(request.Search) + || ga.Code.Contains(request.Search) + ) .OrderBy(record => record.CreateTime, OrderByType.Desc) .Select((record, ga, department, bplace, bshift, type) => new BalanceRecordResponse { @@ -65,10 +75,26 @@ public class BalanceService : IBalanceService DepartmentName = department.Name, BusinessPlaceName = bplace.Name, BusinessShiftName = type.Display, - CostDateTime = record.CreateTime // todo + CostDateTime = record.CostDateTime, + TenantId = record.TenantId, }) .ToPageListAsync(request); + try + { + var tenantResult = await _tenantApi.GetTenantsInternalAsync(); + var tenants = tenantResult.Data; + + page.Data.ForEach(item => + { + item.TenantName = tenants.FirstOrDefault(x => x.Code == item.TenantId)?.Name; + }); + + } + catch (Exception) + { + } + return page; } /// @@ -158,8 +184,8 @@ public class BalanceService : IBalanceService { var paeginfo = await _dbContext.Queryable() .LeftJoin((record, department) => record.DepartmentNo == department.No) - .LeftJoin((record, department, bplace) => record.BusinessPlaceCode == bplace.Code) - .LeftJoin((record, department, bplace, bshift) => record.BusinessShiftCode == bshift.Code) + .LeftJoin((record, department, bplace) => record.BusinessPlaceCode == bplace.Code) + .LeftJoin((record, department, bplace, bshift) => record.BusinessShiftCode == bshift.Code) .LeftJoin((record, department, bplace, bshift, type) => record.BusinessShiftCode.ToString() == type.Code) .WhereIF(!request.Name.IsEmpty(), record => record.Name == request.Name) .WhereIF(!request.DepartmentNo.IsEmpty(), record => record.DepartmentNo == request.DepartmentNo) @@ -387,6 +413,7 @@ public class BalanceService : IBalanceService obj.LunchNumberOfPeople = first.Where(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Lunch"]).Select(x => x.UserNo).Distinct().Count(); obj.DinnerNumberOfPeople = first.Where(x => x.BusinessShiftCode.ToString() == shiftCodeDict["Dinner"]).Select(x => x.UserNo).Distinct().Count(); obj.LateNightNumberOfPeople = first.Where(x => x.BusinessShiftCode.ToString() == shiftCodeDict["LateNight"]).Select(x => x.UserNo).Distinct().Count(); + obj.TotalNumberOfPeople = obj.BreakfastNumberOfPeople + obj.LunchNumberOfPeople + obj.DinnerNumberOfPeople + obj.LateNightNumberOfPeople; lst.Add(obj); } // 分页操作 @@ -405,7 +432,7 @@ public class BalanceService : IBalanceService } - [IgnoreTenant] + public async Task> GetPageByDeviceUniqueNoAsync(BalanceRecordPageByDeviceUniqueNoRequest request) { var now = DateTime.Now; @@ -434,8 +461,8 @@ public class BalanceService : IBalanceService var page = await _dbContext.Queryable() .LeftJoin((record, ga) => record.UserNo == ga.No) .LeftJoin((record, ga, department) => ga.DepartmentNo == department.No) - .LeftJoin((record, ga, department, bplace) => record.BusinessPlaceCode == bplace.Code) - .LeftJoin((record, ga, department, bplace, bshift) => record.BusinessShiftCode == bshift.Code) + .LeftJoin((record, ga, department, bplace) => record.BusinessPlaceCode == bplace.Code) + .LeftJoin((record, ga, department, bplace, bshift) => record.BusinessShiftCode == bshift.Code) .LeftJoin((record, ga, department, bplace, bshift, type) => record.BusinessShiftCode.ToString() == type.Code) .WhereIF(request.SearchDate.HasValue, record => record.CostDate >= start) .WhereIF(request.SearchDate.HasValue, record => record.CostDate <= end) @@ -490,8 +517,8 @@ public class BalanceService : IBalanceService var page = await _dbContext.Queryable() .LeftJoin((record, ga) => record.UserNo == ga.No) .LeftJoin((record, ga, department) => ga.DepartmentNo == department.No) - .LeftJoin((record, ga, department, bplace) => record.BusinessPlaceCode == bplace.Code) - .LeftJoin((record, ga, department, bplace, bshift) => record.BusinessShiftCode == bshift.Code) + .LeftJoin((record, ga, department, bplace) => record.BusinessPlaceCode == bplace.Code) + .LeftJoin((record, ga, department, bplace, bshift) => record.BusinessShiftCode == bshift.Code) .LeftJoin((record, ga, department, bplace, bshift, type) => record.BusinessShiftCode.ToString() == type.Code) .WhereIF(request.SearchDateTimePeriod?.Start.HasValue == true, record => record.CostDate >= request.SearchDateTimePeriod.Start) .WhereIF(request.SearchDateTimePeriod?.End.HasValue == true, record => record.CostDate <= request.SearchDateTimePeriod.End) @@ -507,14 +534,13 @@ public class BalanceService : IBalanceService No = record.No, BusinessPlaceName = bplace.Name, BusinessShiftName = type.Display, - CostDateTime = record.CreateTime // todo + CostDateTime = record.CostDateTime }) .ToPageListAsync(request); return page; } - [Transactional] public async Task ObsoluteAsync(BalanceRecordObsoluteRequest request) { var balanceRecord = await _dbContext.Queryable().Where(x => x.Id == request.Id).SingleAsync(); @@ -547,6 +573,364 @@ public class BalanceService : IBalanceService }, 5); } + public async Task GetConsumeNumberAsync() + { + var devices = await _dbContext.Queryable() + .Where(x => x.Type == DeviceTypeEnumType.Meal) + .ToListAsync(); + + var offCount = await GetOfflineDevicesCount(devices); + + var places = await _dbContext.Queryable() + .Where(x => x.Status == CanteenBusinessPlaceStatusEnumType.Opened) + .ToListAsync(); + + + //所有消费数据 + var bs = await _dbContext.Queryable() + .Where(x => x.Type == BalanceRecordTypeEnumType.Out) + .ToListAsync(); + //可消费人数 + var consumerable = bs.Select(x => x.UserCode).Distinct().Count(); + + var dailyNumber = SameDay(bs,""); + + + //消费走势 + var months = await GetConsumptionTrendsAsync(ConsumptionTrendEnumType.Month); + var days = await GetConsumptionTrendsAsync(ConsumptionTrendEnumType.Day); + + var res = new ConsumeNumberResponse + { + Total = devices.Count, + OffNumber = offCount.Count, + PlaceNumber = places.Count, + ConsumerableNumber = consumerable, + DailyNumber = dailyNumber, + Days = days, + Months = months + }; + return res; + + } + /// + /// 获取单日消费数据 + /// + /// + /// + /// + private int SameDay(List bs,string userCode) + { + var time = DateTime.Now.ToString("yyyy-MM-dd"); + var b = userCode.IsEmpty(); + //当日消费数据 + var monthbs = bs + .Where(x => x.CostDateTime.ToString("yyyy-MM-dd") == time) + .WhereIF(!userCode.IsEmpty(),x => x.UserCode == userCode); + //当日消费额 + var monthlySpending = monthbs.Sum(x => x.Amount); + //当日消费人数 + var monthlyCount = monthbs.Select(x => x.UserCode).Distinct().Count(); + var dailyNumber = monthlyCount != 0 ? Math.Abs((int)Math.Ceiling(monthlySpending / monthlyCount)) : 0; + return dailyNumber; + } + + public async Task> GetSpendingDetailsAsync(SpendingDetailRequest request) + { + var brs = await _dbContext + .Queryable() + .Where(x => x.Type == BalanceRecordTypeEnumType.Out) + .ToListAsync(); + var codes = brs.Select(x => x.UserCode).ToList().Distinct().ToList(); + + var lst = new List(); + foreach (var code in codes) + { + var br = brs.Where(x => x.UserCode == code).FirstOrDefault(); + var res = br.Adapt(); + lst.Add(res); + } + + // 分页操作 + var startIndex = (request.PageIndex - 1) * request.PageSize; + var pagedData = lst.Skip(startIndex).Take(request.PageSize).ToList(); + + var pagination = new Pagination + { + PageIndex = request.PageIndex, + PageSize = request.PageSize, + Total = lst.Count, // 使用原始列表的总数进行分页 + Data = pagedData + }; + + return pagination; + } + + public async Task> AverageDailyAsync(AverageDailyRequest request) + { + //所有消费数据 + var brs = await _dbContext.Queryable() + .Where(x => x.Type == BalanceRecordTypeEnumType.Out) + .ToListAsync(); + var codes = brs.Select(x => x.UserCode).ToList().Distinct().ToList(); + + var lst = new List(); + foreach (var code in codes) + { + var br = brs.Where(x => x.UserCode == code).FirstOrDefault(); + var res = br.Adapt(); + var dailyNumber = SameDay(brs,code); + res.AverageAmount = dailyNumber; + lst.Add(res); + } + + // 分页操作 + var startIndex = (request.PageIndex - 1) * request.PageSize; + var pagedData = lst.Skip(startIndex).Take(request.PageSize).ToList(); + + var pagination = new Pagination + { + PageIndex = request.PageIndex, + PageSize = request.PageSize, + Total = lst.Count, // 使用原始列表的总数进行分页 + Data = pagedData + }; + + return pagination; + } + + public async Task> GetConsumeDeviceAsync(GetConsumeDeviceRequest request) + { + if (request.IsOff) + { + //消费机离线设备 + var devices = await _dbContext.Queryable() + .Where(x => x.Type == DeviceTypeEnumType.Meal) + .ToListAsync(); + + var offs = await GetOfflineDevicesCount(devices); + var offUniqueNos = offs.Select(x => x.UniqueNo).ToList(); + + var offpage = await _dbContext.Queryable() + .LeftJoin((d, dt) => d.TemplateCode == dt.Code) + .Where(d => offUniqueNos.Contains(d.UniqueNo)) + .Select((d, dt) => new DevicePageResponse + { + FuncsStr = d.Funcs.ToString(), + Brand = dt.Brand, + Type = d.Type, + + }, true) + .ToPageListAsync(request); + return offpage; + } + //消费机总数 + var page = await _dbContext.Queryable() + .LeftJoin((d, dt) => d.TemplateCode == dt.Code) + .Where(d => d.Type == DeviceTypeEnumType.Meal) + .Select((d, dt) => new DevicePageResponse + { + FuncsStr = d.Funcs.ToString(), + Brand = dt.Brand, + Type = d.Type, + + }, true) + .ToPageListAsync(request); + return page; + } + + + /// + /// 获取离线设备 + /// + /// + /// + private async Task> GetOfflineDevicesCount(List devices) + { + var now = DateTime.Now; + + var deviceUniqueNos = devices.Select(x => x.UniqueNo).ToList(); + + var deviceRegistries = await _dbContext.Queryable() + .LeftJoin((x, d) => x.DeviceUniqueNo == d.UniqueNo) + .Where(x => deviceUniqueNos.Contains(x.DeviceUniqueNo)) + .Select((x, d) => new + { + UniqueNo = d.UniqueNo, + d.HeartbeatInterval, + x.LastHeartbeatTime + }) + .ToListAsync(); + + var OnlineDevices = deviceRegistries + .Where(x => + { + if (x.HeartbeatInterval <= 0) + { + return true; + } + + return x.LastHeartbeatTime.AddSeconds(x.HeartbeatInterval * 3) >= now; + + }) + .Select(x => new + { + UniqueNo = x.UniqueNo, + }) + .ToList(); + var onlineUniqueNos = new List(); + OnlineDevices.ForEach(x => onlineUniqueNos.Add(x.UniqueNo)); + + var offUniqueNos = deviceUniqueNos.Except(onlineUniqueNos).ToList(); + var list = devices.Where(x => offUniqueNos.Contains(x.UniqueNo)).ToList(); + return list; + } + + private async Task> GetConsumptionTrendsAsync(ConsumptionTrendEnumType type) + { + var bs = await _dbContext.Queryable() + .Where(x => x.Type == BalanceRecordTypeEnumType.Out) + .ToListAsync(); + //消费总额 + var totalAmount = Math.Round(Math.Abs(bs.Sum(x => x.Amount)), 2); + + var list = new List(); + if (type == ConsumptionTrendEnumType.Month) + { + //月与总额对比 + var months = GetMonthsOfYear(); + + foreach (var month in months) + { + var currentAmount = Math.Round(Math.Abs(bs.Where(x => x.CostDateTime.ToString("yyyy-MM") == month) + .Sum(x => x.Amount)), 2); + + var res = new ConsumptionTrendResponse + { + DateTime = month, + CurrentAmount = currentAmount, + TotalAmount = totalAmount + }; + list.Add(res); + } + } + else + { + //日与总额对比 + var days = GetDaysOfMonth(); + + foreach (var day in days) + { + var currentAmount = Math.Round(Math.Abs(bs.Where(x => x.CostDateTime.ToString("yyyy-MM-dd") == day) + .Sum(x => x.Amount)), 2); + + var res = new ConsumptionTrendResponse + { + DateTime = day, + CurrentAmount = currentAmount, + TotalAmount = totalAmount + }; + list.Add(res); + } + } + return list; + } + + + public async Task DispositionDeviceAsync(DispositionDeviceRequest request) + { + var places = await _dbContext.Queryable().ToListAsync(); + var place = places.Where(x => x.Code.ToString() == request.PlaceCode).First(); + place.ThrowIfNull($"{request.PlaceCode}:该营业点不存在"); + + var devices = await _dbContext.Queryable().ToListAsync(); + + if (request.UniqueNos != null && request.UniqueNos.Count > 0) + { + foreach (var no in request.UniqueNos) + { + var device = devices.Where(x => x.UniqueNo == no).First(); + device.ThrowIfNull($"{no}:设备不存在"); + } + } + + var regiss = await _dbContext.Queryable() + .Where(x => request.UniqueNos.Contains(x.DeviceUniqueNo)) + .ToListAsync(); + + var list = new List(); + + foreach (var regis in regiss) + { + regis.BusinessPlaceCode = place.Code; + regis.BusinessPlaceCustomCode = place.CustomCode; + list.Add(regis); + } + await _dbContext.UpdateAsync(list); + } + + public async Task> GetOaEMealVoucherAsync(OaEMealVoucherRequest request) + { + var page = await _dbContext.Queryable() + .Where(x => x.UserCode == request.UserCode) + .Select() + .ToPageListAsync(request); + return page; + } + + public async Task> GetOaEMealVoucherDetailAsync(OaEMealVoucherDetailRequest request) + { + var page = await _dbContext.Queryable() + .Where(x => x.VoucherId == request.OaEMealVoucherId) + .Select() + .ToPageListAsync(request); + + return page; + } + + /// + /// 获取当前年所有月 + /// + /// + private List GetMonthsOfYear() + { + List months = new List(); + // 获取当前年份 + int year = DateTime.Now.Year; + // 遍历12个月份并添加到列表中 + for (int month = 1; month <= 12; month++) + { + DateTime monthDate = new DateTime(year, month, 1); + months.Add(monthDate.ToString("yyyy-MM")); + } + return months; + } + + /// + /// 获取当前月所有天 + /// + /// + private List GetDaysOfMonth() + { + List days = new List(); + + // 获取当前年份和月份 + int year = DateTime.Now.Year; + int month = DateTime.Now.Month; + + // 获取当前月份的第一天和最后一天 + DateTime firstDayOfMonth = new DateTime(year, month, 1); + DateTime lastDayOfMonth = firstDayOfMonth.AddMonths(1).AddDays(-1); + + // 遍历当前月的所有日期并添加到列表中 + for (DateTime date = firstDayOfMonth; date <= lastDayOfMonth; date = date.AddDays(1)) + { + days.Add(date.ToString("yyyy-MM-dd")); + } + + return days; + } + //************************************************************** help methods ****************************************************************** @@ -572,29 +956,57 @@ public class BalanceService : IBalanceService throw new UserFriendlyException("操作失败,请重试"); } - [IgnoreTenant] public async Task> GetShiftCode() { - _dbContext.Client.QueryFilter.ClearAndBackup(); try { var shiftCodeDict = new Dictionary(); - var shifts = await _dbContext.Queryable().ToListAsync(); + var shifts = await _dbContext.Queryable().ToListAsync(); //guid - var breakfastCode = shifts.Where(x => x.CustomCode == "shift-breakfast").Select(x => x.Code).First().ToString(); + //早餐 + var breakfast = shifts.Where(x => x.CustomCode == "shift-breakfast").FirstOrDefault(); + var codeBreakfast = "Breakfast"; + if (breakfast != null) + { + codeBreakfast = breakfast.Code.ToString(); + } + //午餐 + var lunch = shifts.Where(x => x.CustomCode == "shift-lunch").FirstOrDefault(); + var codeLunch = "Lunch"; + if (lunch != null) + { + codeLunch = lunch.Code.ToString(); + } + //晚餐 + var dinner = shifts.Where(x => x.CustomCode == "shift-dinner").FirstOrDefault(); + var codeDinner = "Dinner"; + if (dinner != null) + { + codeDinner = dinner.Code.ToString(); + } + //宵夜 + var nightsnack = shifts.Where(x => x.CustomCode == "shift-nightsnack").FirstOrDefault(); + var codeNightsnack = "Nightsnack"; + if (nightsnack != null) + { + codeNightsnack = nightsnack.Code.ToString(); + } + //凌晨 + var midnightsnack = shifts.Where(x => x.CustomCode == "shift-midnightsnack").FirstOrDefault(); + var codeMidnightsnack = "Midnightsnack"; + if (midnightsnack != null) + { + codeMidnightsnack = midnightsnack.Code.ToString(); + } - var lunchCode = shifts.Where(x => x.CustomCode == "shift-lunch").Select(x => x.Code).First().ToString(); - var dinnerCode = shifts.Where(x => x.CustomCode == "shift-dinner").Select(x => x.Code).First().ToString(); - - var lateNightCode = shifts.Where(x => x.CustomCode == "shift-midnightsnack").Select(x => x.Code).First().ToString(); - - shiftCodeDict.Add("Breakfast", breakfastCode.ToString()); - shiftCodeDict.Add("Lunch", lunchCode.ToString()); - shiftCodeDict.Add("Dinner", dinnerCode.ToString()); - shiftCodeDict.Add("LateNight", lateNightCode.ToString()); + shiftCodeDict.Add("Breakfast", codeBreakfast); + shiftCodeDict.Add("Lunch", codeLunch); + shiftCodeDict.Add("Dinner", codeDinner); + shiftCodeDict.Add("LateNight", codeNightsnack); + shiftCodeDict.Add("Midnightsnack", codeMidnightsnack); return shiftCodeDict; } @@ -607,6 +1019,6 @@ public class BalanceService : IBalanceService { _dbContext.Client.QueryFilter.Restore(); } - + } } diff --git a/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs b/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs index f0cd8c8f..c2cabe68 100644 --- a/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs +++ b/Src/Dlhis.Canteen/Order/Services/Implements/OrderService.cs @@ -1,4 +1,5 @@ -using Dlhis.Canteen.Order.Request; +using Dlhis.Basic.Api; +using Dlhis.Canteen.Order.Request; using Dlhis.Canteen.Order.Response; using Dlhis.Canteen.Setting.Services; using Dlhis.Entity.Basic.Entity; @@ -10,7 +11,6 @@ using Kurisu.AspNetCore.Utils; using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Core.CustomClass; using Kurisu.Core.DataAccess.Dto; -using Kurisu.SqlSugar.Attributes; using Kurisu.SqlSugar.Extensions; using Kurisu.SqlSugar.Services; using Mapster; @@ -22,14 +22,17 @@ public class OrderService : IOrderService { private readonly IDbContext _dbContext; private readonly ICanteenSettingService _canteenSettingService; + private readonly IDevicePermissionApi _devicePermissionApi; private readonly ILogger _logger; public OrderService(IDbContext dbContext, ICanteenSettingService canteenSettingService, + IDevicePermissionApi devicePermissionApi, ILogger logger) { _dbContext = dbContext; _canteenSettingService = canteenSettingService; + _devicePermissionApi = devicePermissionApi; _logger = logger; } @@ -42,7 +45,14 @@ public class OrderService : IOrderService return page; } - [IgnoreTenant] + private async Task CheckPAsync(string userNo, string deviceUniqueNo) + { + var uniqueNosResult = await _devicePermissionApi.GetCanteenDeviceUniqueNosByUserNoAsync(userNo); + var uniqueNos = uniqueNosResult.Data; + + uniqueNos.Contains(deviceUniqueNo).ThrowIfFalse("无消费权限"); + } + public async Task AddAsync(OrderAddRequest request) { //尝试匹配userId @@ -50,8 +60,9 @@ public class OrderService : IOrderService //根据档案号 var gainfo = await _dbContext.Queryable() - .Where(x => x.No == request.UserUniqueNo || x.Code == request.UserUniqueNo || x.Id == userId) + .Where(x => x.No == request.UserUniqueNo || x.Code == request.UserUniqueNo || x.Id == userId || x.CardNo == request.UserUniqueNo) .FirstAsync(); + gainfo.ThrowIfNull($"用户{request.UserUniqueNo}不存在,无法操作"); var canteenUserExists = await _dbContext.Queryable() @@ -59,6 +70,9 @@ public class OrderService : IOrderService .AnyAsync(); canteenUserExists.ThrowIfFalse($"就餐用户{request.UserUniqueNo}不存在,无法操作"); + //消费权限控制 + await CheckPAsync(gainfo.No, request.DeviceUniqueNo); + //消费 if (request.Type == BalanceRecordTypeEnumType.Out) { @@ -81,7 +95,7 @@ public class OrderService : IOrderService UserName = ga.Name, DepartmentNo = ga.DepartmentNo, DepartmentName = department.Name, - Balance = mealUser.Balance + CurrentBalance = mealUser.Balance }) .SingleAsync(); } @@ -98,11 +112,20 @@ public class OrderService : IOrderService device.ThrowIfNull("设备不存在或者尚未启用"); var registry = await _dbContext.Queryable().Where(x => x.DeviceUniqueNo == request.DeviceUniqueNo).FirstAsync(); registry.ThrowIfNull("设备尚未注册"); - var businessPlace = await _dbContext.Queryable().Where(x => x.Code == registry.BusinessPlaceCode).FirstAsync(); - businessPlace.ThrowIfNull("营业点尚未设置"); + var businessPlaceInfo = await _dbContext.Queryable() + .LeftJoin((x, sr) => x.Code == sr.PlaceCode) + .Where(x => x.Code == registry.BusinessPlaceCode) + .Select((x, sr) => new + { + x.Code, + sr.ShiftCode, + x.TenantId + }) + .FirstAsync(); + businessPlaceInfo.ThrowIfNull("营业点尚未设置"); //获取当前消费餐次 - var shifts = await _dbContext.Queryable().Where(x => x.BusinessPlaceCode == businessPlace.Code).ToListAsync(); + var shifts = await _dbContext.Queryable().Where(x => x.ShiftCode == businessPlaceInfo.ShiftCode).ToListAsync(); var shift = shifts.FirstOrDefault(x => new TimePeriod() { @@ -148,8 +171,9 @@ public class OrderService : IOrderService UserNo = gainfo.No, UserCode = gainfo.Code, Amount = request.Amount, - BusinessPlaceCode = businessPlace.Code, - BusinessShiftCode = shift?.Code, + BusinessPlaceCode = businessPlaceInfo.Code, + BusinessShiftCode = shift?.ShiftCode, + ShiftType = shift?.ShiftType, CostDate = costDate.Date, CostId = gainfo.CostId, DepartmentNo = gainfo.DepartmentNo, @@ -170,7 +194,7 @@ public class OrderService : IOrderService CostDateTime = request.CostDateTime, //标识当前消费的基地 - TenantId = businessPlace.TenantId, + TenantId = businessPlaceInfo.TenantId, }; var orderId = await _dbContext.InsertReturnIdentityAsync(order); @@ -312,7 +336,20 @@ public class OrderService : IOrderService { if (settings.TryGetValue(item, out var v)) { - return v.Adapt(); + try + { + + if (int.TryParse(v, out var n)) + { + return n.Adapt(); + } + + return v.Adapt(); + } + catch (Exception) + { + return defaultValue; + } } return defaultValue; @@ -328,7 +365,7 @@ public class OrderService : IOrderService /// /// /// - private async Task Verify(Dictionary settings, List shifts, DateTime costDateTime, CanteenOrderEntity order) + private async Task Verify(Dictionary settings, List shifts, DateTime costDateTime, CanteenOrderEntity order) { //在线 if (!order.IsOffline) @@ -352,7 +389,7 @@ public class OrderService : IOrderService var dayMarketCostMax = GetSetting(settings, CanteenSettingItemEnumType.DayMarketCostMax); if (dayMarketCostMax != default) { - var businessPlaceCodes = await _dbContext.Queryable() + var businessPlaceCodes = await _dbContext.Queryable() .Where(x => x.Catalog == CanteenBusinessPlaceCatalogEnumType.Supermarket) .Select(x => x.Code).ToListAsync(); @@ -437,30 +474,35 @@ public class OrderService : IOrderService /// /// /// - private async Task GenerateConsumeRecordAsync(long orderId, Dictionary settings, List shifts, DateTime costDateTime) + private async Task GenerateConsumeRecordAsync(long orderId, Dictionary settings, List shifts, DateTime costDateTime) { var order = await _dbContext.Queryable().Where(x => x.Id == orderId).FirstAsync(); //校验 await Verify(settings, shifts, costDateTime, order); + var allowNoBalance = GetSetting(settings, CanteenSettingItemEnumType.AllowNoBalance); + await ContinueUpdateAsync(async () => { - var canteenUser = await _dbContext.Queryable().Where(x => x.UserNo == order.UserNo).FirstAsync(); - //金额+- - canteenUser.Balance += order.Amount; - if (canteenUser.Balance < 0) + if (!allowNoBalance) { - throw new UserFriendlyException("余额不足,无法消费"); + var canteenUser = await _dbContext.Queryable().Where(x => x.UserNo == order.UserNo).FirstAsync(); + //金额+- + canteenUser.Balance += order.Amount; + if (canteenUser.Balance < 0) + { + throw new UserFriendlyException("余额不足,无法消费"); + } + + var effect = await _dbContext.Updateable() + .SetColumns(x => x.Balance == canteenUser.Balance) + .SetColumns(x => x.Version == x.Version + 1) + .Where(x => x.UserNo == order.UserNo && x.Version == canteenUser.Version)//必须与查询时的version一致 + .ExecuteCommandAsync(); + + if (effect <= 0) throw new VersionExceptions("更新版本冲突"); } - var effect = await _dbContext.Updateable() - .SetColumns(x => x.Balance == canteenUser.Balance) - .SetColumns(x => x.Version == x.Version + 1) - .Where(x => x.UserNo == order.UserNo && x.Version == canteenUser.Version)//必须与查询时的version一致 - .ExecuteCommandAsync(); - - if (effect <= 0) throw new VersionExceptions("更新版本冲突"); - //写入记录 var balanceRecord = order.Adapt(); await _dbContext.InsertAsync(balanceRecord); diff --git a/Src/Dlhis.Canteen/Program.cs b/Src/Dlhis.Canteen/Program.cs index d64882de..ce855b85 100644 --- a/Src/Dlhis.Canteen/Program.cs +++ b/Src/Dlhis.Canteen/Program.cs @@ -1,4 +1,5 @@ using Kurisu.AspNetCore.Startup; +using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Startup; using Microsoft.IdentityModel.Logging; @@ -18,4 +19,17 @@ public class Startup : DefaultStartup { IdentityModelEventSource.ShowPII = true; } + + public override void ConfigureServices(IServiceCollection services) + { + base.ConfigureServices(services); + + services.AddRedis(); + } + + public override void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + base.Configure(app, env); + app.UseSnowFlakeInit(); + } } \ No newline at end of file diff --git a/Src/Dlhis.Canteen/Setting/Services/Implements/CanteenSettingService.cs b/Src/Dlhis.Canteen/Setting/Services/Implements/CanteenSettingService.cs index 0d24c61e..cb707ca8 100644 --- a/Src/Dlhis.Canteen/Setting/Services/Implements/CanteenSettingService.cs +++ b/Src/Dlhis.Canteen/Setting/Services/Implements/CanteenSettingService.cs @@ -90,6 +90,7 @@ public class CanteenSettingService : ICanteenSettingService CanteenSettingItemEnumType.InitBalance => IsValidFormat(value), //bool + CanteenSettingItemEnumType.AllowNoBalance => IsValidFormat(value, request.Item), CanteenSettingItemEnumType.AllowSendAtMeal => IsValidFormat(value, request.Item), CanteenSettingItemEnumType.AllowRepeatInOneShift => IsValidFormat(value, request.Item), CanteenSettingItemEnumType.AllowCrossTenant => IsValidFormat(value, request.Item), diff --git a/Src/Dlhis.Canteen/SqlSugarPack.cs b/Src/Dlhis.Canteen/SqlSugarPack.cs index 3abac33b..f8d3a155 100644 --- a/Src/Dlhis.Canteen/SqlSugarPack.cs +++ b/Src/Dlhis.Canteen/SqlSugarPack.cs @@ -1,5 +1,9 @@ using Kurisu.AspNetCore.Startup; +using Kurisu.Core.DataAccess.Entity; using Kurisu.SqlSugar.Extensions; +using Kurisu.SqlSugar.Services; +using Microsoft.AspNetCore.Mvc.Filters; +using SqlSugar; namespace Dlhis.Canteen; @@ -12,3 +16,101 @@ public class SqlSugarPack : BaseAppPack services.AddSqlSugar(); } } + + + + + +/// +/// 日志 +/// +[AttributeUsage(AttributeTargets.Method)] +public class LogAttribute : Attribute, IAsyncActionFilter +{ + public LogAttribute(string title) + { + Title = title; + } + + /// + /// + /// + public string Title { get; set; } + + /// + /// 是否处理差异 + /// + public bool Diff { get; set; } + + + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + PreSetting(context.HttpContext); + await next(); + } + + private void PreSetting(HttpContext httpContext) + { + var options = httpContext.RequestServices.GetService(); + + options.Title = Title; + options.Diff = Diff; + options.BatchNo = Guid.NewGuid(); + options.RoutePath = httpContext.Request.Path.Value; + options.RaiseTime = DateTime.Now; + } +} + +/// +/// 事务 +/// +[AttributeUsage(AttributeTargets.Method)] +public class TransactionalAttribute : Attribute, IAsyncActionFilter +{ + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + var dbContext = context.HttpContext.RequestServices.GetRequiredService(); + await dbContext.Ado.BeginTranAsync(); + try + { + var result = await next(); + if (result.Exception != null) + { + throw result.Exception; + } + else + { + await dbContext.Ado.CommitTranAsync(); + } + } + catch (Exception) + { + await dbContext.Ado.RollbackTranAsync(); + throw; + } + } +} + +/// +/// 忽略租户 +/// +[AttributeUsage(AttributeTargets.Method)] +public class IgnoreTenantAttribute : Attribute, IAsyncActionFilter +{ + public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + var dbContext = context.HttpContext.RequestServices.GetRequiredService(); + var options = context.HttpContext!.RequestServices.GetService(); + try + { + dbContext.QueryFilter.ClearAndBackup(); + options.IgnoreTenant = true; + await next(); + } + finally + { + options.IgnoreTenant = false; + dbContext.QueryFilter.Restore(); + } + } +} diff --git a/Src/Dlhis.Canteen/appsettings.Development.json b/Src/Dlhis.Canteen/appsettings.Development.json index 20ab3f6d..69b2958a 100644 --- a/Src/Dlhis.Canteen/appsettings.Development.json +++ b/Src/Dlhis.Canteen/appsettings.Development.json @@ -6,7 +6,8 @@ } }, "InternalRequest": { - "CommonRequestUrl": "http://localhost:5001" + "CommonRequestUrl": "http://localhost:5001", + "BasicRequestUrl": "http://localhost:5002" }, "SwaggerOptions": { "Enable": true, diff --git a/Src/Dlhis.Canteen/appsettings.json b/Src/Dlhis.Canteen/appsettings.json index 57e5193a..5610a28f 100644 --- a/Src/Dlhis.Canteen/appsettings.json +++ b/Src/Dlhis.Canteen/appsettings.json @@ -7,7 +7,11 @@ }, "AllowedHosts": "*", "InternalRequest": { - "CommonRequestUrl": "http://common" + "CommonRequestUrl": "http://common", + "BasicRequestUrl": "http://basic" + }, + "FilterOptions": { + "EnableApiRequestLog": true }, "SqlSugarOptions": { "DefaultConnectionString": "server=10.27.21.22;port=3306;userid=root;password=dlhis123;database=yikatong;charset=utf8mb4;", @@ -39,6 +43,9 @@ "ClientSecret": "123456" } }, + "RedisOptions": { + "ConnectionString": "192.168.199.30:30115,defaultDatabase:1,password=dlhis123" + }, "Serilog": { "MinimumLevel": { "Default": "Information", diff --git a/Src/Dlhis.Common.Api/Dlhis.Common.Api.csproj b/Src/Dlhis.Common.Api/Dlhis.Common.Api.csproj index 26abb5b7..314a11ed 100644 --- a/Src/Dlhis.Common.Api/Dlhis.Common.Api.csproj +++ b/Src/Dlhis.Common.Api/Dlhis.Common.Api.csproj @@ -8,7 +8,7 @@ - + diff --git a/Src/Dlhis.Common/ApiLog/ApiLogController.cs b/Src/Dlhis.Common/ApiLog/ApiLogController.cs new file mode 100644 index 00000000..2a912ac4 --- /dev/null +++ b/Src/Dlhis.Common/ApiLog/ApiLogController.cs @@ -0,0 +1,36 @@ +using Dlhis.Common.Request; +using Dlhis.Common.Response; +using Dlhis.Common.Services.Implements; +using Kurisu.Core.DataAccess.Dto; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Dlhis.Common.ApiLog +{ + /// + /// API日志控制器 + /// + [Authorize] + [Route("api/[controller]")] + [ApiController] + public class ApiLogController : ControllerBase + { + private readonly IApiLogService _apiLogService; + + public ApiLogController(IApiLogService apiLogService) + { + _apiLogService = apiLogService; + } + + /// + /// 获取api日志 + /// + /// + [HttpGet("logs")] + public async Task> GetLogsAsync([FromQuery] ApiLogRequest request) + { + return await _apiLogService.GetLogsAsync(request); + } + } +} diff --git a/Src/Dlhis.Common/Dlhis.Common.csproj b/Src/Dlhis.Common/Dlhis.Common.csproj index 69affc46..02b72955 100644 --- a/Src/Dlhis.Common/Dlhis.Common.csproj +++ b/Src/Dlhis.Common/Dlhis.Common.csproj @@ -21,7 +21,7 @@ - + diff --git a/Src/Dlhis.Common/EnumSetting/EnumsController.cs b/Src/Dlhis.Common/EnumSetting/EnumsController.cs index f930928c..fde3ebb5 100644 --- a/Src/Dlhis.Common/EnumSetting/EnumsController.cs +++ b/Src/Dlhis.Common/EnumSetting/EnumsController.cs @@ -57,7 +57,6 @@ public class EnumsController : ControllerBase }) .ToList(); - //todo cache 速度 var dbTypes = _dbContext.Queryable().OrderBy(x => x.Sequence).ToList(); var dbResult = dbTypes.GroupBy(x => x.Type).Select(g => { @@ -77,6 +76,29 @@ public class EnumsController : ControllerBase result.AddRange(dbResult); + result.Add(GetCurrentEnums("LogLevel", "日志级别", typeof(LogLevel))); + return result; } + + + private static CommonEnumResponse GetCurrentEnums(string code, string name, Type enumType) + { + return new CommonEnumResponse + { + Code = code, + Name = name, + Data = enumType.GetFields(BindingFlags.Public | BindingFlags.Static).Select(item => + { + var e = (Enum)Enum.Parse(item.FieldType, item.GetValue(null).ToString()); + var fd = e.GetDescription(); + return new KeyValueDto + { + Key = fd, + Value = (int)item.GetValue(null) + }; + }) + .ToList() + }; + } } diff --git a/Src/Dlhis.Common/FileOperation/Controllers/ApkFileController.cs b/Src/Dlhis.Common/FileOperation/Controllers/ApkFileController.cs index 7646dd3e..7ab679b7 100644 --- a/Src/Dlhis.Common/FileOperation/Controllers/ApkFileController.cs +++ b/Src/Dlhis.Common/FileOperation/Controllers/ApkFileController.cs @@ -29,6 +29,7 @@ public class ApkFileController : ControllerBase /// /// [HttpPost("minlo")] + [RequestSizeLimit(300_000_000)] public async Task FormSingleFileUpload([FromForm] ApkRequest request) { return await _apkFIileService.FormSingleFileUpload(request); @@ -45,41 +46,19 @@ public class ApkFileController : ControllerBase return await _apkFIileService.RequestDownloadFile(fileName); } - /// - /// 本地文件上传 + /// 获取最新版本 /// - /// + /// /// - [HttpPost("local")] - [RequestSizeLimit(100_000_0000)] - - public async Task FormLocalFileUpload([FromForm] ApkRequest request) + [HttpGet("lastversion")] + public async Task GetLastVersionAsync(string productCode) { - return await _apkFIileService.FormLocalFileUpload(request); + return await _apkFIileService.GetLastVersionAsync(productCode); } - /// - /// 查看最新版本 - /// - /// - /// - [HttpGet("lastfile")] - public async Task LatestFile(string fileName) - { - return await _apkFIileService.LatestFile(fileName); - } - /// - /// 本地文件下载 - /// - /// - /// - [HttpGet("local")] - public IActionResult FormLocalDownloadFile(string fileName) - { - return _apkFIileService.FormLocalDownloadFile(fileName); - } + } diff --git a/Src/Dlhis.Common/FileOperation/Response/ApkFileResponse.cs b/Src/Dlhis.Common/FileOperation/Response/ApkFileResponse.cs index 886ca16a..763f5cf9 100644 --- a/Src/Dlhis.Common/FileOperation/Response/ApkFileResponse.cs +++ b/Src/Dlhis.Common/FileOperation/Response/ApkFileResponse.cs @@ -17,11 +17,21 @@ namespace Dlhis.Common.FileOperation.Response /// /// 版本名称 /// - public int VersionName { get; set; } + public string VersionName { get; set; } /// /// 修改信息 /// public string UpdateMessage { get; set; } + + /// + /// 产品代码 + /// + public string ProductCode { get; set; } + + /// + /// url + /// + public string? Url { get; set; } } } diff --git a/Src/Dlhis.Common/FileOperation/Services/IApkFIileService.cs b/Src/Dlhis.Common/FileOperation/Services/IApkFIileService.cs index f9f29d73..5dfa8536 100644 --- a/Src/Dlhis.Common/FileOperation/Services/IApkFIileService.cs +++ b/Src/Dlhis.Common/FileOperation/Services/IApkFIileService.cs @@ -21,21 +21,10 @@ public interface IApkFIileService : IScopeDependency /// Task RequestDownloadFile(string fileName); - /// - /// 本地上传 - /// - /// - /// - Task FormLocalFileUpload(ApkRequest request); - Task LatestFile(string fileName); + Task GetLastVersionAsync(string productCode); + - /// - /// 本地下载 - /// - /// - /// - IActionResult FormLocalDownloadFile(string fileName); } diff --git a/Src/Dlhis.Common/FileOperation/Services/Implements/ApkFIileService.cs b/Src/Dlhis.Common/FileOperation/Services/Implements/ApkFIileService.cs index 28726ace..4f5743aa 100644 --- a/Src/Dlhis.Common/FileOperation/Services/Implements/ApkFIileService.cs +++ b/Src/Dlhis.Common/FileOperation/Services/Implements/ApkFIileService.cs @@ -15,11 +15,13 @@ public class ApkFIileService : IApkFIileService { private readonly IMinioService _minioService; private readonly IDbContext _dbContext; + private readonly IConfiguration Configuration; - public ApkFIileService(IMinioService minioService, IDbContext dbContext) + public ApkFIileService(IMinioService minioService, IDbContext dbContext, IConfiguration configuration) { _minioService = minioService; _dbContext = dbContext; + Configuration = configuration; } /// @@ -34,31 +36,58 @@ public class ApkFIileService : IApkFIileService string fileName = request.formFile.FileName; + var lenFive = request.formFile.FileName.Length >= 5; + + lenFive.ThrowIfFalse("文件名长度不能小于5"); + var houzhui = request.formFile.FileName.Substring(request.formFile.FileName.Length - 4, 4).ToLower(); + var isApk = request.formFile.FileName.Substring(request.formFile.FileName.Length - 4, 4) == ".apk"; + isApk.ThrowIfFalse("文件不是apk类型"); + //更新数据库 - var entity = await _dbContext.Queryable().Where(x => x.Name == fileName).FirstAsync(); + var entity = await _dbContext.Queryable().Where(x => x.ProductCode == request.ProductCode).FirstAsync(); if (entity == null) { var info = new ApkFileEntity { Name = fileName, - Url = fileUrl, + ProductCode = request.ProductCode, VersionCode = request.VersionCode, UpdateMessage = request.UpdateMessage, + VersionName = request.VersionName, }; await _dbContext.InsertAsync(info); } else { - entity.Url = fileUrl; entity.VersionCode = request.VersionCode; entity.UpdateMessage = request.UpdateMessage; + entity.VersionName = request.VersionName; + entity.Name = fileName; await _dbContext.UpdateAsync(entity); } - var newEntity = await _dbContext.Queryable().Where(x => x.Name == fileName).FirstAsync(); + var newEntity = await _dbContext.Queryable().Where(x => x.ProductCode == request.ProductCode).FirstAsync(); var res = newEntity.Adapt(); return res; } + public async Task GetLastVersionAsync(string productCode) + { + var info = await _dbContext.Queryable() + .Where(x => x.ProductCode == productCode) + .Select() + .FirstAsync(); + info.ThrowIfNull($"产品{productCode}下没有apk存在"); + var url =await RequestDownloadFile(info.Name); + + var host = Configuration["MinIO:Endpoint"]; + + var modified_url = url.Replace(host, "https://yikatong.isawesome.cn/m"); + + info.Url = modified_url; + + return info; + } + /// /// 文件下载 /// @@ -68,116 +97,8 @@ public class ApkFIileService : IApkFIileService return await _minioService.MinloDownloadLinkAsync(fileName); } - /// - /// 本地文件上传 - /// - /// - /// - public async Task FormLocalFileUpload(ApkRequest request) - { - //上传 - var completeFilePath = FormSingleFileUpload(request.formFile); - if (completeFilePath == "No file uploaded" || completeFilePath.IsEmpty()) - { - return null; - } - //更新数据库 - var fileName = request.formFile.FileName; - var entity = await _dbContext.Queryable().Where(x => x.Name == fileName).FirstAsync(); - if (entity == null) - { - var info = new ApkFileEntity - { - Name = fileName, - Url = completeFilePath, - VersionCode = request.VersionCode, - VersionName = request.VersionName, - UpdateMessage = request.UpdateMessage, - }; - await _dbContext.InsertAsync(info); - } - else - { - entity.Url = completeFilePath; - entity.VersionCode = request.VersionCode; - entity.UpdateMessage = request.UpdateMessage; - entity.VersionName = request.VersionName; - await _dbContext.UpdateAsync(entity); - } - var newEntity = await _dbContext.Queryable().Where(x => x.Name == fileName).FirstAsync(); - var res = newEntity.Adapt(); - return res; - - } - public string FormSingleFileUpload(IFormFile formFile) - { - try - { - var filePath = "C:\\apk\\"; - - // 创建存储文件夹 - if (!Directory.Exists(filePath)) - { - Directory.CreateDirectory(filePath); - } - - if (formFile != null && formFile.Length > 0) - { - // 保存的文件名称 - var saveName = Path.GetFileName(formFile.FileName); - - // 文件保存 - using (var fs = File.Create(Path.Combine(filePath, saveName))) - { - formFile.CopyTo(fs); - fs.Flush(); - } - - // 完整的文件路径 - var completeFilePath = Path.Combine(filePath, saveName); - return completeFilePath; - } - else - { - return "No file uploaded"; - } - } - catch (Exception) - { - return null; - throw; - } - } - - /// - /// 返回最新版本信息 - /// - /// - /// - public async Task LatestFile(string fileName) - { - var info = await _dbContext.Queryable() - .Where(x => x.Name == fileName) - .OrderByDescending(x => x.VersionCode) - .FirstAsync(); - info.ThrowIfNull($"没有{fileName}信息"); - var res = info.Adapt(); - return res; - } - /// - /// 本地下载 - /// - /// - /// - public IActionResult FormLocalDownloadFile(string fileName) - { - - var Path = "C:\\apk\\"; - var filePath = Path + fileName; - return new FileStreamResult(new FileStream(filePath, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName }; - } } diff --git a/Src/Dlhis.Common/Program.cs b/Src/Dlhis.Common/Program.cs index 1bcd1be2..6b0c15e8 100644 --- a/Src/Dlhis.Common/Program.cs +++ b/Src/Dlhis.Common/Program.cs @@ -1,4 +1,5 @@ using Kurisu.AspNetCore.Startup; +using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Startup; class Program @@ -13,6 +14,6 @@ public class Startup : DefaultStartup { public Startup(IConfiguration configuration) : base(configuration) { - + } } \ No newline at end of file diff --git a/Src/Dlhis.Common/Request/ApiLogRequest.cs b/Src/Dlhis.Common/Request/ApiLogRequest.cs new file mode 100644 index 00000000..ca4d00cc --- /dev/null +++ b/Src/Dlhis.Common/Request/ApiLogRequest.cs @@ -0,0 +1,14 @@ +using Kurisu.Core.DataAccess.Dto; +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Common.Request +{ + public class ApiLogRequest : PageDto + { + /// + /// 日志级别 LogLevel + /// + [EnumDataType(typeof(LogLevel))] + public LogLevel? Level { get; set; } + } +} diff --git a/Src/Dlhis.Common/Request/ApkRequest.cs b/Src/Dlhis.Common/Request/ApkRequest.cs index 3665dafb..86156c02 100644 --- a/Src/Dlhis.Common/Request/ApkRequest.cs +++ b/Src/Dlhis.Common/Request/ApkRequest.cs @@ -15,11 +15,16 @@ /// /// 版本名称 /// - public int VersionName { get; set; } + public string VersionName { get; set; } /// /// 修改信息 /// public string UpdateMessage { get; set; } + + /// + /// 产品代码 + /// + public string? ProductCode { get; set; } } } diff --git a/Src/Dlhis.Common/Response/ApiLogResponse.cs b/Src/Dlhis.Common/Response/ApiLogResponse.cs new file mode 100644 index 00000000..ff51f853 --- /dev/null +++ b/Src/Dlhis.Common/Response/ApiLogResponse.cs @@ -0,0 +1,57 @@ +using Newtonsoft.Json; + +namespace Dlhis.Common.Response +{ + public class ApiLogResponse + { + /// + /// 时间UTC + /// + [JsonIgnore] + public string Timestamp { get; set; } + + /// + /// 产生时间 + /// + public DateTime RaiseTime + { + get + { + return DateTime.Parse(Timestamp).ToLocalTime(); + } + } + + /// + /// 级别 + /// + public string Level { get; set; } + + /// + /// 模板 + /// + [JsonIgnore] + public string Template { get; set; } + + /// + /// 消息内容 + /// + public string Message { get; set; } + + /// + /// 异常内容 + /// + public string Exception { get; set; } + + /// + /// 属性 + /// + [JsonIgnore] + public string Properties { get; set; } + + /// + /// 时间 + /// + [JsonIgnore] + public DateTime _TS { get; set; } + } +} diff --git a/Src/Dlhis.Common/Services/ApiLogService.cs b/Src/Dlhis.Common/Services/ApiLogService.cs new file mode 100644 index 00000000..2a6b9550 --- /dev/null +++ b/Src/Dlhis.Common/Services/ApiLogService.cs @@ -0,0 +1,34 @@ +using Dlhis.Common.Request; +using Dlhis.Common.Response; +using Dlhis.Common.Services.Implements; +using Dlhis.Entity.Common; +using Kurisu.AspNetCore.Utils.Extensions; +using Kurisu.Core.DataAccess.Dto; +using Kurisu.SqlSugar.Extensions; +using Kurisu.SqlSugar.Services; +using SqlSugar; + +namespace Dlhis.Common.Services +{ + public class ApiLogService : IApiLogService + { + private readonly IDbContext _dbContext; + + public ApiLogService(IDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task> GetLogsAsync(ApiLogRequest request) + { + var logs = await _dbContext.ChangeDb("log").Queryable() + .WhereIF(request.Search.IsPresent(), x => x.Message.Contains(request.Search)) + .WhereIF(request.Level.HasValue, x => x.Level == request.Level) + .OrderBy(x => x.Timestamp,OrderByType.Desc) + .Select() + .ToPageListAsync(request); + + return logs; + } + } +} diff --git a/Src/Dlhis.Common/Services/Implements/ApkFIileService.cs b/Src/Dlhis.Common/Services/Implements/ApkFIileService.cs index bdd03d8f..03848263 100644 --- a/Src/Dlhis.Common/Services/Implements/ApkFIileService.cs +++ b/Src/Dlhis.Common/Services/Implements/ApkFIileService.cs @@ -39,7 +39,7 @@ public class ApkFIileService : IApkFIileService var info = new ApkFileEntity { Name = fileName, - Url = fileUrl, + //Url = fileUrl, VersionCode = request.VersionCode, UpdateMessage = request.UpdateMessage, }; @@ -47,7 +47,7 @@ public class ApkFIileService : IApkFIileService } else { - entity.Url = fileUrl; + //entity.Url = fileUrl; entity.VersionCode = request.VersionCode; entity.UpdateMessage = request.UpdateMessage; await _dbContext.UpdateAsync(entity); @@ -87,7 +87,7 @@ public class ApkFIileService : IApkFIileService var info = new ApkFileEntity { Name = fileName, - Url = completeFilePath, + //Url = completeFilePath, VersionCode = request.VersionCode, VersionName = request.VersionName, UpdateMessage = request.UpdateMessage, @@ -96,7 +96,7 @@ public class ApkFIileService : IApkFIileService } else { - entity.Url = completeFilePath; + //entity.Url = completeFilePath; entity.VersionCode = request.VersionCode; entity.VersionName = request.VersionName; entity.UpdateMessage = request.UpdateMessage; diff --git a/Src/Dlhis.Common/Services/Implements/IApiLogService.cs b/Src/Dlhis.Common/Services/Implements/IApiLogService.cs new file mode 100644 index 00000000..4006c3b1 --- /dev/null +++ b/Src/Dlhis.Common/Services/Implements/IApiLogService.cs @@ -0,0 +1,11 @@ +using Dlhis.Common.Request; +using Dlhis.Common.Response; +using Kurisu.Core.DataAccess.Dto; + +namespace Dlhis.Common.Services.Implements +{ + public interface IApiLogService : IScopeDependency + { + Task> GetLogsAsync(ApiLogRequest request); + } +} diff --git a/Src/Dlhis.Common/SqlSugarPack.cs b/Src/Dlhis.Common/SqlSugarPack.cs new file mode 100644 index 00000000..185e346e --- /dev/null +++ b/Src/Dlhis.Common/SqlSugarPack.cs @@ -0,0 +1,48 @@ +using Kurisu.AspNetCore.Startup; +using Kurisu.SqlSugar.Extensions; +using SqlSugar; + +namespace Dlhis.Common; + +public class SqlSugarPack : BaseAppPack +{ + + public override bool IsBeforeUseRouting { get; } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddSqlSugar(sp => + { + var configuration = sp.GetService(); + return new List + { + new ConnectionConfig + { + ConfigId = "face", + ConnectionString = configuration.GetSection("SqlSugarOptions:FaceConnectionString").Value, + DbType = DbType.MySql, + InitKeyType = InitKeyType.Attribute, + IsAutoCloseConnection = true, + MoreSettings = new ConnMoreSettings + { + DisableNvarchar = true, + } + }, + new ConnectionConfig + { + ConfigId = "log", + ConnectionString = configuration.GetSection("SqlSugarOptions:LogConnectionString").Value, + DbType = DbType.MySql, + InitKeyType = InitKeyType.Attribute, + IsAutoCloseConnection = true, + MoreSettings = new ConnMoreSettings + { + DisableNvarchar = true, + } + + } + }; + }); + } + +} \ No newline at end of file diff --git a/Src/Dlhis.Common/appsettings.json b/Src/Dlhis.Common/appsettings.json index a08dbf50..32fc514e 100644 --- a/Src/Dlhis.Common/appsettings.json +++ b/Src/Dlhis.Common/appsettings.json @@ -8,6 +8,8 @@ "SqlSugarOptions": { "DefaultConnectionString": "server=10.27.21.22;port=3306;userid=root;password=dlhis123;database=yikatong;charset=utf8mb4;", "SlaveConnectionString": [], + "FaceConnectionString": "server=192.168.199.124;port=3306;userid=root;password=dlhis123;database=face;charset=utf8mb4;", + "LogConnectionString": "server=192.168.199.124;port=3306;userid=root;password=dlhis123;database=log;charset=utf8mb4;", "Timeout": 30, "SlowSqlTime": 1, "EnableSqlLog": false diff --git a/Src/Dlhis.Entity/Basic/Entity/BasicTypeEntity.cs b/Src/Dlhis.Entity/Basic/Entity/BasicTypeEntity.cs index e6173c9f..14e5a309 100644 --- a/Src/Dlhis.Entity/Basic/Entity/BasicTypeEntity.cs +++ b/Src/Dlhis.Entity/Basic/Entity/BasicTypeEntity.cs @@ -12,13 +12,13 @@ public class BasicTypeEntity : SugarBaseEntity /// /// 类型 /// - [SugarColumn(ColumnDescription = "类型")] + [SugarColumn(ColumnDescription = "类型", UniqueGroupNameList = new[] { "idx_BasicType_type_code" })] public string Type { get; set; } /// /// 编码 /// - [SugarColumn(ColumnDescription = "编码")] + [SugarColumn(ColumnDescription = "编码", UniqueGroupNameList = new[] { "idx_BasicType_type_code" })] public string Code { get; set; } /// diff --git a/Src/Dlhis.Entity/Basic/Entity/CanteenBusinessPlaceEntity.cs b/Src/Dlhis.Entity/Basic/Entity/BusinessPlaceEntity.cs similarity index 91% rename from Src/Dlhis.Entity/Basic/Entity/CanteenBusinessPlaceEntity.cs rename to Src/Dlhis.Entity/Basic/Entity/BusinessPlaceEntity.cs index 8af87c99..866f030a 100644 --- a/Src/Dlhis.Entity/Basic/Entity/CanteenBusinessPlaceEntity.cs +++ b/Src/Dlhis.Entity/Basic/Entity/BusinessPlaceEntity.cs @@ -9,8 +9,8 @@ namespace Dlhis.Entity.Basic.Entity; /// /// 营业点 /// -[SugarTable(TableName = "CanteenBusinessPlace", TableDescription = "营业点")] -public class CanteenBusinessPlaceEntity : SugarBaseEntity, ITenantId +[SugarTable(TableName = "BusinessPlace", TableDescription = "营业点")] +public class BusinessPlaceEntity : SugarBaseEntity, ITenantId { /// /// 代号 diff --git a/Src/Dlhis.Entity/Basic/Entity/BusinessPlaceGroupEntity.cs b/Src/Dlhis.Entity/Basic/Entity/BusinessPlaceGroupEntity.cs new file mode 100644 index 00000000..fdb19b6c --- /dev/null +++ b/Src/Dlhis.Entity/Basic/Entity/BusinessPlaceGroupEntity.cs @@ -0,0 +1,25 @@ +using Kurisu.Core.DataAccess.Entity; +using Kurisu.SqlSugar; +using SqlSugar; + +namespace Dlhis.Entity.Basic.Entity; + +/// +/// 营业点分组 +/// +[SugarTable(TableName = "BusinessPlaceGroup", TableDescription = "营业点分组")] +public class BusinessPlaceGroupEntity : SugarBaseEntity, ITenantId +{ + public Guid? PCode { get; set; } + + public Guid Code { get; set; } + + [SugarColumn(ColumnDescription = "名称")] + public string Name { get; set; } + + + [SugarColumn(ColumnDescription = "备注")] + public string Remark { get; set; } + + public string TenantId { get; set; } +} diff --git a/Src/Dlhis.Entity/Basic/Entity/BusinessPlaceGroupRelationshipEntity.cs b/Src/Dlhis.Entity/Basic/Entity/BusinessPlaceGroupRelationshipEntity.cs new file mode 100644 index 00000000..49a36419 --- /dev/null +++ b/Src/Dlhis.Entity/Basic/Entity/BusinessPlaceGroupRelationshipEntity.cs @@ -0,0 +1,16 @@ +using Kurisu.Core.DataAccess.Entity; +using Kurisu.SqlSugar; +using SqlSugar; + +namespace Dlhis.Entity.Basic.Entity; + + +[SugarTable(TableName = "BusinessPlaceGroupRelationship", TableDescription = "营业点分组")] +public class BusinessPlaceGroupRelationshipEntity : SugarBaseEntity, ITenantId +{ + public Guid BusinessPlaceGroupCode { get; set; } + + public Guid BusinessPlaceCode { get; set; } + + public string TenantId { get; set; } +} diff --git a/Src/Dlhis.Entity/Basic/Entity/BusinessPlaceShiftRelationshipEntity.cs b/Src/Dlhis.Entity/Basic/Entity/BusinessPlaceShiftRelationshipEntity.cs new file mode 100644 index 00000000..b4419ca8 --- /dev/null +++ b/Src/Dlhis.Entity/Basic/Entity/BusinessPlaceShiftRelationshipEntity.cs @@ -0,0 +1,18 @@ +using Kurisu.Core.DataAccess.Entity; +using Kurisu.SqlSugar; +using SqlSugar; + +namespace Dlhis.Entity.Basic.Entity; + +[SugarTable(TableName = "BusinessPlaceShiftRelationship", TableDescription = "营业点餐次关系")] +public class BusinessPlaceShiftRelationshipEntity : SugarBaseEntity, ITenantId +{ + [SugarColumn(ColumnDescription = "营业点code")] + public Guid PlaceCode { get; set; } + + + [SugarColumn(ColumnDescription = "餐次code")] + public Guid ShiftCode { get; set; } + + public string TenantId { get; set; } +} diff --git a/Src/Dlhis.Entity/Basic/Entity/BusinessShiftEntity.cs b/Src/Dlhis.Entity/Basic/Entity/BusinessShiftEntity.cs new file mode 100644 index 00000000..9893a6c6 --- /dev/null +++ b/Src/Dlhis.Entity/Basic/Entity/BusinessShiftEntity.cs @@ -0,0 +1,32 @@ +using Kurisu.Core.DataAccess.Entity; +using Kurisu.SqlSugar; +using SqlSugar; + +namespace Dlhis.Entity.Basic.Entity; + +/// +/// 营业点餐次 +/// +[SugarTable(TableName = "BusinessShift", TableDescription = "营业点餐次")] +public class BusinessShiftEntity : SugarBaseEntity, ITenantId +{ + /// + /// 代号 + /// + [SugarColumn(ColumnDescription = "code")] + public Guid Code { get; set; } + + /// + /// 自定义代号 + /// + [SugarColumn(ColumnDescription = "自定义手工号")] + public string CustomCode { get; set; } + + /// + /// 餐次名称 + /// + [SugarColumn(ColumnDescription = "餐次名称")] + public string Name { get; set; } + + public string TenantId { get; set; } +} \ No newline at end of file diff --git a/Src/Dlhis.Entity/Basic/Entity/CanteenBusinessShiftEntity.cs b/Src/Dlhis.Entity/Basic/Entity/BusinessShiftMealRelationshipEntity.cs similarity index 50% rename from Src/Dlhis.Entity/Basic/Entity/CanteenBusinessShiftEntity.cs rename to Src/Dlhis.Entity/Basic/Entity/BusinessShiftMealRelationshipEntity.cs index be1316ee..6172c059 100644 --- a/Src/Dlhis.Entity/Basic/Entity/CanteenBusinessShiftEntity.cs +++ b/Src/Dlhis.Entity/Basic/Entity/BusinessShiftMealRelationshipEntity.cs @@ -4,29 +4,26 @@ using SqlSugar; namespace Dlhis.Entity.Basic.Entity; -/// -/// 营业点餐次 -/// -[SugarTable(TableName = "CanteenBusinessShift", TableDescription = "营业点餐次")] -public class CanteenBusinessShiftEntity : SugarBaseEntity, ITenantId +[SugarTable(TableName = "BusinessShiftMealRelationship", TableDescription = "营业点餐次组合")] +public class BusinessShiftMealRelationshipEntity : SugarBaseEntity, ITenantId { /// - /// 营业点代号 + /// 餐次组合Code /// - [SugarColumn(ColumnDescription = "营业点code")] - public Guid BusinessPlaceCode { get; set; } + [SugarColumn(ColumnDescription = "餐次组合Code", UniqueGroupNameList = new[] { "idx_CanteenBusinessShiftRelationship_shiftcode_shifttype" })] + public Guid ShiftCode { get; set; } /// - /// 代号 + /// 餐次组合手工号 /// - [SugarColumn(ColumnDescription = "code")] - public Guid Code { get; set; } + [SugarColumn(ColumnDescription = "餐次组合手工号")] + public string ShiftCustomCode { get; set; } /// - /// 自定义代号 + /// 餐次 /// - [SugarColumn(ColumnDescription = "自定义手工号")] - public string CustomCode { get; set; } + [SugarColumn(ColumnDescription = "餐次", UniqueGroupNameList = new[] { "idx_CanteenBusinessShiftRelationship_shiftcode_shifttype" })] + public string ShiftType { get; set; } /// /// 经营开始时间 @@ -46,5 +43,6 @@ public class CanteenBusinessShiftEntity : SugarBaseEntity, ITenantId [SugarColumn(ColumnDescription = "序号")] public int Sequence { get; set; } + public string TenantId { get; set; } -} \ No newline at end of file +} diff --git a/Src/Dlhis.Entity/Basic/Entity/DeviceEntity.cs b/Src/Dlhis.Entity/Basic/Entity/DeviceEntity.cs index 6c24630f..ddedd0ec 100644 --- a/Src/Dlhis.Entity/Basic/Entity/DeviceEntity.cs +++ b/Src/Dlhis.Entity/Basic/Entity/DeviceEntity.cs @@ -21,7 +21,7 @@ public class DeviceEntity : SugarBaseEntity, ITenantId /// /// 设备唯一码,IP/MAC/SNO /// - [SugarColumn(ColumnDescription = "设备唯一码", UniqueGroupNameList = new[] { "Idx_Device_UniqueNo" })] + [SugarColumn(ColumnDescription = "设备唯一码", IsNullable = false, UniqueGroupNameList = new[] { "Idx_Device_UniqueNo" })] public string UniqueNo { get; set; } /// @@ -84,5 +84,12 @@ public class DeviceEntity : SugarBaseEntity, ITenantId [SugarColumn(ColumnDataType = "bit", ColumnDescription = "是否启用")] public bool Enable { get; set; } + + /// + /// 是否作为考勤设备 + /// + [SugarColumn(ColumnDataType = "bit", ColumnDescription = "是否作为考勤设备")] + public bool AsAttendance { get; set; } + public string TenantId { get; set; } } \ No newline at end of file diff --git a/Src/Dlhis.Entity/Basic/Entity/DeviceTemplateEntity.cs b/Src/Dlhis.Entity/Basic/Entity/DeviceTemplateEntity.cs index 8ea2e16b..84cf035b 100644 --- a/Src/Dlhis.Entity/Basic/Entity/DeviceTemplateEntity.cs +++ b/Src/Dlhis.Entity/Basic/Entity/DeviceTemplateEntity.cs @@ -1,6 +1,4 @@ -using System; -using Kurisu.Core.DataAccess.Entity; -using Kurisu.SqlSugar; +using Kurisu.SqlSugar; using SqlSugar; namespace Dlhis.Entity.Basic.Entity; @@ -9,7 +7,7 @@ namespace Dlhis.Entity.Basic.Entity; /// 设备配置模板 /// [SugarTable(TableName = "DeviceTemplate", TableDescription = "设备配置模板")] -public class DeviceTemplateEntity : SugarBaseEntity, ITenantId +public class DeviceTemplateEntity : SugarBaseEntity { /// /// 模板code @@ -34,7 +32,4 @@ public class DeviceTemplateEntity : SugarBaseEntity, ITenantId /// [SugarColumn(ColumnDescription = "模板json")] public string Template { get; set; } - - - public string TenantId { get; set; } } \ No newline at end of file diff --git a/Src/Dlhis.Entity/Basic/Entity/GainfoCardRecordEntity.cs b/Src/Dlhis.Entity/Basic/Entity/GainfoCardRecordEntity.cs new file mode 100644 index 00000000..59c4b49d --- /dev/null +++ b/Src/Dlhis.Entity/Basic/Entity/GainfoCardRecordEntity.cs @@ -0,0 +1,33 @@ +using Kurisu.Core.DataAccess.Entity; +using Kurisu.SqlSugar; +using SqlSugar; + +namespace Dlhis.Entity.Basic.Entity; + +[SugarTable(TableName = "GainfoCardRecord", TableDescription = "卡记录")] +public class GainfoCardRecordEntity : SugarBaseEntity, ITenantId +{ + /// + /// 档案No + /// + [SugarColumn(IsNullable = false, IndexGroupNameList = new[] { "idx_GainfoCardRecord_userno" })] + public string UserNo { get; set; } + + /// + /// 卡号 + /// + [SugarColumn(IsNullable = false)] + public string CardNo { get; set; } + + /// + /// 更换原因 + /// + public string Reason { get; set; } + + /// + /// 备注 + /// + public string Remark { get; set; } + + public string TenantId { get; set; } +} diff --git a/Src/Dlhis.Entity/Basic/Entity/GainfoEntity.cs b/Src/Dlhis.Entity/Basic/Entity/GainfoEntity.cs index 1ddbe206..63631b17 100644 --- a/Src/Dlhis.Entity/Basic/Entity/GainfoEntity.cs +++ b/Src/Dlhis.Entity/Basic/Entity/GainfoEntity.cs @@ -14,7 +14,7 @@ public class GainfoEntity : SugarBaseEntity /// /// 档案号 /// - [SugarColumn(ColumnDescription = "档案号")] + [SugarColumn(ColumnDescription = "档案号", UniqueGroupNameList = new[] { "idx_Gainfo_No" })] public string No { get; set; } /// @@ -44,7 +44,7 @@ public class GainfoEntity : SugarBaseEntity /// /// 学工号 /// - [SugarColumn(ColumnDescription = "学工号")] + [SugarColumn(ColumnDescription = "学工号", UniqueGroupNameList = new[] { "idx_Gainfo_Code" })] public string Code { get; set; } /// @@ -128,7 +128,7 @@ public class GainfoEntity : SugarBaseEntity /// /// 卡号 /// - [SugarColumn(ColumnDescription = "卡号")] + [SugarColumn(ColumnDescription = "卡号", IndexGroupNameList = new[] { "idx_Gainfo_CardNo" })] public string CardNo { get; set; } /// @@ -143,31 +143,65 @@ public class GainfoEntity : SugarBaseEntity [SugarColumn(ColumnDescription = "人事范围")] public string Werks { get; set; } + /// + /// 工作地 + /// + [SugarColumn(ColumnDescription = "工作地")] + public string Workplace { get; set; } + + /// + /// 厂区 + /// + [SugarColumn(ColumnDescription = "厂区")] + public string Factory { get; set; } + /// /// 成本中心 /// public string CostId { get; set; } /// - /// 在职状态 + /// 身份认证用户Id(如果为空匹配code) /// - [SugarColumn(ColumnDescription = "在职状态")] - public string Employed { get; set; } - - /// - /// 身份认证用户Id - /// - [SugarColumn(ColumnDescription = "身份认证用户Id")] + [SugarColumn(ColumnDescription = "身份认证用户Id", IsNullable = true)] public int IdentityUserId { get; set; } } -//[SugarTable(TableName = "GainfoLeave", TableDescription = "离职人员档案表")] -//public class GainfoLeaveEntity : GainfoEntity -//{ +/// +/// 离职人员档案表 +/// +[SugarTable(TableName = "GainfoLeave", TableDescription = "离职人员档案表")] +public class GainfoLeaveEntity : GainfoEntity +{ +} -//} -//[SugarTable(TableName = "GainfoExternal", TableDescription = "外部人员档案表")] -//public class GainfoExternalEntity : GainfoEntity -//{ +/// +/// 外部人员档案表 +/// +[SugarTable(TableName = "GainfoExternal", TableDescription = "外部人员档案表")] +public class GainfoExternalEntity : GainfoEntity +{ + /// + /// 公司 + /// + [SugarColumn(ColumnDescription = "公司", IsNullable = false)] + public string Company { get; set; } -//} + /// + /// 到达时间 + /// + [SugarColumn(ColumnDescription = "到达时间")] + public DateTime From { get; set; } + + /// + /// 离开时间 + /// + [SugarColumn(ColumnDescription = "离开时间")] + public DateTime To { get; set; } + + /// + /// 对接人No + /// + [SugarColumn(ColumnDescription = "对接人", IsNullable = false)] + public string JinkoNo { get; set; } +} diff --git a/Src/Dlhis.Entity/Basic/Entity/GainfoFaceEntity.cs b/Src/Dlhis.Entity/Basic/Entity/GainfoFaceEntity.cs index 8cd7d09c..967ce16f 100644 --- a/Src/Dlhis.Entity/Basic/Entity/GainfoFaceEntity.cs +++ b/Src/Dlhis.Entity/Basic/Entity/GainfoFaceEntity.cs @@ -22,6 +22,6 @@ public class GainfoFaceEntity : SugarBaseEntity /// /// 人脸 /// - [SugarColumn(ColumnDataType = "text", IsNullable = false)] + [SugarColumn(ColumnDataType = "longtext", IsNullable = false)] public string Face { get; set; } } diff --git a/Src/Dlhis.Entity/Basic/Entity/GainfoGroupEntity.cs b/Src/Dlhis.Entity/Basic/Entity/GainfoGroupEntity.cs index 2243b9e5..809c8727 100644 --- a/Src/Dlhis.Entity/Basic/Entity/GainfoGroupEntity.cs +++ b/Src/Dlhis.Entity/Basic/Entity/GainfoGroupEntity.cs @@ -1,4 +1,5 @@ -using Kurisu.SqlSugar; +using Kurisu.Core.DataAccess.Entity; +using Kurisu.SqlSugar; using SqlSugar; namespace Dlhis.Entity.Basic.Entity; @@ -7,8 +8,13 @@ namespace Dlhis.Entity.Basic.Entity; /// 档案组 /// [SugarTable(TableName = "GainfoGroup", TableDescription = "档案组")] -public class GainfoGroupEntity : SugarBaseEntity +public class GainfoGroupEntity : SugarBaseEntity, ITenantId { + /// + /// 父级Code + /// + public Guid? PCode { get; set; } + /// /// 分组code /// @@ -26,4 +32,19 @@ public class GainfoGroupEntity : SugarBaseEntity /// [SugarColumn(ColumnDescription = "备注")] public string Remark { get; set; } + + /// + /// 是否默认分组 + /// + [SugarColumn(ColumnDescription = "是否默认分组", ColumnDataType = "bit", DefaultValue = "1")] + public bool IsDefault { get; set; } = true; + + /// + /// 是否可编辑 + /// + [SugarColumn(ColumnDescription = "是否可编辑", ColumnDataType = "bit", DefaultValue = "1")] + public bool Editable { get; set; } = true; + + + public string TenantId { get; set; } } diff --git a/Src/Dlhis.Entity/Canteen/Entities/BalanceRecordEntity.cs b/Src/Dlhis.Entity/Canteen/Entities/BalanceRecordEntity.cs index 3303970b..9b69d0ce 100644 --- a/Src/Dlhis.Entity/Canteen/Entities/BalanceRecordEntity.cs +++ b/Src/Dlhis.Entity/Canteen/Entities/BalanceRecordEntity.cs @@ -64,7 +64,7 @@ public class BalanceRecordEntity : SugarBaseEntity, ITenantId /// /// 营业点 /// - [SugarColumn(ColumnDescription = "营业点" , IsNullable = true)] + [SugarColumn(ColumnDescription = "营业点", IsNullable = true)] public Guid? BusinessPlaceCode { get; set; } /// @@ -73,6 +73,12 @@ public class BalanceRecordEntity : SugarBaseEntity, ITenantId [SugarColumn(ColumnDescription = "餐次", IsNullable = true)] public Guid? BusinessShiftCode { get; set; } + /// + /// 餐次段 + /// + [SugarColumn(ColumnDescription = "餐次段", IsNullable = true)] + public string ShiftType { get; set; } + /// /// 所属成本日期 /// diff --git a/Src/Dlhis.Entity/Canteen/Enums/CanteenBusinessPlaceCatalogEnumType.cs b/Src/Dlhis.Entity/Canteen/Enums/CanteenBusinessPlaceCatalogEnumType.cs index 9f0c4f97..80724371 100644 --- a/Src/Dlhis.Entity/Canteen/Enums/CanteenBusinessPlaceCatalogEnumType.cs +++ b/Src/Dlhis.Entity/Canteen/Enums/CanteenBusinessPlaceCatalogEnumType.cs @@ -20,10 +20,15 @@ public enum CanteenBusinessPlaceCatalogEnumType [Description("食堂")] Cafeteria = 2, - /// /// 超市 /// [Description("超市")] Supermarket = 3, + + /// + /// 水吧 + /// + [Description("水吧")] + Bar = 4, } \ No newline at end of file diff --git a/Src/Dlhis.Entity/Canteen/Enums/CanteenSettingItemEnumType.cs b/Src/Dlhis.Entity/Canteen/Enums/CanteenSettingItemEnumType.cs index 1b01b10f..a5ac6a98 100644 --- a/Src/Dlhis.Entity/Canteen/Enums/CanteenSettingItemEnumType.cs +++ b/Src/Dlhis.Entity/Canteen/Enums/CanteenSettingItemEnumType.cs @@ -71,6 +71,13 @@ public enum CanteenSettingItemEnumType AllowCrossTenant, + /// + /// 允许不使用余额消费 + /// + [Description("允许不使用余额消费(0/1)")] + AllowNoBalance, + + //--------------------------------------------------------------------[400,500)---------------------------------------------------------------------------------- /// diff --git a/Src/Dlhis.Entity/Canteen/Enums/ConsumptionTrendEnumType.cs b/Src/Dlhis.Entity/Canteen/Enums/ConsumptionTrendEnumType.cs new file mode 100644 index 00000000..e8573b48 --- /dev/null +++ b/Src/Dlhis.Entity/Canteen/Enums/ConsumptionTrendEnumType.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Dlhis.Entity.Canteen.Enums +{ + public enum ConsumptionTrendEnumType + { + /// + /// 日 + /// + [Description("日")] + Day = 1, + + /// + /// 月 + /// + [Description("月")] + Month = 2, + } +} diff --git a/Src/Dlhis.Entity/Common/ApiLogEntity.cs b/Src/Dlhis.Entity/Common/ApiLogEntity.cs new file mode 100644 index 00000000..70e0849e --- /dev/null +++ b/Src/Dlhis.Entity/Common/ApiLogEntity.cs @@ -0,0 +1,55 @@ +using SqlSugar; +using SqlSugar.DbConvert; +using Microsoft.Extensions.Logging; + +namespace Dlhis.Entity.Common; + +/// +/// api日志表 +/// +[SugarTable(TableName = "Logs", TableDescription = "api日志表")] +public class ApiLogEntity +{ + /// + /// 时间 + /// + [SugarColumn(ColumnDescription = "时间")] + public string Timestamp { get; set; } + + /// + /// 级别 + /// + [SugarColumn(ColumnDescription = "级别", SqlParameterDbType = typeof(EnumToStringConvert))] + public LogLevel Level { get; set; } + + /// + /// 模板 + /// + [SugarColumn(ColumnDescription = "模板")] + public string Template { get; set; } + + /// + /// 消息内容 + /// + [SugarColumn(ColumnDescription = "消息内容")] + public string Message { get; set; } + + /// + /// 异常内容 + /// + [SugarColumn(ColumnDescription = "异常内容")] + public string Exception { get; set; } + + /// + /// 属性 + /// + [SugarColumn(ColumnDescription = "属性")] + public string Properties { get; set; } + + /// + /// 时间 + /// + [SugarColumn(ColumnDescription = "时间")] + public DateTime _ts { get; set; } + +} diff --git a/Src/Dlhis.Entity/Common/ApkFileEntity.cs b/Src/Dlhis.Entity/Common/ApkFileEntity.cs index 7a6c7ce2..2c15bf57 100644 --- a/Src/Dlhis.Entity/Common/ApkFileEntity.cs +++ b/Src/Dlhis.Entity/Common/ApkFileEntity.cs @@ -20,12 +20,6 @@ public class ApkFileEntity : SugarBaseEntity [SugarColumn(ColumnDescription = "文件名称")] public string Name { get; set; } - /// - /// 地址 - /// - [SugarColumn(ColumnDescription = "地址")] - public string Url { get; set; } - /// /// 版本号 /// @@ -36,13 +30,19 @@ public class ApkFileEntity : SugarBaseEntity /// 版本名称 /// [SugarColumn(ColumnDescription = "版本名称")] - public int VersionName { get; set; } + public string VersionName { get; set; } /// /// 修改信息 /// [SugarColumn(ColumnDescription = "修改信息")] public string UpdateMessage { get; set; } - } + + /// + /// 产品代码 + /// + [SugarColumn(ColumnDescription = "产品代码")] + public string? ProductCode { get; set; } +} diff --git a/Src/Dlhis.Entity/Device/Entities/DeviceGroupEntity.cs b/Src/Dlhis.Entity/Device/Entities/DeviceGroupEntity.cs index 3c06d72e..0d22dc00 100644 --- a/Src/Dlhis.Entity/Device/Entities/DeviceGroupEntity.cs +++ b/Src/Dlhis.Entity/Device/Entities/DeviceGroupEntity.cs @@ -12,12 +12,11 @@ namespace Dlhis.Entity.Device.Entities; [SugarTable(TableName = "DeviceGroup", TableDescription = "设备组")] public class DeviceGroupEntity : SugarBaseEntity, ITenantId, ISoftDeleted { - /// /// 设备组功能类型 /// - [SugarColumn(IsNullable = true, ColumnDescription = "设备组功能类型", ColumnDataType = "varchar(50)", SqlParameterDbType = typeof(EnumToStringConvert))] - public DeviceTypeEnumType? Type { get; set; } + [SugarColumn(IsNullable = false, ColumnDescription = "设备组功能类型", ColumnDataType = "varchar(50)", SqlParameterDbType = typeof(EnumToStringConvert))] + public DeviceTypeEnumType Type { get; set; } /// /// 上级设备组code @@ -38,6 +37,25 @@ public class DeviceGroupEntity : SugarBaseEntity, ITenantId, ISoftDeleted public string Name { get; set; } + /// + /// 是否可编辑 + /// + [SugarColumn(ColumnDataType = "bit", DefaultValue = "1", ColumnDescription = "是否可编辑")] + public bool Editable { get; set; } = true; + + /// + /// 可设置下级 + /// + [SugarColumn(ColumnDataType = "bit", DefaultValue = "1")] + public bool AbleSetNext { get; set; } = true; + + /// + /// 备注 + /// + [SugarColumn(IsNullable = true)] + public string Remark { get; set; } + + public string TenantId { get; set; } diff --git a/Src/Dlhis.Entity/Device/Entities/DeviceGroupRelationshipEntity.cs b/Src/Dlhis.Entity/Device/Entities/DeviceGroupRelationshipEntity.cs index 352c55c4..d3d79fe0 100644 --- a/Src/Dlhis.Entity/Device/Entities/DeviceGroupRelationshipEntity.cs +++ b/Src/Dlhis.Entity/Device/Entities/DeviceGroupRelationshipEntity.cs @@ -8,7 +8,7 @@ namespace Dlhis.Entity.Device.Entities; /// 设备组/设备关系 /// [SugarTable(TableName = "DeviceGroupRelationship", TableDescription = "设备组/设备关系")] -public class DeviceGroupRelationshipEntity : SugarBaseEntity, ITenantId, ISoftDeleted +public class DeviceGroupRelationshipEntity : SugarBaseEntity, ITenantId { /// /// 设备组code @@ -22,8 +22,5 @@ public class DeviceGroupRelationshipEntity : SugarBaseEntity, ITenantId, ISoftDe [SugarColumn(ColumnDescription = "设备code")] public Guid DeviceCode { get; set; } - public string TenantId { get; set; } - - public bool IsDeleted { get; set; } } \ No newline at end of file diff --git a/Src/Dlhis.Entity/Device/Entities/DeviceRegistryEntity.cs b/Src/Dlhis.Entity/Device/Entities/DeviceRegistryEntity.cs index aa88393e..4d45f3ef 100644 --- a/Src/Dlhis.Entity/Device/Entities/DeviceRegistryEntity.cs +++ b/Src/Dlhis.Entity/Device/Entities/DeviceRegistryEntity.cs @@ -13,7 +13,7 @@ public class DeviceRegistryEntity : SugarBaseEntity, ITenantId /// /// 设备唯一号 /// - [SugarColumn(ColumnDescription = "设备唯一号")] + [SugarColumn(ColumnDescription = "设备唯一号", UniqueGroupNameList = new[] { "idx_DeviceRegistry_DeviceUniqueNo" })] public string DeviceUniqueNo { get; set; } /// diff --git a/Src/Dlhis.Entity/Device/Entities/DeviceTask.cs b/Src/Dlhis.Entity/Device/Entities/DeviceTask.cs index 4d0b5a4b..f1348681 100644 --- a/Src/Dlhis.Entity/Device/Entities/DeviceTask.cs +++ b/Src/Dlhis.Entity/Device/Entities/DeviceTask.cs @@ -23,14 +23,10 @@ public class DeviceTaskEntity : SugarBaseEntity, ITenantId, IVersion /// public string UserNo { get; set; } - /// - /// 用户学工号 - /// - public string UserCode { get; set; } - /// /// 设备唯一号 /// + [SugarColumn(IsNullable = false, IndexGroupNameList = new[] { "idx_DeviceTask_DeviceUniqueNo" })] public string DeviceUniqueNo { get; set; } /// diff --git a/Src/Dlhis.Entity/Device/Entities/DeviceUploadLogEntity.cs b/Src/Dlhis.Entity/Device/Entities/DeviceUploadLogEntity.cs index 6381a33e..39904219 100644 --- a/Src/Dlhis.Entity/Device/Entities/DeviceUploadLogEntity.cs +++ b/Src/Dlhis.Entity/Device/Entities/DeviceUploadLogEntity.cs @@ -1,26 +1,21 @@ using Dlhis.Entity.Device.Enums; using Kurisu.Core.DataAccess.Entity; +using Kurisu.SqlSugar; using SqlSugar; using SqlSugar.DbConvert; namespace Dlhis.Entity.Device.Entities; [SugarTable(TableName = "DeviceUploadLog", TableDescription = "设备上传记录")] -public class DeviceUploadLogEntity : ITenantId +public class DeviceUploadLogEntity : SugarBaseEntity, ITenantId { - /// - /// id - /// - [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] - public long Id { get; set; } - - [SugarColumn(IsNullable = true)] public string SerialNo { get; set; } /// /// 设备号 /// + [SugarColumn(IsNullable = false)] public string DeviceUniqueNo { get; set; } /// @@ -32,9 +27,9 @@ public class DeviceUploadLogEntity : ITenantId /// /// 产生时间 /// + [SugarColumn(IsNullable = false)] public DateTime RaiseDateTime { get; set; } - /// /// 日志类型 /// diff --git a/Src/Dlhis.Entity/Device/Enums/DeviceFuncEnumType.cs b/Src/Dlhis.Entity/Device/Enums/DeviceFuncEnumType.cs index d8985ae7..b60112f4 100644 --- a/Src/Dlhis.Entity/Device/Enums/DeviceFuncEnumType.cs +++ b/Src/Dlhis.Entity/Device/Enums/DeviceFuncEnumType.cs @@ -8,23 +8,11 @@ namespace Dlhis.Entity.Device.Enums; [Description("设备功能")] public enum DeviceFuncEnumType { - /// - /// 门禁 - /// - [Description("门禁")] - Door = 1, - - /// - /// 就餐 - /// - [Description("就餐")] - Meal, - /// /// 人脸 /// [Description("人脸")] - Face, + Face = 1, /// /// 刷卡 @@ -38,22 +26,9 @@ public enum DeviceFuncEnumType [Description("二维码")] QrCode, - /// /// 布控 /// [Description("布控")] Control, - - /// - /// 考勤 - /// - [Description("考勤")] - Attendance, - - /// - /// 计划 - /// - [Description("计划")] - Schedule } diff --git a/Src/Dlhis.Entity/Device/Enums/DeviceTaskEnumType.cs b/Src/Dlhis.Entity/Device/Enums/DeviceTaskEnumType.cs index d642dcd5..788c41ec 100644 --- a/Src/Dlhis.Entity/Device/Enums/DeviceTaskEnumType.cs +++ b/Src/Dlhis.Entity/Device/Enums/DeviceTaskEnumType.cs @@ -59,7 +59,7 @@ public enum DeviceTaskEnumType /// - /// 同步时间 + /// 上传记录 /// [Description("上传记录")] UploadRecord = 500, diff --git a/Src/Dlhis.Entity/Device/Enums/DeviceTypeEnumType.cs b/Src/Dlhis.Entity/Device/Enums/DeviceTypeEnumType.cs index 230e6e17..48c2ec70 100644 --- a/Src/Dlhis.Entity/Device/Enums/DeviceTypeEnumType.cs +++ b/Src/Dlhis.Entity/Device/Enums/DeviceTypeEnumType.cs @@ -25,10 +25,4 @@ public enum DeviceTypeEnumType /// [Description("考勤")] Attendance = 3, - - /// - /// 门禁并作为考勤 - /// - [Description("门禁并作为考勤")] - DoorAndAttendance = 4, } diff --git a/Src/Dlhis.Entity/Dlhis.Entity.csproj b/Src/Dlhis.Entity/Dlhis.Entity.csproj index beec1bdc..8000930a 100644 --- a/Src/Dlhis.Entity/Dlhis.Entity.csproj +++ b/Src/Dlhis.Entity/Dlhis.Entity.csproj @@ -11,7 +11,7 @@ - + diff --git a/Src/Dlhis.Entity/Sys/Entities/DevicePermissionEntity.cs b/Src/Dlhis.Entity/Sys/Entities/DevicePermissionEntity.cs index cb4e2e0f..39135b68 100644 --- a/Src/Dlhis.Entity/Sys/Entities/DevicePermissionEntity.cs +++ b/Src/Dlhis.Entity/Sys/Entities/DevicePermissionEntity.cs @@ -10,10 +10,10 @@ namespace Dlhis.Entity.Sys.Entities; /// 设备权限 /// [SugarTable(TableName = "DevicePermission", TableDescription = "设备权限")] -public class DevicePermissionEntity : SugarBaseEntity, ITenantId, ISoftDeleted +public class DevicePermissionEntity : SugarBaseEntity, ITenantId { /// - /// 分配权限类型 + /// 分配权限类型(User,Role,Group...) /// [SugarColumn(ColumnDataType = "varchar(50)", ColumnDescription = "分配权限类型", SqlParameterDbType = typeof(EnumToStringConvert))] public AssignTypeEnumType AssignType { get; set; } @@ -24,9 +24,8 @@ public class DevicePermissionEntity : SugarBaseEntity, ITenantId, ISoftDeleted [SugarColumn(ColumnDescription = "分配值")] public string AssignValue { get; set; } - /// - /// 设备权限类型 + /// 设备权限类型(Device/DeviceGroup) /// [SugarColumn(ColumnDataType = "varchar(50)", ColumnDescription = "设备权限类型", SqlParameterDbType = typeof(EnumToStringConvert))] public DeviceAssignmentTypeEnumType UniqueType { get; set; } @@ -37,7 +36,6 @@ public class DevicePermissionEntity : SugarBaseEntity, ITenantId, ISoftDeleted [SugarColumn(ColumnDescription = "类型值")] public Guid UniqueValue { get; set; } - public string TenantId { get; set; } - public bool IsDeleted { get; set; } + public string TenantId { get; set; } } \ No newline at end of file diff --git a/Src/Dlhis.Entity/Sys/Entities/RoleEntity.cs b/Src/Dlhis.Entity/Sys/Entities/RoleEntity.cs index 610770db..f9a7afa6 100644 --- a/Src/Dlhis.Entity/Sys/Entities/RoleEntity.cs +++ b/Src/Dlhis.Entity/Sys/Entities/RoleEntity.cs @@ -17,5 +17,5 @@ public class RoleEntity public string NormalizedName { get; set; } - public string ConcurrencyStamp { get; set; } + public Guid ConcurrencyStamp { get; set; } } diff --git a/Src/Dlhis.Entity/Sys/Entities/TenantEntity.cs b/Src/Dlhis.Entity/Sys/Entities/TenantEntity.cs index 5897f319..c67fa035 100644 --- a/Src/Dlhis.Entity/Sys/Entities/TenantEntity.cs +++ b/Src/Dlhis.Entity/Sys/Entities/TenantEntity.cs @@ -12,6 +12,11 @@ public class TenantEntity { public int Id { get; set; } + /// + /// 上级租户 + /// + public string PCode { get; set; } + /// /// 基地code /// diff --git a/Src/Dlhis.Entity/Sys/Entities/UserRoleEntity.cs b/Src/Dlhis.Entity/Sys/Entities/UserRoleEntity.cs new file mode 100644 index 00000000..8fe6a3cf --- /dev/null +++ b/Src/Dlhis.Entity/Sys/Entities/UserRoleEntity.cs @@ -0,0 +1,13 @@ +using Microsoft.Extensions.DependencyInjection; +using SqlSugar; + +namespace Dlhis.Entity.Sys.Entities; + +[SkipScan] +[SugarTable(TableName = "UserRoles")] +public class UserRoleEntity +{ + public int UserId { get; set; } + + public int RoleId { get; set; } +} diff --git a/Src/Dlhis.Entity/Sys/Enums/AssignTypeEnumType.cs b/Src/Dlhis.Entity/Sys/Enums/AssignTypeEnumType.cs index f41385d5..c4dcdf1b 100644 --- a/Src/Dlhis.Entity/Sys/Enums/AssignTypeEnumType.cs +++ b/Src/Dlhis.Entity/Sys/Enums/AssignTypeEnumType.cs @@ -21,7 +21,7 @@ public enum AssignTypeEnumType Role = 2, /// - /// 组别 + /// 用户组别 /// [Description("组别")] Group = 3, diff --git a/Src/Dlhis.ScheduledTask/CommonJobs/CreateDefinitelyGroupJob.cs b/Src/Dlhis.ScheduledTask/CommonJobs/CreateDefinitelyGroupJob.cs new file mode 100644 index 00000000..9daadce1 --- /dev/null +++ b/Src/Dlhis.ScheduledTask/CommonJobs/CreateDefinitelyGroupJob.cs @@ -0,0 +1,172 @@ +using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Device.Entities; +using Dlhis.Entity.Device.Enums; +using Dlhis.Sys.Api; +using Dlhis.Sys.Api.Response; +using Kurisu.AspNetCore.Utils.Extensions; +using Kurisu.Core.DataAccess.Entity; +using Kurisu.SqlSugar.Services; +using Newtonsoft.Json; +using SqlSugar; + +namespace Dlhis.ScheduledTask.CommonJobs; + +public class CreateDefinitelyGroupJob +{ + private readonly ILogger _logger; + private readonly ITenantApi _tenantApi; + private readonly IDbContext _dbContext; + + public CreateDefinitelyGroupJob(ILogger logger, + ITenantApi tenantApi, + IDbContext dbContext, + ISqlSugarOptionsService sqlSugarOptionsService) + { + _logger = logger; + _tenantApi = tenantApi; + _dbContext = dbContext; + + sqlSugarOptionsService.IgnoreTenant = true; + } + + private static readonly string _remark = "系统自动生成"; + + public async Task RunAsync() + { + _dbContext.Client.QueryFilter.ClearAndBackup(); + try + { + //获取所有基地信息 + var tenantResult = await _tenantApi.GetTenantsInternalAsync(); + var tenants = tenantResult.Data; + + _logger.LogInformation("获取基地信息:{json}", JsonConvert.SerializeObject(tenants)); + + //****************************************************系统人员分组**********************************************************/ + + await CreateGainfoGroupsAsync(tenants); + + //*****************************************************考勤设备组*********************************************************/ + + await CreateDeviceGroupAsync(DeviceTypeEnumType.Attendance, new List { "*通用考勤设备组", "*专用考勤设备组" }, tenants); + + //*****************************************************门禁设备组********************************************************/ + + await CreateDeviceGroupAsync(DeviceTypeEnumType.Door, new List { "*共用区域", "*非共用区域" }, tenants); + } + catch (Exception ex) + { + _logger.LogError(ex, "自动创建系统分组失败:{message}", ex.Message); + } + finally + { + _dbContext.Client.QueryFilter.Restore(); + } + } + + private async Task CreateGainfoGroupsAsync(List tenants) + { + var grps = new List { "*自定义人员分组", "*督查组", "*X3及以上领导组" }; + var searchGrps = await _dbContext.Queryable().Where(x => grps.Contains(x.Name)).ToListAsync(); + foreach (var grp in grps) + { + if (!searchGrps.Any(x => x.Name == grp)) + { + await _dbContext.InsertAsync(new GainfoGroupEntity + { + PCode = null, + Code = Guid.NewGuid(), + Name = grp, + IsDefault = grp.Equals("*自定义人员分组"), + Editable = false, + Remark = _remark, + + CreatedBy = 1, + CreateTime = DateTime.Now, + ModifiedBy = 1, + ModifiedTime = DateTime.Now, + TenantId = null + }); + } + } + } + + + private async Task CreateDeviceGroupAsync(DeviceTypeEnumType type, List topGroups, List tenants) + { + foreach (var grp in topGroups) + { + var topGrp = await _dbContext.Queryable().FirstAsync(x => x.Name == grp); + if (topGrp == null) + { + topGrp = new DeviceGroupEntity + { + PCode = null, + Code = Guid.NewGuid(), + Type = type, + Name = grp, + Editable = false, + AbleSetNext = false, + Remark = _remark, + + CreatedBy = 1, + CreateTime = DateTime.Now, + ModifiedBy = 1, + ModifiedTime = DateTime.Now, + TenantId = null, + }; + await _dbContext.InsertAsync(topGrp); + } + + foreach (var workplace in tenants.Where(x => x.PCode.IsEmpty())) + { + var workplaceName = $"*{type.GetDescription()}工作地系统组({workplace.Name})"; + var workplaceGrp = await _dbContext.Queryable().Where(x => x.PCode == topGrp.Code).FirstAsync(x => x.Name == workplaceName); + if (workplaceGrp == null) + { + workplaceGrp = new DeviceGroupEntity + { + PCode = topGrp.Code, + Code = Guid.NewGuid(), + Type = type, + Name = workplaceName, + Editable = false, + AbleSetNext = false, + Remark = _remark, + + CreatedBy = 1, + CreateTime = DateTime.Now, + ModifiedBy = 1, + ModifiedTime = DateTime.Now, + TenantId = null, + }; + await _dbContext.InsertAsync(workplaceGrp); + } + + foreach (var factory in tenants.Where(x => x.PCode == workplace.Code)) + { + var factoryName = $"*{type.GetDescription()}厂区系统组({factory.Name})"; + if (!await _dbContext.Queryable().Where(x => x.PCode == workplaceGrp.Code).AnyAsync(x => x.Name == factoryName)) + { + await _dbContext.InsertAsync(new DeviceGroupEntity + { + PCode = workplaceGrp.Code, + Code = Guid.NewGuid(), + Type = type, + Name = factoryName, + Editable = false, + AbleSetNext = true, + Remark = _remark, + + CreatedBy = 1, + CreateTime = DateTime.Now, + ModifiedBy = 1, + ModifiedTime = DateTime.Now, + TenantId = factory.Code, + }); + } + } + } + } + } +} diff --git a/Src/Dlhis.ScheduledTask/CommonJobs/HeartbeatJob.cs b/Src/Dlhis.ScheduledTask/CommonJobs/HeartbeatJob.cs new file mode 100644 index 00000000..bd7cdb19 --- /dev/null +++ b/Src/Dlhis.ScheduledTask/CommonJobs/HeartbeatJob.cs @@ -0,0 +1,54 @@ +using System.Net; +using System.Net.Sockets; +using Dlhis.Entity.Basic.Entity; +using Dlhis.Entity.Device.Entities; +using Kurisu.Core.DataAccess.Entity; +using Kurisu.SqlSugar.Services; +using Newtonsoft.Json; + +namespace Dlhis.ScheduledTask.CommonJobs; + +public class HeartbeatJob +{ + private readonly IDbContext _dbContext; + private readonly ILogger _logger; + + public HeartbeatJob(IDbContext dbContext, ILogger logger) + { + _dbContext = dbContext; + _logger = logger; + } + + public async Task RunAsync() + { + _dbContext.Client.QueryFilter.ClearAndBackup(); + try + { + var devices = await _dbContext.Queryable().Where(x => x.Enable).Where(x => x.HeartbeatInterval > 0).ToListAsync(); + foreach (var device in devices) + { + try + { + var ipInfo = JsonConvert.DeserializeObject(device.ConnectionInfo); + + //超时时间 + var CancellationTokenSource = new CancellationTokenSource(300); + await new TcpClient().ConnectAsync(IPAddress.Parse(ipInfo.IP), ipInfo.Port, CancellationTokenSource.Token); + + await _dbContext.Updateable().SetColumns(x => x.LastHeartbeatTime == DateTime.Now) + .Where(x => x.DeviceUniqueNo == device.UniqueNo).ExecuteCommandAsync(); + + // _logger.LogInformation("心跳设备成功:{no}", device.UniqueNo); + } + catch (Exception) + { + //_logger.LogError(ex, "心跳设备失败:{no}. {message}", device.UniqueNo, ex.Message); + } + } + } + finally + { + _dbContext.Client.QueryFilter.Restore(); + } + } +} diff --git a/Src/Dlhis.ScheduledTask/DeviceUploadLog/Controllers/DeviceUploadLogsController.cs b/Src/Dlhis.ScheduledTask/DeviceUploadLog/Controllers/DeviceUploadLogsController.cs index b9e57664..9e2e6567 100644 --- a/Src/Dlhis.ScheduledTask/DeviceUploadLog/Controllers/DeviceUploadLogsController.cs +++ b/Src/Dlhis.ScheduledTask/DeviceUploadLog/Controllers/DeviceUploadLogsController.cs @@ -1,6 +1,7 @@ using Dlhis.ScheduledTask.DeviceUploadLog.Request; using Dlhis.ScheduledTask.DeviceUploadLog.Response; using Dlhis.ScheduledTask.DeviceUploadLog.Services; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; using Kurisu.Core.DataAccess.Dto; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -33,25 +34,27 @@ public class DeviceUploadLogsController : ControllerBase return await _deviceUploadLogService.GetPageAsync(request); } - /// - /// 上传记录 - /// - /// - /// - [HttpPost("record")] - public async Task UploadLogAsync(DeviceUploadLogUploadRequest request) - { - await _deviceUploadLogService.UploadLogAsync(request); - } - /// /// 批量上传记录 /// /// /// [HttpPost("records")] + [IgnoreTenant] public async Task UploadLogsAsync(List requests) { await _deviceUploadLogService.UploadLogsAsync(requests); } + + /// + /// 设置设备获取记录开始时间 + /// + /// + /// + [HttpPost("reset-begintime")] + [Transactional] + public async Task SetUploadLogsBeginDateTime(List requests) + { + await _deviceUploadLogService.SetUploadLogsBeginDateTimeAsync(requests); + } } diff --git a/Src/Dlhis.ScheduledTask/DeviceUploadLog/Request/DeviceUploadLogPageRequest.cs b/Src/Dlhis.ScheduledTask/DeviceUploadLog/Request/DeviceUploadLogPageRequest.cs index 37aef592..670a5411 100644 --- a/Src/Dlhis.ScheduledTask/DeviceUploadLog/Request/DeviceUploadLogPageRequest.cs +++ b/Src/Dlhis.ScheduledTask/DeviceUploadLog/Request/DeviceUploadLogPageRequest.cs @@ -20,7 +20,7 @@ public class DeviceUploadLogPageRequest : PageDto, IValidatableObject public IEnumerable Validate(ValidationContext validationContext) { - var now = DateTime.Now; + var today = DateTime.Today; if (SearchDateTimeDimension.HasValue) { //重新赋值 @@ -28,37 +28,37 @@ public class DeviceUploadLogPageRequest : PageDto, IValidatableObject if (SearchDateTimeDimension == DateTimeSearchEnumType.Day) { - SearchDateTimePeriod.Start = new DateTime(now.Year, now.Month, now.Day, 0, 0, 1); - SearchDateTimePeriod.End = new DateTime(now.Year, now.Month, now.Day, 23, 59, 59); + SearchDateTimePeriod.Start = today; + SearchDateTimePeriod.End = today.AddDays(1).AddSeconds(-1); } else if (SearchDateTimeDimension == DateTimeSearchEnumType.Week) { - SearchDateTimePeriod.Start = now.AddDays(1 - (int)now.DayOfWeek); + SearchDateTimePeriod.Start = today.AddDays(1 - (int)today.DayOfWeek); SearchDateTimePeriod.End = SearchDateTimePeriod.Start.Value.AddDays(6); } else if (SearchDateTimeDimension == DateTimeSearchEnumType.Month) { - SearchDateTimePeriod.Start = new DateTime(now.Year, now.Month, 1); + SearchDateTimePeriod.Start = new DateTime(today.Year, today.Month, 1); SearchDateTimePeriod.End = SearchDateTimePeriod.Start.Value.AddMonths(1).AddDays(-1); } else if (SearchDateTimeDimension == DateTimeSearchEnumType.Season) { - SearchDateTimePeriod.Start = now.AddMonths(0 - ((now.Month - 1) % 3)); - SearchDateTimePeriod.End = now.AddMonths(3 - ((now.Month - 1) % 3)).AddMonths(1).AddDays(-1); + SearchDateTimePeriod.Start = new DateTime(today.Year, today.Month, 1).AddMonths(2 - ((today.Month - 1) % 3)); + SearchDateTimePeriod.End = SearchDateTimePeriod.Start.Value.AddMonths(2); } else if (SearchDateTimeDimension == DateTimeSearchEnumType.HalfOfYear) { - SearchDateTimePeriod.Start = now.AddDays(-180); - SearchDateTimePeriod.End = now; + SearchDateTimePeriod.Start = today.AddDays(-180); + SearchDateTimePeriod.End = today; } else if (SearchDateTimeDimension == DateTimeSearchEnumType.Year) { - SearchDateTimePeriod.Start = new DateTime(now.Year, 1, 1); + SearchDateTimePeriod.Start = new DateTime(today.Year, 1, 1); SearchDateTimePeriod.End = SearchDateTimePeriod.Start.Value.AddYears(1).AddDays(-1); } - SearchDateTimePeriod.Start = SearchDateTimePeriod.Start.Value.Date; - SearchDateTimePeriod.End = SearchDateTimePeriod.End.Value.Date; + SearchDateTimePeriod.Start = SearchDateTimePeriod.Start.Value; + SearchDateTimePeriod.End = SearchDateTimePeriod.End.Value; } yield break; diff --git a/Src/Dlhis.ScheduledTask/DeviceUploadLog/Request/SetUploadLogsBeginDateTimeRequest.cs b/Src/Dlhis.ScheduledTask/DeviceUploadLog/Request/SetUploadLogsBeginDateTimeRequest.cs new file mode 100644 index 00000000..e5a65b58 --- /dev/null +++ b/Src/Dlhis.ScheduledTask/DeviceUploadLog/Request/SetUploadLogsBeginDateTimeRequest.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.ScheduledTask.DeviceUploadLog.Request; + +public class SetUploadLogsBeginDateTimeRequest +{ + /// + /// 设备号不能为空 + /// + [Required(ErrorMessage = "设备号不能为空")] + public string DeviceUniqueNo { get; set; } + + /// + /// 开始时间 + /// + public DateTime Begin { get; set; } +} diff --git a/Src/Dlhis.ScheduledTask/DeviceUploadLog/Response/DeviceUploadLogResponse.cs b/Src/Dlhis.ScheduledTask/DeviceUploadLog/Response/DeviceUploadLogResponse.cs index c8de21fd..47c6039d 100644 --- a/Src/Dlhis.ScheduledTask/DeviceUploadLog/Response/DeviceUploadLogResponse.cs +++ b/Src/Dlhis.ScheduledTask/DeviceUploadLog/Response/DeviceUploadLogResponse.cs @@ -1,4 +1,6 @@ -namespace Dlhis.ScheduledTask.DeviceUploadLog.Response; +using Newtonsoft.Json; + +namespace Dlhis.ScheduledTask.DeviceUploadLog.Response; public class DeviceUploadLogResponse { @@ -29,4 +31,14 @@ public class DeviceUploadLogResponse /// 上传时间 /// public DateTime RaiseDateTime { get; set; } + + + /// + /// 序列号 + /// + public string SerialNo { get; set; } + + + [JsonIgnore] + public string OriginUserUniqueNo { get; set; } } diff --git a/Src/Dlhis.ScheduledTask/DeviceUploadLog/Services/IDeviceUploadLogService.cs b/Src/Dlhis.ScheduledTask/DeviceUploadLog/Services/IDeviceUploadLogService.cs index 8a6285e2..0d41b450 100644 --- a/Src/Dlhis.ScheduledTask/DeviceUploadLog/Services/IDeviceUploadLogService.cs +++ b/Src/Dlhis.ScheduledTask/DeviceUploadLog/Services/IDeviceUploadLogService.cs @@ -8,7 +8,7 @@ public interface IDeviceUploadLogService : IScopeDependency { Task> GetPageAsync(DeviceUploadLogPageRequest request); - Task UploadLogAsync(DeviceUploadLogUploadRequest request); - Task UploadLogsAsync(List requests); + + Task SetUploadLogsBeginDateTimeAsync(List requests); } diff --git a/Src/Dlhis.ScheduledTask/DeviceUploadLog/Services/Implements/DeviceUploadLogService.cs b/Src/Dlhis.ScheduledTask/DeviceUploadLog/Services/Implements/DeviceUploadLogService.cs index e479e4df..775d130d 100644 --- a/Src/Dlhis.ScheduledTask/DeviceUploadLog/Services/Implements/DeviceUploadLogService.cs +++ b/Src/Dlhis.ScheduledTask/DeviceUploadLog/Services/Implements/DeviceUploadLogService.cs @@ -1,11 +1,11 @@ using System.Linq.Expressions; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Device.Entities; +using Dlhis.Entity.Device.Enums; using Dlhis.ScheduledTask.DeviceUploadLog.Request; using Dlhis.ScheduledTask.DeviceUploadLog.Response; using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Core.DataAccess.Dto; -using Kurisu.SqlSugar.Attributes; using Kurisu.SqlSugar.Extensions; using Kurisu.SqlSugar.Services; using SqlSugar; @@ -24,41 +24,38 @@ public class DeviceUploadLogService : IDeviceUploadLogService public async Task> GetPageAsync(DeviceUploadLogPageRequest request) { var page = await _dbContext.Queryable() - .LeftJoin((deviceUp, ga) => deviceUp.UserUniqueNo == ga.No) + .LeftJoin((deviceUp, ga) => deviceUp.UserUniqueNo == ga.Code) .WhereIF(request.SearchDateTimePeriod?.Start.HasValue == true, deviceUp => deviceUp.RaiseDateTime >= request.SearchDateTimePeriod.Start) .WhereIF(request.SearchDateTimePeriod?.End.HasValue == true, deviceUp => deviceUp.RaiseDateTime <= request.SearchDateTimePeriod.End) + .WhereIF(request.Search.IsPresent(), (deviceUp, ga) => ga.Code.Contains(request.Search) || ga.Name.Contains(request.Search) || deviceUp.DeviceUniqueNo.Contains(request.Search)) + .OrderBy(deviceUp => deviceUp.CreateTime, OrderByType.Desc) .Select((deviceUp, ga) => new DeviceUploadLogResponse { + OriginUserUniqueNo = deviceUp.UserUniqueNo, DeviceUniqueNo = deviceUp.DeviceUniqueNo, Id = deviceUp.Id, RaiseDateTime = deviceUp.RaiseDateTime, + SerialNo = deviceUp.SerialNo, UserNo = ga.No, UserCode = ga.Code, UserName = ga.Name, }) .ToPageListAsync(request); + //var gainfos = await _dbContext.Queryable().ToListAsync(); + //page.Data.ForEach(item => + //{ + // _ = int.TryParse(item.OriginUserUniqueNo, out var userId); + + // var gainfo = gainfos.FirstOrDefault(x => x.No == item.OriginUserUniqueNo || x.Id == userId || x.Code == item.OriginUserUniqueNo); + // item.UserNo = gainfo?.No; + // item.UserCode = gainfo?.Code; + // item.UserName = gainfo?.Name; + //}); + return page; } - [IgnoreTenant] - public async Task UploadLogAsync(DeviceUploadLogUploadRequest request) - { - var device = await _dbContext.Queryable().Where(x => x.UniqueNo == request.DeviceUniqueNo).SingleAsync(); - device.ThrowIfNull("设备信息不存在"); - - var entity = new DeviceUploadLogEntity - { - DeviceUniqueNo = request.DeviceUniqueNo, - // UploadTime = request.UploadTime, - UserUniqueNo = request.UserUniqueNo, - TenantId = device.TenantId, - }; - - await _dbContext.InsertAsync(entity); - } - - [IgnoreTenant] public async Task UploadLogsAsync(List requests) { //判断存在的记录 @@ -101,4 +98,30 @@ public class DeviceUploadLogService : IDeviceUploadLogService await _dbContext.InsertAsync(records); } + + public async Task SetUploadLogsBeginDateTimeAsync(List requests) + { + var uniqueNos = requests.DistinctBy(x => x.DeviceUniqueNo).Select(x => x.DeviceUniqueNo).ToList(); + var devices = await _dbContext.Queryable() + .Where(x => uniqueNos.Contains(x.UniqueNo)) + .Where(x => x.Type == DeviceTypeEnumType.Attendance || x.Type == DeviceTypeEnumType.Door) + .ToListAsync(); + + (uniqueNos.Count != devices.Count).ThrowIfTrue("存在非考勤和门禁的设备"); + + var tenantId = devices.FirstOrDefault().TenantId; + var logs = new List(requests.Count); + foreach (var request in requests) + { + logs.Add(new DeviceUploadLogEntity + { + DeviceUniqueNo = request.DeviceUniqueNo, + Type = DeviceUploadLogTypeEnumType.Raise, + RaiseDateTime = request.Begin, + TenantId = tenantId + }); + } + + await _dbContext.InsertAsync(logs); + } } diff --git a/Src/Dlhis.ScheduledTask/Dlhis.ScheduledTask.csproj b/Src/Dlhis.ScheduledTask/Dlhis.ScheduledTask.csproj index c87349e5..9efffeb1 100644 --- a/Src/Dlhis.ScheduledTask/Dlhis.ScheduledTask.csproj +++ b/Src/Dlhis.ScheduledTask/Dlhis.ScheduledTask.csproj @@ -20,8 +20,7 @@ - - + diff --git a/Src/Dlhis.ScheduledTask/IPPortDto.cs b/Src/Dlhis.ScheduledTask/IPPortDto.cs new file mode 100644 index 00000000..6a1bbd8a --- /dev/null +++ b/Src/Dlhis.ScheduledTask/IPPortDto.cs @@ -0,0 +1,8 @@ +namespace Dlhis.ScheduledTask +{ + public class IPPortDto + { + public string IP { get; set; } + public int Port { get; set; } + } +} diff --git a/Src/Dlhis.ScheduledTask/Packs/HangfirePack.cs b/Src/Dlhis.ScheduledTask/Packs/HangfirePack.cs index 2fc2c372..9e90db6e 100644 --- a/Src/Dlhis.ScheduledTask/Packs/HangfirePack.cs +++ b/Src/Dlhis.ScheduledTask/Packs/HangfirePack.cs @@ -48,7 +48,7 @@ public class HangfirePack : BaseAppPack new BasicAuthAuthorizationUser { Login ="admin", - PasswordClear = "admin" + PasswordClear = "dlhis123" } } }) diff --git a/Src/Dlhis.ScheduledTask/Packs/JobPack.cs b/Src/Dlhis.ScheduledTask/Packs/JobPack.cs index a6d1ed78..a6191095 100644 --- a/Src/Dlhis.ScheduledTask/Packs/JobPack.cs +++ b/Src/Dlhis.ScheduledTask/Packs/JobPack.cs @@ -1,4 +1,5 @@ -using Dlhis.ScheduledTask.SyncSapInfo.Job; +using Dlhis.ScheduledTask.CommonJobs; +using Dlhis.ScheduledTask.SyncSapInfo.Job; using Dlhis.ScheduledTask.TaskAssignment.Job; using Hangfire; using Kurisu.AspNetCore.Startup; @@ -23,12 +24,26 @@ public class JobPack : BaseAppPack TimeZone = TimeZoneInfo.Local }); + //创建设备同步时间任务 RecurringJob.AddOrUpdate("SyncServerDateTime", s => s.RunAsync(), "0 0 0/1 * * ?", new RecurringJobOptions { TimeZone = TimeZoneInfo.Local }); - RecurringJob.AddOrUpdate("CreateGetAttendance", s => s.RunAsync(), "0 0/1 * * * ?", new RecurringJobOptions + //创建设备获取的任务 + RecurringJob.AddOrUpdate("CreateGetUploadRecord", s => s.RunAsync(), "0 0/1 * * * ?", new RecurringJobOptions + { + TimeZone = TimeZoneInfo.Local + }); + + //创建系统默认分组 + RecurringJob.AddOrUpdate("CreateDefinitelyGroup", s => s.RunAsync(), "0 0 5 * * ?", new RecurringJobOptions + { + TimeZone = TimeZoneInfo.Local + }); + + //心跳 + RecurringJob.AddOrUpdate("Heartbeat", s => s.RunAsync(), "0/10 * * * * ?", new RecurringJobOptions { TimeZone = TimeZoneInfo.Local }); diff --git a/Src/Dlhis.ScheduledTask/Program.cs b/Src/Dlhis.ScheduledTask/Program.cs index a69cba51..9f35b5ee 100644 --- a/Src/Dlhis.ScheduledTask/Program.cs +++ b/Src/Dlhis.ScheduledTask/Program.cs @@ -1,4 +1,6 @@ using Kurisu.AspNetCore.Startup; +using Kurisu.AspNetCore.Utils; +using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Startup; using Microsoft.AspNetCore.DataProtection; using StackExchange.Redis; @@ -20,16 +22,11 @@ public class Startup : DefaultStartup public override void ConfigureServices(IServiceCollection services) { base.ConfigureServices(services); + services.AddRedis(); + } - services.AddSingleton(sp => - { - return ConnectionMultiplexer.Connect("10.27.21.15:30050,defaultDatabase:1,password=dlhis123"); - }); - - //todo - var redis = ConnectionMultiplexer.Connect($"10.27.21.15:30050,defaultDatabase:1,password=dlhis123"); - services.AddDataProtection() - .SetApplicationName("basic") - .PersistKeysToStackExchangeRedis(redis, "DataProtection-Keys"); + public override void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + base.Configure(app, env); } } \ No newline at end of file diff --git a/Src/Dlhis.ScheduledTask/Properties/launchSettings.json b/Src/Dlhis.ScheduledTask/Properties/launchSettings.json index a6ea1802..fd368e29 100644 --- a/Src/Dlhis.ScheduledTask/Properties/launchSettings.json +++ b/Src/Dlhis.ScheduledTask/Properties/launchSettings.json @@ -5,7 +5,7 @@ "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, - "applicationUrl": "http://*:5010" + "applicationUrl": "http://localhost:5010" } } } \ No newline at end of file diff --git a/Src/Dlhis.ScheduledTask/SyncSapInfo/Comparers/SapOrgComparer.cs b/Src/Dlhis.ScheduledTask/SyncSapInfo/Comparers/SapOrgComparer.cs deleted file mode 100644 index da2ef800..00000000 --- a/Src/Dlhis.ScheduledTask/SyncSapInfo/Comparers/SapOrgComparer.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Dlhis.ScheduledTask.SyncSapInfo.Dto; - -namespace Dlhis.ScheduledTask.SyncSapInfo.Comparers; - -/// -/// 机构数据比较器 -/// -public class SapOrganizationComparer : IEqualityComparer -{ - public bool Equals(SapOrganizationItem x, SapOrganizationItem y) - { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null) || ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; - - return x.BEGDA == y.BEGDA - && x.ENDDA == y.ENDDA - && x.LANGU == y.LANGU - && x.OBJID == y.OBJID - && x.STEXT == y.STEXT - && x.SOBID == y.SOBID - && x.ORGID01 == y.ORGID01 - && x.ORGID02 == y.ORGID02; - } - - public int GetHashCode(SapOrganizationItem obj) - { - return obj.BEGDA.SafeGetHashCode() ^ obj.ENDDA.SafeGetHashCode() - ^ obj.LANGU.SafeGetHashCode() ^ obj.OBJID.SafeGetHashCode() - ^ obj.STEXT.SafeGetHashCode() ^ obj.SOBID.SafeGetHashCode() - ^ obj.ORGID01.SafeGetHashCode() ^ obj.ORGID02.SafeGetHashCode(); - } -} diff --git a/Src/Dlhis.ScheduledTask/SyncSapInfo/Comparers/SapPositionComparer.cs b/Src/Dlhis.ScheduledTask/SyncSapInfo/Comparers/SapPositionComparer.cs deleted file mode 100644 index 2f6a04b2..00000000 --- a/Src/Dlhis.ScheduledTask/SyncSapInfo/Comparers/SapPositionComparer.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Dlhis.ScheduledTask.SyncSapInfo.Dto; - -namespace Dlhis.ScheduledTask.SyncSapInfo.Comparers; - -/// -/// 岗位信息比较器 -/// -public class SapPositionComparer : IEqualityComparer -{ - public bool Equals(SapPositionItem x, SapPositionItem y) - { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null) || ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; - - return x.BEGDA == y.BEGDA - && x.ENDDA == y.ENDDA - && x.LANGU == y.LANGU - && x.ZJOBID == y.ZJOBID - && x.ZJOBNAME == y.ZJOBNAME - && x.ZZWID == y.ZZWID - && x.ZDEPID == y.ZDEPID; - } - - public int GetHashCode(SapPositionItem obj) - { - return obj.BEGDA.SafeGetHashCode() ^ obj.ENDDA.SafeGetHashCode() - ^ obj.LANGU.SafeGetHashCode() ^ obj.ZJOBID.SafeGetHashCode() - ^ obj.ZJOBNAME.SafeGetHashCode() ^ obj.ZZWID.SafeGetHashCode() - ^ obj.ZDEPID.SafeGetHashCode(); - } -} diff --git a/Src/Dlhis.ScheduledTask/SyncSapInfo/Job/SyncPersonJob.cs b/Src/Dlhis.ScheduledTask/SyncSapInfo/Job/SyncPersonJob.cs index 7dfc0596..0502a332 100644 --- a/Src/Dlhis.ScheduledTask/SyncSapInfo/Job/SyncPersonJob.cs +++ b/Src/Dlhis.ScheduledTask/SyncSapInfo/Job/SyncPersonJob.cs @@ -137,7 +137,6 @@ public class SyncPersonJob Position = item.web, Remark = item.remark, Werks = item.WERKS, - Employed = item.isowner, }; if (item.isowner == "T") @@ -150,6 +149,7 @@ public class SyncPersonJob } } + private void UpdatePerson(IDbContext db, GainfoSapEntity item) { var gainfoSap = db.Queryable().Where(x => x.ename == item.ename).First(); diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Controllers/AssignmentsController.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Controllers/AssignmentsController.cs index 80dae44b..f49e4ee8 100644 --- a/Src/Dlhis.ScheduledTask/TaskAssignment/Controllers/AssignmentsController.cs +++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Controllers/AssignmentsController.cs @@ -1,6 +1,7 @@ using Dlhis.ScheduledTask.TaskAssignment.Request; using Dlhis.ScheduledTask.TaskAssignment.Response; using Dlhis.ScheduledTask.TaskAssignment.Services; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -25,6 +26,7 @@ public class AssignmentsController : ControllerBase /// 获取设备任务(按设备) /// /// + [IgnoreTenant] [HttpGet("devicetasks")] public async Task> DeviceGetTasksGetAsync([FromQuery] DeviceTaskGetRequest request) { @@ -36,6 +38,7 @@ public class AssignmentsController : ControllerBase /// /// [HttpPost("devicetasks")] + [IgnoreTenant] public async Task> DeviceGetTasksPostAsync(DeviceTaskGetRequest request) { return await _assignmentService.DeviceGetTasksAsync(request); @@ -47,6 +50,7 @@ public class AssignmentsController : ControllerBase /// /// [HttpPost("taskstatus")] + [IgnoreTenant] public async Task UpdateDeviceTaskStatusAsync(List requests) { await _assignmentService.UpdateDeviceTaskStatusAsync(requests); @@ -58,6 +62,7 @@ public class AssignmentsController : ControllerBase /// /// [HttpGet("renewfaceurl")] + [IgnoreTenant] public async Task RenewFaceUrlAsync(long taskId) { return await _assignmentService.RenewFaceUrlAsync(taskId); diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Controllers/DeviceTaskAssignmentsController.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Controllers/DeviceTaskAssignmentsController.cs index c11add0f..5d488ed9 100644 --- a/Src/Dlhis.ScheduledTask/TaskAssignment/Controllers/DeviceTaskAssignmentsController.cs +++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Controllers/DeviceTaskAssignmentsController.cs @@ -1,6 +1,7 @@ using Dlhis.ScheduledTask.TaskAssignment.Request; using Dlhis.ScheduledTask.TaskAssignment.Response; using Dlhis.ScheduledTask.TaskAssignment.Services; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; using Kurisu.Core.DataAccess.Dto; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -28,9 +29,19 @@ public class DeviceTaskAssignmentsController : ControllerBase /// /// [HttpGet("page")] + [DataPermission] public async Task> GetPageAsync([FromQuery] DeviceTaskAssignmentRequest request) { return await _deviceTaskAssignmentService.GetPageAsync(request); } + /// + /// 重置任务 + /// + /// + [HttpPost("reset")] + public async Task ResetAsync(DeviceTaskResetRequest request) + { + await _deviceTaskAssignmentService.ResetAsync(request); + } } diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Job/CreateGetAttendanceJob.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Job/CreateGetUploadRecordJob.cs similarity index 60% rename from Src/Dlhis.ScheduledTask/TaskAssignment/Job/CreateGetAttendanceJob.cs rename to Src/Dlhis.ScheduledTask/TaskAssignment/Job/CreateGetUploadRecordJob.cs index 90a1703a..c66584a7 100644 --- a/Src/Dlhis.ScheduledTask/TaskAssignment/Job/CreateGetAttendanceJob.cs +++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Job/CreateGetUploadRecordJob.cs @@ -7,12 +7,12 @@ using Kurisu.SqlSugar.Services; namespace Dlhis.ScheduledTask.TaskAssignment.Job; -public class CreateGetAttendanceJob +public class CreateGetUploadRecordJob { private readonly IDbContext _dbContext; private readonly ILogger _logger; - public CreateGetAttendanceJob(IDbContext dbContext, ILogger logger) + public CreateGetUploadRecordJob(IDbContext dbContext, ILogger logger) { _logger = logger; _dbContext = dbContext; @@ -23,30 +23,25 @@ public class CreateGetAttendanceJob _dbContext.Client.QueryFilter.ClearAndBackup(); try { - var devices = await _dbContext.Queryable().Where(x => x.Enable).ToListAsync(); - devices = devices.Where(x => x.Funcs.Contains(DeviceFuncEnumType.Attendance)).ToList(); + var devices = await _dbContext.Queryable() + .Where(x => x.Enable) + .Where(x => x.Type == DeviceTypeEnumType.Attendance || x.Type == DeviceTypeEnumType.Door) + .ToListAsync(); - var ds = devices.Select(x => x.UniqueNo).ToList(); + //过滤处理中的任务 + var currentUniqueNos = await _dbContext.Queryable() + .Where(x => x.Type == DeviceTaskEnumType.UploadRecord) + .Where(x => x.Status == DeviceTaskStatusEnumType.Process || x.Status == DeviceTaskStatusEnumType.Wait) + .Select(x => x.DeviceUniqueNo) + .ToListAsync(); - var b = await _dbContext.Queryable() - .Where(x => ds.Contains(x.DeviceUniqueNo)) - .Where(x => x.Type == DeviceTaskEnumType.UploadRecord) - .Where(x => x.Status == DeviceTaskStatusEnumType.Process) - .AnyAsync(); - if (b) - { - return; - } - - var tasks = devices.Select(x => new DeviceTaskEntity + var tasks = devices.Where(x => !currentUniqueNos.Contains(x.UniqueNo)).Select(x => new DeviceTaskEntity { DeviceUniqueNo = x.UniqueNo, Status = DeviceTaskStatusEnumType.Wait, TenantId = x.TenantId, Version = 0, - UserNo = string.Empty, - UserCode = string.Empty, Type = DeviceTaskEnumType.UploadRecord, CreatedBy = 1, @@ -54,7 +49,8 @@ public class CreateGetAttendanceJob ModifiedBy = 1, ModifiedTime = DateTime.Now, - }).ToList(); + }) + .ToList(); await _dbContext.InsertAsync(tasks); } diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Job/CreateSyncServerDateTimeJob.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Job/CreateSyncServerDateTimeJob.cs index 44a2618c..789d6722 100644 --- a/Src/Dlhis.ScheduledTask/TaskAssignment/Job/CreateSyncServerDateTimeJob.cs +++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Job/CreateSyncServerDateTimeJob.cs @@ -1,6 +1,7 @@ using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Device.Entities; using Dlhis.Entity.Device.Enums; +using Kurisu.Core.DataAccess.Entity; using Kurisu.SqlSugar.Services; namespace Dlhis.ScheduledTask.TaskAssignment.Job @@ -19,20 +20,29 @@ namespace Dlhis.ScheduledTask.TaskAssignment.Job public async Task RunAsync() { - _dbContext.Client.QueryFilter.ClearAndBackup(); + _dbContext.Client.QueryFilter.ClearAndBackup(); try { - var devices = await _dbContext.Queryable().Where(x => x.Enable).ToListAsync(); + var deviceUniqueNos = await _dbContext.Queryable() + .Where(x => x.Status == DeviceTaskStatusEnumType.Wait || x.Status == DeviceTaskStatusEnumType.Process) + .Where(x => x.Type == DeviceTaskEnumType.SyncDateTime) + .Select(x => x.DeviceUniqueNo) + .Distinct() + .ToListAsync(); + + var devices = await _dbContext.Queryable() + .Where(x => !deviceUniqueNos.Contains(x.UniqueNo)) + .Where(x => x.Enable) + .ToListAsync(); + var tasks = devices.Select(x => new DeviceTaskEntity { - DeviceUniqueNo = x.UniqueNo, Status = DeviceTaskStatusEnumType.Wait, TenantId = x.TenantId, Version = 0, UserNo = string.Empty, - UserCode = string.Empty, Type = DeviceTaskEnumType.SyncDateTime, CreatedBy = 1, @@ -46,7 +56,7 @@ namespace Dlhis.ScheduledTask.TaskAssignment.Job } catch (Exception ex) { - _logger.LogError(ex, "生成同步时间任务失败:" + ex.Message); + _logger.LogError(ex, "生成同步时间任务失败:{message}", ex.Message); } finally { diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Request/DeviceTaskAssignmentRequest.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Request/DeviceTaskAssignmentRequest.cs index a7c196f0..fee03043 100644 --- a/Src/Dlhis.ScheduledTask/TaskAssignment/Request/DeviceTaskAssignmentRequest.cs +++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Request/DeviceTaskAssignmentRequest.cs @@ -1,7 +1,27 @@ -using Kurisu.Core.DataAccess.Dto; +using System.ComponentModel.DataAnnotations; +using Dlhis.Entity.Device.Enums; +using Kurisu.Core.DataAccess.Dto; namespace Dlhis.ScheduledTask.TaskAssignment.Request; public class DeviceTaskAssignmentRequest : PageDto { + /// + /// 设备类型 + /// + [EnumDataType(typeof(DeviceTypeEnumType))] + public DeviceTypeEnumType? Type { get; set; } + + + /// + /// 任务类型 + /// + [EnumDataType(typeof(DeviceTaskEnumType))] + public DeviceTaskEnumType? TaskType { get; set; } + + /// + /// 任务下发状态 + /// + [EnumDataType(typeof(DeviceTaskStatusEnumType))] + public DeviceTaskStatusEnumType? Status { get; set; } } diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Request/DeviceTaskGetRequest.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Request/DeviceTaskGetRequest.cs index 178481ce..84cf335e 100644 --- a/Src/Dlhis.ScheduledTask/TaskAssignment/Request/DeviceTaskGetRequest.cs +++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Request/DeviceTaskGetRequest.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using Dlhis.Entity.Device.Enums; using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Core.DataAccess.Dto; @@ -16,6 +17,11 @@ public class DeviceTaskGetRequest : PageDto, IValidatableObject /// public string DeviceUniqueNo { get; set; } + /// + /// 任务类型 + /// + public List Types { get; set; } + /// /// 获取处理中的任务 /// diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Request/DeviceTaskResetRequest.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Request/DeviceTaskResetRequest.cs new file mode 100644 index 00000000..516be624 --- /dev/null +++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Request/DeviceTaskResetRequest.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.ScheduledTask.TaskAssignment.Request; + +public class DeviceTaskResetRequest +{ + /// + /// 任务ids + /// + [MinLength(1, ErrorMessage = "请选择任务进行重置")] + public List Ids { get; set; } + + /// + /// 是否强制重置 + /// + public bool Force { get; set; } +} diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Response/DeviceTaskAssignmentResponse.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Response/DeviceTaskAssignmentResponse.cs index e525d6d3..beeb82ed 100644 --- a/Src/Dlhis.ScheduledTask/TaskAssignment/Response/DeviceTaskAssignmentResponse.cs +++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Response/DeviceTaskAssignmentResponse.cs @@ -1,10 +1,13 @@ using Dlhis.Entity.Device.Enums; using Kurisu.AspNetCore.Utils.Extensions; +using SqlSugar; namespace Dlhis.ScheduledTask.TaskAssignment.Response; public class DeviceTaskAssignmentResponse { + public long Id { get; set; } + /// /// 任务类型 /// @@ -55,8 +58,34 @@ public class DeviceTaskAssignmentResponse /// public string ResultMessage { get; set; } + /// + /// 任务创建时间 + /// + public DateTime CreateTime { get; set; } + /// /// 数据状态最新修改时间 /// public DateTime LastUpdateTime { get; set; } + + + /// + /// 厂区id + /// + public string TenantId { get; set; } + + /// + /// 厂区名称 + /// + public string TenantName { get; set; } + + /// + /// 部署点 + /// + public string DeployPlace { get; set; } + + /// + /// 连接信息 + /// + public string ConnectionInfo { get; set; } } diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Services/IDeviceTaskAssignmentService.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Services/IDeviceTaskAssignmentService.cs index e5bc005c..67c172dd 100644 --- a/Src/Dlhis.ScheduledTask/TaskAssignment/Services/IDeviceTaskAssignmentService.cs +++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Services/IDeviceTaskAssignmentService.cs @@ -8,4 +8,6 @@ public interface IDeviceTaskAssignmentService : IScopeDependency { Task> GetPageAsync(DeviceTaskAssignmentRequest request); + Task ResetAsync(DeviceTaskResetRequest request); + } diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Services/Implements/AssignmentService.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Services/Implements/AssignmentService.cs index 53c70e7d..2cd633bd 100644 --- a/Src/Dlhis.ScheduledTask/TaskAssignment/Services/Implements/AssignmentService.cs +++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Services/Implements/AssignmentService.cs @@ -12,13 +12,12 @@ using Microsoft.AspNetCore.DataProtection; using Microsoft.Extensions.Options; using SqlSugar; using Kurisu.SqlSugar.Extensions; -using Kurisu.SqlSugar.Attributes; using Newtonsoft.Json.Linq; using Kurisu.Core.CustomClass; +using Kurisu.AspNetCore; namespace Dlhis.ScheduledTask.TaskAssignment.Services.Implements; -[IgnoreTenant] public class AssignmentService : IAssignmentService { private readonly IDbContext _dbContext; @@ -41,6 +40,7 @@ public class AssignmentService : IAssignmentService .LeftJoin((deviceTask, deivce, deivceTamplate, ga, department) => department.No == ga.DepartmentNo) .WhereIF(request.Brand.IsPresent(), (deviceTask, deivce, deivceTamplate) => deivceTamplate.Brand == request.Brand) .WhereIF(request.DeviceUniqueNo.IsPresent(), (deviceTask, deivce) => deviceTask.DeviceUniqueNo == request.DeviceUniqueNo) + .WhereIF(request.Types.IsPresent(), deviceTask => request.Types.Contains(deviceTask.Type)) .WhereIF(!request.IsProcess, deviceTask => deviceTask.Status == DeviceTaskStatusEnumType.Wait) .WhereIF(request.IsProcess, deviceTask => deviceTask.Status == DeviceTaskStatusEnumType.Process) .OrderBy(deviceTask => deviceTask.CreateTime) @@ -54,7 +54,7 @@ public class AssignmentService : IAssignmentService deivceTamplate.Brand, FuncStr = deivce.Funcs.ToString(), deviceTask.UserNo, - deviceTask.UserCode, + UserCode = ga.Code, UserName = ga.Name, ga.DepartmentNo, DepartmentName = department.Name, @@ -99,6 +99,7 @@ public class AssignmentService : IAssignmentService } return page.Data; + }, 5); if (!data.Any()) @@ -284,25 +285,21 @@ public class AssignmentService : IAssignmentService { var task = await _dbContext.Queryable().Where(x => x.Id == taskId).FirstAsync(); task.ThrowIfNull("该任务不存在"); - (task.Status != DeviceTaskStatusEnumType.Process).ThrowIfTrue($"设备任务下发状态需为{DeviceTaskStatusEnumType.Process.GetDescription()}"); - return Scoped.Request.Value.Create((sp) => - { - var faceUrlOptions = sp.GetService>().Value; - var dataProtectionProvider = sp.GetService(); + var faceUrlOptions = App.GetServiceProvider(true).GetService>().Value; + var dataProtectionProvider = App.GetServiceProvider(true).GetService(); - var timeLimitedDataProtector = dataProtectionProvider - .CreateProtector("down:face") - .ToTimeLimitedDataProtector(); + var timeLimitedDataProtector = dataProtectionProvider + .CreateProtector("down:face") + .ToTimeLimitedDataProtector(); - var userCode = task.UserCode; + var userCode = await _dbContext.Queryable().Where(x => x.No == task.UserNo).Select(x => x.Code).SingleAsync(); - var key = $"face:{userCode}"; - var token = timeLimitedDataProtector.Protect(key, TimeSpan.FromSeconds(faceUrlOptions.Limit)); + var key = $"face:{userCode}"; + var token = timeLimitedDataProtector.Protect(key, TimeSpan.FromSeconds(faceUrlOptions.Limit)); - return faceUrlOptions.Url + token; - }); + return faceUrlOptions.Url + token; } @@ -318,11 +315,6 @@ public class AssignmentService : IAssignmentService requests = requests.Where(x => taskIds.Contains(x.TaskId)).ToList(); - deviceTasks.ForEach(x => - { - - }); - var now = DateTime.Now; var tasks = new List(requests.Count); foreach (var request in requests) @@ -359,7 +351,7 @@ public class AssignmentService : IAssignmentService } catch (Exception ex) when (ex is VersionExceptions) { - _logger.LogWarning(ex, ex.Message); + _logger.LogWarning(ex, "并发更新失败:{message},", ex.Message); } catch (Exception) { diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Services/Implements/DeviceTaskAssignmentService.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Services/Implements/DeviceTaskAssignmentService.cs index 699bd273..2b8d28f8 100644 --- a/Src/Dlhis.ScheduledTask/TaskAssignment/Services/Implements/DeviceTaskAssignmentService.cs +++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Services/Implements/DeviceTaskAssignmentService.cs @@ -3,44 +3,101 @@ using Dlhis.Entity.Device.Entities; using Dlhis.Entity.Device.Enums; using Dlhis.ScheduledTask.TaskAssignment.Request; using Dlhis.ScheduledTask.TaskAssignment.Response; +using Dlhis.Sys.Api; using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Core.DataAccess.Dto; using Kurisu.SqlSugar.Extensions; using Kurisu.SqlSugar.Services; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; +using SqlSugar; namespace Dlhis.ScheduledTask.TaskAssignment.Services.Implements; public class DeviceTaskAssignmentService : IDeviceTaskAssignmentService { private readonly IDbContext _dbContext; + private readonly ITenantApi _tenantApi; - public DeviceTaskAssignmentService(IDbContext dbContext) + public DeviceTaskAssignmentService(IDbContext dbContext, ITenantApi tenantApi) { _dbContext = dbContext; + _tenantApi = tenantApi; } public async Task> GetPageAsync(DeviceTaskAssignmentRequest request) { var page = await _dbContext.Queryable() .LeftJoin((deviceTask, ga) => deviceTask.UserNo == ga.No) - .Select((deviceTask, ga) => new DeviceTaskAssignmentResponse - { - DeviceUniqueNo = deviceTask.DeviceUniqueNo, - ResultMessage = deviceTask.ResultMessage, - RetryCount = deviceTask.Version, - Status = deviceTask.Status, - UserNo = deviceTask.UserNo, - UserName = ga.Name, - UserCode = deviceTask.UserCode, - Type = deviceTask.Type, - LastUpdateTime = deviceTask.ModifiedTime - }) - .ToPageListAsync(request); + .LeftJoin((deviceTask, ga, device) => deviceTask.DeviceUniqueNo == device.UniqueNo) + .WhereIF(!request.Search.IsEmpty(), (deviceTask, ga, device) => + deviceTask.UserNo == request.Search + || ga.Name.Contains(request.Search) + || ga.Code == request.Search + || deviceTask.DeviceUniqueNo == request.Search + ) + .WhereIF(request.TaskType.HasValue, deviceTask => deviceTask.Type == request.TaskType) + .WhereIF(request.Type.HasValue, (deviceTask, ga, device) => device.Type == request.Type) + .WhereIF(request.Status.HasValue, deviceTask => deviceTask.Status == request.Status) + .Where(deviceTask => 1 == 1) + .OrderBy(deviceTask => deviceTask.CreateTime, OrderByType.Desc) + .Select((deviceTask, ga, device) => new DeviceTaskAssignmentResponse + { + Id = deviceTask.Id, + DeviceUniqueNo = deviceTask.DeviceUniqueNo, + ResultMessage = deviceTask.ResultMessage, + RetryCount = deviceTask.Version, + Status = deviceTask.Status, + UserNo = deviceTask.UserNo, + UserName = ga.Name, + UserCode = ga.Code, + Type = deviceTask.Type, + CreateTime = deviceTask.CreateTime, + LastUpdateTime = deviceTask.ModifiedTime, + TenantId = deviceTask.TenantId, + DeployPlace = device.DeployPlace, + ConnectionInfo = device.ConnectionInfo + }) + .ToPageListAsync(request); + + + try + { + var tenantResult = await _tenantApi.GetTenantsInternalAsync(); + var tenants = tenantResult.Data; + + page.Data.ForEach(item => + { + item.TenantName = tenants.FirstOrDefault(x => x.Code == item.TenantId)?.Name; + }); + + } + catch (Exception) + { + } + return page; } + public async Task ResetAsync(DeviceTaskResetRequest request) + { + var tasks = await _dbContext.Queryable() + .Where(x => request.Ids.Contains(x.Id)) + .Where(x => x.Status == DeviceTaskStatusEnumType.Process || x.Status == DeviceTaskStatusEnumType.Error) + .ToListAsync(); + + //判断process任务时间 + if (!request.Force) + { + var now = DateTime.Now; + tasks.Any(x => x.ModifiedTime.AddMinutes(10) < now && x.Status == DeviceTaskStatusEnumType.Process).ThrowIfTrue("请等待任务处理完成"); + } + + //更新任务状态 + await _dbContext.Updateable() + .SetColumns(x => x.Status == DeviceTaskStatusEnumType.Wait) + .Where(x => request.Ids.Contains(x.Id)) + .Where(x => x.Status == DeviceTaskStatusEnumType.Process || x.Status == DeviceTaskStatusEnumType.Error) + .ExecuteCommandAsync(); + } } diff --git a/Src/Dlhis.ScheduledTask/appsettings.Development.json b/Src/Dlhis.ScheduledTask/appsettings.Development.json index fbfb5dd0..8674e444 100644 --- a/Src/Dlhis.ScheduledTask/appsettings.Development.json +++ b/Src/Dlhis.ScheduledTask/appsettings.Development.json @@ -5,6 +5,9 @@ "Microsoft.AspNetCore": "Warning" } }, + "InternalRequest": { + "SysRequestUrl": "http://localhost:5003" + }, "FaceUrlOptions": { "Url": "http://localhost:5002/api/gainfos/face?token=", "Limit": 7200 diff --git a/Src/Dlhis.ScheduledTask/appsettings.json b/Src/Dlhis.ScheduledTask/appsettings.json index ce8a7ff3..6ab2844d 100644 --- a/Src/Dlhis.ScheduledTask/appsettings.json +++ b/Src/Dlhis.ScheduledTask/appsettings.json @@ -8,6 +8,10 @@ "InternalRequest": { "SysRequestUrl": "http://sys" }, + "DataProtectionOptions": { + "Enable": true, + "AppName": "yiliantong" + }, "SqlSugarOptions": { "DefaultConnectionString": "server=10.27.21.22;port=3306;userid=root;password=dlhis123;database=yikatong;charset=utf8mb4;", "SlaveConnectionString": [], @@ -54,6 +58,9 @@ "AttendanceUploadInterval": "30", "BeginSyncDate": "2020-5-1" }, + "RedisOptions": { + "ConnectionString": "192.168.199.30:30115,defaultDatabase:1,password=dlhis123" + }, "AllowedHosts": "*", "Serilog": { "MinimumLevel": { diff --git a/Src/Dlhis.Sys.Api/Dlhis.Sys.Api.csproj b/Src/Dlhis.Sys.Api/Dlhis.Sys.Api.csproj index 71b4a337..50a74ddd 100644 --- a/Src/Dlhis.Sys.Api/Dlhis.Sys.Api.csproj +++ b/Src/Dlhis.Sys.Api/Dlhis.Sys.Api.csproj @@ -7,7 +7,7 @@ - + diff --git a/Src/Dlhis.Sys.Api/ITenantApi.cs b/Src/Dlhis.Sys.Api/ITenantApi.cs index efa3634f..72feba3b 100644 --- a/Src/Dlhis.Sys.Api/ITenantApi.cs +++ b/Src/Dlhis.Sys.Api/ITenantApi.cs @@ -1,4 +1,4 @@ -using Dlhis.User.Api.Response; +using Dlhis.Sys.Api.Response; using Kurisu.Core.Result; using Kurisu.RemoteCall.Attributes; @@ -18,6 +18,11 @@ public interface ITenantApi [Get("/api/tenants/list")] Task>> GetTenantsAsync(); + + [Auth] + [Get("/api/tenants/tree")] + Task>> GetTenantTreeAsync(); + /// /// 获取所有租户(内部使用) /// diff --git a/Src/Dlhis.Sys.Api/Response/TenantResponse.cs b/Src/Dlhis.Sys.Api/Response/TenantResponse.cs index 1201754e..329c0b98 100644 --- a/Src/Dlhis.Sys.Api/Response/TenantResponse.cs +++ b/Src/Dlhis.Sys.Api/Response/TenantResponse.cs @@ -1,9 +1,11 @@ -namespace Dlhis.User.Api.Response; +namespace Dlhis.Sys.Api.Response; public class TenantResponse { public int Id { get; set; } + public string PCode { get; set; } + public string Code { get; set; } public string Name { get; set; } diff --git a/Src/Dlhis.Sys.Api/Response/TenantTreeResponse.cs b/Src/Dlhis.Sys.Api/Response/TenantTreeResponse.cs new file mode 100644 index 00000000..a81ba190 --- /dev/null +++ b/Src/Dlhis.Sys.Api/Response/TenantTreeResponse.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Dlhis.Sys.Api.Response; + +public class TenantTreeResponse : TenantResponse +{ + /// + /// 下级 + /// + [JsonProperty(Order = 99)] + public List Next { get; set; } +} diff --git a/Src/Dlhis.Sys/Dlhis.Sys.csproj b/Src/Dlhis.Sys/Dlhis.Sys.csproj index fe92dc2c..b14b421d 100644 --- a/Src/Dlhis.Sys/Dlhis.Sys.csproj +++ b/Src/Dlhis.Sys/Dlhis.Sys.csproj @@ -17,8 +17,7 @@ - - + diff --git a/Src/Dlhis.Sys/Identity/Controllers/RolesController.cs b/Src/Dlhis.Sys/Identity/Controllers/RolesController.cs index ae0ba399..cd65a44d 100644 --- a/Src/Dlhis.Sys/Identity/Controllers/RolesController.cs +++ b/Src/Dlhis.Sys/Identity/Controllers/RolesController.cs @@ -10,7 +10,7 @@ namespace Dlhis.Sys.Identity.Controllers; /// /// 角色 /// -[Authorize(Roles = "admin")] +[Authorize] [Route("api/[controller]")] [ApiController] public class RolesController : ControllerBase @@ -27,9 +27,66 @@ public class RolesController : ControllerBase /// /// /// - [HttpGet] - public async Task> GetPageAsync(RolePageRequest request) + [Authorize(Roles = "admin")] + [HttpGet("page")] + public async Task> GetPageAsync([FromQuery] RolePageRequest request) { return await _roleService.GetPageAsync(request); } + + /// + /// 角色列表 + /// + /// + [Authorize(Roles = "admin")] + [HttpGet("list")] + public async Task> GetListAsync() + { + return await _roleService.GetListAsync(); + } + + /// + /// 分配角色 + /// + /// + /// + [Authorize(Roles = "admin")] + [HttpPost("assign")] + public async Task AssignRole(AssignRoleRequest request) + { + await _roleService.AssignRoleAsync(request); + } + + /// + /// 保存角色 + /// + /// + [Authorize(Roles = "admin")] + [HttpPost("save")] + public async Task SaveRole(RoleSaveRequest request) + { + await _roleService.SaveRoleAsync(request); + } + + /// + /// 移除角色 + /// + /// + /// + [Authorize(Roles = "admin")] + [HttpPost("delete")] + public async Task RemoveRole(RoleRemoveRequest request) + { + await _roleService.RemoveRoleAsync(request); + } + + /// + /// 我的角色 + /// + /// + [HttpGet("my")] + public async Task GetMyRoleAsync(int? userId) + { + return await _roleService.GetMyRoleAsync(userId); + } } diff --git a/Src/Dlhis.Sys/Identity/Controllers/TenantsController.cs b/Src/Dlhis.Sys/Identity/Controllers/TenantsController.cs index 1a0a605d..4018d12d 100644 --- a/Src/Dlhis.Sys/Identity/Controllers/TenantsController.cs +++ b/Src/Dlhis.Sys/Identity/Controllers/TenantsController.cs @@ -51,6 +51,16 @@ public class TenantsController : ControllerBase return await _tenantService.GetListAsync(); } + /// + /// 租户树 + /// + /// + [HttpGet("tree")] + public async Task> GetTreeAsync() + { + return await _tenantService.GetTreeAsync(); + } + /// /// 获取租户 /// @@ -61,6 +71,17 @@ public class TenantsController : ControllerBase return await _tenantService.GetTenantByCode(code); } + /// + /// 保存租户 + /// + /// + /// + [HttpPost("save")] + public async Task SaveTenantAsync(TenantSaveRequest request) + { + await _tenantService.SaveTenantAsync(request); + } + /// /// 获取我的管理租户 /// diff --git a/Src/Dlhis.Sys/Identity/Request/AssignRoleRequest.cs b/Src/Dlhis.Sys/Identity/Request/AssignRoleRequest.cs new file mode 100644 index 00000000..c66d25c5 --- /dev/null +++ b/Src/Dlhis.Sys/Identity/Request/AssignRoleRequest.cs @@ -0,0 +1,8 @@ +namespace Dlhis.Sys.Identity.Request; + +public class AssignRoleRequest +{ + public int UserId { get; set; } + + public int? RoleId { get; set; } +} diff --git a/Src/Dlhis.Sys/Identity/Request/RoleRemoveRequest.cs b/Src/Dlhis.Sys/Identity/Request/RoleRemoveRequest.cs new file mode 100644 index 00000000..ade423a8 --- /dev/null +++ b/Src/Dlhis.Sys/Identity/Request/RoleRemoveRequest.cs @@ -0,0 +1,6 @@ +namespace Dlhis.Sys.Identity.Request; + +public class RoleRemoveRequest +{ + public int Id { get; set; } +} diff --git a/Src/Dlhis.Sys/Identity/Request/RoleSaveRequest.cs b/Src/Dlhis.Sys/Identity/Request/RoleSaveRequest.cs new file mode 100644 index 00000000..843bc8c5 --- /dev/null +++ b/Src/Dlhis.Sys/Identity/Request/RoleSaveRequest.cs @@ -0,0 +1,11 @@ +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Sys.Identity.Request; + +public class RoleSaveRequest +{ + public int? Id { get; set; } + + [Required(ErrorMessage = "角色名不能为空")] + public string Name { get; set; } +} diff --git a/Src/Dlhis.Sys/Identity/Request/TenantSaveRequest.cs b/Src/Dlhis.Sys/Identity/Request/TenantSaveRequest.cs new file mode 100644 index 00000000..700c3ba0 --- /dev/null +++ b/Src/Dlhis.Sys/Identity/Request/TenantSaveRequest.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations; + +namespace Dlhis.Sys.Identity.Request; + +public class TenantSaveRequest +{ + public int? Id { get; set; } + + /// + /// 上级 + /// + public string PCode { get; set; } + + /// + /// 代码 + /// + [Required(ErrorMessage = "基地/租户代码不能为空")] + public string Code { get; set; } + + /// + /// 名称 + /// + [Required(ErrorMessage = "基地/租户名称不能为空")] + public string Name { get; set; } +} diff --git a/Src/Dlhis.Sys/Identity/Response/RoleRespone.cs b/Src/Dlhis.Sys/Identity/Response/RoleRespone.cs index 0b233448..03cd8c23 100644 --- a/Src/Dlhis.Sys/Identity/Response/RoleRespone.cs +++ b/Src/Dlhis.Sys/Identity/Response/RoleRespone.cs @@ -7,4 +7,9 @@ public class RoleRespone public string Name { get; set; } public string NormalizedName { get; set; } + + /// + /// 是否可编辑 + /// + public bool Editable => Id > 5; } diff --git a/Src/Dlhis.Sys/Identity/Response/TenantResponse.cs b/Src/Dlhis.Sys/Identity/Response/TenantResponse.cs index 1949b745..b265def5 100644 --- a/Src/Dlhis.Sys/Identity/Response/TenantResponse.cs +++ b/Src/Dlhis.Sys/Identity/Response/TenantResponse.cs @@ -4,6 +4,8 @@ public class TenantResponse { public int Id { get; set; } + public string PCode { get; set; } + public string Code { get; set; } public string Name { get; set; } diff --git a/Src/Dlhis.Sys/Identity/Response/TenantTreeResponse.cs b/Src/Dlhis.Sys/Identity/Response/TenantTreeResponse.cs new file mode 100644 index 00000000..dc940ba0 --- /dev/null +++ b/Src/Dlhis.Sys/Identity/Response/TenantTreeResponse.cs @@ -0,0 +1,12 @@ +using Newtonsoft.Json; + +namespace Dlhis.Sys.Identity.Response; + +public class TenantTreeResponse : TenantResponse +{ + /// + /// 下级 + /// + [JsonProperty(Order = 99)] + public List Next { get; set; } +} diff --git a/Src/Dlhis.Sys/Identity/Response/UserResponse.cs b/Src/Dlhis.Sys/Identity/Response/UserResponse.cs index d11dec11..15c1fe33 100644 --- a/Src/Dlhis.Sys/Identity/Response/UserResponse.cs +++ b/Src/Dlhis.Sys/Identity/Response/UserResponse.cs @@ -4,25 +4,64 @@ public class UserResponse { public int Id { get; set; } + /// + /// 账号名称 + /// public string UserName { get; set; } + /// + /// 规范化账号名称 + /// public string NormalizedUserName { get; set; } + /// + /// 邮件 + /// public string Email { get; set; } + /// + /// 规范化邮件 + /// public string NormalizedEmail { get; set; } + /// + /// 邮件是否已确认 + /// public bool EmailConfirmed { get; set; } + /// + /// 手机号 + /// public string PhoneNumber { get; set; } + /// + /// 手机号是否已确认 + /// public bool PhoneNumberConfirmed { get; set; } + /// + /// 是否开启双因素认证 + /// public bool TwoFactorEnabled { get; set; } + /// + /// 账号锁定结束时间 + /// public DateTime? LockoutEnd { get; set; } + /// + /// 是否开启账号锁定功能 + /// public bool LockoutEnabled { get; set; } + /// + /// 登陆失败次数 + /// public int AccessFailedCount { get; set; } + + + /// + /// 角色 + /// + public string Role { get; set; } } diff --git a/Src/Dlhis.Sys/Identity/Services/IRoleService.cs b/Src/Dlhis.Sys/Identity/Services/IRoleService.cs index 41af7fd6..8b02ba23 100644 --- a/Src/Dlhis.Sys/Identity/Services/IRoleService.cs +++ b/Src/Dlhis.Sys/Identity/Services/IRoleService.cs @@ -7,4 +7,14 @@ namespace Dlhis.User.Identity.Services; public interface IRoleService : IScopeDependency { Task> GetPageAsync(RolePageRequest request); + + Task> GetListAsync(); + + Task SaveRoleAsync(RoleSaveRequest request); + + Task GetMyRoleAsync(int? userId); + + Task RemoveRoleAsync(RoleRemoveRequest request); + + Task AssignRoleAsync(AssignRoleRequest request); } diff --git a/Src/Dlhis.Sys/Identity/Services/ITenantService.cs b/Src/Dlhis.Sys/Identity/Services/ITenantService.cs index c8a144fb..c7fb53d8 100644 --- a/Src/Dlhis.Sys/Identity/Services/ITenantService.cs +++ b/Src/Dlhis.Sys/Identity/Services/ITenantService.cs @@ -10,7 +10,11 @@ public interface ITenantService : IScopeDependency Task> GetListAsync(); + Task> GetTreeAsync(); + Task GetTenantByCode(string code); Task> GetMyTenantsAsync(); + + Task SaveTenantAsync(TenantSaveRequest request); } diff --git a/Src/Dlhis.Sys/Identity/Services/Implements/RoleService.cs b/Src/Dlhis.Sys/Identity/Services/Implements/RoleService.cs index 8768d181..2702f1a2 100644 --- a/Src/Dlhis.Sys/Identity/Services/Implements/RoleService.cs +++ b/Src/Dlhis.Sys/Identity/Services/Implements/RoleService.cs @@ -2,8 +2,11 @@ using Dlhis.Sys.Identity.Request; using Dlhis.Sys.Identity.Response; using Dlhis.User.Identity.Services; +using Kurisu.AspNetCore.Scope; +using Kurisu.AspNetCore.UnifyResultAndValidation; using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.Core.DataAccess.Dto; +using Kurisu.Core.User.Abstractions; using Kurisu.SqlSugar.Extensions; using Kurisu.SqlSugar.Services; @@ -11,20 +14,176 @@ namespace Dlhis.Sys.Identity.Services.Implements; public class RoleService : IRoleService { - private readonly IDbContext _dbContext; + private readonly IConfiguration _configuration; + private readonly ICurrentUser _currentUser; - public RoleService(IDbContext dbContext, IConfiguration configuration) + public RoleService(IConfiguration configuration, ICurrentUser currentUser) { - var identityConnection = configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value; - dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection; - _dbContext = dbContext; + _configuration = configuration; + _currentUser = currentUser; } public async Task> GetPageAsync(RolePageRequest request) { - return await _dbContext.Queryable() + return await Scoped.Request.Value.CreateAsync(async sp => + { + var dbContext = sp.GetService(); + var identityConnection = _configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value; + dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection; + + + return await dbContext.Queryable() .WhereIF(request.Search.IsPresent(), x => x.Name.Contains(request.Search) || x.NormalizedName.Contains(request.Search)) .Select() .ToPageListAsync(request); + }); + } + + public async Task GetMyRoleAsync(int? userId) + { + userId ??= _currentUser.GetIntSubjectId(); + + return await Scoped.Request.Value.CreateAsync(async sp => + { + var dbContext = sp.GetService(); + var identityConnection = _configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value; + dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection; + + return await dbContext.Queryable() + .LeftJoin((x, r) => x.RoleId == r.Id) + .Where(x => x.UserId == userId) + .Select((x, r) => new RoleRespone + { + Id = r.Id, + Name = r.Name, + NormalizedName = r.NormalizedName, + }) + .FirstAsync(); + }); + } + + public async Task SaveRoleAsync(RoleSaveRequest request) + { + await Scoped.Request.Value.CreateAsync(async sp => + { + var dbContext = sp.GetService(); + var identityConnection = _configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value; + dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection; + + + await CheckDuplicateNameAsync(dbContext, request); + + if (!request.Id.HasValue) + { + await dbContext.InsertAsync(new RoleEntity + { + Name = request.Name, + NormalizedName = request.Name.ToUpperInvariant(), + ConcurrencyStamp = Guid.NewGuid(), + }); + } + else + { + var entity = await dbContext.Queryable().Where(x => x.Id == request.Id).FirstAsync(); + entity.ThrowIfNull("角色不存在"); + + entity.Name = request.Name; + entity.NormalizedName = request.Name.ToUpperInvariant(); + entity.ConcurrencyStamp = Guid.NewGuid(); + + await dbContext.UpdateAsync(entity); + } + }); + } + + + private async Task CheckDuplicateNameAsync(IDbContext db, RoleSaveRequest request) + { + var exists = await db.Queryable() + .WhereIF(request.Id.HasValue, x => x.Id != request.Id) + .Where(x => x.Name == request.Name) + .AnyAsync(); + + exists.ThrowIfTrue($"重复角色名称{request.Name}"); + } + + public async Task AssignRoleAsync(AssignRoleRequest request) + { + var myUserId = _currentUser.GetIntSubjectId(); + + await Scoped.Request.Value.CreateAsync(async sp => + { + //不能被其他人修改 + if (request.UserId == 1 && request.UserId != myUserId) + { + throw new UserFriendlyException("无法修改admin角色"); + } + + var dbContext = sp.GetService(); + var identityConnection = _configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value; + dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection; + + var existsUser = await dbContext.Queryable().Where(x => x.Id == request.UserId).AnyAsync(); + existsUser.ThrowIfFalse("分配的用户不存在"); + + var userRole = await dbContext.Queryable().Where(x => x.UserId == request.UserId).FirstAsync(); + + if (request.RoleId.HasValue) + { + var existsRole = await dbContext.Queryable().Where(x => x.Id == request.RoleId).AnyAsync(); + existsUser.ThrowIfFalse("分配的角色不存在"); + + if (userRole == null) + { + await dbContext.InsertAsync(new UserRoleEntity + { + UserId = request.UserId, + RoleId = request.RoleId.Value, + }); + } + else + { + await dbContext.Updateable() + .SetColumns(x => x.RoleId == request.RoleId) + .Where(x => x.UserId == request.UserId) + .ExecuteCommandAsync(); + } + } + else + { + await dbContext.DeleteReallyAsync(userRole); + } + }); + } + + public async Task RemoveRoleAsync(RoleRemoveRequest request) + { + await Scoped.Request.Value.CreateAsync(async sp => + { + var dbContext = sp.GetService(); + var identityConnection = _configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value; + dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection; + + var role = await dbContext.Queryable().FirstAsync(x => x.Id == request.Id); + role.ThrowIfNull("角色不存在"); + + //删除角色,自动删除UserRoles对应关系 + await dbContext.DeleteReallyAsync(role); + }); + } + + public async Task> GetListAsync() + { + return await Scoped.Request.Value.CreateAsync(async sp => + { + var dbContext = sp.GetService(); + var identityConnection = _configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value; + dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection; + + + return await dbContext.Queryable() + .Select() + .ToListAsync(); + }); } } diff --git a/Src/Dlhis.Sys/Identity/Services/Implements/TenantService.cs b/Src/Dlhis.Sys/Identity/Services/Implements/TenantService.cs index 441bd744..ecb8795b 100644 --- a/Src/Dlhis.Sys/Identity/Services/Implements/TenantService.cs +++ b/Src/Dlhis.Sys/Identity/Services/Implements/TenantService.cs @@ -14,11 +14,9 @@ public class TenantService : ITenantService private readonly IDbContext _dbContext; private readonly ICurrentUser _currentUser; - public TenantService(IDbContext dbContext, IConfiguration configuration, ICurrentUser currentUser) + public TenantService(IDbContext dbContext, ICurrentUser currentUser) { - var identityConnection = configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value; - dbContext.Client.CurrentConnectionConfig.ConnectionString = identityConnection; - _dbContext = dbContext; + _dbContext = dbContext.ChangeDb("identity"); _currentUser = currentUser; } @@ -45,25 +43,77 @@ public class TenantService : ITenantService .FirstAsync(); } + + public async Task SaveTenantAsync(TenantSaveRequest request) + { + if (!request.Id.HasValue) + { + await _dbContext.InsertAsync(new TenantEntity + { + PCode = request.PCode, + Code = request.Code, + Name = request.Name, + }); + } + else + { + var entity = await _dbContext.Queryable().FirstAsync(x => x.Id == request.Id); + entity.ThrowIfNull("基地/租户信息不存在"); + + entity.Code = request.Code; + entity.Name = request.Name; + + await _dbContext.UpdateAsync(entity); + } + } + public async Task> GetMyTenantsAsync() { var userId = _currentUser.GetIntSubjectId(); + var tenantId = _currentUser.GetStringTenantId(); + var myTenants = await _dbContext.Queryable() .LeftJoin((ut, t) => ut.TenantCode == t.Code) .Where(ut => ut.UserId == userId) .Select((ut, t) => new TenantResponse { Id = t.Id, + PCode = t.PCode, Code = t.Code, Name = t.Name, }) .ToListAsync(); - if (myTenants.IsEmpty()) - { - myTenants = await GetListAsync(); - } + var currentTenant = myTenants.First(x => x.Code == tenantId); + var otherTenants = myTenants.Where(x => x.Code != tenantId); - return myTenants; + var result = new List { currentTenant }; + result.AddRange(otherTenants); + return result; + } + + public async Task> GetTreeAsync() + { + var tenants = await _dbContext.Queryable() + .Select() + .ToListAsync(); + + var tops = tenants.Where(x => x.PCode.IsEmpty()).ToList(); + foreach (var item in tops) + { + RecursionGetNext(item, tenants); + } + return tops; + } + + + private void RecursionGetNext(TenantTreeResponse current, List data) + { + var next = data.Where(x => x.PCode == current.Code).ToList(); + current.Next = next; + foreach (var item in next) + { + RecursionGetNext(item, data); + } } } diff --git a/Src/Dlhis.Sys/Identity/Services/Implements/UserService.cs b/Src/Dlhis.Sys/Identity/Services/Implements/UserService.cs index f27e0528..55877e73 100644 --- a/Src/Dlhis.Sys/Identity/Services/Implements/UserService.cs +++ b/Src/Dlhis.Sys/Identity/Services/Implements/UserService.cs @@ -1,5 +1,4 @@ -using System.Configuration; -using Dlhis.Entity.Sys.Entities; +using Dlhis.Entity.Sys.Entities; using Dlhis.Sys.Identity.Request; using Dlhis.Sys.Identity.Response; using Kurisu.AspNetCore.Utils.Extensions; @@ -23,13 +22,19 @@ public class UserService : IUserService public async Task> GetPageAsync(UserPageRequest request) { return await _dbContext.Queryable() + .LeftJoin((x, ur) => x.Id == ur.UserId) + .LeftJoin((x, ur, r) => ur.RoleId == r.Id) .WhereIF(request.Search.IsPresent(), x => x.UserName.Contains(request.Search) || x.NormalizedUserName.Contains(request.Search) || x.Email.Contains(request.Search) || x.NormalizedEmail.Contains(request.Search) || x.PhoneNumber.Contains(request.Search) ) - .Select() + .Select((x, ur, r) => new UserResponse + { + Id = x.Id, + Role = r.Name + }, true) .ToPageListAsync(request); } @@ -44,7 +49,6 @@ public class UserService : IUserService public async Task> GetUsersAsync() { - //todo tenant var users = await _dbContext.Queryable().Select().ToListAsync(); return users; } diff --git a/Src/Dlhis.Sys/Menu/Controllers/MenusController.cs b/Src/Dlhis.Sys/Menu/Controllers/MenusController.cs index 006fb4f1..cf0247f4 100644 --- a/Src/Dlhis.Sys/Menu/Controllers/MenusController.cs +++ b/Src/Dlhis.Sys/Menu/Controllers/MenusController.cs @@ -5,6 +5,7 @@ using Dlhis.Sys.Menu.Services; using Dlhis.Sys.Menu.Response; using Dlhis.Sys.Menu.Request; using Dlhis.Entity.Sys.Enums; +using Kurisu.AspNetCore.DataAccess.SqlSugar.Aop; namespace Dlhis.Sys.Menu.Controllers; @@ -40,6 +41,7 @@ public class MenusController : ControllerBase /// /// [HttpPost] + [Transactional] public async Task Save(SaveMenuRequest request) { await _menuService.SaveMenuAsync(request); diff --git a/Src/Dlhis.Sys/Menu/Services/Implements/MenuService.cs b/Src/Dlhis.Sys/Menu/Services/Implements/MenuService.cs index b6c8601d..9fd30309 100644 --- a/Src/Dlhis.Sys/Menu/Services/Implements/MenuService.cs +++ b/Src/Dlhis.Sys/Menu/Services/Implements/MenuService.cs @@ -41,7 +41,7 @@ public class MenuService : IMenuService return top; } - [Transactional] + public async Task SaveMenuAsync(SaveMenuRequest request) { await CheckAsync(request); diff --git a/Src/Dlhis.Sys/SqlSugarPack.cs b/Src/Dlhis.Sys/SqlSugarPack.cs index 26d783d6..9458f466 100644 --- a/Src/Dlhis.Sys/SqlSugarPack.cs +++ b/Src/Dlhis.Sys/SqlSugarPack.cs @@ -1,5 +1,6 @@ using Kurisu.AspNetCore.Startup; using Kurisu.SqlSugar.Extensions; +using SqlSugar; namespace Dlhis.Sys; @@ -9,6 +10,27 @@ public class SqlSugarPack : BaseAppPack public override void ConfigureServices(IServiceCollection services) { - services.AddSqlSugar(); + services.AddSqlSugar(sp => + { + var configuration = sp.GetService(); + + return new List + { + new ConnectionConfig + { + ConfigId = "identity", + + ConnectionString = configuration.GetSection("SqlSugarOptions:IdentityConnectionString").Value, + DbType = DbType.MySql, + InitKeyType = InitKeyType.Attribute, + IsAutoCloseConnection = true, + + MoreSettings = new ConnMoreSettings + { + DisableNvarchar = true, + } + } + }; + }); } -} +} \ No newline at end of file diff --git a/Src/Dlhis.User/Dlhis.User.csproj b/Src/Dlhis.User/Dlhis.User.csproj index 78c08d85..7934a4d0 100644 --- a/Src/Dlhis.User/Dlhis.User.csproj +++ b/Src/Dlhis.User/Dlhis.User.csproj @@ -17,7 +17,6 @@ - - +