790 lines
28 KiB
C#
790 lines
28 KiB
C#
using System.Text.Json;
|
||
using ClosedXML.Excel;
|
||
using MiAssessment.Admin.Business.Data;
|
||
using MiAssessment.Admin.Business.Entities;
|
||
using MiAssessment.Admin.Business.Models;
|
||
using MiAssessment.Admin.Business.Models.AssessmentRecord;
|
||
using MiAssessment.Admin.Business.Models.Common;
|
||
using MiAssessment.Admin.Business.Services.Interfaces;
|
||
using MiAssessment.Core.Interfaces;
|
||
using MiAssessment.Core.Models;
|
||
using MiAssessment.Core.Services;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.Logging;
|
||
|
||
namespace MiAssessment.Admin.Business.Services;
|
||
|
||
/// <summary>
|
||
/// 测评记录服务实现
|
||
/// </summary>
|
||
public class AssessmentRecordService : IAssessmentRecordService
|
||
{
|
||
private readonly AdminBusinessDbContext _dbContext;
|
||
private readonly ILogger<AssessmentRecordService> _logger;
|
||
private readonly IRedisService _redisService;
|
||
|
||
/// <summary>
|
||
/// 状态名称映射
|
||
/// </summary>
|
||
private static readonly Dictionary<int, string> StatusNames = new()
|
||
{
|
||
{ 1, "待测评" },
|
||
{ 2, "测评中" },
|
||
{ 3, "生成中" },
|
||
{ 4, "已完成" },
|
||
{ 5, "生成失败" }
|
||
};
|
||
|
||
/// <summary>
|
||
/// 学历阶段名称映射
|
||
/// </summary>
|
||
private static readonly Dictionary<int, string> EducationStageNames = new()
|
||
{
|
||
{ 1, "小学及以下" },
|
||
{ 2, "初中" },
|
||
{ 3, "高中" },
|
||
{ 4, "大专" },
|
||
{ 5, "本科" },
|
||
{ 6, "研究生及以上" }
|
||
};
|
||
|
||
/// <summary>
|
||
/// 性别名称映射
|
||
/// </summary>
|
||
private static readonly Dictionary<int, string> GenderNames = new()
|
||
{
|
||
{ 1, "男" },
|
||
{ 2, "女" }
|
||
};
|
||
|
||
/// <summary>
|
||
/// 构造函数
|
||
/// </summary>
|
||
/// <param name="dbContext">数据库上下文</param>
|
||
/// <param name="logger">日志记录器</param>
|
||
/// <param name="redisService">Redis服务</param>
|
||
public AssessmentRecordService(
|
||
AdminBusinessDbContext dbContext,
|
||
ILogger<AssessmentRecordService> logger,
|
||
IRedisService redisService)
|
||
{
|
||
_dbContext = dbContext;
|
||
_logger = logger;
|
||
_redisService = redisService;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<PagedResult<AssessmentRecordDto>> GetRecordListAsync(AssessmentRecordQueryRequest request)
|
||
{
|
||
// 构建查询,过滤软删除记录,使用 AsNoTracking 提高只读查询性能
|
||
var query = _dbContext.AssessmentRecords
|
||
.AsNoTracking()
|
||
.Where(r => !r.IsDeleted);
|
||
|
||
// 应用过滤条件
|
||
query = ApplyRecordQueryFilters(query, request);
|
||
|
||
// 获取总数
|
||
var total = await query.CountAsync();
|
||
|
||
// 分页查询,按创建时间降序排列
|
||
// 使用左连接获取关联数据(User、AssessmentType、Order)
|
||
var items = await query
|
||
.OrderByDescending(r => r.CreateTime)
|
||
.Skip(request.Skip)
|
||
.Take(request.PageSize)
|
||
.Select(r => new AssessmentRecordDto
|
||
{
|
||
Id = r.Id,
|
||
UserId = r.UserId,
|
||
UserNickname = r.User != null ? r.User.Nickname : null,
|
||
OrderId = r.OrderId,
|
||
OrderNo = r.Order != null ? r.Order.OrderNo : null,
|
||
AssessmentTypeId = r.AssessmentTypeId,
|
||
AssessmentTypeName = r.AssessmentType != null ? r.AssessmentType.Name : null,
|
||
Name = r.Name,
|
||
Phone = r.Phone,
|
||
Gender = r.Gender,
|
||
GenderName = GetGenderName(r.Gender),
|
||
Age = r.Age,
|
||
EducationStage = r.EducationStage,
|
||
EducationStageName = GetEducationStageName(r.EducationStage),
|
||
Province = r.Province,
|
||
City = r.City,
|
||
District = r.District,
|
||
Status = r.Status,
|
||
StatusName = GetStatusName(r.Status),
|
||
StartTime = r.StartTime,
|
||
SubmitTime = r.SubmitTime,
|
||
CompleteTime = r.CompleteTime,
|
||
CreateTime = r.CreateTime
|
||
})
|
||
.ToListAsync();
|
||
|
||
_logger.LogInformation("查询测评记录列表成功,总数: {Total}, 当前页数量: {Count}", total, items.Count);
|
||
|
||
return PagedResult<AssessmentRecordDto>.Create(items, total, request.Page, request.PageSize);
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<AssessmentRecordDetailDto?> GetRecordDetailAsync(long id)
|
||
{
|
||
// 查询测评记录,包含关联的用户、订单、测评类型信息
|
||
var record = await _dbContext.AssessmentRecords
|
||
.AsNoTracking()
|
||
.Include(r => r.User)
|
||
.Include(r => r.Order)
|
||
.Include(r => r.AssessmentType)
|
||
.Where(r => r.Id == id && !r.IsDeleted)
|
||
.FirstOrDefaultAsync();
|
||
|
||
// 如果记录不存在或已软删除,返回 null(调用方处理错误码 3241)
|
||
if (record == null)
|
||
{
|
||
_logger.LogWarning("测评记录不存在,ID: {RecordId}", id);
|
||
return null;
|
||
}
|
||
|
||
// 查询答案列表,按题号升序排列,并关联题目获取题目内容
|
||
var answers = await _dbContext.AssessmentAnswers
|
||
.AsNoTracking()
|
||
.Include(a => a.Question)
|
||
.Where(a => a.RecordId == id)
|
||
.OrderBy(a => a.QuestionNo)
|
||
.Select(a => new AnswerDetailDto
|
||
{
|
||
Id = a.Id,
|
||
QuestionId = a.QuestionId,
|
||
QuestionNo = a.QuestionNo,
|
||
QuestionContent = a.Question != null ? a.Question.Content : string.Empty,
|
||
AnswerValue = a.AnswerValue,
|
||
CreateTime = a.CreateTime
|
||
})
|
||
.ToListAsync();
|
||
|
||
// 查询结果列表,关联分类获取分类名称和分类类型名称
|
||
var results = await _dbContext.AssessmentResults
|
||
.AsNoTracking()
|
||
.Include(r => r.Category)
|
||
.Where(r => r.RecordId == id)
|
||
.Select(r => new ResultDetailDto
|
||
{
|
||
Id = r.Id,
|
||
CategoryId = r.CategoryId,
|
||
CategoryName = r.Category != null ? r.Category.Name : string.Empty,
|
||
CategoryTypeName = r.Category != null ? GetCategoryTypeName(r.Category.CategoryType) : string.Empty,
|
||
Score = r.Score,
|
||
MaxScore = r.MaxScore,
|
||
Percentage = r.Percentage,
|
||
Rank = r.Rank,
|
||
StarLevel = r.StarLevel,
|
||
CreateTime = r.CreateTime
|
||
})
|
||
.ToListAsync();
|
||
|
||
// 构建详情 DTO
|
||
var detail = new AssessmentRecordDetailDto
|
||
{
|
||
Id = record.Id,
|
||
UserId = record.UserId,
|
||
UserNickname = record.User?.Nickname,
|
||
OrderId = record.OrderId,
|
||
OrderNo = record.Order?.OrderNo,
|
||
AssessmentTypeId = record.AssessmentTypeId,
|
||
AssessmentTypeName = record.AssessmentType?.Name,
|
||
Name = record.Name,
|
||
Phone = record.Phone,
|
||
Gender = record.Gender,
|
||
GenderName = GetGenderName(record.Gender),
|
||
Age = record.Age,
|
||
EducationStage = record.EducationStage,
|
||
EducationStageName = GetEducationStageName(record.EducationStage),
|
||
Province = record.Province,
|
||
City = record.City,
|
||
District = record.District,
|
||
Status = record.Status,
|
||
StatusName = GetStatusName(record.Status),
|
||
StartTime = record.StartTime,
|
||
SubmitTime = record.SubmitTime,
|
||
CompleteTime = record.CompleteTime,
|
||
CreateTime = record.CreateTime,
|
||
Answers = answers,
|
||
Results = results
|
||
};
|
||
|
||
_logger.LogInformation("查询测评记录详情成功,ID: {RecordId}, 答案数: {AnswerCount}, 结果数: {ResultCount}",
|
||
id, answers.Count, results.Count);
|
||
|
||
return detail;
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<AssessmentReportDto?> GetRecordReportAsync(long id)
|
||
{
|
||
// 查询测评记录,包含关联的用户、测评类型信息
|
||
var record = await _dbContext.AssessmentRecords
|
||
.AsNoTracking()
|
||
.Include(r => r.User)
|
||
.Include(r => r.AssessmentType)
|
||
.Where(r => r.Id == id && !r.IsDeleted)
|
||
.FirstOrDefaultAsync();
|
||
|
||
// 如果记录不存在或已软删除,返回 null(调用方处理错误码 3241)
|
||
if (record == null)
|
||
{
|
||
_logger.LogWarning("测评记录不存在,ID: {RecordId}", id);
|
||
return null;
|
||
}
|
||
|
||
// 如果记录状态不是 4(已完成),返回 null(调用方处理错误码 3242)
|
||
if (record.Status != 4)
|
||
{
|
||
_logger.LogWarning("测评报告尚未生成,记录ID: {RecordId}, 当前状态: {Status}", id, record.Status);
|
||
return null;
|
||
}
|
||
|
||
// 查询结果列表,关联分类获取分类信息
|
||
var results = await _dbContext.AssessmentResults
|
||
.AsNoTracking()
|
||
.Include(r => r.Category)
|
||
.Where(r => r.RecordId == id)
|
||
.ToListAsync();
|
||
|
||
// 获取所有分类ID,用于查询父分类(分类类型)
|
||
var categoryIds = results.Select(r => r.CategoryId).Distinct().ToList();
|
||
|
||
// 查询所有相关分类
|
||
var categories = await _dbContext.ReportCategories
|
||
.AsNoTracking()
|
||
.Where(c => categoryIds.Contains(c.Id) && !c.IsDeleted)
|
||
.ToListAsync();
|
||
|
||
// 获取所有父分类ID(分类类型)
|
||
var parentIds = categories.Where(c => c.ParentId > 0).Select(c => c.ParentId).Distinct().ToList();
|
||
|
||
// 查询父分类(分类类型)
|
||
var parentCategories = await _dbContext.ReportCategories
|
||
.AsNoTracking()
|
||
.Where(c => parentIds.Contains(c.Id) && !c.IsDeleted)
|
||
.ToDictionaryAsync(c => c.Id, c => c);
|
||
|
||
// 查询结论,根据分类ID和星级匹配
|
||
// 优先从 assessment_record_conclusions 读取记录级别结论
|
||
var recordConclusions = await _dbContext.AssessmentRecordConclusions
|
||
.AsNoTracking()
|
||
.Where(c => c.RecordId == id && !c.IsDeleted)
|
||
.ToListAsync();
|
||
|
||
// 构建结论字典,key 为 CategoryId
|
||
// 如果有记录级别结论,使用记录级别的;否则回退到模板结论
|
||
Dictionary<long, (long? conclusionId, string? content)> 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<long, (long?, string?)>();
|
||
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
|
||
.Where(r => r.Category != null)
|
||
.GroupBy(r =>
|
||
{
|
||
var category = categories.FirstOrDefault(c => c.Id == r.CategoryId);
|
||
if (category == null) return (0L, "未知");
|
||
|
||
// 如果有父分类,使用父分类作为分组依据
|
||
if (category.ParentId > 0 && parentCategories.TryGetValue(category.ParentId, out var parent))
|
||
{
|
||
return (parent.Id, parent.Name);
|
||
}
|
||
|
||
// 否则使用分类类型名称
|
||
return (category.Id, GetCategoryTypeName(category.CategoryType));
|
||
})
|
||
.Select(g => new ReportCategoryGroup
|
||
{
|
||
CategoryTypeId = g.Key.Item1,
|
||
CategoryTypeName = g.Key.Item2,
|
||
Items = g.Select(r =>
|
||
{
|
||
var category = categories.FirstOrDefault(c => c.Id == r.CategoryId);
|
||
conclusionDict.TryGetValue(r.CategoryId, out var conclusionData);
|
||
|
||
return new ReportCategoryItem
|
||
{
|
||
CategoryId = r.CategoryId,
|
||
CategoryName = category?.Name ?? string.Empty,
|
||
Score = r.Score,
|
||
MaxScore = r.MaxScore,
|
||
Percentage = r.Percentage,
|
||
StarLevel = r.StarLevel,
|
||
ConclusionId = conclusionData.conclusionId,
|
||
ConclusionContent = conclusionData.content
|
||
};
|
||
}).ToList()
|
||
})
|
||
.ToList();
|
||
|
||
// 构建报告 DTO
|
||
var report = new AssessmentReportDto
|
||
{
|
||
Id = record.Id,
|
||
UserId = record.UserId,
|
||
UserNickname = record.User?.Nickname,
|
||
AssessmentTypeId = record.AssessmentTypeId,
|
||
AssessmentTypeName = record.AssessmentType?.Name,
|
||
Name = record.Name,
|
||
Phone = record.Phone,
|
||
Gender = record.Gender,
|
||
GenderName = GetGenderName(record.Gender),
|
||
Age = record.Age,
|
||
EducationStage = record.EducationStage,
|
||
EducationStageName = GetEducationStageName(record.EducationStage),
|
||
Province = record.Province,
|
||
City = record.City,
|
||
District = record.District,
|
||
Status = record.Status,
|
||
StatusName = GetStatusName(record.Status),
|
||
StartTime = record.StartTime,
|
||
SubmitTime = record.SubmitTime,
|
||
CompleteTime = record.CompleteTime,
|
||
CreateTime = record.CreateTime,
|
||
ResultGroups = resultGroups
|
||
};
|
||
|
||
_logger.LogInformation("查询测评报告成功,ID: {RecordId}, 分组数: {GroupCount}",
|
||
id, resultGroups.Count);
|
||
|
||
return report;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 导出数量上限
|
||
/// </summary>
|
||
private const int ExportMaxCount = 10000;
|
||
|
||
/// <inheritdoc />
|
||
public async Task<byte[]> ExportRecordsAsync(AssessmentRecordQueryRequest request)
|
||
{
|
||
// 构建查询,过滤软删除记录
|
||
var query = _dbContext.AssessmentRecords
|
||
.AsNoTracking()
|
||
.Where(r => !r.IsDeleted);
|
||
|
||
// 应用与列表查询相同的过滤条件 (Requirements 4.1)
|
||
query = ApplyRecordQueryFilters(query, request);
|
||
|
||
// 获取总数,检查是否超过导出上限 (Requirements 4.3)
|
||
var total = await query.CountAsync();
|
||
if (total > ExportMaxCount)
|
||
{
|
||
_logger.LogWarning("导出数据量过大,总数: {Total}, 上限: {MaxCount}", total, ExportMaxCount);
|
||
throw new BusinessException(ErrorCodes.ExportDataTooLarge, "导出数据量过大,请缩小查询范围");
|
||
}
|
||
|
||
// 查询所有匹配记录,按创建时间降序排列
|
||
// 包含关联数据(User、AssessmentType)用于获取用户昵称和测评类型名称
|
||
var records = await query
|
||
.OrderByDescending(r => r.CreateTime)
|
||
.Select(r => new
|
||
{
|
||
r.Id,
|
||
UserNickname = r.User != null ? r.User.Nickname : null,
|
||
AssessmentTypeName = r.AssessmentType != null ? r.AssessmentType.Name : null,
|
||
r.Name,
|
||
r.Phone,
|
||
r.Gender,
|
||
r.Age,
|
||
r.EducationStage,
|
||
r.Province,
|
||
r.City,
|
||
r.District,
|
||
r.Status,
|
||
r.StartTime,
|
||
r.SubmitTime,
|
||
r.CompleteTime,
|
||
r.CreateTime
|
||
})
|
||
.ToListAsync();
|
||
|
||
// 使用 ClosedXML 生成 Excel 文件 (Requirements 4.4)
|
||
using var workbook = new XLWorkbook();
|
||
var worksheet = workbook.Worksheets.Add("测评记录");
|
||
|
||
// 添加表头行 (Requirements 4.2)
|
||
var headers = new[]
|
||
{
|
||
"记录ID", "用户昵称", "测评类型", "姓名", "手机号", "性别", "年龄",
|
||
"学历阶段", "省份", "城市", "区县", "状态", "开始时间", "提交时间",
|
||
"完成时间", "创建时间"
|
||
};
|
||
|
||
for (var i = 0; i < headers.Length; i++)
|
||
{
|
||
worksheet.Cell(1, i + 1).Value = headers[i];
|
||
}
|
||
|
||
// 设置表头样式
|
||
var headerRow = worksheet.Row(1);
|
||
headerRow.Style.Font.Bold = true;
|
||
headerRow.Style.Fill.BackgroundColor = XLColor.LightGray;
|
||
|
||
// 添加数据行
|
||
var rowIndex = 2;
|
||
foreach (var record in records)
|
||
{
|
||
worksheet.Cell(rowIndex, 1).Value = record.Id;
|
||
worksheet.Cell(rowIndex, 2).Value = record.UserNickname ?? string.Empty;
|
||
worksheet.Cell(rowIndex, 3).Value = record.AssessmentTypeName ?? string.Empty;
|
||
worksheet.Cell(rowIndex, 4).Value = record.Name;
|
||
worksheet.Cell(rowIndex, 5).Value = record.Phone;
|
||
worksheet.Cell(rowIndex, 6).Value = GetGenderName(record.Gender);
|
||
worksheet.Cell(rowIndex, 7).Value = record.Age;
|
||
worksheet.Cell(rowIndex, 8).Value = GetEducationStageName(record.EducationStage);
|
||
worksheet.Cell(rowIndex, 9).Value = record.Province;
|
||
worksheet.Cell(rowIndex, 10).Value = record.City;
|
||
worksheet.Cell(rowIndex, 11).Value = record.District;
|
||
worksheet.Cell(rowIndex, 12).Value = GetStatusName(record.Status);
|
||
worksheet.Cell(rowIndex, 13).Value = record.StartTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? string.Empty;
|
||
worksheet.Cell(rowIndex, 14).Value = record.SubmitTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? string.Empty;
|
||
worksheet.Cell(rowIndex, 15).Value = record.CompleteTime?.ToString("yyyy-MM-dd HH:mm:ss") ?? string.Empty;
|
||
worksheet.Cell(rowIndex, 16).Value = record.CreateTime.ToString("yyyy-MM-dd HH:mm:ss");
|
||
rowIndex++;
|
||
}
|
||
|
||
// 自动调整列宽
|
||
worksheet.Columns().AdjustToContents();
|
||
|
||
// 将工作簿保存到内存流并返回字节数组
|
||
using var stream = new MemoryStream();
|
||
workbook.SaveAs(stream);
|
||
|
||
_logger.LogInformation("导出测评记录成功,总数: {Total}", records.Count);
|
||
|
||
return stream.ToArray();
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task RegenerateReportAsync(long recordId)
|
||
{
|
||
// 查找记录,过滤软删除
|
||
var record = await _dbContext.AssessmentRecords
|
||
.FirstOrDefaultAsync(r => r.Id == recordId && !r.IsDeleted);
|
||
|
||
if (record == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.AssessmentRecordNotFound, "测评记录不存在");
|
||
}
|
||
|
||
// 校验状态:仅允许状态为 3(生成中)或 5(生成失败)的记录重新生成
|
||
if (record.Status != 3 && record.Status != 5)
|
||
{
|
||
throw new BusinessException(ErrorCodes.InvalidOperation, "当前状态不允许重新生成");
|
||
}
|
||
|
||
// 重置状态为 3(生成中)
|
||
record.Status = 3;
|
||
record.UpdateTime = DateTime.Now;
|
||
|
||
// 清除已有的测评结果数据
|
||
var existingResults = await _dbContext.AssessmentResults
|
||
.Where(r => r.RecordId == recordId)
|
||
.ToListAsync();
|
||
|
||
if (existingResults.Count > 0)
|
||
{
|
||
_dbContext.AssessmentResults.RemoveRange(existingResults);
|
||
}
|
||
|
||
// 构造队列消息并入队
|
||
var message = new ReportQueueMessage
|
||
{
|
||
RecordId = recordId,
|
||
RetryCount = 0,
|
||
EnqueueTime = DateTime.Now
|
||
};
|
||
|
||
var json = JsonSerializer.Serialize(message);
|
||
await _redisService.ListLeftPushAsync(ReportQueueProducer.ReportQueueKey, json);
|
||
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
_logger.LogInformation("重新生成报告已入队,记录ID: {RecordId}", recordId);
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task<BatchRegenerateResult> BatchRegenerateReportAsync(List<long> recordIds)
|
||
{
|
||
// 校验参数
|
||
if (recordIds == null || recordIds.Count == 0)
|
||
{
|
||
throw new BusinessException(ErrorCodes.ParamError, "记录ID列表不能为空");
|
||
}
|
||
|
||
var successCount = 0;
|
||
var skippedCount = 0;
|
||
|
||
foreach (var recordId in recordIds)
|
||
{
|
||
try
|
||
{
|
||
// 查找记录,过滤软删除
|
||
var record = await _dbContext.AssessmentRecords
|
||
.FirstOrDefaultAsync(r => r.Id == recordId && !r.IsDeleted);
|
||
|
||
// 记录不存在,跳过
|
||
if (record == null)
|
||
{
|
||
_logger.LogWarning("批量重新生成:记录不存在,ID: {RecordId}", recordId);
|
||
skippedCount++;
|
||
continue;
|
||
}
|
||
|
||
// 状态不符,跳过
|
||
if (record.Status != 3 && record.Status != 5)
|
||
{
|
||
_logger.LogWarning("批量重新生成:状态不符,ID: {RecordId}, 状态: {Status}", recordId, record.Status);
|
||
skippedCount++;
|
||
continue;
|
||
}
|
||
|
||
// 重置状态为 3(生成中)
|
||
record.Status = 3;
|
||
record.UpdateTime = DateTime.Now;
|
||
|
||
// 清除已有的测评结果数据
|
||
var existingResults = await _dbContext.AssessmentResults
|
||
.Where(r => r.RecordId == recordId)
|
||
.ToListAsync();
|
||
|
||
if (existingResults.Count > 0)
|
||
{
|
||
_dbContext.AssessmentResults.RemoveRange(existingResults);
|
||
}
|
||
|
||
// 构造队列消息并入队
|
||
var message = new ReportQueueMessage
|
||
{
|
||
RecordId = recordId,
|
||
RetryCount = 0,
|
||
EnqueueTime = DateTime.Now
|
||
};
|
||
|
||
var json = JsonSerializer.Serialize(message);
|
||
await _redisService.ListLeftPushAsync(ReportQueueProducer.ReportQueueKey, json);
|
||
|
||
successCount++;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
_logger.LogError(ex, "批量重新生成:处理失败,ID: {RecordId}", recordId);
|
||
skippedCount++;
|
||
}
|
||
}
|
||
|
||
// 统一保存所有变更
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
_logger.LogInformation("批量重新生成完成,成功: {SuccessCount}, 跳过: {SkippedCount}", successCount, skippedCount);
|
||
|
||
return new BatchRegenerateResult
|
||
{
|
||
SuccessCount = successCount,
|
||
SkippedCount = skippedCount
|
||
};
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
public async Task DeleteRecordAsync(long id)
|
||
{
|
||
var record = await _dbContext.AssessmentRecords
|
||
.FirstOrDefaultAsync(r => r.Id == id && !r.IsDeleted);
|
||
|
||
if (record == null)
|
||
{
|
||
throw new BusinessException(ErrorCodes.AssessmentRecordNotFound, "测评记录不存在");
|
||
}
|
||
|
||
// 软删除
|
||
record.IsDeleted = true;
|
||
record.UpdateTime = DateTime.Now;
|
||
await _dbContext.SaveChangesAsync();
|
||
|
||
_logger.LogInformation("测评记录已删除,ID: {RecordId}", id);
|
||
}
|
||
|
||
/// <inheritdoc />
|
||
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 私有方法
|
||
|
||
/// <summary>
|
||
/// 分类类型名称映射
|
||
/// </summary>
|
||
private static readonly Dictionary<int, string> CategoryTypeNames = new()
|
||
{
|
||
{ 1, "八大智能" },
|
||
{ 2, "个人特质" },
|
||
{ 3, "细分能力" },
|
||
{ 4, "先天学习类型" },
|
||
{ 5, "学习关键能力" },
|
||
{ 6, "科学大脑类型" },
|
||
{ 7, "性格类型" },
|
||
{ 8, "未来发展能力" }
|
||
};
|
||
|
||
/// <summary>
|
||
/// 应用测评记录查询过滤条件
|
||
/// </summary>
|
||
/// <param name="query">查询</param>
|
||
/// <param name="request">请求</param>
|
||
/// <returns>过滤后的查询</returns>
|
||
private static IQueryable<AssessmentRecord> ApplyRecordQueryFilters(
|
||
IQueryable<AssessmentRecord> query,
|
||
AssessmentRecordQueryRequest request)
|
||
{
|
||
// 按用户ID筛选
|
||
if (request.UserId.HasValue)
|
||
{
|
||
query = query.Where(r => r.UserId == request.UserId.Value);
|
||
}
|
||
|
||
// 按测评类型ID筛选
|
||
if (request.AssessmentTypeId.HasValue)
|
||
{
|
||
query = query.Where(r => r.AssessmentTypeId == request.AssessmentTypeId.Value);
|
||
}
|
||
|
||
// 按状态筛选
|
||
if (request.Status.HasValue)
|
||
{
|
||
query = query.Where(r => r.Status == request.Status.Value);
|
||
}
|
||
|
||
// 按创建时间范围筛选 - 开始日期
|
||
if (request.StartDate.HasValue)
|
||
{
|
||
query = query.Where(r => r.CreateTime >= request.StartDate.Value);
|
||
}
|
||
|
||
// 按创建时间范围筛选 - 结束日期(包含当天)
|
||
if (request.EndDate.HasValue)
|
||
{
|
||
var endDate = request.EndDate.Value.AddDays(1);
|
||
query = query.Where(r => r.CreateTime < endDate);
|
||
}
|
||
|
||
return query;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取状态名称
|
||
/// </summary>
|
||
/// <param name="status">状态值</param>
|
||
/// <returns>状态名称</returns>
|
||
private static string GetStatusName(int status)
|
||
{
|
||
return StatusNames.TryGetValue(status, out var name) ? name : "未知";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取学历阶段名称
|
||
/// </summary>
|
||
/// <param name="educationStage">学历阶段值</param>
|
||
/// <returns>学历阶段名称</returns>
|
||
private static string GetEducationStageName(int educationStage)
|
||
{
|
||
return EducationStageNames.TryGetValue(educationStage, out var name) ? name : "未知";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取性别名称
|
||
/// </summary>
|
||
/// <param name="gender">性别值</param>
|
||
/// <returns>性别名称</returns>
|
||
private static string GetGenderName(int gender)
|
||
{
|
||
return GenderNames.TryGetValue(gender, 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>
|
||
/// 将星级映射到结论类型
|
||
/// 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 (最弱)
|
||
/// </summary>
|
||
/// <param name="starLevel">星级(1-5)</param>
|
||
/// <returns>结论类型(1-4)</returns>
|
||
private static int MapStarLevelToConclusionType(int starLevel)
|
||
{
|
||
return starLevel switch
|
||
{
|
||
5 => 1, // 最强
|
||
4 => 2, // 较强
|
||
3 => 2, // 较强
|
||
2 => 3, // 较弱
|
||
1 => 4, // 最弱
|
||
_ => 2 // 默认较强
|
||
};
|
||
}
|
||
|
||
#endregion
|
||
}
|