From 0227f066582130154f65a06dd4bd16f642220aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=B7=E6=AD=A6=20=E9=BB=84?= <865114447@qq.com> Date: Wed, 1 Jul 2026 15:04:42 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=AD=97=E6=AE=B5=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=EF=BC=8C=E4=BB=A5=E5=8F=8A=E7=BB=9F=E8=AE=A1=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BatchClaimPointsOutput.cs | 7 +- .../Entity/JournalPageTaskUserAnswer.cs | 2 +- .../UserAnswerTaskService.cs | 151 +++++++++++------- .../WeChat/UserAnswerTaskController.cs | 4 +- 4 files changed, 98 insertions(+), 66 deletions(-) diff --git a/QYZH.InteractiveMagazine.Models/Dto/UserAnswerTaskService/BatchClaimPointsOutput.cs b/QYZH.InteractiveMagazine.Models/Dto/UserAnswerTaskService/BatchClaimPointsOutput.cs index b9a2f48..8432e3e 100644 --- a/QYZH.InteractiveMagazine.Models/Dto/UserAnswerTaskService/BatchClaimPointsOutput.cs +++ b/QYZH.InteractiveMagazine.Models/Dto/UserAnswerTaskService/BatchClaimPointsOutput.cs @@ -8,7 +8,7 @@ public class BatchClaimPointsInput /// /// 任务分组Id列表(JournalPageTaskGroupId) /// - public List GroupIds { get; set; } = []; + public HashSet GroupIds { get; set; } = []; } /// @@ -73,6 +73,11 @@ public class BatchClaimFailItem /// public long GroupId { get; set; } + /// + /// 任务编号(从 JournalPageTask.No 字段按 - 分割取索引2,前面加"任务") + /// + public string TaskNo { get; set; } + /// /// 失败原因 /// diff --git a/QYZH.InteractiveMagazine.Models/Entity/JournalPageTaskUserAnswer.cs b/QYZH.InteractiveMagazine.Models/Entity/JournalPageTaskUserAnswer.cs index 3c5fc6f..2a18c41 100644 --- a/QYZH.InteractiveMagazine.Models/Entity/JournalPageTaskUserAnswer.cs +++ b/QYZH.InteractiveMagazine.Models/Entity/JournalPageTaskUserAnswer.cs @@ -63,7 +63,7 @@ namespace QYZH.InteractiveMagazine.Models.Entity /// Default:0 /// Nullable:False /// - public float GrowthPoints { get; set; } + public int GrowthPoints { get; set; } /// /// Desc:题目得分 diff --git a/QYZH.InteractiveMagazine.Service/UserAnswerTaskService.cs b/QYZH.InteractiveMagazine.Service/UserAnswerTaskService.cs index c946979..3d8a047 100644 --- a/QYZH.InteractiveMagazine.Service/UserAnswerTaskService.cs +++ b/QYZH.InteractiveMagazine.Service/UserAnswerTaskService.cs @@ -1218,7 +1218,7 @@ public class UserAnswerTaskService( var mainTask = relatedTasks.FirstOrDefault(t => t.Id == groupId); if (mainTask == null) { - throw new BusinessException("子题目无法单独领取积分,请从主任务入口领取", ResultCode.BAD_REQUEST); + throw new BusinessException("跨页题目需要完成所有任务才能领取积分", ResultCode.BAD_REQUEST); } // 查询用户答题记录 @@ -1275,8 +1275,8 @@ public class UserAnswerTaskService( } // 积分、成长值、得分(均取主任务数据) - int totalPoints = mainTask.Points; - int totalGrowthPoints = mainTask.GrowthPoint; + int totalPoints = mainTaskAnswer.Points; + int totalGrowthPoints = mainTaskAnswer.GrowthPoints; float score = mainTaskAnswer.Score; if (totalPoints <= 0) @@ -1315,6 +1315,7 @@ public class UserAnswerTaskService( await answerRepository.Context.Updateable() .SetColumns(u => u.Points == newPointsBalance) .SetColumns(u => u.GrowthPoints == newGrowthPointsBalance) + .SetColumns(u => u.UpdatedBy == userId.ToString()) .SetColumns(u => u.UpdatedAt == DateTime.Now) .Where(u => u.Id == userId) .ExecuteCommandAsync(); @@ -1401,82 +1402,111 @@ public class UserAnswerTaskService( { throw new BusinessException("任务分组Id列表不能为空", ResultCode.BAD_REQUEST); } - - var groupIds = input.GroupIds.Distinct().ToList(); + #endregion - #region 2. 批量查询已领取的积分记录 - // 批量查询已领取的积分记录(用于重复领取校验) + #region 2. 批量查询所有数据(一次性查询,避免N+1) + // ============================================ + // 2. 批量查询所有数据 + // - 所有 GroupId 对应的任务(JournalPageTask) + // - 所有 GroupId 对应的用户答题记录(JournalPageTaskUserAnswer) + // - 已领取的积分记录(用于重复领取校验) + // ============================================ + + // 2.1 批量查询所有相关任务(按 GroupId 分组) + var allTasks = await taskRepository.Context.Queryable() + .Where(t => input.GroupIds.Contains(t.GroupId)) + .ToListAsync(); + var tasksByGroupId = allTasks.GroupBy(t => t.GroupId).ToDictionary(g => g.Key, g => g.ToList()); + + // 2.2 批量查询所有用户答题记录(按 JournalPageTaskGroupId 分组) + var allUserAnswers = await answerRepository.Context.Queryable() + .Where(a => input.GroupIds.Contains(a.JournalPageTaskGroupId) && a.UserId == userId) + .ToListAsync(); + var answersByGroupId = allUserAnswers.GroupBy(a => a.JournalPageTaskGroupId).ToDictionary(g => g.Key, g => g.ToList()); + + // 2.3 批量查询已领取的积分记录(用于重复领取校验) var claimedRecords = await answerRepository.Context.Queryable() - .Where(r => groupIds.Contains((long)r.RelatedId!) && r.UserId == userId) - .Where(r => r.ChangeType == PointsChangeTypeEnum.TaskReward.ToString()) + .Where(r => input.GroupIds.Contains((long)r.RelatedId!) && r.UserId == userId) + .Where(r => r.ChangeType == nameof(PointsChangeTypeEnum.TaskReward)) .ToListAsync(); var claimedGroupIds = claimedRecords.Select(r => r.RelatedId!.Value).ToHashSet(); #endregion - #region 3. 逐个校验每个 GroupId,筛选可领取的任务 - // 存储可领取的任务信息 - var claimableTasks = new List<(long GroupId, int Points, int GrowthPoints, float Score, string TaskName, string TaskNo)>(); + #region 3. 内存中筛选可领取的任务 + // ============================================ + // 3. 在内存中逐个校验每个 GroupId,筛选可领取的任务 + // 所有数据已在步骤2中一次性查询完成,这里只做内存筛选 + // ============================================ + var claimableTasks = new List<(long GroupId, int Points, int GrowthPoints, float Score, string TaskNo)>(); - foreach (var groupId in groupIds) + foreach (var groupId in input.GroupIds) { try { - #region 3.1 根据 GroupId 查询所有相关任务 - var relatedTasks = await taskRepository.Context.Queryable() - .Where(t => t.GroupId == groupId) - .ToListAsync(); - - if (relatedTasks == null || relatedTasks.Count == 0) + // 3.1 从内存中获取相关任务 + if (!tasksByGroupId.TryGetValue(groupId, out var relatedTasks) || relatedTasks.Count == 0) { result.FailItems.Add(new BatchClaimFailItem { GroupId = groupId, + TaskNo = string.Empty, Message = "任务不存在" }); continue; } - #endregion - #region 3.2 判断是否跨页题目并找主任务 + // 3.2 判断是否跨页题目并找主任务 bool isCrossPage = relatedTasks.Count > 1; - // 主任务:Id == GroupId var mainTask = relatedTasks.FirstOrDefault(t => t.Id == groupId); + + // ============================================ + // 【任务编号计算】 + // 从 JournalPageTask.No 字段按 - 分割,取索引为 2 的部分,前面加"任务" + // 如果找不到主任务,尝试从子任务中计算 + // ============================================ + string taskNo = string.Empty; + var taskForNo = mainTask ?? relatedTasks.FirstOrDefault(); + if (taskForNo != null && !string.IsNullOrEmpty(taskForNo.No)) + { + var parts = taskForNo.No.Split('-'); + if (parts.Length >= 3) + { + taskNo = $"任务{parts[2]}"; + } + } + if (mainTask == null) { result.FailItems.Add(new BatchClaimFailItem { GroupId = groupId, + TaskNo = taskNo, Message = "子题目无法单独领取积分,请从主任务入口领取" }); continue; } - #endregion - #region 3.3 查询用户答题记录 - var userAnswers = await answerRepository.Context.Queryable() - .Where(a => a.JournalPageTaskGroupId == groupId && a.UserId == userId) - .ToListAsync(); - - if (userAnswers == null || userAnswers.Count == 0) + // 3.3 从内存中获取用户答题记录 + if (!answersByGroupId.TryGetValue(groupId, out var userAnswers) || userAnswers.Count == 0) { result.FailItems.Add(new BatchClaimFailItem { GroupId = groupId, + TaskNo = taskNo, Message = "请先完成答题再领取积分" }); continue; } - #endregion - #region 3.4 完成校验 - // 校验主任务完成状态:Status=1 + // 3.4 完成校验 var mainTaskAnswer = userAnswers.FirstOrDefault(a => a.JournalPageTaskId == groupId); if (mainTaskAnswer == null || mainTaskAnswer.Status != (int)UserAnswerStatusEnum.Complete) { result.FailItems.Add(new BatchClaimFailItem { GroupId = groupId, + TaskNo = taskNo, Message = "请先完成答题再领取积分" }); continue; @@ -1485,43 +1515,38 @@ public class UserAnswerTaskService( // 跨页题目:校验所有任务 if (isCrossPage) { - bool allCompleted = true; - foreach (var task in relatedTasks) + bool allCompleted = relatedTasks.All(task => { var answer = userAnswers.FirstOrDefault(a => a.JournalPageTaskId == task.Id); - if (answer == null || answer.Status != (int)UserAnswerStatusEnum.Complete) - { - allCompleted = false; - break; - } - } + return answer != null && answer.Status == (int)UserAnswerStatusEnum.Complete; + }); if (!allCompleted) { result.FailItems.Add(new BatchClaimFailItem { GroupId = groupId, + TaskNo = taskNo, Message = "跨页题目需要完成所有任务才能领取积分" }); continue; } } - #endregion - #region 3.5 重复领取校验 + // 3.5 重复领取校验 if (claimedGroupIds.Contains(groupId)) { result.FailItems.Add(new BatchClaimFailItem { GroupId = groupId, + TaskNo = taskNo, Message = "积分已领取,请勿重复领取" }); continue; } - #endregion - #region 3.6 计算积分、成长值、得分 - int points = mainTask.Points; - int growthPoints = mainTask.GrowthPoint; + // 3.6 计算积分、成长值、得分 + int points = mainTaskAnswer.Points; + int growthPoints = mainTaskAnswer.GrowthPoints; float score = mainTaskAnswer.Score; if (points <= 0) @@ -1529,34 +1554,35 @@ public class UserAnswerTaskService( result.FailItems.Add(new BatchClaimFailItem { GroupId = groupId, + TaskNo = taskNo, Message = "该任务无积分可领取" }); continue; } - var taskName = mainTask.Task ?? string.Empty; - - // 任务编号计算 - string taskNo = string.Empty; - if (!string.IsNullOrEmpty(mainTask.No)) - { - var parts = mainTask.No.Split('-'); - if (parts.Length >= 3) - { - taskNo = $"任务{parts[2]}"; - } - } - #endregion - // 添加到可领取列表 - claimableTasks.Add((groupId, points, growthPoints, score, taskName, taskNo)); + claimableTasks.Add((groupId, points, growthPoints, score, taskNo)); } catch (Exception ex) { - // 捕获异常并添加到失败列表,不中断其他任务的处理 + // 异常情况下,尝试从已查询的数据中计算 taskNo + string taskNo = string.Empty; + if (tasksByGroupId.TryGetValue(groupId, out var tasks) && tasks.Count > 0) + { + var task = tasks.FirstOrDefault(t => t.Id == groupId) ?? tasks.FirstOrDefault(); + if (task != null && !string.IsNullOrEmpty(task.No)) + { + var parts = task.No.Split('-'); + if (parts.Length >= 3) + { + taskNo = $"任务{parts[2]}"; + } + } + } result.FailItems.Add(new BatchClaimFailItem { GroupId = groupId, + TaskNo = taskNo, Message = ex.Message }); } @@ -1612,6 +1638,7 @@ public class UserAnswerTaskService( await answerRepository.Context.Updateable() .SetColumns(u => u.Points == newPointsBalance) .SetColumns(u => u.GrowthPoints == newGrowthPointsBalance) + .SetColumns(u => u.UpdatedBy == userId.ToString()) .SetColumns(u => u.UpdatedAt == DateTime.Now) .Where(u => u.Id == userId) .ExecuteCommandAsync(); diff --git a/QYZH.InteractiveMagazine.WebApi/Controllers/WeChat/UserAnswerTaskController.cs b/QYZH.InteractiveMagazine.WebApi/Controllers/WeChat/UserAnswerTaskController.cs index 6290909..e80782f 100644 --- a/QYZH.InteractiveMagazine.WebApi/Controllers/WeChat/UserAnswerTaskController.cs +++ b/QYZH.InteractiveMagazine.WebApi/Controllers/WeChat/UserAnswerTaskController.cs @@ -203,7 +203,7 @@ public class UserAnswerTaskController : WeChatBaseController { try { - var userId = ConstUserId;//GetCurrentUserId(); + var userId = GetCurrentUserId(); if (userId == 0) { return BaseResponse.Fail(ResultCode.DENY, "未获取到用户信息"); @@ -239,7 +239,7 @@ public class UserAnswerTaskController : WeChatBaseController { try { - var userId = GetCurrentUserId(); + var userId = ConstUserId;//GetCurrentUserId(); if (userId == 0) { return BaseResponse.Fail(ResultCode.DENY, "未获取到用户信息");