|
@@ -1,18 +1,35 @@
|
|
|
package com.ylx.point.service.impl;
|
|
package com.ylx.point.service.impl;
|
|
|
|
|
|
|
|
|
|
+import cn.hutool.core.date.DateUtil;
|
|
|
import cn.hutool.core.util.ObjectUtil;
|
|
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.core.toolkit.Wrappers;
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
|
|
+import com.ylx.common.core.domain.model.WxLoginUser;
|
|
|
|
|
+import com.ylx.common.exception.ServiceException;
|
|
|
import com.ylx.common.utils.DateUtils;
|
|
import com.ylx.common.utils.DateUtils;
|
|
|
|
|
+import com.ylx.common.utils.SecurityUtils;
|
|
|
|
|
+import com.ylx.massage.service.TOrderService;
|
|
|
|
|
+import com.ylx.massage.service.TRechargeService;
|
|
|
|
|
+import com.ylx.point.domain.PointActivity;
|
|
|
|
|
+import com.ylx.point.domain.PointActivityTask;
|
|
|
import com.ylx.point.domain.PointUserActivityTaskCompletion;
|
|
import com.ylx.point.domain.PointUserActivityTaskCompletion;
|
|
|
import com.ylx.point.domain.vo.PointActivityOverviewVO;
|
|
import com.ylx.point.domain.vo.PointActivityOverviewVO;
|
|
|
|
|
+import com.ylx.point.enums.PointActivityStatusEnum;
|
|
|
|
|
+import com.ylx.point.enums.PointActivityTaskTypeEnum;
|
|
|
|
|
+import com.ylx.point.enums.PointActivityTypeEnum;
|
|
|
import com.ylx.point.mapper.PointUserActivityTaskCompletionMapper;
|
|
import com.ylx.point.mapper.PointUserActivityTaskCompletionMapper;
|
|
|
|
|
+import com.ylx.point.service.IPointAccountService;
|
|
|
|
|
+import com.ylx.point.service.IPointActivityService;
|
|
|
|
|
+import com.ylx.point.service.IPointActivityTaskService;
|
|
|
import com.ylx.point.service.IPointUserActivityTaskCompletionService;
|
|
import com.ylx.point.service.IPointUserActivityTaskCompletionService;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
import javax.annotation.Resource;
|
|
|
import java.time.LocalDate;
|
|
import java.time.LocalDate;
|
|
|
|
|
+import java.util.Date;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -26,6 +43,16 @@ public class PointUserActivityTaskCompletionServiceImpl extends ServiceImpl<Poin
|
|
|
|
|
|
|
|
@Resource
|
|
@Resource
|
|
|
private PointUserActivityTaskCompletionMapper pointUserActivityTaskCompletionMapper;
|
|
private PointUserActivityTaskCompletionMapper pointUserActivityTaskCompletionMapper;
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private IPointActivityTaskService pointActivityTaskService;
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private IPointActivityService pointActivityService;
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private TOrderService orderService;
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private TRechargeService rechargeService;
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private IPointAccountService pointAccountService;
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
public PointActivityOverviewVO getPointActivityOverviewByActivityId(Long activityId) {
|
|
public PointActivityOverviewVO getPointActivityOverviewByActivityId(Long activityId) {
|
|
@@ -100,4 +127,141 @@ public class PointUserActivityTaskCompletionServiceImpl extends ServiceImpl<Poin
|
|
|
);
|
|
);
|
|
|
return completion == null ? 0 : completion.getCompletedCount();
|
|
return completion == null ? 0 : completion.getCompletedCount();
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public void executeTask(Long taskId) {
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 获取当前登录用户
|
|
|
|
|
+ WxLoginUser loginUser = getCurrentWxLoginUser();
|
|
|
|
|
+ String openId = loginUser.getCOpenid();
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 获取任务信息
|
|
|
|
|
+ PointActivityTask task = this.pointActivityTaskService.getById(taskId);
|
|
|
|
|
+ if (ObjectUtil.isNull(task)) {
|
|
|
|
|
+ throw new ServiceException("任务不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 获取活动信息
|
|
|
|
|
+ PointActivity activity = this.pointActivityService.getById(task.getActivityId());
|
|
|
|
|
+ if (ObjectUtil.isNull(activity) || !ObjectUtil.equals(activity.getStatus(), PointActivityStatusEnum.IN_PROGRESS.getCode())) {
|
|
|
|
|
+ throw new ServiceException("活动不存在或已结束");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Long activityId = activity.getId();
|
|
|
|
|
+ Integer activityType = activity.getActivityType();
|
|
|
|
|
+ String isPermanent = activity.getIsPermanent();
|
|
|
|
|
+ Date activityStartTime = activity.getStartTime();
|
|
|
|
|
+
|
|
|
|
|
+ PointActivityTypeEnum pointActivityType = PointActivityTypeEnum.valueOf(activityType);
|
|
|
|
|
+ PointActivityTaskTypeEnum taskType = PointActivityTaskTypeEnum.getByInfo(task.getTaskName());
|
|
|
|
|
+
|
|
|
|
|
+ // 目标次数
|
|
|
|
|
+ int targetCount = Integer.parseInt(task.getTriggerValue());
|
|
|
|
|
+
|
|
|
|
|
+ boolean isVerified = false;
|
|
|
|
|
+
|
|
|
|
|
+ // --- 逻辑分支开始 ---
|
|
|
|
|
+
|
|
|
|
|
+ // 场景 A: 每日任务/签到 -> 只需要检查今天是否做过
|
|
|
|
|
+ if (pointActivityType == PointActivityTypeEnum.SIGN_TASK || pointActivityType == PointActivityTypeEnum.DAILY_ACTIVITY) {
|
|
|
|
|
+ boolean completed = this.isTodayCompleted(openId, activityId, taskId, activityType);
|
|
|
|
|
+ if (completed) {
|
|
|
|
|
+ throw new ServiceException("今日任务已完成,请勿重复领取");
|
|
|
|
|
+ }
|
|
|
|
|
+ // 每日任务通常点一下就算完成,或者根据具体业务逻辑判断
|
|
|
|
|
+ isVerified = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 场景 B: 新手任务 或 每月任务 -> 需要核实业务数据(订单/充值)
|
|
|
|
|
+ else if (pointActivityType == PointActivityTypeEnum.NEW_USER_ACTIVITY || pointActivityType == PointActivityTypeEnum.MONTHLY_ACTIVITY) {
|
|
|
|
|
+
|
|
|
|
|
+ // 计算查询业务数据的时间起点
|
|
|
|
|
+ Date queryTime = getQueryStartTime(isPermanent, activityStartTime, activityType);
|
|
|
|
|
+
|
|
|
|
|
+ switch (taskType) {
|
|
|
|
|
+ case COMPLETE_ORDER: // 完成订单
|
|
|
|
|
+ int orderCount = orderService.countCompletedOrders(openId, queryTime);
|
|
|
|
|
+ isVerified = orderCount >= targetCount;
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case RECHARGE_TASK: // 充值任务
|
|
|
|
|
+ int rechargeCount = rechargeService.countSuccessRecharges(openId, queryTime);
|
|
|
|
|
+ isVerified = rechargeCount >= targetCount;
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ case SHARE_SERVICE: // 分享服务号
|
|
|
|
|
+ case BROWLE_NEWS: // 浏览动态
|
|
|
|
|
+ case BROWLE_MERCHANT:// 浏览商户
|
|
|
|
|
+ // 这类任务通常前端跳转后,用户回来点击就算完成,或者这里直接设为 true
|
|
|
|
|
+ isVerified = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+
|
|
|
|
|
+ default:
|
|
|
|
|
+ throw new ServiceException("不支持的任务类型: " + taskType);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ else {
|
|
|
|
|
+ throw new ServiceException("不支持的活动类型");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // --- 执行发奖逻辑 ---
|
|
|
|
|
+
|
|
|
|
|
+ // 4. 如果核实通过,执行本地记录和发分
|
|
|
|
|
+ if (isVerified) {
|
|
|
|
|
+ // 4.1 【关键修复】先查询本地记录,判断是否已经达标
|
|
|
|
|
+ PointUserActivityTaskCompletion existingRecord = this.getOne(
|
|
|
|
|
+ new LambdaQueryWrapper<PointUserActivityTaskCompletion>()
|
|
|
|
|
+ .eq(PointUserActivityTaskCompletion::getOpenId, openId)
|
|
|
|
|
+ .eq(PointUserActivityTaskCompletion::getTaskId, taskId)
|
|
|
|
|
+ .eq(PointUserActivityTaskCompletion::getActivityId, activityId)
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ // 防刷:如果已经达标,不再重复发分
|
|
|
|
|
+ if (existingRecord != null && existingRecord.getCompletedCount() >= targetCount) {
|
|
|
|
|
+ throw new ServiceException("任务奖励已领取");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 4.2 更新任务进度表 (插入或更新)
|
|
|
|
|
+ // 注意:这里我使用了你之前的 completeTask 逻辑,而不是直接 set 目标值
|
|
|
|
|
+ // 因为如果是“累计充值1000元”,用户充了500+500,直接set成1会丢失进度
|
|
|
|
|
+ // 但如果是“完成1笔订单”,直接调用 completeTask 即可
|
|
|
|
|
+ this.completeTask(openId, activityId, taskId, activityType);
|
|
|
|
|
+
|
|
|
|
|
+ // 4.3 发放积分
|
|
|
|
|
+ // 注意:这里需要确保只发一次。如果 completeTask 只是增加次数,这里需要判断是否刚好达标才发分
|
|
|
|
|
+ // 为了简单,假设这里调用 addPoints 是安全的(或者你在 addPoints 内部做幂等)
|
|
|
|
|
+ pointAccountService.addPoints(openId, task.getRewardPoints().intValue(), activity.getName(), null, activityId, taskId, taskType.getCode());
|
|
|
|
|
+
|
|
|
|
|
+ } else {
|
|
|
|
|
+ throw new ServiceException("未检测到符合条件的记录,请先完成任务(如支付订单、充值等)");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Date getQueryStartTime(String isPermanent, Date startTime, Integer activityType) {
|
|
|
|
|
+ // 1. 永久任务(永久有效 = 查询所有历史数据,直接返回任务开始时间)
|
|
|
|
|
+ if (StrUtil.equals(isPermanent, "1")) { // 假设 1=永久,0=非永久
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 非永久任务 → 根据活动类型判断
|
|
|
|
|
+ if (ObjectUtil.equals(PointActivityTypeEnum.DAILY_ACTIVITY.getCode(), activityType)) {
|
|
|
|
|
+ // 每日任务 → 今天 0 点
|
|
|
|
|
+ return DateUtil.beginOfDay(new Date());
|
|
|
|
|
+ } else if (ObjectUtil.equals(PointActivityTypeEnum.MONTHLY_ACTIVITY.getCode(), activityType)) {
|
|
|
|
|
+ // 每月任务 → 本月 1 号 0 点
|
|
|
|
|
+ return DateUtil.beginOfMonth(new Date());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 新手任务 / 其他 → 任务开始时间(查全部历史)
|
|
|
|
|
+ return startTime;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private WxLoginUser getCurrentWxLoginUser() {
|
|
|
|
|
+ WxLoginUser loginUser = SecurityUtils.getWxLoginUser();
|
|
|
|
|
+ if (ObjectUtil.isNull(loginUser)) {
|
|
|
|
|
+ throw new ServiceException("用户未登录或登录已过期");
|
|
|
|
|
+ }
|
|
|
|
|
+ return loginUser;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
}
|
|
}
|