Implement camera bug report system with CRUD operations and logging

This commit is contained in:
2025-12-07 17:54:55 +03:30
parent 3c0ec01f77
commit 167b2bce09
35 changed files with 2879 additions and 6 deletions

View File

@@ -0,0 +1,34 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
using CompanyManagment.App.Contracts.CameraBugReport;
namespace ServiceHost.Areas.AdminNew.Pages.BugReport
{
public class BugReportPageModel : PageModel
{
protected readonly ICameraBugReportApplication _bugReportApplication;
public BugReportPageModel(ICameraBugReportApplication bugReportApplication)
{
_bugReportApplication = bugReportApplication;
}
public List<CameraBugReportViewModel> BugReports { get; set; } = new();
public CameraBugReportDetailViewModel BugReportDetails { get; set; }
protected List<CameraBugReportViewModel> GetBugReportsList(CameraBugReportSearchModel searchModel)
{
return _bugReportApplication.GetAll(searchModel);
}
protected CameraBugReportDetailViewModel GetBugReportDetails(long id)
{
return _bugReportApplication.GetDetails(id);
}
protected bool IsExist(long id)
{
return _bugReportApplication.IsExist(id);
}
}
}

View File

@@ -0,0 +1,52 @@
@page "{id:long}"
@model ServiceHost.Areas.AdminNew.Pages.BugReport.DeleteModel
@{
ViewData["Title"] = "حذف گزارش خرابی";
}
<div class="container-fluid mt-4">
<a asp-page="./Index" class="btn btn-secondary mb-3">بازگشت</a>
@if (Model.BugReportDetails != null)
{
<div class="card border-danger">
<div class="card-header bg-danger text-white">
<h5 class="mb-0">تأیید حذف</h5>
</div>
<div class="card-body">
<div class="alert alert-warning">
<strong>هشدار:</strong> آیا مطمئن هستید که می‌خواهید این گزارش خرابی را حذف کنید؟ این عمل غیرقابل بازگشت است.
</div>
<div class="mb-3">
<label class="form-label"><strong>عنوان:</strong></label>
<p>@Model.BugReportDetails.Title</p>
</div>
<div class="mb-3">
<label class="form-label"><strong>کاربر:</strong></label>
<p>@Model.BugReportDetails.UserEmail</p>
</div>
<div class="mb-3">
<label class="form-label"><strong>تاریخ گزارش:</strong></label>
<p>@Model.BugReportDetails.CreationDate.ToString("yyyy-MM-dd HH:mm:ss")</p>
</div>
<form method="post">
<input type="hidden" name="id" value="@Model.BugReportDetails.Id" />
<button type="submit" class="btn btn-danger">حذف</button>
<a asp-page="./Index" class="btn btn-secondary">انصراف</a>
</form>
</div>
</div>
}
else
{
<div class="alert alert-danger">
گزارش خرابی یافت نشد
</div>
}
</div>

View File

@@ -0,0 +1,33 @@
using CompanyManagment.App.Contracts.CameraBugReport;
using Microsoft.AspNetCore.Mvc;
namespace ServiceHost.Areas.AdminNew.Pages.BugReport;
public class DeleteModel : BugReportPageModel
{
public DeleteModel(ICameraBugReportApplication bugReportApplication) : base(bugReportApplication)
{
}
public void OnGet(long id)
{
BugReportDetails = GetBugReportDetails(id);
if (BugReportDetails == null)
{
TempData["ErrorMessage"] = "گزارش خرابی یافت نشد";
}
}
public IActionResult OnPost(long id)
{
var result = _bugReportApplication.Delete(id);
if (result.IsSuccedded)
{
TempData["SuccessMessage"] = result.Message;
return RedirectToPage("./Index");
}
TempData["ErrorMessage"] = result.Message;
return Page();
}
}

View File

@@ -0,0 +1,238 @@
@page "{id:long}"
@model ServiceHost.Areas.AdminNew.Pages.BugReport.DetailsModel
@{
ViewData["Title"] = "جزئیات گزارش خرابی";
}
@if (Model.BugReportDetails == null)
{
<div class="alert alert-danger">
گزارش خرابی یافت نشد
</div>
<a asp-page="./Index" class="btn btn-secondary">بازگشت</a>
}
else
{
<div class="container-fluid mt-4">
<a asp-page="./Index" class="btn btn-secondary mb-3">بازگشت</a>
<div class="row">
<!-- معلومات اصلی -->
<div class="col-md-8">
<div class="card mb-4">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">@Model.BugReportDetails.Title</h5>
</div>
<div class="card-body">
<dl class="row">
<dt class="col-sm-3">کاربر:</dt>
<dd class="col-sm-9">@Model.BugReportDetails.UserEmail</dd>
<dt class="col-sm-3">نوع:</dt>
<dd class="col-sm-9">
<span class="badge bg-info">@Model.BugReportDetails.Type</span>
</dd>
<dt class="col-sm-3">اولویت:</dt>
<dd class="col-sm-9">
@switch (Model.BugReportDetails.Priority)
{
case CameraBugPriority.Critical:
<span class="badge bg-danger">بحرانی</span>
break;
case CameraBugPriority.High:
<span class="badge bg-warning">بالا</span>
break;
case CameraBugPriority.Medium:
<span class="badge bg-primary">متوسط</span>
break;
case CameraBugPriority.Low:
<span class="badge bg-success">پایین</span>
break;
}
</dd>
<dt class="col-sm-3">وضعیت:</dt>
<dd class="col-sm-9">
@switch (Model.BugReportDetails.Status)
{
case CameraBugReportStatus.Open:
<span class="badge bg-secondary">باز</span>
break;
case CameraBugReportStatus.InProgress:
<span class="badge bg-warning">در حال بررسی</span>
break;
case CameraBugReportStatus.Fixed:
<span class="badge bg-info">رفع شده</span>
break;
case CameraBugReportStatus.Closed:
<span class="badge bg-success">بسته شده</span>
break;
case CameraBugReportStatus.Reopened:
<span class="badge bg-danger">مجدداً باز</span>
break;
}
</dd>
<dt class="col-sm-3">تاریخ گزارش:</dt>
<dd class="col-sm-9">@Model.BugReportDetails.CreationDate.ToString("yyyy-MM-dd HH:mm:ss")</dd>
<dt class="col-sm-3">آخرین به‌روزرسانی:</dt>
<dd class="col-sm-9">@(Model.BugReportDetails.UpdateDate?.ToString("yyyy-MM-dd HH:mm:ss") ?? "-")</dd>
</dl>
</div>
</div>
<!-- توضیحات -->
<div class="card mb-4">
<div class="card-header bg-secondary text-white">
<h6 class="mb-0">توضیحات</h6>
</div>
<div class="card-body">
<p>@Html.Raw(Model.BugReportDetails.Description.Replace(Environment.NewLine, "<br>"))</p>
</div>
</div>
<!-- Stack Trace -->
@if (!string.IsNullOrEmpty(Model.BugReportDetails.StackTrace))
{
<div class="card mb-4">
<div class="card-header bg-danger text-white">
<h6 class="mb-0">Stack Trace</h6>
</div>
<div class="card-body">
<pre style="background-color: #f5f5f5; padding: 10px; border-radius: 4px; overflow-x: auto;">@Model.BugReportDetails.StackTrace</pre>
</div>
</div>
}
<!-- لاگ‌ها -->
@if (Model.BugReportDetails.Logs != null && Model.BugReportDetails.Logs.Count > 0)
{
<div class="card mb-4">
<div class="card-header bg-warning">
<h6 class="mb-0">لاگ‌ها (@Model.BugReportDetails.Logs.Count)</h6>
</div>
<div class="card-body">
<ul class="list-unstyled">
@foreach (var log in Model.BugReportDetails.Logs)
{
<li style="padding: 5px 0; border-bottom: 1px solid #eee;">
<small>@log</small>
</li>
}
</ul>
</div>
</div>
}
<!-- عکس‌ها -->
@if (Model.BugReportDetails.Screenshots != null && Model.BugReportDetails.Screenshots.Count > 0)
{
<div class="card mb-4">
<div class="card-header bg-info text-white">
<h6 class="mb-0">عکس‌های ضمیمه شده (@Model.BugReportDetails.Screenshots.Count)</h6>
</div>
<div class="card-body">
<div class="row">
@foreach (var screenshot in Model.BugReportDetails.Screenshots)
{
<div class="col-md-6 mb-3">
<div class="card">
<img src="data:image/jpeg;base64,@screenshot.Base64Data" class="card-img-top" style="max-height: 300px; object-fit: cover;">
<div class="card-footer">
<small class="text-muted">@screenshot.FileName</small><br>
<small class="text-muted">@screenshot.UploadDate.ToString("yyyy-MM-dd HH:mm")</small>
</div>
</div>
</div>
}
</div>
</div>
</div>
}
</div>
<!-- معلومات دستگاه -->
<div class="col-md-4">
<div class="card mb-4">
<div class="card-header bg-success text-white">
<h6 class="mb-0">معلومات دستگاه</h6>
</div>
<div class="card-body">
<dl class="row" style="font-size: 0.9rem;">
<dt class="col-6">مدل:</dt>
<dd class="col-6">@Model.BugReportDetails.DeviceModel</dd>
<dt class="col-6">سیستم‌عامل:</dt>
<dd class="col-6">@Model.BugReportDetails.OsVersion</dd>
<dt class="col-6">پلتفرم:</dt>
<dd class="col-6">@Model.BugReportDetails.Platform</dd>
<dt class="col-6">سازنده:</dt>
<dd class="col-6">@Model.BugReportDetails.Manufacturer</dd>
<dt class="col-6">شناسه دستگاه:</dt>
<dd class="col-6"><small>@Model.BugReportDetails.DeviceId</small></dd>
<dt class="col-6">وضوح صفحه:</dt>
<dd class="col-6">@Model.BugReportDetails.ScreenResolution</dd>
<dt class="col-6">حافظه:</dt>
<dd class="col-6">@Model.BugReportDetails.MemoryInMB MB</dd>
<dt class="col-6">ذخیره‌سازی:</dt>
<dd class="col-6">@Model.BugReportDetails.StorageInMB MB</dd>
<dt class="col-6">باتری:</dt>
<dd class="col-6">@Model.BugReportDetails.BatteryLevel %</dd>
<dt class="col-6">شارژ گیر:</dt>
<dd class="col-6">@(Model.BugReportDetails.IsCharging ? "بله" : "خیر")</dd>
<dt class="col-6">شبکه:</dt>
<dd class="col-6">@Model.BugReportDetails.NetworkType</dd>
</dl>
</div>
</div>
<!-- معلومات برنامه -->
<div class="card">
<div class="card-header bg-dark text-white">
<h6 class="mb-0">معلومات برنامه</h6>
</div>
<div class="card-body">
<dl class="row" style="font-size: 0.9rem;">
<dt class="col-6">نسخه:</dt>
<dd class="col-6">@Model.BugReportDetails.AppVersion</dd>
<dt class="col-6">بیلد:</dt>
<dd class="col-6">@Model.BugReportDetails.BuildNumber</dd>
<dt class="col-6">پکیج:</dt>
<dd class="col-6"><small>@Model.BugReportDetails.PackageName</small></dd>
<dt class="col-6">نسخه (Flavor):</dt>
<dd class="col-6">@Model.BugReportDetails.Flavor</dd>
<dt class="col-6">نصب:</dt>
<dd class="col-6"><small>@Model.BugReportDetails.InstallTime.ToString("yyyy-MM-dd")</small></dd>
<dt class="col-6">آپدیت:</dt>
<dd class="col-6"><small>@Model.BugReportDetails.LastUpdateTime.ToString("yyyy-MM-dd")</small></dd>
</dl>
</div>
</div>
<!-- عملیات -->
<div class="mt-3">
<a asp-page="./Edit" asp-route-id="@Model.BugReportDetails.Id" class="btn btn-warning w-100">ویرایش وضعیت و اولویت</a>
<a asp-page="./Delete" asp-route-id="@Model.BugReportDetails.Id" class="btn btn-danger w-100 mt-2" onclick="return confirm('آیا مطمئن هستید؟');">حذف</a>
</div>
</div>
</div>
</div>
}

View File

@@ -0,0 +1,19 @@
using CompanyManagment.App.Contracts.CameraBugReport;
namespace ServiceHost.Areas.AdminNew.Pages.BugReport;
public class DetailsModel : BugReportPageModel
{
public DetailsModel(ICameraBugReportApplication bugReportApplication) : base(bugReportApplication)
{
}
public void OnGet(long id)
{
BugReportDetails = GetBugReportDetails(id);
if (BugReportDetails == null)
{
TempData["ErrorMessage"] = "گزارش خرابی یافت نشد";
}
}
}

View File

@@ -0,0 +1,75 @@
@page "{id:long}"
@model ServiceHost.Areas.AdminNew.Pages.BugReport.EditModel
@{
ViewData["Title"] = "ویرایش گزارش خرابی";
}
<div class="container-fluid mt-4">
<a asp-page="./Details" asp-route-id="@Model.Id" class="btn btn-secondary mb-3">بازگشت</a>
<div class="card">
<div class="card-header bg-warning text-white">
<h5 class="mb-0">ویرایش وضعیت و اولویت</h5>
</div>
<div class="card-body">
@if (Model.BugReportDetail != null)
{
<form method="post">
<input type="hidden" asp-for="Id" />
<div class="mb-3">
<label class="form-label"><strong>عنوان:</strong></label>
<p>@Model.BugReportDetail.Title</p>
</div>
<div class="mb-3">
<label class="form-label"><strong>کاربر:</strong></label>
<p>@Model.BugReportDetail.UserEmail</p>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="Priority" class="form-label">اولویت</label>
<select id="Priority" asp-for="Priority" class="form-control">
<option value="1" selected="@(Model.Priority == CameraBugPriority.Critical)">بحرانی</option>
<option value="2" selected="@(Model.Priority == CameraBugPriority.High)">بالا</option>
<option value="3" selected="@(Model.Priority == CameraBugPriority.Medium)">متوسط</option>
<option value="4" selected="@(Model.Priority == CameraBugPriority.Low)">پایین</option>
</select>
<span asp-validation-for="Priority" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="Status" class="form-label">وضعیت</label>
<select id="Status" asp-for="Status" class="form-control">
<option value="1" selected="@(Model.Status == CameraBugReportStatus.Open)">باز</option>
<option value="2" selected="@(Model.Status == CameraBugReportStatus.InProgress)">در حال بررسی</option>
<option value="3" selected="@(Model.Status == CameraBugReportStatus.Fixed)">رفع شده</option>
<option value="4" selected="@(Model.Status == CameraBugReportStatus.Closed)">بسته شده</option>
<option value="5" selected="@(Model.Status == CameraBugReportStatus.Reopened)">مجدداً باز</option>
</select>
<span asp-validation-for="Status" class="text-danger"></span>
</div>
</div>
</div>
<div class="mt-4">
<button type="submit" class="btn btn-success">ذخیره تغییرات</button>
<a asp-page="./Details" asp-route-id="@Model.Id" class="btn btn-secondary">انصراف</a>
</div>
</form>
}
else
{
<div class="alert alert-danger">
گزارش خرابی یافت نشد
</div>
}
</div>
</div>
</div>

View File

@@ -0,0 +1,62 @@
using Microsoft.AspNetCore.Mvc;
using CompanyManagment.App.Contracts.CameraBugReport;
namespace ServiceHost.Areas.AdminNew.Pages.BugReport
{
public class EditModel : BugReportPageModel
{
public EditModel(ICameraBugReportApplication cameraBugReportApplication) : base(cameraBugReportApplication)
{
}
[BindProperty]
public long Id { get; set; }
[BindProperty]
public CameraBugPriority Priority { get; set; }
[BindProperty]
public CameraBugReportStatus Status { get; set; }
public CameraBugReportDetailViewModel BugReportDetail { get; set; }
public void OnGet(long id)
{
BugReportDetail = GetBugReportDetails(id);
if (BugReportDetail != null)
{
Id = BugReportDetail.Id;
Priority = BugReportDetail.Priority;
Status = BugReportDetail.Status;
}
else
{
TempData["ErrorMessage"] = "گزارش خرابی یافت نشد";
}
}
public IActionResult OnPost()
{
if (!ModelState.IsValid)
return Page();
var command = new EditCameraBugReportCommand()
{
Id = Id,
Priority = Priority,
Status = Status
};
var result = _bugReportApplication.Edit(command);
if (result.IsSuccedded)
{
TempData["SuccessMessage"] = result.Message;
return RedirectToPage("./Details", new { id = Id });
}
TempData["ErrorMessage"] = result.Message;
return Page();
}
}
}

View File

@@ -0,0 +1,181 @@
@page
@model ServiceHost.Areas.AdminNew.Pages.BugReport.IndexModel
@{
ViewData["Title"] = "مدیریت گزارش‌های خرابی";
}
<div class="container-fluid mt-4">
<div class="card">
<div class="card-header bg-primary text-white">
<h5 class="mb-0">لیست گزارش‌های خرابی</h5>
</div>
<div class="card-body">
<!-- فیلترها -->
<form method="get" class="row g-3 mb-4">
<div class="col-md-3">
<label for="searchTerm" class="form-label">جستجو</label>
<input type="text" class="form-control" id="searchTerm" name="searchTerm" value="@Model.SearchTerm" placeholder="عنوان، توضیحات، ایمیل...">
</div>
<div class="col-md-2">
<label for="typeFilter" class="form-label">نوع</label>
<select class="form-control" id="typeFilter" name="typeFilter">
<option value="">همه</option>
<option value="1" selected="@(Model.TypeFilter == 1)">کرش</option>
<option value="2" selected="@(Model.TypeFilter == 2)">مشکل UI</option>
<option value="3" selected="@(Model.TypeFilter == 3)">عملکرد</option>
<option value="4" selected="@(Model.TypeFilter == 4)">فیچر</option>
<option value="5" selected="@(Model.TypeFilter == 5)">شبکه</option>
<option value="6" selected="@(Model.TypeFilter == 6)">دوربین</option>
<option value="7" selected="@(Model.TypeFilter == 7)">تشخیص چهره</option>
<option value="8" selected="@(Model.TypeFilter == 8)">دیتابیس</option>
<option value="9" selected="@(Model.TypeFilter == 9)">لاگین</option>
<option value="10" selected="@(Model.TypeFilter == 10)">سایر</option>
</select>
</div>
<div class="col-md-2">
<label for="priorityFilter" class="form-label">اولویت</label>
<select class="form-control" id="priorityFilter" name="priorityFilter">
<option value="">همه</option>
<option value="1" selected="@(Model.PriorityFilter == 1)">بحرانی</option>
<option value="2" selected="@(Model.PriorityFilter == 2)">بالا</option>
<option value="3" selected="@(Model.PriorityFilter == 3)">متوسط</option>
<option value="4" selected="@(Model.PriorityFilter == 4)">پایین</option>
</select>
</div>
<div class="col-md-2">
<label for="statusFilter" class="form-label">وضعیت</label>
<select class="form-control" id="statusFilter" name="statusFilter">
<option value="">همه</option>
<option value="1" selected="@(Model.StatusFilter == 1)">باز</option>
<option value="2" selected="@(Model.StatusFilter == 2)">در حال بررسی</option>
<option value="3" selected="@(Model.StatusFilter == 3)">رفع شده</option>
<option value="4" selected="@(Model.StatusFilter == 4)">بسته شده</option>
<option value="5" selected="@(Model.StatusFilter == 5)">مجدداً باز</option>
</select>
</div>
<div class="col-md-3 d-flex align-items-end">
<button type="submit" class="btn btn-primary w-100">جستجو</button>
</div>
</form>
<!-- جدول -->
<div class="table-responsive">
<table class="table table-hover">
<thead class="table-light">
<tr>
<th>عنوان</th>
<th>کاربر</th>
<th>نوع</th>
<th>اولویت</th>
<th>وضعیت</th>
<th>دستگاه</th>
<th>نسخه</th>
<th>تاریخ</th>
<th>عملیات</th>
</tr>
</thead>
<tbody>
@if (Model.BugReports.Count > 0)
{
@foreach (var report in Model.BugReports)
{
<tr>
<td>
<strong>@Html.DisplayFor(modelItem => report.Title)</strong>
</td>
<td>
<small>@report.UserEmail</small>
</td>
<td>
<span class="badge bg-info">@report.Type</span>
</td>
<td>
@switch (report.Priority)
{
case CameraBugPriority.Critical:
<span class="badge bg-danger">بحرانی</span>
break;
case CameraBugPriority.High:
<span class="badge bg-warning">بالا</span>
break;
case CameraBugPriority.Medium:
<span class="badge bg-primary">متوسط</span>
break;
case CameraBugPriority.Low:
<span class="badge bg-success">پایین</span>
break;
}
</td>
<td>
@switch (report.Status)
{
case CameraBugReportStatus.Open:
<span class="badge bg-secondary">باز</span>
break;
case CameraBugReportStatus.InProgress:
<span class="badge bg-warning">در حال بررسی</span>
break;
case CameraBugReportStatus.Fixed:
<span class="badge bg-info">رفع شده</span>
break;
case CameraBugReportStatus.Closed:
<span class="badge bg-success">بسته شده</span>
break;
case CameraBugReportStatus.Reopened:
<span class="badge bg-danger">مجدداً باز</span>
break;
}
</td>
<td>
<small>@report.DeviceModel</small>
</td>
<td>
<small>@report.AppVersion</small>
</td>
<td>
<small>@report.CreationDate.ToString("yyyy-MM-dd HH:mm")</small>
</td>
<td>
<a asp-page="./Details" asp-route-id="@report.Id" class="btn btn-sm btn-info">مشاهده</a>
<a asp-page="./Edit" asp-route-id="@report.Id" class="btn btn-sm btn-warning">ویرایش</a>
<a asp-page="./Delete" asp-route-id="@report.Id" class="btn btn-sm btn-danger" onclick="return confirm('آیا مطمئن هستید؟');">حذف</a>
</td>
</tr>
}
}
else
{
<tr>
<td colspan="9" class="text-center text-muted py-4">
هیچ گزارش خرابی یافت نشد
</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
</div>
<style>
.table-hover tbody tr:hover {
background-color: #f5f5f5;
}
.badge {
padding: 0.35rem 0.65rem;
font-size: 0.875rem;
}
.btn-sm {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
}
</style>

View File

@@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Mvc;
using CompanyManagment.App.Contracts.CameraBugReport;
namespace ServiceHost.Areas.AdminNew.Pages.BugReport
{
public class IndexModel : BugReportPageModel
{
public IndexModel(ICameraBugReportApplication bugReportApplication) : base(bugReportApplication)
{
}
[BindProperty(SupportsGet = true)]
public int? TypeFilter { get; set; }
[BindProperty(SupportsGet = true)]
public int? PriorityFilter { get; set; }
[BindProperty(SupportsGet = true)]
public int? StatusFilter { get; set; }
[BindProperty(SupportsGet = true)]
public string SearchTerm { get; set; }
[BindProperty(SupportsGet = true)]
public int PageNumber { get; set; } = 1;
public void OnGet()
{
var searchModel = new CameraBugReportSearchModel()
{
Type = TypeFilter.HasValue ? (CameraBugReportType)TypeFilter.Value : null,
Priority = PriorityFilter.HasValue ? (CameraBugPriority)PriorityFilter.Value : null,
Status = StatusFilter.HasValue ? (CameraBugReportStatus)StatusFilter.Value : null,
SearchTerm = SearchTerm ?? "",
PageNumber = PageNumber > 0 ? PageNumber : 1,
PageSize = 10
};
BugReports = GetBugReportsList(searchModel);
}
}
}

View File

@@ -16,6 +16,7 @@ using CompanyManagment.App.Contracts.RollCallEmployee;
using CompanyManagment.App.Contracts.RollCallService;
using CompanyManagment.App.Contracts.Employee;
using CompanyManagment.App.Contracts.EmployeeFaceEmbedding;
using CompanyManagment.App.Contracts.CameraBugReport;
using Microsoft.AspNetCore.Authorization;
namespace ServiceHost.Areas.Camera.Controllers;
@@ -38,6 +39,7 @@ public class CameraController : CameraBaseController
private readonly IHttpClientFactory _httpClientFactory;
private readonly HttpClient _faceEmbeddingHttpClient;
private readonly IAndroidApkVersionApplication _androidApkVersionApplication;
private readonly ICameraBugReportApplication _cameraBugReportApplication;
public CameraController(IWebHostEnvironment webHostEnvironment,
IConfiguration configuration,
@@ -50,7 +52,10 @@ public class CameraController : CameraBaseController
IAccountApplication accountApplication,
IPasswordHasher passwordHasher,
ICameraAccountApplication cameraAccountApplication,
IEmployeeFaceEmbeddingApplication employeeFaceEmbeddingApplication, IHttpClientFactory httpClientFactory, IAndroidApkVersionApplication androidApkVersionApplication)
IEmployeeFaceEmbeddingApplication employeeFaceEmbeddingApplication,
IHttpClientFactory httpClientFactory,
IAndroidApkVersionApplication androidApkVersionApplication,
ICameraBugReportApplication cameraBugReportApplication)
{
_webHostEnvironment = webHostEnvironment;
_configuration = configuration;
@@ -66,6 +71,7 @@ public class CameraController : CameraBaseController
_employeeFaceEmbeddingApplication = employeeFaceEmbeddingApplication;
_httpClientFactory = httpClientFactory;
_androidApkVersionApplication = androidApkVersionApplication;
_cameraBugReportApplication = cameraBugReportApplication;
_faceEmbeddingHttpClient = httpClientFactory.CreateClient();
_faceEmbeddingHttpClient.BaseAddress = new Uri("http://localhost:8000/");
_workshopId= authHelper.GetWorkshopId();
@@ -335,12 +341,84 @@ public class CameraController : CameraBaseController
_accountApplication.Logout();
return new JsonResult(new { isSuccess = true });
}
/// <summary>
/// ارسال گزارش خرابی از طرف دوربین
/// </summary>
[HttpPost("bug-report")]
[AllowAnonymous]
public IActionResult SubmitBugReport([FromBody] SubmitBugReportRequest request)
{
if (!ModelState.IsValid)
{
return BadRequest(new { success = false, message = "داده‌های ارسالی معتبر نیستند" });
}
try
{
var command = new CreateCameraBugReportCommand
{
Title = request.Title,
Description = request.Description,
UserEmail = request.UserEmail,
AccountId = request.AccountId,
DeviceModel = request.DeviceInfo?.DeviceModel,
OsVersion = request.DeviceInfo?.OsVersion,
Platform = request.DeviceInfo?.Platform,
Manufacturer = request.DeviceInfo?.Manufacturer,
DeviceId = request.DeviceInfo?.DeviceId,
ScreenResolution = request.DeviceInfo?.ScreenResolution,
MemoryInMB = request.DeviceInfo?.MemoryInMB ?? 0,
StorageInMB = request.DeviceInfo?.StorageInMB ?? 0,
BatteryLevel = request.DeviceInfo?.BatteryLevel ?? 0,
IsCharging = request.DeviceInfo?.IsCharging ?? false,
NetworkType = request.DeviceInfo?.NetworkType,
AppVersion = request.AppInfo?.AppVersion,
BuildNumber = request.AppInfo?.BuildNumber,
PackageName = request.AppInfo?.PackageName,
InstallTime = request.AppInfo?.InstallTime ?? DateTime.Now,
LastUpdateTime = request.AppInfo?.LastUpdateTime ?? DateTime.Now,
Flavor = request.AppInfo?.Flavor,
Type = (CameraBugReportType)request.Type,
Priority = (CameraBugPriority)request.Priority,
StackTrace = request.StackTrace,
Logs = request.Logs,
Screenshots = request.Screenshots
};
var result = _cameraBugReportApplication.Create(command);
if (result.IsSuccedded)
{
return Ok(new
{
success = true,
message = result.Message ?? "گزارش خرابی با موفقیت ارسال شد"
});
}
return BadRequest(new
{
success = false,
message = result.Message ?? "خطا در ارسال گزارش خرابی"
});
}
catch (Exception ex)
{
return BadRequest(new
{
success = false,
message = $"خطا در ارسال گزارش خرابی: {ex.Message}"
});
}
}
}
public class RollCallExitRequest:RollCallEnterRequest
{
public long FlagId { get; set; }
}
public class RollCallEnterRequest
{
public long EmployeeId { get; set; }
@@ -352,4 +430,53 @@ public class CameraFlagRequest
{
public long EmployeeId { get; set; }
public long WorkshopId { get; set; }
}
}
/// <summary>
/// درخواست ارسال گزارش خرابی از طرف دوربین
/// </summary>
public class SubmitBugReportRequest
{
public string Title { get; set; }
public string Description { get; set; }
public string UserEmail { get; set; }
public long? AccountId { get; set; }
public int Type { get; set; } // BugReportType enum value
public int Priority { get; set; } // BugPriority enum value
public string StackTrace { get; set; }
public List<string> Logs { get; set; }
public List<string> Screenshots { get; set; } // Base64 encoded images
public DeviceInfoRequest DeviceInfo { get; set; }
public AppInfoRequest AppInfo { get; set; }
}
/// <summary>
/// اطلاعات دستگاه
/// </summary>
public class DeviceInfoRequest
{
public string DeviceModel { get; set; }
public string OsVersion { get; set; }
public string Platform { get; set; }
public string Manufacturer { get; set; }
public string DeviceId { get; set; }
public string ScreenResolution { get; set; }
public int MemoryInMB { get; set; }
public int StorageInMB { get; set; }
public int BatteryLevel { get; set; }
public bool IsCharging { get; set; }
public string NetworkType { get; set; }
}
/// <summary>
/// اطلاعات برنامه
/// </summary>
public class AppInfoRequest
{
public string AppVersion { get; set; }
public string BuildNumber { get; set; }
public string PackageName { get; set; }
public DateTime InstallTime { get; set; }
public DateTime LastUpdateTime { get; set; }
public string Flavor { get; set; }
}

View File

@@ -0,0 +1,105 @@
using CompanyManagment.App.Contracts.CameraBugReport;
using Microsoft.AspNetCore.Mvc;
namespace ServiceHost.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class BugReportController : ControllerBase
{
private readonly ICameraBugReportApplication _bugReportApplication;
public BugReportController(ICameraBugReportApplication bugReportApplication)
{
_bugReportApplication = bugReportApplication;
}
/// <summary>
/// ثبت یک گزارش خرابی جدید
/// </summary>
[HttpPost("submit")]
public IActionResult SubmitBugReport([FromBody] CreateCameraBugReportCommand command)
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
var result = _bugReportApplication.Create(command);
if (result.IsSuccedded)
return Ok(new { success = true, message = result.Message });
return BadRequest(new { success = false, message = result.Message });
}
/// <summary>
/// دریافت تمام گزارش‌های خرابی (برای Admin)
/// </summary>
[HttpGet("list")]
public IActionResult GetBugReports(
[FromQuery] int? type,
[FromQuery] int? priority,
[FromQuery] int? status,
[FromQuery] string searchTerm = "",
[FromQuery] int pageNumber = 1,
[FromQuery] int pageSize = 10)
{
var searchModel = new CameraBugReportSearchModel
{
Type = type.HasValue ? (CameraBugReportType)type.Value : null,
Priority = priority.HasValue ? (CameraBugPriority)priority.Value : null,
Status = status.HasValue ? (CameraBugReportStatus)status.Value : null,
SearchTerm = searchTerm,
PageNumber = pageNumber,
PageSize = pageSize
};
var bugReports = _bugReportApplication.GetAll(searchModel);
return Ok(new { success = true, data = bugReports });
}
/// <summary>
/// دریافت جزئیات یک گزارش خرابی
/// </summary>
[HttpGet("{id}")]
public IActionResult GetBugReportDetails(long id)
{
var bugReport = _bugReportApplication.GetDetails(id);
if (bugReport == null)
return NotFound(new { success = false, message = "گزارش خرابی یافت نشد." });
return Ok(new { success = true, data = bugReport });
}
/// <summary>
/// ویرایش یک گزارش خرابی
/// </summary>
[HttpPut("{id}")]
public IActionResult EditBugReport(long id, [FromBody] EditCameraBugReportCommand command)
{
if (id != command.Id)
return BadRequest(new { success = false, message = "ID مطابقت ندارد." });
if (!ModelState.IsValid)
return BadRequest(ModelState);
var result = _bugReportApplication.Edit(command);
if (result.IsSuccedded)
return Ok(new { success = true, message = result.Message });
return BadRequest(new { success = false, message = result.Message });
}
/// <summary>
/// حذف یک گزارش خرابی
/// </summary>
[HttpDelete("{id}")]
public IActionResult DeleteBugReport(long id)
{
var result = _bugReportApplication.Delete(id);
if (result.IsSuccedded)
return Ok(new { success = true, message = result.Message });
return BadRequest(new { success = false, message = result.Message });
}
}
}

View File

@@ -19,7 +19,7 @@
"sqlDebugging": true,
"dotnetRunMessages": "true",
"nativeDebugging": true,
"applicationUrl": "https://localhost:5004;http://localhost:5003;",
"applicationUrl": "https://localhost:5004;http://localhost:5003;https://192.168.0.117:5006",
"jsWebView2Debugging": false,
"hotReloadEnabled": true
},