fix add and list telementry
This commit is contained in:
@@ -268,3 +268,5 @@ public class VoiceCallTestController : ControllerBase
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.9">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.9" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GreenHome.Application\GreenHome.Application.csproj" />
|
||||
<ProjectReference Include="..\GreenHome.Infrastructure\GreenHome.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\GreenHome.Sms.Ippanel\GreenHome.Sms.Ippanel.csproj" />
|
||||
<ProjectReference Include="..\GreenHome.VoiceCall.Avanak\GreenHome.VoiceCall.Avanak.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.9">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.9" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GreenHome.Application\GreenHome.Application.csproj" />
|
||||
<ProjectReference Include="..\GreenHome.Infrastructure\GreenHome.Infrastructure.csproj" />
|
||||
<ProjectReference Include="..\GreenHome.Sms.Ippanel\GreenHome.Sms.Ippanel.csproj" />
|
||||
<ProjectReference Include="..\GreenHome.VoiceCall.Avanak\GreenHome.VoiceCall.Avanak.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,89 +1,89 @@
|
||||
using FluentValidation;
|
||||
using GreenHome.Application;
|
||||
using GreenHome.Infrastructure;
|
||||
using GreenHome.Sms.Ippanel;
|
||||
using GreenHome.VoiceCall.Avanak;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
// Application/Infrastructure DI
|
||||
builder.Services.AddAutoMapper(typeof(GreenHome.Application.MappingProfile));
|
||||
builder.Services.AddValidatorsFromAssemblyContaining<GreenHome.Application.DeviceDtoValidator>();
|
||||
|
||||
// CORS for Next.js dev (adjust origins as needed)
|
||||
const string CorsPolicy = "DefaultCors";
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy(CorsPolicy, policy =>
|
||||
policy
|
||||
.WithOrigins(
|
||||
"http://green.nabaksoft.ir",
|
||||
"https://green.nabaksoft.ir",
|
||||
"http://gh1.nabaksoft.ir",
|
||||
"https://gh1.nabaksoft.ir",
|
||||
"http://localhost:3000",
|
||||
"http://localhost:3000",
|
||||
"http://127.0.0.1:3000",
|
||||
"https://localhost:3000"
|
||||
)
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod()
|
||||
.AllowCredentials()
|
||||
);
|
||||
});
|
||||
|
||||
builder.Services.AddDbContext<GreenHome.Infrastructure.GreenHomeDbContext>(options =>
|
||||
{
|
||||
options.UseSqlServer(builder.Configuration.GetConnectionString("Default"));
|
||||
});
|
||||
builder.Services.AddScoped<GreenHome.Application.IDeviceService, GreenHome.Infrastructure.DeviceService>();
|
||||
builder.Services.AddScoped<GreenHome.Application.ITelemetryService, GreenHome.Infrastructure.TelemetryService>();
|
||||
builder.Services.AddScoped<GreenHome.Application.IDeviceSettingsService, GreenHome.Infrastructure.DeviceSettingsService>();
|
||||
builder.Services.AddScoped<GreenHome.Application.IAuthService, GreenHome.Infrastructure.AuthService>();
|
||||
builder.Services.AddScoped<GreenHome.Application.IAlertService, GreenHome.Infrastructure.AlertService>();
|
||||
|
||||
// SMS Service Configuration
|
||||
builder.Services.AddIppanelSms(builder.Configuration);
|
||||
|
||||
// Voice Call Service Configuration
|
||||
builder.Services.AddAvanakVoiceCall(builder.Configuration);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Apply pending migrations automatically
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var services = scope.ServiceProvider;
|
||||
try
|
||||
{
|
||||
var context = services.GetRequiredService<GreenHome.Infrastructure.GreenHomeDbContext>();
|
||||
context.Database.Migrate();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var logger = services.GetRequiredService<ILogger<Program>>();
|
||||
logger.LogError(ex, "An error occurred while migrating the database.");
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
//if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseCors(CorsPolicy);
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
using FluentValidation;
|
||||
using GreenHome.Application;
|
||||
using GreenHome.Infrastructure;
|
||||
using GreenHome.Sms.Ippanel;
|
||||
using GreenHome.VoiceCall.Avanak;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
|
||||
// Application/Infrastructure DI
|
||||
builder.Services.AddAutoMapper(typeof(GreenHome.Application.MappingProfile));
|
||||
builder.Services.AddValidatorsFromAssemblyContaining<GreenHome.Application.DeviceDtoValidator>();
|
||||
|
||||
// CORS for Next.js dev (adjust origins as needed)
|
||||
const string CorsPolicy = "DefaultCors";
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy(CorsPolicy, policy =>
|
||||
policy
|
||||
.WithOrigins(
|
||||
"http://green.nabaksoft.ir",
|
||||
"https://green.nabaksoft.ir",
|
||||
"http://gh1.nabaksoft.ir",
|
||||
"https://gh1.nabaksoft.ir",
|
||||
"http://localhost:3000",
|
||||
"http://localhost:3000",
|
||||
"http://127.0.0.1:3000",
|
||||
"https://localhost:3000"
|
||||
)
|
||||
.AllowAnyHeader()
|
||||
.AllowAnyMethod()
|
||||
.AllowCredentials()
|
||||
);
|
||||
});
|
||||
|
||||
builder.Services.AddDbContext<GreenHome.Infrastructure.GreenHomeDbContext>(options =>
|
||||
{
|
||||
options.UseSqlServer(builder.Configuration.GetConnectionString("Default"));
|
||||
});
|
||||
builder.Services.AddScoped<GreenHome.Application.IDeviceService, GreenHome.Infrastructure.DeviceService>();
|
||||
builder.Services.AddScoped<GreenHome.Application.ITelemetryService, GreenHome.Infrastructure.TelemetryService>();
|
||||
builder.Services.AddScoped<GreenHome.Application.IDeviceSettingsService, GreenHome.Infrastructure.DeviceSettingsService>();
|
||||
builder.Services.AddScoped<GreenHome.Application.IAuthService, GreenHome.Infrastructure.AuthService>();
|
||||
builder.Services.AddScoped<GreenHome.Application.IAlertService, GreenHome.Infrastructure.AlertService>();
|
||||
|
||||
// SMS Service Configuration
|
||||
builder.Services.AddIppanelSms(builder.Configuration);
|
||||
|
||||
// Voice Call Service Configuration
|
||||
builder.Services.AddAvanakVoiceCall(builder.Configuration);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Apply pending migrations automatically
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var services = scope.ServiceProvider;
|
||||
try
|
||||
{
|
||||
var context = services.GetRequiredService<GreenHome.Infrastructure.GreenHomeDbContext>();
|
||||
context.Database.Migrate();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var logger = services.GetRequiredService<ILogger<Program>>();
|
||||
logger.LogError(ex, "An error occurred while migrating the database.");
|
||||
}
|
||||
}
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
//if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseCors(CorsPolicy);
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
|
||||
@@ -2,12 +2,10 @@
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"GreenHome.VoiceCall.Avanak": "Information"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"Default": "Server=.;Database=GreenHomeDb;Trusted_Connection=True;MultipleActiveResultSets=true;TrustServerCertificate=True"
|
||||
},
|
||||
"IppanelSms": {
|
||||
"BaseUrl": "https://edge.ippanel.com/v1",
|
||||
"AuthorizationToken": "YTA1Zjk3N2EtNzkwOC00ZTg5LWFjZmYtZGEyZDAyNjNlZWQxM2Q2ZDVjYWE0MTA2Yzc1NDYzZDY1Y2VkMjlhMzcwNjA=",
|
||||
|
||||
@@ -5,6 +5,7 @@ public sealed class TelemetryRecord
|
||||
public int Id { get; set; }
|
||||
public int DeviceId { get; set; }
|
||||
public DateTime TimestampUtc { get; set; }
|
||||
public DateTime ServerTimestampUtc { get; set; } // زمان ثبت در سرور
|
||||
public decimal TemperatureC { get; set; } // decimal(18,2)
|
||||
public decimal HumidityPercent { get; set; } // decimal(18,2)
|
||||
public decimal SoilPercent { get; set; } // decimal(18,2)
|
||||
|
||||
@@ -43,6 +43,7 @@ public sealed class GreenHomeDbContext : DbContext
|
||||
b.Property(x => x.PersianDate).HasMaxLength(10);
|
||||
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 });
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Domain.DeviceSettings>(b =>
|
||||
|
||||
381
src/GreenHome.Infrastructure/Migrations/20251127125625_AddServerTimestampUtc.Designer.cs
generated
Normal file
381
src/GreenHome.Infrastructure/Migrations/20251127125625_AddServerTimestampUtc.Designer.cs
generated
Normal file
@@ -0,0 +1,381 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using GreenHome.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace GreenHome.Infrastructure.Migrations
|
||||
{
|
||||
[DbContext(typeof(GreenHomeDbContext))]
|
||||
[Migration("20251127125625_AddServerTimestampUtc")]
|
||||
partial class AddServerTimestampUtc
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.9")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("GreenHome.Domain.AlertNotification", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("AlertType")
|
||||
.IsRequired()
|
||||
.HasMaxLength(50)
|
||||
.HasColumnType("nvarchar(50)");
|
||||
|
||||
b.Property<int>("DeviceId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("ErrorMessage")
|
||||
.HasMaxLength(1000)
|
||||
.HasColumnType("nvarchar(1000)");
|
||||
|
||||
b.Property<bool>("IsSent")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.IsRequired()
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<string>("MessageOutboxIds")
|
||||
.HasMaxLength(500)
|
||||
.HasColumnType("nvarchar(500)");
|
||||
|
||||
b.Property<DateTime>("SentAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.HasIndex("DeviceId", "UserId", "AlertType", "SentAt");
|
||||
|
||||
b.ToTable("AlertNotifications", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GreenHome.Domain.Device", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("DeviceName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("nvarchar(10)");
|
||||
|
||||
b.Property<string>("Location")
|
||||
.IsRequired()
|
||||
.HasMaxLength(250)
|
||||
.HasColumnType("nvarchar(250)");
|
||||
|
||||
b.Property<string>("NeshanLocation")
|
||||
.IsRequired()
|
||||
.HasMaxLength(80)
|
||||
.HasColumnType("nvarchar(80)");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("Devices", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GreenHome.Domain.DeviceSettings", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<decimal>("DangerMaxTemperature")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<decimal>("DangerMinTemperature")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<int>("DeviceId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("MaxGasPPM")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("MaxHumidityPercent")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<decimal>("MaxLux")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<decimal>("MaxTemperature")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<int>("MinGasPPM")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("MinHumidityPercent")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<decimal>("MinLux")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<decimal>("MinTemperature")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DeviceId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("DeviceSettings", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GreenHome.Domain.DeviceUser", b =>
|
||||
{
|
||||
b.Property<int>("DeviceId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("DeviceId", "UserId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("DeviceUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GreenHome.Domain.TelemetryRecord", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("DeviceId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("GasPPM")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<decimal>("HumidityPercent")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<decimal>("Lux")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<string>("PersianDate")
|
||||
.IsRequired()
|
||||
.HasMaxLength(10)
|
||||
.HasColumnType("nvarchar(10)");
|
||||
|
||||
b.Property<int>("PersianMonth")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<int>("PersianYear")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("ServerTimestampUtc")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<decimal>("SoilPercent")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<decimal>("TemperatureC")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
b.Property<DateTime>("TimestampUtc")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DeviceId", "ServerTimestampUtc");
|
||||
|
||||
b.HasIndex("DeviceId", "TimestampUtc");
|
||||
|
||||
b.HasIndex("DeviceId", "PersianYear", "PersianMonth");
|
||||
|
||||
b.ToTable("Telemetry", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GreenHome.Domain.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Family")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<DateTime?>("LastLoginAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<string>("Mobile")
|
||||
.IsRequired()
|
||||
.HasMaxLength(11)
|
||||
.HasColumnType("nvarchar(11)");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("nvarchar(100)");
|
||||
|
||||
b.Property<int>("Role")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Mobile")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Users", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GreenHome.Domain.VerificationCode", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("Code")
|
||||
.IsRequired()
|
||||
.HasMaxLength(4)
|
||||
.HasColumnType("nvarchar(4)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<bool>("IsUsed")
|
||||
.HasColumnType("bit");
|
||||
|
||||
b.Property<string>("Mobile")
|
||||
.IsRequired()
|
||||
.HasMaxLength(11)
|
||||
.HasColumnType("nvarchar(11)");
|
||||
|
||||
b.Property<DateTime?>("UsedAt")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Mobile", "Code", "IsUsed");
|
||||
|
||||
b.ToTable("VerificationCodes", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GreenHome.Domain.AlertNotification", b =>
|
||||
{
|
||||
b.HasOne("GreenHome.Domain.Device", "Device")
|
||||
.WithMany()
|
||||
.HasForeignKey("DeviceId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("GreenHome.Domain.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Device");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GreenHome.Domain.Device", b =>
|
||||
{
|
||||
b.HasOne("GreenHome.Domain.User", "User")
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GreenHome.Domain.DeviceSettings", b =>
|
||||
{
|
||||
b.HasOne("GreenHome.Domain.Device", "Device")
|
||||
.WithMany()
|
||||
.HasForeignKey("DeviceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Device");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GreenHome.Domain.DeviceUser", b =>
|
||||
{
|
||||
b.HasOne("GreenHome.Domain.Device", "Device")
|
||||
.WithMany("DeviceUsers")
|
||||
.HasForeignKey("DeviceId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("GreenHome.Domain.User", "User")
|
||||
.WithMany("DeviceUsers")
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Device");
|
||||
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GreenHome.Domain.Device", b =>
|
||||
{
|
||||
b.Navigation("DeviceUsers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("GreenHome.Domain.User", b =>
|
||||
{
|
||||
b.Navigation("DeviceUsers");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace GreenHome.Infrastructure.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddServerTimestampUtc : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "ServerTimestampUtc",
|
||||
table: "Telemetry",
|
||||
type: "datetime2",
|
||||
nullable: false,
|
||||
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Telemetry_DeviceId_ServerTimestampUtc",
|
||||
table: "Telemetry",
|
||||
columns: new[] { "DeviceId", "ServerTimestampUtc" });
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Telemetry_DeviceId_ServerTimestampUtc",
|
||||
table: "Telemetry");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ServerTimestampUtc",
|
||||
table: "Telemetry");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,6 +203,9 @@ namespace GreenHome.Infrastructure.Migrations
|
||||
b.Property<int>("PersianYear")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<DateTime>("ServerTimestampUtc")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<decimal>("SoilPercent")
|
||||
.HasColumnType("decimal(18,2)");
|
||||
|
||||
@@ -214,6 +217,8 @@ namespace GreenHome.Infrastructure.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("DeviceId", "ServerTimestampUtc");
|
||||
|
||||
b.HasIndex("DeviceId", "TimestampUtc");
|
||||
|
||||
b.HasIndex("DeviceId", "PersianYear", "PersianMonth");
|
||||
|
||||
@@ -29,6 +29,9 @@ public sealed class TelemetryService : ITelemetryService
|
||||
dto.DeviceId = device.Id; // Update DTO for alert service
|
||||
}
|
||||
}
|
||||
// ذخیره زمان سرور در لحظه ثبت
|
||||
entity.ServerTimestampUtc = DateTime.Now;
|
||||
|
||||
var dt = dto.TimestampUtc;
|
||||
var py = PersianCalendar.GetYear(dt);
|
||||
var pm = PersianCalendar.GetMonth(dt);
|
||||
@@ -37,6 +40,16 @@ public sealed class TelemetryService : ITelemetryService
|
||||
entity.PersianMonth = pm;
|
||||
entity.PersianDate = $"{py:0000}/{pm:00}/{pd:00}";
|
||||
|
||||
if(entity.Lux < 0)
|
||||
{
|
||||
var lastItem = await dbContext.TelemetryRecords
|
||||
.Where(x => x.DeviceId == entity.DeviceId)
|
||||
.OrderByDescending(x => x.TimestampUtc)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
|
||||
entity.Lux = lastItem?.Lux ?? 0;
|
||||
}
|
||||
|
||||
dbContext.TelemetryRecords.Add(entity);
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
return entity.Id;
|
||||
@@ -51,24 +64,34 @@ public sealed class TelemetryService : ITelemetryService
|
||||
}
|
||||
if (filter.StartDateUtc.HasValue)
|
||||
{
|
||||
var start = filter.StartDateUtc.Value.Date.AddDays(1);
|
||||
query = query.Where(x => x.TimestampUtc >= start);
|
||||
//var start = filter.StartDateUtc.Value.Date.AddDays(1);
|
||||
query = query.Where(x => x.ServerTimestampUtc >= filter.StartDateUtc.Value);
|
||||
}
|
||||
|
||||
if (filter.EndDateUtc.HasValue)
|
||||
{
|
||||
var end = filter.EndDateUtc.Value.Date.AddDays(1);
|
||||
query = query.Where(x => x.TimestampUtc < end);
|
||||
query = query.Where(x => x.ServerTimestampUtc < filter.EndDateUtc.Value);
|
||||
}
|
||||
|
||||
if(filter.Page <= 0) filter.Page = 1;
|
||||
if(filter.PageSize <= 0) filter.PageSize = 1000000; // No limit
|
||||
var total = await query.CountAsync(cancellationToken);
|
||||
var skip = (filter.Page - 1) * filter.PageSize;
|
||||
var items = await query
|
||||
.OrderByDescending(x => x.TimestampUtc)
|
||||
.OrderByDescending(x => x.ServerTimestampUtc)
|
||||
.Skip(skip)
|
||||
.Take(filter.PageSize)
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
if(items.Any(x => x.Lux < 0))
|
||||
{
|
||||
for (int i = 1; i < items.Count; i++)
|
||||
{
|
||||
if (items[i].Lux < 0)
|
||||
items[i].Lux = items[i - 1].Lux;
|
||||
}
|
||||
}
|
||||
|
||||
return new PagedResult<TelemetryDto>
|
||||
{
|
||||
Items = mapper.Map<IReadOnlyList<TelemetryDto>>(items),
|
||||
|
||||
Reference in New Issue
Block a user