From 59dafeee40a3421a24c740e5ae7bd167ce4aebdb Mon Sep 17 00:00:00 2001 From: liguoyang Date: Fri, 13 Jun 2025 20:00:48 +0800 Subject: [PATCH] =?UTF-8?q?fix=20=E6=B3=A8=E9=87=8A+=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E8=84=B1=E6=95=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/Implements/AppointmentService.cs | 23 ++++-- .../IDevicePermissionManuallyService.cs | 40 +++++---- .../DevicePermissionManuallyService.cs | 21 ++++- .../Services/Implements/DeviceService.cs | 81 +++++++++++++++++-- .../Services/Implements/GainfoService.cs | 7 +- 5 files changed, 139 insertions(+), 33 deletions(-) diff --git a/Src/Dlhis.Basic/Appointment/Services/Implements/AppointmentService.cs b/Src/Dlhis.Basic/Appointment/Services/Implements/AppointmentService.cs index ea7b561d..bf76d40e 100644 --- a/Src/Dlhis.Basic/Appointment/Services/Implements/AppointmentService.cs +++ b/Src/Dlhis.Basic/Appointment/Services/Implements/AppointmentService.cs @@ -182,25 +182,31 @@ public class AppointmentService : IAppointmentService public async Task FocusLeaveAsync(long id, string reason) { + // 根据访客预约详情ID获取记录,若不存在则抛出异常 var detail = await _dbContext.Queryable().Where(x => x.Id == id).SingleAsync(); detail.ThrowIfNull("数据不存在"); - - //Wait->obsolete; Arrived->FocusLeave + + // 如果已进入门区且门区状态为“已到达”,则将门区状态更新为“强制离场” if (detail.IsEnterDoorArea && detail.DoorStatus == VisitorAppointmentDetailStatusEnumType.Arrived) detail.DoorStatus = VisitorAppointmentDetailStatusEnumType.FocusLeave; - + + // 如果已进入门区且门区状态为“待到达”,则将门区状态更新为“作废” if (detail.IsEnterDoorArea && detail.DoorStatus == VisitorAppointmentDetailStatusEnumType.Wait) detail.DoorStatus = VisitorAppointmentDetailStatusEnumType.Obsolete; - + + // 如果已进入工作区且工作区状态为“已到达”,则将工作区状态更新为“强制离场” if (detail.IsEnterWorkplace && detail.WorkplaceStatus == VisitorAppointmentDetailStatusEnumType.Arrived) detail.WorkplaceStatus = VisitorAppointmentDetailStatusEnumType.FocusLeave; - + + // 如果已进入工作区且工作区状态为“待到达”,则将工作区状态更新为“作废” if (detail.IsEnterWorkplace && detail.WorkplaceStatus == VisitorAppointmentDetailStatusEnumType.Wait) detail.WorkplaceStatus = VisitorAppointmentDetailStatusEnumType.Obsolete; - + + // 记录强制离场时间和原因 detail.FocusLeaveDateTime = DateTime.Now; detail.Remark += "强制离场:" + reason; - + + // 生成设备任务并更新数据库 await GenerateDeviceTasksAsync(detail, true, DeviceTaskEnumType.Delete, $"强制离场:{reason}"); await _dbContext.UpdateAsync(detail); } @@ -370,12 +376,14 @@ public class AppointmentService : IAppointmentService foreach (var ext in exts) { + // 查询该外部人员最近一次通行记录的时间 DateTime? raiseDateTime = await _dbContext.Queryable() .Where(x => x.UserUniqueNo == ext.Code) .GroupBy(x => x.UserUniqueNo) .Select(x => SqlFunc.IsNull(SqlFunc.AggregateMax(x.RaiseDateTime), default(DateTime?))) .SingleAsync(); + // 如果最近通行时间存在且超出未通行天数阈值,则自动归档该外部人员 if (raiseDateTime.HasValue && raiseDateTime.Value.AddDays(setting.NoRecordAutoResignExternalDays) < DateTime.Now) { try @@ -389,6 +397,7 @@ public class AppointmentService : IAppointmentService } catch (Exception e) { + // 记录自动归档异常日志 _logger.LogError(e, "自动归档外部人员异常:{msg}", e.Message); } } diff --git a/Src/Dlhis.Basic/Device/Services/IDevicePermissionManuallyService.cs b/Src/Dlhis.Basic/Device/Services/IDevicePermissionManuallyService.cs index 043f236f..a28a6131 100644 --- a/Src/Dlhis.Basic/Device/Services/IDevicePermissionManuallyService.cs +++ b/Src/Dlhis.Basic/Device/Services/IDevicePermissionManuallyService.cs @@ -3,39 +3,51 @@ using Dlhis.Basic.Device.Response; using Kurisu.AspNetCore.DataAccess.Dto; namespace Dlhis.Basic.Device.Services; - /// -/// 例外人员权限分配 +/// 例外人员权限分配服务接口,定义了对设备权限进行手动分配、查询及清理的相关操作。 /// public interface IDevicePermissionManuallyService : IScopeDependency { /// - /// 获取例外分配分页 + /// 获取例外分配的分页数据。 /// - /// - /// + /// 分页及筛选请求参数。 + /// 包含例外分配信息的分页结果。 Task> GetPageAsync(DevicePermissionManuallyRequest requestDto); /// - /// 添加例外 + /// 添加新的例外分配记录。 /// - /// - /// + /// 例外分配添加请求参数。 + /// 异步操作任务。 Task AddAsync(DevicePermissionManuallyAddRequest request); /// - /// 删除例外 + /// 根据主键删除指定的例外分配记录。 /// - /// - /// + /// 要删除的记录主键ID。 + /// 异步操作任务。 Task DeleteAsync(long id); /// - /// 批量删除 + /// 批量删除例外分配记录。 /// - /// - /// + /// 要删除的记录主键ID集合。 + /// 异步操作任务。 Task DeleteAsync(List ids); + /// + /// 清除指定用户的所有设备例外权限。 + /// + /// 用户编号。 + /// 异步操作任务。 + Task ClearUserDeviceManuallyPermissionsAsync(string userNo); + + /// + /// 清除指定工厂下指定用户的所有设备权限。 + /// + /// 工厂编码。 + /// 用户编码。 + /// 异步操作任务。 Task ClearAllDevicePermissions(string factory, string userCode); } \ No newline at end of file diff --git a/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionManuallyService.cs b/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionManuallyService.cs index aab8a62b..a2c3e0c6 100644 --- a/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionManuallyService.cs +++ b/Src/Dlhis.Basic/Device/Services/Implements/DevicePermissionManuallyService.cs @@ -42,7 +42,6 @@ public class DevicePermissionManuallyService : IDevicePermissionManuallyService Type = d.Type }); - _logger.LogInformation(query.ToSqlString()); return await query.ToPageListAsync(requestDto); } @@ -88,6 +87,25 @@ public class DevicePermissionManuallyService : IDevicePermissionManuallyService public async Task DeleteAsync(List ids) { var data = await _dbContext.Queryable().Where(x => ids.Contains(x.Id)).ToListAsync(); + await DeleteAsync(data); + } + + + public async Task ClearUserDeviceManuallyPermissionsAsync(string userNo) + { + var userInfo = await _dbContext.Queryable().Where(x => x.No == userNo).Select(x => new { x.Id, x.No }).SingleAsync() + ?? await _dbContext.Queryable().Where(x => x.No == userNo).Select(x => new { Id = (long)x.IdentityUserId, x.No }).SingleAsync(); + userInfo.ThrowIfNull($"{userNo}不存在"); + var data = await _dbContext.Queryable().Where(x => x.UserNo == userInfo.No).ToListAsync(); + + if (data.Count > 0) + { + await DeleteAsync(data); + } + } + + private async Task DeleteAsync(List data) + { var deviceCodes = data.Select(x => x.DeviceCode).Distinct().ToList(); var devices = await _dbContext.Queryable().Where(x => deviceCodes.Contains(x.Code)).ToListAsync(); @@ -122,6 +140,7 @@ public class DevicePermissionManuallyService : IDevicePermissionManuallyService } } + /// public async Task ClearAllDevicePermissions(string factory, string userCode) { diff --git a/Src/Dlhis.Basic/Device/Services/Implements/DeviceService.cs b/Src/Dlhis.Basic/Device/Services/Implements/DeviceService.cs index b0aa3211..1631c102 100644 --- a/Src/Dlhis.Basic/Device/Services/Implements/DeviceService.cs +++ b/Src/Dlhis.Basic/Device/Services/Implements/DeviceService.cs @@ -14,12 +14,14 @@ using Kurisu.AspNetCore.Utils.Extensions; using Mapster; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using SqlSugar; namespace Dlhis.Basic.Device.Services.Implements; public class DeviceService : IDeviceService { + private const string DefaultPassword = "********"; + private const string DefaultPasswordKey = "password"; + private readonly IDbContext _dbContext; private readonly ICurrentUser _currentUser; private readonly ITenantApi _tenantApi; @@ -55,6 +57,26 @@ public class DeviceService : IDeviceService }, true) .ToPageListAsync(request); + //获取当前用户角色 + var role = _currentUser.GetRole(); + + //如果当前用户不是管理员或租户管理员,则对设备列表中的连接信息进行脱敏处理,将密码字段替换为默认密码,防止敏感信息泄露。 + if (role is not ("admin" or "tenant-admin")) + { + foreach (var item in page.Data) + { + var jObj = JObject.Parse(item.ConnectionInfo); + if (jObj.TryGetValue(DefaultPasswordKey, out var password)) + { + if (password.Value().IsPresent()) + { + jObj[DefaultPasswordKey] = DefaultPassword; + item.ConnectionInfo = jObj.ToString(); + } + } + } + } + return page; } @@ -71,6 +93,12 @@ public class DeviceService : IDeviceService if (!request.Id.HasValue) { + var password = GetJsonValue(request.ConnectionInfo, DefaultPasswordKey); + if (password.IsPresent()) + { + (password == DefaultPassword).ThrowIfTrue("不支持错误的密码类型"); + } + entity = request.Adapt(); entity.UniqueNo = request.UniqueNo; entity.Code = Guid.NewGuid(); @@ -106,6 +134,21 @@ public class DeviceService : IDeviceService existsG.ThrowIfTrue("存在设备分组,不可修改设备类型"); } + //检查更新的密码字段是否为默认密码,如果是,则将原有密码从数据库实体中取出并替换到请求的连接信息中,防止默认密码被保存。 + // 这样可以避免用户在未修改密码时,前端传递默认密码导致密码被覆盖为不可用的默认值。 + var updatePassword = GetJsonValue(request.ConnectionInfo, DefaultPasswordKey); + if (updatePassword.IsPresent()) + { + if (updatePassword == DefaultPassword) + { + var jObj = JObject.Parse(request.ConnectionInfo); + var password = GetJsonValue(entity.ConnectionInfo, DefaultPasswordKey); + + jObj[DefaultPasswordKey] = password; + request.ConnectionInfo = jObj.ToString(); + } + } + request.Adapt(entity); await _dbContext.UpdateAsync(entity); @@ -128,6 +171,28 @@ public class DeviceService : IDeviceService }, "设备启用/关闭时段码功能"); } + /// + /// 获取json的字段值 + /// + /// + /// + /// + /// + private static string GetJsonValue(string json, string key) + { + try + { + var jObj = JObject.Parse(json); + return jObj.TryGetValue(key, out var value) + ? value.Value() + : string.Empty; + } + catch (JsonReaderException) + { + throw new UserFriendlyException("连接信息参数格式不正确"); + } + } + /// /// 批量导入 /// @@ -141,7 +206,7 @@ public class DeviceService : IDeviceService await _dbContext.Client.Ado.BeginTranAsync(); try { - for (int i = 0; i < request.Count; i++) + for (var i = 0; i < request.Count; i++) { var row = i + 1; try @@ -218,18 +283,18 @@ public class DeviceService : IDeviceService /// private async Task CheckTemplateFieldAsync(Guid templateCode, string jsonContent) { - var template = await _dbContext.Queryable().Where(x => x.Code == templateCode).FirstAsync(); + var template = await _dbContext.Queryable().Where(x => x.Code == templateCode).SingleAsync(); template.ThrowIfNull("设备模板不存在"); try { - var templateJobj = JObject.Parse(template.Template); - var jobj = JObject.Parse(jsonContent); - (templateJobj.Count == jobj.Count).ThrowIfFalse("连接参数字段数不一致"); + var templateJObj = JObject.Parse(template.Template); + var jObj = JObject.Parse(jsonContent); + (templateJObj.Count == jObj.Count).ThrowIfFalse("连接参数字段数不一致"); - foreach (var item in templateJobj) + foreach (var item in templateJObj) { - jobj.ContainsKey(item.Key).ThrowIfFalse($"设备连接参数需要和模板一致.缺失{item.Key}"); + jObj.ContainsKey(item.Key).ThrowIfFalse($"设备连接参数需要和模板一致.缺失{item.Key}"); } } catch (Exception ex) when (ex is JsonReaderException) diff --git a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs index a72862b5..b824e883 100644 --- a/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs +++ b/Src/Dlhis.Basic/Organization/Services/Implements/GainfoService.cs @@ -267,13 +267,14 @@ public class GainfoService : IGainfoService if (gainfo.Type == GainfoUserTypeEnumType.External) { - //获取人员所有的访客申请 + // 查询当前外部人员的访客预约明细,筛选出尚未离开门区或工作区的记录 var visitorAppointmentDetails = await _dbContext.Queryable() .Where(x => x.UserNo == gainfo.No) .Where(x => (x.IsEnterDoorArea && x.DoorStatus != VisitorAppointmentDetailStatusEnumType.Leave) || (x.IsEnterWorkplace && x.WorkplaceStatus != VisitorAppointmentDetailStatusEnumType.Leave)) .ToListAsync(); - + + // 对每条未离开的访客预约明细,调用预约服务进行强制离开处理 foreach (var visitorAppointment in visitorAppointmentDetails) { await _appointmentService.FocusLeaveAsync(visitorAppointment.Id, request.Reason); @@ -286,7 +287,7 @@ public class GainfoService : IGainfoService await _dbContext.Deleteable().Where(x => x.UserNo == gainfo.No).ExecuteCommandAsync(); await _dbContext.Deleteable().Where(x => x.AssignType == AssignTypeEnumType.User && x.AssignValue == gainfo.No).ExecuteCommandAsync(); } - + //先写入离职表 gainfo.IdentityUserId = (int)gainfo.Id; //记录原始id gainfo.ResignationDate = request.Date;