重构项目结构,移除Assignment相关功能,优化Submission模块
Some checks failed
TechAct / explore-gitea-actions (push) Failing after 12s

This commit is contained in:
SpecialX
2025-10-09 18:57:28 +08:00
parent 403b34a098
commit ac900159ba
289 changed files with 11948 additions and 20150 deletions

View File

@@ -0,0 +1,11 @@
using Entities.Contracts;
using Entities.DTO;
namespace TechHelper.Services.Beta
{
public interface IQuestionService : IBaseService<QuestionDto, Guid>
{
Task<ApiResponse> FindByTitle(string title);
Task<ApiResponse> CheckTitlesExistence(IEnumerable<string> titles);
}
}

View File

@@ -0,0 +1,302 @@
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;
}
}