using Microsoft.Extensions.Logging; using QYZH.InteractiveMagazine.Infrastructure.OSS; using QYZH.InteractiveMagazine.IService; using QYZH.InteractiveMagazine.Models.Common; using QYZH.InteractiveMagazine.Models.Dto; using QYZH.InteractiveMagazine.Models.Dto.Banner; using QYZH.InteractiveMagazine.Models.Entity; using QYZH.InteractiveMagazine.Models.Enum; using QYZH.InteractiveMagazine.Repository; using SqlSugar; namespace QYZH.InteractiveMagazine.Service; /// /// 轮播图服务实现 /// public class BannerService( BaseRepository bannerRepository, OssService ossService, ILogger logger) : BaseRepository, IBannerService { /// /// 创建轮播图 /// public async Task CreateAsync(BannerInput input) { ValidateInput(input); var banner = new Banner { Title = input.Title.Trim(), ImageUrl = input.ImageUrl.Trim(), Position = input.Position, TargetType = input.TargetType, TargetValue = NormalizeTargetValue(input), Sort = input.Sort, StartTime = input.StartTime, EndTime = input.EndTime, Remark = input.Remark, Status = (int)DefaultStatusEnum.Active, IsDeleted = false, CreatedBy = "System", CreatedAt = DateTime.Now, UpdatedBy = "System", UpdatedAt = DateTime.Now }; var result = await bannerRepository.InsertAsync(banner); BusinessException.ThrowIf(!result, "创建轮播图失败", ResultCode.GLOBAL_ERROR); if (OssImageHelper.IsTempImage(banner.ImageUrl)) { var helper = CreateImageHelper(); banner.ImageUrl = await helper.MoveToFormalAsync(banner.ImageUrl, GetBannerFormalFolder(banner.Position, banner.Id)); await bannerRepository.UpdateAsync(banner); } logger.LogInformation("轮播图创建成功:{Id}", banner.Id); return MapToOutput(banner); } /// /// 更新轮播图 /// public async Task UpdateAsync(long id, BannerInput input) { ValidateInput(input); var banner = await GetValidBannerAsync(id); var newImageUrl = input.ImageUrl.Trim(); if (OssImageHelper.IsTempImage(newImageUrl)) { var helper = CreateImageHelper(); newImageUrl = await helper.MoveToFormalAsync(newImageUrl, GetBannerFormalFolder(input.Position, banner.Id)); } banner.Title = input.Title.Trim(); banner.ImageUrl = newImageUrl; banner.Position = input.Position; banner.TargetType = input.TargetType; banner.TargetValue = NormalizeTargetValue(input); banner.Sort = input.Sort; banner.StartTime = input.StartTime; banner.EndTime = input.EndTime; banner.Remark = input.Remark; banner.UpdatedBy = "System"; banner.UpdatedAt = DateTime.Now; var result = await bannerRepository.UpdateAsync(banner); BusinessException.ThrowIf(!result, "更新轮播图失败", ResultCode.GLOBAL_ERROR); logger.LogInformation("轮播图更新成功:{Id}", banner.Id); return MapToOutput(banner); } /// /// 删除轮播图 /// public async Task DeleteAsync(long id) { await GetValidBannerAsync(id); var result = await bannerRepository.Context.Updateable() .SetColumns(b => new Banner { IsDeleted = true, UpdatedBy = "System", UpdatedAt = DateTime.Now }) .Where(b => b.Id == id && !b.IsDeleted) .ExecuteCommandAsync(); BusinessException.ThrowIf(result <= 0, "删除轮播图失败", ResultCode.GLOBAL_ERROR); logger.LogInformation("轮播图删除成功:{Id}", id); } /// /// 根据ID获取轮播图 /// public async Task GetByIdAsync(long id) { var banner = await GetValidBannerAsync(id); return MapToOutput(banner); } /// /// 分页查询轮播图列表 /// public async Task> GetListAsync(BannerQueryInput input) { BusinessException.ThrowIf(input.PageIndex <= 0, "页码必须大于0", ResultCode.BAD_REQUEST); BusinessException.ThrowIf(input.PageSize <= 0 || input.PageSize > 100, "每页条数必须在1-100之间", ResultCode.BAD_REQUEST); RefAsync totalNumber = 0; var banners = await bannerRepository.Queryable() .Where(b => !b.IsDeleted) .WhereIF(!string.IsNullOrWhiteSpace(input.Title), b => b.Title.Contains(input.Title!.Trim())) .WhereIF(input.Position.HasValue, b => b.Position == input.Position!.Value) .WhereIF(input.Status.HasValue, b => b.Status == input.Status!.Value) .OrderBy(b => b.Sort) .OrderByDescending(b => b.CreatedAt) .ToPageListAsync(input.PageIndex, input.PageSize, totalNumber); var result = banners.Select(MapToOutput).ToList(); return new PageListModel(result, input.PageIndex, input.PageSize, totalNumber); } /// /// 更新轮播图启用/禁用状态 /// public async Task UpdateStatusAsync(long id) { var banner = await GetValidBannerAsync(id); banner.Status = banner.Status == (int)DefaultStatusEnum.Active ? (int)DefaultStatusEnum.Inactive : (int)DefaultStatusEnum.Active; banner.UpdatedBy = "System"; banner.UpdatedAt = DateTime.Now; var result = await bannerRepository.UpdateAsync(banner); BusinessException.ThrowIf(!result, "更新轮播图状态失败", ResultCode.GLOBAL_ERROR); logger.LogInformation("轮播图状态更新成功:{Id},状态:{Status}", id, banner.Status); return result; } /// /// 获取启用轮播图列表 /// public async Task> GetEnabledListAsync(BannerPositionEnum position) { var now = DateTime.Now; var banners = await bannerRepository.Queryable() .Where(b => !b.IsDeleted) .Where(b => b.Status == (int)DefaultStatusEnum.Active) .Where(b => b.Position == position) .Where(b => b.StartTime == null || b.StartTime <= now) .Where(b => b.EndTime == null || b.EndTime >= now) .OrderBy(b => b.Sort) .OrderByDescending(b => b.CreatedAt) .ToListAsync(); return banners.Select(MapToOutput).ToList(); } private async Task GetValidBannerAsync(long id) { var banner = await bannerRepository.Queryable() .Where(b => b.Id == id && !b.IsDeleted) .FirstAsync(); BusinessException.ThrowIf(banner == null, "轮播图不存在", ResultCode.NOT_FOUND); return banner; } private static void ValidateInput(BannerInput input) { BusinessException.ThrowIf(string.IsNullOrWhiteSpace(input.Title), "标题不能为空", ResultCode.BAD_REQUEST); BusinessException.ThrowIf(input.Title.Trim().Length > 100, "标题长度不能超过100个字符", ResultCode.BAD_REQUEST); BusinessException.ThrowIf(string.IsNullOrWhiteSpace(input.ImageUrl), "轮播图不能为空", ResultCode.BAD_REQUEST); BusinessException.ThrowIf(input.ImageUrl.Trim().Length > 500, "轮播图路径长度不能超过500个字符", ResultCode.BAD_REQUEST); BusinessException.ThrowIf(input.Sort < 0, "排序值不能小于0", ResultCode.BAD_REQUEST); BusinessException.ThrowIf(input.StartTime.HasValue && input.EndTime.HasValue && input.EndTime <= input.StartTime, "结束时间必须大于开始时间", ResultCode.BAD_REQUEST); BusinessException.ThrowIf(input.TargetType != BannerTargetTypeEnum.None && string.IsNullOrWhiteSpace(input.TargetValue), "跳转值不能为空", ResultCode.BAD_REQUEST); BusinessException.ThrowIf(!string.IsNullOrWhiteSpace(input.TargetValue) && input.TargetValue.Length > 500, "跳转值长度不能超过500个字符", ResultCode.BAD_REQUEST); BusinessException.ThrowIf(!string.IsNullOrWhiteSpace(input.Remark) && input.Remark.Length > 500, "备注长度不能超过500个字符", ResultCode.BAD_REQUEST); } private static string? NormalizeTargetValue(BannerInput input) { return input.TargetType == BannerTargetTypeEnum.None ? null : input.TargetValue?.Trim(); } private OssImageHelper CreateImageHelper() => new(ossService, logger); private static string GetBannerFormalFolder(BannerPositionEnum position, long bannerId) { return $"banner/{position.ToString().ToLowerInvariant()}/{bannerId}"; } private static BannerOutput MapToOutput(Banner banner) { return new BannerOutput { Id = banner.Id, Title = banner.Title, ImageUrl = banner.ImageUrl, Position = banner.Position, TargetType = banner.TargetType, TargetValue = banner.TargetValue, Sort = banner.Sort, StartTime = banner.StartTime, EndTime = banner.EndTime, Remark = banner.Remark, Status = banner.Status, CreatedBy = banner.CreatedBy, CreatedAt = banner.CreatedAt, UpdatedBy = banner.UpdatedBy, UpdatedAt = banner.UpdatedAt }; } }