Files
TechHelper/TechHelper.Server/Services/Question/QuestionService.cs
SpecialX ac900159ba
Some checks failed
TechAct / explore-gitea-actions (push) Failing after 12s
重构项目结构,移除Assignment相关功能,优化Submission模块
2025-10-09 18:57:28 +08:00

303 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 AutoMapper;
using Entities.Contracts;
using SharedDATA.Api;
using System.Linq.Expressions;
using System.Linq;
using Entities.DTO;
namespace TechHelper.Services.Beta
{
public class QuestionService : IQuestionService
{
private readonly IUnitOfWork _work;
private readonly IMapper _mapper;
private readonly IExamService _examService;
public QuestionService(IUnitOfWork work, IMapper mapper, IExamService examService)
{
_work = work;
_mapper = mapper;
_examService = examService;
}
#region CRUD Operations
/// <summary>
/// 创建新题目
/// </summary>
/// <param name="model">题目DTO</param>
/// <returns>API响应结果</returns>
public async Task<ApiResponse> AddAsync(QuestionDto model)
{
try
{
// 业务逻辑校验:检查题目是否已存在
var existingQuestion = await _work.GetRepository<Question>().GetFirstOrDefaultAsync(
predicate: q => q.Title == model.Title && !q.IsDeleted
);
if (existingQuestion != null)
{
return ApiResponse.Error($"题目 '{model.Title}' 已存在,请勿重复添加。");
}
var newQuestion = _mapper.Map<Question>(model);
// 插入数据
await _work.GetRepository<Question>().InsertAsync(newQuestion);
await _work.SaveChangesAsync();
return ApiResponse.Success("题目添加成功。", newQuestion);
}
catch (Exception ex)
{
return ApiResponse.Error($"添加题目失败: {ex.Message}");
}
}
/// <summary>
/// 根据ID获取题目
/// </summary>
/// <param name="id">题目ID</param>
/// <returns>API响应结果</returns>
public async Task<ApiResponse> GetAsync(Guid id)
{
try
{
var question = await _work.GetRepository<Question>().GetFirstOrDefaultAsync(
predicate: q => q.Id == id && !q.IsDeleted
);
if (question == null)
{
return ApiResponse.Error($"找不到 ID 为 {id} 的题目。");
}
return ApiResponse.Success(result: question);
}
catch (Exception ex)
{
return ApiResponse.Error($"获取题目时发生错误: {ex.Message}");
}
}
/// <summary>
/// 获取所有题目(支持分页和搜索)
/// </summary>
/// <param name="query">查询参数</param>
/// <returns>API响应结果</returns>
public async Task<ApiResponse> GetAllAsync(QueryParameter query)
{
try
{
var repository = _work.GetRepository<Question>();
if (query.Search != null && !string.IsNullOrWhiteSpace(query.Search))
{
var questions = await repository.GetPagedListAsync(
predicate: q => q.Title.Contains(query.Search) && !q.IsDeleted,
pageSize: query.PageSize,
pageIndex: query.PageIndex
);
var questionDtosFiltered = _mapper.Map<IEnumerable<QuestionDto>>(questions.Items);
return new ApiResponse(true, questionDtosFiltered);
}
else
{
var questions = await repository.GetPagedListAsync(
predicate: q => !q.IsDeleted,
pageSize: query.PageSize,
pageIndex: query.PageIndex
);
var questionDtos = _mapper.Map<IEnumerable<QuestionDto>>(questions.Items);
return new ApiResponse(true, questionDtos);
}
}
catch (Exception ex)
{
return new ApiResponse($"获取所有题目时发生错误: {ex.Message}");
}
}
/// <summary>
/// 更新题目信息
/// </summary>
/// <param name="model">题目DTO</param>
/// <returns>API响应结果</returns>
public async Task<ApiResponse> UpdateAsync(QuestionDto model)
{
try
{
// 检查题目是否存在
var existingQuestion = await _work.GetRepository<Question>().GetFirstOrDefaultAsync(
predicate: q => q.Id == model.Id && !q.IsDeleted
);
if (existingQuestion == null)
{
return ApiResponse.Error($"找不到 ID 为 {model.Id} 的题目,无法更新。");
}
// 检查题目文本是否与其他题目重复
var duplicateCheck = await _work.GetRepository<Question>().GetFirstOrDefaultAsync(
predicate: q => q.Id != model.Id && q.Title == model.Title && !q.IsDeleted
);
if (duplicateCheck != null)
{
return ApiResponse.Error($"题目文本 '{model.Title}' 已被其他题目占用,请修改。");
}
// 使用mapper更新实体属性保留系统字段
_mapper.Map(model, existingQuestion);
existingQuestion.UpdatedAt = DateTime.UtcNow;
// 更新数据
_work.GetRepository<Question>().Update(existingQuestion);
await _work.SaveChangesAsync();
return ApiResponse.Success("题目更新成功。", existingQuestion);
}
catch (Exception ex)
{
return ApiResponse.Error($"更新题目失败: {ex.Message}");
}
}
/// <summary>
/// 删除题目(软删除)
/// </summary>
/// <param name="id">题目ID</param>
/// <returns>API响应结果</returns>
public async Task<ApiResponse> DeleteAsync(Guid id)
{
try
{
var questionToDelete = await _work.GetRepository<Question>().GetFirstOrDefaultAsync(
predicate: q => q.Id == id && !q.IsDeleted
);
if (questionToDelete == null)
{
return ApiResponse.Error($"找不到 ID 为 {id} 的题目,或者该题目已被删除。");
}
// 软删除
questionToDelete.IsDeleted = true;
questionToDelete.UpdatedAt = DateTime.UtcNow;
_work.GetRepository<Question>().Update(questionToDelete);
await _work.SaveChangesAsync();
return ApiResponse.Success($"ID 为 {id} 的题目已成功删除 (软删除)。");
}
catch (Exception ex)
{
return ApiResponse.Error($"删除题目时发生错误: {ex.Message}");
}
}
#endregion
#region Business Logic Methods
/// <summary>
/// 根据标题查找题目
/// </summary>
/// <param name="title">题目标题</param>
/// <returns>API响应结果</returns>
public async Task<ApiResponse> FindByTitle(string title)
{
try
{
var question = await _work.GetRepository<Question>().GetFirstOrDefaultAsync(
predicate: q => q.Title == title && !q.IsDeleted
);
if (question == null)
{
return ApiResponse.Error($"未找到题目 '{title}'。");
}
return ApiResponse.Success(result: question);
}
catch (Exception ex)
{
return ApiResponse.Error($"查找题目时出现问题: {ex.Message}");
}
}
/// <summary>
/// 批量检查题目标题是否存在
/// </summary>
/// <param name="titles">题目标题集合</param>
/// <returns>API响应结果</returns>
public async Task<ApiResponse> CheckTitlesExistence(IEnumerable<string> titles)
{
try
{
if (titles == null || !titles.Any())
{
return ApiResponse.Success("未指定查询的题目文本,返回空结果。", new Dictionary<string, bool>());
}
var distinctTitles = titles.Distinct(StringComparer.OrdinalIgnoreCase).ToList();
var existingQuestions = await _work.GetRepository<Question>().GetAllAsync(
predicate: q => distinctTitles.Contains(q.Title) && !q.IsDeleted
);
var existingQuestionTexts = new HashSet<string>(
existingQuestions.Select(q => q.Title),
StringComparer.OrdinalIgnoreCase
);
var resultDictionary = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
foreach (var title in titles)
{
resultDictionary[title] = existingQuestionTexts.Contains(title);
}
return ApiResponse.Success(result: resultDictionary);
}
catch (Exception ex)
{
return ApiResponse.Error($"批量查找题目存在性时出现问题: {ex.Message}");
}
}
#endregion
}
/// <summary>
/// 表达式构建器扩展类
/// </summary>
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> And<T>(
this Expression<Func<T, bool>> first,
Expression<Func<T, bool>> second)
{
var invokedExpr = Expression.Invoke(second, first.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(
Expression.AndAlso(first.Body, invokedExpr),
first.Parameters
);
}
public static Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> first,
Expression<Func<T, bool>> second)
{
var invokedExpr = Expression.Invoke(second, first.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(
Expression.OrElse(first.Body, invokedExpr),
first.Parameters
);
}
public static Expression<Func<T, bool>> True<T>() => f => true;
public static Expression<Func<T, bool>> False<T>() => f => false;
}
}