using Dlhis.Basic.Appointment.Request; using Dlhis.Basic.Appointment.Response; using Dlhis.Basic.Organization.Request; using Dlhis.Basic.Organization.Services; using Dlhis.Dh.Api; using Dlhis.Dh.Api.Requests; using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Device.Entities; using Dlhis.Entity.Device.Enums; using Dlhis.Entity.Visitor.Entity; using Dlhis.Entity.Visitor.Enums; using Kurisu.AspNetCore.Authentication.Abstractions; using Kurisu.AspNetCore.CustomClass; using Kurisu.AspNetCore.DataAccess.Dto; using Kurisu.AspNetCore.DataAccess.SqlSugar.Extensions; using Kurisu.AspNetCore.DataAccess.SqlSugar.Services; using Kurisu.AspNetCore.Utils.Extensions; using Microsoft.AspNetCore.DataProtection; using SqlSugar; namespace Dlhis.Basic.Appointment.Services.Implements; public class AppointmentService : IAppointmentService { private readonly IDbContext _dbContext; private readonly IDataProtectionProvider _dataProtectionProvider; private readonly ICurrentUser _currentUser; private readonly IDhApi _dhApi; private readonly ILogger _logger; private readonly IGainfoFaceService _gainfoFaceService; public AppointmentService(IDbContext dbContext, IDataProtectionProvider dataProtectionProvider, ICurrentUser currentUser, IDhApi dhApi, IGainfoFaceService gainfoFaceService, ILogger logger) { _dbContext = dbContext; _dataProtectionProvider = dataProtectionProvider; _currentUser = currentUser; _dhApi = dhApi; _logger = logger; _gainfoFaceService = gainfoFaceService; } public async Task> GetPageAsync(AppointmentPageRequest request) { var factory = _currentUser.GetStringTenantId(); return await _dbContext.Queryable() .WhereIF(request.SearchDateTimePeriod.HasValue(), x => x.Date >= request.SearchDateTimePeriod.Start && x.Date <= request.SearchDateTimePeriod.End) .WhereIF(request.Search.IsPresent(), x => x.Code == request.Search || SqlFunc.Subqueryable() .Where(p => p.No == x.No) .Where(p => p.IdNo == request.Search || p.Name == request.Search).Any() ) .Where(x => x.Factory == factory) .Select() .ToPageListAsync(request); } public async Task> GetAppointmentDetailsByIdAsync(string no) { return await _dbContext.Queryable() .Where(x => x.No == no) .Select(x => new AppointmentDetailResponse { Id = x.Id, No = x.No, UserNo = x.UserNo, IdType = x.IdType, IdNo = x.IdNo, Name = x.Name, Phone = x.Phone, LicensePlate = x.LicensePlate, IsEnterDoorArea = x.IsEnterDoorArea, ApplyDoorArea = SqlFunc.Subqueryable().Where(p => p.CustomCode == x.ApplyDoorArea).Select(p => p.Name), DoorStart = x.DoorStart, DoorEnd = x.DoorEnd, IsEnterWorkplace = x.IsEnterWorkplace, ApplyWorkplaceAreas = SqlFunc.Subqueryable().Where(p => x.ApplyWorkplaceAreas.ToString().Contains(p.CustomCode)).ToList(p => p.Name), WorkplaceStart = x.WorkplaceStart, WorkplaceEnd = x.WorkplaceEnd, ImageUrl = x.ImageUrl, Remark = x.Remark, DoorStatus = x.DoorStatus, WorkplaceStatus = x.WorkplaceStatus, }) .ToListAsync(); } public async Task ConfirmAsync(AppointmentConfirmRequest request) { var detail = await _dbContext.Queryable().Where(x => x.Id == request.Id).SingleAsync(); detail.ThrowIfNull("预约信息不存在"); if (detail.DoorStatus == VisitorAppointmentDetailStatusEnumType.Wait) { detail.DoorStatus = VisitorAppointmentDetailStatusEnumType.Arrived; } else { true.ThrowIfTrue($"数据有误:{detail.DoorStatus.GetDescription()}"); } detail.ArrivedTime = DateTime.Now; await _dbContext.UpdateAsync(detail); await _gainfoFaceService.UploadExternalFaceAsync(new GainfoUploadFaceRequest { UserNo = detail.IdNo, Face = request.Base64Image }); await GenerateDeviceTasksAsync(detail, false, DeviceTaskEnumType.Add, "访客门岗认证生成"); try { var r = await _dhApi.SendFaceAsync(new FaceRequest { IdNo = detail.IdNo, Face = request.Base64Image }); _logger.LogInformation("上传face数据到大华成功: {idNo} . {json}", detail.IdNo, r.ToJson()); } catch (Exception e) { _logger.LogError(e, "上传face数据到大华失败:{msg}", e.Message); } } public async Task GetTodayByIdNoAsync(string idNo, string factory) { var protector = _dataProtectionProvider.CreateProtector("gainfo:external:idno"); try { idNo = protector.Unprotect(idNo); } catch (Exception) { //ignore } var now = DateTime.Now; return await _dbContext.Queryable() .LeftJoin((x, ga) => ga.IdNo == x.IdNo) .Where((x, ga) => ga.IdNo == idNo) .Where(x => x.IsEnterDoorArea && now >= x.DoorStart && now <= x.DoorEnd && x.DoorStatus == VisitorAppointmentDetailStatusEnumType.Wait) .Where(x => SqlFunc.Subqueryable().Where(p => p.No == x.No).Where(p => p.Factory == factory).Any()) .Select(x => new AppointmentDetailResponse { No = x.No, Mst = SqlFunc.Subqueryable() .LeftJoin((p, ga) => p.Code == ga.Code) .Where((p, ga) => p.No == x.No) .First((p, ga) => new AppointmentPageResponse { No = p.No, Title = p.Title, Code = p.Code, Name = ga.Name, Source = p.Source, Date = p.Date, Remark = p.Remark, Factory = p.Factory, Reason = p.Reason }, false) }, true) .FirstAsync(); } public async Task FocusLeaveAsync(long id, string reason) { var detail = await _dbContext.Queryable().Where(x => x.Id == id).SingleAsync(); detail.ThrowIfNull("数据不存在"); if (detail.IsEnterDoorArea && detail.DoorStatus == VisitorAppointmentDetailStatusEnumType.Arrived) { detail.DoorStatus = VisitorAppointmentDetailStatusEnumType.FocusLeave; } if (detail.IsEnterWorkplace && detail.WorkplaceStatus == VisitorAppointmentDetailStatusEnumType.Arrived) { detail.WorkplaceStatus = VisitorAppointmentDetailStatusEnumType.FocusLeave; } detail.FocusLeaveDateTime = DateTime.Now; detail.Remark += "强制离场原因:" + reason; await GenerateDeviceTasksAsync(detail, true, DeviceTaskEnumType.Delete, "访客手动强制离场"); } public async Task> GetRecordPageByIdNoAsync(AppointmentIdNoPageRequest request) { var protector = _dataProtectionProvider.CreateProtector("gainfo:external:idno"); try { request.IdNo = protector.Unprotect(request.IdNo); } catch (Exception) { //ignore } var page = await _dbContext.Queryable() .LeftJoin((x, ga) => x.UserNo == ga.No) .Where((x, ga) => ga.IdNo == request.IdNo) .OrderBy(x => x.CreateTime) .Select() .ToPageListAsync(request); return page; } public async Task> GetRecordListAsync(string factory, string search, VisitorAppointmentDetailStatusEnumType? status, DateTime start, DateTime end) { var now = DateTime.Now; var currentDateTime = start.Date.AddHours(now.Hour).AddMinutes(now.Minute); return await _dbContext.Queryable() .LeftJoin((x, ga) => ga.IdNo == x.IdNo) .Where(x => SqlFunc.Subqueryable() .Where(p => p.No == x.No) .Where(p => p.Factory == factory).Any() ) .Where(x => x.IsEnterDoorArea) .WhereIF(status.HasValue, x => x.DoorStatus == status) .WhereIF(status.HasValue && status == VisitorAppointmentDetailStatusEnumType.Wait, x => currentDateTime >= x.DoorStart && currentDateTime <= x.DoorEnd) .WhereIF(status.HasValue && status == VisitorAppointmentDetailStatusEnumType.Arrived, x => currentDateTime >= x.DoorStart && currentDateTime <= x.DoorEnd) .WhereIF(!status.HasValue, x => x.DoorStart >= start && x.DoorEnd <= end) .WhereIF(search.IsPresent(), x => x.Name.Contains(search) || x.No == search || x.IdNo == search) .Select(x => new AppointmentDetailResponse { No = x.No, Mst = SqlFunc.Subqueryable() .LeftJoin((p, ga) => p.Code == ga.Code) .Where((p, ga) => p.No == x.No) .First((p, ga) => new AppointmentPageResponse { No = p.No, Title = p.Title, Code = p.Code, Name = ga.Name, Source = p.Source, Date = p.Date, Remark = p.Remark, Factory = p.Factory, Reason = p.Reason }, false) }, true) .ToListAsync(); } public async Task GetSummaryAsync(string factory, DateTime start, DateTime end) { var total = await _dbContext.Queryable() .Where(x => x.IsEnterDoorArea && SqlFunc.DateIsSame(x.DoorStart, start)) .Where(x => SqlFunc.Subqueryable().Where(p => p.No == x.No).Where(p => p.Factory == factory).Any()) .CountAsync(); var @in = await _dbContext.Queryable() .Where(x => x.IsEnterDoorArea && SqlFunc.DateIsSame(x.ArrivedTime, start) && x.DoorStatus == VisitorAppointmentDetailStatusEnumType.Arrived) .Where(x => SqlFunc.Subqueryable().Where(p => p.No == x.No).Where(p => p.Factory == factory).Any()) .CountAsync(); var notIn = await _dbContext.Queryable() .Where(x => x.IsEnterDoorArea && SqlFunc.DateIsSame(x.DoorStart, start)) .Where(x => x.IsEnterDoorArea && x.DoorStatus == VisitorAppointmentDetailStatusEnumType.Wait) .Where(x => SqlFunc.Subqueryable().Where(p => p.No == x.No).Where(p => p.Factory == factory).Any()) .CountAsync(); var leave = await _dbContext.Queryable() .Where(x => x.IsEnterDoorArea && SqlFunc.DateIsSame(x.DoorEnd, start)) .Where(x => x.IsEnterDoorArea && (x.DoorStatus == VisitorAppointmentDetailStatusEnumType.Leave || x.DoorStatus == VisitorAppointmentDetailStatusEnumType.FocusLeave)) .Where(x => SqlFunc.Subqueryable().Where(p => p.No == x.No).Where(p => p.Factory == factory).Any()) .CountAsync(); var notLeave = await _dbContext.Queryable() .Where(x => x.IsEnterDoorArea && SqlFunc.DateIsSame(x.DoorEnd, start) && x.DoorStatus == VisitorAppointmentDetailStatusEnumType.Arrived) .Where(x => SqlFunc.Subqueryable().Where(p => p.No == x.No).Where(p => p.Factory == factory).Any()) .CountAsync(); return new AppointmentSummaryResponse { Total = total, In = @in, NotIn = notIn, Leave = leave, NotLeave = notLeave }; } public async Task UpdateFaceAsync(long detailId, string base64) { var detail = await _dbContext.Queryable().Where(x => x.Id == detailId).SingleAsync(); detail.ThrowIfNull("数据不存在"); await _gainfoFaceService.UploadExternalFaceAsync(new GainfoUploadFaceRequest { UserNo = detail.IdNo, Face = base64 }); await GenerateDeviceTasksAsync(detail, true, DeviceTaskEnumType.Update, "访客手动上传照片"); } //********************************************************************************************************************* private async Task GenerateDeviceTasksAsync(VisitorAppointmentDetailEntity detail, bool withWorkplace, DeviceTaskEnumType deviceTaskType, string source) { var areaCodes = GetValidAreaCodes(detail, withWorkplace).ToList(); if (areaCodes.IsEmpty()) return; var existsAreaCodes = await _dbContext.Queryable().Where(x => areaCodes.Contains(x.CustomCode)).Select(x => x.CustomCode).ToListAsync(); //当前分配的设备组code var deviceGroupCodes = await _dbContext.Queryable() .Where(x => existsAreaCodes.Contains(x.CustomCode)) .Select(x => x.DeviceGroupCode) .ToListAsync(); var deviceCodes = await _dbContext.Queryable().Where(x => deviceGroupCodes.Contains(x.GroupCode)).Select(x => x.DeviceCode).ToListAsync(); var deviceUniqueNos = await _dbContext.Queryable().Where(x => deviceCodes.Contains(x.Code)).Select(x => new { x.UniqueNo, x.TenantId }).ToListAsync(); await _dbContext.InsertAsync(deviceUniqueNos.Select(x => new DeviceTaskEntity { DeviceType = DeviceTypeEnumType.Door, DeviceUniqueNo = x.UniqueNo, Status = DeviceTaskStatusEnumType.Wait, UserNo = detail.UserNo, Type = deviceTaskType, Priority = 100, TenantId = x.TenantId, Source = source, Version = 0 }).ToList()); } private static IEnumerable GetValidAreaCodes(VisitorAppointmentDetailEntity detail, bool withWorkplace) { if (detail.IsEnterDoorArea) { yield return detail.ApplyDoorArea; } if (withWorkplace && detail.IsEnterWorkplace) { foreach (var item in detail.ApplyWorkplaceAreas) { yield return item; } } } }