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; /// /// 测评管理服务实现 /// public class AssessmentService : IAssessmentService { private readonly AdminBusinessDbContext _dbContext; private readonly ILogger _logger; /// /// 测评类型状态名称映射 /// private static readonly Dictionary TypeStatusNames = new() { { 0, "已下线" }, { 1, "已上线" }, { 2, "即将上线" } }; /// /// 构造函数 /// /// 数据库上下文 /// 日志记录器 public AssessmentService( AdminBusinessDbContext dbContext, ILogger logger) { _dbContext = dbContext; _logger = logger; } #region 测评类型操作 /// public async Task> 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.Create(items, total, request.Page, request.PageSize); } /// public async Task 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; } /// public async Task 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; } /// public async Task 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; } /// public async Task 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; } /// public async Task 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; } /// public async Task 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 题目操作 /// /// 题目状态名称映射 /// private static readonly Dictionary QuestionStatusNames = new() { { 0, "禁用" }, { 1, "启用" } }; /// public async Task> 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.Create(questions, total, request.Page, request.PageSize); } /// public async Task 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; } /// public async Task 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; } /// public async Task 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; } /// public async Task 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; } /// public async Task 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; } /// public async Task BatchImportQuestionsAsync(BatchImportQuestionsRequest request) { var result = new BatchImportResult { TotalCount = request.Questions.Count, SuccessCount = 0, FailedCount = 0, Errors = new List() }; // 验证测评类型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(); var questionsToAdd = new List(); 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; } /// public async Task 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 报告分类操作 /// /// 分类类型名称映射 /// private static readonly Dictionary CategoryTypeNames = new() { { 1, "八大智能" }, { 2, "个人特质" }, { 3, "细分能力" }, { 4, "先天学习" }, { 5, "学习能力" }, { 6, "大脑类型" }, { 7, "性格类型" }, { 8, "未来能力" } }; /// /// 计分规则名称映射 /// private static readonly Dictionary ScoreRuleNames = new() { { 1, "累加(1-10分)" }, { 2, "二值(0/1分)" } }; /// public async Task> 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() }) .ToListAsync(); // 构建树形结构 return BuildCategoryTree(categories); } /// public async Task 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() }) .FirstOrDefaultAsync(); if (category == null) { throw new BusinessException(ErrorCodes.CategoryNotFound, "分类不存在"); } return category; } /// public async Task 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; } /// public async Task 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; } /// public async Task 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; } /// public async Task HasChildCategoriesAsync(long categoryId) { return await _dbContext.ReportCategories .AnyAsync(c => c.ParentId == categoryId && !c.IsDeleted); } #endregion #region 私有方法 /// /// 获取测评类型状态名称 /// /// 状态值 /// 状态名称 private static string GetTypeStatusName(int status) { return TypeStatusNames.TryGetValue(status, out var name) ? name : "未知"; } /// /// 获取题目状态名称 /// /// 状态值 /// 状态名称 private static string GetQuestionStatusName(int status) { return QuestionStatusNames.TryGetValue(status, out var name) ? name : "未知"; } /// /// 获取分类类型名称 /// /// 分类类型值 /// 分类类型名称 private static string GetCategoryTypeName(int categoryType) { return CategoryTypeNames.TryGetValue(categoryType, out var name) ? name : "未知"; } /// /// 获取计分规则名称 /// /// 计分规则值 /// 计分规则名称 private static string GetScoreRuleName(int scoreRule) { return ScoreRuleNames.TryGetValue(scoreRule, out var name) ? name : "未知"; } /// /// 构建分类树形结构 /// /// 所有分类列表 /// 树形结构列表(顶级分类) private static List BuildCategoryTree(List categories) { // 创建字典以便快速查找 var categoryDict = categories.ToDictionary(c => c.Id); // 顶级分类列表 var rootCategories = new List(); 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 题目分类映射操作 /// public async Task> 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; } /// public async Task> 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; } /// public async Task 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 报告结论操作 /// /// 结论类型名称映射 /// private static readonly Dictionary ConclusionTypeNames = new() { { 1, "最强" }, { 2, "较强" }, { 3, "较弱" }, { 4, "最弱" } }; /// public async Task> 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; } /// public async Task 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; } /// public async Task 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; } /// public async Task 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; } /// public async Task 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; } /// /// 获取结论类型名称 /// /// 结论类型值 /// 结论类型名称 private static string GetConclusionTypeName(int conclusionType) { return ConclusionTypeNames.TryGetValue(conclusionType, out var name) ? name : "未知"; } #endregion }