namespace Evergreen.Health.Application.Health.Sleep;
/// 本版没有异常判定算法;未来规则只产生事件,不直接操作音箱或家具。
public sealed record SleepAnomaly(string Code, string Description, DateTimeOffset ObservedAt);
public interface ISleepAnomalyRule
{
string Key { get; }
SleepAnomaly? Evaluate(IReadOnlyList observations);
}
/// 每个舒睡会话独立持有;只有语音实际播报后才记录提醒时间。
public sealed class AnomalyReminderGate
{
private readonly TimeSpan _interval;
private readonly Dictionary _delivered = new(StringComparer.OrdinalIgnoreCase);
private readonly object _lock = new();
public AnomalyReminderGate(SleepWorkflowOptions options) => _interval = options.SameAnomalyReminderInterval;
public bool CanRemind(string code, DateTimeOffset now)
{
lock (_lock) return !_delivered.TryGetValue(code, out var last) || now - last >= _interval;
}
public void MarkDelivered(string code, DateTimeOffset now)
{
lock (_lock) _delivered[code] = now;
}
}