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 }; } }