mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-22 07:11:52 +08:00
拼团活动:完善 review 提到的问题
This commit is contained in:
parent
7b727d5ce2
commit
53e67c0437
@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.promotion.api.combination;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
|
||||
|
||||
@ -29,9 +30,9 @@ public interface CombinationRecordApi {
|
||||
* 创建开团记录
|
||||
*
|
||||
* @param reqDTO 请求 DTO
|
||||
* @return 开团记录编号
|
||||
* @return key 开团记录编号 value 团长编号
|
||||
*/
|
||||
Long createCombinationRecord(@Valid CombinationRecordCreateReqDTO reqDTO);
|
||||
KeyValue<Long, Long> createCombinationRecord(@Valid CombinationRecordCreateReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 查询拼团记录是否成功
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.promotion.api.combination;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
|
||||
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
|
||||
@ -29,7 +30,7 @@ public class CombinationRecordApiImpl implements CombinationRecordApi {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
|
||||
public KeyValue<Long, Long> createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
|
||||
return recordService.createCombinationRecord(reqDTO);
|
||||
}
|
||||
|
||||
|
@ -54,10 +54,11 @@ public class CombinationRecordController {
|
||||
@PreAuthorize("@ss.hasPermission('promotion:combination-record:query')")
|
||||
public CommonResult<CombinationRecordSummaryVO> getCombinationRecordSummary() {
|
||||
CombinationRecordSummaryVO summaryVO = new CombinationRecordSummaryVO();
|
||||
summaryVO.setUserCount(combinationRecordService.getCombinationRecordCount(null, null)); // 获取所有拼团记录
|
||||
summaryVO.setUserCount(combinationRecordService.getCombinationRecordCount(null, null, null)); // 获取拼团用户参与数量
|
||||
summaryVO.setSuccessCount(combinationRecordService.getCombinationRecordCount( // 获取成团记录
|
||||
CombinationRecordStatusEnum.SUCCESS.getStatus(), null));
|
||||
summaryVO.setVirtualGroupCount(combinationRecordService.getCombinationRecordCount(null, Boolean.TRUE));// 获取虚拟成团记录
|
||||
CombinationRecordStatusEnum.SUCCESS.getStatus(), null, CombinationRecordDO.HEAD_ID_GROUP));
|
||||
summaryVO.setVirtualGroupCount(combinationRecordService.getCombinationRecordCount(// 获取虚拟成团记录
|
||||
null, Boolean.TRUE, CombinationRecordDO.HEAD_ID_GROUP));
|
||||
return success(summaryVO);
|
||||
}
|
||||
|
||||
|
@ -46,8 +46,8 @@ public class AppCombinationRecordController {
|
||||
@Operation(summary = "获得拼团记录的概要信息", description = "用于小程序首页")
|
||||
public CommonResult<AppCombinationRecordSummaryRespVO> getCombinationRecordSummary() {
|
||||
AppCombinationRecordSummaryRespVO summary = new AppCombinationRecordSummaryRespVO();
|
||||
// 1. 获得拼团记录数量
|
||||
Long count = combinationRecordService.getCombinationRecordCount(null, null);
|
||||
// 1. 获得拼团参与用户数量
|
||||
Long count = combinationRecordService.getCombinationRecordCount(null, null, null);
|
||||
if (count == 0) {
|
||||
summary.setAvatars(Collections.emptyList());
|
||||
summary.setUserCount(count);
|
||||
|
@ -28,7 +28,6 @@ import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -114,9 +113,6 @@ public interface CombinationActivityConvert {
|
||||
ProductSpuRespDTO spu, ProductSkuRespDTO sku) {
|
||||
return convert(reqDTO).setVirtualGroup(false)
|
||||
.setStatus(CombinationRecordStatusEnum.IN_PROGRESS.getStatus()) // 创建后默认状态为进行中
|
||||
.setStartTime(LocalDateTime.now()) // TODO @puhui999:想了下,这个 startTime 应该是团长的;
|
||||
// TODO @puhui999:有团长的情况下,expireTime 应该是团长的;
|
||||
.setExpireTime(activity.getStartTime().plusHours(activity.getLimitDuration()))
|
||||
.setUserSize(activity.getUserSize()).setUserCount(1) // 默认就是 1 插入后会接着更新一次所有的拼团记录
|
||||
// 用户信息
|
||||
.setNickname(user.getNickname()).setAvatar(user.getAvatar())
|
||||
|
@ -100,13 +100,21 @@ public interface CombinationRecordMapper extends BaseMapperX<CombinationRecordDO
|
||||
.betweenIfPresent(CombinationRecordDO::getCreateTime, pageVO.getCreateTime()));
|
||||
}
|
||||
|
||||
// TODO @puhui999:这个最好把 headId 也作为一个参数;因为有个要求 userCount,它要 DISTINCT 下;整体可以参考 selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId
|
||||
default Long selectCountByHeadAndStatusAndVirtualGroup(Integer status, Boolean virtualGroup) {
|
||||
return selectCount(new LambdaQueryWrapperX<CombinationRecordDO>()
|
||||
.eq(status != null || virtualGroup != null,
|
||||
CombinationRecordDO::getHeadId, CombinationRecordDO.HEAD_ID_GROUP) // 统计团信息则指定团长
|
||||
.eqIfPresent(CombinationRecordDO::getStatus, status)
|
||||
.eqIfPresent(CombinationRecordDO::getVirtualGroup, virtualGroup));
|
||||
/**
|
||||
* 查询指定条件的记录数
|
||||
* 如果参数都为 null 时则查询用户拼团记录(DISTINCT 去重),也就是说查询会员表中的用户有多少人参与过拼团活动每个人只统计一次
|
||||
*
|
||||
* @param status 状态,可为 null
|
||||
* @param virtualGroup 是否虚拟成团,可为 null
|
||||
* @param headId 团长编号,可为 null
|
||||
* @return 记录数
|
||||
*/
|
||||
default Long selectCountByHeadAndStatusAndVirtualGroup(Integer status, Boolean virtualGroup, Long headId) {
|
||||
return selectCount(new QueryWrapper<CombinationRecordDO>()
|
||||
.select(status == null && virtualGroup == null && headId == null, "COUNT(DISTINCT(user_id))")
|
||||
.eq(status != null, "status", status)
|
||||
.eq(virtualGroup != null, "virtual_group", virtualGroup)
|
||||
.eq(headId != null, "head_id", headId));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -49,9 +49,9 @@ public interface CombinationRecordService {
|
||||
* 创建拼团记录
|
||||
*
|
||||
* @param reqDTO 创建信息
|
||||
* @return 开团记录编号
|
||||
* @return key 开团记录编号 value 团长编号
|
||||
*/
|
||||
Long createCombinationRecord(CombinationRecordCreateReqDTO reqDTO);
|
||||
KeyValue<Long, Long> createCombinationRecord(CombinationRecordCreateReqDTO reqDTO);
|
||||
|
||||
/**
|
||||
* 获得拼团记录
|
||||
@ -90,9 +90,10 @@ public interface CombinationRecordService {
|
||||
*
|
||||
* @param status 状态-允许为空
|
||||
* @param virtualGroup 是否虚拟成团-允许为空
|
||||
* @param headId 团长编号,允许空。目的 headId 设置为 {@link CombinationRecordDO#HEAD_ID_GROUP} 时,可以设置
|
||||
* @return 记录数
|
||||
*/
|
||||
Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup);
|
||||
Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup, @Nullable Long headId);
|
||||
|
||||
/**
|
||||
* 获取最近的 count 条拼团记录
|
||||
|
@ -20,7 +20,6 @@ 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.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -28,6 +27,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
@ -62,8 +62,6 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
||||
@Resource
|
||||
@Lazy
|
||||
private ProductSkuApi productSkuApi;
|
||||
@Resource
|
||||
private TradeOrderApi tradeOrderApi;
|
||||
|
||||
// TODO @芋艿:在详细预览下;
|
||||
@Override
|
||||
@ -164,32 +162,36 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
|
||||
// 1. 校验拼团活动
|
||||
public KeyValue<Long, Long> createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
|
||||
// 1.校验拼团活动
|
||||
KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(reqDTO.getUserId(),
|
||||
reqDTO.getActivityId(), reqDTO.getHeadId(), reqDTO.getSkuId(), reqDTO.getCount());
|
||||
|
||||
// 2.1 组合数据创建拼团记录
|
||||
// 2.组合数据创建拼团记录
|
||||
MemberUserRespDTO user = memberUserApi.getUser(reqDTO.getUserId());
|
||||
ProductSpuRespDTO spu = productSpuApi.getSpu(reqDTO.getSpuId());
|
||||
ProductSkuRespDTO sku = productSkuApi.getSku(reqDTO.getSkuId());
|
||||
CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO, keyValue.getKey(), user, spu, sku);
|
||||
// 2.2 如果是团长需要设置 headId 为 CombinationRecordDO#HEAD_ID_GROUP
|
||||
// 2.1.如果是团长需要设置 headId 为 CombinationRecordDO#HEAD_ID_GROUP
|
||||
if (record.getHeadId() == null) {
|
||||
record.setHeadId(CombinationRecordDO.HEAD_ID_GROUP);
|
||||
record.setStartTime(LocalDateTime.now())
|
||||
.setExpireTime(keyValue.getKey().getStartTime().plusHours(keyValue.getKey().getLimitDuration()))
|
||||
.setHeadId(CombinationRecordDO.HEAD_ID_GROUP);
|
||||
} else {
|
||||
// 2.2.有团长的情况下需要设置开始时间和过期时间为团长的
|
||||
CombinationRecordDO headRecord = recordMapper.selectByHeadId(record.getHeadId(),
|
||||
CombinationRecordStatusEnum.IN_PROGRESS.getStatus()); // 查询进行中的父拼团
|
||||
record.setStartTime(headRecord.getStartTime()).setExpireTime(headRecord.getExpireTime());
|
||||
}
|
||||
recordMapper.insert(record);
|
||||
|
||||
if (ObjUtil.equal(CombinationRecordDO.HEAD_ID_GROUP, record.getHeadId())) {
|
||||
return record.getId();
|
||||
return new KeyValue<>(record.getId(), record.getHeadId());
|
||||
}
|
||||
|
||||
// TODO @puhui:是不是这里的更新,放到 order 模块那;支付完成后;
|
||||
// 4、更新拼团相关信息到订单
|
||||
tradeOrderApi.updateOrderCombinationInfo(record.getOrderId(), record.getActivityId(), record.getId(), record.getHeadId());
|
||||
// 4、更新拼团记录
|
||||
// 3、更新拼团记录
|
||||
updateCombinationRecordWhenCreate(reqDTO.getHeadId(), keyValue.getKey());
|
||||
return record.getId();
|
||||
return new KeyValue<>(record.getId(), record.getHeadId());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -241,8 +243,8 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup) {
|
||||
return recordMapper.selectCountByHeadAndStatusAndVirtualGroup(status, virtualGroup);
|
||||
public Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup, @Nullable Long headId) {
|
||||
return recordMapper.selectCountByHeadAndStatusAndVirtualGroup(status, virtualGroup, headId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -33,7 +33,8 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR = new ErrorCode(1_011_000_028, "支付订单调价失败,原因:调整后支付价格不能小于 0.01 元");
|
||||
ErrorCode ORDER_DELETE_FAIL_STATUS_NOT_CANCEL = new ErrorCode(1_011_000_029, "交易订单删除失败,订单不是【已取消】状态");
|
||||
ErrorCode ORDER_RECEIVE_FAIL_DELIVERY_TYPE_NOT_PICK_UP = new ErrorCode(1_011_000_030, "交易订单自提失败,收货方式不是【用户自提】");
|
||||
ErrorCode ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1_011_000_031, "交易订单修改收货地址失败,原因:订单已发货");
|
||||
ErrorCode ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1_011_000_031, "交易订单修改收货地址失败,原因:订单不是【待发货】状态");
|
||||
ErrorCode ORDER_CREATE_FAIL_EXIST_UNPAID = new ErrorCode(1_011_000_032, "交易订单创建失败,原因:存在未付款订单");
|
||||
|
||||
// ========== After Sale 模块 1-011-000-100 ==========
|
||||
ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在");
|
||||
|
@ -110,4 +110,16 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
|
||||
return selectOne(TradeOrderDO::getPickUpVerifyCode, pickUpVerifyCode);
|
||||
}
|
||||
|
||||
default TradeOrderDO selectByUserIdAndActivityIdAndStatus(Long userId, Long activityId, Integer status) {
|
||||
return selectOne(new LambdaQueryWrapperX<TradeOrderDO>()
|
||||
.and(q -> q.eq(TradeOrderDO::getUserId, userId)
|
||||
.eq(TradeOrderDO::getStatus, status))
|
||||
.and(q -> q.eq(TradeOrderDO::getCombinationActivityId, activityId)
|
||||
.or()
|
||||
.eq(TradeOrderDO::getSeckillActivityId, activityId)
|
||||
.or()
|
||||
.eq(TradeOrderDO::getBargainActivityId, activityId))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,6 +40,16 @@ public interface TradeOrderQueryService {
|
||||
*/
|
||||
TradeOrderDO getOrder(Long userId, Long id);
|
||||
|
||||
/**
|
||||
* 获得指定用户,指定活动,指定状态的交易订单
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param activityId 活动编号
|
||||
* @param status 订单状态
|
||||
* @return 交易订单
|
||||
*/
|
||||
TradeOrderDO getActivityOrderByUserIdAndActivityIdAndStatus(Long userId, Long activityId, Integer status);
|
||||
|
||||
/**
|
||||
* 获得订单列表
|
||||
*
|
||||
@ -95,7 +105,7 @@ public interface TradeOrderQueryService {
|
||||
/**
|
||||
* 【会员】在指定秒杀活动下,用户购买的商品数量
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param userId 用户编号
|
||||
* @param activityId 活动编号
|
||||
* @return 秒杀商品数量
|
||||
*/
|
||||
|
@ -72,6 +72,11 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
|
||||
return order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TradeOrderDO getActivityOrderByUserIdAndActivityIdAndStatus(Long userId, Long activityId, Integer status) {
|
||||
return tradeOrderMapper.selectByUserIdAndActivityIdAndStatus(userId, activityId, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TradeOrderDO> getOrderList(Collection<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
|
@ -742,9 +742,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
|
||||
public void updateOrderAddress(TradeOrderUpdateAddressReqVO reqVO) {
|
||||
// 校验交易订单
|
||||
TradeOrderDO order = validateOrderExists(reqVO.getId());
|
||||
// 发货后,不允许修改;
|
||||
// TODO @puhui999:只有待发货,可以执行 update
|
||||
if (TradeOrderStatusEnum.isDelivered(order.getStatus())) {
|
||||
// 只有待发货状态才可以修改订单收货地址;
|
||||
if (!TradeOrderStatusEnum.isUndelivered(order.getStatus())) {
|
||||
throw exception(ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED);
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,23 @@
|
||||
package cn.iocoder.yudao.module.trade.service.order.handler;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi;
|
||||
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
|
||||
import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_CREATE_FAIL_EXIST_UNPAID;
|
||||
|
||||
/**
|
||||
* 拼团订单 handler 接口实现类
|
||||
*
|
||||
@ -19,6 +26,11 @@ import java.util.List;
|
||||
@Component
|
||||
public class TradeCombinationHandler implements TradeOrderHandler {
|
||||
|
||||
@Resource
|
||||
private TradeOrderQueryService orderQueryService;
|
||||
@Resource
|
||||
private TradeOrderUpdateService orderUpdateService;
|
||||
|
||||
@Resource
|
||||
private CombinationRecordApi combinationRecordApi;
|
||||
|
||||
@ -34,21 +46,30 @@ public class TradeCombinationHandler implements TradeOrderHandler {
|
||||
TradeOrderItemDO item = orderItems.get(0);
|
||||
combinationRecordApi.validateCombinationRecord(order.getUserId(), order.getCombinationActivityId(),
|
||||
order.getCombinationHeadId(), item.getSkuId(), item.getCount());
|
||||
// TODO @puhui999:这里还要限制下,是不是已经 createOrder;就是还没支付的时候,重复下单了;需要校验下;不然的话,一个拼团可以下多个单子了;
|
||||
// 校验该用户是否存在未支付的拼团活动订单;就是还没支付的时候,重复下单了;需要校验下;不然的话,一个拼团可以下多个单子了;
|
||||
TradeOrderDO activityOrder = orderQueryService.getActivityOrderByUserIdAndActivityIdAndStatus(
|
||||
order.getUserId(), order.getCombinationActivityId(), TradeOrderStatusEnum.UNPAID.getStatus());
|
||||
if (activityOrder != null) {
|
||||
throw exception(ORDER_CREATE_FAIL_EXIST_UNPAID);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
|
||||
// 如果不是拼团订单则结束
|
||||
// 1.如果不是拼团订单则结束
|
||||
if (TradeOrderTypeEnum.isCombination(order.getType())) {
|
||||
return;
|
||||
}
|
||||
Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品");
|
||||
|
||||
// 获取商品信息
|
||||
// 2.获取商品信息
|
||||
TradeOrderItemDO item = orderItems.get(0);
|
||||
// 创建拼团记录
|
||||
combinationRecordApi.createCombinationRecord(TradeOrderConvert.INSTANCE.convert(order, item));
|
||||
// 2.1.创建拼团记录
|
||||
KeyValue<Long, Long> recordIdAndHeadId = combinationRecordApi.createCombinationRecord(
|
||||
TradeOrderConvert.INSTANCE.convert(order, item));
|
||||
// 3.更新拼团相关信息到订单
|
||||
orderUpdateService.updateOrderCombinationInfo(order.getId(), order.getCombinationActivityId(),
|
||||
recordIdAndHeadId.getKey(), recordIdAndHeadId.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user