Merge branch 'development'

This commit is contained in:
2024-03-08 11:31:56 +08:00
226 changed files with 5698 additions and 1610 deletions

View File

@@ -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}

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Kurisu.RemoteCall" Version="0.0.3" />
</ItemGroup>
</Project>

View File

@@ -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<DefaultApiResult<List<string>>> GetCanteenDeviceUniqueNosByUserNoAsync(string userNo);
}

View File

@@ -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);
}
/// <summary>
/// 营业点保存餐次
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost("shift/save")]
public async Task SaveShiftAsync(BusinessPlaceSaveShiftRequest request)
{
await _businessPlaceService.SaveShiftAsync(request);
}
/// <summary>
/// 根据手工号获取餐别
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost("shifts")]
[IgnoreTenant]
public async Task<BusinessPlaceWithShiftResponse> GetShiftsByPlaceCustomCodeHttpPostAsync(BusinessPlaceGetShiftsRequest request)
{
return await _businessPlaceShiftService.GetShiftsByPlaceCustomCodeAsync(request.CustomCode);
@@ -75,8 +88,20 @@ public class BusinessPlacesController : ControllerBase
/// <param name="request"></param>
/// <returns></returns>
[HttpGet("shifts")]
[IgnoreTenant]
public async Task<BusinessPlaceWithShiftResponse> GetShiftsByPlaceCustomCodeHttpGetAsync([FromQuery] BusinessPlaceGetShiftsRequest request)
{
return await _businessPlaceShiftService.GetShiftsByPlaceCustomCodeAsync(request.CustomCode);
}
/// <summary>
/// 根据营业点获取下面的设备
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpGet("devicenos")]
public async Task<Pagination<DeviceCodeNameDetail>> GetDevicesByCode([FromQuery]BusinessPlaceGetByCodeRequest request)
{
return await _businessPlaceShiftService.GetDevicesByCode(request);
}
}

View File

@@ -0,0 +1,14 @@
using Kurisu.Core.DataAccess.Dto;
using System.ComponentModel.DataAnnotations;
namespace Dlhis.Basic.BusinessPlace.Request
{
public class BusinessPlaceGetByCodeRequest : PageDto
{
/// <summary>
/// 营业点号
/// </summary>
[Required(ErrorMessage = "营业点号不能为空")]
public string Code { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
namespace Dlhis.Basic.BusinessPlace.Request;
public class BusinessPlaceSaveShiftRequest
{
/// <summary>
/// 营业点id
/// </summary>
public long Id { get; set; }
/// <summary>
/// 餐次code
/// </summary>
public Guid ShiftCode { get; set; }
}

View File

@@ -35,9 +35,21 @@ public class BusinessPlaceResponse
public CanteenBusinessPlaceSourceEnumType Source { get; set; }
public string SourceDesc => Source.GetDescription();
/// <summary>
/// 分类,超市/餐厅/食堂
/// </summary>
public CanteenBusinessPlaceCatalogEnumType Catalog { get; set; }
public string CatalogDesc => Catalog.GetDescription();
/// <summary>
/// 餐次code
/// </summary>
public Guid? ShiftCode { get; set; }
/// <summary>
/// 餐次名称
/// </summary>
public string ShiftName { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace Dlhis.Basic.BusinessPlace.Response
{
public class DeviceCodeNameDetail
{
public string UniqueNo { get; set; }
public string UniqueName { get; set; }
}
}

View File

@@ -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
{
/// <summary>
/// 营业点分页
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
Task<Pagination<BusinessPlaceResponse>> GetPageAsync(BusinessPlacePageRequest request);
/// <summary>
@@ -15,6 +19,17 @@ public interface IBusinessPlaceService : IScopeDependency
/// <returns></returns>
Task<List<BusinessPlaceResponse>> GetOpenedListAsync();
/// <summary>
/// 保存营业点
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
Task SaveAsync(BusinessPlaceSaveRequest request);
/// <summary>
/// 营业点保存餐次
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
Task SaveShiftAsync(BusinessPlaceSaveShiftRequest request);
}

View File

@@ -22,46 +22,83 @@ public class BusinessPlaceService : IBusinessPlaceService
public async Task<Pagination<BusinessPlaceResponse>> GetPageAsync(BusinessPlacePageRequest request)
{
var page = await _dbContext.Queryable<CanteenBusinessPlaceEntity>()
.WhereIF(request.Search.IsPresent(), x => x.CustomCode.Contains(request.Search))
var page = await _dbContext.Queryable<BusinessPlaceEntity>()
.LeftJoin<BusinessPlaceShiftRelationshipEntity>((x, ps) => x.Code == ps.PlaceCode)
.LeftJoin<BusinessShiftEntity>((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<BusinessPlaceResponse>()
.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<List<BusinessPlaceResponse>> GetOpenedListAsync()
{
return await _dbContext.Queryable<CanteenBusinessPlaceEntity>()
return await _dbContext.Queryable<BusinessPlaceEntity>()
.Where(x => x.Status == CanteenBusinessPlaceStatusEnumType.Opened)
.Select<BusinessPlaceResponse>()
.ToListAsync();
.Select<BusinessPlaceResponse>()
.ToListAsync();
}
public async Task SaveAsync(BusinessPlaceSaveRequest request)
{
await CheckExistsCustomCodeAsync(request);
if (!request.Id.HasValue)
{
var entity = request.Adapt<CanteenBusinessPlaceEntity>();
var entity = request.Adapt<BusinessPlaceEntity>();
entity.Code = Guid.NewGuid();
await _dbContext.InsertAsync(entity);
}
else
{
var entity = await _dbContext.Queryable<CanteenBusinessPlaceEntity>().FirstAsync(x => x.Id == request.Id);
var entity = await _dbContext.Queryable<BusinessPlaceEntity>().FirstAsync(x => x.Id == request.Id);
request.Adapt(entity);
await _dbContext.UpdateAsync(entity);
}
}
public async Task SaveShiftAsync(BusinessPlaceSaveShiftRequest request)
{
var place = await _dbContext.Queryable<BusinessPlaceEntity>().Where(x => x.Id == request.Id).FirstAsync();
place.ThrowIfNull("营业点不存在");
var relationship = await _dbContext.Queryable<BusinessPlaceShiftRelationshipEntity>().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 ***************************************************//
/// <summary>
@@ -71,7 +108,7 @@ public class BusinessPlaceService : IBusinessPlaceService
/// <returns></returns>
private async Task CheckExistsCustomCodeAsync(BusinessPlaceSaveRequest request)
{
var existsSameCustomCode = await _dbContext.Queryable<CanteenBusinessPlaceEntity>()
var existsSameCustomCode = await _dbContext.Queryable<BusinessPlaceEntity>()
.Where(x => x.CustomCode == request.CustomCode)
.WhereIF(request.Id.HasValue, x => x.Id != request.Id)
.AnyAsync();

View File

@@ -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);
}
/// <summary>
/// 餐次列表
/// </summary>
/// <returns></returns>
[HttpGet("list")]
public async Task<List<BusinessPlaceShiftResponse>> GetListAsync()
{
return await _businessPlaceShiftService.GetListAsync();
}
/// <summary>
/// 餐次保存
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost("save")]
public async Task SaveAsync(BusinessPlaceShiftSaveRequest request)
public async Task<Guid> SaveAsync(BusinessPlaceShiftSaveRequest request)
{
await _businessPlaceShiftService.SaveAsync(request);
return await _businessPlaceShiftService.SaveAsync(request);
}
/// <summary>
/// 保存餐次段
/// </summary>
/// <param name="requests"></param>
/// <returns></returns>
[HttpPost("meal/save")]
[Transactional]
public async Task SaveMealShiftsAsync(List<BusinessPlaceShiftSaveMealShiftRequest> requests)
{
await _businessPlaceShiftService.SaveMealShiftsAsync(requests);
}
}

View File

@@ -4,5 +4,5 @@ namespace Dlhis.Basic.BusinessShift.Request;
public class BusinessPlaceShiftPageRequest : PageDto
{
public Guid? BusinessPlaceCode { get; set; }
}

View File

@@ -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; }
/// <summary>
/// 餐次Code
/// </summary>
public Guid ShiftCode { get; set; }
/// <summary>
/// mealshift
/// </summary>
public string ShiftType { get; set; }
/// <summary>
/// 序号
/// </summary>
public int Sequence { get; set; } = 100;
/// <summary>
/// 时间段
/// </summary>
public TimePeriod TimePeriod { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (TimePeriod.Start == TimePeriod.End)
{
yield return new ValidationResult("时间段设置区间有误,开始时段不能等于结束时段.", new[] { nameof(TimePeriod) });
}
}
}

View File

@@ -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; }
/// <summary>
/// 营业点代号
/// </summary>
[Required(ErrorMessage = "营业点不能为空")]
public Guid BusinessPlaceCode { get; set; }
/// <summary>
/// 自定义代号 mealshift
/// </summary>
[Required(ErrorMessage = "餐次编号不能为空")]
[Required(ErrorMessage = "餐次手工号不能为空")]
public string CustomCode { get; set; }
/// <summary>
/// 序号
/// 餐次名称
/// </summary>
public int Sequence { get; set; } = 100;
/// <summary>
/// 时间段
/// </summary>
public TimePeriod TimePeriod { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (TimePeriod.Start == TimePeriod.End)
{
yield return new ValidationResult("时间段设置区间有误,开始时段不能等于结束时段.", new[] { nameof(TimePeriod) });
}
}
[Required(ErrorMessage = "餐次名称")]
public string Name { get; set; }
}

View File

@@ -4,21 +4,6 @@ public class BusinessPlaceShiftResponse
{
public long Id { get; set; }
/// <summary>
/// 营业点代号
/// </summary>
public Guid BusinessPlaceCode { get; set; }
/// <summary>
/// 营业点手工号
/// </summary>
public string BusinessPlaceCustomCode { get; set; }
/// <summary>
/// 营业点
/// </summary>
public string BusinessPlace { get; set; }
/// <summary>
/// 代号
/// </summary>
@@ -35,12 +20,7 @@ public class BusinessPlaceShiftResponse
public string Name { get; set; }
/// <summary>
/// 经营开始时间
/// 就餐时间段
/// </summary>
public TimeOnly Start { get; set; }
/// <summary>
/// 经营结束时间
/// </summary>
public TimeOnly End { get; set; }
public List<MealShiftResponse> MealShifts { get; set; }
}

View File

@@ -3,7 +3,7 @@
public class BusinessPlaceShiftTinyResponse
{
/// <summary>
/// 自定义代号
/// 无用字段,暂时不删除 //todo
/// </summary>
public string CustomCode { get; set; }

View File

@@ -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; }
}
}

View File

@@ -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<Pagination<BusinessPlaceShiftResponse>> GetPageAsync(BusinessPlaceShiftPageRequest request);
Task SaveAsync(BusinessPlaceShiftSaveRequest request);
Task<List<BusinessPlaceShiftResponse>> GetListAsync();
Task<Guid> SaveAsync(BusinessPlaceShiftSaveRequest request);
Task SaveMealShiftsAsync(List<BusinessPlaceShiftSaveMealShiftRequest> requests);
Task<BusinessPlaceWithShiftResponse> GetShiftsByPlaceCustomCodeAsync(string placeCustomCode);
Task<Pagination<DeviceCodeNameDetail>> GetDevicesByCode(BusinessPlaceGetByCodeRequest request);
}

View File

@@ -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<Pagination<BusinessPlaceShiftResponse>> GetPageAsync(BusinessPlaceShiftPageRequest request)
{
var page = await _dbContext.Queryable<CanteenBusinessShiftEntity>()
.LeftJoin<CanteenBusinessPlaceEntity>((shift, place) => shift.BusinessPlaceCode == place.Code)
.LeftJoin<BasicTypeEntity>((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<BusinessShiftEntity>()
.WhereIF(request.Search.IsPresent(), (shift) => shift.CustomCode.Contains(request.Search) || shift.Name.Contains(request.Search))
.Select<BusinessPlaceShiftResponse>()
.ToPageListAsync(request);
var shiftCodes = page.Data.Select(x => x.Code).ToList();
//下级就餐时间段
var mealShifts = await _dbContext.Queryable<BusinessShiftMealRelationshipEntity>()
.LeftJoin<BasicTypeEntity>((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<List<BusinessPlaceShiftResponse>> GetListAsync()
{
await CheckExistsCustomCodeAsync(request);
await DuplicateTimePeriodAsync(request);
await CheckPlaceExistsAsync(request.BusinessPlaceCode);
var result = await _dbContext.Queryable<BusinessShiftEntity>()
.Select<BusinessPlaceShiftResponse>().ToListAsync();
return result;
}
public async Task<Guid> SaveAsync(BusinessPlaceShiftSaveRequest request)
{
await CheckExistsAsync(request);
Guid code = Guid.Empty;
if (!request.Id.HasValue)
{
var entity = request.Adapt<CanteenBusinessShiftEntity>();
var entity = request.Adapt<BusinessShiftEntity>();
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<CanteenBusinessShiftEntity>().FirstAsync(x => x.Id == request.Id);
entity.Adapt(request);
var entity = await _dbContext.Queryable<BusinessShiftEntity>().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<BusinessPlaceShiftSaveMealShiftRequest> 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<BusinessShiftMealRelationshipEntity>();
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<BusinessShiftMealRelationshipEntity>().FirstAsync(x => x.Id == request.Id);
entity.ThrowIfNull("餐次段不存在");
request.Adapt(entity);
await _dbContext.UpdateAsync(entity);
}
}
}
[IgnoreTenant]
public async Task<BusinessPlaceWithShiftResponse> GetShiftsByPlaceCustomCodeAsync(string placeCustomCode)
{
var place = await _dbContext.Queryable<CanteenBusinessPlaceEntity>().Where(x => x.CustomCode == placeCustomCode).FirstAsync();
var place = await _dbContext.Queryable<BusinessPlaceEntity>().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<CanteenBusinessShiftEntity>()
.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<BusinessPlaceShiftRelationshipEntity>().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<BusinessPlaceShiftTinyResponse>()
};
}
var shifts = await _dbContext.Queryable<BusinessShiftMealRelationshipEntity>()
.Where(x => x.ShiftCode == relationship.ShiftCode)
.OrderBy(x => x.Sequence) //排序
.ToListAsync();
var list = new List<BusinessPlaceShiftTinyResponse>();
foreach (CanteenBusinessShiftEntity shiftinfo in shifts)
foreach (BusinessShiftMealRelationshipEntity shiftinfo in shifts)
{
var name = _dbContext.Queryable<BasicTypeEntity>()
.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<Pagination<DeviceCodeNameDetail>> GetDevicesByCode(BusinessPlaceGetByCodeRequest request)
{
var existsPlace = await _dbContext.Queryable<CanteenBusinessPlaceEntity>().AnyAsync(x => x.Code == code);
existsPlace.ThrowIfFalse("营业点不存在,请检查.");
var regiss = await _dbContext.Queryable<DeviceRegistryEntity>()
.Where(x => x.BusinessPlaceCode.ToString() == request.Code)
.ToListAsync();
var devices = await _dbContext.Queryable<DeviceEntity>().ToListAsync();
var list = new List<DeviceCodeNameDetail>();
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<DeviceCodeNameDetail>
{
PageIndex = request.PageIndex,
PageSize = request.PageSize,
Total = list.Count, // 使用原始列表的总数进行分页
Data = pagedData
};
return pagination;
}
//*********************************************** help methods ***************************************************//
/// <summary>
/// 时间段重合检查
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private async Task DuplicateTimePeriodAsync(BusinessPlaceShiftSaveRequest request)
private async Task DuplicateTimePeriodAsync(BusinessPlaceShiftSaveMealShiftRequest request)
{
var periods = await _dbContext.Queryable<CanteenBusinessShiftEntity>()
.Where(x => x.BusinessPlaceCode == request.BusinessPlaceCode)
var periods = await _dbContext.Queryable<BusinessShiftMealRelationshipEntity>()
.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}]");
}
}
/// <summary>
/// 检查重复手工号
/// 检查重复
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private async Task CheckExistsCustomCodeAsync(BusinessPlaceShiftSaveRequest request)
private async Task CheckExistsAsync(BusinessPlaceShiftSaveRequest request)
{
var existsSameCustomCode = await _dbContext.Queryable<CanteenBusinessShiftEntity>()
.Where(x => x.CustomCode == request.CustomCode)
var existsSameCustomCode = await _dbContext.Queryable<BusinessShiftEntity>()
.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<BusinessShiftEntity> CheckExistsShiftAsync(Guid shiftCode)
{
var shift = await _dbContext.Queryable<BusinessShiftEntity>().Where(x => x.Code == shiftCode).FirstAsync();
shift.ThrowIfNull("餐次不存在");
return shift;
}
private async Task<List<string>> GetMealShiftAsync()
{
return await _dbContext.Queryable<BasicTypeEntity>().Where(x => x.Type == "mealshift").Select(x => x.Code).ToListAsync();
}
private async Task CheckExistsMealShiftAsync(BusinessPlaceShiftSaveMealShiftRequest request)
{
var exists = await _dbContext.Queryable<BusinessShiftMealRelationshipEntity>()
.Where(x => x.ShiftCode == request.ShiftCode)
.Where(x => x.ShiftType == request.ShiftType)
.WhereIF(request.Id.HasValue, x => x.Id != request.Id)
.AnyAsync();
exists.ThrowIfTrue("重复设置!");
}
}

View File

@@ -0,0 +1,73 @@
using Kurisu.Core.CustomClass;
namespace Dlhis.Basic.BusinessShift;
/// <summary>
/// 临时的扩展类 todo: 嵌入框架
/// </summary>
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;
}
}

View File

@@ -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;
}
/// <summary>
/// 设备组分页
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost("page")]
public async Task<Pagination<DeviceGroupPageResponse>> GetPageAsync([FromQuery] DeviceGroupPageRequest request)
{
return await _deviceGroupService.GetPageAsync(request);
}
/// <summary>
/// 获取设备组树
/// </summary>
/// <param name="showDevices">是否显示设备</param>
/// <param name="type">分组类型</param>
/// <returns></returns>
[HttpGet("tree")]
public async Task<List<DeviceGroupTreeResponse>> GetTreeAsync(bool showDevices)
[IgnoreTenant]
public async Task<List<DeviceGroupTreeResponse>> GetTreeAsync(bool showDevices, DeviceTypeEnumType? type)
{
return await _deviceGroupService.GetTreeAsync(showDevices);
return await _deviceGroupService.GetTreeAsync(showDevices, type);
}
/// <summary>
@@ -50,6 +43,7 @@ public class DeviceGroupsController : ControllerBase
/// <param name="request"></param>
/// <returns></returns>
[HttpPost("save")]
[IgnoreTenant]
public async Task SaveAsync(DeviceGroupSaveRequest request)
{
await _deviceGroupService.SaveAsync(request);
@@ -72,6 +66,8 @@ public class DeviceGroupsController : ControllerBase
/// <param name="request"></param>
/// <returns></returns>
[HttpPost("assign")]
[IgnoreTenant]
[Transactional]
public async Task AssignDevicesAsync(DeviceAssignRequest request)
{
await _deviceGroupService.AssignDevicesAsync(request);

View File

@@ -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
/// <param name="request"></param>
/// <returns></returns>
[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);
}
/// <summary>
/// 就餐设备唯一号权限
/// </summary>
/// <param name="userNo"></param>
/// <returns></returns>
[HttpGet("canteen/uniqueNos")]
[IgnoreTenant]
public async Task<List<string>> GetCanteenDeviceUniqueNosByUserNoAsync(string userNo)
{
return await _devicePermissionService.GetCanteenDeviceUniqueNosByUserNoAsync(userNo);
}
}

View File

@@ -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
/// <param name="request"></param>
/// <returns></returns>
[HttpPost("save")]
[Transactional]
public async Task SaveAsync(DeviceSaveRequest request)
{
await _deviceService.SaveAsync(request);
@@ -81,6 +83,7 @@ public class DevicesController : ControllerBase
/// <param name="request"></param>
/// <returns></returns>
[HttpPost("resigter")]
[IgnoreTenant]
public async Task<string> ResigterAsync(DeviceResigterRequest request)
{
return await _deviceConnectService.ResigterAsync(request);
@@ -128,6 +131,7 @@ public class DevicesController : ControllerBase
/// <param name="request"></param>
/// <returns></returns>
[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);
}
/// <summary>
/// 获取设备
/// </summary>
@@ -194,12 +199,34 @@ public class DevicesController : ControllerBase
}
/// <summary>
/// 获取近期30天新增设备数
/// 获取门禁在线/离线设备详情列表
/// </summary>
/// <returns></returns>
[HttpGet("dooroffline")]
public async Task<Pagination<DevicePageResponse>> GetDoorOfflineAsync([FromQuery] GetDoorOfflineRequest request)
{
return await _deviceService.GetDoorOfflineAsync(request);
}
/// <summary>
/// 获取近期30天考勤新增设备数
/// </summary>
/// <returns></returns>
[HttpGet("recentnumbers")]
public async Task<Dictionary<string, int>> GetRecentNumbersAsync()
public async Task<List<RecentNumberResponse>> GetRecentNumbersAsync()
{
return await _deviceService.GetRecentNumbersAsync();
}
/// <summary>
/// 批量导入设备信息
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost("bulkUploads")]
public async Task BulkUploadsAsync([FromForm] BulkUploadsRequest request)
{
await _deviceService.BulkUploadsAsync(request);
}
}

View File

@@ -0,0 +1,14 @@
namespace Dlhis.Basic.Device.Dtos;
public class UserTenantDeviceCodeDto
{
/// <summary>
/// 租户
/// </summary>
public string Tenant { get; set; }
/// <summary>
/// 设备权限
/// </summary>
public List<Guid> Codes { get; set; }
}

View File

@@ -0,0 +1,10 @@
namespace Dlhis.Basic.Device.Request
{
public class BulkUploadsRequest
{
/// <summary>
/// 设备信息文件
/// </summary>
public IFormFile FormFile { get; set; }
}
}

View File

@@ -1,7 +0,0 @@
using Kurisu.Core.DataAccess.Dto;
namespace Dlhis.Basic.Device.Request;
public class DeviceGroupPageRequest : PageDto
{
}

View File

@@ -10,7 +10,7 @@ public class DeviceGroupSaveRequest
/// <summary>
/// 上级分组
/// </summary>
public Guid? PCode { get; set; }
public Guid PCode { get; set; }
/// <summary>
/// 设备组名称
@@ -21,7 +21,11 @@ public class DeviceGroupSaveRequest
/// <summary>
/// 分类类型 DeviceTypeEnumType
/// </summary>
[Required(ErrorMessage = "分类不能为空")]
[EnumDataType(typeof(DeviceTypeEnumType))]
public DeviceTypeEnumType Type { get; set; }
public DeviceTypeEnumType? Type { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
}

View File

@@ -2,7 +2,7 @@
namespace Dlhis.Basic.Device.Request
{
public class DeviceOnlineAndofflineRequest :PageDto
public class DeviceOnlineAndofflineRequest : PageDto
{
public bool IsOnAndOff { get; set; }
}

View File

@@ -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
{
/// <summary>
/// 分配类型 AssignTypeEnumType
@@ -12,9 +13,12 @@ public class DevicePermissionSaveReuqest
public AssignTypeEnumType AssignType { get; set; }
/// <summary>
/// 人员档案号
/// 分配值
/// </summary>
[Required(ErrorMessage = "人员档案号不能为空")]
/// <remarks>
/// 如果<see cref="AssignTypeEnumType"/>为用户,那么AssignValue=UserNo
/// </remarks>
[Required(ErrorMessage = "分配值不能为空")]
public string AssignValue { get; set; }
/// <summary>
@@ -24,7 +28,23 @@ public class DevicePermissionSaveReuqest
public DeviceAssignmentTypeEnumType UniqueType { get; set; }
/// <summary>
/// 分配权限值不能为空
/// 分配权限值
/// </summary>
public List<Guid> Codes { get; set; } = new List<Guid>();
public List<Guid> Codes { get; set; }
/// <summary>
/// AssignType == AssignTypeEnumType.Department 需要传入
/// </summary>
public string TenantId { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (AssignType == AssignTypeEnumType.Department)
{
if (TenantId.IsEmpty())
{
yield return new ValidationResult("请明确具体的厂区", new[] { nameof(TenantId) });
}
}
}
}

View File

@@ -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
/// <summary>
/// 心跳间隔
/// </summary>
[Range(0, int.MaxValue)]
public int HeartbeatInterval { get; set; }
/// <summary>
@@ -70,4 +70,22 @@ public class DeviceSaveRequest
/// 是否启用
/// </summary>
public bool Enable { get; set; }
/// <summary>
/// 是否作为考勤设备
/// </summary>
public bool AsAttendance { get; set; }
public IEnumerable<ValidationResult> 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) });
}
}
}

View File

@@ -10,6 +10,6 @@
/// <summary>
/// 营业点
/// </summary>
public Guid BusinessPlaceCode { get; set; }
public Guid? BusinessPlaceCode { get; set; }
}
}

View File

@@ -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; }
/// <summary>
/// 设备唯一号
/// </summary>
public string UniqueNo { get; set; }
/// <summary>
/// 模板code
/// </summary>
public Guid TemplateCode { get; set; }
/// <summary>
/// 供应商信息
/// </summary>
public string Supplier { get; set; }
/// <summary>
/// 设备功能
/// </summary>
public List<DeviceFuncEnumType> Funcs { get; set; }
/// <summary>
/// 设备类型 DeviceTypeEnumType
/// </summary>
public DeviceTypeEnumType Type { get; set; }
/// <summary>
/// 心跳间隔
/// </summary>
public int HeartbeatInterval { get; set; }
/// <summary>
/// 部署点
/// </summary>
public string DeployPlace { get; set; }
/// <summary>
/// 部署坐标
/// </summary>
public string DeployPosition { get; set; }
/// <summary>
/// 部署方向
/// </summary>
public string DeployDirection { get; set; }
/// <summary>
/// 连接信息
/// </summary>
public string ConnectionInfo { get; set; }
/// <summary>
/// 是否启用
/// </summary>
public bool Enable { get; set; }
/// <summary>
/// 是否作为考勤设备
/// </summary>
public bool AsAttendance { get; set; }
/// <summary>
/// 营业点code
/// </summary>
public Guid? PlaceeCode { get; set; }
}
}

View File

@@ -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
/// 设备组名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 分组类型
/// </summary>
public DeviceTypeEnumType? Type { get; set; }
/// <summary>
/// 是否可编辑
/// </summary>
public bool Editable { get; set; }
/// <summary>
/// 分组类型描述
/// </summary>
public string TypeDesc
{
get
{
if (!Type.HasValue)
{
return string.Empty;
}
return Type.Value.GetDescription();
}
}
}

View File

@@ -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; }
/// <summary>
/// 上级分组code
/// </summary>
@@ -17,6 +24,42 @@ public class DeviceGroupTreeResponse
/// </summary>
public string Name { get; set; }
/// <summary>
/// 设备组类型
/// </summary>
public DeviceTypeEnumType? Type { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
/// <summary>
/// 分组类型描述
/// </summary>
public string TypeDesc
{
get
{
if (!Type.HasValue)
{
return string.Empty;
}
return Type.Value.GetDescription();
}
}
public bool Editable { get; set; }
/// <summary>
/// 是否允许设置下级
/// </summary>
public bool AbleSetNext { get; set; }
[JsonIgnore]
public string TenantId { get; set; }
/// <summary>
/// 下级
/// </summary>

View File

@@ -84,4 +84,51 @@ public class DevicePageResponse
/// 是否启用
/// </summary>
public bool Enable { get; set; }
//*********************************************************附加信息**************************************************************/
/// <summary>
/// 最后心跳时间
/// </summary>
public DateTime? LastHeartbeatTime { get; set; }
/// <summary>
/// 是否在线
/// </summary>
public bool IsOnline
{
get
{
if (!LastHeartbeatTime.HasValue)
return false;
else
{
if (HeartbeatInterval <= 0)
return true;
else
return LastHeartbeatTime.Value.AddSeconds(HeartbeatInterval * 3) >= DateTime.Now;
}
}
}
/// <summary>
/// 消费总额
/// </summary>
public decimal Amount { get; set; }
/// <summary>
/// 消费人次
/// </summary>
public int UserCount { get; set; }
/// <summary>
/// 营业点code
/// </summary>
public Guid? BusinessPlaceCode { get; set; }
/// <summary>
/// 营业点名称
/// </summary>
public string BusinessPlaceName { get; set; }
}

View File

@@ -88,6 +88,9 @@ public class DeviceResponse
}
}
public DeviceTypeEnumType Type { get; set; }
public string TypeDesc => Type.GetDescription();
/// <summary>
/// 连接信息
/// </summary>

View File

@@ -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
/// </summary>
public string Brand { get; set; }
/// <summary>
/// 设备类型
/// </summary>
public DeviceTypeEnumType Type { get; set; }
/// <summary>
/// 是否启用
/// </summary>

View File

@@ -0,0 +1,9 @@
using Kurisu.Core.DataAccess.Dto;
namespace Dlhis.Basic.Device.Response
{
public class GetDoorOfflineRequest : PageDto
{
public bool IsOnAndOff { get; set; }
}
}

View File

@@ -0,0 +1,9 @@
namespace Dlhis.Basic.Device.Response
{
public class RecentNumberResponse
{
public string Date { get; set; }
public int Num { get; set; }
}
}

View File

@@ -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<Pagination<DeviceGroupPageResponse>> GetPageAsync(DeviceGroupPageRequest request);
Task<List<DeviceGroupTreeResponse>> GetTreeAsync(bool showDevices);
Task<List<DeviceGroupTreeResponse>> GetTreeAsync(bool showDevices, DeviceTypeEnumType? type);
Task SaveAsync(DeviceGroupSaveRequest request);

View File

@@ -50,6 +50,14 @@ public interface IDevicePermissionService : IScopeDependency
/// <returns></returns>
Task<List<Guid>> GetAssignDeviceCodesAsync(string userNo);
/// <summary>
/// 获取就餐权限唯一号
/// </summary>
/// <param name="userNo"></param>
/// <returns></returns>
Task<List<string>> GetCanteenDeviceUniqueNosByUserNoAsync(string userNo);
/// <summary>
/// 获取用户权限设备
/// </summary>

View File

@@ -23,5 +23,11 @@ public interface IDeviceService : IScopeDependency
Task<Pagination<DevicePageResponse>> GetAttendanceOfflineAsync(DeviceOnlineAndofflineRequest request);
Task<Dictionary<string, int>> GetRecentNumbersAsync();
Task<Pagination<DevicePageResponse>> GetDoorOfflineAsync(GetDoorOfflineRequest request);
Task<List<RecentNumberResponse>> GetRecentNumbersAsync();
Task BulkUploadsAsync(BulkUploadsRequest request);
}

View File

@@ -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<string> ResigterAsync(DeviceResigterRequest request)
{
var businessPlace = await _dbContext.Queryable<CanteenBusinessPlaceEntity>().Where(x => x.CustomCode == request.BusinessCustomCode).FirstAsync();
var businessPlace = await _dbContext.Queryable<BusinessPlaceEntity>().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<List<DeviceNotSetBusinessPlaceResponse>> GetNotSetBusinessPlaceDevicesAsync()
{
var devices = await _dbContext.Queryable<DeviceEntity>().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<List<DeviceNotSetBusinessPlaceResponse>>();
}
@@ -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<DeviceRegistryEntity>().Where(x => x.DeviceUniqueNo == request.UniqueNo).FirstAsync();
registry.ThrowIfNull($"{request.UniqueNo}的设备注册信息不存在,无法设置营业点");
var businessPlace = await _dbContext.Queryable<CanteenBusinessPlaceEntity>().Where(x => x.Code == request.BusinessPlaceCode).FirstAsync();
businessPlace.ThrowIfNull("营业点不存在,无法设置到设备注册信息中");
if (request.BusinessPlaceCode.HasValue)
{
var businessPlace = await _dbContext.Queryable<BusinessPlaceEntity>().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<DeviceRegistryEntity>().Where(x => x.DeviceUniqueNo == request.UniqueNo).FirstAsync();

View File

@@ -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<Pagination<DeviceGroupPageResponse>> GetPageAsync(DeviceGroupPageRequest request)
{
return await _dbContext.Queryable<DeviceGroupEntity>()
.WhereIF(request.Search.IsPresent(), x => x.Name.Contains(request.Search))
.Select<DeviceGroupPageResponse>()
.ToPageListAsync(request);
}
public async Task<List<DeviceGroupTreeResponse>> GetTreeAsync(bool showDevices)
public async Task<List<DeviceGroupTreeResponse>> GetTreeAsync(bool showDevices, DeviceTypeEnumType? type)
{
var deviceGroups = await _dbContext.Queryable<DeviceGroupEntity>()
.WhereIF(type.HasValue, x => x.Type == type)
.Select<DeviceGroupTreeResponse>()
.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<DeviceGroupEntity>();
entity.Code = Guid.NewGuid();
entity.TenantId = _currentUser.GetStringTenantId();
await _dbContext.InsertAsync(entity);
}
else
{
var entity = await _dbContext.Queryable<DeviceGroupEntity>().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<DeviceGroupEntity>()
.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<DeviceGroupEntity>().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<DeviceGroupEntity>().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<DeviceGroupRelationshipEntity>().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 *********************************************************/
/// <summary>
/// 检查父级分组存在设备
/// 检查父级分组
/// </summary>
/// <param name="pCode"></param>
/// <param name="request"></param>
/// <returns></returns>
private async Task CheckPCodeExistsDeviceAsync(Guid? pCode)
private async Task<DeviceGroupEntity> CheckPCodeAsync(DeviceGroupSaveRequest request)
{
if (pCode.HasValue)
var pGrp = await _dbContext.Queryable<DeviceGroupEntity>().Where(x => x.Code == request.PCode).FirstAsync();
if (pGrp != null)
{
var exists = await _dbContext.Queryable<DeviceGroupRelationshipEntity>().Where(x => x.GroupCode == pCode).AnyAsync();
exists.ThrowIfTrue("父级分组存在设备");
(!pGrp.AbleSetNext && !request.Id.HasValue).ThrowIfTrue($"{pGrp.Name}不可添加下级");
var exists = await _dbContext.Queryable<DeviceGroupRelationshipEntity>().Where(x => x.GroupCode == request.PCode).AnyAsync();
exists.ThrowIfTrue("上级分组存在设备");
}
(pGrp != null && pGrp.Type != request.Type).ThrowIfTrue("分组类型必须与上级类型保持一致");
return pGrp;
}
/// <summary>
///
/// 检查分组是否存在下级
/// </summary>
/// <param name="currentCode"></param>
/// <returns></returns>
private async Task CheckNoNextGroupAsync(Guid currentCode)
{
var exists = await _dbContext.Queryable<DeviceGroupEntity>().Where(x => x.PCode == currentCode).AnyAsync();
exists.ThrowIfTrue("存在下级设备分组");
exists.ThrowIfTrue("存在下级设备分组,无法分配设备");
}
/// <summary>
@@ -150,10 +169,7 @@ public class DeviceGroupService : IDeviceGroupService
{
var devices = await _dbContext.Queryable<DeviceEntity>().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
/// 递归获取树
/// </summary>
/// <param name="current"></param>
/// <param name="departments"></param>
/// <param name="deviceGroups"></param>
/// <param name="showDevices"></param>
private void RecursionGetNext(DeviceGroupTreeResponse current, List<DeviceGroupTreeResponse> departments, bool showDevices)
private void RecursionGetNext(DeviceGroupTreeResponse current, List<DeviceGroupTreeResponse> 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);
}
//最后层级附加设备信息

View File

@@ -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<DevicePermissionEntity>()
.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<DevicePermissionEntity>()
.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<List<Guid>> GetAssignmentsAsync(DeviceAssignmentTypeEnumType uniqueType, AssignTypeEnumType assignType, string assignValue)
{
var devicePermissions = await _dbContext.Queryable<DevicePermissionEntity>()
.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<DevicePermissionEntity>().ToListAsync();
var deviceGroups = await _dbContext.Queryable<DeviceGroupEntity>().ToListAsync();
if (uniqueType == DeviceAssignmentTypeEnumType.Group)
{
var deviceGroups = await _dbContext.Queryable<DeviceGroupEntity>().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<DeviceGroupEntity> deviceGroups, List<Guid> 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<Task> func, List<string> userNos)
{
if (userNos.IsEmpty())
_dbContext.Client.QueryFilter.ClearAndBackup<ITenantId>();
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<ITenantId>();
//获取当前的权限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<DeviceEntity>().ToListAsync();
var waitTasks = new List<DeviceTaskEntity>();
var deivces = await _dbContext.Queryable<DeviceEntity>().ToListAsync();
foreach (var userNo in userNos)
{
beforeDic.TryGetValue(userNo, out var beforeCodes);
beforeCodes ??= new List<Guid>();
currentDic.TryGetValue(userNo, out var currentCodes);
currentCodes ??= new List<Guid>();
//被删除
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<GainfoEntity>()
.LeftJoin<DepartmentEntity>((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<List<string>> GetCanteenDeviceUniqueNosByUserNoAsync(string userNo)
{
var all = await GetAssignDeviceCodesAsync(userNo);
return await _dbContext.Queryable<DeviceEntity>().Where(x => x.Type == DeviceTypeEnumType.Meal)
.Where(x => all.Contains(x.Code))
.Select(x => x.UniqueNo)
.ToListAsync();
}
/// <summary>
/// 获取用户所有设备权限
/// </summary>
@@ -206,125 +213,17 @@ public class DevicePermissionService : IDevicePermissionService
public async Task<List<Guid>> GetAssignDeviceCodesAsync(string userNo)
{
var gainfo = await _dbContext.Queryable<GainfoEntity>().Where(x => x.No == userNo).SingleAsync();
var gainfoGroups = await _dbContext.Queryable<GainfoGroupRelationshipEntity>().Where(x => x.UserNo == userNo).Select(x => x.GroupCode).ToListAsync();
var gainfoGroups = await _dbContext.Queryable<GainfoGroupRelationshipEntity>().ToListAsync();
var deviceCodes = new List<Guid>();
var deviceGroupCodes = new List<Guid>();
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<DeviceGroupRelationshipEntity>().Where(x => deviceGroupCodes.Contains(x.GroupCode)).Select(x => x.DeviceCode).ToListAsync();
deviceCodes.AddRange(deveiceCodesByGroup);
return deviceCodes.Distinct().ToList();
}
private async Task<Dictionary<string, List<Guid>>> GetAssignDeviceCodesAsync(List<string> userNos)
{
var gainfos = await _dbContext.Queryable<GainfoEntity>().Where(x => userNos.Contains(x.No)).ToListAsync();
if (!gainfos.Any())
{
return new Dictionary<string, List<Guid>>();
}
var gainfoGroupRelationships = await _dbContext.Queryable<GainfoGroupRelationshipEntity>().ToListAsync();
var devicePermissions = await _dbContext.Queryable<DevicePermissionEntity>().ToListAsync();
var deviceGroups = await _dbContext.Queryable<DeviceGroupEntity>().ToListAsync();
var deviceRelationships = await _dbContext.Queryable<DeviceGroupRelationshipEntity>().ToListAsync();
var result = new Dictionary<string, List<Guid>>();
foreach (var gainfo in gainfos)
{
var userNo = gainfo.No;
var gainfoGroups = gainfoGroupRelationships.Where(x => x.UserNo == userNo).ToList();
var deviceCodes = new List<Guid>();
var deviceGroupCodes = new List<Guid>();
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<DeviceGroupRelationshipEntity>().Where(x => deviceGroupCodes.Contains(x.GroupCode)).Select(x => x.DeviceCode).ToListAsync();
deviceCodes.AddRange(deveiceCodesByGroup);
result.Add(userNo, deviceCodes.Distinct().ToList());
}
return result;
List<Guid> 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<DeviceGroupEntity> deviceGroups, List<Guid> 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();
}
/// <summary>
/// 获取用户权限设备
/// </summary>
@@ -340,7 +239,7 @@ public class DevicePermissionService : IDevicePermissionService
var devices = await _dbContext.Queryable<DeviceEntity>().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<GainfoEntity>().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<Guid> GetAssignmentsInternal(
List<DevicePermissionEntity> devicePermissions, List<DeviceGroupEntity> 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<DeviceGroupEntity> deviceGroups, List<Guid> 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<UserTenantDeviceCodeDto> GetAssignCodesInternal(
List<DevicePermissionEntity> devicePermissions, List<DeviceGroupEntity> 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<DeviceGroupEntity> deviceGroups, List<UserTenantDeviceCodeDto> 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<Guid> { item.Code } });
}
RecursionGetNext(item, deviceGroups, result, result.SelectMany(x => x.Codes).Contains(item.Code));
}
}
}
private async Task<Dictionary<string, List<UserTenantDeviceCodeDto>>> GetAssignDeviceCodesInternalAsync(List<string> userNos)
{
var gainfos = await _dbContext.Queryable<GainfoEntity>().Where(x => userNos.Contains(x.No)).ToListAsync();
if (!gainfos.Any())
{
return new Dictionary<string, List<UserTenantDeviceCodeDto>>();
}
var gainfoGroupRelationships = await _dbContext.Queryable<GainfoGroupRelationshipEntity>().Where(x => userNos.Contains(x.UserNo)).ToListAsync();
var devicePermissions = await _dbContext.Queryable<DevicePermissionEntity>().ToListAsync();
var deviceGroups = await _dbContext.Queryable<DeviceGroupEntity>().ToListAsync();
var deviceRelationships = await _dbContext.Queryable<DeviceGroupRelationshipEntity>().ToListAsync();
var result = new Dictionary<string, List<UserTenantDeviceCodeDto>>(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<UserTenantDeviceCodeDto> GetAssignDeviceCodesInternal(
GainfoEntity gainfo,
List<GainfoGroupRelationshipEntity> gainfoGroupRelationships,
List<DevicePermissionEntity> devicePermissions,
List<DeviceGroupEntity> deviceGroups,
List<DeviceGroupRelationshipEntity> deviceGroupRelationships)
{
var userNo = gainfo.No;
var deviceCodes = new List<UserTenantDeviceCodeDto>();
var deviceGroupCodes = new List<UserTenantDeviceCodeDto>();
//获取人员的分组,跨基地
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();
}
}

View File

@@ -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<DeviceEntity>()
.LeftJoin<DeviceTemplateEntity>((d, dt) => d.TemplateCode == dt.Code)
.LeftJoin<DeviceRegistryEntity>((d, dt, dr) => d.UniqueNo == dr.DeviceUniqueNo)
.LeftJoin<BusinessPlaceEntity>((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<BalanceRecordEntity>().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<DeviceEntity>();
entity = request.Adapt<DeviceEntity>();
entity.Code = Guid.NewGuid();
await _dbContext.InsertAsync(entity);
//同时保存注册信息
@@ -72,7 +106,7 @@ public class DeviceService : IDeviceService
}
else
{
var entity = await _dbContext.Queryable<DeviceEntity>().Where(x => x.Id == request.Id).FirstAsync();
entity = await _dbContext.Queryable<DeviceEntity>().Where(x => x.Id == request.Id).FirstAsync();
entity.ThrowIfNull("设备不存在,无法更新");
//分类发生变化,检查是否存在分组
@@ -81,21 +115,94 @@ public class DeviceService : IDeviceService
var existsG = await _dbContext.Queryable<DeviceGroupRelationshipEntity>()
.LeftJoin<DeviceGroupEntity>((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);
}
/// <summary>
/// 批量导入
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task BulkUploadsAsync(BulkUploadsRequest request)
{
var lst = ImportFromExcel(request.FormFile);
var places = await _dbContext.Queryable<BusinessPlaceEntity>().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<DeviceEntity>().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<DeviceEntity>();
saveEntity.Code = Guid.NewGuid();
await _dbContext.InsertAsync(saveEntity);
//保存注册信息
var regis = await _dbContext.Queryable<DeviceRegistryEntity>()
.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<DeviceUploadLogEntity>()
.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,
});
}
}
}
/// <summary>
/// 检查设备模板是否存在
/// </summary>
@@ -148,14 +255,15 @@ public class DeviceService : IDeviceService
/// <returns></returns>
private async Task CheckUniqueNoExistsAsync(DeviceSaveRequest request)
{
var exists = await _dbContext.Queryable<DeviceEntity>().Where(x => x.UniqueNo == request.UniqueNo)
var exists = await _dbContext.Queryable<DeviceEntity>()
.Where(x => x.UniqueNo == request.UniqueNo)
.WhereIF(request.Id.HasValue, x => x.Id != request.Id)
.AnyAsync();
exists.ThrowIfTrue("唯一号已经存在");
}
[IgnoreTenant]
public async Task<bool> CheckExistsAsync(string deviceUniqueNo)
{
return await _dbContext.Queryable<DeviceEntity>()
@@ -187,36 +295,81 @@ public class DeviceService : IDeviceService
{
var devices = await _dbContext.Queryable<DeviceEntity>()
.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<DeviceNumberResponse> GetDeviceNumberAsync()
{
var devices = await _dbContext.Queryable<DeviceEntity>()
.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<DeviceEntity>().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;
}
/// <summary>
/// 获取离线设备
/// </summary>
/// <returns></returns>
private async Task<List<DeviceEntity>> GetOfflineDevicesCount(List<DeviceEntity> devices)
{
var now = DateTime.Now;
var deviceUniqueNos = devices.Select(x => x.UniqueNo).ToList();
var deviceRegistries = await _dbContext.Queryable<DeviceRegistryEntity>()
.LeftJoin<DeviceEntity>((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<string>();
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<Pagination<DevicePageResponse>> GetAttendanceTotalAsync(AttendanceTotalRequest request)
{
var page = await _dbContext.Queryable<DeviceEntity>()
.LeftJoin<DeviceTemplateEntity>((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<Pagination<DevicePageResponse>> GetAttendanceOfflineAsync(DeviceOnlineAndofflineRequest request)
{
var devices = await _dbContext.Queryable<DeviceEntity>().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<DeviceEntity>()
.LeftJoin<DeviceTemplateEntity>((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<long> onids = ids.Except(offids).ToList();
var page = await _dbContext.Queryable<DeviceEntity>()
.LeftJoin<DeviceTemplateEntity>((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<Dictionary<string, int>> GetRecentNumbersAsync()
public async Task<Pagination<DevicePageResponse>> GetDoorOfflineAsync(GetDoorOfflineRequest request)
{
var devices = await _dbContext.Queryable<DeviceEntity>().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<DeviceEntity>()
.LeftJoin<DeviceTemplateEntity>((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<long> onids = ids.Except(offids).ToList();
var page = await _dbContext.Queryable<DeviceEntity>()
.LeftJoin<DeviceTemplateEntity>((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<List<RecentNumberResponse>> 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<DeviceEntity>().ToListAsync();
var devices = await _dbContext.Queryable<DeviceEntity>().Where(x => x.Type == DeviceTypeEnumType.Attendance || (x.Type == DeviceTypeEnumType.Door && x.AsAttendance)).ToListAsync();
var dic = new Dictionary<string, int>();
var list = new List<RecentNumberResponse>();
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<ImportDeviceSaveRequest> ImportFromExcel(IFormFile formFile)
{
var requests = new List<ImportDeviceSaveRequest>();
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<DeviceFuncEnumType>();
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;
}
}

View File

@@ -46,6 +46,7 @@ public class DeviceTemplateService : IDeviceTemplateService
{
var entity = await _dbContext.Queryable<DeviceTemplateEntity>().Where(x => x.Id == request.Id).FirstAsync();
await ExistsDeviceUsingTemplateAsync(entity.Code);
request.Adapt(entity);
await _dbContext.UpdateAsync(entity);
}
}

View File

@@ -17,9 +17,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Kurisu.AspNetCore" Version="0.5.2" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="6.0.26" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.4" />
<PackageReference Include="Kurisu.AspNetCore" Version="0.6.1" />
<PackageReference Include="NPOI" Version="2.6.2" />
</ItemGroup>
<ItemGroup>
@@ -28,4 +27,8 @@
<ProjectReference Include="..\Dlhis.Sys.Api\Dlhis.Sys.Api.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Summary\Request\" />
</ItemGroup>
</Project>

View File

@@ -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();
}
/// <summary>
/// 获取部门树分页
/// </summary>
/// <returns></returns>
[HttpGet("treepage")]
public async Task<Pagination<DepartmentTreeResponse>> GetTreePageAsync([FromQuery]DepartmentPageRequest request)
{
return await _departmentService.GetTreePageAsync(request);
}
}

View File

@@ -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;
/// <summary>
/// 档案卡记录
/// </summary>
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class GainfoCardsController : ControllerBase
{
private readonly IGainfoCardService _gainfoCardService;
public GainfoCardsController(IGainfoCardService gainfoCardService)
{
_gainfoCardService = gainfoCardService;
}
/// <summary>
/// 换卡
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost("change")]
[Transactional]
public async Task ChangeCardAsync(ChangeCardRequest request)
{
await _gainfoCardService.ChangeCardAsync(request);
}
/// <summary>
/// 换卡记录
/// </summary>
/// <param name="userNo"></param>
/// <returns></returns>
[HttpGet("records")]
public async Task<List<ChangeCardResponse>> GetChangeRecordsAsync(string userNo)
{
return await _gainfoCardService.GetChangeRecordsAsync(userNo);
}
}

View File

@@ -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
}
/// <summary>
/// 获取档案组分页
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpGet("page")]
public async Task<Pagination<GainfoGroupResponse>> GetPageAsync([FromQuery] GainfoGroupPageRequest request)
{
return await _gainfoGroupService.GetPageAsync(request);
}
/// <summary>
/// 获取所有档案组
/// 获取档案分组树
/// </summary>
/// <returns></returns>
[HttpGet("list")]
public async Task<List<GainfoGroupResponse>> GetListAsync()
[HttpGet("tree")]
[IgnoreTenant]
public async Task<List<GainfoGroupTreeResponse>> GetTreeAsync()
{
return await _gainfoGroupService.GetListAsync();
return await _gainfoGroupService.GetTreeAsync();
}
/// <summary>
@@ -50,7 +40,7 @@ public class GainfoGroupsController : ControllerBase
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost]
[HttpPost("save")]
public async Task SaveAsync(GainfoGroupSaveRequest request)
{
await _gainfoGroupService.SaveAsync(request);
@@ -73,6 +63,8 @@ public class GainfoGroupsController : ControllerBase
/// <param name="request"></param>
/// <returns></returns>
[HttpPost("assign")]
[IgnoreTenant]
[Transactional]
public async Task AssignGainfosAsync(GainfoAssignRequest request)
{
await _gainfoGroupService.AssignGainfosAsync(request);

View File

@@ -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
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpGet("page")]
public async Task<Pagination<GainfoPageResponse>> GetPageAsync([FromQuery] GainfoPageRequest request)
[HttpPost("page")]
public async Task<Pagination<GainfoPageResponse>> GetPageAsync(GainfoPageRequest request)
{
return await _gainfoService.GetPageAsync(request);
}
@@ -102,6 +100,17 @@ public class GainfosController : ControllerBase
await _gainfoService.UploadFaceAsync(request);
}
/// <summary>
/// 批量上传人脸照片
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost("batchface")]
public async Task UploadFaceAsync(List<GainfoUploadFaceRequest> request)
{
await _gainfoService.UploadFacesAsync(request);
}
/// <summary>
/// 通过token获取人脸照片,失效或者找不到返回HttpStatus404状态码
/// </summary>
@@ -142,72 +151,21 @@ public class GainfosController : ControllerBase
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpGet("gainfolist")]
public async Task<Pagination<GainfoListResponse>> GetGainfoListTaskAsync([FromQuery]GainfoListRequest request)
[HttpGet("list")]
public async Task<Pagination<GainfoListResponse>> GetGainfoListTaskAsync([FromQuery] GainfoListRequest request)
{
return await _gainfoService.GetGainfoListTaskAsync(request);
return await _gainfoService.GetGainfoListAsync(request);
}
/// <summary>
/// 档案详情
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpGet("gainfodetail")]
public async Task<GainfoDetailResponse> GetGainfoDetailTaskAsync([FromQuery] GainfoDetailRequest request)
[HttpGet("detail")]
public async Task<GainfoDetailResponse> GetGainfoDetailAsync([FromQuery] GainfoDetailRequest request)
{
return await _gainfoService.GetGainfoDetailTaskAsync(request);
}
/// <summary>
/// 获取近七天入职人数及列表
/// </summary>
/// <returns></returns>
[HttpGet("weeknumber")]
public async Task<Dictionary<string, Pagination<GainfoDetailResponse>>> GetWeekNumberAsync([FromQuery] WeekNumberRequest request)
{
// 从请求头中获取 Token
string token = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
return await _gainfoService.GetWeekNumberAsync(request,token);
}
/// <summary>
/// 获取近七天离职人数及列表
/// </summary>
/// <returns></returns>
[HttpGet("weeknumberdimission")]
public async Task<Dictionary<string, Pagination<GainfoDetailResponse>>> GetDimissionWeekNumberAsync([FromQuery] WeekNumberRequest request)
{
// 从请求头中获取 Token
string token = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
return await _gainfoService.GetDimissionWeekNumberAsync(request,token);
}
/// <summary>
/// 获取基地在职、离职、考勤数
/// </summary>
/// <returns></returns>
[HttpGet("gainfonumbers")]
public async Task<GainfoGainfoNumbersResponse> GetGainfoNumbersAsync()
{
// 从请求头中获取 Token
string token = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
return await _gainfoService.GetGainfoNumbersAsync(token);
}
/// <summary>
/// 获取本基地管理员列表
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpGet("administrator")]
public async Task<Pagination<GainfoDetailResponse>> 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);
}
}

View File

@@ -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;

View File

@@ -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; }
}

View File

@@ -1,8 +0,0 @@
using Kurisu.Core.DataAccess.Dto;
namespace Dlhis.Basic.Organization.Request
{
public class DepartmentPageRequest : PageDto
{
}
}

View File

@@ -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
{
/// <summary>
/// 档案类型
/// </summary>
[Required]
[EnumDataType(typeof(GainfoStatusEnumType))]
public GainfoStatusEnumType Status { get; set; }
namespace Dlhis.Basic.Organization.Request;
/// <summary>
/// 档案号
/// </summary>
[Required]
public string No { get; set; }
}
public class GainfoDetailRequest
{
/// <summary>
/// 档案类型
/// </summary>
[Required]
[EnumDataType(typeof(GainfoStatusEnumType))]
public GainfoStatusEnumType Status { get; set; }
/// <summary>
/// 档案号
/// </summary>
[Required]
public string No { get; set; }
/// <summary>
/// 是否需要人脸信息
/// </summary>
public bool WithFace { get; set; }
}

View File

@@ -1,7 +0,0 @@
using Kurisu.Core.DataAccess.Dto;
namespace Dlhis.Basic.Organization.Request;
public class GainfoGroupPageRequest: PageDto
{
}

View File

@@ -4,6 +4,11 @@ public class GainfoGroupSaveRequest
{
public long? Id { get; set; }
/// <summary>
/// 上级
/// </summary>
public Guid? PCode { get; set; }
/// <summary>
/// 名称
/// </summary>

View File

@@ -10,10 +10,10 @@ public class GainfoPageRequest : PageDto
/// 档案类型
/// </summary>
[EnumDataType(typeof(GainfoStatusEnumType))]
public GainfoStatusEnumType Status { get; set; } = GainfoStatusEnumType.Working;
public GainfoStatusEnumType? Status { get; set; } = GainfoStatusEnumType.Working;
/// <summary>
/// 部门号(部门树传入)
/// 部门选择
/// </summary>
public List<string> DepartmentNos { get; set; }
public List<TenantDepartmentRequest> DepartmentSelections { get; set; } = new List<TenantDepartmentRequest>();
}

View File

@@ -110,11 +110,6 @@ public class GainfoSaveRequest : IValidatableObject
/// </summary>
public string Werks { get; set; }
/// <summary>
/// 在职状态
/// </summary>
public string Employed { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (GainfoStatus.HasValue && GainfoStatus == GainfoStatusEnumType.Leave)

View File

@@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace Dlhis.Basic.Organization.Request;
public class TenantDepartmentRequest
{
[Required]
public string Tenant { get; set; }
/// <summary>
/// 部门号(部门树传入)
/// </summary>
public List<string> DepartmentNos { get; set; }
}

View File

@@ -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; }
}

View File

@@ -1,7 +1,14 @@
namespace Dlhis.Basic.Organization.Response;
using Newtonsoft.Json;
namespace Dlhis.Basic.Organization.Response;
public class DepartmentTreeResponse
{
/// <summary>
/// 上级
/// </summary>
public string PNo { get; set; }
/// <summary>
/// 部门号
/// </summary>
@@ -12,8 +19,24 @@ public class DepartmentTreeResponse
/// </summary>
public string Name { get; set; }
public string Tenant { get; set; }
/// <summary>
/// 下级部门
/// </summary>
public List<DepartmentTreeResponse> Next { get; set; } = new List<DepartmentTreeResponse>();
[JsonIgnore]
public List<DepartmentTreeResponse> _Next { get; set; } = new List<DepartmentTreeResponse>();
/// <summary>
/// 下级部门
/// </summary>
public List<DepartmentTreeResponse> Next
{
get
{
_Next.ForEach(x => x.Tenant = Tenant);
return _Next;
}
}
}

View File

@@ -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
{
/// <summary>
/// 档案号
/// </summary>
public string No { get; set; }
/// <summary>
/// 档案号
/// </summary>
public string No { get; set; }
/// <summary>
/// SAP内部编号
/// </summary>
public string SapNo { get; set; }
/// <summary>
/// SAP内部编号
/// </summary>
public string SapNo { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 学工号
/// </summary>
public string Code { get; set; }
/// <summary>
/// 学工号
/// </summary>
public string Code { get; set; }
/// <summary>
/// 部门名称
/// </summary>
public string DepartmentName { get; set; }
/// <summary>
/// 部门名称
/// </summary>
public string DepartmentName { get; set; }
/// <summary>
/// 证件类型代码
/// </summary>
public string IdClass { get; set; }
/// <summary>
/// 证件类型代码
/// </summary>
public string IdClass { get; set; }
/// <summary>
/// 身份证号
/// </summary>
public string IdNo { get; set; }
/// <summary>
/// 证件类型名称
/// </summary>
public string ClassName { get; set; }
/// <summary>
/// 性别
/// </summary>
public GenderEnumType Gender { get; set; }
/// <summary>
/// 身份证号
/// </summary>
public string IdNo { get; set; }
/// <summary>
/// 身份证籍贯
/// </summary>
public string NativeName { get; set; }
/// <summary>
/// 性别
/// </summary>
public GenderEnumType Gender { get; set; }
/// <summary>
/// 身份证地址
/// </summary>
public string Address { get; set; }
/// <summary>
/// 性别描述
/// </summary>
public string GenderCN => Gender.GetDescription();
/// <summary>
/// 生日
/// </summary>
public DateTime Birthday { get; set; }
/// <summary>
/// 身份证籍贯
/// </summary>
public string NativeName { get; set; }
/// <summary>
/// 手机号码
/// </summary>
public string Phone { get; set; }
/// <summary>
/// 身份证地址
/// </summary>
public string Address { get; set; }
/// <summary>
/// 岗位
/// </summary>
public string Position { get; set; }
/// <summary>
/// 生日
/// </summary>
public DateTime Birthday { get; set; }
/// <summary>
/// 职等
/// </summary>
public string Grade { get; set; }
/// <summary>
/// 手机号码
/// </summary>
public string Phone { get; set; }
/// <summary>
/// 学历
/// </summary>
public string Degree { get; set; }
/// <summary>
/// 岗位
/// </summary>
public string Position { get; set; }
/// <summary>
/// 人事范围
/// </summary>
public string Werks { get; set; }
/// <summary>
/// 职等
/// </summary>
public string Grade { get; set; }
/// <summary>
/// 成本中心
/// </summary>
public string CostId { get; set; }
/// <summary>
/// 学历
/// </summary>
public string Degree { get; set; }
/// <summary>
/// 已婚/未婚
/// </summary>
public string Marital { get; set; }
/// <summary>
/// 人事范围
/// </summary>
public string Werks { get; set; }
/// <summary>
/// 国家
/// </summary>
public string CountryName { get; set; }
/// <summary>
/// 成本中心
/// </summary>
public string CostId { get; set; }
/// <summary>
/// 已婚/未婚
/// </summary>
public string Marital { get; set; }
/// <summary>
/// 国家
/// </summary>
public string CountryName { get; set; }
/// <summary>
/// 民族代码
/// </summary>
public string NationName { get; set; }
/// <summary>
/// 人脸base64
/// </summary>
public string Face { get; set; }
/// <summary>
/// 民族代码
/// </summary>
public string NationName { get; set; }
/// <summary>
/// 是否在职
/// </summary>
public bool IsActive { get; set; }
}
}

View File

@@ -1,21 +0,0 @@
namespace Dlhis.Basic.Organization.Response;
public class GainfoGroupResponse
{
public long Id { get; set; }
/// <summary>
/// 分组code
/// </summary>
public Guid Code { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
}

View File

@@ -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; }
/// <summary>
/// 分组code
/// </summary>
public Guid Code { get; set; }
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 备注
/// </summary>
public string Remark { get; set; }
/// <summary>
/// 是否可编辑
/// </summary>
public bool Editable { get; set; }
/// <summary>
/// 是否为业务默认分组
/// </summary>
public bool IsDefault { get; set; }
/// <summary>
/// 是否允许设置下级
/// </summary>
public bool AbleSetNext => IsDefault;
[JsonIgnore]
public string TenantId { get; set; }
/// <summary>
/// 下级数据
/// </summary>
public List<GainfoGroupTreeResponse> Next { get; set; }
}

View File

@@ -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
{
/// <summary>
/// 档案号
/// </summary>
public string No { get; set; }
/// <summary>
/// 档案号
/// </summary>
public string No { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 姓名
/// </summary>
public string Name { get; set; }
/// <summary>
/// 学工号
/// </summary>
public string Code { get; set; }
/// <summary>
/// 学工号
/// </summary>
public string Code { get; set; }
/// <summary>
/// 身份证号
/// </summary>
public string IdNo { get; set; }
/// <summary>
/// 身份证号
/// </summary>
public string IdNo { get; set; }
/// <summary>
/// 性别
/// </summary>
public GenderEnumType Gender { get; set; }
/// <summary>
/// 性别
/// </summary>
public GenderEnumType Gender { get; set; }
/// <summary>
/// 手机号码
/// </summary>
public string Phone { get; set; }
/// <summary>
/// 手机号码
/// </summary>
public string Phone { get; set; }
/// <summary>
/// 部门编码
/// </summary>
public string DepartmentNo { get; set; }
/// <summary>
/// 部门编码
/// </summary>
public string DepartmentNo { get; set; }
/// <summary>
/// 部门名称
/// </summary>
public string DepartmentName { get; set; }
/// <summary>
/// 部门名称
/// </summary>
public string DepartmentName { get; set; }
/// <summary>
/// 身份证地址
/// </summary>
public string Address { get; set; }
/// <summary>
/// 在职状态
/// </summary>
public string Employed { get; set; }
}
/// <summary>
/// 身份证地址
/// </summary>
public string Address { get; set; }
}

View File

@@ -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
/// <summary>
/// 性别
/// </summary>
public string Gender { get; set; }
public GenderEnumType Gender { get; set; }
public string GenderDesc => Gender.GetDescription();
/// <summary>
/// 生日
@@ -108,9 +112,4 @@ public class GainfoPageResponse
/// 人事范围
/// </summary>
public string Werks { get; set; }
/// <summary>
/// 在职状态
/// </summary>
public string Employed { get; set; }
}

View File

@@ -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<List<DepartmentTreeResponse>> GetTreeAsync();
Task<Pagination<DepartmentTreeResponse>> GetTreePageAsync(DepartmentPageRequest request);
}

View File

@@ -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<List<ChangeCardResponse>> GetChangeRecordsAsync(string userNo);
}

View File

@@ -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<Pagination<GainfoGroupResponse>> GetPageAsync(GainfoGroupPageRequest request);
Task<List<GainfoGroupResponse>> GetListAsync();
Task<List<GainfoGroupTreeResponse>> GetTreeAsync();
Task SaveAsync(GainfoGroupSaveRequest request);

View File

@@ -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<GainfoUploadFaceRequest> request);
Task<IActionResult> GetFaceAsync(string token);
Task<string> GetFaceByUserCodeAsync(string userCode);
Task<Dictionary<string, string>> GetFaceByUserCodesAsync(GetFaceByUserCodesRequest request);
Task<Pagination<GainfoListResponse>> GetGainfoListTaskAsync(GainfoListRequest request);
Task<Pagination<GainfoListResponse>> GetGainfoListAsync(GainfoListRequest request);
Task<GainfoDetailResponse> GetGainfoDetailTaskAsync(GainfoDetailRequest request);
Task<Dictionary<string, Pagination<GainfoDetailResponse>>> GetWeekNumberAsync(WeekNumberRequest request, string token);
Task<Dictionary<string, Pagination<GainfoDetailResponse>>> GetDimissionWeekNumberAsync([FromQuery] WeekNumberRequest request, string token);
Task<GainfoGainfoNumbersResponse> GetGainfoNumbersAsync(string token);
Task<Pagination<GainfoDetailResponse>> GetAdministratorAsync(WeekNumberRequest request,string token);
Task<GainfoDetailResponse> GetGainfoDetailAsync(GainfoDetailRequest request);
}

View File

@@ -9,7 +9,7 @@ namespace Dlhis.Basic.Organization.Services;
/// </summary>
public interface IGainfoStore : IScopeDependency
{
ISugarQueryable<GainfoEntity> Queryable(GainfoStatusEnumType? gainfoStatus);
ISugarQueryable<GainfoEntity> Queryable(GainfoStatusEnumType? gainfoStatus = null);

View File

@@ -1,4 +1,4 @@
using Dlhis.User.Api.Response;
using Dlhis.Sys.Api.Response;
namespace Dlhis.Basic.Organization.Services;

View File

@@ -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<List<DepartmentTreeResponse>> GetTreeAsync()
{
var departments = await _dbContext.Queryable<DepartmentEntity>().ToListAsync();
var departments = await _dbContext.Queryable<DepartmentEntity>().Select<DepartmentTreeResponse>().ToListAsync();
//00001000 一定是这个开头的
var tops = departments.Where(x => x.PNo == "00001000").Adapt<List<DepartmentTreeResponse>>();
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<DepartmentTreeResponse>();
i.PNo = x.Code;
return i;
}).ToList()
})
.ToList();
}
public async Task<Pagination<DepartmentTreeResponse>> GetTreePageAsync(DepartmentPageRequest request)
private void RecursionGetNext(DepartmentTreeResponse current, List<DepartmentTreeResponse> data)
{
var departments = await _dbContext
.Queryable<DepartmentEntity>()
.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<List<DepartmentTreeResponse>>();
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<DepartmentTreeResponse>
{
PageIndex = request.PageIndex,
PageSize = request.PageSize,
Total = tops.Count, // 使用原始列表的总数进行分页
Data = pagedData
};
return pagination;
}
private void RecursionGetNext(DepartmentTreeResponse current, List<DepartmentEntity> departments)
{
var next = departments.Where(x => x.PNo == current.No).ToList();
current.Next = next.Adapt<List<DepartmentTreeResponse>>();
var next = data.Where(x => x.PNo == current.No).ToList();
current._Next = next;
foreach (var item in next)
{
RecursionGetNext(item.Adapt<DepartmentTreeResponse>(), departments);
RecursionGetNext(item, data);
}
}
}

View File

@@ -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<List<ChangeCardResponse>> GetChangeRecordsAsync(string userNo)
{
return await _dbContext.Queryable<GainfoCardRecordEntity>()
.LeftJoin<GainfoEntity>((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();
}
}

View File

@@ -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<Pagination<GainfoGroupResponse>> GetPageAsync(GainfoGroupPageRequest request)
public async Task<List<GainfoGroupTreeResponse>> GetTreeAsync()
{
return await _dbContext.Queryable<GainfoGroupEntity>()
.Select<GainfoGroupResponse>()
.ToPageListAsync(request);
}
var gainfoGroups = await _dbContext.Queryable<GainfoGroupEntity>()
.Select<GainfoGroupTreeResponse>()
.ToListAsync();
var tops = gainfoGroups.Where(x => !x.PCode.HasValue).ToList();
tops.ForEach(x =>
{
RecursionGetNext(x, gainfoGroups);
});
public async Task<List<GainfoGroupResponse>> GetListAsync()
{
return await _dbContext.Queryable<GainfoGroupEntity>()
.Select<GainfoGroupResponse>()
.ToListAsync();
return tops;
}
public async Task SaveAsync(GainfoGroupSaveRequest request)
{
await CheckDuplicateNameAsync(request);
try
{
//todo
_dbContext.Client.QueryFilter.ClearAndBackup<ITenantId>();
var defaultGroup = await _dbContext.Queryable<GainfoGroupEntity>().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<GainfoGroupEntity>().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<GainfoGroupRelationshipEntity>().Where(x => x.GroupCode == request.GroupCode).ToListAsync();
await _dbContext.DeleteReallyAsync(relations);
request.UserNos = await _dbContext.Queryable<GainfoEntity>().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<GainfoGroupEntity>().Where(x => x.Code == request.GroupCode).FirstAsync();
(pGrp?.Editable == false && pGrp?.IsDefault == true).ThrowIfTrue("不可分配人员到当前分组");
}
}).ToList();
var existsNextGroup = await _dbContext.Queryable<GainfoGroupEntity>().Where(x => x.PCode == request.GroupCode).AnyAsync();
existsNextGroup.ThrowIfTrue("存在下级分组,无法分配人员");
await _dbContext.InsertAsync(entities);
await _devicePermissionService.GenerateDeviceTasksAsync(async () =>
{
var relations = await _dbContext.Queryable<GainfoGroupRelationshipEntity>().Where(x => x.GroupCode == request.GroupCode).ToListAsync();
await _dbContext.DeleteReallyAsync(relations);
request.UserNos = await _dbContext.Queryable<GainfoEntity>().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<GainfoGroupTreeResponse> 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);
}
}
}

View File

@@ -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<GainfoService> _logger;
public GainfoService(IGainfoStore gainfoStore,
IDbContext dbContext,
ICurrentUser currentUser,
IDevicePermissionService devicePermissionService,
RedisCache redisCache,
IServiceProvider serviceProvider,
IConfiguration configuration,
ILogger<GainfoService> 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
/// <returns></returns>
public async Task<Pagination<GainfoPageResponse>> GetPageAsync(GainfoPageRequest request)
{
request.DepartmentNos = await GetRecursionDepartmentNosAsync(request.DepartmentNos);
var currentTenantId = _currentUser.GetStringTenantId();
var tenant = _currentUser.GetStringTenantId();
//根据组织架构搜索档案
Expression<Func<GainfoEntity, bool>> where = LinqExpressionExtension.False<GainfoEntity>();
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<GainfoPageResponse>()
.ToPageListAsync(request);
@@ -104,7 +123,6 @@ public class GainfoService : IGainfoService
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[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<GainfoCurrentResponse> 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<DepartmentEntity>((ga, department) => ga.DepartmentNo == department.No)
.LeftJoin<CanteenUserEntity>((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<IActionResult> GetFaceAsync(string token)
{
return await Scoped.Request.Value.CreateAsync<IActionResult>(async (sp) =>
var timeLimitedDataProtector = _serviceProvider.GetService<IDataProtectionProvider>()
.CreateProtector("down:face")
.ToTimeLimitedDataProtector();
try
{
var timeLimitedDataProtector = sp.GetService<IDataProtectionProvider>()
.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<IConfiguration>();
var faceConnectionString = configuration.GetSection("SqlSugarOptions:FaceConnectionString").Value;
if (faceConnectionString.IsEmpty())
{
_logger.LogWarning("暂不支持获取人脸");
return new NotFoundResult();
}
//todo 切换连接字符串
var dbContext = sp.GetService<IDbContext>();
dbContext.Client.CurrentConnectionConfig.ConnectionString = faceConnectionString;
var face = await dbContext.Queryable<GainfoFaceEntity>().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<GainfoFaceEntity>().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<GainfoFaceEntity>().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<IConfiguration>();
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<IDbContext>();
//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<GainfoUploadFaceRequest> 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<GainfoFaceEntity>().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<string> GetFaceByUserCodeAsync(string userCode)
{
var gainfo = await _dbContext.Queryable<GainfoEntity>().Where(x => x.Code == userCode).SingleAsync();
gainfo.ThrowIfNull($"{userCode}人员信息不存在");
return await Scoped.Request.Value.CreateAsync<string>(async (sp) =>
{
var configuration = sp.GetService<IConfiguration>();
var dbContext = _dbContext.ChangeDb("face");
var faceConnectionString = configuration.GetSection($"{nameof(SqlSugarOptions)}:FaceConnectionString").Value;
if (faceConnectionString.IsEmpty())
{
throw new UserFriendlyException("暂不支持获取人脸");
}
var face = await dbContext.Queryable<GainfoFaceEntity>()
.Where(x => x.UserCode == userCode)
.SingleAsync();
var dbContext = sp.GetService<IDbContext>();
//todo 切换连接字符串
dbContext.Client.CurrentConnectionConfig.ConnectionString = faceConnectionString;
face.ThrowIfNull("人脸照片尚未录入");
var face = await dbContext.Queryable<GainfoFaceEntity>()
.Where(x => x.UserCode == userCode)
.SingleAsync();
var timeLimitedDataProtector = _serviceProvider.GetService<IDataProtectionProvider>()
.CreateProtector("down:face")
.ToTimeLimitedDataProtector();
face.ThrowIfNull("人脸照片尚未录入");
var faceUrl = _configuration.GetSection("FaceUrlOptions:Url").Value;
var limit = _configuration.GetSection("FaceUrlOptions:Limit").Get<int>();
var timeLimitedDataProtector = sp.GetService<IDataProtectionProvider>()
.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<int>();
var dataProtectionProvider = sp.GetService<IDataProtectionProvider>();
var key = $"face:{userCode}";
var token = timeLimitedDataProtector.Protect(key, TimeSpan.FromSeconds(limit));
return faceUrl + token;
});
return faceUrl + token;
}
public async Task<Dictionary<string, string>> GetFaceByUserCodesAsync(GetFaceByUserCodesRequest request)
@@ -314,7 +350,7 @@ public class GainfoService : IGainfoService
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task<Pagination<GainfoListResponse>> GetGainfoListTaskAsync(GainfoListRequest request)
public async Task<Pagination<GainfoListResponse>> GetGainfoListAsync(GainfoListRequest request)
{
var page = await _gainfoStore.Queryable(request.Status)
.LeftJoin<DepartmentEntity>((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;
}
/// <summary>
/// 档案详情
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public async Task<GainfoDetailResponse> GetGainfoDetailTaskAsync(GainfoDetailRequest request)
public async Task<GainfoDetailResponse> GetGainfoDetailAsync(GainfoDetailRequest request)
{
var info = await _gainfoStore.Queryable(request.Status)
.LeftJoin<DepartmentEntity>((ga, de) => ga.DepartmentNo == de.No)
.LeftJoin<BasicTypeEntity>((ga, de, type) => ga.Native == type.Code && type.Type == "native")
.LeftJoin<PositionEntity>((ga, de, type, posi) => ga.Position == posi.Name)
.LeftJoin<BasicTypeEntity>((ga, de, type, posi, type2) => ga.CountryNo == type2.Code && type2.Type == "country")
.LeftJoin<BasicTypeEntity>((ga, de, type, posi, type2, type3) => ga.Nation == type3.Code && type3.Type == "nation")
.LeftJoin<BasicTypeEntity>((ga, de, type, posi, type2, type3) => ga.Nation == type3.Code && type3.Type == "nation")
.LeftJoin<BasicTypeEntity>((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<GainfoFaceEntity>().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<Pagination<GainfoDetailResponse>> GetAdministratorAsync( WeekNumberRequest request, string token)
{
var werks = GetClaimValueFromToken(token, "tenant");
var info = await _gainfoStore.Queryable(GainfoStatusEnumType.Working)
.LeftJoin<DepartmentEntity>((ga, de) => ga.DepartmentNo == de.No)
.LeftJoin<BasicTypeEntity>((ga, de, type) => ga.Native == type.Code && type.Type == "native")
.LeftJoin<PositionEntity>((ga, de, type, posi) => ga.Position == posi.Name)
.LeftJoin<BasicTypeEntity>((ga, de, type, posi, type2) => ga.CountryNo == type2.Code && type2.Type == "country")
.LeftJoin<BasicTypeEntity>((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 ------------------------------------------------
/// <summary>
@@ -561,7 +566,7 @@ public class GainfoService : IGainfoService
/// <returns></returns>
private async Task<List<string>> GetRecursionDepartmentNosAsync(List<string> departmentNos)
{
if (departmentNos == null || departmentNos.Count == 0)
if (departmentNos.IsEmpty())
{
return new List<string>();
}
@@ -579,13 +584,10 @@ public class GainfoService : IGainfoService
void RecursionGetNext(string currentCode, List<DepartmentEntity> departments, List<string> 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);
}
}

View File

@@ -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;

View File

@@ -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<IConfiguration>();
return new List<ConnectionConfig>
{
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<ISqlSugarClient>();
//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<IDbContext>();
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<ISqlSugarClient>();
//var faceConnectionString = Configuration.GetSection("SqlSugarOptions:FaceConnectionString").Value;
//dbFace.CurrentConnectionConfig.ConnectionString = faceConnectionString;
//dbFace.DbMaintenance.CreateDatabase();
//dbFace.CodeFirst.InitTables<GainfoFaceEntity>();
var faceDb = db.ChangeDb("face");
faceDb.Client.DbMaintenance.CreateDatabase();
faceDb.Client.CodeFirst.InitTables<GainfoFaceEntity>();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
/// <summary>
/// 考勤首页
/// </summary>
/// <returns></returns>
[HttpGet("attendance-summary")]
public async Task<AttendanceSummaryResponse> GetKaoQingSummary()
{
return await _summaryService.GetAttendanceSummaryAsync();
}
/// <summary>
/// 门禁首页数据
/// </summary>
/// <returns></returns>
[HttpGet("door-summary")]
public async Task<DoorHomeDataResponse> GetDoorHomeDataAsync()
{
return await _summaryService.GetDoorHomeDataAsync();
}
}

View File

@@ -0,0 +1,8 @@
namespace Dlhis.Basic.Summary.Dto;
public class TrendDto
{
public DateTime Date { get; set; }
public int Count { get; set; }
}

View File

@@ -0,0 +1,24 @@
namespace Dlhis.Basic.Summary.Response;
public class AttendanceDetailSummary
{
/// <summary>
/// 考勤总人数
/// </summary>
public int Total => Employed + Unemployed;
/// <summary>
/// 在职人数
/// </summary>
public int Employed { get; set; }
/// <summary>
/// 非在职人数
/// </summary>
public int Unemployed { get; set; }
/// <summary>
/// 人员趋势 7天
/// </summary>
public GainfoTrendResponse GainfoTrend { get; set; }
}

View File

@@ -0,0 +1,31 @@
using Dlhis.Basic.Summary.Dto;
namespace Dlhis.Basic.Summary.Response;
public class AttendanceDeviceSummary
{
/// <summary>
/// 总数
/// </summary>
public int Total { get; set; }
/// <summary>
/// 在线数
/// </summary>
public int OnlineCount { get; set; }
/// <summary>
/// 启用数
/// </summary>
public int EnableCount { get; set; }
/// <summary>
/// 离线数
/// </summary>
public int OfflineCount => Total - OnlineCount;
/// <summary>
/// 新增设备趋势 30天内
/// </summary>
public List<TrendDto> Trend { get; set; }
}

View File

@@ -0,0 +1,9 @@
namespace Dlhis.Basic.Summary.Response
{
public class AttendanceSummaryResponse
{
public AttendanceDeviceSummary DeviceSummary { get; set; }
public AttendanceDetailSummary AttendanceDetailSummary { get; set; }
}
}

View File

@@ -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<TrendDto> Trend { get; set; }
}
}

View File

@@ -0,0 +1,10 @@
using Dlhis.Basic.Summary.Dto;
namespace Dlhis.Basic.Summary.Response
{
public class GainfoTrendResponse
{
public List<TrendDto> EmployedTrend { get; set; }
public List<TrendDto> UnemployedTrend { get; set; }
}
}

View File

@@ -0,0 +1,11 @@
using Dlhis.Basic.Device.Response;
using Dlhis.Basic.Summary.Response;
namespace Dlhis.Basic.Summary.Services;
public interface ISummaryService : IScopeDependency
{
Task<AttendanceSummaryResponse> GetAttendanceSummaryAsync();
Task<DoorHomeDataResponse> GetDoorHomeDataAsync();
}

View File

@@ -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<AttendanceSummaryResponse> 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<DeviceEntity>().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<DeviceRegistryEntity>()
.LeftJoin<DeviceEntity>((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<DoorHomeDataResponse> GetDoorHomeDataAsync()
{
var devices = await _dbContext.Queryable<DeviceEntity>().Where(x => x.Type == DeviceTypeEnumType.Door).ToListAsync();
var offDevices = await GetOfflineDevicesCount(devices);
//获取近30天并转化为"yyyy-MM-dd"集合
DateTime currentDate = DateTime.Now;
List<string> times = new List<string>();
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<TrendDto>();
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<TrendDto> FixTrend(IEnumerable<TrendDto> 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();
}
/// <summary>
/// 获取离线设备
/// </summary>
/// <returns></returns>
private async Task<List<DeviceEntity>> GetOfflineDevicesCount(List<DeviceEntity> devices)
{
var now = DateTime.Now;
var deviceUniqueNos = devices.Select(x => x.UniqueNo).ToList();
var deviceRegistries = await _dbContext.Queryable<DeviceRegistryEntity>()
.LeftJoin<DeviceEntity>((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<string>();
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;
}
}

View File

@@ -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",

View File

@@ -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,

View File

@@ -17,8 +17,9 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Dlhis.Common.Api\Dlhis.Common.Api.csproj" />
<ProjectReference Include="..\Dlhis.Basic.Api\Dlhis.Basic.Api.csproj" />
<ProjectReference Include="..\Dlhis.Entity\Dlhis.Entity.csproj" />
<ProjectReference Include="..\Dlhis.Sys.Api\Dlhis.Sys.Api.csproj" />
</ItemGroup>
<ItemGroup>
@@ -35,7 +36,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Kurisu.AspNetCore" Version="0.5.2" />
<PackageReference Include="Kurisu.AspNetCore" Version="0.7.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.4" />
</ItemGroup>
</Project>

View File

@@ -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"

View File

@@ -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
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[DataPermission]
[HttpGet]
public async Task<Pagination<BalanceRecordResponse>> GetPageAsync([FromQuery] BalanceRecordPageRequest request)
{
@@ -39,6 +42,7 @@ public class BalancesController : ControllerBase
/// <param name="request"></param>
/// <returns></returns>
[HttpGet("device/page")]
[IgnoreTenant]
public async Task<Pagination<BalanceRecordResponse>> GetPageByDeviceUniqueNoGetAsync([FromQuery] BalanceRecordPageByDeviceUniqueNoRequest request)
{
return await _balanceService.GetPageByDeviceUniqueNoAsync(request);
@@ -50,6 +54,7 @@ public class BalancesController : ControllerBase
/// <param name="request"></param>
/// <returns></returns>
[HttpPost("device/page")]
[IgnoreTenant]
public async Task<Pagination<BalanceRecordResponse>> GetPageByDeviceUniqueNoPostAsync(BalanceRecordPageByDeviceUniqueNoRequest request)
{
return await _balanceService.GetPageByDeviceUniqueNoAsync(request);
@@ -72,6 +77,7 @@ public class BalancesController : ControllerBase
/// <param name="request"></param>
/// <returns></returns>
[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<Pagination<BalanceRecordCostIdResponse>> RecordByCostIdAsync([FromQuery] BalanceRecordPageByCostIdRequest request)
{
return await _balanceService.RecordByCostIdAsync(request);
return await _balanceService.RecordByCostIdAsync(request);
}
/// <summary>
@@ -132,4 +138,81 @@ public class BalancesController : ControllerBase
{
return await _balanceService.BalanceRecordAnalysisAsync(request);
}
/// <summary>
/// 获取消费机总数、离线设备数量、营业点数量、可消费人数、人均日消费额
/// </summary>
/// <returns></returns>
[HttpGet("consumenumbers")]
public async Task<ConsumeNumberResponse> GetConsumeNumberAsync()
{
return await _balanceService.GetConsumeNumberAsync();
}
/// <summary>
/// 消费人数详情
/// </summary>
/// <returns></returns>
[HttpGet("spendingDetails")]
public async Task<Pagination<SpendingDetailResponse>> GetSpendingDetailsAsync([FromQuery]SpendingDetailRequest request)
{
return await _balanceService.GetSpendingDetailsAsync(request);
}
/// <summary>
/// 日消费均额
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpGet("averageDaily")]
public async Task<Pagination<AverageDailyResponse>> AverageDailyAsync([FromQuery] AverageDailyRequest request)
{
return await _balanceService.AverageDailyAsync(request);
}
/// <summary>
/// 获取消费机详情
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpGet("consumeDevices")]
public async Task<Pagination<DevicePageResponse>> GetConsumeDeviceAsync([FromQuery] GetConsumeDeviceRequest request)
{
return await _balanceService.GetConsumeDeviceAsync(request);
}
/// <summary>
/// 营业点增加配置设备接口
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPost("disposition")]
public async Task DispositionDeviceAsync([FromQuery] DispositionDeviceRequest request)
{
await _balanceService.DispositionDeviceAsync(request);
}
/// <summary>
/// 电子餐券申请记录查询
/// </summary>
/// <returns></returns>
[HttpGet("oaemealvoucher")]
public async Task<Pagination<OaEMealVoucherResponse>> GetOaEMealVoucherAsync([FromQuery] OaEMealVoucherRequest request)
{
return await _balanceService.GetOaEMealVoucherAsync(request);
}
/// <summary>
/// 电子餐券使用记录查询
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpGet("oaemealvoucherdetail")]
public async Task<Pagination<OaEMealVoucherDetailResponse>> GetOaEMealVoucherDetailAsync([FromQuery] OaEMealVoucherDetailRequest request)
{
return await _balanceService.GetOaEMealVoucherDetailAsync(request);
}
}

View File

@@ -39,9 +39,9 @@ public class OrdersController : ControllerBase
/// <param name="request"></param>
/// <returns></returns>
[HttpPost]
[IgnoreTenant]
public async Task<OrderResultResponse> AddAsync(OrderAddRequest request)
{
return await _orderService.AddAsync(request);
}
}

View File

@@ -0,0 +1,8 @@
using Kurisu.Core.DataAccess.Dto;
namespace Dlhis.Canteen.Order.Request
{
public class AverageDailyRequest : PageDto
{
}
}

View File

@@ -7,6 +7,17 @@ namespace Dlhis.Canteen.Order.Request;
public class BalanceRecordPageRequest : PageDto, IValidatableObject
{
/// <summary>
/// 营业点codes
/// </summary>
public List<Guid?> BusinessplaceCodes { get; set; }
/// <summary>
/// 餐次
/// </summary>
public List<string> ShiftTypes { get; set; }
/// <summary>
/// 查询日期范围类型 DateTimeSearchEnumType
/// </summary>

View File

@@ -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; }
}
}

View File

@@ -0,0 +1,9 @@
namespace Dlhis.Canteen.Order.Request
{
public class DispositionDeviceRequest
{
public string PlaceCode { get; set; }
public List<string> UniqueNos { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More