using FoodLabeling.Application.Helpers; using FoodLabeling.Application.Contracts.Dtos.Common; using FoodLabeling.Application.Contracts.Dtos.Partner; 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; /// /// 合作伙伴管理(fl_partner) /// public class PartnerAppService : ApplicationService, IPartnerAppService { private const int ExportPdfMaxRows = 5000; private readonly ISqlSugarDbContext _dbContext; private readonly IGuidGenerator _guidGenerator; public PartnerAppService(ISqlSugarDbContext dbContext, IGuidGenerator guidGenerator) { _dbContext = dbContext; _guidGenerator = guidGenerator; } /// public async Task> GetListAsync(PartnerGetListInputVo input) { RefAsync total = 0; var query = BuildPartnerListQuery(input); var entities = await query.ToPageListAsync(input.SkipCount, input.MaxResultCount, total); var items = entities.Select(MapListItem).ToList(); return BuildPagedResult(input.SkipCount, input.MaxResultCount, total, items); } /// public async Task GetAsync(string id) { var partnerId = id?.Trim(); if (string.IsNullOrWhiteSpace(partnerId)) { throw new UserFriendlyException("合作伙伴Id不能为空"); } var entity = await _dbContext.SqlSugarClient.Queryable() .FirstAsync(x => !x.IsDeleted && x.Id == partnerId); if (entity is null) { throw new UserFriendlyException("合作伙伴不存在"); } return MapDetail(entity); } /// [UnitOfWork] public async Task CreateAsync(PartnerCreateInputVo input) { var name = input.PartnerName?.Trim(); if (string.IsNullOrWhiteSpace(name)) { throw new UserFriendlyException("合作伙伴名称不能为空"); } var email = input.ContactEmail?.Trim(); if (!string.IsNullOrWhiteSpace(email) && !IsPlausibleEmail(email)) { throw new UserFriendlyException("联系邮箱格式不正确"); } var now = Clock.Now; var entity = new FlPartnerDbEntity { Id = _guidGenerator.Create().ToString(), IsDeleted = false, PartnerName = name, ContactEmail = string.IsNullOrWhiteSpace(email) ? null : email, PhoneNumber = string.IsNullOrWhiteSpace(input.PhoneNumber) ? null : input.PhoneNumber.Trim(), State = input.State, CreationTime = now, CreatorId = CurrentUser?.Id?.ToString(), LastModificationTime = now, LastModifierId = CurrentUser?.Id?.ToString() }; await _dbContext.SqlSugarClient.Insertable(entity).ExecuteCommandAsync(); return await GetAsync(entity.Id); } /// [UnitOfWork] public async Task UpdateAsync(string id, PartnerUpdateInputVo input) { var partnerId = id?.Trim(); if (string.IsNullOrWhiteSpace(partnerId)) { throw new UserFriendlyException("合作伙伴Id不能为空"); } var entity = await _dbContext.SqlSugarClient.Queryable() .FirstAsync(x => !x.IsDeleted && x.Id == partnerId); if (entity is null) { throw new UserFriendlyException("合作伙伴不存在"); } var name = input.PartnerName?.Trim(); if (string.IsNullOrWhiteSpace(name)) { throw new UserFriendlyException("合作伙伴名称不能为空"); } var email = input.ContactEmail?.Trim(); if (!string.IsNullOrWhiteSpace(email) && !IsPlausibleEmail(email)) { throw new UserFriendlyException("联系邮箱格式不正确"); } entity.PartnerName = name; entity.ContactEmail = string.IsNullOrWhiteSpace(email) ? null : email; entity.PhoneNumber = string.IsNullOrWhiteSpace(input.PhoneNumber) ? null : input.PhoneNumber.Trim(); entity.State = input.State; entity.LastModificationTime = Clock.Now; entity.LastModifierId = CurrentUser?.Id?.ToString(); await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync(); return await GetAsync(partnerId); } /// [UnitOfWork] public async Task DeleteAsync(string id) { var partnerId = id?.Trim(); if (string.IsNullOrWhiteSpace(partnerId)) { throw new UserFriendlyException("合作伙伴Id不能为空"); } var entity = await _dbContext.SqlSugarClient.Queryable() .FirstAsync(x => !x.IsDeleted && x.Id == partnerId); if (entity is null) { throw new UserFriendlyException("合作伙伴不存在"); } entity.IsDeleted = true; entity.LastModificationTime = Clock.Now; entity.LastModifierId = CurrentUser?.Id?.ToString(); await _dbContext.SqlSugarClient.Updateable(entity).ExecuteCommandAsync(); } /// public async Task ExportPdfAsync(PartnerGetListInputVo input) { QuestPDF.Settings.License = LicenseType.Community; var count = await BuildPartnerListQuery(input).CountAsync(); var query = BuildPartnerListQuery(input); if (count > ExportPdfMaxRows) { throw new UserFriendlyException($"导出数据超过上限 {ExportPdfMaxRows} 条,请缩小筛选范围"); } var rows = await query.Take(ExportPdfMaxRows).ToListAsync(); var fileName = $"companies_{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("Companies").SemiBold().FontSize(18); page.Content().PaddingTop(12).Table(table => { table.ColumnsDefinition(c => { c.RelativeColumn(2.2f); c.RelativeColumn(2.4f); c.RelativeColumn(1.8f); 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("Company"); table.Cell().Element(CellHeader).Text("Contact"); table.Cell().Element(CellHeader).Text("Phone"); 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.PartnerName ?? string.Empty); table.Cell().BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(5) .Text(string.IsNullOrWhiteSpace(e.ContactEmail) ? "无" : e.ContactEmail!); table.Cell().BorderBottom(0.5f).BorderColor(Colors.Grey.Lighten2).Padding(5) .Text(string.IsNullOrWhiteSpace(e.PhoneNumber) ? "无" : e.PhoneNumber!); 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 ISugarQueryable BuildPartnerListQuery(PartnerGetListInputVo input) { var keyword = input.Keyword?.Trim(); var query = _dbContext.SqlSugarClient.Queryable() .Where(x => !x.IsDeleted) .WhereIF(input.State != null, x => x.State == input.State) .WhereIF(!string.IsNullOrWhiteSpace(keyword), x => x.PartnerName.Contains(keyword!) || (x.ContactEmail != null && x.ContactEmail.Contains(keyword!)) || (x.PhoneNumber != null && x.PhoneNumber.Contains(keyword!))); if (!string.IsNullOrWhiteSpace(input.Sorting)) { var sorting = input.Sorting.Trim(); if (sorting.Equals("PartnerName desc", StringComparison.OrdinalIgnoreCase)) { query = query.OrderByDescending(x => x.PartnerName); } else if (sorting.Equals("PartnerName asc", StringComparison.OrdinalIgnoreCase)) { query = query.OrderBy(x => x.PartnerName); } else if (sorting.Equals("CreationTime desc", StringComparison.OrdinalIgnoreCase)) { query = query.OrderByDescending(x => x.CreationTime); } else if (sorting.Equals("CreationTime asc", StringComparison.OrdinalIgnoreCase)) { query = query.OrderBy(x => x.CreationTime); } else if (sorting.Equals("State desc", StringComparison.OrdinalIgnoreCase)) { query = query.OrderByDescending(x => x.State); } else if (sorting.Equals("State asc", StringComparison.OrdinalIgnoreCase)) { query = query.OrderBy(x => x.State); } else { query = query.OrderByDescending(x => x.CreationTime); } } else { query = query.OrderByDescending(x => x.CreationTime); } return query; } private static PartnerGetListOutputDto MapListItem(FlPartnerDbEntity x) => new() { Id = x.Id, PartnerName = x.PartnerName, ContactEmail = x.ContactEmail, PhoneNumber = x.PhoneNumber, State = x.State, CreationTime = x.CreationTime }; private static PartnerGetOutputDto MapDetail(FlPartnerDbEntity x) => new() { Id = x.Id, PartnerName = x.PartnerName, ContactEmail = x.ContactEmail, PhoneNumber = x.PhoneNumber, State = x.State, CreationTime = x.CreationTime, LastModificationTime = x.LastModificationTime }; private static bool IsPlausibleEmail(string email) { if (email.Length > 256) { return false; } var at = email.IndexOf('@'); return at > 0 && at < email.Length - 1 && email.IndexOf('@', at + 1) < 0; } 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 }; } }