This commit is contained in:
fuenmao
2026-11-12 15:45:52 +08:00
parent 61e25175db
commit 718b138033
30 changed files with 1511 additions and 99 deletions
@@ -1,5 +1,6 @@
using Evergreen.Health.Application.Health.Entities;
using Evergreen.Health.Application.Health.Integrations.Ib43;
using Evergreen.Health.Application.Health.Sleep;
using Furion.FriendlyException;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
@@ -26,11 +27,14 @@ public sealed record Ib43CurrentOutput(Ib43BedOutput Bed, string Transport, bool
IReadOnlyDictionary<string, Ib43MetricOutput> Readings);
public sealed record Ib43HistoryItem(long Id, long ReceivedAtMs, IReadOnlyDictionary<string, Ib43Reading> Readings);
public sealed record Ib43HistoryOutput(IReadOnlyList<Ib43HistoryItem> Items, long? NextBeforeId);
public sealed record Ib43AutomationRecord(string Id, string ActionKey, string TargetLabel, long TriggeredAtMs, long? CompletedAtMs, string Result);
public sealed record Ib43AutomationOutput(bool Available, bool Enabled, string ActionLabel, string TargetLabel,
IReadOnlyList<Ib43AutomationRecord> Records, IReadOnlyList<BedPresenceRuleStatus> Rules);
[Authorize]
[ApiDescriptionSettings("健康管理", Name = "HealthIb43", Order = 53)]
public sealed class HealthIb43Service(Ib43DataRepository repository, IHttpContextAccessor context,
IOptions<Ib43Options> options, Ib43StateStore states, TimeProvider clock) : IDynamicApiController, ITransient
IOptions<Ib43Options> options, Ib43StateStore states, TimeProvider clock, BedPresenceAutomation automation) : IDynamicApiController, ITransient
{
[HttpGet("/api/health/ib43/admin/bindings")]
public async Task<Ib43AdminBedOutput[]> ActiveBindings(string productKey, string serialNumber)
@@ -69,8 +73,49 @@ public sealed class HealthIb43Service(Ib43DataRepository repository, IHttpContex
{
var admin = await RequireAdmin();
await repository.Release(bindingId, admin, clock.GetUtcNow().ToUnixTimeMilliseconds());
automation.RevokeBinding(bindingId);
}
[HttpGet("/api/health/ib43/bindings/{bindingId}/automation")]
public async Task<Ib43AutomationOutput> AutomationStatus(string bindingId)
{
var user = await RequireUser();
var binding = await repository.OwnedBinding(user, bindingId);
var records = await repository.AutomationHistory(user, binding.Id);
var bed = AutomationBinding(binding);
var canManage = CanManageAutomation(binding);
var primary = canManage ? automation.PrimaryRuleStatus(bed) : null;
return new(primary is not null, primary?.Enabled == true, primary?.ActionLabel ?? "",
primary?.TargetLabel ?? "", records.Select(row => new Ib43AutomationRecord(row.Id, row.ActionKey,
row.TargetLabel, row.TriggeredAtMs, row.CompletedAtMs, row.Result)).ToArray(),
canManage ? automation.RuleStatuses(bed) : []);
}
[HttpPost("/api/health/ib43/bindings/{bindingId}/automation/start")]
public async Task StartAutomation(string bindingId, [FromQuery] string? actionKey = null)
{
var user = await RequireUser();
var binding = await repository.OwnedBinding(user, bindingId);
if (!CanManageAutomation(binding) || !automation.TryEnable(AutomationBinding(binding), actionKey))
throw new AppFriendlyException("该床位当前无法开启家具联动", null) { StatusCode = 409 };
}
[HttpPost("/api/health/ib43/bindings/{bindingId}/automation/stop")]
public async Task StopAutomation(string bindingId, [FromQuery] string? actionKey = null)
{
var user = await RequireUser();
var binding = await repository.OwnedBinding(user, bindingId);
if (!automation.Disable(binding.Id, user, actionKey))
throw new AppFriendlyException("家具联动规则不存在", null) { StatusCode = 404 };
}
private bool CanManageAutomation(HealthIb43Binding binding) => binding.EndedAtMs is null &&
binding.Side is "left" or "right" && options.Value.Enabled && options.Value.Devices.Any(device =>
device.Simulation && device.ProductKey == binding.ProductKey && device.SerialNumber == binding.SerialNumber);
private static BedAutomationBinding AutomationBinding(HealthIb43Binding binding) => new(binding.Id, binding.UserId,
binding.ProductKey, binding.SerialNumber, binding.Side == "left" ? BedSide.Left : BedSide.Right, binding.Simulation);
[HttpGet("/api/health/ib43/bindings")]
public async Task<Ib43BedOutput[]> Bindings()
{