mi-assessment/server/MiAssessment/src/MiAssessment.Admin.Business/Services/ContentService.cs
zpc 5e809c6cd1 fix(content): 修复 ErrorCodes.ContentNotFound 编译错误
- ErrorCodes 新增 NavigationNotFound = 3121
- ContentService 中 3 处 ContentNotFound 替换为 NavigationNotFound
2026-02-23 13:01:03 +08:00

743 lines
22 KiB
C#
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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>
/// 内容管ç<C2A1>†æœ<C3A6>务实现
/// </summary>
public class ContentService : IContentService
{
private readonly AdminBusinessDbContext _dbContext;
private readonly ILogger<ContentService> _logger;
/// <summary>
/// 跳转类åžå<E280B9><C3A5>称映射
/// </summary>
private static readonly Dictionary<int, string> LinkTypeNames = new()
{
{ 0, "æ—? },
{ 1, "内部页é<C2B5>¢" },
{ 2, "外部链接" },
{ 3, "å°<C3A5>ç¨åº? }
};
/// <summary>
/// 状æ€<C3A6>å<EFBFBD><C3A5>称映å°?
/// </summary>
private static readonly Dictionary<int, string> StatusNames = new()
{
{ 0, "ç¦<C3A7>用" },
{ 1, <>¯ç”¨" }
};
/// <summary>
/// 宣传å¾ä½<C3A4>ç½®å<C2AE><C3A5>称映å°?
/// </summary>
private static readonly Dictionary<int, string> PositionNames = new()
{
{ 1, "首页底部" },
{ 2, "团队� }
};
/// <summary>
/// 构造函�
/// </summary>
/// <param name="dbContext">æ•°æ<C2B0>®åº“ä¸Šä¸æ‡</param>
/// <param name="logger">日志记录�/param>
public ContentService(
AdminBusinessDbContext dbContext,
ILogger<ContentService> logger)
{
_dbContext = dbContext;
_logger = logger;
}
#region Banner è½®æ­å¾æ<EFBFBD>ä½?
/// <inheritdoc />
public async Task<PagedResult<BannerDto>> GetBannerListAsync(BannerQueryRequest request)
{
// 构建查询,过滤软删除记录
var query = _dbContext.Banners
.AsNoTracking()
.Where(b => !b.IsDeleted);
// 状æ€<C3A6>ç­é€?
if (request.Status.HasValue)
{
query = query.Where(b => b.Status == request.Status.Value);
}
// 获å<C2B7>总数
var total = await query.CountAsync();
// 分页查询,按 Sort é™<C3A9>åº<C3A5>æŽåˆ
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, "è½®æ­å¾ä¸<C3A4>存在");
}
return banner;
}
/// <inheritdoc />
public async Task<long> CreateBannerAsync(CreateBannerRequest request)
{
// 验è¯<C3A8>å¾ç‰‡URLå¿…å¡«
if (string.IsNullOrWhiteSpace(request.ImageUrl))
{
throw new BusinessException(ErrorCodes.BannerImageRequired, ¾ç‰‡URLä¸<C3A4>能为空");
}
// 验è¯<C3A8> 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(ˆå»ºè½®æ­å¾æˆ<C3A6>功,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, "è½®æ­å¾ä¸<C3A4>存在");
}
// 验è¯<C3A8>å¾ç‰‡URLå¿…å¡«
if (string.IsNullOrWhiteSpace(request.ImageUrl))
{
throw new BusinessException(ErrorCodes.BannerImageRequired, ¾ç‰‡URLä¸<C3A4>能为空");
}
// 验è¯<C3A8> 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(´æ°è½®æ­å¾æˆ<C3A6>功,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, "è½®æ­å¾ä¸<C3A4>存在");
}
// 软删�
banner.IsDeleted = true;
banner.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation(ˆ é™¤è½®æ­å¾æˆ<C3A6>功,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, "è½®æ­å¾ä¸<C3A4>存在");
}
// 更新状�
banner.Status = status;
banner.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation(´æ°è½®æ­å¾çжæ€<C3A6>æˆ<C3A6>功,ID: {BannerId}, 状æ€? {Status}", id, status);
return true;
}
/// <inheritdoc />
public async Task<bool> UpdateBannerSortAsync(List<SortItem> items)
{
if (items == null || items.Count == 0)
{
return true;
}
// 获å<C2B7>所有需è¦<C3A8>æ´æ°çš„è½®æ­å¾ID
var ids = items.Select(i => i.Id).ToList();
// 批é‡<C3A9>查询轮æ­å?
var banners = await _dbContext.Banners
.Where(b => ids.Contains(b.Id) && !b.IsDeleted)
.ToListAsync();
// æ´æ°æŽåº<C3A5>
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("批é‡<C3A9>æ´æ°è½®æ­å¾æŽåº<C3A5>æˆ<C3A6>åŠŸï¼Œæ´æ°æ•°é‡<C3A9>: {Count}", items.Count);
return true;
}
#endregion
#region Promotion 宣传徿<EFBFBD>�
/// <inheritdoc />
public async Task<PagedResult<PromotionDto>> GetPromotionListAsync(PromotionQueryRequest request)
{
// 构建查询,过滤软删除记录
var query = _dbContext.Promotions
.AsNoTracking()
.Where(p => !p.IsDeleted);
// ä½<C3A4>ç½®ç­é€?
if (request.Position.HasValue)
{
query = query.Where(p => p.Position == request.Position.Value);
}
// 状æ€<C3A6>ç­é€?
if (request.Status.HasValue)
{
query = query.Where(p => p.Status == request.Status.Value);
}
// 获å<C2B7>总数
var total = await query.CountAsync();
// 分页查询,按 Sort é™<C3A9>åº<C3A5>æŽåˆ
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, "宣传å¾ä¸<C3A4>存在");
}
return promotion;
}
/// <inheritdoc />
public async Task<long> CreatePromotionAsync(CreatePromotionRequest request)
{
// 验è¯<C3A8>å¾ç‰‡URLå¿…å¡«
if (string.IsNullOrWhiteSpace(request.ImageUrl))
{
throw new BusinessException(ErrorCodes.PromotionImageRequired, ¾ç‰‡URLä¸<C3A4>能为空");
}
// 验è¯<C3A8> 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(ˆå»ºå®£ä¼ å¾æˆ<C3A6>功,ID: {PromotionId}, 标题: {Title}, ä½<C3A4>ç½®: {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, "宣传å¾ä¸<C3A4>存在");
}
// 验è¯<C3A8>å¾ç‰‡URLå¿…å¡«
if (string.IsNullOrWhiteSpace(request.ImageUrl))
{
throw new BusinessException(ErrorCodes.PromotionImageRequired, ¾ç‰‡URLä¸<C3A4>能为空");
}
// 验è¯<C3A8> 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(´æ°å®£ä¼ å¾æˆ<C3A6>功,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, "宣传å¾ä¸<C3A4>存在");
}
// 软删�
promotion.IsDeleted = true;
promotion.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation(ˆ é™¤å®£ä¼ å¾æˆ<C3A6>功,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, "宣传å¾ä¸<C3A4>存在");
}
// 更新状�
promotion.Status = status;
promotion.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation(´æ°å®£ä¼ å¾çжæ€<C3A6>æˆ<C3A6>功,ID: {PromotionId}, 状æ€? {Status}", id, status);
return true;
}
#endregion
#region ç§<EFBFBD>æœæ¹æ³
/// <summary>
/// 验è¯<C3A8> LinkType 和对应字æ®?
/// </summary>
/// <param name="linkType">跳转类型</param>
/// <param name="linkUrl">跳转地å<C2B0>€</param>
/// <param name="appId">å°<C3A5>ç¨åº<C3A5>AppId</param>
private void ValidateLinkType(int linkType, string? linkUrl, string? appId)
{
switch (linkType)
{
case 0:
// 无跳转,ä¸<C3A4>需è¦<C3A8>验è¯?
break;
case 1:
case 2:
// 内部页é<C2B5>¢æˆå¤éƒ¨é“¾æŽ¥ï¼ŒLinkUrl å¿…å¡«
if (string.IsNullOrWhiteSpace(linkUrl))
{
throw new BusinessException(ErrorCodes.BannerLinkUrlRequired, "跳转地å<C2B0>€ä¸<C3A4>能为空");
}
break;
case 3:
// å°<C3A5>ç¨åº<C3A5>,LinkUrl å’?AppId 都必å¡?
if (string.IsNullOrWhiteSpace(linkUrl))
{
throw new BusinessException(ErrorCodes.BannerLinkUrlRequired, "跳转地å<C2B0>€ä¸<C3A4>能为空");
}
if (string.IsNullOrWhiteSpace(appId))
{
throw new BusinessException(ErrorCodes.BannerLinkUrlRequired, "å°<C3A5>ç¨åº<C3A5>AppIdä¸<C3A4>能为空");
}
break;
}
}
/// <summary>
/// 获å<C2B7>跳转类åžå<E280B9><C3A5>ç§°
/// </summary>
/// <param name="linkType">跳转类型</param>
/// <returns>ç±»åžå<E280B9><C3A5>ç§°</returns>
private static string GetLinkTypeName(int linkType)
{
return LinkTypeNames.TryGetValue(linkType, out var name) ? name : "未知";
}
/// <summary>
/// 获å<C2B7>状æ€<C3A6>å<EFBFBD><C3A5>ç§?
/// </summary>
/// <param name="status">状æ€<C3A6>å€?/param>
/// <returns>状æ€<C3A6>å<EFBFBD><C3A5>ç§?/returns>
private static string GetStatusName(int status)
{
return StatusNames.TryGetValue(status, out var name) ? name : "未知";
}
/// <summary>
/// 获å<C2B7>ä½<C3A4>ç½®å<C2AE><C3A5>ç§°
/// </summary>
/// <param name="position">ä½<C3A4>ç½®å€?/param>
/// <returns>ä½<C3A4>ç½®å<C2AE><C3A5>ç§°</returns>
private static string GetPositionName(int position)
{
return PositionNames.TryGetValue(position, out var name) ? name : "未知";
}
/// <summary>
/// 导航状æ€<C3A6>å<EFBFBD><C3A5>称映å°?
/// </summary>
private static readonly Dictionary<int, string> NavigationStatusNames = new()
{
{ 0, <>³å°†ä¸Šçº¿" },
{ 1, "已上� }
};
/// <summary>
/// 验è¯<C3A8> Position å€?
/// </summary>
/// <param name="position">ä½<C3A4>ç½®å€?/param>
private void ValidatePosition(int position)
{
if (position != 1 && position != 2)
{
throw new BusinessException(ErrorCodes.ParamError, "ä½<C3A4>置值必须为1(é¦é¡µåº•部)æˆ?(团队页ï¼?);
}
}
#endregion
#region HomeNavigation é¦é¡µå¯¼èˆªæ<EFBFBD>作
/// <inheritdoc />
public async Task<PagedResult<HomeNavigationDto>> GetNavigationListAsync(HomeNavigationQueryRequest request)
{
var query = _dbContext.HomeNavigations
.AsNoTracking()
.Where(n => !n.IsDeleted);
if (!string.IsNullOrWhiteSpace(request.Name))
{
query = query.Where(n => n.Name.Contains(request.Name));
}
if (request.Status.HasValue)
{
query = query.Where(n => n.Status == request.Status.Value);
}
var total = await query.CountAsync();
var items = await query
.OrderByDescending(n => n.Sort)
.ThenByDescending(n => n.CreateTime)
.Skip(request.Skip)
.Take(request.PageSize)
.Select(n => new HomeNavigationDto
{
Id = n.Id,
Name = n.Name,
ImageUrl = n.ImageUrl,
LinkUrl = n.LinkUrl,
Sort = n.Sort,
Status = n.Status,
StatusName = NavigationStatusNames.ContainsKey(n.Status) ? NavigationStatusNames[n.Status] : "未知",
CreateTime = n.CreateTime
})
.ToListAsync();
return PagedResult<HomeNavigationDto>.Create(items, total, request.Page, request.PageSize);
}
/// <inheritdoc />
public async Task<HomeNavigationDto> GetNavigationByIdAsync(long id)
{
var nav = await _dbContext.HomeNavigations
.AsNoTracking()
.Where(n => n.Id == id && !n.IsDeleted)
.Select(n => new HomeNavigationDto
{
Id = n.Id,
Name = n.Name,
ImageUrl = n.ImageUrl,
LinkUrl = n.LinkUrl,
Sort = n.Sort,
Status = n.Status,
StatusName = NavigationStatusNames.ContainsKey(n.Status) ? NavigationStatusNames[n.Status] : "未知",
CreateTime = n.CreateTime
})
.FirstOrDefaultAsync();
if (nav == null)
{
throw new BusinessException(ErrorCodes.NavigationNotFound, "é¦é¡µå¯¼èˆªä¸<C3A4>å­˜åœ?);
}
return nav;
}
/// <inheritdoc />
public async Task<long> CreateNavigationAsync(CreateHomeNavigationRequest request)
{
if (string.IsNullOrWhiteSpace(request.Name))
{
throw new BusinessException(ErrorCodes.ParamError, "导航å<C2AA><C3A5>ç§°ä¸<C3A4>能为空");
}
var entity = new HomeNavigation
{
Name = request.Name,
ImageUrl = request.ImageUrl,
LinkUrl = request.LinkUrl,
Sort = request.Sort,
Status = request.Status,
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now,
IsDeleted = false
};
_dbContext.HomeNavigations.Add(entity);
await _dbContext.SaveChangesAsync();
_logger.LogInformation(ˆå»ºé¦é¡µå¯¼èˆªæˆ<C3A6>功,ID: {Id}, å<><C3A5>ç§°: {Name}", entity.Id, entity.Name);
return entity.Id;
}
/// <inheritdoc />
public async Task<bool> UpdateNavigationAsync(UpdateHomeNavigationRequest request)
{
var entity = await _dbContext.HomeNavigations
.Where(n => n.Id == request.Id && !n.IsDeleted)
.FirstOrDefaultAsync();
if (entity == null)
{
throw new BusinessException(ErrorCodes.NavigationNotFound, "é¦é¡µå¯¼èˆªä¸<C3A4>å­˜åœ?);
}
if (string.IsNullOrWhiteSpace(request.Name))
{
throw new BusinessException(ErrorCodes.ParamError, "导航å<C2AA><C3A5>ç§°ä¸<C3A4>能为空");
}
entity.Name = request.Name;
entity.ImageUrl = request.ImageUrl;
entity.LinkUrl = request.LinkUrl;
entity.Sort = request.Sort;
entity.Status = request.Status;
entity.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation(´æ°é¦é¡µå¯¼èˆªæˆ<C3A6>功,ID: {Id}", entity.Id);
return true;
}
/// <inheritdoc />
public async Task<bool> DeleteNavigationAsync(long id)
{
var entity = await _dbContext.HomeNavigations.FindAsync(id);
if (entity == null || entity.IsDeleted) return false;
entity.IsDeleted = true;
entity.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation(ˆ é™¤é¦é¡µå¯¼èˆªæˆ<C3A6>功,ID: {Id}", id);
return true;
}
/// <inheritdoc />
public async Task<bool> UpdateNavigationStatusAsync(long id, int status)
{
var entity = await _dbContext.HomeNavigations
.Where(n => n.Id == id && !n.IsDeleted)
.FirstOrDefaultAsync();
if (entity == null)
{
throw new BusinessException(ErrorCodes.NavigationNotFound, "é¦é¡µå¯¼èˆªä¸<C3A4>å­˜åœ?);
}
entity.Status = status;
entity.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation(´æ°é¦é¡µå¯¼èˆªçжæ€<C3A6>æˆ<C3A6>功,ID: {Id}, 状æ€? {Status}", id, status);
return true;
}
#endregion
}