359 lines
11 KiB
C#
359 lines
11 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// 广告管理服务实现
|
|
/// </summary>
|
|
public class AdvertService : IAdvertService
|
|
{
|
|
private readonly HoneyBoxDbContext _dbContext;
|
|
private readonly ILogger<AdvertService> _logger;
|
|
|
|
public AdvertService(
|
|
HoneyBoxDbContext dbContext,
|
|
ILogger<AdvertService> logger)
|
|
{
|
|
_dbContext = dbContext;
|
|
_logger = logger;
|
|
}
|
|
|
|
#region 广告管理
|
|
|
|
/// <inheritdoc />
|
|
public async Task<PagedResult<AdvertResponse>> 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<AdvertResponse>.Create(list, total, request.Page, request.PageSize);
|
|
}
|
|
|
|
|
|
/// <inheritdoc />
|
|
public async Task<AdvertResponse?> 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<int, string> { { advert.Type, typeName } });
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<int> 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;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<bool> 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;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<bool> 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 广告类型管理
|
|
|
|
/// <inheritdoc />
|
|
public async Task<List<AdvertTypeResponse>> 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();
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<int> 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;
|
|
}
|
|
|
|
|
|
/// <inheritdoc />
|
|
public async Task<bool> 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;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public async Task<bool> 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
|
|
|
|
/// <summary>
|
|
/// 验证广告请求参数
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 映射广告实体到响应模型
|
|
/// </summary>
|
|
private AdvertResponse MapToResponse(Advert advert, Dictionary<int, string> 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
|
|
}
|