|
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.service.combination;
|
|
|
|
|
|
import cn.hutool.core.collection.CollUtil;
|
|
|
import cn.hutool.core.util.ObjUtil;
|
|
|
+import cn.hutool.extra.spring.SpringUtil;
|
|
|
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
|
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
|
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
@@ -20,7 +21,9 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationP
|
|
|
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
|
|
|
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper;
|
|
|
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
|
|
|
+import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
|
|
|
import org.springframework.context.annotation.Lazy;
|
|
|
+import org.springframework.scheduling.annotation.Async;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
@@ -31,8 +34,7 @@ import java.time.LocalDateTime;
|
|
|
import java.util.*;
|
|
|
|
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
|
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.findFirst;
|
|
|
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
|
|
|
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
|
|
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.afterNow;
|
|
|
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.beforeNow;
|
|
|
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
|
|
@@ -52,7 +54,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
|
|
@Lazy
|
|
|
private CombinationActivityService combinationActivityService;
|
|
|
@Resource
|
|
|
- private CombinationRecordMapper recordMapper;
|
|
|
+ private CombinationRecordMapper combinationRecordMapper;
|
|
|
|
|
|
@Resource
|
|
|
private MemberUserApi memberUserApi;
|
|
@@ -63,6 +65,9 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
|
|
@Lazy
|
|
|
private ProductSkuApi productSkuApi;
|
|
|
|
|
|
+ @Resource
|
|
|
+ private TradeOrderApi tradeOrderApi;
|
|
|
+
|
|
|
// TODO @芋艿:在详细预览下;
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
@@ -72,12 +77,12 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
|
|
|
|
|
// 更新状态
|
|
|
record.setStatus(status);
|
|
|
- recordMapper.updateById(record);
|
|
|
+ combinationRecordMapper.updateById(record);
|
|
|
}
|
|
|
|
|
|
private CombinationRecordDO validateCombinationRecord(Long userId, Long orderId) {
|
|
|
// 校验拼团是否存在
|
|
|
- CombinationRecordDO recordDO = recordMapper.selectByUserIdAndOrderId(userId, orderId);
|
|
|
+ CombinationRecordDO recordDO = combinationRecordMapper.selectByUserIdAndOrderId(userId, orderId);
|
|
|
if (recordDO == null) {
|
|
|
throw exception(COMBINATION_RECORD_NOT_EXISTS);
|
|
|
}
|
|
@@ -106,7 +111,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
|
|
// 2. 父拼团是否存在,是否已经满了
|
|
|
if (headId != null) {
|
|
|
// 2.1. 查询进行中的父拼团
|
|
|
- CombinationRecordDO record = recordMapper.selectByHeadId(headId, CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
|
|
|
+ CombinationRecordDO record = combinationRecordMapper.selectByHeadId(headId, CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
|
|
|
if (record == null) {
|
|
|
throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS);
|
|
|
}
|
|
@@ -141,7 +146,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
|
|
}
|
|
|
|
|
|
// 6.1 校验是否有拼团记录
|
|
|
- List<CombinationRecordDO> recordList = recordMapper.selectListByUserIdAndActivityId(userId, activityId);
|
|
|
+ List<CombinationRecordDO> recordList = combinationRecordMapper.selectListByUserIdAndActivityId(userId, activityId);
|
|
|
recordList.removeIf(record -> CombinationRecordStatusEnum.isFailed(record.getStatus())); // 取消的订单,不算数
|
|
|
if (CollUtil.isEmpty(recordList)) { // 如果为空,说明可以参与,直接返回
|
|
|
return new KeyValue<>(activity, product);
|
|
@@ -179,11 +184,11 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
|
|
.setHeadId(CombinationRecordDO.HEAD_ID_GROUP);
|
|
|
} else {
|
|
|
// 2.2.有团长的情况下需要设置开始时间和过期时间为团长的
|
|
|
- CombinationRecordDO headRecord = recordMapper.selectByHeadId(record.getHeadId(),
|
|
|
+ CombinationRecordDO headRecord = combinationRecordMapper.selectByHeadId(record.getHeadId(),
|
|
|
CombinationRecordStatusEnum.IN_PROGRESS.getStatus()); // 查询进行中的父拼团
|
|
|
record.setStartTime(headRecord.getStartTime()).setExpireTime(headRecord.getExpireTime());
|
|
|
}
|
|
|
- recordMapper.insert(record);
|
|
|
+ combinationRecordMapper.insert(record);
|
|
|
|
|
|
if (ObjUtil.equal(CombinationRecordDO.HEAD_ID_GROUP, record.getHeadId())) {
|
|
|
return new KeyValue<>(record.getId(), record.getHeadId());
|
|
@@ -206,31 +211,33 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
|
|
if (CollUtil.isEmpty(records)) {
|
|
|
return;
|
|
|
}
|
|
|
- CombinationRecordDO headRecord = recordMapper.selectById(headId);
|
|
|
+ CombinationRecordDO headRecord = combinationRecordMapper.selectById(headId);
|
|
|
|
|
|
// 2. 批量更新记录
|
|
|
List<CombinationRecordDO> updateRecords = new ArrayList<>();
|
|
|
records.add(headRecord); // 加入团长,团长也需要更新
|
|
|
boolean isFull = records.size() >= activity.getUserSize();
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
records.forEach(item -> {
|
|
|
CombinationRecordDO updateRecord = new CombinationRecordDO();
|
|
|
updateRecord.setId(item.getId()).setUserCount(records.size());
|
|
|
if (isFull) {
|
|
|
updateRecord.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus());
|
|
|
+ updateRecord.setEndTime(now);
|
|
|
}
|
|
|
updateRecords.add(updateRecord);
|
|
|
});
|
|
|
- recordMapper.updateBatch(updateRecords);
|
|
|
+ combinationRecordMapper.updateBatch(updateRecords);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public CombinationRecordDO getCombinationRecord(Long userId, Long orderId) {
|
|
|
- return recordMapper.selectByUserIdAndOrderId(userId, orderId);
|
|
|
+ return combinationRecordMapper.selectByUserIdAndOrderId(userId, orderId);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public List<CombinationRecordDO> getCombinationRecordListByUserIdAndActivityId(Long userId, Long activityId) {
|
|
|
- return recordMapper.selectListByUserIdAndActivityId(userId, activityId);
|
|
|
+ return combinationRecordMapper.selectListByUserIdAndActivityId(userId, activityId);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -244,51 +251,51 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
|
|
|
|
|
@Override
|
|
|
public Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup, @Nullable Long headId) {
|
|
|
- return recordMapper.selectCountByHeadAndStatusAndVirtualGroup(status, virtualGroup, headId);
|
|
|
+ return combinationRecordMapper.selectCountByHeadAndStatusAndVirtualGroup(status, virtualGroup, headId);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public List<CombinationRecordDO> getLatestCombinationRecordList(int count) {
|
|
|
- return recordMapper.selectLatestList(count);
|
|
|
+ return combinationRecordMapper.selectLatestList(count);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public List<CombinationRecordDO> getHeadCombinationRecordList(Long activityId, Integer status, Integer count) {
|
|
|
- return recordMapper.selectListByActivityIdAndStatusAndHeadId(activityId, status,
|
|
|
+ return combinationRecordMapper.selectListByActivityIdAndStatusAndHeadId(activityId, status,
|
|
|
CombinationRecordDO.HEAD_ID_GROUP, count);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public CombinationRecordDO getCombinationRecordById(Long id) {
|
|
|
- return recordMapper.selectById(id);
|
|
|
+ return combinationRecordMapper.selectById(id);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public List<CombinationRecordDO> getCombinationRecordListByHeadId(Long headId) {
|
|
|
- return recordMapper.selectList(CombinationRecordDO::getHeadId, headId);
|
|
|
+ return combinationRecordMapper.selectList(CombinationRecordDO::getHeadId, headId);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public PageResult<CombinationRecordDO> getCombinationRecordPage(CombinationRecordReqPageVO pageVO) {
|
|
|
- return recordMapper.selectPage(pageVO);
|
|
|
+ return combinationRecordMapper.selectPage(pageVO);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public Map<Long, Integer> getCombinationRecordCountMapByActivity(Collection<Long> activityIds,
|
|
|
@Nullable Integer status, @Nullable Long headId) {
|
|
|
- return recordMapper.selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId(activityIds, status, headId);
|
|
|
+ return combinationRecordMapper.selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId(activityIds, status, headId);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public CombinationRecordDO getCombinationRecordByIdAndUser(Long userId, Long id) {
|
|
|
- return recordMapper.selectOne(CombinationRecordDO::getUserId, userId, CombinationRecordDO::getId, id);
|
|
|
+ return combinationRecordMapper.selectOne(CombinationRecordDO::getUserId, userId, CombinationRecordDO::getId, id);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public void cancelCombinationRecord(Long userId, Long id, Long headId) {
|
|
|
// 删除记录
|
|
|
- recordMapper.deleteById(id);
|
|
|
+ combinationRecordMapper.deleteById(id);
|
|
|
|
|
|
// 需要更新的记录
|
|
|
List<CombinationRecordDO> updateRecords = new ArrayList<>();
|
|
@@ -315,7 +322,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
|
|
});
|
|
|
} else { // 情况二:团员
|
|
|
// 团长
|
|
|
- CombinationRecordDO recordHead = recordMapper.selectById(headId);
|
|
|
+ CombinationRecordDO recordHead = combinationRecordMapper.selectById(headId);
|
|
|
// 团员
|
|
|
List<CombinationRecordDO> records = getCombinationRecordListByHeadId(headId);
|
|
|
if (CollUtil.isEmpty(records)) {
|
|
@@ -331,7 +338,112 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
|
|
}
|
|
|
|
|
|
// 更新拼团记录
|
|
|
- recordMapper.updateBatch(updateRecords);
|
|
|
+ combinationRecordMapper.updateBatch(updateRecords);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public KeyValue<Integer, Integer> expireCombinationRecord() {
|
|
|
+ // 1。获取所有正在进行中的过期的父拼团
|
|
|
+ List<CombinationRecordDO> headExpireRecords = combinationRecordMapper.selectListByHeadIdAndStatusAndExpireTimeLt(
|
|
|
+ CombinationRecordDO.HEAD_ID_GROUP, CombinationRecordStatusEnum.IN_PROGRESS.getStatus(), LocalDateTime.now());
|
|
|
+ if (CollUtil.isEmpty(headExpireRecords)) {
|
|
|
+ return new KeyValue<>(0, 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2.获取拼团活动
|
|
|
+ List<CombinationActivityDO> combinationActivities = combinationActivityService.getCombinationActivityListByIds(
|
|
|
+ convertSet(headExpireRecords, CombinationRecordDO::getActivityId));
|
|
|
+ Map<Long, CombinationActivityDO> activityMap = convertMap(combinationActivities, CombinationActivityDO::getId);
|
|
|
+
|
|
|
+ // 3.校验是否虚拟成团
|
|
|
+ List<CombinationRecordDO> virtualGroupHeadRecords = new ArrayList<>(); // 虚拟成团
|
|
|
+ for (Iterator<CombinationRecordDO> iterator = headExpireRecords.iterator(); iterator.hasNext(); ) {
|
|
|
+ CombinationRecordDO record = iterator.next();
|
|
|
+ // 3.1 不匹配,则直接跳过
|
|
|
+ CombinationActivityDO activityDO = activityMap.get(record.getActivityId());
|
|
|
+ if (activityDO == null || !activityDO.getVirtualGroup()) { // 取不到活动的或者不是虚拟拼团的
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // 3.2 匹配,则移除,添加到虚拟成团中,并结束寻找
|
|
|
+ virtualGroupHeadRecords.add(record);
|
|
|
+ iterator.remove();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4.处理过期的拼团
|
|
|
+ getSelf().handleExpireRecord(headExpireRecords);
|
|
|
+ // 5.虚拟成团
|
|
|
+ getSelf().handleVirtualGroupRecord(virtualGroupHeadRecords);
|
|
|
+
|
|
|
+ return new KeyValue<>(headExpireRecords.size(), virtualGroupHeadRecords.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ @Async
|
|
|
+ protected void handleExpireRecord(List<CombinationRecordDO> headExpireRecords) {
|
|
|
+ if (CollUtil.isEmpty(headExpireRecords)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1.更新拼团记录
|
|
|
+ List<CombinationRecordDO> headsAndRecords = updateBatchCombinationRecords(headExpireRecords,
|
|
|
+ CombinationRecordStatusEnum.FAILED);
|
|
|
+ if (headsAndRecords == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2.订单取消 TODO 以现在的取消回滚逻辑好像只能循环了
|
|
|
+ headsAndRecords.forEach(item -> {
|
|
|
+ tradeOrderApi.cancelPaidOrder(item.getUserId(), item.getOrderId());
|
|
|
+ });
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Async
|
|
|
+ protected void handleVirtualGroupRecord(List<CombinationRecordDO> virtualGroupHeadRecords) {
|
|
|
+ if (CollUtil.isEmpty(virtualGroupHeadRecords)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1.团员补齐
|
|
|
+ combinationRecordMapper.insertBatch(CombinationActivityConvert.INSTANCE.convertVirtualGroupList(virtualGroupHeadRecords));
|
|
|
+ // 2.更新拼团记录
|
|
|
+ updateBatchCombinationRecords(virtualGroupHeadRecords, CombinationRecordStatusEnum.SUCCESS);
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<CombinationRecordDO> updateBatchCombinationRecords(List<CombinationRecordDO> headRecords, CombinationRecordStatusEnum status) {
|
|
|
+ // 1. 查询团成员
|
|
|
+ List<CombinationRecordDO> records = combinationRecordMapper.selectListByHeadIds(
|
|
|
+ convertSet(headRecords, CombinationRecordDO::getId));
|
|
|
+ if (CollUtil.isEmpty(records)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ Map<Long, List<CombinationRecordDO>> recordsMap = convertMultiMap(records, CombinationRecordDO::getHeadId);
|
|
|
+ headRecords.forEach(item -> {
|
|
|
+ recordsMap.get(item.getId()).add(item); // 把团长加进团里
|
|
|
+ });
|
|
|
+ // 2.批量更新拼团记录 status 和 失败/成团时间
|
|
|
+ List<CombinationRecordDO> headsAndRecords = mergeValuesFromMap(recordsMap);
|
|
|
+ List<CombinationRecordDO> updateRecords = new ArrayList<>(headsAndRecords.size());
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ headsAndRecords.forEach(item -> {
|
|
|
+ CombinationRecordDO record = new CombinationRecordDO().setId(item.getId())
|
|
|
+ .setStatus(status.getStatus()).setEndTime(now);
|
|
|
+ if (CombinationRecordStatusEnum.isSuccess(status.getStatus())) { // 虚拟成团完事更改状态成功后还需要把参与人数修改为成团需要人数
|
|
|
+ record.setUserCount(record.getUserSize());
|
|
|
+ }
|
|
|
+ updateRecords.add(record);
|
|
|
+ });
|
|
|
+ combinationRecordMapper.updateBatch(updateRecords);
|
|
|
+ return headsAndRecords;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获得自身的代理对象,解决 AOP 生效问题
|
|
|
+ *
|
|
|
+ * @return 自己
|
|
|
+ */
|
|
|
+ private CombinationRecordServiceImpl getSelf() {
|
|
|
+ return SpringUtil.getBean(getClass());
|
|
|
}
|
|
|
|
|
|
}
|