This commit is contained in:
glz
2026-07-01 17:05:41 +08:00
4 changed files with 98 additions and 66 deletions

View File

@ -8,7 +8,7 @@ public class BatchClaimPointsInput
/// <summary> /// <summary>
/// 任务分组Id列表JournalPageTaskGroupId /// 任务分组Id列表JournalPageTaskGroupId
/// </summary> /// </summary>
public List<long> GroupIds { get; set; } = []; public HashSet<long> GroupIds { get; set; } = [];
} }
/// <summary> /// <summary>
@ -73,6 +73,11 @@ public class BatchClaimFailItem
/// </summary> /// </summary>
public long GroupId { get; set; } public long GroupId { get; set; }
/// <summary>
/// 任务编号(从 JournalPageTask.No 字段按 - 分割取索引2前面加"任务"
/// </summary>
public string TaskNo { get; set; }
/// <summary> /// <summary>
/// 失败原因 /// 失败原因
/// </summary> /// </summary>

View File

@ -63,7 +63,7 @@ namespace QYZH.InteractiveMagazine.Models.Entity
/// Default:0 /// Default:0
/// Nullable:False /// Nullable:False
/// </summary> /// </summary>
public float GrowthPoints { get; set; } public int GrowthPoints { get; set; }
/// <summary> /// <summary>
/// Desc:题目得分 /// Desc:题目得分

View File

@ -1218,7 +1218,7 @@ public class UserAnswerTaskService(
var mainTask = relatedTasks.FirstOrDefault(t => t.Id == groupId); var mainTask = relatedTasks.FirstOrDefault(t => t.Id == groupId);
if (mainTask == null) 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 totalPoints = mainTaskAnswer.Points;
int totalGrowthPoints = mainTask.GrowthPoint; int totalGrowthPoints = mainTaskAnswer.GrowthPoints;
float score = mainTaskAnswer.Score; float score = mainTaskAnswer.Score;
if (totalPoints <= 0) if (totalPoints <= 0)
@ -1315,6 +1315,7 @@ public class UserAnswerTaskService(
await answerRepository.Context.Updateable<Users>() await answerRepository.Context.Updateable<Users>()
.SetColumns(u => u.Points == newPointsBalance) .SetColumns(u => u.Points == newPointsBalance)
.SetColumns(u => u.GrowthPoints == newGrowthPointsBalance) .SetColumns(u => u.GrowthPoints == newGrowthPointsBalance)
.SetColumns(u => u.UpdatedBy == userId.ToString())
.SetColumns(u => u.UpdatedAt == DateTime.Now) .SetColumns(u => u.UpdatedAt == DateTime.Now)
.Where(u => u.Id == userId) .Where(u => u.Id == userId)
.ExecuteCommandAsync(); .ExecuteCommandAsync();
@ -1401,82 +1402,111 @@ public class UserAnswerTaskService(
{ {
throw new BusinessException("任务分组Id列表不能为空", ResultCode.BAD_REQUEST); throw new BusinessException("任务分组Id列表不能为空", ResultCode.BAD_REQUEST);
} }
var groupIds = input.GroupIds.Distinct().ToList();
#endregion #endregion
#region 2. #region 2. N+1
// 批量查询已领取的积分记录(用于重复领取校验) // ============================================
// 2. 批量查询所有数据
// - 所有 GroupId 对应的任务JournalPageTask
// - 所有 GroupId 对应的用户答题记录JournalPageTaskUserAnswer
// - 已领取的积分记录(用于重复领取校验)
// ============================================
// 2.1 批量查询所有相关任务(按 GroupId 分组)
var allTasks = await taskRepository.Context.Queryable<JournalPageTask>()
.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<JournalPageTaskUserAnswer>()
.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<PointsRecord>() var claimedRecords = await answerRepository.Context.Queryable<PointsRecord>()
.Where(r => groupIds.Contains((long)r.RelatedId!) && r.UserId == userId) .Where(r => input.GroupIds.Contains((long)r.RelatedId!) && r.UserId == userId)
.Where(r => r.ChangeType == PointsChangeTypeEnum.TaskReward.ToString()) .Where(r => r.ChangeType == nameof(PointsChangeTypeEnum.TaskReward))
.ToListAsync(); .ToListAsync();
var claimedGroupIds = claimedRecords.Select(r => r.RelatedId!.Value).ToHashSet(); var claimedGroupIds = claimedRecords.Select(r => r.RelatedId!.Value).ToHashSet();
#endregion #endregion
#region 3. GroupId #region 3.
// 存储可领取的任务信息 // ============================================
var claimableTasks = new List<(long GroupId, int Points, int GrowthPoints, float Score, string TaskName, string TaskNo)>(); // 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 try
{ {
#region 3.1 GroupId // 3.1 从内存中获取相关任
var relatedTasks = await taskRepository.Context.Queryable<JournalPageTask>() if (!tasksByGroupId.TryGetValue(groupId, out var relatedTasks) || relatedTasks.Count == 0)
.Where(t => t.GroupId == groupId)
.ToListAsync();
if (relatedTasks == null || relatedTasks.Count == 0)
{ {
result.FailItems.Add(new BatchClaimFailItem result.FailItems.Add(new BatchClaimFailItem
{ {
GroupId = groupId, GroupId = groupId,
TaskNo = string.Empty,
Message = "任务不存在" Message = "任务不存在"
}); });
continue; continue;
} }
#endregion
#region 3.2 // 3.2 判断是否跨页题目并找主任
bool isCrossPage = relatedTasks.Count > 1; bool isCrossPage = relatedTasks.Count > 1;
// 主任务Id == GroupId
var mainTask = relatedTasks.FirstOrDefault(t => t.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) if (mainTask == null)
{ {
result.FailItems.Add(new BatchClaimFailItem result.FailItems.Add(new BatchClaimFailItem
{ {
GroupId = groupId, GroupId = groupId,
TaskNo = taskNo,
Message = "子题目无法单独领取积分,请从主任务入口领取" Message = "子题目无法单独领取积分,请从主任务入口领取"
}); });
continue; continue;
} }
#endregion
#region 3.3 // 3.3 从内存中获取用户答题记
var userAnswers = await answerRepository.Context.Queryable<JournalPageTaskUserAnswer>() if (!answersByGroupId.TryGetValue(groupId, out var userAnswers) || userAnswers.Count == 0)
.Where(a => a.JournalPageTaskGroupId == groupId && a.UserId == userId)
.ToListAsync();
if (userAnswers == null || userAnswers.Count == 0)
{ {
result.FailItems.Add(new BatchClaimFailItem result.FailItems.Add(new BatchClaimFailItem
{ {
GroupId = groupId, GroupId = groupId,
TaskNo = taskNo,
Message = "请先完成答题再领取积分" Message = "请先完成答题再领取积分"
}); });
continue; continue;
} }
#endregion
#region 3.4 // 3.4 完成校
// 校验主任务完成状态Status=1
var mainTaskAnswer = userAnswers.FirstOrDefault(a => a.JournalPageTaskId == groupId); var mainTaskAnswer = userAnswers.FirstOrDefault(a => a.JournalPageTaskId == groupId);
if (mainTaskAnswer == null || mainTaskAnswer.Status != (int)UserAnswerStatusEnum.Complete) if (mainTaskAnswer == null || mainTaskAnswer.Status != (int)UserAnswerStatusEnum.Complete)
{ {
result.FailItems.Add(new BatchClaimFailItem result.FailItems.Add(new BatchClaimFailItem
{ {
GroupId = groupId, GroupId = groupId,
TaskNo = taskNo,
Message = "请先完成答题再领取积分" Message = "请先完成答题再领取积分"
}); });
continue; continue;
@ -1485,43 +1515,38 @@ public class UserAnswerTaskService(
// 跨页题目:校验所有任务 // 跨页题目:校验所有任务
if (isCrossPage) if (isCrossPage)
{ {
bool allCompleted = true; bool allCompleted = relatedTasks.All(task =>
foreach (var task in relatedTasks)
{ {
var answer = userAnswers.FirstOrDefault(a => a.JournalPageTaskId == task.Id); var answer = userAnswers.FirstOrDefault(a => a.JournalPageTaskId == task.Id);
if (answer == null || answer.Status != (int)UserAnswerStatusEnum.Complete) return answer != null && answer.Status == (int)UserAnswerStatusEnum.Complete;
{ });
allCompleted = false;
break;
}
}
if (!allCompleted) if (!allCompleted)
{ {
result.FailItems.Add(new BatchClaimFailItem result.FailItems.Add(new BatchClaimFailItem
{ {
GroupId = groupId, GroupId = groupId,
TaskNo = taskNo,
Message = "跨页题目需要完成所有任务才能领取积分" Message = "跨页题目需要完成所有任务才能领取积分"
}); });
continue; continue;
} }
} }
#endregion
#region 3.5 // 3.5 重复领取校
if (claimedGroupIds.Contains(groupId)) if (claimedGroupIds.Contains(groupId))
{ {
result.FailItems.Add(new BatchClaimFailItem result.FailItems.Add(new BatchClaimFailItem
{ {
GroupId = groupId, GroupId = groupId,
TaskNo = taskNo,
Message = "积分已领取,请勿重复领取" Message = "积分已领取,请勿重复领取"
}); });
continue; continue;
} }
#endregion
#region 3.6 // 3.6 计算积分、成长值、得
int points = mainTask.Points; int points = mainTaskAnswer.Points;
int growthPoints = mainTask.GrowthPoint; int growthPoints = mainTaskAnswer.GrowthPoints;
float score = mainTaskAnswer.Score; float score = mainTaskAnswer.Score;
if (points <= 0) if (points <= 0)
@ -1529,34 +1554,35 @@ public class UserAnswerTaskService(
result.FailItems.Add(new BatchClaimFailItem result.FailItems.Add(new BatchClaimFailItem
{ {
GroupId = groupId, GroupId = groupId,
TaskNo = taskNo,
Message = "该任务无积分可领取" Message = "该任务无积分可领取"
}); });
continue; 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) 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 result.FailItems.Add(new BatchClaimFailItem
{ {
GroupId = groupId, GroupId = groupId,
TaskNo = taskNo,
Message = ex.Message Message = ex.Message
}); });
} }
@ -1612,6 +1638,7 @@ public class UserAnswerTaskService(
await answerRepository.Context.Updateable<Users>() await answerRepository.Context.Updateable<Users>()
.SetColumns(u => u.Points == newPointsBalance) .SetColumns(u => u.Points == newPointsBalance)
.SetColumns(u => u.GrowthPoints == newGrowthPointsBalance) .SetColumns(u => u.GrowthPoints == newGrowthPointsBalance)
.SetColumns(u => u.UpdatedBy == userId.ToString())
.SetColumns(u => u.UpdatedAt == DateTime.Now) .SetColumns(u => u.UpdatedAt == DateTime.Now)
.Where(u => u.Id == userId) .Where(u => u.Id == userId)
.ExecuteCommandAsync(); .ExecuteCommandAsync();

View File

@ -203,7 +203,7 @@ public class UserAnswerTaskController : WeChatBaseController
{ {
try try
{ {
var userId = ConstUserId;//GetCurrentUserId(); var userId = GetCurrentUserId();
if (userId == 0) if (userId == 0)
{ {
return BaseResponse<ClaimTaskPointsOutput>.Fail(ResultCode.DENY, "未获取到用户信息"); return BaseResponse<ClaimTaskPointsOutput>.Fail(ResultCode.DENY, "未获取到用户信息");
@ -239,7 +239,7 @@ public class UserAnswerTaskController : WeChatBaseController
{ {
try try
{ {
var userId = GetCurrentUserId(); var userId = ConstUserId;//GetCurrentUserId();
if (userId == 0) if (userId == 0)
{ {
return BaseResponse<BatchClaimPointsOutput>.Fail(ResultCode.DENY, "未获取到用户信息"); return BaseResponse<BatchClaimPointsOutput>.Fail(ResultCode.DENY, "未获取到用户信息");