From f8a9aaf71fcf95b4839779c35e200cb0d2d38840 Mon Sep 17 00:00:00 2001 From: zpc Date: Tue, 31 Mar 2026 15:08:23 +0800 Subject: [PATCH] feat(assessment): Add retest status for equal score detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add new assessment status 7 ("需重测") for cases where all scores are equal - Create AllScoresEqualException to handle scenarios where 8 intelligences or 40 ability dimensions have identical scores - Implement CheckAllScoresEqual validation in ReportGenerationService to detect and prevent invalid report generation - Add UpdateRecordStatusToRetestAsync method in ReportQueueConsumer to handle retest status updates - Update admin UI status tag mapping to display retest status with warning indicator - Add user-friendly message for retest status in AssessmentService - Update status description mappings across services to include new retest status - Prevent PDF generation when all scores are equal, prompting users to retake the assessment --- .../Services/AssessmentRecordService.cs | 3 +- .../business/assessment/record/index.vue | 1 + .../BackgroundServices/ReportQueueConsumer.cs | 45 +++++++ .../Exceptions/AllScoresEqualException.cs | 27 +++++ .../Services/AssessmentService.cs | 6 + .../Services/ReportGenerationService.cs | 37 ++++++ .../Models/Assessment/ResultStatusDto.cs | 2 +- uniapp/pages/assessment/history/index.vue | 43 ++++++- uniapp/pages/assessment/loading/index.vue | 112 +++++++++++++++++- uniapp/pages/assessment/questions/index.vue | 2 +- 10 files changed, 269 insertions(+), 9 deletions(-) create mode 100644 server/MiAssessment/src/MiAssessment.Core/Exceptions/AllScoresEqualException.cs diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AssessmentRecordService.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AssessmentRecordService.cs index 1865ccc..f7240ff 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AssessmentRecordService.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Services/AssessmentRecordService.cs @@ -34,7 +34,8 @@ public class AssessmentRecordService : IAssessmentRecordService { 3, "生成中" }, { 4, "已完成" }, { 5, "生成失败" }, - { 6, "数据已就绪" } + { 6, "数据已就绪" }, + { 7, "需重测" } }; /// 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 0443cef..289aa5e 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 @@ -495,6 +495,7 @@ function getStatusTagType(status: number): 'info' | 'primary' | 'warning' | 'suc case 4: return 'success' case 5: return 'danger' case 6: return 'primary' + case 7: return 'warning' default: return 'info' } } diff --git a/server/MiAssessment/src/MiAssessment.Api/BackgroundServices/ReportQueueConsumer.cs b/server/MiAssessment/src/MiAssessment.Api/BackgroundServices/ReportQueueConsumer.cs index 55f68b3..f057e83 100644 --- a/server/MiAssessment/src/MiAssessment.Api/BackgroundServices/ReportQueueConsumer.cs +++ b/server/MiAssessment/src/MiAssessment.Api/BackgroundServices/ReportQueueConsumer.cs @@ -1,4 +1,5 @@ using System.Text.Json; +using MiAssessment.Core.Exceptions; using MiAssessment.Core.Interfaces; using MiAssessment.Core.Models; using MiAssessment.Core.Services; @@ -165,6 +166,13 @@ public class ReportQueueConsumer : BackgroundService await UpdateRecordStatusToFailedAsync(message.RecordId); } } + catch (AllScoresEqualException asEx) + { + // 同分异常:不重试,直接设置状态为需重测(Status=7) + _logger.LogWarning("检测到全同分情况,RecordId: {RecordId}, ScoreType: {ScoreType}, Message: {Message}", + message.RecordId, asEx.ScoreType, asEx.Message); + await UpdateRecordStatusToRetestAsync(message.RecordId); + } catch (Exception ex) { // 报告生成失败,执行重试或死信逻辑 @@ -257,4 +265,41 @@ public class ReportQueueConsumer : BackgroundService _logger.LogError(ex, "更新测评记录状态为生成失败时发生异常,RecordId: {RecordId}", recordId); } } + + /// + /// 更新测评记录状态为需重测(Status=7) + /// + /// + /// 当检测到八大智能8项全同分或40项细分维度全同分时调用, + /// 不生成PDF,提示用户重新测评。 + /// + /// 测评记录ID + private async Task UpdateRecordStatusToRetestAsync(long recordId) + { + try + { + using var scope = _serviceScopeFactory.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + + var record = await dbContext.AssessmentRecords + .FirstOrDefaultAsync(r => r.Id == recordId); + + if (record != null) + { + record.Status = 7; + record.UpdateTime = DateTime.Now; + await dbContext.SaveChangesAsync(); + + _logger.LogInformation("测评记录状态已更新为需重测,RecordId: {RecordId}", recordId); + } + else + { + _logger.LogWarning("更新状态失败,测评记录不存在,RecordId: {RecordId}", recordId); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "更新测评记录状态为需重测时发生异常,RecordId: {RecordId}", recordId); + } + } } diff --git a/server/MiAssessment/src/MiAssessment.Core/Exceptions/AllScoresEqualException.cs b/server/MiAssessment/src/MiAssessment.Core/Exceptions/AllScoresEqualException.cs new file mode 100644 index 0000000..dc38b7a --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Core/Exceptions/AllScoresEqualException.cs @@ -0,0 +1,27 @@ +namespace MiAssessment.Core.Exceptions; + +/// +/// 所有分数相同异常(八大智能全同分或40项细分全同分) +/// +/// +/// 当检测到八大智能8项全部同分或40项细分维度全部同分时抛出, +/// 表示无法生成有效的差异化报告,需要用户重新测评。 +/// +public class AllScoresEqualException : Exception +{ + /// + /// 同分类型:Intelligence=八大智能同分,Ability=细分维度同分 + /// + public string ScoreType { get; } + + /// + /// 构造函数 + /// + /// 同分类型 + /// 异常信息 + public AllScoresEqualException(string scoreType, string message) + : base(message) + { + ScoreType = scoreType; + } +} diff --git a/server/MiAssessment/src/MiAssessment.Core/Services/AssessmentService.cs b/server/MiAssessment/src/MiAssessment.Core/Services/AssessmentService.cs index 96f90a7..32b5507 100644 --- a/server/MiAssessment/src/MiAssessment.Core/Services/AssessmentService.cs +++ b/server/MiAssessment/src/MiAssessment.Core/Services/AssessmentService.cs @@ -119,6 +119,11 @@ public class AssessmentService : IAssessmentService { record.Message = "报告生成失败,请联系客服"; } + // 对需重测状态设置提示信息 + else if (record.Status == 7) + { + record.Message = "分析得出多个智能处于同一梯队,我们需要更细致的分析维度,接下来请您重新进行测评"; + } _logger.LogDebug("查询报告状态成功,status: {Status}, isCompleted: {IsCompleted}", record.Status, record.IsCompleted); } @@ -655,6 +660,7 @@ public class AssessmentService : IAssessmentService 4 => "已完成", 5 => "生成失败", 6 => "报告生成中", + 7 => "需重测", _ => "未知" }; } diff --git a/server/MiAssessment/src/MiAssessment.Core/Services/ReportGenerationService.cs b/server/MiAssessment/src/MiAssessment.Core/Services/ReportGenerationService.cs index 96d60b0..098d9d7 100644 --- a/server/MiAssessment/src/MiAssessment.Core/Services/ReportGenerationService.cs +++ b/server/MiAssessment/src/MiAssessment.Core/Services/ReportGenerationService.cs @@ -1,3 +1,4 @@ +using MiAssessment.Core.Exceptions; using MiAssessment.Model.Data; using MiAssessment.Model.Entities; using Microsoft.EntityFrameworkCore; @@ -60,6 +61,9 @@ public class ReportGenerationService _logger.LogDebug("父级汇总完成,recordId: {RecordId}, 新增父级数量: {Count}, 总数量: {Total}", recordId, parentScores.Count, categoryScores.Count); + // 步骤3.6:检测同分情况(八大智能全同分或细分维度全同分) + CheckAllScoresEqual(categoryScores); + // 步骤4:按 CategoryType 分组排名 var rankedScores = CalculateRanks(categoryScores); @@ -300,6 +304,39 @@ public class ReportGenerationService return parentScores; } + /// + /// 检测同分情况:八大智能8项全同分或细分维度全同分 + /// + /// 所有分类得分(含叶子和父级) + /// 当检测到全同分时抛出 + internal static void CheckAllScoresEqual(List categoryScores) + { + // CategoryType=1 为八大智能(父级汇总后的得分) + var intelligenceScores = categoryScores + .Where(s => s.CategoryType == 1) + .Select(s => s.Percentage) + .ToList(); + + if (intelligenceScores.Count >= 8 && intelligenceScores.Distinct().Count() == 1) + { + throw new AllScoresEqualException("Intelligence", + $"八大智能8项得分全部相同({intelligenceScores.First()}%),无法生成差异化报告"); + } + + // CategoryType=3 为细分能力维度 + var abilityScores = categoryScores + .Where(s => s.CategoryType == 3) + .Select(s => s.Percentage) + .ToList(); + + if (abilityScores.Count >= 40 && abilityScores.Distinct().Count() == 1) + { + throw new AllScoresEqualException("Ability", + $"40项细分维度得分全部相同({abilityScores.First()}%),无法生成差异化报告"); + } + } + + /// /// 分类得分计算结果 /// diff --git a/server/MiAssessment/src/MiAssessment.Model/Models/Assessment/ResultStatusDto.cs b/server/MiAssessment/src/MiAssessment.Model/Models/Assessment/ResultStatusDto.cs index 36f63b3..c6cccf9 100644 --- a/server/MiAssessment/src/MiAssessment.Model/Models/Assessment/ResultStatusDto.cs +++ b/server/MiAssessment/src/MiAssessment.Model/Models/Assessment/ResultStatusDto.cs @@ -6,7 +6,7 @@ namespace MiAssessment.Model.Models.Assessment; public class ResultStatusDto { /// - /// 状态:0待支付 1待测评 2测评中 3生成中 4已完成 5生成失败 + /// 状态:0待支付 1待测评 2测评中 3生成中 4已完成 5生成失败 6数据已就绪 7需重测 /// public int Status { get; set; } diff --git a/uniapp/pages/assessment/history/index.vue b/uniapp/pages/assessment/history/index.vue index c252b56..abbdd7f 100644 --- a/uniapp/pages/assessment/history/index.vue +++ b/uniapp/pages/assessment/history/index.vue @@ -21,7 +21,8 @@ const ASSESSMENT_STATUS = { GENERATING: 3, // 生成中 COMPLETED: 4, // 已完成 FAILED: 5, // 生成失败 - DATA_READY: 6 // 数据已就绪(PDF生成中) + DATA_READY: 6, // 数据已就绪(PDF生成中) + NEED_RETEST: 7 // 需重测(全同分) } // 状态 @@ -46,7 +47,8 @@ function getStatusClass(status) { [ASSESSMENT_STATUS.GENERATING]: 'status-generating', [ASSESSMENT_STATUS.COMPLETED]: 'status-completed', [ASSESSMENT_STATUS.FAILED]: 'status-failed', - [ASSESSMENT_STATUS.DATA_READY]: 'status-generating' + [ASSESSMENT_STATUS.DATA_READY]: 'status-generating', + [ASSESSMENT_STATUS.NEED_RETEST]: 'status-retest' } return classMap[status] || '' } @@ -155,6 +157,14 @@ function viewResult(record) { }) return } + + // 需重测 - 跳转到测评首页重新开始 + if (record.status === ASSESSMENT_STATUS.NEED_RETEST) { + uni.navigateTo({ + url: `/pages/assessment/info/index?typeId=${record.assessmentTypeId || 1}` + }) + return + } } /** @@ -208,13 +218,19 @@ onMounted(() => { - + 查看报告 + + + 重新测试 + + + @@ -296,6 +312,18 @@ onMounted(() => { color: $success-color; background-color: rgba(82, 196, 26, 0.1); } + + // 生成失败 - 红色 + &.status-failed { + color: $error-color; + background-color: rgba(255, 77, 79, 0.1); + } + + // 需重测 - 橙色 + &.status-retest { + color: $warning-color; + background-color: rgba(250, 173, 20, 0.1); + } } } @@ -331,6 +359,15 @@ onMounted(() => { align-items: center; color: $primary-color; font-size: $font-size-md; + + &.retest-btn { + color: $warning-color; + + .arrow-icon { + border-right-color: $warning-color; + border-bottom-color: $warning-color; + } + } .arrow-icon { width: 12rpx; diff --git a/uniapp/pages/assessment/loading/index.vue b/uniapp/pages/assessment/loading/index.vue index cf8c7b7..874929d 100644 --- a/uniapp/pages/assessment/loading/index.vue +++ b/uniapp/pages/assessment/loading/index.vue @@ -7,6 +7,7 @@ * - 显示提示文字 * - 轮询查询报告生成状态(3秒间隔) * - 生成完成自动跳转结果页 + * - Status=7 需重测:显示提示和重新测试按钮 */ import { ref, onMounted, onUnmounted } from 'vue' @@ -19,6 +20,7 @@ const userStore = useUserStore() // 页面参数 const recordId = ref('') +const typeId = ref('') // 轮询定时器 let pollTimer = null @@ -32,6 +34,10 @@ const MAX_POLL_COUNT = 100 // 当前轮询次数 const pollCount = ref(0) +// 是否需要重新测评(Status=7) +const needRetest = ref(false) +const retestMessage = ref('') + /** * 开始轮询查询状态 */ @@ -90,7 +96,7 @@ async function checkStatus() { if (res && res.code === 0 && res.data) { const status = res.data.status - // 状态:3-生成中 4-已完成 5-失败 + // 状态:3-生成中 4-已完成 5-失败 7-需重测 if (status === 4) { // 生成完成,跳转结果页 stopPolling() @@ -100,6 +106,11 @@ async function checkStatus() { uni.redirectTo({ url: `/pages/assessment/result/index?recordId=${recordId.value}&reportUrl=${encodeURIComponent(reportUrl)}` }) + } else if (status === 7) { + // 需重测:停止轮询,显示提示和重新测试按钮 + stopPolling() + needRetest.value = true + retestMessage.value = res.data.message || '分析得出多个智能处于同一梯队,我们需要更细致的分析维度,接下来请您重新进行测评' } else if (status === 5) { // 生成失败 stopPolling() @@ -115,7 +126,7 @@ async function checkStatus() { } }) } - // status === 3 继续轮询 + // status === 3 或 6 继续轮询 } } catch (error) { console.error('查询状态失败:', error) @@ -123,11 +134,28 @@ async function checkStatus() { } } +/** + * 重新测试 + */ +function handleRetest() { + // 跳转到测评首页重新开始 + if (typeId.value) { + uni.redirectTo({ + url: `/pages/assessment/info/index?typeId=${typeId.value}` + }) + } else { + uni.switchTab({ + url: '/pages/index/index' + }) + } +} + /** * 页面加载 */ onLoad((options) => { recordId.value = options.recordId || '' + typeId.value = options.typeId || '' // 恢复用户登录状态 userStore.restoreFromStorage() @@ -149,8 +177,17 @@ onUnmounted(() => { + + + + + + {{ retestMessage }} + + + - + @@ -160,6 +197,13 @@ onUnmounted(() => { 可在往期测评中查看测评结果 + + + + + 重新测试 + + @@ -170,6 +214,8 @@ onUnmounted(() => { .assessment-loading-page { min-height: 100vh; background-color: $bg-white; + display: flex; + flex-direction: column; } .loading-content { @@ -205,4 +251,64 @@ onUnmounted(() => { font-size: $font-size-md; color: $text-placeholder; } + +// 需重测内容 +.retest-content { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: $spacing-xl 64rpx; +} + +.retest-image { + width: 360rpx; + height: 360rpx; + margin-bottom: $spacing-xl; +} + +.retest-text { + text-align: center; +} + +.retest-title { + display: block; + font-size: $font-size-lg; + color: $text-secondary; + line-height: 1.6; +} + +// 底部操作栏 +.bottom-action { + position: fixed; + left: 0; + right: 0; + bottom: 0; + padding: $spacing-lg $spacing-xl; + padding-bottom: calc(#{$spacing-lg} + env(safe-area-inset-bottom)); + background-color: transparent; + z-index: 100; +} + +// 重新测试按钮 +.retest-btn { + width: 100%; + height: 88rpx; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, #FF8A6B 0%, #FF6B6B 100%); + border-radius: $border-radius-round; + + text { + font-size: $font-size-lg; + color: $text-white; + font-weight: $font-weight-medium; + } + + &:active { + opacity: 0.85; + } +} diff --git a/uniapp/pages/assessment/questions/index.vue b/uniapp/pages/assessment/questions/index.vue index 7d9437b..d910dc2 100644 --- a/uniapp/pages/assessment/questions/index.vue +++ b/uniapp/pages/assessment/questions/index.vue @@ -198,7 +198,7 @@ async function handleSubmit() { if (res && res.code === 0) { const resRecordId = res.data?.recordId || res.data?.id || recordId.value - uni.redirectTo({ url: `/pages/assessment/loading/index?recordId=${resRecordId}` }) + uni.redirectTo({ url: `/pages/assessment/loading/index?recordId=${resRecordId}&typeId=${typeId.value}` }) } else { uni.showToast({ title: res?.message || '提交失败,请重试', icon: 'none' }) }