2026-06-04 14:23:50 +08:00

323 lines
11 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 YD_Event.Application.Project.Dtos;
using YD_Event.Core.Entities;
using YD_Event.Core.Enum;
using Mapster;
using SqlSugar;
using Microsoft.Extensions.Logging;
using Easy.Core;
namespace YD_Event.Application.Project;
/// <summary>
/// 项目管理
/// </summary>
public class ProjectService : BaseService<YD_Event.Core.Entities.Project>, ITransient
{
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Project> _repository;
private readonly ISqlSugarRepository<ProjectGroup> _groupRepository;
private readonly ISqlSugarRepository<EventProject> _eventProjectRepository;
private readonly ISqlSugarRepository<EventParticipationRecord> _participationRecordRepository;
private readonly IIdGenerator _idGenerator;
private readonly ILogger<ProjectService> _logger;
public ProjectService(
ISqlSugarRepository<YD_Event.Core.Entities.Project> repository,
ISqlSugarRepository<ProjectGroup> groupRepository,
ISqlSugarRepository<EventProject> eventProjectRepository,
ISqlSugarRepository<EventParticipationRecord> participationRecordRepository,
IIdGenerator idGenerator,
ILogger<ProjectService> logger) : base(repository)
{
_repository = repository;
_groupRepository = groupRepository;
_eventProjectRepository = eventProjectRepository;
_participationRecordRepository = participationRecordRepository;
_idGenerator = idGenerator;
_logger = logger;
}
/// <summary>
/// 项目分页查询
/// </summary>
[DisplayName("项目分页查询")]
public async Task<PageResult<ProjectDto>> Page(ProjectQueryInput input)
{
var query = _repository.AsQueryable()
.WhereIF(!string.IsNullOrWhiteSpace(input.ProjectName), p => p.Name.Contains(input.ProjectName))
.WhereIF(input.ProjectType.HasValue, p => p.ProjectType == (ProjectTypeEnum)input.ProjectType.Value)
.OrderByDescending(p => p.Id);
var list = await query.Select(p => new ProjectDto
{
Id = p.Id,
Name = p.Name,
ProjectType = (long)p.ProjectType,
CompletionCount = p.CompletionCount,
Fee = p.Fee,
DurationMinutes = p.DurationMinutes,
DurationSeconds = p.DurationSeconds,
VenueCount = p.VenueCount,
SortIndex = p.SortIndex,
Status = p.Status,
DeleteMark = p.DeleteMark,
CreatedUserId = p.CreatedUserId,
CreatedTime = p.CreatedTime,
UpdatedUserId = p.UpdatedUserId,
UpdatedTime = p.UpdatedTime
})
.ToPagedListAsync(input);
foreach (var item in list.Rows)
{
item.ProjectTypeName = ((ProjectTypeEnum)item.ProjectType).Description();
}
return list;
}
/// <summary>
/// 添加项目
/// </summary>
[DisplayName("添加项目")]
[UnitOfWork]
public async Task Add(AddProjectInput input)
{
var entity = input.Adapt<YD_Event.Core.Entities.Project>();
entity.Id = _idGenerator.NewLong();
entity.CreatedTime = DateTime.Now;
entity.Status = Core.Enum.AvailabilityStatus.Enable;
await _repository.InsertAsync(entity);
if (input.Groups != null && input.Groups.Any())
{
var groups = input.Groups.Select(g =>
{
var group = g.Adapt<ProjectGroup>();
group.Id = _idGenerator.NewLong();
group.ProjectId = entity.Id;
return group;
}).ToList();
await _groupRepository.InsertRangeAsync(groups);
}
}
/// <summary>
/// 更新项目
/// </summary>
[DisplayName("更新项目")]
[UnitOfWork]
public async Task Update(UpdateProjectInput input)
{
var entity = await _repository.GetByIdAsync(input.Id);
if (entity == null) throw new InvalidOperationException("项目不存在");
input.Adapt(entity);
entity.UpdatedTime = DateTime.Now;
await _repository.UpdateAsync(entity);
await _groupRepository.DeleteAsync(x => x.ProjectId == entity.Id);
if (input.Groups != null && input.Groups.Any())
{
var groups = input.Groups.Select(g =>
{
var group = g.Adapt<ProjectGroup>();
group.Id = _idGenerator.NewLong();
group.ProjectId = entity.Id;
return group;
}).ToList();
await _groupRepository.InsertRangeAsync(groups);
}
}
/// <summary>
/// 删除项目
/// </summary>
[DisplayName("删除项目")]
[UnitOfWork]
public async Task Delete(List<long> ids)
{
_logger.LogInformation("开始删除项目:项目ID列表={Ids}", string.Join(",", ids));
// 检查项目是否被赛事使用
var hasEventProjects = await _eventProjectRepository.IsAnyAsync(x => ids.Contains(x.ProjectId));
if (hasEventProjects)
{
_logger.LogWarning("删除失败:部分项目正在被赛事使用");
throw new InvalidOperationException("部分项目正在被赛事使用,无法删除。请先从赛事中移除这些项目。");
}
// 检查是否有报名数据关联
var eventProjectIds = await _eventProjectRepository.AsQueryable()
.Where(x => ids.Contains(x.ProjectId))
.Select(x => x.Id)
.ToListAsync();
if (eventProjectIds.Any())
{
var hasParticipations = await _participationRecordRepository.IsAnyAsync(x =>
eventProjectIds.Contains(x.ProjectId) && !x.DeleteMark);
if (hasParticipations)
{
_logger.LogWarning("删除失败:部分项目已有报名数据");
throw new InvalidOperationException("部分项目已有报名数据,为保证数据完整性,无法删除。");
}
}
// 执行删除
await _repository.DeleteAsync(x => ids.Contains(x.Id));
await _groupRepository.DeleteAsync(x => ids.Contains(x.ProjectId));
_logger.LogInformation("删除项目成功:项目ID列表={Ids}", string.Join(",", ids));
}
/// <summary>
/// 获取项目详情
/// </summary>
[DisplayName("获取项目详情")]
public async Task<ProjectDto> Detail(long id)
{
var p = await _repository.GetByIdAsync(id);
if (p == null) throw new InvalidOperationException("项目不存在");
var dto = p.Adapt<ProjectDto>();
dto.ProjectType = (long)p.ProjectType;
dto.ProjectTypeName = p.ProjectType.Description();
var groups = await _groupRepository.GetListAsync(x => x.ProjectId == id);
dto.Groups = groups.Adapt<List<ProjectGroupDto>>();
return dto;
}
/// <summary>
/// 项目统计类型
/// </summary>
public enum ProjectStatisticType
{
/// <summary>
/// 参赛人数
/// </summary>
ParticipationCount = 1,
/// <summary>
/// 完成人数
/// </summary>
CompletionCount = 2
}
/// <summary>
/// 获取项目统计数据(单个项目)
/// </summary>
/// <param name="projectId">项目ID</param>
/// <param name="statisticType">统计类型</param>
/// <returns>统计数值</returns>
[DisplayName("获取项目统计数据")]
public async Task<int> GetProjectStatisticAsync(long projectId, ProjectStatisticType statisticType)
{
if (projectId <= 0)
{
_logger.LogWarning("Invalid projectId: {ProjectId}", projectId);
return 0;
}
var eventProjectIds = await _eventProjectRepository.AsQueryable()
.Where(ep => ep.ProjectId == projectId)
.Select(ep => ep.Id)
.ToListAsync();
if (!eventProjectIds.Any())
{
return 0;
}
var query = _participationRecordRepository.AsQueryable()
.Where(pr => eventProjectIds.Contains(pr.ProjectId) && !pr.DeleteMark);
switch (statisticType)
{
case ProjectStatisticType.ParticipationCount:
query = query.Where(pr => pr.ParticipationStatus == ParticipationStatus.Participated || pr.ParticipationStatus == ParticipationStatus.Completed);
break;
case ProjectStatisticType.CompletionCount:
query = query.Where(pr => pr.ParticipationStatus == ParticipationStatus.Completed);
break;
default:
_logger.LogWarning("Unknown statistic type: {StatisticType}", statisticType);
return 0;
}
var count = await query.CountAsync();
return count;
}
/// <summary>
/// 批量获取项目统计数据(性能优化,避免N+1查询)
/// </summary>
/// <param name="projectIds">项目ID列表</param>
/// <param name="statisticType">统计类型</param>
/// <returns>字典,Key为项目ID,Value为统计数值</returns>
[DisplayName("批量获取项目统计数据")]
public async Task<Dictionary<long, int>> GetProjectStatisticsBatchAsync(List<long> projectIds, ProjectStatisticType statisticType)
{
var result = new Dictionary<long, int>();
if (projectIds == null || !projectIds.Any())
{
return result;
}
foreach (var projectId in projectIds)
{
result[projectId] = 0;
}
var eventProjects = await _eventProjectRepository.AsQueryable()
.Where(ep => projectIds.Contains(ep.ProjectId))
.Select(ep => new { ep.Id, ep.ProjectId })
.ToListAsync();
if (!eventProjects.Any())
{
return result;
}
var eventProjectIds = eventProjects.Select(ep => ep.Id).ToList();
var query = _participationRecordRepository.AsQueryable()
.Where(pr => eventProjectIds.Contains(pr.ProjectId) && !pr.DeleteMark);
switch (statisticType)
{
case ProjectStatisticType.ParticipationCount:
query = query.Where(pr => pr.ParticipationStatus == ParticipationStatus.Participated || pr.ParticipationStatus == ParticipationStatus.Completed);
break;
case ProjectStatisticType.CompletionCount:
query = query.Where(pr => pr.ParticipationStatus == ParticipationStatus.Completed);
break;
default:
_logger.LogWarning("Unknown statistic type: {StatisticType}", statisticType);
return result;
}
var records = await query.ToListAsync();
foreach (var record in records)
{
var projectId = eventProjects.FirstOrDefault(ep => ep.Id == record.ProjectId)?.ProjectId;
if (projectId.HasValue && result.ContainsKey(projectId.Value))
{
result[projectId.Value]++;
}
}
return result;
}
}