- 系统配置管理模块 (Config) - 内容管理模块 (Banner, Promotion) - 测评管理模块 (Type, Question, Category, Mapping, Conclusion) - 用户管理模块 (User) - 订单管理模块 (Order) - 规划师管理模块 (Planner) - 分销管理模块 (InviteCode, Commission, Withdrawal) - 数据统计仪表盘模块 (Dashboard) - 权限控制集成 - 服务注册配置 全部381个测试通过
572 lines
17 KiB
C#
572 lines
17 KiB
C#
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.Logging;
|
||
using MiAssessment.Admin.Business.Data;
|
||
using MiAssessment.Admin.Business.Entities;
|
||
using MiAssessment.Admin.Business.Models;
|
||
using MiAssessment.Admin.Business.Models.Common;
|
||
using MiAssessment.Admin.Business.Models.Content;
|
||
using MiAssessment.Admin.Business.Services.Interfaces;
|
||
|
||
namespace MiAssessment.Admin.Business.Services;
|
||
|
||
/// <summary>
|
||
/// 内容管理服务实现
|
||
/// </summary>
|
||
public class ContentService : IContentService
|
||
{
|
||
private readonly AdminBusinessDbContext _dbContext;
|
||
private readonly ILogger<ContentService> _logger;
|
||
|
||
/// <summary>
|
||
/// 跳转类型名称映射
|
||
/// </summary>
|
||
private static readonly Dictionary<int, string> LinkTypeNames = new()
|
||
{
|
||
{ 0, "无" },
|
||
{ 1, "内部页面" },
|
||
{ 2, "外部链接" },
|
||
{ 3, "小程序" }
|
||
};
|
||
|
||
/// <summary>
|
||
/// 状态名称映射
|
||
/// </summary>
|
||
private static readonly Dictionary<int, string> StatusNames = new()
|
||
{
|
||
{ 0, "禁用" },
|
||
{ 1, "启用" }
|
||
};
|
||
|
||
/// <summary>
|
||
/// 宣传图位置名称映射
|
||
/// </summary>
|
||
private static readonly Dictionary<int, string> PositionNames = new()
|
||
{
|
||
{ 1, "首页底部" },
|
||
{ 2, "团队页" }
|
||
};
|
||
|
||
/// <summary>
|
||
/// 构造函数
|
||
/// </summary>
|
||
/// <param name="dbContext">数据库上下文</param>
|
||
/// <param name="logger">日志记录器</param>
|
||
public ContentService(
|
||
AdminBusinessDbContext dbContext,
|
||
ILogger<ContentService> logger)
|
||
{
|
||
_dbContext = dbContext;
|
||
_logger = logger;
|
||
}
|
||
|
||
#region Banner 轮播图操作
|
||
|
||
/// <inheritdoc />
|
||
public async Task<PagedResult<BannerDto>> GetBannerListAsync(BannerQueryRequest request)
|
||
{
|
||
// 构建查询,过滤软删除记录
|
||
var query = _dbContext.Banners
|
||
.AsNoTracking()
|
||
.Where(b => !b.IsDeleted);
|
||
|
||
// 状态筛选
|
||
if (request.Status.HasValue)
|
||
{
|
||
query = query.Where(b => b.Status == request.Status.Value);
|
||
}
|
||
|
||
// 获取总数
|
||
var total = await query.CountAsync();
|
||
|
||
// 分页查询,按 Sort 降序排列
|
||
var items = await query
|
||
.OrderByDescending(b => b.Sort)
|
||
.ThenByDescending(b => b.CreateTime)
|
||
.Skip(request.Skip)
|
||
.Take(request.PageSize)
|
||
.Select(b => new BannerDto
|
||
{
|
||
Id = b.Id,
|
||
Title = b.Title,
|
||
ImageUrl = b.ImageUrl,
|
||
LinkType = b.LinkType,
|
||
LinkTypeName = GetLinkTypeName(b.LinkType),
|
||
LinkUrl = b.LinkUrl,
|
||
AppId = b.AppId,
|
||
Sort = b.Sort,
|
||
Status = b.Status,
|
||
StatusName = GetStatusName(b.Status),
|
||
CreateTime = b.CreateTime
|
||
})
|
||
.ToListAsync();
|
||
|
||
return PagedResult<BannerDto>.Create(items, total, request.Page, request.PageSize);
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<BannerDto> GetBannerByIdAsync(long id)
|
||
{
|
||
var banner = await _dbContext.Banners
|
||
.AsNoTracking()
|
||
.Where(b => b.Id == id && !b.IsDeleted)
|
||
.Select(b => new BannerDto
|
||
{
|
||
Id = b.Id,
|
||
Title = b.Title,
|
||
ImageUrl = b.ImageUrl,
|
||
LinkType = b.LinkType,
|
||
LinkTypeName = GetLinkTypeName(b.LinkType),
|
||
LinkUrl = b.LinkUrl,
|
||
AppId = b.AppId,
|
||
Sort = b.Sort,
|
||
Status = b.Status,
|
||
StatusName = GetStatusName(b.Status),
|
||
CreateTime = b.CreateTime
|
||
})
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (banner == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.BannerNotFound, "轮播图不存在");
|
||
}
|
||
|
||
return banner;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<long> CreateBannerAsync(CreateBannerRequest request)
|
||
{
|
||
// 验证图片URL必填
|
||
if (string.IsNullOrWhiteSpace(request.ImageUrl))
|
||
{
|
||
throw new BusinessException(ErrorCodes.BannerImageRequired, "图片URL不能为空");
|
||
}
|
||
|
||
// 验证 LinkType 和对应字段
|
||
ValidateLinkType(request.LinkType, request.LinkUrl, request.AppId);
|
||
|
||
// 创建实体
|
||
var banner = new Banner
|
||
{
|
||
Title = request.Title,
|
||
ImageUrl = request.ImageUrl,
|
||
LinkType = request.LinkType,
|
||
LinkUrl = request.LinkUrl,
|
||
AppId = request.AppId,
|
||
Sort = request.Sort,
|
||
Status = request.Status,
|
||
CreateTime = DateTime.Now,
|
||
UpdateTime = DateTime.Now,
|
||
IsDeleted = false
|
||
};
|
||
|
||
_dbContext.Banners.Add(banner);
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
_logger.LogInformation("创建轮播图成功,ID: {BannerId}, 标题: {Title}", banner.Id, banner.Title);
|
||
|
||
return banner.Id;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> UpdateBannerAsync(UpdateBannerRequest request)
|
||
{
|
||
// 查找轮播图
|
||
var banner = await _dbContext.Banners
|
||
.Where(b => b.Id == request.Id && !b.IsDeleted)
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (banner == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.BannerNotFound, "轮播图不存在");
|
||
}
|
||
|
||
// 验证图片URL必填
|
||
if (string.IsNullOrWhiteSpace(request.ImageUrl))
|
||
{
|
||
throw new BusinessException(ErrorCodes.BannerImageRequired, "图片URL不能为空");
|
||
}
|
||
|
||
// 验证 LinkType 和对应字段
|
||
ValidateLinkType(request.LinkType, request.LinkUrl, request.AppId);
|
||
|
||
// 更新字段
|
||
banner.Title = request.Title;
|
||
banner.ImageUrl = request.ImageUrl;
|
||
banner.LinkType = request.LinkType;
|
||
banner.LinkUrl = request.LinkUrl;
|
||
banner.AppId = request.AppId;
|
||
banner.Sort = request.Sort;
|
||
banner.Status = request.Status;
|
||
banner.UpdateTime = DateTime.Now;
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
_logger.LogInformation("更新轮播图成功,ID: {BannerId}", banner.Id);
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> DeleteBannerAsync(long id)
|
||
{
|
||
// 查找轮播图
|
||
var banner = await _dbContext.Banners
|
||
.Where(b => b.Id == id && !b.IsDeleted)
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (banner == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.BannerNotFound, "轮播图不存在");
|
||
}
|
||
|
||
// 软删除
|
||
banner.IsDeleted = true;
|
||
banner.UpdateTime = DateTime.Now;
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
_logger.LogInformation("删除轮播图成功,ID: {BannerId}", id);
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> UpdateBannerStatusAsync(long id, int status)
|
||
{
|
||
// 查找轮播图
|
||
var banner = await _dbContext.Banners
|
||
.Where(b => b.Id == id && !b.IsDeleted)
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (banner == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.BannerNotFound, "轮播图不存在");
|
||
}
|
||
|
||
// 更新状态
|
||
banner.Status = status;
|
||
banner.UpdateTime = DateTime.Now;
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
_logger.LogInformation("更新轮播图状态成功,ID: {BannerId}, 状态: {Status}", id, status);
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> UpdateBannerSortAsync(List<SortItem> items)
|
||
{
|
||
if (items == null || items.Count == 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
// 获取所有需要更新的轮播图ID
|
||
var ids = items.Select(i => i.Id).ToList();
|
||
|
||
// 批量查询轮播图
|
||
var banners = await _dbContext.Banners
|
||
.Where(b => ids.Contains(b.Id) && !b.IsDeleted)
|
||
.ToListAsync();
|
||
|
||
// 更新排序
|
||
foreach (var item in items)
|
||
{
|
||
var banner = banners.FirstOrDefault(b => b.Id == item.Id);
|
||
if (banner != null)
|
||
{
|
||
banner.Sort = item.Sort;
|
||
banner.UpdateTime = DateTime.Now;
|
||
}
|
||
}
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
_logger.LogInformation("批量更新轮播图排序成功,更新数量: {Count}", items.Count);
|
||
|
||
return true;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Promotion 宣传图操作
|
||
|
||
/// <inheritdoc />
|
||
public async Task<PagedResult<PromotionDto>> GetPromotionListAsync(PromotionQueryRequest request)
|
||
{
|
||
// 构建查询,过滤软删除记录
|
||
var query = _dbContext.Promotions
|
||
.AsNoTracking()
|
||
.Where(p => !p.IsDeleted);
|
||
|
||
// 位置筛选
|
||
if (request.Position.HasValue)
|
||
{
|
||
query = query.Where(p => p.Position == request.Position.Value);
|
||
}
|
||
|
||
// 状态筛选
|
||
if (request.Status.HasValue)
|
||
{
|
||
query = query.Where(p => p.Status == request.Status.Value);
|
||
}
|
||
|
||
// 获取总数
|
||
var total = await query.CountAsync();
|
||
|
||
// 分页查询,按 Sort 降序排列
|
||
var items = await query
|
||
.OrderByDescending(p => p.Sort)
|
||
.ThenByDescending(p => p.CreateTime)
|
||
.Skip(request.Skip)
|
||
.Take(request.PageSize)
|
||
.Select(p => new PromotionDto
|
||
{
|
||
Id = p.Id,
|
||
Title = p.Title,
|
||
ImageUrl = p.ImageUrl,
|
||
Position = p.Position,
|
||
PositionName = GetPositionName(p.Position),
|
||
Sort = p.Sort,
|
||
Status = p.Status,
|
||
StatusName = GetStatusName(p.Status),
|
||
CreateTime = p.CreateTime
|
||
})
|
||
.ToListAsync();
|
||
|
||
return PagedResult<PromotionDto>.Create(items, total, request.Page, request.PageSize);
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<PromotionDto> GetPromotionByIdAsync(long id)
|
||
{
|
||
var promotion = await _dbContext.Promotions
|
||
.AsNoTracking()
|
||
.Where(p => p.Id == id && !p.IsDeleted)
|
||
.Select(p => new PromotionDto
|
||
{
|
||
Id = p.Id,
|
||
Title = p.Title,
|
||
ImageUrl = p.ImageUrl,
|
||
Position = p.Position,
|
||
PositionName = GetPositionName(p.Position),
|
||
Sort = p.Sort,
|
||
Status = p.Status,
|
||
StatusName = GetStatusName(p.Status),
|
||
CreateTime = p.CreateTime
|
||
})
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (promotion == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.PromotionNotFound, "宣传图不存在");
|
||
}
|
||
|
||
return promotion;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<long> CreatePromotionAsync(CreatePromotionRequest request)
|
||
{
|
||
// 验证图片URL必填
|
||
if (string.IsNullOrWhiteSpace(request.ImageUrl))
|
||
{
|
||
throw new BusinessException(ErrorCodes.PromotionImageRequired, "图片URL不能为空");
|
||
}
|
||
|
||
// 验证 Position 值
|
||
ValidatePosition(request.Position);
|
||
|
||
// 创建实体
|
||
var promotion = new Promotion
|
||
{
|
||
Title = request.Title,
|
||
ImageUrl = request.ImageUrl,
|
||
Position = request.Position,
|
||
Sort = request.Sort,
|
||
Status = request.Status,
|
||
CreateTime = DateTime.Now,
|
||
UpdateTime = DateTime.Now,
|
||
IsDeleted = false
|
||
};
|
||
|
||
_dbContext.Promotions.Add(promotion);
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
_logger.LogInformation("创建宣传图成功,ID: {PromotionId}, 标题: {Title}, 位置: {Position}",
|
||
promotion.Id, promotion.Title, promotion.Position);
|
||
|
||
return promotion.Id;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> UpdatePromotionAsync(UpdatePromotionRequest request)
|
||
{
|
||
// 查找宣传图
|
||
var promotion = await _dbContext.Promotions
|
||
.Where(p => p.Id == request.Id && !p.IsDeleted)
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (promotion == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.PromotionNotFound, "宣传图不存在");
|
||
}
|
||
|
||
// 验证图片URL必填
|
||
if (string.IsNullOrWhiteSpace(request.ImageUrl))
|
||
{
|
||
throw new BusinessException(ErrorCodes.PromotionImageRequired, "图片URL不能为空");
|
||
}
|
||
|
||
// 验证 Position 值
|
||
ValidatePosition(request.Position);
|
||
|
||
// 更新字段
|
||
promotion.Title = request.Title;
|
||
promotion.ImageUrl = request.ImageUrl;
|
||
promotion.Position = request.Position;
|
||
promotion.Sort = request.Sort;
|
||
promotion.Status = request.Status;
|
||
promotion.UpdateTime = DateTime.Now;
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
_logger.LogInformation("更新宣传图成功,ID: {PromotionId}", promotion.Id);
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> DeletePromotionAsync(long id)
|
||
{
|
||
// 查找宣传图
|
||
var promotion = await _dbContext.Promotions
|
||
.Where(p => p.Id == id && !p.IsDeleted)
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (promotion == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.PromotionNotFound, "宣传图不存在");
|
||
}
|
||
|
||
// 软删除
|
||
promotion.IsDeleted = true;
|
||
promotion.UpdateTime = DateTime.Now;
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
_logger.LogInformation("删除宣传图成功,ID: {PromotionId}", id);
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<bool> UpdatePromotionStatusAsync(long id, int status)
|
||
{
|
||
// 查找宣传图
|
||
var promotion = await _dbContext.Promotions
|
||
.Where(p => p.Id == id && !p.IsDeleted)
|
||
.FirstOrDefaultAsync();
|
||
|
||
if (promotion == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.PromotionNotFound, "宣传图不存在");
|
||
}
|
||
|
||
// 更新状态
|
||
promotion.Status = status;
|
||
promotion.UpdateTime = DateTime.Now;
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
_logger.LogInformation("更新宣传图状态成功,ID: {PromotionId}, 状态: {Status}", id, status);
|
||
|
||
return true;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 私有方法
|
||
|
||
/// <summary>
|
||
/// 验证 LinkType 和对应字段
|
||
/// </summary>
|
||
/// <param name="linkType">跳转类型</param>
|
||
/// <param name="linkUrl">跳转地址</param>
|
||
/// <param name="appId">小程序AppId</param>
|
||
private void ValidateLinkType(int linkType, string? linkUrl, string? appId)
|
||
{
|
||
switch (linkType)
|
||
{
|
||
case 0:
|
||
// 无跳转,不需要验证
|
||
break;
|
||
case 1:
|
||
case 2:
|
||
// 内部页面或外部链接,LinkUrl 必填
|
||
if (string.IsNullOrWhiteSpace(linkUrl))
|
||
{
|
||
throw new BusinessException(ErrorCodes.BannerLinkUrlRequired, "跳转地址不能为空");
|
||
}
|
||
break;
|
||
case 3:
|
||
// 小程序,LinkUrl 和 AppId 都必填
|
||
if (string.IsNullOrWhiteSpace(linkUrl))
|
||
{
|
||
throw new BusinessException(ErrorCodes.BannerLinkUrlRequired, "跳转地址不能为空");
|
||
}
|
||
if (string.IsNullOrWhiteSpace(appId))
|
||
{
|
||
throw new BusinessException(ErrorCodes.BannerLinkUrlRequired, "小程序AppId不能为空");
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取跳转类型名称
|
||
/// </summary>
|
||
/// <param name="linkType">跳转类型</param>
|
||
/// <returns>类型名称</returns>
|
||
private static string GetLinkTypeName(int linkType)
|
||
{
|
||
return LinkTypeNames.TryGetValue(linkType, out var name) ? name : "未知";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取状态名称
|
||
/// </summary>
|
||
/// <param name="status">状态值</param>
|
||
/// <returns>状态名称</returns>
|
||
private static string GetStatusName(int status)
|
||
{
|
||
return StatusNames.TryGetValue(status, out var name) ? name : "未知";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取位置名称
|
||
/// </summary>
|
||
/// <param name="position">位置值</param>
|
||
/// <returns>位置名称</returns>
|
||
private static string GetPositionName(int position)
|
||
{
|
||
return PositionNames.TryGetValue(position, out var name) ? name : "未知";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证 Position 值
|
||
/// </summary>
|
||
/// <param name="position">位置值</param>
|
||
private void ValidatePosition(int position)
|
||
{
|
||
if (position != 1 && position != 2)
|
||
{
|
||
throw new BusinessException(ErrorCodes.ParamError, "位置值必须为1(首页底部)或2(团队页)");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|