version 3
This commit is contained in:
62
src/GreenHome.Api/Controllers/AlertLogsController.cs
Normal file
62
src/GreenHome.Api/Controllers/AlertLogsController.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using GreenHome.Application;
|
||||
|
||||
namespace GreenHome.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class AlertLogsController : ControllerBase
|
||||
{
|
||||
private readonly IAlertLogService _alertLogService;
|
||||
|
||||
public AlertLogsController(IAlertLogService alertLogService)
|
||||
{
|
||||
_alertLogService = alertLogService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت لیست لاگهای هشدار با فیلتر و صفحهبندی
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<PagedResult<AlertLogDto>>> GetAlertLogs(
|
||||
[FromQuery] int? deviceId,
|
||||
[FromQuery] int? userId,
|
||||
[FromQuery] Domain.AlertType? alertType,
|
||||
[FromQuery] Domain.AlertStatus? status,
|
||||
[FromQuery] DateTime? startDate,
|
||||
[FromQuery] DateTime? endDate,
|
||||
[FromQuery] int page = 1,
|
||||
[FromQuery] int pageSize = 20,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var filter = new AlertLogFilter
|
||||
{
|
||||
DeviceId = deviceId,
|
||||
UserId = userId,
|
||||
AlertType = alertType,
|
||||
Status = status,
|
||||
StartDate = startDate,
|
||||
EndDate = endDate,
|
||||
Page = page,
|
||||
PageSize = pageSize
|
||||
};
|
||||
|
||||
var result = await _alertLogService.GetAlertLogsAsync(filter, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت جزئیات کامل یک لاگ هشدار
|
||||
/// </summary>
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<AlertLogDto>> GetAlertLogById(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await _alertLogService.GetAlertLogByIdAsync(id, cancellationToken);
|
||||
if (result == null)
|
||||
{
|
||||
return NotFound(new { error = $"لاگ هشدار با شناسه {id} یافت نشد" });
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
|
||||
98
src/GreenHome.Api/Controllers/ChecklistsController.cs
Normal file
98
src/GreenHome.Api/Controllers/ChecklistsController.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using GreenHome.Application;
|
||||
|
||||
namespace GreenHome.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class ChecklistsController : ControllerBase
|
||||
{
|
||||
private readonly IChecklistService _checklistService;
|
||||
|
||||
public ChecklistsController(IChecklistService checklistService)
|
||||
{
|
||||
_checklistService = checklistService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت چکلیست فعال یک دستگاه
|
||||
/// </summary>
|
||||
[HttpGet("active/{deviceId}")]
|
||||
public async Task<ActionResult<ChecklistDto>> GetActiveChecklist(int deviceId, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await _checklistService.GetActiveChecklistByDeviceIdAsync(deviceId, cancellationToken);
|
||||
if (result == null)
|
||||
{
|
||||
return NotFound(new { error = "چکلیست فعالی برای این دستگاه یافت نشد" });
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت تمام چکلیستهای یک دستگاه
|
||||
/// </summary>
|
||||
[HttpGet("device/{deviceId}")]
|
||||
public async Task<ActionResult<List<ChecklistDto>>> GetChecklists(int deviceId, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await _checklistService.GetChecklistsByDeviceIdAsync(deviceId, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت جزئیات یک چکلیست
|
||||
/// </summary>
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<ChecklistDto>> GetChecklistById(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await _checklistService.GetChecklistByIdAsync(id, cancellationToken);
|
||||
if (result == null)
|
||||
{
|
||||
return NotFound(new { error = $"چکلیست با شناسه {id} یافت نشد" });
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ایجاد چکلیست جدید (چکلیست قبلی غیرفعال میشود)
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<int>> CreateChecklist(
|
||||
CreateChecklistRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var id = await _checklistService.CreateChecklistAsync(request, cancellationToken);
|
||||
return Ok(new { id, message = "چکلیست با موفقیت ایجاد شد و چکلیست قبلی غیرفعال شد" });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت سابقه تکمیلهای یک چکلیست
|
||||
/// </summary>
|
||||
[HttpGet("{checklistId}/completions")]
|
||||
public async Task<ActionResult<List<ChecklistCompletionDto>>> GetCompletions(
|
||||
int checklistId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await _checklistService.GetCompletionsByChecklistIdAsync(checklistId, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ثبت تکمیل چکلیست
|
||||
/// </summary>
|
||||
[HttpPost("complete")]
|
||||
public async Task<ActionResult<int>> CompleteChecklist(
|
||||
CompleteChecklistRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var id = await _checklistService.CompleteChecklistAsync(request, cancellationToken);
|
||||
return Ok(new { id, message = "چکلیست با موفقیت تکمیل شد" });
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return NotFound(new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,5 +73,71 @@ public class DailyReportController : ControllerBase
|
||||
return StatusCode(500, new { error = "خطای سرور در پردازش درخواست" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت تحلیل هفتگی
|
||||
/// </summary>
|
||||
[HttpGet("weekly")]
|
||||
public async Task<ActionResult<DailyReportResponse>> GetWeeklyAnalysis(
|
||||
[FromQuery] int deviceId,
|
||||
[FromQuery] string startDate,
|
||||
[FromQuery] string endDate,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new WeeklyAnalysisRequest
|
||||
{
|
||||
DeviceId = deviceId,
|
||||
StartDate = startDate.Trim(),
|
||||
EndDate = endDate.Trim()
|
||||
};
|
||||
|
||||
var result = await _dailyReportService.GetWeeklyAnalysisAsync(request, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return BadRequest(new { error = ex.Message });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting weekly analysis");
|
||||
return StatusCode(500, new { error = "خطا در دریافت تحلیل هفتگی" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت تحلیل ماهانه
|
||||
/// </summary>
|
||||
[HttpGet("monthly")]
|
||||
public async Task<ActionResult<DailyReportResponse>> GetMonthlyAnalysis(
|
||||
[FromQuery] int deviceId,
|
||||
[FromQuery] int year,
|
||||
[FromQuery] int month,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var request = new MonthlyAnalysisRequest
|
||||
{
|
||||
DeviceId = deviceId,
|
||||
Year = year,
|
||||
Month = month
|
||||
};
|
||||
|
||||
var result = await _dailyReportService.GetMonthlyAnalysisAsync(request, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return BadRequest(new { error = ex.Message });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error getting monthly analysis");
|
||||
return StatusCode(500, new { error = "خطا در دریافت تحلیل ماهانه" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
212
src/GreenHome.Api/Controllers/DevicePostsController.cs
Normal file
212
src/GreenHome.Api/Controllers/DevicePostsController.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using GreenHome.Application;
|
||||
|
||||
namespace GreenHome.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class DevicePostsController : ControllerBase
|
||||
{
|
||||
private readonly IDevicePostService _postService;
|
||||
private readonly ILogger<DevicePostsController> _logger;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
|
||||
public DevicePostsController(
|
||||
IDevicePostService postService,
|
||||
ILogger<DevicePostsController> logger,
|
||||
IWebHostEnvironment environment)
|
||||
{
|
||||
_postService = postService;
|
||||
_logger = logger;
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت پستهای گروه مجازی دستگاه (تایملاین)
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<PagedResult<DevicePostDto>>> GetPosts(
|
||||
[FromQuery] int deviceId,
|
||||
[FromQuery] int? authorUserId,
|
||||
[FromQuery] int page = 1,
|
||||
[FromQuery] int pageSize = 20,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var filter = new DevicePostFilter
|
||||
{
|
||||
DeviceId = deviceId,
|
||||
AuthorUserId = authorUserId,
|
||||
Page = page,
|
||||
PageSize = pageSize
|
||||
};
|
||||
|
||||
var result = await _postService.GetPostsAsync(filter, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت جزئیات یک پست
|
||||
/// </summary>
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<DevicePostDto>> GetPostById(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await _postService.GetPostByIdAsync(id, cancellationToken);
|
||||
if (result == null)
|
||||
{
|
||||
return NotFound(new { error = $"پست با شناسه {id} یافت نشد" });
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ایجاد پست جدید در گروه مجازی
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<int>> CreatePost(
|
||||
CreateDevicePostRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var id = await _postService.CreatePostAsync(request, cancellationToken);
|
||||
return Ok(new { id, message = "پست با موفقیت ایجاد شد" });
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
return Unauthorized(new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ویرایش پست
|
||||
/// </summary>
|
||||
[HttpPut]
|
||||
public async Task<ActionResult> UpdatePost(
|
||||
UpdateDevicePostRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _postService.UpdatePostAsync(request, cancellationToken);
|
||||
return Ok(new { message = "پست با موفقیت ویرایش شد" });
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return NotFound(new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// حذف پست
|
||||
/// </summary>
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<ActionResult> DeletePost(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _postService.DeletePostAsync(id, cancellationToken);
|
||||
return Ok(new { message = "پست با موفقیت حذف شد" });
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return NotFound(new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// آپلود تصویر برای پست
|
||||
/// </summary>
|
||||
[HttpPost("{postId}/images")]
|
||||
[Consumes("multipart/form-data")]
|
||||
public async Task<ActionResult<int>> UploadImage(
|
||||
int postId,
|
||||
[FromForm] IFormFile file,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (file == null || file.Length == 0)
|
||||
{
|
||||
return BadRequest(new { error = "فایل انتخاب نشده است" });
|
||||
}
|
||||
|
||||
// Validate file type
|
||||
var allowedTypes = new[] { "image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp" };
|
||||
if (!allowedTypes.Contains(file.ContentType.ToLower()))
|
||||
{
|
||||
return BadRequest(new { error = "فقط فایلهای تصویری مجاز هستند" });
|
||||
}
|
||||
|
||||
// Validate file size (max 5MB)
|
||||
if (file.Length > 5 * 1024 * 1024)
|
||||
{
|
||||
return BadRequest(new { error = "حجم فایل نباید بیشتر از 5 مگابایت باشد" });
|
||||
}
|
||||
|
||||
// Create upload directory
|
||||
var uploadsFolder = Path.Combine(_environment.WebRootPath ?? "wwwroot", "uploads", "posts");
|
||||
Directory.CreateDirectory(uploadsFolder);
|
||||
|
||||
// Generate unique filename
|
||||
var fileName = $"{Guid.NewGuid()}_{Path.GetFileName(file.FileName)}";
|
||||
var filePath = Path.Combine(uploadsFolder, fileName);
|
||||
|
||||
// Save file
|
||||
using (var stream = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await file.CopyToAsync(stream, cancellationToken);
|
||||
}
|
||||
|
||||
// Save to database
|
||||
var relativePath = $"/uploads/posts/{fileName}";
|
||||
var imageId = await _postService.AddImageToPostAsync(
|
||||
postId,
|
||||
file.FileName,
|
||||
relativePath,
|
||||
file.ContentType,
|
||||
file.Length,
|
||||
cancellationToken);
|
||||
|
||||
_logger.LogInformation("Image uploaded for post {PostId}: {ImageId}", postId, imageId);
|
||||
|
||||
return Ok(new { imageId, filePath = relativePath, message = "تصویر با موفقیت آپلود شد" });
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return NotFound(new { error = ex.Message });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error uploading image for post {PostId}", postId);
|
||||
return StatusCode(500, new { error = "خطا در آپلود تصویر" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// حذف تصویر از پست
|
||||
/// </summary>
|
||||
[HttpDelete("images/{imageId}")]
|
||||
public async Task<ActionResult> DeleteImage(int imageId, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _postService.DeleteImageAsync(imageId, cancellationToken);
|
||||
return Ok(new { message = "تصویر با موفقیت حذف شد" });
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return NotFound(new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// بررسی دسترسی کاربر به دستگاه
|
||||
/// </summary>
|
||||
[HttpGet("access/{userId}/{deviceId}")]
|
||||
public async Task<ActionResult<bool>> CheckAccess(int userId, int deviceId, CancellationToken cancellationToken)
|
||||
{
|
||||
var hasAccess = await _postService.CanUserAccessDeviceAsync(userId, deviceId, cancellationToken);
|
||||
return Ok(new { hasAccess });
|
||||
}
|
||||
}
|
||||
|
||||
62
src/GreenHome.Api/Controllers/MonthlyReportController.cs
Normal file
62
src/GreenHome.Api/Controllers/MonthlyReportController.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using GreenHome.Application;
|
||||
|
||||
namespace GreenHome.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class MonthlyReportController : ControllerBase
|
||||
{
|
||||
private readonly IMonthlyReportService _monthlyReportService;
|
||||
private readonly ILogger<MonthlyReportController> _logger;
|
||||
|
||||
public MonthlyReportController(
|
||||
IMonthlyReportService monthlyReportService,
|
||||
ILogger<MonthlyReportController> logger)
|
||||
{
|
||||
_monthlyReportService = monthlyReportService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت گزارش آماری ماهانه
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<MonthlyReportDto>> GetMonthlyReport(
|
||||
[FromQuery] int deviceId,
|
||||
[FromQuery] int year,
|
||||
[FromQuery] int month,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (deviceId <= 0)
|
||||
{
|
||||
return BadRequest(new { error = "شناسه دستگاه نامعتبر است" });
|
||||
}
|
||||
|
||||
if (month < 1 || month > 12)
|
||||
{
|
||||
return BadRequest(new { error = "ماه باید بین 1 تا 12 باشد" });
|
||||
}
|
||||
|
||||
var result = await _monthlyReportService.GetMonthlyReportAsync(deviceId, year, month, cancellationToken);
|
||||
|
||||
_logger.LogInformation(
|
||||
"گزارش ماهانه برای دستگاه {DeviceId} و ماه {Month}/{Year} ایجاد شد",
|
||||
deviceId, month, year);
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return NotFound(new { error = ex.Message });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error generating monthly report");
|
||||
return StatusCode(500, new { error = "خطا در ایجاد گزارش ماهانه" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
60
src/GreenHome.Api/Controllers/PowerOutageController.cs
Normal file
60
src/GreenHome.Api/Controllers/PowerOutageController.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using GreenHome.Application;
|
||||
|
||||
namespace GreenHome.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class PowerOutageController : ControllerBase
|
||||
{
|
||||
private readonly IAlertService _alertService;
|
||||
private readonly ILogger<PowerOutageController> _logger;
|
||||
|
||||
public PowerOutageController(
|
||||
IAlertService alertService,
|
||||
ILogger<PowerOutageController> logger)
|
||||
{
|
||||
_alertService = alertService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ارسال هشدار قطع برق برای یک دستگاه
|
||||
/// </summary>
|
||||
/// <param name="deviceId">شناسه دستگاه</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>نتیجه عملیات</returns>
|
||||
[HttpPost]
|
||||
public async Task<ActionResult> SendPowerOutageAlert(
|
||||
[FromQuery] int deviceId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (deviceId <= 0)
|
||||
{
|
||||
return BadRequest(new { error = "شناسه دستگاه نامعتبر است" });
|
||||
}
|
||||
|
||||
await _alertService.SendPowerOutageAlertAsync(deviceId, cancellationToken);
|
||||
|
||||
_logger.LogInformation("Power outage alert processed for device {DeviceId}", deviceId);
|
||||
|
||||
return Ok(new {
|
||||
success = true,
|
||||
message = "هشدار قطع برق با موفقیت ارسال شد"
|
||||
});
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Invalid operation for power outage alert: DeviceId={DeviceId}", deviceId);
|
||||
return BadRequest(new { error = ex.Message });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error sending power outage alert: DeviceId={DeviceId}", deviceId);
|
||||
return StatusCode(500, new { error = "خطا در ارسال هشدار قطع برق" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
210
src/GreenHome.Api/Controllers/UserDailyReportsController.cs
Normal file
210
src/GreenHome.Api/Controllers/UserDailyReportsController.cs
Normal file
@@ -0,0 +1,210 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using GreenHome.Application;
|
||||
|
||||
namespace GreenHome.Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class UserDailyReportsController : ControllerBase
|
||||
{
|
||||
private readonly IUserDailyReportService _reportService;
|
||||
private readonly ILogger<UserDailyReportsController> _logger;
|
||||
private readonly IWebHostEnvironment _environment;
|
||||
|
||||
public UserDailyReportsController(
|
||||
IUserDailyReportService reportService,
|
||||
ILogger<UserDailyReportsController> logger,
|
||||
IWebHostEnvironment environment)
|
||||
{
|
||||
_reportService = reportService;
|
||||
_logger = logger;
|
||||
_environment = environment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت لیست گزارشهای روزانه کاربران با فیلتر
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<PagedResult<UserDailyReportDto>>> GetReports(
|
||||
[FromQuery] int? deviceId,
|
||||
[FromQuery] int? userId,
|
||||
[FromQuery] string? persianDate,
|
||||
[FromQuery] int? year,
|
||||
[FromQuery] int? month,
|
||||
[FromQuery] int page = 1,
|
||||
[FromQuery] int pageSize = 20,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var filter = new UserDailyReportFilter
|
||||
{
|
||||
DeviceId = deviceId,
|
||||
UserId = userId,
|
||||
PersianDate = persianDate,
|
||||
Year = year,
|
||||
Month = month,
|
||||
Page = page,
|
||||
PageSize = pageSize
|
||||
};
|
||||
|
||||
var result = await _reportService.GetReportsAsync(filter, cancellationToken);
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// دریافت جزئیات یک گزارش روزانه
|
||||
/// </summary>
|
||||
[HttpGet("{id}")]
|
||||
public async Task<ActionResult<UserDailyReportDto>> GetReportById(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await _reportService.GetReportByIdAsync(id, cancellationToken);
|
||||
if (result == null)
|
||||
{
|
||||
return NotFound(new { error = $"گزارش با شناسه {id} یافت نشد" });
|
||||
}
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ایجاد گزارش روزانه جدید
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<int>> CreateReport(
|
||||
CreateUserDailyReportRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var id = await _reportService.CreateReportAsync(request, cancellationToken);
|
||||
return Ok(new { id, message = "گزارش با موفقیت ایجاد شد" });
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
return BadRequest(new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ویرایش گزارش روزانه
|
||||
/// </summary>
|
||||
[HttpPut]
|
||||
public async Task<ActionResult> UpdateReport(
|
||||
UpdateUserDailyReportRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _reportService.UpdateReportAsync(request, cancellationToken);
|
||||
return Ok(new { message = "گزارش با موفقیت ویرایش شد" });
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return NotFound(new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// حذف گزارش روزانه
|
||||
/// </summary>
|
||||
[HttpDelete("{id}")]
|
||||
public async Task<ActionResult> DeleteReport(int id, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _reportService.DeleteReportAsync(id, cancellationToken);
|
||||
return Ok(new { message = "گزارش با موفقیت حذف شد" });
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return NotFound(new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// آپلود تصویر برای گزارش روزانه
|
||||
/// </summary>
|
||||
[HttpPost("{reportId}/images")]
|
||||
[Consumes("multipart/form-data")]
|
||||
public async Task<ActionResult<int>> UploadImage(
|
||||
int reportId,
|
||||
[FromForm] IFormFile file,
|
||||
[FromForm] string? description,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (file == null || file.Length == 0)
|
||||
{
|
||||
return BadRequest(new { error = "فایل انتخاب نشده است" });
|
||||
}
|
||||
|
||||
// Validate file type
|
||||
var allowedTypes = new[] { "image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp" };
|
||||
if (!allowedTypes.Contains(file.ContentType.ToLower()))
|
||||
{
|
||||
return BadRequest(new { error = "فقط فایلهای تصویری مجاز هستند" });
|
||||
}
|
||||
|
||||
// Validate file size (max 5MB)
|
||||
if (file.Length > 5 * 1024 * 1024)
|
||||
{
|
||||
return BadRequest(new { error = "حجم فایل نباید بیشتر از 5 مگابایت باشد" });
|
||||
}
|
||||
|
||||
// Create upload directory
|
||||
var uploadsFolder = Path.Combine(_environment.WebRootPath ?? "wwwroot", "uploads", "reports");
|
||||
Directory.CreateDirectory(uploadsFolder);
|
||||
|
||||
// Generate unique filename
|
||||
var fileName = $"{Guid.NewGuid()}_{Path.GetFileName(file.FileName)}";
|
||||
var filePath = Path.Combine(uploadsFolder, fileName);
|
||||
|
||||
// Save file
|
||||
using (var stream = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await file.CopyToAsync(stream, cancellationToken);
|
||||
}
|
||||
|
||||
// Save to database
|
||||
var relativePath = $"/uploads/reports/{fileName}";
|
||||
var imageId = await _reportService.AddImageToReportAsync(
|
||||
reportId,
|
||||
file.FileName,
|
||||
relativePath,
|
||||
file.ContentType,
|
||||
file.Length,
|
||||
description,
|
||||
cancellationToken);
|
||||
|
||||
_logger.LogInformation("Image uploaded for report {ReportId}: {ImageId}", reportId, imageId);
|
||||
|
||||
return Ok(new { imageId, filePath = relativePath, message = "تصویر با موفقیت آپلود شد" });
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return NotFound(new { error = ex.Message });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error uploading image for report {ReportId}", reportId);
|
||||
return StatusCode(500, new { error = "خطا در آپلود تصویر" });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// حذف تصویر از گزارش
|
||||
/// </summary>
|
||||
[HttpDelete("images/{imageId}")]
|
||||
public async Task<ActionResult> DeleteImage(int imageId, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _reportService.DeleteImageAsync(imageId, cancellationToken);
|
||||
return Ok(new { message = "تصویر با موفقیت حذف شد" });
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return NotFound(new { error = ex.Message });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<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" />
|
||||
<PackageReference Include="Scalar.AspNetCore" Version="2.11.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -5,12 +5,13 @@ using GreenHome.Infrastructure;
|
||||
using GreenHome.Sms.Ippanel;
|
||||
using GreenHome.VoiceCall.Avanak;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Scalar.AspNetCore;
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
builder.Services.AddOpenApi();
|
||||
|
||||
// Application/Infrastructure DI
|
||||
builder.Services.AddAutoMapper(typeof(GreenHome.Application.MappingProfile));
|
||||
@@ -60,6 +61,11 @@ builder.Services.AddScoped<GreenHome.Application.IAlertConditionService, GreenHo
|
||||
builder.Services.AddScoped<GreenHome.Application.ISunCalculatorService, GreenHome.Infrastructure.SunCalculatorService>();
|
||||
builder.Services.AddScoped<GreenHome.Application.IAIQueryService, GreenHome.Infrastructure.AIQueryService>();
|
||||
builder.Services.AddScoped<GreenHome.Application.IDailyReportService, GreenHome.Infrastructure.DailyReportService>();
|
||||
builder.Services.AddScoped<GreenHome.Application.IAlertLogService, GreenHome.Infrastructure.AlertLogService>();
|
||||
builder.Services.AddScoped<GreenHome.Application.IUserDailyReportService, GreenHome.Infrastructure.UserDailyReportService>();
|
||||
builder.Services.AddScoped<GreenHome.Application.IChecklistService, GreenHome.Infrastructure.ChecklistService>();
|
||||
builder.Services.AddScoped<GreenHome.Application.IMonthlyReportService, GreenHome.Infrastructure.MonthlyReportService>();
|
||||
builder.Services.AddScoped<GreenHome.Application.IDevicePostService, GreenHome.Infrastructure.DevicePostService>();
|
||||
|
||||
// SMS Service Configuration
|
||||
builder.Services.AddIppanelSms(builder.Configuration);
|
||||
@@ -89,11 +95,8 @@ using (var scope = app.Services.CreateScope())
|
||||
}
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
//if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
app.MapOpenApi();
|
||||
app.MapScalarApiReference();
|
||||
|
||||
// HTTPS Redirection فقط در Production
|
||||
if (!app.Environment.IsDevelopment())
|
||||
|
||||
Reference in New Issue
Block a user