using FoodLabeling.Application.Helpers;
using FoodLabeling.Application.Contracts.Dtos.Common;
using FoodLabeling.Application.Contracts.Dtos.Group;
using FoodLabeling.Application.Contracts.IServices;
using FoodLabeling.Application.Services.DbModels;
using Microsoft.AspNetCore.Mvc;
using QuestPDF.Fluent;
using QuestPDF.Helpers;
using QuestPDF.Infrastructure;
using SqlSugar;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Guids;
using Volo.Abp.Uow;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace FoodLabeling.Application.Services;
///
/// 组织(Group)管理(fl_group)
///
public class GroupAppService : ApplicationService, IGroupAppService
{
private const int ExportPdfMaxRows = 5000;
private readonly ISqlSugarDbContext _dbContext;
private readonly IGuidGenerator _guidGenerator;
public GroupAppService(ISqlSugarDbContext dbContext, IGuidGenerator guidGenerator)
{
_dbContext = dbContext;
_guidGenerator = guidGenerator;
}
///
public async Task> GetListAsync(GroupGetListInputVo input)
{
RefAsync total = 0;
var query = await BuildGroupJoinedQueryAsync(input);
var projected = query.Select((g, p) => new GroupGetListOutputDto
{
Id = g.Id,
GroupName = g.GroupName,
PartnerId = g.PartnerId,
PartnerName = string.IsNullOrWhiteSpace(p.PartnerName) ? "None" : p.PartnerName.Trim(),
State = g.State,
CreationTime = g.CreationTime
});
var items = await projected.ToPageListAsync(input.SkipCount, input.MaxResultCount, total);
return BuildPagedResult(input.SkipCount, input.MaxResultCount, total, items);
}
///
public async Task GetAsync(Guid id)
{
if (id == Guid.Empty)
{
throw new UserFriendlyException("Group id is required.");
}
var groupId = id.ToString();
var entity = await _dbContext.SqlSugarClient.Queryable()
.FirstAsync(x => !x.IsDeleted && x.Id == groupId);
if (entity is null)
{
throw new UserFriendlyException("Group not found.");
}
var partnerName = await ResolvePartnerNameAsync(entity.PartnerId);
return MapDetail(entity, partnerName);
}
///
[UnitOfWork]
public async Task CreateAsync(GroupCreateInputVo input)
{
var name = input.GroupName?.Trim();
if (string.IsNullOrWhiteSpace(name))
{
throw new UserFriendlyException("Region name is required.");
}
var partnerId = input.PartnerId?.Trim();
if (string.IsNullOrWhiteSpace(partnerId))
{
throw new UserFriendlyException("Parent company is required.");
}
await EnsurePartnerExistsAsync(partnerId);
var now = Clock.Now;
var entity = new FlGroupDbEntity
{
Id = _guidGenerator.Create().ToString(),
IsDeleted = false,
GroupName = name,
PartnerId = partnerId,
State = input.State,
CreationTime = now,
CreatorId = CurrentUser?.Id?.ToString(),
LastModificationTime = now,
LastModifierId = CurrentUser?.Id?.ToString()
};
await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync();
return await GetAsync(Guid.Parse(entity.Id));
}
///
[UnitOfWork]
public async Task UpdateAsync(Guid id, GroupUpdateInputVo input)
{
if (id == Guid.Empty)
{
throw new UserFriendlyException("Group id is required.");
}
var groupId = id.ToString();
var entity = await _dbContext.SqlSugarClient.Queryable()
.FirstAsync(x => !x.IsDeleted && x.Id == groupId);
if (entity is null)
{
throw new UserFriendlyException("Group not found.");
}
var name = input.GroupName?.Trim();
if (string.IsNullOrWhiteSpace(name))
{
throw new UserFriendlyException("Region name is required.");
}
var partnerId = input.PartnerId?.Trim();
if (string.IsNullOrWhiteSpace(partnerId))
{
throw new UserFriendlyException("Parent company is required.");
}
await EnsurePartnerExistsAsync(partnerId);
entity.GroupName = name;
entity.PartnerId = partnerId;
entity.State = input.State;
entity.LastModificationTime = Clock.Now;
entity.LastModifierId = CurrentUser?.Id?.ToString();
await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync();
return await GetAsync(id);
}
///
[UnitOfWork]
public async Task DeleteAsync(Guid id)
{
if (id == Guid.Empty)
{
throw new UserFriendlyException("Group id is required.");
}
var groupId = id.ToString();
var entity = await _dbContext.SqlSugarClient.Queryable()
.FirstAsync(x => !x.IsDeleted && x.Id == groupId);
if (entity is null)
{
throw new UserFriendlyException("Group not found.");
}
entity.IsDeleted = true;
entity.LastModificationTime = Clock.Now;
entity.LastModifierId = CurrentUser?.Id?.ToString();
await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync();
}
///
[HttpGet]
public async Task ExportPdfAsync([FromQuery] GroupGetListInputVo input)
{
QuestPDF.Settings.License = LicenseType.Community;
var exportBase = await BuildGroupJoinedQueryAsync(input);
var count = await exportBase.CountAsync();
if (count > ExportPdfMaxRows)
{
throw new UserFriendlyException(
$"Export exceeds the maximum of {ExportPdfMaxRows} rows. Narrow your filters and try again.");
}
var rows = await (await BuildGroupJoinedQueryAsync(input))
.Select((g, p) => new GroupGetListOutputDto
{
Id = g.Id,
GroupName = g.GroupName,
PartnerId = g.PartnerId,
PartnerName = string.IsNullOrWhiteSpace(p.PartnerName) ? "None" : p.PartnerName.Trim(),
State = g.State,
CreationTime = g.CreationTime
})
.Take(ExportPdfMaxRows)
.ToListAsync();
var fileName = $"regions_{Clock.Now:yyyy-MM-dd_HH-mm-ss}.pdf";
var document = Document.Create(container =>
{
container.Page(page =>
{
page.Margin(28);
page.DefaultTextStyle(x => x.FontSize(10));
page.Header().Text("Regions").SemiBold().FontSize(18);
page.Content().PaddingTop(12).Table(table =>
{
table.ColumnsDefinition(c =>
{
c.RelativeColumn(2.2f);
c.RelativeColumn(2.4f);
c.RelativeColumn(1f);
c.RelativeColumn(1.6f);
});
static IContainer CellHeader(IContainer c) =>
c.Background(Colors.Grey.Lighten3).Padding(6).DefaultTextStyle(x => x.SemiBold());
table.Cell().Element(CellHeader).Text("Region Name");
table.Cell().Element(CellHeader).Text("Parent company");
table.Cell().Element(CellHeader).Text("Status");
table.Cell().Element(CellHeader).Text("Created");
foreach (var e in rows)
{
var status = e.State ? "active" : "inactive";
table.Cell().BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(5)
.Text(e.GroupName ?? string.Empty);
table.Cell().BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(5)
.Text(string.IsNullOrWhiteSpace(e.PartnerName) ? "None" : e.PartnerName);
table.Cell().BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(5).Text(status);
table.Cell().BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(5)
.Text(e.CreationTime.ToString("yyyy-MM-dd HH:mm"));
}
});
});
});
var stream = new MemoryStream();
document.GeneratePdf(stream);
stream.Position = 0;
return new FileStreamResult(stream, "application/pdf") { FileDownloadName = fileName };
}
private async Task> BuildGroupJoinedQueryAsync(
GroupGetListInputVo input)
{
var scope = await PartnerScopeHelper.ResolveGroupScopeAsync(CurrentUser, _dbContext);
var keyword = input.Keyword?.Trim();
var partnerId = input.PartnerId?.Trim();
var query = _dbContext.SqlSugarClient.Queryable()
.LeftJoin((g, p) => g.PartnerId == p.Id && !p.IsDeleted)
.Where((g, p) => !g.IsDeleted);
query = PartnerScopeHelper.ApplyGroupScope(query, scope);
query = query
.WhereIF(input.State != null, (g, p) => g.State == input.State)
.WhereIF(!string.IsNullOrWhiteSpace(partnerId), (g, p) => g.PartnerId == partnerId)
.WhereIF(!string.IsNullOrWhiteSpace(keyword),
(g, p) => g.GroupName.Contains(keyword!) ||
(p.PartnerName != null && p.PartnerName.Contains(keyword!)));
if (!string.IsNullOrWhiteSpace(input.Sorting))
{
var sorting = input.Sorting.Trim();
if (sorting.Equals("GroupName desc", StringComparison.OrdinalIgnoreCase))
{
query = query.OrderByDescending((g, p) => g.GroupName);
}
else if (sorting.Equals("GroupName asc", StringComparison.OrdinalIgnoreCase))
{
query = query.OrderBy((g, p) => g.GroupName);
}
else if (sorting.Equals("CreationTime desc", StringComparison.OrdinalIgnoreCase))
{
query = query.OrderByDescending((g, p) => g.CreationTime);
}
else if (sorting.Equals("CreationTime asc", StringComparison.OrdinalIgnoreCase))
{
query = query.OrderBy((g, p) => g.CreationTime);
}
else if (sorting.Equals("State desc", StringComparison.OrdinalIgnoreCase))
{
query = query.OrderByDescending((g, p) => g.State);
}
else if (sorting.Equals("State asc", StringComparison.OrdinalIgnoreCase))
{
query = query.OrderBy((g, p) => g.State);
}
else if (sorting.Equals("PartnerName desc", StringComparison.OrdinalIgnoreCase))
{
query = query.OrderByDescending((g, p) => p.PartnerName);
}
else if (sorting.Equals("PartnerName asc", StringComparison.OrdinalIgnoreCase))
{
query = query.OrderBy((g, p) => p.PartnerName);
}
else
{
query = query.OrderByDescending((g, p) => g.CreationTime);
}
}
else
{
query = query.OrderByDescending((g, p) => g.CreationTime);
}
return query;
}
private async Task EnsurePartnerExistsAsync(string partnerId)
{
var ok = await _dbContext.SqlSugarClient.Queryable()
.AnyAsync(x => !x.IsDeleted && x.Id == partnerId);
if (!ok)
{
throw new UserFriendlyException("The selected company does not exist or has been removed.");
}
}
private async Task ResolvePartnerNameAsync(string partnerId)
{
var p = await _dbContext.SqlSugarClient.Queryable()
.FirstAsync(x => !x.IsDeleted && x.Id == partnerId);
if (p is null || string.IsNullOrWhiteSpace(p.PartnerName))
{
return "None";
}
return p.PartnerName.Trim();
}
private static GroupGetOutputDto MapDetail(FlGroupDbEntity x, string partnerName) => new()
{
Id = x.Id,
GroupName = x.GroupName,
PartnerId = x.PartnerId,
PartnerName = partnerName,
State = x.State,
CreationTime = x.CreationTime,
LastModificationTime = x.LastModificationTime
};
private static PagedResultWithPageDto BuildPagedResult(int skipCount, int maxResultCount, int total,
List items)
{
var pageSize = maxResultCount <= 0 ? items.Count : maxResultCount;
var pageIndex = pageSize <= 0 ? 1 : PagedQueryConvention.PageIndexFromSkipCount(skipCount);
var totalPages = pageSize <= 0 ? 0 : (int)Math.Ceiling(total / (double)pageSize);
return new PagedResultWithPageDto
{
PageIndex = pageIndex,
PageSize = pageSize,
TotalCount = total,
TotalPages = totalPages,
Items = items
};
}
}