using Microsoft.Extensions.Logging; using QYZH.InteractiveMagazine.Common.Helpers; using QYZH.InteractiveMagazine.Infrastructure.OSS; using QYZH.InteractiveMagazine.IService; using QYZH.InteractiveMagazine.Models.Common; using QYZH.InteractiveMagazine.Models.Dto; using QYZH.InteractiveMagazine.Models.Entity; using QYZH.InteractiveMagazine.Models.Enum; using QYZH.InteractiveMagazine.Repository; using SqlSugar; namespace QYZH.InteractiveMagazine.Service; /// /// 商品服务实现 /// public class ProductService( BaseRepository productRepository, OssService ossService, ILogger logger) : BaseRepository, IProductService { /// /// 创建商品 /// public async Task CreateAsync(ProductInput input) { logger.LogInformation("正在创建商品,商品名称: {Name}", input.Name); if (string.IsNullOrWhiteSpace(input.Name)) { throw new BusinessException("商品名称不能为空", ResultCode.BAD_REQUEST); } if (string.IsNullOrWhiteSpace(input.Type)) { throw new BusinessException("商品类型不能为空", ResultCode.BAD_REQUEST); } if (input.Price < 0) { throw new BusinessException("商品价格不能为负数", ResultCode.BAD_REQUEST); } var product = new Product { Name = input.Name.Trim(), Description = input.Description, ImageUrl = input.ImageUrl, Price = input.Price, Type = Enum.Parse(input.Type, true), Status = (int)input.Status, MetaData = input.MetaData, IsActive = input.IsActive, Stock = input.Stock, IsVirtual = input.IsVirtual, TypeId = input.TypeId, CreatedBy = "System", UpdatedBy = "System", CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now, IsDeleted = false }; var result = await productRepository.InsertAsync(product); if (!result) { logger.LogError("商品创建失败,商品名称: {Name}", input.Name); throw new BusinessException("创建商品失败", ResultCode.GLOBAL_ERROR); } // 将 temp 目录下的图片搬运到正式目录 if (OssImageHelper.IsTempImage(product.ImageUrl)) { var helper = CreateImageHelper(); var folder = GetProductFormalFolder(product.Type, product.Id); var formalPath = await helper.MoveToFormalAsync(product.ImageUrl, folder); product.ImageUrl = formalPath; await productRepository.UpdateAsync(product); } logger.LogInformation("商品创建成功,商品名称: {Name}, ID: {Id}", input.Name, product.Id); return new ProductOutput { Id = product.Id, Name = product.Name, Description = product.Description, ImageUrl = product.ImageUrl, Price = product.Price, Type = product.Type.ToString(), Status = product.Status.ToString(), MetaData = product.MetaData, IsActive = product.IsActive, Stock = product.Stock, IsVirtual = product.IsVirtual, TypeId = product.TypeId, CreatedBy = product.CreatedBy, CreatedAt = product.CreatedAt, UpdatedBy = product.UpdatedBy, UpdatedAt = product.UpdatedAt }; } /// /// 更新商品 /// public async Task UpdateAsync(long id, ProductInput input) { logger.LogInformation("正在更新商品,ID: {Id}", id); var product = await productRepository.GetByIdAsync(id); if (product == null) { logger.LogWarning("未找到要更新的商品,ID: {Id}", id); throw new BusinessException("商品不存在", ResultCode.NOT_FOUND); } if (string.IsNullOrWhiteSpace(input.Name)) { throw new BusinessException("商品名称不能为空", ResultCode.BAD_REQUEST); } if (string.IsNullOrWhiteSpace(input.Type)) { throw new BusinessException("商品类型不能为空", ResultCode.BAD_REQUEST); } if (input.Price < 0) { throw new BusinessException("商品价格不能为负数", ResultCode.BAD_REQUEST); } // 将 temp 目录下的新图片搬运到正式目录 var newImageUrl = input.ImageUrl; if (OssImageHelper.IsTempImage(newImageUrl)) { var helper = CreateImageHelper(); var folder = GetProductFormalFolder(Enum.Parse(input.Type, true), product.Id); newImageUrl = await helper.MoveToFormalAsync(newImageUrl, folder); } product.Name = input.Name.Trim(); product.Description = input.Description; product.ImageUrl = newImageUrl; product.Price = input.Price; product.Type = Enum.Parse(input.Type, true); product.Status = (int)input.Status; product.MetaData = input.MetaData; product.IsActive = input.IsActive; product.Stock = input.Stock; product.IsVirtual = input.IsVirtual; product.TypeId = input.TypeId; product.UpdatedBy = "System"; product.UpdatedAt = DateTime.Now; var result = await productRepository.UpdateAsync(product); if (!result) { logger.LogError("商品更新失败,ID: {Id}", id); throw new BusinessException("更新商品失败", ResultCode.GLOBAL_ERROR); } logger.LogInformation("商品更新成功,ID: {Id}", id); return new ProductOutput { Id = product.Id, Name = product.Name, Description = product.Description, ImageUrl = product.ImageUrl, Price = product.Price, Type = product.Type.ToString(), Status = product.Status.ToString(), MetaData = product.MetaData, IsActive = product.IsActive, Stock = product.Stock, IsVirtual = product.IsVirtual, TypeId = product.TypeId, CreatedBy = product.CreatedBy, CreatedAt = product.CreatedAt, UpdatedBy = product.UpdatedBy, UpdatedAt = product.UpdatedAt }; } /// /// 删除商品(软删除) /// public async Task DeleteAsync(long id) { logger.LogInformation("正在删除商品,ID: {Id}", id); var product = await productRepository.GetByIdAsync(id); if (product == null) { logger.LogWarning("未找到要删除的商品,ID: {Id}", id); throw new BusinessException("商品不存在", ResultCode.NOT_FOUND); } var result = await productRepository.DeleteByIdAsync(id); if (!result) { logger.LogError("商品删除失败,ID: {Id}", id); throw new BusinessException("删除商品失败", ResultCode.GLOBAL_ERROR); } logger.LogInformation("商品删除成功,ID: {Id}", id); } /// /// 根据ID获取商品 /// public async Task GetByIdAsync(long id) { logger.LogInformation("正在获取商品信息,ID: {Id}", id); var product = await productRepository.GetByIdAsync(id); if (product == null) { logger.LogWarning("未找到商品,ID: {Id}", id); throw new BusinessException("商品不存在", ResultCode.NOT_FOUND); } return new ProductOutput { Id = product.Id, Name = product.Name, Description = product.Description, ImageUrl = DomainHelper.OssFullUrl(product.ImageUrl), Price = product.Price, Type = product.Type.ToString(), Status = product.Status.ToString(), MetaData = product.MetaData, IsActive = product.IsActive, Stock = product.Stock, IsVirtual = product.IsVirtual, TypeId = product.TypeId, CreatedBy = product.CreatedBy, CreatedAt = product.CreatedAt, UpdatedBy = product.UpdatedBy, UpdatedAt = product.UpdatedAt }; } /// /// 分页查询商品列表 /// public async Task> GetListAsync(ProductQueryInput input) { logger.LogInformation("正在查询商品列表,页码: {PageIndex}, 每页条数: {PageSize}", input.PageIndex, input.PageSize); if (input.PageIndex <= 0) { throw new BusinessException("页码必须大于0", ResultCode.BAD_REQUEST); } if (input.PageSize <= 0 || input.PageSize > 100) { throw new BusinessException("每页条数必须在1-100之间", ResultCode.BAD_REQUEST); } RefAsync totalNumber = 0; var pageResult = await productRepository.Queryable() .WhereIF(!string.IsNullOrWhiteSpace(input.Name), p => p.Name.Contains(input.Name)) .WhereIF(!string.IsNullOrWhiteSpace(input.Type), p => p.Type.ToString() == input.Type) .WhereIF(input.Status.HasValue, p => p.Status == (int)input.Status) .WhereIF(input.IsActive.HasValue, p => p.IsActive == input.IsActive.Value) .OrderByDescending(p => p.CreatedAt) .ToPageListAsync(input.PageIndex, input.PageSize, totalNumber); var result = pageResult.Select(p => new ProductOutput { Id = p.Id, Name = p.Name, Description = p.Description, ImageUrl = DomainHelper.OssFullUrl(p.ImageUrl), Price = p.Price, Type = p.Type.ToString(), Status = p.Status.ToString(), MetaData = p.MetaData, IsActive = p.IsActive, Stock = p.Stock, IsVirtual = p.IsVirtual, TypeId = p.TypeId, CreatedBy = p.CreatedBy, CreatedAt = p.CreatedAt, UpdatedBy = p.UpdatedBy, UpdatedAt = p.UpdatedAt }).ToList(); return new PageListModel(result, input.PageIndex, input.PageSize, totalNumber); } /// /// 更新商品上架/下架状态(取反) /// public async Task UpdateSaleStatusAsync(long id) { logger.LogInformation("正在更新商品上下架状态,ID: {Id}", id); var product = await productRepository.GetByIdAsync(id); if (product == null) { logger.LogWarning("未找到要更新状态的商品,ID: {Id}", id); throw new BusinessException("商品不存在", ResultCode.NOT_FOUND); } product.Status = product.Status == (int)ProductStatusEnum.OnSale ? (int)ProductStatusEnum.OffSale : (int)ProductStatusEnum.OnSale; product.UpdatedBy = "System"; product.UpdatedAt = DateTime.Now; var result = await productRepository.UpdateAsync(product); if (!result) { logger.LogError("商品状态更新失败,ID: {Id}", id); throw new BusinessException("更新商品状态失败", ResultCode.GLOBAL_ERROR); } logger.LogInformation("商品上下架状态更新成功,ID: {Id}, SaleStatus: {SaleStatus}", id, product.Status); } #region OSS 图片搬运 private OssImageHelper CreateImageHelper() => new(ossService, logger); private static string GetProductFormalFolder(ProductTypeEnum type, long productId) { var typeFolder = type switch { ProductTypeEnum.MakeUpCard => "makeupcard", ProductTypeEnum.PetSkin => "petskin", _ => "other" }; return $"product/{typeFolder}/{productId}"; } #endregion }