diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Controllers/AssessmentRecordController.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Controllers/AssessmentRecordController.cs index 0a243f0..f6ccb9f 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Controllers/AssessmentRecordController.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Controllers/AssessmentRecordController.cs @@ -213,4 +213,38 @@ public class AssessmentRecordController : BusinessControllerBase } } + /// + /// 更新测评记录结论 + /// + /// 更新请求 + /// 操作结果 + [HttpPost("updateConclusion")] + [BusinessPermission(BusinessPermissions.AssessmentRecord.View)] + public async Task UpdateConclusion([FromBody] UpdateRecordConclusionRequest request) + { + if (request.Id <= 0) + { + return ValidationError("结论ID无效"); + } + + if (string.IsNullOrWhiteSpace(request.Content)) + { + return ValidationError("结论内容不能为空"); + } + + try + { + await _assessmentRecordService.UpdateRecordConclusionAsync(request); + return Ok(); + } + catch (BusinessException ex) + { + return Error(ex.Code, ex.Message); + } + catch (Exception) + { + return Error(ErrorCodes.SystemError, "更新结论失败"); + } + } + } diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs index e4de907..d1ef6fa 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Data/AdminBusinessDbContext.cs @@ -88,6 +88,11 @@ public class AdminBusinessDbContext : DbContext /// public DbSet AssessmentResults { get; set; } = null!; + /// + /// 测评记录结论表 + /// + public DbSet AssessmentRecordConclusions { get; set; } = null!; + #endregion #region 用户管理 @@ -374,6 +379,16 @@ public class AdminBusinessDbContext : DbContext .HasColumnType("decimal(18,2)"); }); + // ============================================= + // AssessmentRecordConclusion 配置 + // ============================================= + modelBuilder.Entity(entity => + { + // Content 使用 nvarchar(max) + entity.Property(e => e.Content) + .HasColumnType("nvarchar(max)"); + }); + // ============================================= // BusinessPage 配置 // ============================================= diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/AssessmentRecordConclusion.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/AssessmentRecordConclusion.cs new file mode 100644 index 0000000..e634917 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Entities/AssessmentRecordConclusion.cs @@ -0,0 +1,78 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace MiAssessment.Admin.Business.Entities; + +/// +/// 测评记录结论表 +/// +[Table("assessment_record_conclusions")] +public class AssessmentRecordConclusion +{ + /// + /// 主键ID + /// + [Key] + public long Id { get; set; } + + /// + /// 测评记录ID + /// + public long RecordId { get; set; } + + /// + /// 分类ID + /// + public long CategoryId { get; set; } + + /// + /// 结论类型:1最强 2较强 3较弱 4最弱 + /// + public int ConclusionType { get; set; } + + /// + /// 星级(1-5),记录级别的星级,可由管理员覆盖 + /// + public int StarLevel { get; set; } + + /// + /// 结论标题 + /// + [MaxLength(100)] + public string? Title { get; set; } + + /// + /// 结论内容(富文本) + /// + [Required] + [Column(TypeName = "nvarchar(max)")] + public string Content { get; set; } = null!; + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 更新时间 + /// + public DateTime UpdateTime { get; set; } + + /// + /// 软删除标记 + /// + public bool IsDeleted { get; set; } + + /// + /// 关联的测评记录 + /// + [ForeignKey(nameof(RecordId))] + public virtual AssessmentRecord? Record { get; set; } + + /// + /// 关联的报告分类 + /// + [ForeignKey(nameof(CategoryId))] + public virtual ReportCategory? Category { get; set; } +} diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/AssessmentRecord/ReportCategoryItem.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/AssessmentRecord/ReportCategoryItem.cs index 75dd83f..d8d96d6 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/AssessmentRecord/ReportCategoryItem.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/AssessmentRecord/ReportCategoryItem.cs @@ -35,6 +35,11 @@ public class ReportCategoryItem /// public int StarLevel { get; set; } + /// + /// 结论记录ID(assessment_record_conclusions 表的 ID,用于编辑) + /// + public long? ConclusionId { get; set; } + /// /// 结论内容 /// diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/AssessmentRecord/UpdateRecordConclusionRequest.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/AssessmentRecord/UpdateRecordConclusionRequest.cs new file mode 100644 index 0000000..c6bd0f1 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/AssessmentRecord/UpdateRecordConclusionRequest.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; + +namespace MiAssessment.Admin.Business.Models.AssessmentRecord; + +/// +/// 更新测评记录结论请求 +/// +public class UpdateRecordConclusionRequest +{ + /// + /// 结论记录ID + /// + [Required] + public long Id { get; set; } + + /// + /// 结论内容 + /// + [Required] + public string Content { get; set; } = null!; +} diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AssessmentRecordService.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AssessmentRecordService.cs index a4050f7..f2c9a07 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AssessmentRecordService.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AssessmentRecordService.cs @@ -269,21 +269,49 @@ public class AssessmentRecordService : IAssessmentRecordService .ToDictionaryAsync(c => c.Id, c => c); // 查询结论,根据分类ID和星级匹配 - // StarLevel (1-5) 映射到 ConclusionType (1-4): - // StarLevel 5 → ConclusionType 1 (最强) - // StarLevel 4 → ConclusionType 2 (较强) - // StarLevel 3 → ConclusionType 2 (较强) - // StarLevel 2 → ConclusionType 3 (较弱) - // StarLevel 1 → ConclusionType 4 (最弱) - var conclusions = await _dbContext.ReportConclusions + // 优先从 assessment_record_conclusions 读取记录级别结论 + var recordConclusions = await _dbContext.AssessmentRecordConclusions .AsNoTracking() - .Where(c => categoryIds.Contains(c.CategoryId) && !c.IsDeleted) + .Where(c => c.RecordId == id && !c.IsDeleted) .ToListAsync(); - // 构建结论字典,key 为 (CategoryId, ConclusionType) - var conclusionDict = conclusions - .GroupBy(c => (c.CategoryId, c.ConclusionType)) - .ToDictionary(g => g.Key, g => g.First().Content); + // 构建结论字典,key 为 CategoryId + // 如果有记录级别结论,使用记录级别的;否则回退到模板结论 + Dictionary conclusionDict; + + if (recordConclusions.Count > 0) + { + // 使用记录级别结论 + conclusionDict = recordConclusions + .GroupBy(c => c.CategoryId) + .ToDictionary( + g => g.Key, + g => ((long?)g.First().Id, (string?)g.First().Content)); + } + else + { + // 回退到模板结论 + var conclusions = await _dbContext.ReportConclusions + .AsNoTracking() + .Where(c => categoryIds.Contains(c.CategoryId) && !c.IsDeleted) + .ToListAsync(); + + // 需要根据星级匹配结论类型 + conclusionDict = new Dictionary(); + var conclusionsByKey = conclusions + .GroupBy(c => (c.CategoryId, c.ConclusionType)) + .ToDictionary(g => g.Key, g => g.First().Content); + + // 为每个结果匹配模板结论 + foreach (var result in results) + { + var conclusionType = MapStarLevelToConclusionType(result.StarLevel); + if (conclusionsByKey.TryGetValue((result.CategoryId, conclusionType), out var content)) + { + conclusionDict[result.CategoryId] = (null, content); + } + } + } // 按分类类型分组结果 var resultGroups = results @@ -309,8 +337,7 @@ public class AssessmentRecordService : IAssessmentRecordService Items = g.Select(r => { var category = categories.FirstOrDefault(c => c.Id == r.CategoryId); - var conclusionType = MapStarLevelToConclusionType(r.StarLevel); - conclusionDict.TryGetValue((r.CategoryId, conclusionType), out var conclusionContent); + conclusionDict.TryGetValue(r.CategoryId, out var conclusionData); return new ReportCategoryItem { @@ -320,7 +347,8 @@ public class AssessmentRecordService : IAssessmentRecordService MaxScore = r.MaxScore, Percentage = r.Percentage, StarLevel = r.StarLevel, - ConclusionContent = conclusionContent + ConclusionId = conclusionData.conclusionId, + ConclusionContent = conclusionData.content }; }).ToList() }) @@ -614,6 +642,24 @@ public class AssessmentRecordService : IAssessmentRecordService _logger.LogInformation("测评记录已删除,ID: {RecordId}", id); } + /// + public async Task UpdateRecordConclusionAsync(UpdateRecordConclusionRequest request) + { + var conclusion = await _dbContext.AssessmentRecordConclusions + .FirstOrDefaultAsync(c => c.Id == request.Id && !c.IsDeleted); + + if (conclusion == null) + { + throw new BusinessException(ErrorCodes.ConclusionNotFound, "结论记录不存在"); + } + + conclusion.Content = request.Content; + conclusion.UpdateTime = DateTime.Now; + await _dbContext.SaveChangesAsync(); + + _logger.LogInformation("更新测评记录结论成功,结论ID: {ConclusionId}, 记录ID: {RecordId}", request.Id, conclusion.RecordId); + } + #region 私有方法 /// diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/Interfaces/IAssessmentRecordService.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/Interfaces/IAssessmentRecordService.cs index 1c9e750..3bd003d 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/Interfaces/IAssessmentRecordService.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/Interfaces/IAssessmentRecordService.cs @@ -54,4 +54,10 @@ public interface IAssessmentRecordService /// /// 记录ID Task DeleteRecordAsync(long id); + + /// + /// 更新测评记录结论 + /// + /// 更新请求 + Task UpdateRecordConclusionAsync(UpdateRecordConclusionRequest request); } diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/business/assessmentRecord.ts b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/business/assessmentRecord.ts index b076af0..454de53 100644 --- a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/business/assessmentRecord.ts +++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/business/assessmentRecord.ts @@ -73,6 +73,7 @@ export interface ReportCategoryItem { maxScore: number percentage: number starLevel: number + conclusionId: number | null conclusionContent: string | null } @@ -167,3 +168,18 @@ export function deleteRecord(id: number): Promise> { data: { id } }) } + +/** 更新记录结论请求 */ +export interface UpdateRecordConclusionRequest { + id: number + content: string +} + +/** 更新测评记录结论 */ +export function updateRecordConclusion(data: UpdateRecordConclusionRequest): Promise> { + return request({ + url: '/admin/assessmentRecord/updateConclusion', + method: 'post', + data + }) +} diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/business/assessment/record/index.vue b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/business/assessment/record/index.vue index 31c3112..6d26d0d 100644 --- a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/business/assessment/record/index.vue +++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/business/assessment/record/index.vue @@ -280,12 +280,52 @@ - + + + + {{ row.conclusionContent || '暂无结论' }} + + + 编辑 + + + + + + + + + + + + 取消 + + 保存 + + + @@ -294,8 +334,8 @@ * 测评记录管理页面 * @description 查看用户测评记录、答案详情和测评报告,支持搜索、导出 */ -import { reactive, ref, onMounted } from 'vue' -import { Search, Refresh, View, Download, Document, RefreshRight, Delete } from '@element-plus/icons-vue' +import { reactive, ref, computed, onMounted } from 'vue' +import { Search, Refresh, View, Download, Document, RefreshRight, Delete, Edit } from '@element-plus/icons-vue' import { ElMessage, ElMessageBox } from 'element-plus' import { getRecordList, @@ -305,6 +345,7 @@ import { regenerateReport, batchRegenerateReport, deleteRecord, + updateRecordConclusion, type AssessmentRecordItem, type AssessmentRecordDetail, type AssessmentReport, @@ -361,6 +402,8 @@ interface RecordPageState { scoreOptionMap: Map batchRegenerateLoading: boolean selectedRows: AssessmentRecordItem[] + editingConclusion: { conclusionId: number; content: string; categoryName: string } | null + editConclusionLoading: boolean } // ============ Refs ============ @@ -391,7 +434,17 @@ const state = reactive({ exportLoading: false, scoreOptionMap: new Map(), batchRegenerateLoading: false, - selectedRows: [] + selectedRows: [], + editingConclusion: null, + editConclusionLoading: false +}) + +/** 编辑结论对话框可见性 */ +const editConclusionDialogVisible = computed({ + get: () => state.editingConclusion !== null, + set: (val: boolean) => { + if (!val) state.editingConclusion = null + } }) // ============ Helper Functions ============ @@ -703,6 +756,47 @@ async function handleExport() { } } +/** 开始编辑结论 */ +function handleEditConclusion(item: any) { + state.editingConclusion = { + conclusionId: item.conclusionId, + content: item.conclusionContent || '', + categoryName: item.categoryName + } +} + +/** 保存结论编辑 */ +async function handleSaveConclusion() { + if (!state.editingConclusion) return + state.editConclusionLoading = true + try { + const res = await updateRecordConclusion({ + id: state.editingConclusion.conclusionId, + content: state.editingConclusion.content + }) + if (res.code === 0) { + ElMessage.success('结论更新成功') + state.editingConclusion = null + // 重新加载报告数据 + if (state.report) { + loadRecordReport(state.report.id) + } + } else { + throw new Error(res.message || '更新失败') + } + } catch (error) { + const message = error instanceof Error ? error.message : '更新结论失败' + ElMessage.error(message) + } finally { + state.editConclusionLoading = false + } +} + +/** 取消编辑结论 */ +function handleCancelEditConclusion() { + state.editingConclusion = null +} + // ============ Lifecycle ============ onMounted(() => { @@ -804,4 +898,15 @@ onMounted(() => { :deep(.el-descriptions) { --el-descriptions-item-bordered-label-background: var(--bg-light, #f5f7fa); } + +.conclusion-cell { + display: flex; + align-items: flex-start; + gap: 8px; +} + +.conclusion-text { + flex: 1; + word-break: break-all; +}