feat: 通行和空开

This commit is contained in:
2025-10-09 14:31:59 +08:00
parent f71e58dfc1
commit efa0962f73
12 changed files with 274 additions and 12 deletions

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Kurisu.RemoteCall" Version="0.4.8" />
</ItemGroup>
<ItemGroup>
<Folder Include="Response\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,27 @@
using System.Net;
using Kurisu.RemoteCall.Abstractions;
using Newtonsoft.Json;
namespace Dlhis.Dormitory.Api;
public class DormitoryRemoteCallResultHandler : IRemoteCallResultHandler
{
public TResult Handle<TResult>(HttpStatusCode statusCode, string responseBody)
{
ApiResult<TResult> apiResult = JsonConvert.DeserializeObject<ApiResult<TResult>>(responseBody);
if ((statusCode < HttpStatusCode.OK ? 0 : (statusCode <= (HttpStatusCode)299 ? 1 : 0)) != 0 && apiResult.Code == 200)
return apiResult.Data;
if (!string.IsNullOrEmpty(apiResult?.Message))
throw new Exception(apiResult.Message);
throw new HttpRequestException($"{statusCode.ToString()} {((int)statusCode).ToString()}");
}
internal class ApiResult<T>
{
public string Message { get; set; }
public T Data { get; set; }
public int Code { get; set; }
}
}

View File

@@ -0,0 +1,16 @@
using Dlhis.Dormitory.Api.Request;
using Kurisu.RemoteCall.Attributes;
namespace Dlhis.Dormitory.Api;
[RequestLog]
[RequestResultHandler(typeof(DormitoryRemoteCallResultHandler))]
[EnableRemoteClient(Name = "dormitory-service", BaseUrl = "https://apartment.jinkosolar.com/pcapi")]
public interface IDormitoryRoomPowerSwitchApi
{
[Post("/api/Energy/DhIccElectricityControl")]
Task SwitchAsync(PowerSwitchRequest request);
[Get("/api/Users/GetDormitoryInfoByUserCode")]
Task<T> GetUserRoomInfoAsync<T>(string userCode);
}

View File

@@ -0,0 +1,21 @@
using Newtonsoft.Json;
namespace Dlhis.Dormitory.Api.Request;
public class PowerSwitchRequest
{
public PowerSwitchRequest(string roomid, int power)
{
this.RoomId = roomid;
this.Power = power;
}
[JsonProperty("roomid")]
public string RoomId { get; set; }
/// <summary>
/// 0 断电 1 上电
/// </summary>
[JsonProperty("power")]
public int Power { get; set; }
}

View File

@@ -10,6 +10,6 @@ public class DormitoryRoomPersonEntity : SugarBaseEntity, ISoftDeleted
public string RoomId { get; set; }
public string UserCode { get; set; }
public string UserName { get; set; }
public bool? In { get; set; }
public bool In { get; set; }
public bool IsDeleted { get; set; }
}

View File

@@ -0,0 +1,12 @@
using Dlhis.ScheduledTask.DeviceUploadLog.EventBus.Message;
using Kurisu.AspNetCore.EventBus.Abstractions.Handler;
namespace Dlhis.ScheduledTask.DeviceUploadLog.EventBus.Handler;
public class DormitoryPowerSwitchHandler : IAsyncChannelHandler<DormitoryPowerSwitchMessage>
{
public async Task InvokeAsync(IServiceProvider serviceProvider, DormitoryPowerSwitchMessage message)
{
await Task.CompletedTask;
}
}

View File

@@ -0,0 +1,8 @@
using Kurisu.AspNetCore.EventBus.Abstractions;
namespace Dlhis.ScheduledTask.DeviceUploadLog.EventBus.Message;
public class DormitoryPowerSwitchMessage : IAsyncChannelMessage
{
}

View File

@@ -0,0 +1,18 @@
namespace Dlhis.ScheduledTask.DeviceUploadLog.Response;
public class DormitoryResponse
{
public string RoomId { get; set; }
public string RoomNo { get; set; }
public string HotelId { get; set; }
public bool MCB { get; set; }
public List<DormitoryPerson> Persons { get; set; }
}
public class DormitoryPerson
{
public string UserCode { get; set; }
public string UserName { get; set; }
}

View File

@@ -1,11 +1,14 @@
using Dlhis.Basic.Api;
using Dlhis.Basic.Api.Requests;
using Dlhis.Constants;
using Dlhis.Dormitory.Api;
using Dlhis.Dormitory.Api.Request;
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Basic.Enums;
using Dlhis.Entity.Device.Entities;
using Dlhis.Entity.Device.Enums;
using Dlhis.Entity.Device.Models;
using Dlhis.Entity.Dormitory.Entity;
using Dlhis.Jip.Api;
using Dlhis.Jip.Api.Requests;
using Dlhis.ScheduledTask.DeviceUploadLog.Request;
@@ -22,18 +25,21 @@ namespace Dlhis.ScheduledTask.DeviceUploadLog.Services.Implements;
public class DeviceUploadLogService : IDeviceUploadLogService
{
private readonly IDbContext _dbContext;
private readonly IDormitoryRoomPowerSwitchApi _dormitoryRoomPowerSwitchApi;
private readonly ICurrentUser _currentUser;
private readonly IJinkoJipApi _jinkoJipApi;
private readonly ILogger<DeviceUploadLogService> _logger;
private readonly IDingTalkApi _dingTalkApi;
public DeviceUploadLogService(IDbContext dbContext,
IDormitoryRoomPowerSwitchApi dormitoryRoomPowerSwitchApi,
ICurrentUser currentUser,
IJinkoJipApi jinkoJipApi,
ILogger<DeviceUploadLogService> logger,
IDingTalkApi dingTalkApi)
{
_dbContext = dbContext;
_dormitoryRoomPowerSwitchApi = dormitoryRoomPowerSwitchApi;
_currentUser = currentUser;
_jinkoJipApi = jinkoJipApi;
_logger = logger;
@@ -44,7 +50,11 @@ public class DeviceUploadLogService : IDeviceUploadLogService
{
var deviceUniqueNos = requests.Select(x => x.DeviceUniqueNo).Distinct().ToList();
var devices = await _dbContext.Queryable<DeviceEntity>().Where(x => deviceUniqueNos.Contains(x.UniqueNo)).ToListAsync();
var existsDeviceUniqueNos = devices.Select(x => x.UniqueNo).ToList();
var existsDeviceUniqueNos = devices.Select(x => x.UniqueNo).ToList();
var deviceCodes = devices.Where(x => x.DeployDirection.HasValue).Select(x => x.Code).Distinct().ToList();
var eFenceDeviceRelations = await _dbContext.Queryable<EFenceDeviceRelationEntity>().Where(x => deviceCodes.Contains(x.DeviceCode)).ToListAsync();
var eFenceDeviceMap = eFenceDeviceRelations.ToDictionary(x => x.DeviceCode, x => x.EFenceCode);
var userCodes = requests.Where(x => x.UserUniqueNo.IsPresent()).Select(x => x.UserUniqueNo).Distinct().ToList();
var gainfo1 = await _dbContext.Queryable<GainfoEntity>().Where(x => userCodes.Contains(x.Code)).Select(x => new { x.Code, x.Name, x.Type }).ToListAsync();
@@ -111,6 +121,11 @@ public class DeviceUploadLogService : IDeviceUploadLogService
? DeviceUploadLogTypeEnumType.Attendance
: DeviceUploadLogTypeEnumType.Door;
if (eFenceDeviceMap.TryGetValue(device.Code, out var eFenceCore))
{
await HandlerPowerSwitchAsync(eFenceCore, request.UserUniqueNo, device.DeployDirection.Value);
}
var workGridInfo = new WorkGridInfo();
var currentWorkGridTimePeriods = workGridTimePeriods.Where(x => x.TenantId == device.TenantId).ToList();
var currentWorkGridSchedules = workGridSchedules.Where(x => x.UserCode == request.UserUniqueNo).ToList();
@@ -262,6 +277,134 @@ public class DeviceUploadLogService : IDeviceUploadLogService
await _dbContext.InsertAsync(inserts);
}
/// <summary>
/// 宿舍只能空开触发
/// </summary>
/// <param name="eFenceCode"></param>
/// <param name="userCode"></param>
/// <param name="directionType"></param>
private async Task HandlerPowerSwitchAsync(Guid eFenceCode, string userCode, DeviceDeployDirectionEnumType directionType)
{
try
{
//调用获取人员信息接口
var roomInfo = await _dormitoryRoomPowerSwitchApi.GetUserRoomInfoAsync<DormitoryResponse>(userCode);
if (!roomInfo.MCB)
{
return;
}
var room = await _dbContext.Queryable<DormitoryRoomEntity>().Where(x => x.RoomId == roomInfo.RoomId).SingleAsync();
if (room == null)
{
room = new DormitoryRoomEntity
{
EFenceCode = eFenceCode,
RoomId = roomInfo.RoomId,
RoomNo = roomInfo.RoomNo,
HotelId = roomInfo.HotelId,
PowerSwitch = true
};
await _dbContext.InsertAsync(room);
var personEntities = roomInfo.Persons.Select(x => new DormitoryRoomPersonEntity
{
RoomId = roomInfo.RoomId,
UserCode = x.UserCode,
UserName = x.UserName,
In = true,
IsDeleted = false
})
.ToList();
await _dbContext.InsertAsync(personEntities);
var person = await _dbContext.Queryable<DormitoryRoomPersonEntity>()
.Where(x => x.RoomId != roomInfo.RoomId && x.UserCode == userCode)
.FirstAsync();
if (person != null)
{
await _dbContext.DeleteAsync(person);
}
}
else
{
var existsPersons = await _dbContext.Queryable<DormitoryRoomPersonEntity>()
.Where(x => x.RoomId == roomInfo.RoomId)
.ToListAsync();
//根据roomInfo.Persons和existsPersons不在当前房间的人
var toDeletePersons = existsPersons.Where(x => roomInfo.Persons.All(p => p.UserCode != x.UserCode)).ToList();
if (toDeletePersons.Any())
{
await _dbContext.DeleteAsync(toDeletePersons);
}
//需要新增的
var toAddPersons = roomInfo.Persons.Where(x => existsPersons.All(p => p.UserCode != x.UserCode)).ToList();
if (toAddPersons.Any())
{
var personEntities = toAddPersons.Select(x => new DormitoryRoomPersonEntity()
{
RoomId = roomInfo.RoomId,
UserCode = x.UserCode,
UserName = x.UserName,
In = true,
IsDeleted = false
}).ToList();
await _dbContext.InsertAsync(personEntities);
}
room.EFenceCode = eFenceCode;
await _dbContext.UpdateAsync(room);
}
//如果DormitoryRoomPersonEntity.in都为false,断电, 否则上电(前提是DormitoryRoomEntity.PowerSwitch==false)
// 查询房间所有人员
var persons = await _dbContext.Queryable<DormitoryRoomPersonEntity>()
.Where(x => x.RoomId == roomInfo.RoomId)
.ToListAsync();
persons.ForEach(x =>
{
if (x.UserCode == userCode)
{
x.In = directionType == DeviceDeployDirectionEnumType.Entry;
}
});
await _dbContext.UpdateAsync(persons);
// 如果所有人都不在房间,且当前房间是通电状态,则断电
if (persons.All(x => !x.In))
{
if (room.PowerSwitch)
{
room.PowerSwitch = false; // 断电
await _dbContext.UpdateAsync(room);
await _dormitoryRoomPowerSwitchApi.SwitchAsync(new PowerSwitchRequest(room.RoomId, 0));
}
}
else
{
// 只在房间断电状态时才上电
if (!room.PowerSwitch)
{
room.PowerSwitch = true; // 上电
await _dbContext.UpdateAsync(room);
await _dormitoryRoomPowerSwitchApi.SwitchAsync(new PowerSwitchRequest(room.RoomId, 1));
}
}
}
catch (Exception e)
{
_logger.LogError(e, "宿舍空开触发失败, userCode:{userCode} directionType:{directionType} error:{error}", userCode, directionType, e.Message);
}
}
public async Task<List<DeviceUploadLogResponse>> GetMyRecordsAsync(DeviceUploadLogMyRecordRequest request)
{
var jinkoCode = _currentUser.GetUserClaim(UserClaimConstants.JinkoCode) ?? string.Empty;

View File

@@ -16,6 +16,9 @@
<ItemGroup>
<Compile Remove="log\**" />
<Content Remove="log\**" />
<Content Include="..\..\.dockerignore">
<Link>.dockerignore</Link>
</Content>
<EmbeddedResource Remove="log\**" />
<None Remove="log\**" />
<Content Update="appsettings.Development.json">
@@ -34,6 +37,7 @@
<ProjectReference Include="..\Dlhis.Basic.Api\Dlhis.Basic.Api.csproj" />
<ProjectReference Include="..\Dlhis.Canteen.Api\Dlhis.Canteen.Api.csproj" />
<ProjectReference Include="..\Dlhis.Constants\Dlhis.Constants.csproj" />
<ProjectReference Include="..\Dlhis.Dormitory.Api\Dlhis.Dormitory.Api.csproj" />
<ProjectReference Include="..\Dlhis.Entity\Dlhis.Entity.csproj" />
<ProjectReference Include="..\Dlhis.Jip.Api\Dlhis.Jip.Api.csproj" />
<ProjectReference Include="..\Dlhis.Sys.Api\Dlhis.Sys.Api.csproj" />

View File

@@ -1,12 +1,8 @@
# 请参阅 https://aka.ms/customizecontainer 以了解如何自定义调试容器,以及 Visual Studio 如何使用此 Dockerfile 生成映像以更快地进行调试。
# 此阶段用于在快速模式(默认为调试配置)下从 VS 运行时
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
# 此阶段用于生成服务项目
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
@@ -18,18 +14,17 @@ COPY ["Src/Dlhis.Entity/Dlhis.Entity.csproj", "Src/Dlhis.Entity/"]
COPY ["Src/Dlhis.DataPermission.Model/Dlhis.DataPermission.Model.csproj", "Src/Dlhis.DataPermission.Model/"]
COPY ["Src/Dlhis.Jip.Api/Dlhis.Jip.Api.csproj", "Src/Dlhis.Jip.Api/"]
COPY ["Src/Dlhis.Sys.Api/Dlhis.Sys.Api.csproj", "Src/Dlhis.Sys.Api/"]
RUN dotnet restore "./Src/Dlhis.ScheduledTask/Dlhis.ScheduledTask.csproj"
COPY ["Src/Dlhis.Dormitory.Api/Dlhis.Dormitory.Api.csproj", "Src/Dlhis.Dormitory.Api/"]
RUN dotnet restore "Src/Dlhis.ScheduledTask/Dlhis.ScheduledTask.csproj"
COPY . .
WORKDIR "/src/Src/Dlhis.ScheduledTask"
RUN dotnet build "./Dlhis.ScheduledTask.csproj" -c $BUILD_CONFIGURATION -o /app/build
# 此阶段用于发布要复制到最终阶段的服务项目
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./Dlhis.ScheduledTask.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
# 此阶段在生产中使用,或在常规模式下从 VS 运行时使用(在不使用调试配置时为默认值)
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "Dlhis.ScheduledTask.dll"]
ENTRYPOINT ["dotnet", "Dlhis.ScheduledTask.dll"]