mi-assessment/server/MiAssessment/src/MiAssessment.Admin.Business/Services/ContentService.cs
zpc 6bf2ea595c feat(admin-business): 完成后台管理系统全部业务模块
- 系统配置管理模块 (Config)
- 内容管理模块 (Banner, Promotion)
- 测评管理模块 (Type, Question, Category, Mapping, Conclusion)
- 用户管理模块 (User)
- 订单管理模块 (Order)
- 规划师管理模块 (Planner)
- 分销管理模块 (InviteCode, Commission, Withdrawal)
- 数据统计仪表盘模块 (Dashboard)
- 权限控制集成
- 服务注册配置

全部381个测试通过
2026-02-03 20:50:51 +08:00

572 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}