using HoneyBox.Admin.Business.Models; using HoneyBox.Admin.Business.Models.Advert; using HoneyBox.Admin.Business.Services.Interfaces; using HoneyBox.Model.Data; using HoneyBox.Model.Entities; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace HoneyBox.Admin.Business.Services; /// /// 广告管理服务实现 /// public class AdvertService : IAdvertService { private readonly HoneyBoxDbContext _dbContext; private readonly ILogger _logger; public AdvertService( HoneyBoxDbContext dbContext, ILogger logger) { _dbContext = dbContext; _logger = logger; } #region 广告管理 /// public async Task> GetAdvertsAsync(AdvertListRequest request) { var query = _dbContext.Adverts.AsNoTracking(); // 应用类型过滤 if (request.TypeId.HasValue) { query = query.Where(a => a.Type == request.TypeId.Value); } // 获取总数 var total = await query.CountAsync(); // 获取列表 var adverts = await query .OrderBy(a => a.Sort) .ThenByDescending(a => a.Id) .Skip(request.Skip) .Take(request.PageSize) .ToListAsync(); // 获取类型名称映射 var typeIds = adverts.Select(a => (int)a.Type).Distinct().ToList(); var types = await _dbContext.AdvertTypes .AsNoTracking() .Where(t => typeIds.Contains(t.Id)) .ToDictionaryAsync(t => t.Id, t => t.Name); // 映射结果 var list = adverts.Select(a => MapToResponse(a, types)).ToList(); return PagedResult.Create(list, total, request.Page, request.PageSize); } /// public async Task GetAdvertByIdAsync(int id) { var advert = await _dbContext.Adverts .AsNoTracking() .FirstOrDefaultAsync(a => a.Id == id); if (advert == null) return null; // 获取类型名称 var typeName = await _dbContext.AdvertTypes .AsNoTracking() .Where(t => t.Id == advert.Type) .Select(t => t.Name) .FirstOrDefaultAsync() ?? "未知类型"; return MapToResponse(advert, new Dictionary { { advert.Type, typeName } }); } /// public async Task CreateAdvertAsync(AdvertCreateRequest request) { // 验证请求 await ValidateAdvertRequest(request); var now = DateTime.Now; var advert = new Advert { Type = (byte)request.TypeId, ImgUrl = request.ImageUrl, Sort = request.Sort, Ttype = (byte)request.JumpType, CouponId = request.CouponId ?? 0, GoodsId = request.GoodsId ?? 0, Url = request.UrlLink, CreatedAt = now, UpdatedAt = now }; _dbContext.Adverts.Add(advert); await _dbContext.SaveChangesAsync(); _logger.LogInformation("创建广告成功: Id={Id}, TypeId={TypeId}", advert.Id, request.TypeId); return advert.Id; } /// public async Task UpdateAdvertAsync(int id, AdvertUpdateRequest request) { var advert = await _dbContext.Adverts.FirstOrDefaultAsync(a => a.Id == id); if (advert == null) { throw new BusinessException(BusinessErrorCodes.NotFound, "广告不存在"); } // 验证请求 await ValidateAdvertRequest(request); advert.Type = (byte)request.TypeId; advert.ImgUrl = request.ImageUrl; advert.Sort = request.Sort; advert.Ttype = (byte)request.JumpType; advert.CouponId = request.CouponId ?? 0; advert.GoodsId = request.GoodsId ?? 0; advert.Url = request.UrlLink; advert.UpdatedAt = DateTime.Now; var result = await _dbContext.SaveChangesAsync() > 0; _logger.LogInformation("更新广告成功: Id={Id}", id); return result; } /// public async Task DeleteAdvertAsync(int id) { var advert = await _dbContext.Adverts.FirstOrDefaultAsync(a => a.Id == id); if (advert == null) { throw new BusinessException(BusinessErrorCodes.NotFound, "广告不存在"); } _dbContext.Adverts.Remove(advert); var result = await _dbContext.SaveChangesAsync() > 0; _logger.LogInformation("删除广告成功: Id={Id}", id); return result; } #endregion #region 广告类型管理 /// public async Task> GetAdvertTypesAsync() { // 获取所有类型 var types = await _dbContext.AdvertTypes .AsNoTracking() .OrderBy(t => t.Sort) .ThenBy(t => t.Id) .ToListAsync(); // 获取每个类型下的广告数量 var typeCounts = await _dbContext.Adverts .AsNoTracking() .GroupBy(a => a.Type) .Select(g => new { TypeId = g.Key, Count = g.Count() }) .ToDictionaryAsync(x => (int)x.TypeId, x => x.Count); return types.Select(t => new AdvertTypeResponse { Id = t.Id, Name = t.Name, Sort = t.Sort, AdvertCount = typeCounts.GetValueOrDefault(t.Id, 0) }).ToList(); } /// public async Task CreateAdvertTypeAsync(AdvertTypeCreateRequest request) { // 验证名称 if (string.IsNullOrWhiteSpace(request.Name)) { throw new BusinessException(BusinessErrorCodes.ValidationFailed, "类型名称不能为空"); } // 检查名称是否已存在 var exists = await _dbContext.AdvertTypes .AnyAsync(t => t.Name == request.Name); if (exists) { throw new BusinessException(BusinessErrorCodes.Conflict, "该类型名称已存在"); } var now = DateTime.Now; var advertType = new AdvertType { Name = request.Name, Sort = request.Sort, CreatedAt = now, UpdatedAt = now }; _dbContext.AdvertTypes.Add(advertType); await _dbContext.SaveChangesAsync(); _logger.LogInformation("创建广告类型成功: Id={Id}, Name={Name}", advertType.Id, advertType.Name); return advertType.Id; } /// public async Task UpdateAdvertTypeAsync(int id, AdvertTypeUpdateRequest request) { var advertType = await _dbContext.AdvertTypes.FirstOrDefaultAsync(t => t.Id == id); if (advertType == null) { throw new BusinessException(BusinessErrorCodes.NotFound, "广告类型不存在"); } // 验证名称 if (string.IsNullOrWhiteSpace(request.Name)) { throw new BusinessException(BusinessErrorCodes.ValidationFailed, "类型名称不能为空"); } // 检查名称是否被其他类型使用 var exists = await _dbContext.AdvertTypes .AnyAsync(t => t.Name == request.Name && t.Id != id); if (exists) { throw new BusinessException(BusinessErrorCodes.Conflict, "该类型名称已被其他类型使用"); } advertType.Name = request.Name; advertType.Sort = request.Sort; advertType.UpdatedAt = DateTime.Now; var result = await _dbContext.SaveChangesAsync() > 0; _logger.LogInformation("更新广告类型成功: Id={Id}, Name={Name}", id, advertType.Name); return result; } /// public async Task DeleteAdvertTypeAsync(int id) { var advertType = await _dbContext.AdvertTypes.FirstOrDefaultAsync(t => t.Id == id); if (advertType == null) { throw new BusinessException(BusinessErrorCodes.NotFound, "广告类型不存在"); } // 检查该类型下是否有广告 var hasAdverts = await _dbContext.Adverts.AnyAsync(a => a.Type == id); if (hasAdverts) { throw new BusinessException(BusinessErrorCodes.ValidationFailed, "删除失败:该类型下仍有关联的广告,请先删除或移动这些广告"); } _dbContext.AdvertTypes.Remove(advertType); var result = await _dbContext.SaveChangesAsync() > 0; _logger.LogInformation("删除广告类型成功: Id={Id}, Name={Name}", id, advertType.Name); return result; } #endregion #region Private Helper Methods /// /// 验证广告请求参数 /// private async Task ValidateAdvertRequest(AdvertCreateRequest request) { // 验证图片URL if (string.IsNullOrWhiteSpace(request.ImageUrl)) { throw new BusinessException(BusinessErrorCodes.ValidationFailed, "广告图片不能为空"); } // 验证类型是否存在 var typeExists = await _dbContext.AdvertTypes.AnyAsync(t => t.Id == request.TypeId); if (!typeExists) { throw new BusinessException(BusinessErrorCodes.ValidationFailed, "选择的广告类型无效"); } // 验证跳转类型 if (request.JumpType < 0 || request.JumpType > 5) { throw new BusinessException(BusinessErrorCodes.ValidationFailed, "跳转类型无效"); } // 根据跳转类型验证关联字段 switch (request.JumpType) { case AdvertJumpTypes.Coupon: if (!request.CouponId.HasValue || request.CouponId <= 0) { throw new BusinessException(BusinessErrorCodes.ValidationFailed, "请选择关联的优惠券"); } break; case AdvertJumpTypes.YiFanShang: case AdvertJumpTypes.WuXianShang: case AdvertJumpTypes.LianJiShang: if (!request.GoodsId.HasValue || request.GoodsId <= 0) { throw new BusinessException(BusinessErrorCodes.ValidationFailed, "请选择关联的盒子"); } break; case AdvertJumpTypes.CustomUrl: if (string.IsNullOrWhiteSpace(request.UrlLink)) { throw new BusinessException(BusinessErrorCodes.ValidationFailed, "请输入跳转链接"); } break; } } /// /// 映射广告实体到响应模型 /// private AdvertResponse MapToResponse(Advert advert, Dictionary typeNames) { return new AdvertResponse { Id = advert.Id, TypeId = advert.Type, TypeName = typeNames.GetValueOrDefault(advert.Type, "未知类型"), ImageUrl = advert.ImgUrl, Sort = advert.Sort, JumpType = advert.Ttype ?? 0, JumpTypeName = AdvertJumpTypes.GetJumpTypeName(advert.Ttype ?? 0), CouponId = advert.CouponId == 0 ? null : advert.CouponId, GoodsId = advert.GoodsId == 0 ? null : advert.GoodsId, UrlLink = advert.Url, CreatedAt = advert.CreatedAt, UpdatedAt = advert.UpdatedAt }; } #endregion }