mi-assessment/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AssessmentService.cs
2026-02-20 19:12:08 +08:00

1470 lines
48 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.Assessment;
using MiAssessment.Admin.Business.Models.Common;
using MiAssessment.Admin.Business.Services.Interfaces;
namespace MiAssessment.Admin.Business.Services;
/// <summary>
/// 测评管理服务实现
/// </summary>
public class AssessmentService : IAssessmentService
{
private readonly AdminBusinessDbContext _dbContext;
private readonly ILogger<AssessmentService> _logger;
/// <summary>
/// 测评类型状态名称映射
/// </summary>
private static readonly Dictionary<int, string> TypeStatusNames = new()
{
{ 0, "已下线" },
{ 1, "已上线" },
{ 2, "即将上线" }
};
/// <summary>
/// 构造函数
/// </summary>
/// <param name="dbContext">数据库上下文</param>
/// <param name="logger">日志记录器</param>
public AssessmentService(
AdminBusinessDbContext dbContext,
ILogger<AssessmentService> logger)
{
_dbContext = dbContext;
_logger = logger;
}
#region
/// <inheritdoc />
public async Task<PagedResult<AssessmentTypeDto>> GetTypeListAsync(AssessmentTypeQueryRequest request)
{
// 构建查询,过滤软删除记录
var query = _dbContext.AssessmentTypes
.AsNoTracking()
.Where(t => !t.IsDeleted);
// 名称筛选
if (!string.IsNullOrWhiteSpace(request.Name))
{
query = query.Where(t => t.Name.Contains(request.Name));
}
// 编码筛选
if (!string.IsNullOrWhiteSpace(request.Code))
{
query = query.Where(t => t.Code.Contains(request.Code));
}
// 状态筛选
if (request.Status.HasValue)
{
query = query.Where(t => t.Status == request.Status.Value);
}
// 获取总数
var total = await query.CountAsync();
// 分页查询,按 Sort 降序排列
var items = await query
.OrderByDescending(t => t.Sort)
.ThenByDescending(t => t.CreateTime)
.Skip(request.Skip)
.Take(request.PageSize)
.Select(t => new AssessmentTypeDto
{
Id = t.Id,
Name = t.Name,
Code = t.Code,
ImageUrl = t.ImageUrl,
DetailImageUrl = t.DetailImageUrl,
IntroContent = t.IntroContent,
Price = t.Price,
QuestionCount = t.QuestionCount,
Sort = t.Sort,
Status = t.Status,
StatusName = GetTypeStatusName(t.Status),
CreateTime = t.CreateTime
})
.ToListAsync();
return PagedResult<AssessmentTypeDto>.Create(items, total, request.Page, request.PageSize);
}
/// <inheritdoc />
public async Task<AssessmentTypeDto> GetTypeByIdAsync(long id)
{
var assessmentType = await _dbContext.AssessmentTypes
.AsNoTracking()
.Where(t => t.Id == id && !t.IsDeleted)
.Select(t => new AssessmentTypeDto
{
Id = t.Id,
Name = t.Name,
Code = t.Code,
ImageUrl = t.ImageUrl,
DetailImageUrl = t.DetailImageUrl,
IntroContent = t.IntroContent,
Price = t.Price,
QuestionCount = t.QuestionCount,
Sort = t.Sort,
Status = t.Status,
StatusName = GetTypeStatusName(t.Status),
CreateTime = t.CreateTime
})
.FirstOrDefaultAsync();
if (assessmentType == null)
{
throw new BusinessException(ErrorCodes.AssessmentTypeNotFound, "测评类型不存在");
}
return assessmentType;
}
/// <inheritdoc />
public async Task<long> CreateTypeAsync(CreateAssessmentTypeRequest request)
{
// 验证名称必填
if (string.IsNullOrWhiteSpace(request.Name))
{
throw new BusinessException(ErrorCodes.ParamError, "测评名称不能为空");
}
// 验证编码必填
if (string.IsNullOrWhiteSpace(request.Code))
{
throw new BusinessException(ErrorCodes.ParamError, "测评编码不能为空");
}
// 验证价格必须为正数
if (request.Price <= 0)
{
throw new BusinessException(ErrorCodes.ParamError, "价格必须大于0");
}
// 验证编码唯一性
if (await IsCodeExistsAsync(request.Code))
{
throw new BusinessException(ErrorCodes.AssessmentTypeCodeExists, "测评类型编码已存在");
}
// 创建实体
var assessmentType = new AssessmentType
{
Name = request.Name,
Code = request.Code,
ImageUrl = request.ImageUrl,
DetailImageUrl = request.DetailImageUrl,
IntroContent = request.IntroContent,
Price = request.Price,
QuestionCount = request.QuestionCount,
Sort = request.Sort,
Status = request.Status,
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now,
IsDeleted = false
};
_dbContext.AssessmentTypes.Add(assessmentType);
await _dbContext.SaveChangesAsync();
_logger.LogInformation("创建测评类型成功ID: {TypeId}, 名称: {Name}, 编码: {Code}",
assessmentType.Id, assessmentType.Name, assessmentType.Code);
return assessmentType.Id;
}
/// <inheritdoc />
public async Task<bool> UpdateTypeAsync(UpdateAssessmentTypeRequest request)
{
// 查找测评类型
var assessmentType = await _dbContext.AssessmentTypes
.Where(t => t.Id == request.Id && !t.IsDeleted)
.FirstOrDefaultAsync();
if (assessmentType == null)
{
throw new BusinessException(ErrorCodes.AssessmentTypeNotFound, "测评类型不存在");
}
// 验证名称必填
if (string.IsNullOrWhiteSpace(request.Name))
{
throw new BusinessException(ErrorCodes.ParamError, "测评名称不能为空");
}
// 验证编码必填
if (string.IsNullOrWhiteSpace(request.Code))
{
throw new BusinessException(ErrorCodes.ParamError, "测评编码不能为空");
}
// 验证价格必须为正数
if (request.Price <= 0)
{
throw new BusinessException(ErrorCodes.ParamError, "价格必须大于0");
}
// 验证编码唯一性(排除自身)
if (await IsCodeExistsAsync(request.Code, request.Id))
{
throw new BusinessException(ErrorCodes.AssessmentTypeCodeExists, "测评类型编码已存在");
}
// 更新字段
assessmentType.Name = request.Name;
assessmentType.Code = request.Code;
assessmentType.ImageUrl = request.ImageUrl;
assessmentType.DetailImageUrl = request.DetailImageUrl;
assessmentType.IntroContent = request.IntroContent;
assessmentType.Price = request.Price;
assessmentType.QuestionCount = request.QuestionCount;
assessmentType.Sort = request.Sort;
assessmentType.Status = request.Status;
assessmentType.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation("更新测评类型成功ID: {TypeId}", assessmentType.Id);
return true;
}
/// <inheritdoc />
public async Task<bool> DeleteTypeAsync(long id)
{
// 查找测评类型
var assessmentType = await _dbContext.AssessmentTypes
.Where(t => t.Id == id && !t.IsDeleted)
.FirstOrDefaultAsync();
if (assessmentType == null)
{
throw new BusinessException(ErrorCodes.AssessmentTypeNotFound, "测评类型不存在");
}
// 软删除
assessmentType.IsDeleted = true;
assessmentType.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation("删除测评类型成功ID: {TypeId}", id);
return true;
}
/// <inheritdoc />
public async Task<bool> UpdateTypeStatusAsync(long id, int status)
{
// 查找测评类型
var assessmentType = await _dbContext.AssessmentTypes
.Where(t => t.Id == id && !t.IsDeleted)
.FirstOrDefaultAsync();
if (assessmentType == null)
{
throw new BusinessException(ErrorCodes.AssessmentTypeNotFound, "测评类型不存在");
}
// 更新状态
assessmentType.Status = status;
assessmentType.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation("更新测评类型状态成功ID: {TypeId}, 状态: {Status}", id, status);
return true;
}
/// <inheritdoc />
public async Task<bool> IsCodeExistsAsync(string code, long? excludeId = null)
{
var query = _dbContext.AssessmentTypes
.Where(t => t.Code == code && !t.IsDeleted);
if (excludeId.HasValue)
{
query = query.Where(t => t.Id != excludeId.Value);
}
return await query.AnyAsync();
}
#endregion
#region
/// <summary>
/// 题目状态名称映射
/// </summary>
private static readonly Dictionary<int, string> QuestionStatusNames = new()
{
{ 0, "禁用" },
{ 1, "启用" }
};
/// <inheritdoc />
public async Task<PagedResult<QuestionDto>> GetQuestionListAsync(QuestionQueryRequest request)
{
// 构建查询,过滤软删除记录
var query = _dbContext.Questions
.AsNoTracking()
.Where(q => !q.IsDeleted);
// 测评类型筛选
if (request.AssessmentTypeId.HasValue)
{
query = query.Where(q => q.AssessmentTypeId == request.AssessmentTypeId.Value);
}
// 题号筛选
if (request.QuestionNo.HasValue)
{
query = query.Where(q => q.QuestionNo == request.QuestionNo.Value);
}
// 内容筛选
if (!string.IsNullOrWhiteSpace(request.Content))
{
query = query.Where(q => q.Content.Contains(request.Content));
}
// 状态筛选
if (request.Status.HasValue)
{
query = query.Where(q => q.Status == request.Status.Value);
}
// 获取总数
var total = await query.CountAsync();
// 分页查询,按 QuestionNo 升序排列
var questions = await query
.OrderBy(q => q.QuestionNo)
.ThenByDescending(q => q.CreateTime)
.Skip(request.Skip)
.Take(request.PageSize)
.Select(q => new QuestionDto
{
Id = q.Id,
AssessmentTypeId = q.AssessmentTypeId,
AssessmentTypeName = q.AssessmentType != null ? q.AssessmentType.Name : "",
QuestionNo = q.QuestionNo,
Content = q.Content,
Sort = q.Sort,
Status = q.Status,
StatusName = GetQuestionStatusName(q.Status),
CreateTime = q.CreateTime
})
.ToListAsync();
// 获取这些题目的分类数量
if (questions.Count > 0)
{
var questionIds = questions.Select(q => q.Id).ToList();
var categoryCounts = await _dbContext.QuestionCategoryMappings
.Where(m => questionIds.Contains(m.QuestionId))
.GroupBy(m => m.QuestionId)
.Select(g => new { QuestionId = g.Key, Count = g.Count() })
.ToDictionaryAsync(x => x.QuestionId, x => x.Count);
foreach (var question in questions)
{
question.CategoryCount = categoryCounts.TryGetValue(question.Id, out var count) ? count : 0;
}
}
return PagedResult<QuestionDto>.Create(questions, total, request.Page, request.PageSize);
}
/// <inheritdoc />
public async Task<QuestionDto> GetQuestionByIdAsync(long id)
{
var question = await _dbContext.Questions
.AsNoTracking()
.Where(q => q.Id == id && !q.IsDeleted)
.Select(q => new QuestionDto
{
Id = q.Id,
AssessmentTypeId = q.AssessmentTypeId,
AssessmentTypeName = q.AssessmentType != null ? q.AssessmentType.Name : "",
QuestionNo = q.QuestionNo,
Content = q.Content,
Sort = q.Sort,
Status = q.Status,
StatusName = GetQuestionStatusName(q.Status),
CreateTime = q.CreateTime
})
.FirstOrDefaultAsync();
if (question == null)
{
throw new BusinessException(ErrorCodes.QuestionNotFound, "题目不存在");
}
return question;
}
/// <inheritdoc />
public async Task<long> CreateQuestionAsync(CreateQuestionRequest request)
{
// 验证测评类型ID必填
if (request.AssessmentTypeId <= 0)
{
throw new BusinessException(ErrorCodes.ParamError, "测评类型ID无效");
}
// 验证题号必填且大于0
if (request.QuestionNo <= 0)
{
throw new BusinessException(ErrorCodes.ParamError, "题号必须大于0");
}
// 验证内容必填
if (string.IsNullOrWhiteSpace(request.Content))
{
throw new BusinessException(ErrorCodes.ParamError, "题目内容不能为空");
}
// 验证测评类型是否存在
var assessmentTypeExists = await _dbContext.AssessmentTypes
.AnyAsync(t => t.Id == request.AssessmentTypeId && !t.IsDeleted);
if (!assessmentTypeExists)
{
throw new BusinessException(ErrorCodes.AssessmentTypeNotFound, "测评类型不存在");
}
// 验证题号唯一性(同一测评类型内)
if (await IsQuestionNoExistsAsync(request.AssessmentTypeId, request.QuestionNo))
{
throw new BusinessException(ErrorCodes.QuestionNoExists, "该测评类型下题号已存在");
}
// 创建实体
var question = new Question
{
AssessmentTypeId = request.AssessmentTypeId,
QuestionNo = request.QuestionNo,
Content = request.Content,
Sort = request.Sort,
Status = request.Status,
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now,
IsDeleted = false
};
_dbContext.Questions.Add(question);
await _dbContext.SaveChangesAsync();
_logger.LogInformation("创建题目成功ID: {QuestionId}, 测评类型ID: {TypeId}, 题号: {QuestionNo}",
question.Id, question.AssessmentTypeId, question.QuestionNo);
return question.Id;
}
/// <inheritdoc />
public async Task<bool> UpdateQuestionAsync(UpdateQuestionRequest request)
{
// 查找题目
var question = await _dbContext.Questions
.Where(q => q.Id == request.Id && !q.IsDeleted)
.FirstOrDefaultAsync();
if (question == null)
{
throw new BusinessException(ErrorCodes.QuestionNotFound, "题目不存在");
}
// 验证测评类型ID必填
if (request.AssessmentTypeId <= 0)
{
throw new BusinessException(ErrorCodes.ParamError, "测评类型ID无效");
}
// 验证题号必填且大于0
if (request.QuestionNo <= 0)
{
throw new BusinessException(ErrorCodes.ParamError, "题号必须大于0");
}
// 验证内容必填
if (string.IsNullOrWhiteSpace(request.Content))
{
throw new BusinessException(ErrorCodes.ParamError, "题目内容不能为空");
}
// 验证测评类型是否存在
var assessmentTypeExists = await _dbContext.AssessmentTypes
.AnyAsync(t => t.Id == request.AssessmentTypeId && !t.IsDeleted);
if (!assessmentTypeExists)
{
throw new BusinessException(ErrorCodes.AssessmentTypeNotFound, "测评类型不存在");
}
// 验证题号唯一性(同一测评类型内,排除自身)
if (await IsQuestionNoExistsAsync(request.AssessmentTypeId, request.QuestionNo, request.Id))
{
throw new BusinessException(ErrorCodes.QuestionNoExists, "该测评类型下题号已存在");
}
// 更新字段
question.AssessmentTypeId = request.AssessmentTypeId;
question.QuestionNo = request.QuestionNo;
question.Content = request.Content;
question.Sort = request.Sort;
question.Status = request.Status;
question.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation("更新题目成功ID: {QuestionId}", question.Id);
return true;
}
/// <inheritdoc />
public async Task<bool> DeleteQuestionAsync(long id)
{
// 查找题目
var question = await _dbContext.Questions
.Where(q => q.Id == id && !q.IsDeleted)
.FirstOrDefaultAsync();
if (question == null)
{
throw new BusinessException(ErrorCodes.QuestionNotFound, "题目不存在");
}
// 软删除
question.IsDeleted = true;
question.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation("删除题目成功ID: {QuestionId}", id);
return true;
}
/// <inheritdoc />
public async Task<bool> UpdateQuestionStatusAsync(long id, int status)
{
// 查找题目
var question = await _dbContext.Questions
.Where(q => q.Id == id && !q.IsDeleted)
.FirstOrDefaultAsync();
if (question == null)
{
throw new BusinessException(ErrorCodes.QuestionNotFound, "题目不存在");
}
// 更新状态
question.Status = status;
question.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation("更新题目状态成功ID: {QuestionId}, 状态: {Status}", id, status);
return true;
}
/// <inheritdoc />
public async Task<BatchImportResult> BatchImportQuestionsAsync(BatchImportQuestionsRequest request)
{
var result = new BatchImportResult
{
TotalCount = request.Questions.Count,
SuccessCount = 0,
FailedCount = 0,
Errors = new List<BatchImportError>()
};
// 验证测评类型ID
if (request.AssessmentTypeId <= 0)
{
throw new BusinessException(ErrorCodes.ParamError, "测评类型ID无效");
}
// 验证测评类型是否存在
var assessmentTypeExists = await _dbContext.AssessmentTypes
.AnyAsync(t => t.Id == request.AssessmentTypeId && !t.IsDeleted);
if (!assessmentTypeExists)
{
throw new BusinessException(ErrorCodes.AssessmentTypeNotFound, "测评类型不存在");
}
// 获取该测评类型下已存在的题号
var existingQuestionNos = await _dbContext.Questions
.Where(q => q.AssessmentTypeId == request.AssessmentTypeId && !q.IsDeleted)
.Select(q => q.QuestionNo)
.ToListAsync();
// 检查导入数据中的重复题号
var importQuestionNos = new HashSet<int>();
var questionsToAdd = new List<Question>();
for (int i = 0; i < request.Questions.Count; i++)
{
var item = request.Questions[i];
var rowIndex = i + 1;
// 验证题号
if (item.QuestionNo <= 0)
{
result.Errors.Add(new BatchImportError
{
RowIndex = rowIndex,
QuestionNo = item.QuestionNo,
ErrorMessage = "题号必须大于0"
});
result.FailedCount++;
continue;
}
// 验证内容
if (string.IsNullOrWhiteSpace(item.Content))
{
result.Errors.Add(new BatchImportError
{
RowIndex = rowIndex,
QuestionNo = item.QuestionNo,
ErrorMessage = "题目内容不能为空"
});
result.FailedCount++;
continue;
}
// 检查是否与数据库中已存在的题号重复
if (existingQuestionNos.Contains(item.QuestionNo))
{
result.Errors.Add(new BatchImportError
{
RowIndex = rowIndex,
QuestionNo = item.QuestionNo,
ErrorMessage = "题号已存在"
});
result.FailedCount++;
continue;
}
// 检查是否与本次导入中的其他题号重复
if (importQuestionNos.Contains(item.QuestionNo))
{
result.Errors.Add(new BatchImportError
{
RowIndex = rowIndex,
QuestionNo = item.QuestionNo,
ErrorMessage = "导入数据中存在重复题号"
});
result.FailedCount++;
continue;
}
importQuestionNos.Add(item.QuestionNo);
// 创建题目实体
var question = new Question
{
AssessmentTypeId = request.AssessmentTypeId,
QuestionNo = item.QuestionNo,
Content = item.Content,
Sort = item.Sort,
Status = item.Status,
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now,
IsDeleted = false
};
questionsToAdd.Add(question);
result.SuccessCount++;
}
// 批量添加题目
if (questionsToAdd.Count > 0)
{
_dbContext.Questions.AddRange(questionsToAdd);
await _dbContext.SaveChangesAsync();
_logger.LogInformation("批量导入题目成功测评类型ID: {TypeId}, 成功: {SuccessCount}, 失败: {FailedCount}",
request.AssessmentTypeId, result.SuccessCount, result.FailedCount);
}
return result;
}
/// <inheritdoc />
public async Task<bool> IsQuestionNoExistsAsync(long assessmentTypeId, int questionNo, long? excludeId = null)
{
var query = _dbContext.Questions
.Where(q => q.AssessmentTypeId == assessmentTypeId && q.QuestionNo == questionNo && !q.IsDeleted);
if (excludeId.HasValue)
{
query = query.Where(q => q.Id != excludeId.Value);
}
return await query.AnyAsync();
}
#endregion
#region
/// <summary>
/// 分类类型名称映射
/// </summary>
private static readonly Dictionary<int, string> CategoryTypeNames = new()
{
{ 1, "八大智能" },
{ 2, "个人特质" },
{ 3, "细分能力" },
{ 4, "先天学习" },
{ 5, "学习能力" },
{ 6, "大脑类型" },
{ 7, "性格类型" },
{ 8, "未来能力" }
};
/// <summary>
/// 计分规则名称映射
/// </summary>
private static readonly Dictionary<int, string> ScoreRuleNames = new()
{
{ 1, "累加(1-10分)" },
{ 2, "二值(0/1分)" }
};
/// <inheritdoc />
public async Task<List<CategoryTreeNode>> GetCategoryTreeAsync(long assessmentTypeId)
{
// 获取该测评类型下所有未删除的分类
var categories = await _dbContext.ReportCategories
.AsNoTracking()
.Where(c => c.AssessmentTypeId == assessmentTypeId && !c.IsDeleted)
.OrderBy(c => c.Sort)
.ThenBy(c => c.CreateTime)
.Select(c => new CategoryTreeNode
{
Id = c.Id,
ParentId = c.ParentId,
AssessmentTypeId = c.AssessmentTypeId,
Name = c.Name,
Code = c.Code,
CategoryType = c.CategoryType,
CategoryTypeName = GetCategoryTypeName(c.CategoryType),
ScoreRule = c.ScoreRule,
ScoreRuleName = GetScoreRuleName(c.ScoreRule),
Sort = c.Sort,
CreateTime = c.CreateTime,
Children = new List<CategoryTreeNode>()
})
.ToListAsync();
// 构建树形结构
return BuildCategoryTree(categories);
}
/// <inheritdoc />
public async Task<CategoryTreeNode> GetCategoryByIdAsync(long id)
{
var category = await _dbContext.ReportCategories
.AsNoTracking()
.Where(c => c.Id == id && !c.IsDeleted)
.Select(c => new CategoryTreeNode
{
Id = c.Id,
ParentId = c.ParentId,
AssessmentTypeId = c.AssessmentTypeId,
Name = c.Name,
Code = c.Code,
CategoryType = c.CategoryType,
CategoryTypeName = GetCategoryTypeName(c.CategoryType),
ScoreRule = c.ScoreRule,
ScoreRuleName = GetScoreRuleName(c.ScoreRule),
Sort = c.Sort,
CreateTime = c.CreateTime,
Children = new List<CategoryTreeNode>()
})
.FirstOrDefaultAsync();
if (category == null)
{
throw new BusinessException(ErrorCodes.CategoryNotFound, "分类不存在");
}
return category;
}
/// <inheritdoc />
public async Task<long> CreateCategoryAsync(CreateCategoryRequest request)
{
// 验证名称必填
if (string.IsNullOrWhiteSpace(request.Name))
{
throw new BusinessException(ErrorCodes.ParamError, "分类名称不能为空");
}
// 验证编码必填
if (string.IsNullOrWhiteSpace(request.Code))
{
throw new BusinessException(ErrorCodes.ParamError, "分类编码不能为空");
}
// 验证分类类型有效性
if (request.CategoryType < 1 || request.CategoryType > 8)
{
throw new BusinessException(ErrorCodes.ParamError, "分类类型无效只能为1-8");
}
// 验证计分规则有效性
if (request.ScoreRule < 1 || request.ScoreRule > 2)
{
throw new BusinessException(ErrorCodes.ParamError, "计分规则无效只能为1或2");
}
// 验证测评类型ID
if (request.AssessmentTypeId <= 0)
{
throw new BusinessException(ErrorCodes.ParamError, "测评类型ID无效");
}
// 验证测评类型是否存在
var assessmentTypeExists = await _dbContext.AssessmentTypes
.AnyAsync(t => t.Id == request.AssessmentTypeId && !t.IsDeleted);
if (!assessmentTypeExists)
{
throw new BusinessException(ErrorCodes.AssessmentTypeNotFound, "测评类型不存在");
}
// 如果有父分类,验证父分类是否存在
if (request.ParentId > 0)
{
var parentExists = await _dbContext.ReportCategories
.AnyAsync(c => c.Id == request.ParentId && !c.IsDeleted);
if (!parentExists)
{
throw new BusinessException(ErrorCodes.CategoryNotFound, "父分类不存在");
}
}
// 创建实体
var category = new ReportCategory
{
AssessmentTypeId = request.AssessmentTypeId,
ParentId = request.ParentId,
Name = request.Name,
Code = request.Code,
CategoryType = request.CategoryType,
ScoreRule = request.ScoreRule,
Sort = request.Sort,
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now,
IsDeleted = false
};
_dbContext.ReportCategories.Add(category);
await _dbContext.SaveChangesAsync();
_logger.LogInformation("创建分类成功ID: {CategoryId}, 名称: {Name}, 编码: {Code}",
category.Id, category.Name, category.Code);
return category.Id;
}
/// <inheritdoc />
public async Task<bool> UpdateCategoryAsync(UpdateCategoryRequest request)
{
// 查找分类
var category = await _dbContext.ReportCategories
.Where(c => c.Id == request.Id && !c.IsDeleted)
.FirstOrDefaultAsync();
if (category == null)
{
throw new BusinessException(ErrorCodes.CategoryNotFound, "分类不存在");
}
// 验证名称必填
if (string.IsNullOrWhiteSpace(request.Name))
{
throw new BusinessException(ErrorCodes.ParamError, "分类名称不能为空");
}
// 验证编码必填
if (string.IsNullOrWhiteSpace(request.Code))
{
throw new BusinessException(ErrorCodes.ParamError, "分类编码不能为空");
}
// 验证分类类型有效性
if (request.CategoryType < 1 || request.CategoryType > 8)
{
throw new BusinessException(ErrorCodes.ParamError, "分类类型无效只能为1-8");
}
// 验证计分规则有效性
if (request.ScoreRule < 1 || request.ScoreRule > 2)
{
throw new BusinessException(ErrorCodes.ParamError, "计分规则无效只能为1或2");
}
// 验证测评类型ID
if (request.AssessmentTypeId <= 0)
{
throw new BusinessException(ErrorCodes.ParamError, "测评类型ID无效");
}
// 验证测评类型是否存在
var assessmentTypeExists = await _dbContext.AssessmentTypes
.AnyAsync(t => t.Id == request.AssessmentTypeId && !t.IsDeleted);
if (!assessmentTypeExists)
{
throw new BusinessException(ErrorCodes.AssessmentTypeNotFound, "测评类型不存在");
}
// 如果有父分类,验证父分类是否存在
if (request.ParentId > 0)
{
// 不能将自己设为父分类
if (request.ParentId == request.Id)
{
throw new BusinessException(ErrorCodes.ParamError, "不能将自己设为父分类");
}
var parentExists = await _dbContext.ReportCategories
.AnyAsync(c => c.Id == request.ParentId && !c.IsDeleted);
if (!parentExists)
{
throw new BusinessException(ErrorCodes.CategoryNotFound, "父分类不存在");
}
}
// 更新字段
category.AssessmentTypeId = request.AssessmentTypeId;
category.ParentId = request.ParentId;
category.Name = request.Name;
category.Code = request.Code;
category.CategoryType = request.CategoryType;
category.ScoreRule = request.ScoreRule;
category.Sort = request.Sort;
category.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation("更新分类成功ID: {CategoryId}", category.Id);
return true;
}
/// <inheritdoc />
public async Task<bool> DeleteCategoryAsync(long id)
{
// 查找分类
var category = await _dbContext.ReportCategories
.Where(c => c.Id == id && !c.IsDeleted)
.FirstOrDefaultAsync();
if (category == null)
{
throw new BusinessException(ErrorCodes.CategoryNotFound, "分类不存在");
}
// 检查是否存在子分类
if (await HasChildCategoriesAsync(id))
{
throw new BusinessException(ErrorCodes.CategoryHasChildren, "该分类存在子分类,无法删除");
}
// 软删除
category.IsDeleted = true;
category.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation("删除分类成功ID: {CategoryId}", id);
return true;
}
/// <inheritdoc />
public async Task<bool> HasChildCategoriesAsync(long categoryId)
{
return await _dbContext.ReportCategories
.AnyAsync(c => c.ParentId == categoryId && !c.IsDeleted);
}
#endregion
#region
/// <summary>
/// 获取测评类型状态名称
/// </summary>
/// <param name="status">状态值</param>
/// <returns>状态名称</returns>
private static string GetTypeStatusName(int status)
{
return TypeStatusNames.TryGetValue(status, out var name) ? name : "未知";
}
/// <summary>
/// 获取题目状态名称
/// </summary>
/// <param name="status">状态值</param>
/// <returns>状态名称</returns>
private static string GetQuestionStatusName(int status)
{
return QuestionStatusNames.TryGetValue(status, out var name) ? name : "未知";
}
/// <summary>
/// 获取分类类型名称
/// </summary>
/// <param name="categoryType">分类类型值</param>
/// <returns>分类类型名称</returns>
private static string GetCategoryTypeName(int categoryType)
{
return CategoryTypeNames.TryGetValue(categoryType, out var name) ? name : "未知";
}
/// <summary>
/// 获取计分规则名称
/// </summary>
/// <param name="scoreRule">计分规则值</param>
/// <returns>计分规则名称</returns>
private static string GetScoreRuleName(int scoreRule)
{
return ScoreRuleNames.TryGetValue(scoreRule, out var name) ? name : "未知";
}
/// <summary>
/// 构建分类树形结构
/// </summary>
/// <param name="categories">所有分类列表</param>
/// <returns>树形结构列表(顶级分类)</returns>
private static List<CategoryTreeNode> BuildCategoryTree(List<CategoryTreeNode> categories)
{
// 创建字典以便快速查找
var categoryDict = categories.ToDictionary(c => c.Id);
// 顶级分类列表
var rootCategories = new List<CategoryTreeNode>();
foreach (var category in categories)
{
if (category.ParentId == 0)
{
// 顶级分类
rootCategories.Add(category);
}
else
{
// 子分类,添加到父分类的 Children 列表
if (categoryDict.TryGetValue(category.ParentId, out var parent))
{
parent.Children.Add(category);
}
}
}
return rootCategories;
}
#endregion
#region
/// <inheritdoc />
public async Task<List<MappingCategoryDto>> GetMappingsByQuestionAsync(long questionId)
{
// 验证题目是否存在
var questionExists = await _dbContext.Questions
.AnyAsync(q => q.Id == questionId && !q.IsDeleted);
if (!questionExists)
{
throw new BusinessException(ErrorCodes.QuestionNotFound, "题目不存在");
}
// 获取该题目关联的所有分类
var mappings = await _dbContext.QuestionCategoryMappings
.AsNoTracking()
.Where(m => m.QuestionId == questionId)
.Join(
_dbContext.ReportCategories.Where(c => !c.IsDeleted),
m => m.CategoryId,
c => c.Id,
(m, c) => new MappingCategoryDto
{
Id = c.Id,
Name = c.Name,
Code = c.Code,
CategoryType = c.CategoryType,
CategoryTypeName = GetCategoryTypeName(c.CategoryType),
MappingCreateTime = m.CreateTime
})
.OrderBy(c => c.CategoryType)
.ThenBy(c => c.Name)
.ToListAsync();
return mappings;
}
/// <inheritdoc />
public async Task<List<MappingQuestionDto>> GetMappingsByCategoryAsync(long categoryId)
{
// 验证分类是否存在
var categoryExists = await _dbContext.ReportCategories
.AnyAsync(c => c.Id == categoryId && !c.IsDeleted);
if (!categoryExists)
{
throw new BusinessException(ErrorCodes.CategoryNotFound, "分类不存在");
}
// 获取该分类关联的所有题目
var mappings = await _dbContext.QuestionCategoryMappings
.AsNoTracking()
.Where(m => m.CategoryId == categoryId)
.Join(
_dbContext.Questions.Where(q => !q.IsDeleted),
m => m.QuestionId,
q => q.Id,
(m, q) => new { Mapping = m, Question = q })
.Join(
_dbContext.AssessmentTypes.Where(t => !t.IsDeleted),
mq => mq.Question.AssessmentTypeId,
t => t.Id,
(mq, t) => new MappingQuestionDto
{
Id = mq.Question.Id,
QuestionNo = mq.Question.QuestionNo,
Content = mq.Question.Content,
AssessmentTypeId = mq.Question.AssessmentTypeId,
AssessmentTypeName = t.Name,
MappingCreateTime = mq.Mapping.CreateTime
})
.OrderBy(q => q.AssessmentTypeId)
.ThenBy(q => q.QuestionNo)
.ToListAsync();
return mappings;
}
/// <inheritdoc />
public async Task<bool> BatchUpdateMappingsAsync(BatchUpdateMappingsRequest request)
{
// 验证题目ID
if (request.QuestionId <= 0)
{
throw new BusinessException(ErrorCodes.ParamError, "题目ID无效");
}
// 验证题目是否存在
var questionExists = await _dbContext.Questions
.AnyAsync(q => q.Id == request.QuestionId && !q.IsDeleted);
if (!questionExists)
{
throw new BusinessException(ErrorCodes.QuestionNotFound, "题目不存在");
}
// 验证所有分类ID是否存在
if (request.CategoryIds != null && request.CategoryIds.Count > 0)
{
// 去重
var uniqueCategoryIds = request.CategoryIds.Distinct().ToList();
var existingCategoryIds = await _dbContext.ReportCategories
.Where(c => uniqueCategoryIds.Contains(c.Id) && !c.IsDeleted)
.Select(c => c.Id)
.ToListAsync();
var invalidCategoryIds = uniqueCategoryIds.Except(existingCategoryIds).ToList();
if (invalidCategoryIds.Count > 0)
{
throw new BusinessException(ErrorCodes.CategoryNotFound,
$"以下分类ID不存在: {string.Join(", ", invalidCategoryIds)}");
}
}
// 使用事务确保原子性
using var transaction = await _dbContext.Database.BeginTransactionAsync();
try
{
// 删除该题目的所有现有映射
var existingMappings = await _dbContext.QuestionCategoryMappings
.Where(m => m.QuestionId == request.QuestionId)
.ToListAsync();
if (existingMappings.Count > 0)
{
_dbContext.QuestionCategoryMappings.RemoveRange(existingMappings);
}
// 创建新的映射
if (request.CategoryIds != null && request.CategoryIds.Count > 0)
{
var uniqueCategoryIds = request.CategoryIds.Distinct().ToList();
var newMappings = uniqueCategoryIds.Select(categoryId => new QuestionCategoryMapping
{
QuestionId = request.QuestionId,
CategoryId = categoryId,
CreateTime = DateTime.Now
}).ToList();
_dbContext.QuestionCategoryMappings.AddRange(newMappings);
}
await _dbContext.SaveChangesAsync();
await transaction.CommitAsync();
_logger.LogInformation("批量更新题目分类映射成功题目ID: {QuestionId}, 分类数量: {CategoryCount}",
request.QuestionId, request.CategoryIds?.Count ?? 0);
return true;
}
catch (Exception ex)
{
await transaction.RollbackAsync();
_logger.LogError(ex, "批量更新题目分类映射失败题目ID: {QuestionId}", request.QuestionId);
throw new BusinessException(ErrorCodes.SystemError, "批量更新映射失败,请稍后重试");
}
}
#endregion
#region
/// <summary>
/// 结论类型名称映射
/// </summary>
private static readonly Dictionary<int, string> ConclusionTypeNames = new()
{
{ 1, "最强" },
{ 2, "较强" },
{ 3, "较弱" },
{ 4, "最弱" }
};
/// <inheritdoc />
public async Task<List<ConclusionDto>> GetConclusionListAsync(long categoryId)
{
// 验证分类ID
if (categoryId <= 0)
{
throw new BusinessException(ErrorCodes.ParamError, "分类ID无效");
}
// 验证分类是否存在
var categoryExists = await _dbContext.ReportCategories
.AnyAsync(c => c.Id == categoryId && !c.IsDeleted);
if (!categoryExists)
{
throw new BusinessException(ErrorCodes.CategoryNotFound, "分类不存在");
}
// 获取该分类下的所有结论,按结论类型排序
var conclusions = await _dbContext.ReportConclusions
.AsNoTracking()
.Where(c => c.CategoryId == categoryId && !c.IsDeleted)
.Join(
_dbContext.ReportCategories.Where(cat => !cat.IsDeleted),
c => c.CategoryId,
cat => cat.Id,
(c, cat) => new ConclusionDto
{
Id = c.Id,
CategoryId = c.CategoryId,
CategoryName = cat.Name,
ConclusionType = c.ConclusionType,
ConclusionTypeName = GetConclusionTypeName(c.ConclusionType),
Title = c.Title,
Content = c.Content,
CreateTime = c.CreateTime
})
.OrderBy(c => c.ConclusionType)
.ToListAsync();
return conclusions;
}
/// <inheritdoc />
public async Task<ConclusionDto> GetConclusionByIdAsync(long id)
{
var conclusion = await _dbContext.ReportConclusions
.AsNoTracking()
.Where(c => c.Id == id && !c.IsDeleted)
.Join(
_dbContext.ReportCategories.Where(cat => !cat.IsDeleted),
c => c.CategoryId,
cat => cat.Id,
(c, cat) => new ConclusionDto
{
Id = c.Id,
CategoryId = c.CategoryId,
CategoryName = cat.Name,
ConclusionType = c.ConclusionType,
ConclusionTypeName = GetConclusionTypeName(c.ConclusionType),
Title = c.Title,
Content = c.Content,
CreateTime = c.CreateTime
})
.FirstOrDefaultAsync();
if (conclusion == null)
{
throw new BusinessException(ErrorCodes.ConclusionNotFound, "结论不存在");
}
return conclusion;
}
/// <inheritdoc />
public async Task<long> CreateConclusionAsync(CreateConclusionRequest request)
{
// 验证分类ID
if (request.CategoryId <= 0)
{
throw new BusinessException(ErrorCodes.ParamError, "分类ID无效");
}
// 验证结论类型有效性
if (request.ConclusionType < 1 || request.ConclusionType > 4)
{
throw new BusinessException(ErrorCodes.ParamError, "结论类型无效只能为1最强、2较强、3较弱或4最弱");
}
// 验证内容必填
if (string.IsNullOrWhiteSpace(request.Content))
{
throw new BusinessException(ErrorCodes.ParamError, "结论内容不能为空");
}
// 验证分类是否存在
var categoryExists = await _dbContext.ReportCategories
.AnyAsync(c => c.Id == request.CategoryId && !c.IsDeleted);
if (!categoryExists)
{
throw new BusinessException(ErrorCodes.CategoryNotFound, "分类不存在");
}
// 创建实体
var conclusion = new ReportConclusion
{
CategoryId = request.CategoryId,
ConclusionType = request.ConclusionType,
Title = request.Title,
Content = request.Content,
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now,
IsDeleted = false
};
_dbContext.ReportConclusions.Add(conclusion);
await _dbContext.SaveChangesAsync();
_logger.LogInformation("创建结论成功ID: {ConclusionId}, 分类ID: {CategoryId}, 结论类型: {ConclusionType}",
conclusion.Id, conclusion.CategoryId, conclusion.ConclusionType);
return conclusion.Id;
}
/// <inheritdoc />
public async Task<bool> UpdateConclusionAsync(UpdateConclusionRequest request)
{
// 查找结论
var conclusion = await _dbContext.ReportConclusions
.Where(c => c.Id == request.Id && !c.IsDeleted)
.FirstOrDefaultAsync();
if (conclusion == null)
{
throw new BusinessException(ErrorCodes.ConclusionNotFound, "结论不存在");
}
// 验证分类ID
if (request.CategoryId <= 0)
{
throw new BusinessException(ErrorCodes.ParamError, "分类ID无效");
}
// 验证结论类型有效性
if (request.ConclusionType < 1 || request.ConclusionType > 4)
{
throw new BusinessException(ErrorCodes.ParamError, "结论类型无效只能为1最强、2较强、3较弱或4最弱");
}
// 验证内容必填
if (string.IsNullOrWhiteSpace(request.Content))
{
throw new BusinessException(ErrorCodes.ParamError, "结论内容不能为空");
}
// 验证分类是否存在
var categoryExists = await _dbContext.ReportCategories
.AnyAsync(c => c.Id == request.CategoryId && !c.IsDeleted);
if (!categoryExists)
{
throw new BusinessException(ErrorCodes.CategoryNotFound, "分类不存在");
}
// 更新字段
conclusion.CategoryId = request.CategoryId;
conclusion.ConclusionType = request.ConclusionType;
conclusion.Title = request.Title;
conclusion.Content = request.Content;
conclusion.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation("更新结论成功ID: {ConclusionId}", conclusion.Id);
return true;
}
/// <inheritdoc />
public async Task<bool> DeleteConclusionAsync(long id)
{
// 查找结论
var conclusion = await _dbContext.ReportConclusions
.Where(c => c.Id == id && !c.IsDeleted)
.FirstOrDefaultAsync();
if (conclusion == null)
{
throw new BusinessException(ErrorCodes.ConclusionNotFound, "结论不存在");
}
// 软删除
conclusion.IsDeleted = true;
conclusion.UpdateTime = DateTime.Now;
await _dbContext.SaveChangesAsync();
_logger.LogInformation("删除结论成功ID: {ConclusionId}", id);
return true;
}
/// <summary>
/// 获取结论类型名称
/// </summary>
/// <param name="conclusionType">结论类型值</param>
/// <returns>结论类型名称</returns>
private static string GetConclusionTypeName(int conclusionType)
{
return ConclusionTypeNames.TryGetValue(conclusionType, out var name) ? name : "未知";
}
#endregion
}