Files
jinko.yikatong/Src/Dlhis.Basic/Appointment/Services/Implements/AppointmentService.cs

445 lines
19 KiB
C#

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.Basic.Enums;
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 IFactorySettingService _factorySettingService;
private readonly ILogger<AppointmentService> _logger;
private readonly IGainfoFaceService _gainfoFaceService;
private readonly IServiceProvider _serviceProvider;
public AppointmentService(IDbContext dbContext,
IDataProtectionProvider dataProtectionProvider,
ICurrentUser currentUser,
IDhApi dhApi,
IFactorySettingService factorySettingService,
IGainfoFaceService gainfoFaceService,
IServiceProvider serviceProvider,
ILogger<AppointmentService> logger)
{
_dbContext = dbContext;
_dataProtectionProvider = dataProtectionProvider;
_currentUser = currentUser;
_dhApi = dhApi;
_factorySettingService = factorySettingService;
_logger = logger;
_gainfoFaceService = gainfoFaceService;
_serviceProvider = serviceProvider;
}
public async Task<Pagination<AppointmentPageResponse>> GetPageAsync(AppointmentPageRequest request)
{
var factory = _currentUser.GetStringTenantId();
return await _dbContext.Queryable<VisitorAppointmentEntity>()
.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<VisitorAppointmentDetailEntity>()
.Where(p => p.No == x.No)
.Where(p => p.IdNo == request.Search || p.Name == request.Search).Any()
)
.Where(x => x.Factory == factory)
.Select<AppointmentPageResponse>()
.ToPageListAsync(request);
}
public async Task<List<AppointmentDetailResponse>> GetAppointmentDetailsByIdAsync(string no)
{
return await _dbContext.Queryable<VisitorAppointmentDetailEntity>()
.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<AppointmentAreaEntity>().Where(p => p.CustomCode == x.ApplyDoorArea).Select(p => p.Name),
DoorStart = x.DoorStart,
DoorEnd = x.DoorEnd,
IsEnterWorkplace = x.IsEnterWorkplace,
ApplyWorkplaceAreas = SqlFunc.Subqueryable<AppointmentAreaEntity>().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<VisitorAppointmentDetailEntity>().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<AppointmentDetailResponse> 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<VisitorAppointmentDetailEntity>()
.LeftJoin<GainfoEntity>((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<VisitorAppointmentEntity>().Where(p => p.No == x.No).Where(p => p.Factory == factory).Any())
.Select(x => new AppointmentDetailResponse
{
No = x.No,
Mst = SqlFunc.Subqueryable<VisitorAppointmentEntity>()
.LeftJoin<GainfoEntity>((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<VisitorAppointmentDetailEntity>().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<Pagination<AppointmentDetailResponse>> 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<VisitorAppointmentDetailEntity>()
.LeftJoin<GainfoEntity>((x, ga) => x.UserNo == ga.No)
.Where((x, ga) => ga.IdNo == request.IdNo)
.OrderBy(x => x.CreateTime)
.Select<AppointmentDetailResponse>()
.ToPageListAsync(request);
return page;
}
public async Task<List<AppointmentDetailResponse>> 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<VisitorAppointmentDetailEntity>()
.LeftJoin<GainfoEntity>((x, ga) => ga.IdNo == x.IdNo)
.Where(x => SqlFunc.Subqueryable<VisitorAppointmentEntity>()
.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<VisitorAppointmentEntity>()
.LeftJoin<GainfoEntity>((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<AppointmentSummaryResponse> GetSummaryAsync(string factory, DateTime start, DateTime end)
{
var total = await _dbContext.Queryable<VisitorAppointmentDetailEntity>()
.Where(x => x.IsEnterDoorArea && SqlFunc.DateIsSame(x.DoorStart, start))
.Where(x => SqlFunc.Subqueryable<VisitorAppointmentEntity>().Where(p => p.No == x.No).Where(p => p.Factory == factory).Any())
.CountAsync();
var @in = await _dbContext.Queryable<VisitorAppointmentDetailEntity>()
.Where(x => x.IsEnterDoorArea && SqlFunc.DateIsSame(x.ArrivedTime, start) && x.DoorStatus == VisitorAppointmentDetailStatusEnumType.Arrived)
.Where(x => SqlFunc.Subqueryable<VisitorAppointmentEntity>().Where(p => p.No == x.No).Where(p => p.Factory == factory).Any())
.CountAsync();
var notIn = await _dbContext.Queryable<VisitorAppointmentDetailEntity>()
.Where(x => x.IsEnterDoorArea && SqlFunc.DateIsSame(x.DoorStart, start))
.Where(x => x.IsEnterDoorArea && x.DoorStatus == VisitorAppointmentDetailStatusEnumType.Wait)
.Where(x => SqlFunc.Subqueryable<VisitorAppointmentEntity>().Where(p => p.No == x.No).Where(p => p.Factory == factory).Any())
.CountAsync();
var leave = await _dbContext.Queryable<VisitorAppointmentDetailEntity>()
.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<VisitorAppointmentEntity>().Where(p => p.No == x.No).Where(p => p.Factory == factory).Any())
.CountAsync();
var notLeave = await _dbContext.Queryable<VisitorAppointmentDetailEntity>()
.Where(x => x.IsEnterDoorArea && SqlFunc.DateIsSame(x.DoorEnd, start) && x.DoorStatus == VisitorAppointmentDetailStatusEnumType.Arrived)
.Where(x => SqlFunc.Subqueryable<VisitorAppointmentEntity>().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<VisitorAppointmentDetailEntity>().Where(x => x.Id == detailId).SingleAsync();
detail.ThrowIfNull("数据不存在");
await _gainfoFaceService.UploadExternalFaceAsync(new GainfoUploadFaceRequest
{
UserNo = detail.IdNo,
Face = base64
});
await GenerateDeviceTasksAsync(detail, true, DeviceTaskEnumType.Update, "访客手动上传照片");
}
public async Task<List<string>> GetVisitorDevicesAsync(long detailId)
{
var detail = await _dbContext.Queryable<VisitorAppointmentDetailEntity>().Where(x => x.Id == detailId).SingleAsync();
detail.ThrowIfNull("数据不存在");
var areaCodes = GetValidAreaCodes(detail, false).ToList();
if (areaCodes.IsEmpty()) return new List<string>();
var existsAreaCodes = await _dbContext.Queryable<AppointmentAreaEntity>().Where(x => areaCodes.Contains(x.CustomCode)).Select(x => x.CustomCode).ToListAsync();
//当前分配的设备组code
var deviceGroupCodes = await _dbContext.Queryable<AppointmentAreaAssignmentEntity>()
.Where(x => existsAreaCodes.Contains(x.CustomCode))
.Select(x => x.DeviceGroupCode)
.ToListAsync();
var deviceCodes = await _dbContext.Queryable<DeviceGroupRelationshipEntity>().Where(x => deviceGroupCodes.Contains(x.GroupCode)).Select(x => x.DeviceCode).ToListAsync();
var deviceUniqueNos = await _dbContext.Queryable<DeviceEntity>().Where(x => deviceCodes.Contains(x.Code)).Select(x => x.UniqueNo).ToListAsync();
return deviceUniqueNos;
}
/// <inheritdoc />
public async Task AutoResignAsync()
{
var outsources = await _dbContext.Queryable<GainfoEntity>()
.Where(x => x.Type == GainfoUserTypeEnumType.External)
.Select(x => new { x.Id, x.Code, x.Factory, x.CreateTime })
.ToListAsync();
var settings = await _factorySettingService.GetListAsync(false);
//0001/1/1 0:00:00
var nullDateTime = new DateTime(1, 1, 1);
//todo 临时处理循环依赖
var gainfoService = _serviceProvider.GetService<IGainfoService>();
foreach (var grp in outsources.GroupBy(x => x.Factory))
{
var setting = settings.First(x => x.Factory == grp.Key);
if (setting.NoRecordAutoResignExternalDays == 0)
{
continue;
}
foreach (var outsource in grp)
{
var raiseDateTime = await _dbContext.Queryable<DeviceUploadLogEntity>()
.Where(x => x.UserUniqueNo == outsource.Code)
.GroupBy(x => x.UserUniqueNo)
.Select(x => SqlFunc.IsNull(SqlFunc.AggregateMax(x.RaiseDateTime), outsource.CreateTime))
.SingleAsync();
raiseDateTime = raiseDateTime == nullDateTime ? outsource.CreateTime : raiseDateTime;
if (raiseDateTime.AddDays(setting.NoRecordAutoResignExternalDays) < DateTime.Now)
{
try
{
await gainfoService.ResignAsync(new GainfoResignRequest
{
Id = outsource.Id,
Reason = $"{setting.NoRecordAutoResignExternalDays}天无通行记录",
Date = DateTime.Now,
IsOutsource = true
});
}
catch (Exception e)
{
_logger.LogError(e, "自动归档外部人员异常:{msg}", e.Message);
}
}
}
}
}
//*********************************************************************************************************************
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<AppointmentAreaEntity>().Where(x => areaCodes.Contains(x.CustomCode)).Select(x => x.CustomCode).ToListAsync();
//当前分配的设备组code
var deviceGroupCodes = await _dbContext.Queryable<AppointmentAreaAssignmentEntity>()
.Where(x => existsAreaCodes.Contains(x.CustomCode))
.Select(x => x.DeviceGroupCode)
.ToListAsync();
var deviceCodes = await _dbContext.Queryable<DeviceGroupRelationshipEntity>().Where(x => deviceGroupCodes.Contains(x.GroupCode)).Select(x => x.DeviceCode).ToListAsync();
var deviceUniqueNos = await _dbContext.Queryable<DeviceEntity>().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<string> 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;
}
}
}
}