|
|
@@ -2,8 +2,10 @@ package com.ylx.point.service.impl;
|
|
|
|
|
|
import cn.hutool.core.collection.CollectionUtil;
|
|
|
import cn.hutool.core.date.DateUtil;
|
|
|
+import cn.hutool.core.util.ObjUtil;
|
|
|
import cn.hutool.core.util.ObjectUtil;
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
import com.ylx.common.core.domain.model.WxLoginUser;
|
|
|
@@ -16,10 +18,7 @@ import com.ylx.point.domain.PointActivity;
|
|
|
import com.ylx.point.domain.PointActivityTask;
|
|
|
import com.ylx.point.domain.PointUserActivityTaskCompletion;
|
|
|
import com.ylx.point.domain.vo.PointActivityOverviewVO;
|
|
|
-import com.ylx.point.enums.TaskNameEnum;
|
|
|
-import com.ylx.point.enums.PointActivityStatusEnum;
|
|
|
-import com.ylx.point.enums.PointActivityTaskTypeEnum;
|
|
|
-import com.ylx.point.enums.PointActivityTypeEnum;
|
|
|
+import com.ylx.point.enums.*;
|
|
|
import com.ylx.point.enums.TaskNameEnum;
|
|
|
import com.ylx.point.mapper.PointUserActivityTaskCompletionMapper;
|
|
|
import com.ylx.point.service.IPointAccountService;
|
|
|
@@ -63,11 +62,7 @@ public class PointUserActivityTaskCompletionServiceImpl extends ServiceImpl<Poin
|
|
|
/**
|
|
|
* 活动类型优先级:新手活动 > 每日活动 > 每月活动
|
|
|
*/
|
|
|
- private static final List<Integer> ACTIVITY_TYPE_PRIORITY = Arrays.asList(
|
|
|
- PointActivityTypeEnum.NEW_USER_ACTIVITY.getCode(),
|
|
|
- PointActivityTypeEnum.DAILY_ACTIVITY.getCode(),
|
|
|
- PointActivityTypeEnum.MONTHLY_ACTIVITY.getCode()
|
|
|
- );
|
|
|
+ private static final List<Integer> ACTIVITY_TYPE_PRIORITY = Arrays.asList(PointActivityTypeEnum.NEW_USER_ACTIVITY.getCode(), PointActivityTypeEnum.DAILY_ACTIVITY.getCode(), PointActivityTypeEnum.MONTHLY_ACTIVITY.getCode());
|
|
|
|
|
|
@Override
|
|
|
public PointActivityOverviewVO getPointActivityOverviewByActivityId(Long activityId) {
|
|
|
@@ -92,13 +87,7 @@ public class PointUserActivityTaskCompletionServiceImpl extends ServiceImpl<Poin
|
|
|
*/
|
|
|
@Override
|
|
|
public boolean isTodayCompleted(String openId, Long activityId, Long taskId, Integer taskType) {
|
|
|
- return pointUserActivityTaskCompletionMapper.exists(Wrappers.lambdaQuery(PointUserActivityTaskCompletion.class)
|
|
|
- .eq(PointUserActivityTaskCompletion::getOpenId, openId)
|
|
|
- .eq(PointUserActivityTaskCompletion::getActivityId, activityId)
|
|
|
- .eq(PointUserActivityTaskCompletion::getTaskId, taskId)
|
|
|
- .eq(PointUserActivityTaskCompletion::getTaskType, taskType)
|
|
|
- .eq(PointUserActivityTaskCompletion::getCompletedDate, LocalDate.now())
|
|
|
- );
|
|
|
+ return pointUserActivityTaskCompletionMapper.exists(Wrappers.lambdaQuery(PointUserActivityTaskCompletion.class).eq(PointUserActivityTaskCompletion::getOpenId, openId).eq(PointUserActivityTaskCompletion::getActivityId, activityId).eq(PointUserActivityTaskCompletion::getTaskId, taskId).eq(PointUserActivityTaskCompletion::getTaskType, taskType).eq(PointUserActivityTaskCompletion::getCompletedDate, LocalDate.now()));
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@@ -183,8 +172,8 @@ public class PointUserActivityTaskCompletionServiceImpl extends ServiceImpl<Poin
|
|
|
if (isVerified) {
|
|
|
Date queryTime = getQueryStartTime(activity.getIsPermanent(), activity.getActivityType());
|
|
|
// 防重复发奖:已达标则直接返回
|
|
|
- PointUserActivityTaskCompletion record = this.getTaskRecord(openId, activityId, taskId, taskTypeEnum.getCode());
|
|
|
- if (record != null && record.getCompletedCount() >= targetCount) {
|
|
|
+ PointUserActivityTaskCompletion record = this.getTaskRecordInPeriod(openId, activityId, taskId, taskTypeEnum.getCode(), queryTime);
|
|
|
+ if (ObjectUtil.isNotNull(record) && record.getCompletedCount() >= targetCount) {
|
|
|
throw new ServiceException("任务奖励已领取");
|
|
|
}
|
|
|
|
|
|
@@ -263,111 +252,179 @@ public class PointUserActivityTaskCompletionServiceImpl extends ServiceImpl<Poin
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 获取任务完成记录
|
|
|
+ * 查询当前周期内的任务记录
|
|
|
+ * 这里需要你修改 Mapper/DAO 层,支持按时间范围查询
|
|
|
*/
|
|
|
- private PointUserActivityTaskCompletion getTaskRecord(String openId, Long activityId, Long taskId, Integer taskType) {
|
|
|
- return this.getOne(Wrappers.lambdaQuery(PointUserActivityTaskCompletion.class)
|
|
|
- .eq(PointUserActivityTaskCompletion::getOpenId, openId)
|
|
|
- .eq(PointUserActivityTaskCompletion::getActivityId, activityId)
|
|
|
- .eq(PointUserActivityTaskCompletion::getTaskId, taskId)
|
|
|
- .eq(PointUserActivityTaskCompletion::getTaskType, taskType)
|
|
|
- );
|
|
|
+ private PointUserActivityTaskCompletion getTaskRecordInPeriod(String openId, Long activityId, Long taskId, Integer taskTypeCode, Date startTime) {
|
|
|
+ // 构建查询条件
|
|
|
+ LambdaQueryWrapper<PointUserActivityTaskCompletion> wrapper = Wrappers.lambdaQuery(PointUserActivityTaskCompletion.class).eq(PointUserActivityTaskCompletion::getOpenId, openId).eq(PointUserActivityTaskCompletion::getActivityId, activityId).eq(PointUserActivityTaskCompletion::getTaskId, taskId).eq(PointUserActivityTaskCompletion::getTaskType, taskTypeCode);
|
|
|
+
|
|
|
+ // 核心:如果 startTime 不为空,则限制在该时间之后(即本周期内)
|
|
|
+ if (startTime != null) {
|
|
|
+ wrapper.ge(PointUserActivityTaskCompletion::getCreateTime, startTime);
|
|
|
+ }
|
|
|
+
|
|
|
+ return this.getOne(wrapper);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 通用优先级任务完成方法
|
|
|
+ * 按活动类型优先级(新手 > 每日 > 每月)遍历匹配任务,完成首个合格任务后立即返回
|
|
|
*/
|
|
|
- private void completeTaskByPriority(String openId, TaskNameEnum activityNameEnum,
|
|
|
- PointActivityTaskTypeEnum taskTypeEnum,
|
|
|
- Function<Date, Integer> countFunction) {
|
|
|
- // 批量查询所有有效任务
|
|
|
+ private void completeTaskByPriority(String openId, TaskNameEnum activityNameEnum, PointActivityTaskTypeEnum taskTypeEnum, Function<Date, Integer> countFunction) {
|
|
|
+ // 1. 批量查询所有有效任务
|
|
|
List<PointActivityTask> allTasks = pointActivityTaskService.list(Wrappers.lambdaQuery(PointActivityTask.class)
|
|
|
.eq(PointActivityTask::getTaskName, activityNameEnum.getInfo())
|
|
|
.eq(PointActivityTask::getStatus, 0)
|
|
|
- .eq(PointActivityTask::getIsDeleted, 0)
|
|
|
- );
|
|
|
+ .eq(PointActivityTask::getIsDeleted, 0));
|
|
|
if (CollectionUtil.isEmpty(allTasks)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // 批量查询关联活动,解决N+1查询
|
|
|
+ // 2. 批量查询关联活动,避免N+1查询
|
|
|
Set<Long> activityIds = allTasks.stream().map(PointActivityTask::getActivityId).collect(Collectors.toSet());
|
|
|
- List<PointActivity> activityList = pointActivityService.listByIds(activityIds);
|
|
|
- Map<Long, PointActivity> activityMap = activityList.stream().collect(Collectors.toMap(PointActivity::getId, a -> a));
|
|
|
+ Map<Long, PointActivity> activityMap = pointActivityService.listByIds(activityIds).stream()
|
|
|
+ .collect(Collectors.toMap(PointActivity::getId, a -> a));
|
|
|
|
|
|
- // 按优先级处理活动类型
|
|
|
+ // 3. 按优先级遍历活动类型
|
|
|
for (Integer typeCode : ACTIVITY_TYPE_PRIORITY) {
|
|
|
- // 过滤当前类型的有效任务
|
|
|
- List<PointActivityTask> taskList = allTasks.stream()
|
|
|
- .filter(task -> {
|
|
|
- PointActivity activity = activityMap.get(task.getActivityId());
|
|
|
- return activity != null
|
|
|
- && Objects.equals(activity.getActivityType(), typeCode)
|
|
|
- && Objects.equals(activity.getStatus(), PointActivityStatusEnum.IN_PROGRESS.getCode());
|
|
|
- })
|
|
|
- .collect(Collectors.toList());
|
|
|
-
|
|
|
- if (CollectionUtil.isEmpty(taskList)) {
|
|
|
+ List<PointActivityTask> filteredTasks = filterValidTasks(allTasks, activityMap, typeCode);
|
|
|
+ if (CollectionUtil.isEmpty(filteredTasks)) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- // 执行任务
|
|
|
- for (PointActivityTask task : taskList) {
|
|
|
- try {
|
|
|
- PointActivity activity = activityMap.get(task.getActivityId());
|
|
|
- int targetCount = Integer.parseInt(task.getTriggerValue());
|
|
|
- Date queryTime = getQueryStartTime(activity.getIsPermanent(), typeCode);
|
|
|
- int completed = countFunction.apply(queryTime);
|
|
|
-
|
|
|
- if (completed < targetCount) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- // 防重复领取
|
|
|
- PointUserActivityTaskCompletion record = getTaskRecord(openId, activity.getId(), task.getId(), taskTypeEnum.getCode());
|
|
|
- if (record != null && record.getCompletedCount() >= targetCount) {
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- // 完成任务+发奖
|
|
|
- completeTask(openId, activity.getId(), task.getId(), taskTypeEnum.getCode());
|
|
|
- pointAccountService.addPoints(openId, task.getRewardPoints().intValue(),
|
|
|
- activity.getName(), null, activity.getId(), task.getId(),
|
|
|
- taskTypeEnum.getCode(), activityNameEnum.getInfo());
|
|
|
- return;
|
|
|
- } catch (ServiceException e) {
|
|
|
- if (!e.getMessage().contains("已完成") && !e.getMessage().contains("已领取")) {
|
|
|
- throw e;
|
|
|
- }
|
|
|
- log.warn("任务处理异常:{}", e.getMessage());
|
|
|
+ // 4. 遍历当前类型的任务,尝试完成
|
|
|
+ for (PointActivityTask task : filteredTasks) {
|
|
|
+ PointActivity activity = activityMap.get(task.getActivityId());
|
|
|
+ if (tryCompleteSingleTask(openId, task, activity, typeCode, taskTypeEnum, activityNameEnum, countFunction)) {
|
|
|
+ return; // 完成首个合格任务后立即返回
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 过滤指定活动类型下的有效任务(活动存在 + 类型匹配 + 进行中)
|
|
|
+ */
|
|
|
+ private List<PointActivityTask> filterValidTasks(List<PointActivityTask> allTasks, Map<Long, PointActivity> activityMap, Integer typeCode) {
|
|
|
+ return allTasks.stream().filter(task -> {
|
|
|
+ PointActivity activity = activityMap.get(task.getActivityId());
|
|
|
+ return activity != null
|
|
|
+ && Objects.equals(activity.getActivityType(), typeCode)
|
|
|
+ && Objects.equals(activity.getStatus(), PointActivityStatusEnum.IN_PROGRESS.getCode());
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 尝试完成单个任务,返回是否成功完成
|
|
|
+ */
|
|
|
+ private boolean tryCompleteSingleTask(String openId, PointActivityTask task, PointActivity activity,
|
|
|
+ Integer typeCode, PointActivityTaskTypeEnum taskTypeEnum,
|
|
|
+ TaskNameEnum activityNameEnum, Function<Date, Integer> countFunction) {
|
|
|
+ try {
|
|
|
+ // 1. 解析频率限制
|
|
|
+ TaskLimitTimesEnum limitEnum = parseFrequencyLimit(task);
|
|
|
+ if (limitEnum == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 校验业务数据是否达标
|
|
|
+ Date queryTime = getQueryStartTime(activity.getIsPermanent(), typeCode);
|
|
|
+ if (!isTaskTargetMet(task, taskTypeEnum, limitEnum.getTime(), countFunction, queryTime)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 防重复领取:利用 TaskLimitTimesEnum 策略模式校验
|
|
|
+ PointUserActivityTaskCompletion record = getTaskRecordInPeriod(openId, activity.getId(), task.getId(), taskTypeEnum.getCode(), queryTime);
|
|
|
+ int completedCount = (record != null) ? record.getCompletedCount() : 0;
|
|
|
+ if (!limitEnum.executeCheck(completedCount)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 完成任务 + 发放积分
|
|
|
+ completeTask(openId, activity.getId(), task.getId(), taskTypeEnum.getCode());
|
|
|
+ pointAccountService.addPoints(openId, task.getRewardPoints().intValue(), activity.getName(),
|
|
|
+ null, activity.getId(), task.getId(), taskTypeEnum.getCode(), activityNameEnum.getInfo());
|
|
|
+ return true;
|
|
|
+ } catch (ServiceException e) {
|
|
|
+ if (!e.getMessage().contains("已完成") && !e.getMessage().contains("已领取")) {
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ log.warn("任务处理异常:{}", e.getMessage());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析任务频率限制配置,返回null表示配置异常(调用方应跳过该任务)
|
|
|
+ */
|
|
|
+ private TaskLimitTimesEnum parseFrequencyLimit(PointActivityTask task) {
|
|
|
+ String frequencyLimitValue = task.getFrequencyLimitValue();
|
|
|
+ if (StrUtil.isBlank(frequencyLimitValue)) {
|
|
|
+ log.warn("任务未配置频率限制值,跳过: taskId={}", task.getId());
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ TaskLimitTimesEnum limitEnum = TaskLimitTimesEnum.getByValue(frequencyLimitValue);
|
|
|
+ if (limitEnum == null || limitEnum.getTime() == null || limitEnum.getTime() <= 0) {
|
|
|
+ log.warn("任务配置异常,跳过处理: taskId={}", task.getId());
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return limitEnum;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 校验任务业务目标是否达成
|
|
|
+ * - 充值任务:实际充值金额 >= triggerValue 目标值
|
|
|
+ * - 完成订单:实际完成次数 >= 频率限制次数
|
|
|
+ */
|
|
|
+ private boolean isTaskTargetMet(PointActivityTask task, PointActivityTaskTypeEnum taskTypeEnum,
|
|
|
+ int targetCount, Function<Date, Integer> countFunction, Date queryTime) {
|
|
|
+ int actualCount = countFunction.apply(queryTime);
|
|
|
+
|
|
|
+ if (ObjUtil.equals(taskTypeEnum.getCode(), PointActivityTaskTypeEnum.RECHARGE_TASK.getCode())) {
|
|
|
+ // 充值任务:triggerValue 为目标金额
|
|
|
+ String targetValueStr = task.getTriggerValue();
|
|
|
+ if (StrUtil.isBlank(targetValueStr)) {
|
|
|
+ log.warn("任务未配置目标值,跳过: taskId={}", task.getId());
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ int targetValue;
|
|
|
+ try {
|
|
|
+ targetValue = Integer.parseInt(targetValueStr);
|
|
|
+ } catch (NumberFormatException e) {
|
|
|
+ log.warn("任务目标值格式错误,跳过: taskId={}, value={}", task.getId(), targetValueStr);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return actualCount >= targetValue;
|
|
|
+ } else if (ObjUtil.equals(taskTypeEnum.getCode(), PointActivityTaskTypeEnum.COMPLETE_ORDER.getCode())) {
|
|
|
+ // 完成订单:实际完成次数 >= 目标次数
|
|
|
+ return actualCount >= targetCount;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 其他任务类型(分享、浏览等)默认视为已达标
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public void completeOrderTaskByPriority(String openId) {
|
|
|
- completeTaskByPriority(openId, TaskNameEnum.COMPLETE_ORDER,
|
|
|
- PointActivityTaskTypeEnum.COMPLETE_ORDER,
|
|
|
- new Function<Date, Integer>() {
|
|
|
- @Override
|
|
|
- public Integer apply(Date queryTime) {
|
|
|
- return orderService.countCompletedOrders(openId, queryTime);
|
|
|
- }
|
|
|
- });
|
|
|
+ completeTaskByPriority(openId, TaskNameEnum.COMPLETE_ORDER, PointActivityTaskTypeEnum.COMPLETE_ORDER, new Function<Date, Integer>() {
|
|
|
+ @Override
|
|
|
+ public Integer apply(Date queryTime) {
|
|
|
+ return orderService.countCompletedOrders(openId, queryTime);
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public void completeRechargeTaskByPriority(String openId) {
|
|
|
- completeTaskByPriority(openId, TaskNameEnum.RECHARGE_TASK,
|
|
|
- PointActivityTaskTypeEnum.RECHARGE_TASK,
|
|
|
- new Function<Date, Integer>() {
|
|
|
- @Override
|
|
|
- public Integer apply(Date queryTime) {
|
|
|
- return rechargeService.countSuccessRecharges(openId, queryTime);
|
|
|
- }
|
|
|
- });
|
|
|
+ completeTaskByPriority(openId, TaskNameEnum.RECHARGE_TASK, PointActivityTaskTypeEnum.RECHARGE_TASK, new Function<Date, Integer>() {
|
|
|
+ @Override
|
|
|
+ public Integer apply(Date queryTime) {
|
|
|
+ int latestAmount = rechargeService.getLatestRechargeAmount(openId, queryTime);
|
|
|
+ return ObjectUtil.defaultIfNull(latestAmount, 0);
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
}
|