add smsRecievers field and send alert messages to user device

This commit is contained in:
2026-01-07 18:18:50 +03:30
parent 9334e3e4c2
commit 3e62c8bf96
10 changed files with 1460 additions and 40 deletions

View File

@@ -1,8 +1,11 @@
using GreenHome.Application;
using GreenHome.Domain;
using GreenHome.Sms.Ippanel;
using GreenHome.VoiceCall.Avanak;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
using Microsoft.Extensions.Logging;
using System.Globalization;
using System.Text.Json;
using static GreenHome.Sms.Ippanel.IppanelSmsService;
@@ -33,7 +36,7 @@ public sealed class AlertService : IAlertService
this.logger = logger;
}
public async Task CheckAndSendAlertsAsync(int deviceId, TelemetryDto telemetry, CancellationToken cancellationToken)
public async Task<string?> CheckAndSendAlertsAsync(int deviceId, TelemetryDto telemetry, CancellationToken cancellationToken)
{
// Get device with all users who should receive alerts
var device = await dbContext.Devices
@@ -45,24 +48,34 @@ public sealed class AlertService : IAlertService
if (device == null)
{
logger.LogWarning("Device not found: DeviceId={DeviceId}", deviceId);
return;
}
// Get all users who should receive alerts
var usersToAlert = device.DeviceUsers
.Where(du => du.ReceiveAlerts)
.Select(du => du.User)
.ToList();
if (usersToAlert.Count == 0)
{
logger.LogInformation("No users with ReceiveAlerts enabled for device: DeviceId={DeviceId}", deviceId);
return;
return null;
}
// Get device settings for location
var settings = await deviceSettingsService.GetByDeviceIdAsync(deviceId, cancellationToken);
var recievers = device.User.Mobile;
recievers += "," + (settings?.SmsRecievers ?? "");
recievers = string.Join(",",
recievers
.Split(',')
.Select(x => x.Trim())
.Where(x => x.Length > 0)
);
// Get all users who should receive alerts
// var usersToAlert = device.DeviceUsers
// .Where(du => du.ReceiveAlerts)
// .Select(du => du.User)
// .ToList();
if (recievers.Length == 0)
{
logger.LogInformation("No users with ReceiveAlerts enabled for device: DeviceId={DeviceId}", deviceId);
return null;
}
// Get all enabled alert conditions for this device
var conditions = await dbContext.AlertConditions
.Include(c => c.Rules)
@@ -72,7 +85,7 @@ public sealed class AlertService : IAlertService
if (!conditions.Any())
{
logger.LogDebug("No enabled alert conditions for device: DeviceId={DeviceId}", deviceId);
return;
return null;
}
// Determine if it's daytime or nighttime
@@ -82,6 +95,7 @@ public sealed class AlertService : IAlertService
isDaytime = sunCalculatorService.IsDaytime(DateTime.UtcNow, settings.Latitude.Value, settings.Longitude.Value);
}
List<string> messages = [];
// Check each condition
foreach (var condition in conditions)
{
@@ -101,9 +115,28 @@ public sealed class AlertService : IAlertService
if (allRulesMatch && condition.Rules.Any())
{
// All rules passed, send alert if cooldown period has passed
await SendAlertForConditionAsync(condition, device, usersToAlert, telemetry, cancellationToken);
// await SendAlertForConditionAsync(condition, device, usersToAlert, telemetry, cancellationToken);
string? message = await GetAlertMessage(condition, device, device.User, telemetry, cancellationToken);
if(message != null && message.Length > 0)
messages.Add(message);
// return message;
}
}
if(telemetry.OldPower == 0 && telemetry.Power == 1)
{
messages.Add("برق دستگاه متصل شد");
}
else if(telemetry.OldPower == 1 && telemetry.Power == 0)
{
messages.Add("برق دستگاه قطع شد");
}
if(messages.Any())
{
return $"tt{recievers}#{string.Join("@", messages)}";
}
return null;
}
private bool CheckRule(Domain.AlertRule rule, TelemetryDto telemetry)
@@ -130,10 +163,59 @@ public sealed class AlertService : IAlertService
};
}
private async Task<string?> GetAlertMessage(
Domain.AlertCondition condition,
Domain.Device device,
User user,
TelemetryDto telemetry,
CancellationToken cancellationToken)
{
// Determine cooldown based on notification type
var cooldownMinutes = condition.NotificationType == Domain.AlertNotificationType.Call
? condition.CallCooldownMinutes
: condition.SmsCooldownMinutes;
// Build alert message once
var message = BuildAlertMessage(condition, device.DeviceName, telemetry);
var sentAt = DateTime.UtcNow;
var cooldownTime = sentAt.AddMinutes(-cooldownMinutes);
var recentAlert = await dbContext.AlertNotifications
.Where(a => a.DeviceId == device.Id &&
a.UserId == user.Id &&
a.AlertConditionId == condition.Id &&
a.SentAt >= cooldownTime)
.FirstOrDefaultAsync(cancellationToken);
if (recentAlert != null)
{
logger.LogInformation("Alert skipped due to cooldown: DeviceId={DeviceId}, UserId={UserId}, ConditionId={ConditionId}",
device.Id, user.Id, condition.Id);
return null;
}
var notification = new Domain.AlertNotification
{
DeviceId = device.Id,
UserId = user.Id,
AlertConditionId = condition.Id,
NotificationType = condition.NotificationType,
Message = message,
//MessageOutboxIds = messageOutboxIds,
//ErrorMessage = errorMessage,
SentAt = sentAt,
IsSent = true
};
dbContext.AlertNotifications.Add(notification);
return message;
}
private async Task SendAlertForConditionAsync(
Domain.AlertCondition condition,
Domain.Device device,
List<Domain.User> usersToAlert,
List<User> usersToAlert,
TelemetryDto telemetry,
CancellationToken cancellationToken)
{

View File

@@ -54,6 +54,7 @@ public sealed class GreenHomeDbContext : DbContext
b.Property(x => x.SoilPercent).HasColumnType("decimal(18,2)");
b.Property(x => x.Lux).HasColumnType("decimal(18,2)");
b.Property(x => x.PersianDate).HasMaxLength(10);
b.Property(x => x.Voltage).HasColumnType("decimal(18,2)");
b.HasIndex(x => new { x.DeviceId, x.PersianYear, x.PersianMonth });
b.HasIndex(x => new { x.DeviceId, x.TimestampUtc });
b.HasIndex(x => new { x.DeviceId, x.ServerTimestampUtc });
@@ -71,6 +72,7 @@ public sealed class GreenHomeDbContext : DbContext
b.Property(x => x.MinimumSmsIntervalMinutes).HasDefaultValue(15);
b.Property(x => x.MinimumCallIntervalMinutes).HasDefaultValue(60);
b.Property(x => x.AreaSquareMeters).HasColumnType("decimal(18,2)");
b.Property(x => x.SmsRecievers).HasMaxLength(120);
b.HasOne(x => x.Device)
.WithMany()
.HasForeignKey(x => x.DeviceId)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace GreenHome.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class AddSmsRecievers : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "DeviceId1",
table: "DeviceSettings",
type: "int",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "SmsRecievers",
table: "DeviceSettings",
type: "nvarchar(120)",
maxLength: 120,
nullable: false,
defaultValue: "");
migrationBuilder.CreateIndex(
name: "IX_DeviceSettings_DeviceId1",
table: "DeviceSettings",
column: "DeviceId1");
migrationBuilder.AddForeignKey(
name: "FK_DeviceSettings_Devices_DeviceId1",
table: "DeviceSettings",
column: "DeviceId1",
principalTable: "Devices",
principalColumn: "Id");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_DeviceSettings_Devices_DeviceId1",
table: "DeviceSettings");
migrationBuilder.DropIndex(
name: "IX_DeviceSettings_DeviceId1",
table: "DeviceSettings");
migrationBuilder.DropColumn(
name: "DeviceId1",
table: "DeviceSettings");
migrationBuilder.DropColumn(
name: "SmsRecievers",
table: "DeviceSettings");
}
}
}

View File

@@ -590,6 +590,9 @@ namespace GreenHome.Infrastructure.Migrations
b.Property<int>("DeviceId")
.HasColumnType("int");
b.Property<int?>("DeviceId1")
.HasColumnType("int");
b.Property<string>("DevicePhoneNumber")
.IsRequired()
.HasColumnType("nvarchar(max)");
@@ -623,6 +626,11 @@ namespace GreenHome.Infrastructure.Migrations
b.Property<int?>("SimCardType")
.HasColumnType("int");
b.Property<string>("SmsRecievers")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("nvarchar(120)");
b.Property<string>("TokenCode")
.HasColumnType("nvarchar(max)");
@@ -643,6 +651,8 @@ namespace GreenHome.Infrastructure.Migrations
b.HasIndex("DeviceId")
.IsUnique();
b.HasIndex("DeviceId1");
b.ToTable("DeviceSettings", (string)null);
});
@@ -1127,6 +1137,10 @@ namespace GreenHome.Infrastructure.Migrations
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("GreenHome.Domain.Device", null)
.WithMany("DeviceSettings")
.HasForeignKey("DeviceId1");
b.Navigation("Device");
});
@@ -1198,6 +1212,8 @@ namespace GreenHome.Infrastructure.Migrations
modelBuilder.Entity("GreenHome.Domain.Device", b =>
{
b.Navigation("DeviceSettings");
b.Navigation("DeviceUsers");
});