feat & fix : add version 0.17.16.23 , fix respons home page , add changelog

add change log dialog , add home page view model
release
Amir Hossein Khademi 2024-02-25 18:21:01 +03:30
parent 07982e63ad
commit fdd14db53d
24 changed files with 1962 additions and 13 deletions

View File

@ -17,7 +17,8 @@
"TaxesFee": 9 "TaxesFee": 9
}, },
"SiteSettings": { "SiteSettings": {
"BaseUrl": "https://api.vesmeh.com", "BaseUrl": "https://api.vesmook.com",
"AdminPanelBaseUrl": "https://admin.vesmook.com",
"KaveNegarApiKey": "3735494B4143727A794346457461576A2B4B6668414973424E333561505A694B", "KaveNegarApiKey": "3735494B4143727A794346457461576A2B4B6668414973424E333561505A694B",
"UserSetting": { "UserSetting": {
"Username": "09214802813", "Username": "09214802813",

View File

@ -18,6 +18,7 @@
}, },
"SiteSettings": { "SiteSettings": {
"BaseUrl": "http://192.168.88.251:32770", "BaseUrl": "http://192.168.88.251:32770",
"AdminPanelBaseUrl": "https://admin.vesmook.com",
"KaveNegarApiKey": "3735494B4143727A794346457461576A2B4B6668414973424E333561505A694B", "KaveNegarApiKey": "3735494B4143727A794346457461576A2B4B6668414973424E333561505A694B",
"UserSetting": { "UserSetting": {
"Username": "netinashop", "Username": "netinashop",

View File

@ -0,0 +1,19 @@
namespace NetinaShop.Api.Controller;
public class DashboardController : ICarterModule
{
public void AddRoutes(IEndpointRouteBuilder app)
{
var group = app.NewVersionedApi("Dashboard")
.MapGroup("api/dashboard")
.RequireAuthorization(builder => builder.AddAuthenticationSchemes("Bearer").RequireAuthenticatedUser());
group.MapGet("home", GetHomeDashboardAsync)
.WithDisplayName("Get Home Dashboard")
.HasApiVersion(1.0);
}
private async Task<IResult> GetHomeDashboardAsync([FromServices] IDashboardService dashboardService, CancellationToken cancellationToken)
=> TypedResults.Ok(await dashboardService.GetHomeDashboardAsyncTask(cancellationToken));
}

View File

@ -1,8 +1,4 @@
using NetinaShop.Repository.Repositories.Entity.Abstracts; namespace NetinaShop.Api.Controller;
using System.Reflection;
using System.Text.Json;
namespace NetinaShop.Api.Controller;
public class PageController : ICarterModule public class PageController : ICarterModule
{ {

View File

@ -26,6 +26,10 @@ public class UserController : ICarterModule
.WithDisplayName("GetUser") .WithDisplayName("GetUser")
.HasApiVersion(1.0); .HasApiVersion(1.0);
group.MapGet("/changelog", GetChangeLogAsync)
.WithDisplayName("GetChangeLog")
.HasApiVersion(1.0);
group.MapPost("", Post) group.MapPost("", Post)
.HasApiVersion(1.0); .HasApiVersion(1.0);
@ -36,6 +40,9 @@ public class UserController : ICarterModule
.HasApiVersion(1.0); .HasApiVersion(1.0);
} }
private async Task<IResult> GetChangeLogAsync(IUserService userService, CancellationToken cancellationToken)
=> TypedResults.Ok(await userService.GetAdminChangeLogAsync(cancellationToken));
public async Task<IResult> GetUserInfoAsync(IUserService userService,ICurrentUserService currentUserService, CancellationToken cancellationToken) public async Task<IResult> GetUserInfoAsync(IUserService userService,ICurrentUserService currentUserService, CancellationToken cancellationToken)
{ {

View File

@ -74,6 +74,7 @@
<Using Include="NetinaShop.Common.Models.Mapper" /> <Using Include="NetinaShop.Common.Models.Mapper" />
<Using Include="NetinaShop.Core" /> <Using Include="NetinaShop.Core" />
<Using Include="NetinaShop.Core.Abstracts" /> <Using Include="NetinaShop.Core.Abstracts" />
<Using Include="NetinaShop.Core.BaseServices.Abstracts" />
<Using Include="NetinaShop.Core.CoreServices.Abstracts" /> <Using Include="NetinaShop.Core.CoreServices.Abstracts" />
<Using Include="NetinaShop.Core.Models.Api" /> <Using Include="NetinaShop.Core.Models.Api" />
<Using Include="NetinaShop.Core.Utilities" /> <Using Include="NetinaShop.Core.Utilities" />

View File

@ -0,0 +1,6 @@
namespace NetinaShop.Core.Abstracts;
public interface IExternalFilesService : IScopedDependency
{
Task<AdminChangeLogResponseDto> GetAdminChangeLogAsync(CancellationToken cancellationToken = default);
}

View File

@ -0,0 +1,6 @@
namespace NetinaShop.Core.BaseServices.Abstracts;
public interface IDashboardService : IScopedDependency
{
public Task<HomeDashboardDto> GetHomeDashboardAsyncTask(CancellationToken cancellationToken = default);
}

View File

@ -0,0 +1,45 @@
using NetinaShop.Domain.Entities.Blogs;
using NetinaShop.Domain.Entities.Brands;
namespace NetinaShop.Core.BaseServices;
public class DashboardService : IDashboardService
{
private readonly IRepositoryWrapper _repositoryWrapper;
private readonly UserManager<ApplicationUser> _userManager;
public DashboardService(IRepositoryWrapper repositoryWrapper,UserManager<ApplicationUser> userManager)
{
_repositoryWrapper = repositoryWrapper;
_userManager = userManager;
}
public async Task<HomeDashboardDto> GetHomeDashboardAsyncTask(CancellationToken cancellationToken = default)
{
var response = new HomeDashboardDto();
response.BlogsCount = await _repositoryWrapper.SetRepository<Blog>()
.TableNoTracking
.CountAsync(cancellationToken);
response.ProductsCount = await _repositoryWrapper.SetRepository<Product>()
.TableNoTracking
.CountAsync(cancellationToken);
response.TodayOrdersCount = await _repositoryWrapper.SetRepository<Order>()
.TableNoTracking
.Where(o => o.OrderAt.Date == DateTime.Today.Date)
.CountAsync(cancellationToken);
response.UnSubmittedOrdersCount = await _repositoryWrapper.SetRepository<Order>()
.TableNoTracking
.Where(o => o.OrderStatus == OrderStatus.Paid || o.OrderStatus == OrderStatus.Submitted)
.CountAsync(cancellationToken);
response.BrandsCount = await _repositoryWrapper.SetRepository<Brand>()
.TableNoTracking
.CountAsync(cancellationToken);
response.SubscribersCount = await _userManager.Users.CountAsync(cancellationToken);
return response;
}
}

View File

@ -10,6 +10,7 @@ public interface IUserService : IScopedDependency
Task<bool> EditUserAsync(UserActionRequestDto request, CancellationToken cancellationToken); Task<bool> EditUserAsync(UserActionRequestDto request, CancellationToken cancellationToken);
Task<bool> EditUserProfileAsync(UserActionRequestDto request, CancellationToken cancellationToken); Task<bool> EditUserProfileAsync(UserActionRequestDto request, CancellationToken cancellationToken);
Task<bool> RemoveUserAsync(Guid userId, CancellationToken cancellationToken); Task<bool> RemoveUserAsync(Guid userId, CancellationToken cancellationToken);
Task<AdminChangeLogResponseDto> GetAdminChangeLogAsync(CancellationToken cancellationToken = default);
Task<List<ApplicationRole>> GetRolesAsync(int page = 0, CancellationToken cancellationToken = default); Task<List<ApplicationRole>> GetRolesAsync(int page = 0, CancellationToken cancellationToken = default);

View File

@ -10,20 +10,17 @@ public class UserService : IUserService
private readonly ICurrentUserService _currentUserService; private readonly ICurrentUserService _currentUserService;
private readonly UserManager<ApplicationUser> _userManager; private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _roleManager; private readonly RoleManager<ApplicationRole> _roleManager;
private readonly IRepositoryWrapper _repositoryWrapper; private readonly IExternalFilesService _externalFilesService;
private readonly IJwtService _jwtService;
public UserService(ICurrentUserService currentUserService, public UserService(ICurrentUserService currentUserService,
UserManager<ApplicationUser> userManager, UserManager<ApplicationUser> userManager,
RoleManager<ApplicationRole> roleManager, RoleManager<ApplicationRole> roleManager,
IRepositoryWrapper repositoryWrapper, IExternalFilesService externalFilesService)
IJwtService jwtService)
{ {
_currentUserService = currentUserService; _currentUserService = currentUserService;
_userManager = userManager; _userManager = userManager;
_roleManager = roleManager; _roleManager = roleManager;
_repositoryWrapper = repositoryWrapper; _externalFilesService = externalFilesService;
_jwtService = jwtService;
} }
@ -256,8 +253,26 @@ public class UserService : IUserService
return true; return true;
} }
public async Task<AdminChangeLogResponseDto> GetAdminChangeLogAsync(CancellationToken cancellationToken = default)
{
if (!Guid.TryParse(_currentUserService.UserId, out var userId))
throw new AppException("Wrong Token", ApiResultStatusCode.UnAuthorized);
var user = await _userManager.FindByIdAsync(userId.ToString());
if (user == null)
throw new AppException("User NotFound", ApiResultStatusCode.NotFound);
var currentVersion = await _externalFilesService.GetAdminChangeLogAsync(cancellationToken);
if (!(user.LatestVersionUsed < currentVersion.VersionNumber)) return currentVersion;
currentVersion.IsNewVersion = true;
user.LatestVersionUsed = currentVersion.VersionNumber;
//await _userManager.UpdateAsync(user);
return currentVersion;
}
public async Task<List<ApplicationRole>> GetRolesAsync(int page = 0, CancellationToken cancellationToken = default) public async Task<List<ApplicationRole>> GetRolesAsync(int page = 0, CancellationToken cancellationToken = default)
{ {

View File

@ -23,7 +23,6 @@
<ItemGroup> <ItemGroup>
<Folder Include="BaseServices\Abstracts\" />
<Folder Include="EntityServices\ProductHandlers\" /> <Folder Include="EntityServices\ProductHandlers\" />
<Folder Include="EntityServices\ReviewHandlers\" /> <Folder Include="EntityServices\ReviewHandlers\" />
<Folder Include="Models\Api\" /> <Folder Include="Models\Api\" />
@ -50,6 +49,7 @@
<Using Include="NetinaShop.Core.EntityServices.Abstracts" /> <Using Include="NetinaShop.Core.EntityServices.Abstracts" />
<Using Include="NetinaShop.Domain.CommandQueries.Commands" /> <Using Include="NetinaShop.Domain.CommandQueries.Commands" />
<Using Include="NetinaShop.Domain.CommandQueries.Queries" /> <Using Include="NetinaShop.Domain.CommandQueries.Queries" />
<Using Include="NetinaShop.Domain.Dtos.DashboardDtos" />
<Using Include="NetinaShop.Domain.Dtos.RequestDtos" /> <Using Include="NetinaShop.Domain.Dtos.RequestDtos" />
<Using Include="NetinaShop.Domain.Dtos.ResponseDtos" /> <Using Include="NetinaShop.Domain.Dtos.ResponseDtos" />
<Using Include="NetinaShop.Domain.Dtos.SmallDtos" /> <Using Include="NetinaShop.Domain.Dtos.SmallDtos" />

View File

@ -0,0 +1,11 @@
namespace NetinaShop.Domain.Dtos.DashboardDtos;
public class HomeDashboardDto
{
public int ProductsCount { get; set; }
public int BlogsCount { get; set; }
public int TodayOrdersCount { get; set; }
public int UnSubmittedOrdersCount { get; set; }
public int BrandsCount { get; set; }
public int SubscribersCount { get; set; }
}

View File

@ -0,0 +1,14 @@
namespace NetinaShop.Domain.Dtos.ResponseDtos;
public class AdminChangeLogResponseDto
{
public string Version { get; set; } = string.Empty;
public double VersionNumber { get; set; }
public string VersionName { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public List<string> Features { get; set; } = new();
public List<string> BugFixes { get; set; } = new();
public bool IsNewVersion { get; set; } = false;
}

View File

@ -10,6 +10,8 @@ public class ApplicationUser : IdentityUser<Guid>
public string LastName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty;
public string NationalId { get; set; } = string.Empty; public string NationalId { get; set; } = string.Empty;
public double LatestVersionUsed { get; set; }
public DateTime BirthDate { get; set; } public DateTime BirthDate { get; set; }
public Gender Gender { get; set; } public Gender Gender { get; set; }
public SignUpStatus SignUpStatus { get; set; } public SignUpStatus SignUpStatus { get; set; }

View File

@ -10,6 +10,7 @@ public class SiteSettings
{ {
public JwtSettings JwtSettings { get; set; } = new JwtSettings(); public JwtSettings JwtSettings { get; set; } = new JwtSettings();
public string BaseUrl { get; set; } = string.Empty; public string BaseUrl { get; set; } = string.Empty;
public string AdminPanelBaseUrl { get; set; } = string.Empty;
public RedisSettings MasterRedisConfiguration { get; set; } = new RedisSettings(); public RedisSettings MasterRedisConfiguration { get; set; } = new RedisSettings();
public UserSetting UserSetting { get; set; } = new UserSetting(); public UserSetting UserSetting { get; set; } = new UserSetting();
public string KaveNegarApiKey { get; set; } = string.Empty; public string KaveNegarApiKey { get; set; } = string.Empty;

View File

@ -37,6 +37,7 @@
<ItemGroup> <ItemGroup>
<Folder Include="Dtos\FilterDtos\" /> <Folder Include="Dtos\FilterDtos\" />
<Folder Include="Dtos\DashboardDtos\" />
<Folder Include="Entities\StorageFiles\" /> <Folder Include="Entities\StorageFiles\" />
<Folder Include="Models\Settings\" /> <Folder Include="Models\Settings\" />
</ItemGroup> </ItemGroup>

View File

@ -5,4 +5,5 @@ public static class RestAddress
public static string BaseKaveNegar => "https://api.kavenegar.com/v1/"; public static string BaseKaveNegar => "https://api.kavenegar.com/v1/";
public static string BaseZarinpal => "https://api.zarinpal.com/pg"; public static string BaseZarinpal => "https://api.zarinpal.com/pg";
public static string DigikalaApi => "https://api.digikala.com"; public static string DigikalaApi => "https://api.digikala.com";
public static string AdminPanel => "https://admin.vesmook.com";
} }

View File

@ -0,0 +1,9 @@
using NetinaShop.Domain.Dtos.ResponseDtos;
namespace NetinaShop.Infrastructure.RestServices;
public interface IFileRestApi
{
[Get("/version.json")]
Task<AdminChangeLogResponseDto> GetAdminChangeLog();
}

View File

@ -7,6 +7,7 @@ public interface IRestApiWrapper : IScopedDependency
IKaveNegarRestApi KaveNegarRestApi { get; } IKaveNegarRestApi KaveNegarRestApi { get; }
IZarinpalRestApi ZarinpalRestApi { get; } IZarinpalRestApi ZarinpalRestApi { get; }
IDigikalaRestApi DigikalaRestApi { get; } IDigikalaRestApi DigikalaRestApi { get; }
IFileRestApi FileRestApi(string address);
} }
public class RestApiWrapper : IRestApiWrapper public class RestApiWrapper : IRestApiWrapper
@ -14,4 +15,10 @@ public class RestApiWrapper : IRestApiWrapper
public IKaveNegarRestApi KaveNegarRestApi => RestService.For<IKaveNegarRestApi>(RestAddress.BaseKaveNegar); public IKaveNegarRestApi KaveNegarRestApi => RestService.For<IKaveNegarRestApi>(RestAddress.BaseKaveNegar);
public IZarinpalRestApi ZarinpalRestApi => RestService.For<IZarinpalRestApi>(RestAddress.BaseZarinpal); public IZarinpalRestApi ZarinpalRestApi => RestService.For<IZarinpalRestApi>(RestAddress.BaseZarinpal);
public IDigikalaRestApi DigikalaRestApi => RestService.For<IDigikalaRestApi>(RestAddress.DigikalaApi); public IDigikalaRestApi DigikalaRestApi => RestService.For<IDigikalaRestApi>(RestAddress.DigikalaApi);
public IFileRestApi FileRestApi(string address)
{
return RestService.For<IFileRestApi>(address);
}
} }

View File

@ -0,0 +1,19 @@
using NetinaShop.Domain.Dtos.ResponseDtos;
namespace NetinaShop.Infrastructure.Services;
public class ExternalFilesService : IExternalFilesService
{
private readonly IRestApiWrapper _restApiWrapper;
private readonly SiteSettings _siteSetting;
public ExternalFilesService(IRestApiWrapper restApiWrapper,IOptionsSnapshot<SiteSettings> snapshot)
{
_restApiWrapper = restApiWrapper;
_siteSetting = snapshot.Value;
}
public Task<AdminChangeLogResponseDto> GetAdminChangeLogAsync(CancellationToken cancellationToken = default)
{
return _restApiWrapper.FileRestApi(_siteSetting.AdminPanelBaseUrl).GetAdminChangeLog();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,31 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace NetinaShop.Repository.Migrations
{
/// <inheritdoc />
public partial class AddUserVersionUsed : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<double>(
name: "LatestVersionUsed",
schema: "public",
table: "Users",
type: "double precision",
nullable: false,
defaultValue: 0.0);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "LatestVersionUsed",
schema: "public",
table: "Users");
}
}
}

View File

@ -1064,6 +1064,9 @@ namespace NetinaShop.Repository.Migrations
.IsRequired() .IsRequired()
.HasColumnType("text"); .HasColumnType("text");
b.Property<double>("LatestVersionUsed")
.HasColumnType("double precision");
b.Property<bool>("LockoutEnabled") b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean"); .HasColumnType("boolean");