using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using VOL.Business.IServices.Device; using VOL.Core.EFDbContext; using VOL.Core.Extensions.AutofacManager; using VOL.Core.ManageUser; using VOL.Entity.DomainModels; using VOL.Entity.DomainModels.XinWei; using VOL.Entity.DomainModels.YiLu; using VOL.Entity.Enum; using VOL.Model; using VOL.Model.Device.Request; using VOL.Model.Device.Response; namespace VOL.Business.Services.Device { /// /// 学校设备管理服务。 /// public class DeviceManageService : IDeviceManageService, IDependency { private readonly VOLContext _dbContext; public DeviceManageService(VOLContext dbContext) { _dbContext = dbContext; } public async Task> GetPageList(DevicePageListParam param) { EnsureSourceType(param.SourceType); var devices = new List(); if (!param.SourceType.HasValue || param.SourceType == DeviceSourceType.AiDevice) devices.AddRange(await QueryAiDevices(param)); if (!param.SourceType.HasValue || param.SourceType == DeviceSourceType.YiLuDevice) devices.AddRange(await QueryYiLuDevices(param)); if (!param.SourceType.HasValue || param.SourceType == DeviceSourceType.XinWeiDevice) devices.AddRange(await QueryXinWeiDevices(param)); var orderedDevices = devices .OrderByDescending(x => x.UpdateTime ?? x.CreateTime) .ThenByDescending(x => x.Id) .ToList(); var total = orderedDevices.Count; return new PageDataDto { Total = total, TotalPages = (int)Math.Ceiling(total / (double)param.PageSize), PageIndex = param.PageIndex, PageSize = param.PageSize, Datas = orderedDevices .Skip((param.PageIndex - 1) * param.PageSize) .Take(param.PageSize) .ToList() }; } public async Task> GetSchoolOptions(string keyword) { var query = GetAllowedSchools(); if (!string.IsNullOrWhiteSpace(keyword)) { keyword = keyword.Trim(); query = query.Where(x => x.SchoolName.Contains(keyword) || x.SchoolCode.Contains(keyword)); } return await query .OrderBy(x => x.SchoolName) .Select(x => new DeviceSchoolOption { OrgId = x.Id, OrgName = x.SchoolName, SchoolCode = x.SchoolCode }) .ToListAsync(); } public async Task Details(long id, DeviceSourceType sourceType) { EnsureSourceType(sourceType); DeviceManageModel result; switch (sourceType) { case DeviceSourceType.AiDevice: result = (await QueryAiDevices(new DevicePageListParam { PageIndex = 1, PageSize = 1 })).FirstOrDefault(x => x.Id == id); break; case DeviceSourceType.YiLuDevice: result = (await QueryYiLuDevices(new DevicePageListParam { PageIndex = 1, PageSize = 1 })).FirstOrDefault(x => x.Id == id); break; default: result = (await QueryXinWeiDevices(new DevicePageListParam { PageIndex = 1, PageSize = 1 })).FirstOrDefault(x => x.Id == id); break; } return result ?? throw new Exception("设备不存在或当前账号无权访问"); } public async Task Add(SaveDeviceParam param) { NormalizeAndValidate(param); var school = await GetAllowedSchool(param.OrgId); switch (param.SourceType) { case DeviceSourceType.AiDevice: await AddAiDevice(param, school); break; case DeviceSourceType.YiLuDevice: await AddYiLuDevice(param, school); break; case DeviceSourceType.XinWeiDevice: await AddXinWeiDevice(param, school); break; default: throw new Exception("设备类型无效"); } } public async Task Update(SaveDeviceParam param) { NormalizeAndValidate(param); if (!param.Id.HasValue || param.Id <= 0) throw new Exception("设备Id不能为空"); var school = await GetAllowedSchool(param.OrgId); switch (param.SourceType) { case DeviceSourceType.AiDevice: await UpdateAiDevice(param, school); break; case DeviceSourceType.YiLuDevice: await UpdateYiLuDevice(param, school); break; case DeviceSourceType.XinWeiDevice: await UpdateXinWeiDevice(param, school); break; default: throw new Exception("设备类型无效"); } } public async Task Delete(DeleteDeviceParam param) { EnsureSourceType(param.SourceType); if (param.Id <= 0) throw new Exception("设备Id无效"); try { switch (param.SourceType) { case DeviceSourceType.AiDevice: await DeleteAiDevice(param.Id); break; case DeviceSourceType.YiLuDevice: await DeleteYiLuDevice(param.Id); break; case DeviceSourceType.XinWeiDevice: await DeleteXinWeiDevice(param.Id); break; } } catch (DbUpdateException) { throw new Exception("该设备已产生关联业务数据,暂时无法删除"); } } private IQueryable GetAllowedSchools() { var query = _dbContext.Set().AsNoTracking(); if (!UserContext.Current.IsSuperAdmin) { var tenantId = UserContext.Current.TenantId; query = query.Where(x => x.SchoolCode == tenantId); } return query; } private async Task GetAllowedSchool(int orgId) { var school = await GetAllowedSchools().FirstOrDefaultAsync(x => x.Id == orgId); return school ?? throw new Exception("学校不存在或当前账号无权操作"); } private async Task> QueryAiDevices(DevicePageListParam param) { var allowedSchoolIds = GetAllowedSchools().Select(x => x.Id); var query = from device in _dbContext.Set().AsNoTracking() join association in _dbContext.Set().AsNoTracking() on device.Code equals association.Code into associationGroup from association in associationGroup.DefaultIfEmpty() join school in _dbContext.Set().AsNoTracking() on association.SchoolCode equals school.SchoolCode into schoolGroup from school in schoolGroup.DefaultIfEmpty() where UserContext.Current.IsSuperAdmin || (school != null && allowedSchoolIds.Contains(school.Id)) select new DeviceManageModel { Id = device.Id, SourceType = DeviceSourceType.AiDevice, SourceTypeName = "一体机设备", OrgId = school == null ? null : (int?)school.Id, OrgName = school == null ? null : school.SchoolName, SerialNumber = device.Code, TenantName = device.Name, DeviceType = device.DeviceType.ToString(), CreateTime = device.CreateDate, UpdateTime = device.ModifyDate }; query = ApplyFilters(query, param); return await query.ToListAsync(); } private async Task> QueryYiLuDevices(DevicePageListParam param) { var allowedSchoolIds = GetAllowedSchools().Select(x => x.Id); var query = from device in _dbContext.Set().AsNoTracking() join school in _dbContext.Set().AsNoTracking() on device.OrgId equals school.Id into schoolGroup from school in schoolGroup.DefaultIfEmpty() where UserContext.Current.IsSuperAdmin || (device.OrgId.HasValue && allowedSchoolIds.Contains(device.OrgId.Value)) select new DeviceManageModel { Id = device.Id, SourceType = DeviceSourceType.YiLuDevice, SourceTypeName = "壹路设备", OrgId = device.OrgId, OrgName = school == null ? null : school.SchoolName, SerialNumber = device.SerialNumber, TenantName = device.TenantName ?? device.Name, CreateTime = device.CreateTime, UpdateTime = device.UpdateTime }; query = ApplyFilters(query, param); return await query.ToListAsync(); } private async Task> QueryXinWeiDevices(DevicePageListParam param) { var allowedSchoolIds = GetAllowedSchools().Select(x => x.Id); var query = _dbContext.Set() .AsNoTracking() .Where(x => UserContext.Current.IsSuperAdmin || (x.OrgId.HasValue && allowedSchoolIds.Contains(x.OrgId.Value))); if (param.OrgId.HasValue && param.OrgId > 0) query = query.Where(x => x.OrgId == param.OrgId); if (!string.IsNullOrWhiteSpace(param.Keyword)) { var keyword = param.Keyword.Trim(); query = query.Where(x => (x.DeviceSerial != null && x.DeviceSerial.Contains(keyword)) || (x.Remark != null && x.Remark.Contains(keyword)) || (x.OrgName != null && x.OrgName.Contains(keyword)) || (x.CameraIp != null && x.CameraIp.Contains(keyword))); } var rows = await query .Select(x => new { x.Id, x.OrgId, x.OrgName, x.DeviceSerial, x.Remark, x.CameraIp, x.Eai100Flag, x.DeviceType }) .ToListAsync(); return rows.Select(x => new DeviceManageModel { Id = x.Id, SourceType = DeviceSourceType.XinWeiDevice, SourceTypeName = "芯未设备", OrgId = x.OrgId, OrgName = x.OrgName, SerialNumber = x.DeviceSerial, TenantName = x.Remark, CameraIp = x.CameraIp, Eai100Flag = x.Eai100Flag, DeviceType = x.DeviceType }).ToList(); } private static IQueryable ApplyFilters( IQueryable query, DevicePageListParam param) { if (param.OrgId.HasValue && param.OrgId > 0) query = query.Where(x => x.OrgId == param.OrgId); if (!string.IsNullOrWhiteSpace(param.Keyword)) { var keyword = param.Keyword.Trim(); query = query.Where(x => (x.SerialNumber != null && x.SerialNumber.Contains(keyword)) || (x.TenantName != null && x.TenantName.Contains(keyword)) || (x.OrgName != null && x.OrgName.Contains(keyword)) || (x.CameraIp != null && x.CameraIp.Contains(keyword))); } return query; } private async Task AddAiDevice(SaveDeviceParam param, S_School school) { if (await _dbContext.Set().AnyAsync(x => x.Code == param.SerialNumber)) throw new Exception($"设备编号{param.SerialNumber}已存在"); var now = DateTime.Now; await using var transaction = await _dbContext.Database.BeginTransactionAsync(); _dbContext.Set().Add(new Ai_Device { Code = param.SerialNumber, Name = param.TenantName, DeviceType = 1, Status = 1, StartTime = now, EndTime = new DateTime(2099, 12, 31), Creator = UserContext.Current.UserId, CreateDate = now, Modifier = 0 }); _dbContext.Set().Add(new Ai_DeviceAssocSchool { Code = param.SerialNumber, SchoolCode = school.SchoolCode }); await _dbContext.SaveChangesAsync(); await transaction.CommitAsync(); } private async Task AddYiLuDevice(SaveDeviceParam param, S_School school) { if (await _dbContext.Set().AnyAsync(x => x.SerialNumber == param.SerialNumber)) throw new Exception($"设备编号{param.SerialNumber}已存在"); var now = DateTime.Now; _dbContext.Set().Add(new YL_Device { SportStationId = 0, OrgId = school.Id, Name = param.TenantName, TenantName = param.TenantName, SerialNumber = param.SerialNumber, IsCalibration = 0, IsSynchronization = 0, IsActive = 0, IsOnline = 0, CreateTime = now }); await _dbContext.SaveChangesAsync(); } private async Task AddXinWeiDevice(SaveDeviceParam param, S_School school) { if (await _dbContext.Set().AnyAsync(x => x.DeviceSerial == param.SerialNumber)) throw new Exception($"设备编号{param.SerialNumber}已存在"); var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); _dbContext.Set().Add(new XW_Device { Type = 1, DeviceType = "1", DeviceSerial = param.SerialNumber, CameraIp = param.CameraIp, Eai100Flag = param.Eai100Flag ?? false, OrgId = school.Id, OrgName = school.SchoolName, Remark = param.TenantName, State = 1, Status = 1, CreateTime = now, CreateBy = UserContext.Current.UserInfo.UserName, SimilarNumber = 80, CameraInfo = string.Empty }); await _dbContext.SaveChangesAsync(); } private async Task UpdateAiDevice(SaveDeviceParam param, S_School school) { var id = ToIntId(param.Id.Value); var device = await _dbContext.Set() .AsTracking() .FirstOrDefaultAsync(x => x.Id == id); if (device == null) throw new Exception("一体机设备不存在"); await EnsureAiDevicePermission(device.Code); if (await _dbContext.Set() .AnyAsync(x => x.Code == param.SerialNumber && x.Id != id)) throw new Exception($"设备编号{param.SerialNumber}已存在"); await using var transaction = await _dbContext.Database.BeginTransactionAsync(); var association = await _dbContext.Set() .AsTracking() .FirstOrDefaultAsync(x => x.Code == device.Code); device.Code = param.SerialNumber; device.Name = param.TenantName; device.Modifier = UserContext.Current.UserId; device.ModifyDate = DateTime.Now; if (association == null) { association = new Ai_DeviceAssocSchool { Code = param.SerialNumber }; _dbContext.Set().Add(association); } association.Code = param.SerialNumber; association.SchoolCode = school.SchoolCode; await _dbContext.SaveChangesAsync(); await transaction.CommitAsync(); } private async Task UpdateYiLuDevice(SaveDeviceParam param, S_School school) { var device = await _dbContext.Set() .AsTracking() .FirstOrDefaultAsync(x => x.Id == param.Id.Value); if (device == null) throw new Exception("壹路设备不存在"); await EnsureOrgPermission(device.OrgId); if (await _dbContext.Set() .AnyAsync(x => x.SerialNumber == param.SerialNumber && x.Id != param.Id.Value)) throw new Exception($"设备编号{param.SerialNumber}已存在"); device.OrgId = school.Id; device.Name = param.TenantName; device.TenantName = param.TenantName; device.SerialNumber = param.SerialNumber; device.UpdateTime = DateTime.Now; await _dbContext.SaveChangesAsync(); } private async Task UpdateXinWeiDevice(SaveDeviceParam param, S_School school) { var id = ToIntId(param.Id.Value); var device = await _dbContext.Set() .AsNoTracking() .Where(x => x.Id == id) .Select(x => new { x.Id, x.OrgId }) .FirstOrDefaultAsync(); if (device == null) throw new Exception("芯未设备不存在"); await EnsureOrgPermission(device.OrgId); if (await _dbContext.Set() .AnyAsync(x => x.DeviceSerial == param.SerialNumber && x.Id != id)) throw new Exception($"设备编号{param.SerialNumber}已存在"); var deviceType = "1"; var eai100Flag = param.Eai100Flag ?? false; var updateTime = DateTime.Now; var updateBy = UserContext.Current.UserInfo.UserName; await _dbContext.Database.ExecuteSqlInterpolatedAsync($@" UPDATE XW_Device SET DeviceType = {deviceType}, DeviceSerial = {param.SerialNumber}, CameraIp = {param.CameraIp}, Eai100Flag = {eai100Flag}, OrgId = {school.Id}, OrgName = {school.SchoolName}, Remark = {param.TenantName}, UpdateTime = {updateTime}, UpdateBy = {updateBy} WHERE Id = {id}"); } private async Task DeleteAiDevice(long deviceId) { var id = ToIntId(deviceId); var device = await _dbContext.Set() .AsTracking() .FirstOrDefaultAsync(x => x.Id == id); if (device == null) throw new Exception("一体机设备不存在"); await EnsureAiDevicePermission(device.Code); await using var transaction = await _dbContext.Database.BeginTransactionAsync(); var associations = await _dbContext.Set() .AsTracking() .Where(x => x.Code == device.Code) .ToListAsync(); _dbContext.Set().RemoveRange(associations); _dbContext.Set().Remove(device); await _dbContext.SaveChangesAsync(); await transaction.CommitAsync(); } private async Task DeleteYiLuDevice(long deviceId) { var device = await _dbContext.Set() .AsTracking() .FirstOrDefaultAsync(x => x.Id == deviceId); if (device == null) throw new Exception("壹路设备不存在"); await EnsureOrgPermission(device.OrgId); _dbContext.Set().Remove(device); await _dbContext.SaveChangesAsync(); } private async Task DeleteXinWeiDevice(long deviceId) { var id = ToIntId(deviceId); var device = await _dbContext.Set() .AsNoTracking() .Where(x => x.Id == id) .Select(x => new { x.Id, x.OrgId }) .FirstOrDefaultAsync(); if (device == null) throw new Exception("芯未设备不存在"); await EnsureOrgPermission(device.OrgId); await _dbContext.Database.ExecuteSqlInterpolatedAsync( $"DELETE FROM XW_Device WHERE Id = {id}"); } private async Task EnsureAiDevicePermission(string serialNumber) { if (UserContext.Current.IsSuperAdmin) return; var tenantId = UserContext.Current.TenantId; var allowed = await _dbContext.Set() .AnyAsync(x => x.Code == serialNumber && x.SchoolCode == tenantId); if (!allowed) throw new Exception("当前账号无权操作该设备"); } private async Task EnsureOrgPermission(int? orgId) { if (UserContext.Current.IsSuperAdmin) return; if (!orgId.HasValue || !await GetAllowedSchools().AnyAsync(x => x.Id == orgId.Value)) throw new Exception("当前账号无权操作该设备"); } private static void NormalizeAndValidate(SaveDeviceParam param) { if (param == null) throw new Exception("设备参数不能为空"); EnsureSourceType(param.SourceType); param.OrgName = param.OrgName?.Trim(); param.SerialNumber = param.SerialNumber?.Trim(); param.TenantName = param.TenantName?.Trim(); param.CameraIp = param.CameraIp?.Trim(); if (param.OrgId <= 0) throw new Exception("请选择学校"); if (string.IsNullOrWhiteSpace(param.OrgName)) throw new Exception("学校名称不能为空"); if (string.IsNullOrWhiteSpace(param.SerialNumber)) throw new Exception("设备编号不能为空"); if (string.IsNullOrWhiteSpace(param.TenantName)) throw new Exception("设备名称不能为空"); if (param.SourceType == DeviceSourceType.YiLuDevice && param.SerialNumber.Length > 64) throw new Exception("壹路设备编号不能超过64个字符"); if (param.SourceType != DeviceSourceType.XinWeiDevice) { param.CameraIp = null; param.Eai100Flag = null; } } private static void EnsureSourceType(DeviceSourceType? sourceType) { if (sourceType.HasValue && !Enum.IsDefined(typeof(DeviceSourceType), sourceType.Value)) throw new Exception("设备类型无效"); } private static int ToIntId(long id) { if (id <= 0 || id > int.MaxValue) throw new Exception("设备Id无效"); return (int)id; } } }