review 拼团、秒杀活动的实现

This commit is contained in:
YunaiV
2023-07-26 19:09:47 +08:00
parent e2ed0b7afb
commit b84f7825c5
46 changed files with 197 additions and 147 deletions

View File

@ -70,6 +70,7 @@ public class LocalDateTimeUtils {
* @param endTime2 校验所需的结束时间 * @param endTime2 校验所需的结束时间
* @return 是否重叠 * @return 是否重叠
*/ */
// TODO @puhui999LocalDateTimeUtil.isOverlap() 是不是可以满足呀?
public static boolean checkTimeOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) { public static boolean checkTimeOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) {
// 判断时间是否重叠 // 判断时间是否重叠
// 开始时间在已配置时段的结束时间之前 且 结束时间在已配置时段的开始时间之后 [] // 开始时间在已配置时段的结束时间之前 且 结束时间在已配置时段的开始时间之后 []

View File

@ -45,10 +45,8 @@ public interface ErrorCodeConstants {
ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1008006004, "商品 SKU 库存不足"); ErrorCode SKU_STOCK_NOT_ENOUGH = new ErrorCode(1008006004, "商品 SKU 库存不足");
// ========== 商品 评价 1008007000 ========== // ========== 商品 评价 1008007000 ==========
ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1008007000, "商品 评价 不存在"); ErrorCode COMMENT_NOT_EXISTS = new ErrorCode(1008007000, "商品评价不存在");
ErrorCode ORDER_SKU_COMMENT_EXISTS = new ErrorCode(1008007001, "订单 商品评价 已存在"); ErrorCode COMMENT_ORDER_EXISTS = new ErrorCode(1008007001, "订单商品评价已存在");
ErrorCode COMMENT_ERROR_OPT = new ErrorCode(1008007002, "商品评价非法操作");
ErrorCode COMMENT_ADDITIONAL_EXISTS = new ErrorCode(1008007003, "商品追加评价已存在");
// ========== 商品 收藏 1008008000 ========== // ========== 商品 收藏 1008008000 ==========
ErrorCode FAVORITE_EXISTS = new ErrorCode(1008008000, "该商品已经被收藏"); ErrorCode FAVORITE_EXISTS = new ErrorCode(1008008000, "该商品已经被收藏");

View File

@ -10,7 +10,7 @@ import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 商品Spu导出 Request VO,参数和 ProductSpuPageReqVO 是一致的") @Schema(description = "管理后台 - 商品 SPU 导出 Request VO参数和 ProductSpuPageReqVO 是一致的")
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor

View File

@ -51,14 +51,16 @@ public class AppProductCommentController {
@Parameter(name = "spuId", description = "商品 SPU 编号", required = true, example = "1024"), @Parameter(name = "spuId", description = "商品 SPU 编号", required = true, example = "1024"),
@Parameter(name = "count", description = "数量", required = true, example = "10") @Parameter(name = "count", description = "数量", required = true, example = "10")
}) })
public CommonResult<List<AppProductCommentRespVO>> getCommentList(@RequestParam("spuId") Long spuId, public CommonResult<List<AppProductCommentRespVO>> getCommentList(
@RequestParam(value = "count", defaultValue = "10") Integer count) { @RequestParam("spuId") Long spuId,
@RequestParam(value = "count", defaultValue = "10") Integer count) {
return success(productCommentService.getCommentList(spuId, count)); return success(productCommentService.getCommentList(spuId, count));
} }
@GetMapping("/page") @GetMapping("/page")
@Operation(summary = "获得商品评价分页") @Operation(summary = "获得商品评价分页")
public CommonResult<PageResult<AppProductCommentRespVO>> getCommentPage(@Valid AppCommentPageReqVO pageVO) { public CommonResult<PageResult<AppProductCommentRespVO>> getCommentPage(@Valid AppCommentPageReqVO pageVO) {
// TODO @puhui999写到 convert 里,可以更简洁哈。
PageResult<ProductCommentDO> commentDOPage = productCommentService.getCommentPage(pageVO, Boolean.TRUE); PageResult<ProductCommentDO> commentDOPage = productCommentService.getCommentPage(pageVO, Boolean.TRUE);
Set<Long> skuIds = CollectionUtils.convertSet(commentDOPage.getList(), ProductCommentDO::getSkuId); Set<Long> skuIds = CollectionUtils.convertSet(commentDOPage.getList(), ProductCommentDO::getSkuId);
List<ProductSkuDO> skuList = productSkuService.getSkuList(skuIds); List<ProductSkuDO> skuList = productSkuService.getSkuList(skuIds);

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.product.convert.comment;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentCreateReqVO; import cn.iocoder.yudao.module.product.controller.admin.comment.vo.ProductCommentCreateReqVO;
@ -34,37 +35,33 @@ public interface ProductCommentConvert {
ProductCommentRespVO convert(ProductCommentDO bean); ProductCommentRespVO convert(ProductCommentDO bean);
@Mapping(target = "scores", expression = "java(calculateOverallScore(goodCount, mediocreCount, negativeCount))")
AppCommentStatisticsRespVO convert(Long goodCount, Long mediocreCount, Long negativeCount);
@Named("calculateOverallScore") @Named("calculateOverallScore")
default double calculateOverallScore(long goodCount, long mediocreCount, long negativeCount) { default double calculateOverallScore(long goodCount, long mediocreCount, long negativeCount) {
return (goodCount * 5 + mediocreCount * 3 + negativeCount) / (double) (goodCount + mediocreCount + negativeCount); return (goodCount * 5 + mediocreCount * 3 + negativeCount) / (double) (goodCount + mediocreCount + negativeCount);
} }
@Mapping(target = "scores", expression = "java(calculateOverallScore(goodCount, mediocreCount, negativeCount))")
AppCommentStatisticsRespVO convert(Long goodCount, Long mediocreCount, Long negativeCount);
List<ProductCommentRespVO> convertList(List<ProductCommentDO> list); List<ProductCommentRespVO> convertList(List<ProductCommentDO> list);
List<AppProductPropertyValueDetailRespVO> convertList01(List<ProductSkuDO.Property> properties);
PageResult<ProductCommentRespVO> convertPage(PageResult<ProductCommentDO> page); PageResult<ProductCommentRespVO> convertPage(PageResult<ProductCommentDO> page);
PageResult<AppProductCommentRespVO> convertPage01(PageResult<ProductCommentDO> pageResult); PageResult<AppProductCommentRespVO> convertPage01(PageResult<ProductCommentDO> pageResult);
default PageResult<AppProductCommentRespVO> convertPage02(PageResult<ProductCommentDO> pageResult, Map<Long, ProductSkuDO> skuDOMap) { default PageResult<AppProductCommentRespVO> convertPage02(PageResult<ProductCommentDO> pageResult,
Map<Long, ProductSkuDO> skuMap) {
PageResult<AppProductCommentRespVO> page = convertPage01(pageResult); PageResult<AppProductCommentRespVO> page = convertPage01(pageResult);
page.getList().forEach(item -> { page.getList().forEach(item -> {
// 判断用户是否选择匿名 // 判断用户是否选择匿名
if (ObjectUtil.equal(item.getAnonymous(), true)) { if (ObjectUtil.equal(item.getAnonymous(), true)) {
item.setUserNickname(ProductCommentDO.NICKNAME_ANONYMOUS); item.setUserNickname(ProductCommentDO.NICKNAME_ANONYMOUS);
} }
ProductSkuDO productSkuDO = skuDOMap.get(item.getSkuId()); MapUtils.findAndThen(skuMap, item.getSkuId(),
if (productSkuDO != null) { sku -> item.setSkuProperties(convertList01(sku.getProperties())));
List<AppProductPropertyValueDetailRespVO> skuProperties = ProductCommentConvert.INSTANCE.convertList01(productSkuDO.getProperties());
item.setSkuProperties(skuProperties);
}
}); });
return page; return page;
} }
List<AppProductPropertyValueDetailRespVO> convertList01(List<ProductSkuDO.Property> properties);
/** /**
* 计算综合评分 * 计算综合评分
@ -83,14 +80,19 @@ public interface ProductCommentConvert {
ProductCommentDO convert(ProductCommentCreateReqDTO createReqDTO); ProductCommentDO convert(ProductCommentCreateReqDTO createReqDTO);
@Mapping(target = "scores", expression = "java(convertScores(createReqDTO.getDescriptionScores(), createReqDTO.getBenefitScores()))") @Mapping(target = "scores",
expression = "java(convertScores(createReqDTO.getDescriptionScores(), createReqDTO.getBenefitScores()))")
default ProductCommentDO convert(ProductCommentCreateReqDTO createReqDTO, ProductSpuDO spuDO, MemberUserRespDTO user) { default ProductCommentDO convert(ProductCommentCreateReqDTO createReqDTO, ProductSpuDO spuDO, MemberUserRespDTO user) {
ProductCommentDO commentDO = convert(createReqDTO); ProductCommentDO commentDO = convert(createReqDTO);
commentDO.setUserId(user.getId()); if (user != null) {
commentDO.setUserNickname(user.getNickname()); commentDO.setUserId(user.getId());
commentDO.setUserAvatar(user.getAvatar()); commentDO.setUserNickname(user.getNickname());
commentDO.setSpuId(spuDO.getId()); commentDO.setUserAvatar(user.getAvatar());
commentDO.setSpuName(spuDO.getName()); }
if (spuDO != null) {
commentDO.setSpuId(spuDO.getId());
commentDO.setSpuName(spuDO.getName());
}
return commentDO; return commentDO;
} }
@ -98,7 +100,8 @@ public interface ProductCommentConvert {
@Mapping(target = "orderId", constant = "0L") @Mapping(target = "orderId", constant = "0L")
@Mapping(target = "orderItemId", constant = "0L") @Mapping(target = "orderItemId", constant = "0L")
@Mapping(target = "anonymous", expression = "java(Boolean.FALSE)") @Mapping(target = "anonymous", expression = "java(Boolean.FALSE)")
@Mapping(target = "scores", expression = "java(convertScores(createReq.getDescriptionScores(), createReq.getBenefitScores()))") @Mapping(target = "scores",
expression = "java(convertScores(createReq.getDescriptionScores(), createReq.getBenefitScores()))")
ProductCommentDO convert(ProductCommentCreateReqVO createReq); ProductCommentDO convert(ProductCommentCreateReqVO createReq);
List<AppProductCommentRespVO> convertList02(List<ProductCommentDO> list); List<AppProductCommentRespVO> convertList02(List<ProductCommentDO> list);

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.product.convert.spu; package cn.iocoder.yudao.module.product.convert.spu;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*; import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
@ -22,6 +21,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import static cn.hutool.core.util.ObjectUtil.defaultIfNull; import static cn.hutool.core.util.ObjectUtil.defaultIfNull;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
/** /**
* 商品 SPU Convert * 商品 SPU Convert
@ -110,8 +110,9 @@ public interface ProductSpuConvert {
} }
default List<ProductSpuDetailRespVO> convertForSpuDetailRespListVO(List<ProductSpuDO> spus, List<ProductSkuDO> skus) { default List<ProductSpuDetailRespVO> convertForSpuDetailRespListVO(List<ProductSpuDO> spus, List<ProductSkuDO> skus) {
ArrayList<ProductSpuDetailRespVO> vos = new ArrayList<>(); List<ProductSpuDetailRespVO> vos = new ArrayList<>(spus.size());
Map<Long, List<ProductSkuDO>> skuMultiMap = CollectionUtils.convertMultiMap(skus, ProductSkuDO::getSpuId); Map<Long, List<ProductSkuDO>> skuMultiMap = convertMultiMap(skus, ProductSkuDO::getSpuId);
// TODO @puhui999可以直接使用 CollUtils.convertList
spus.forEach(spu -> { spus.forEach(spu -> {
ProductSpuDetailRespVO detailRespVO = convert03(spu); ProductSpuDetailRespVO detailRespVO = convert03(spu);
detailRespVO.setSkus(ProductSkuConvert.INSTANCE.convertList(skuMultiMap.get(spu.getId()))); detailRespVO.setSkus(ProductSkuConvert.INSTANCE.convertList(skuMultiMap.get(spu.getId())));

View File

@ -26,6 +26,7 @@ public interface ProductCommentMapper extends BaseMapperX<ProductCommentDO> {
} }
static void appendTabQuery(LambdaQueryWrapperX<ProductCommentDO> queryWrapper, Integer type) { static void appendTabQuery(LambdaQueryWrapperX<ProductCommentDO> queryWrapper, Integer type) {
// TODO @puhui999是不是不用 apply 拉?直接用 mybatis 的方法就好啦
// 构建好评查询语句:好评计算 总评 >= 4 // 构建好评查询语句:好评计算 总评 >= 4
if (ObjectUtil.equal(type, AppCommentPageReqVO.GOOD_COMMENT)) { if (ObjectUtil.equal(type, AppCommentPageReqVO.GOOD_COMMENT)) {
queryWrapper.apply("scores >= 4"); queryWrapper.apply("scores >= 4");

View File

@ -112,7 +112,7 @@ public class ProductCommentServiceImpl implements ProductCommentService {
// 判断当前订单的当前商品用户是否评价过 // 判断当前订单的当前商品用户是否评价过
ProductCommentDO exist = productCommentMapper.selectByUserIdAndOrderItemIdAndSpuId(userId, orderItemId, skuId); ProductCommentDO exist = productCommentMapper.selectByUserIdAndOrderItemIdAndSpuId(userId, orderItemId, skuId);
if (null != exist) { if (null != exist) {
throw exception(ORDER_SKU_COMMENT_EXISTS); throw exception(COMMENT_ORDER_EXISTS);
} }
} }

View File

@ -4,6 +4,8 @@ import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordRe
import javax.validation.Valid; import javax.validation.Valid;
// TODO @puhui999:CombinationRecordApi 分成活动、记录哈
// TODO @芋艿:后面也再撸撸这几个接口
/** /**
* 拼团活动 API 接口 * 拼团活动 API 接口
* *
@ -26,7 +28,6 @@ public interface CombinationApi {
*/ */
boolean validateRecordStatusIsSuccess(Long userId, Long orderId); boolean validateRecordStatusIsSuccess(Long userId, Long orderId);
/** /**
* 更新开团记录状态 * 更新开团记录状态
* *
@ -42,7 +43,6 @@ public interface CombinationApi {
* @param userId 用户编号 * @param userId 用户编号
* @param orderId 订单编号 * @param orderId 订单编号
* @param status 状态值 * @param status 状态值
* @return
*/ */
void updateRecordStatusAndStartTime(Long userId, Long orderId, Integer status); void updateRecordStatusAndStartTime(Long userId, Long orderId, Integer status);

View File

@ -5,6 +5,7 @@ import lombok.Data;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
// TODO @puhui999CombinationRecordCreateReqDTO这样更容易知道是创建噢
/** /**
* 拼团记录 Request DTO * 拼团记录 Request DTO
* *

View File

@ -42,8 +42,7 @@ public interface ErrorCodeConstants {
ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013006004, "满减送活动已关闭,不能重复关闭"); ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1013006004, "满减送活动已关闭,不能重复关闭");
ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1013006005, "满减送活动已结束,不能关闭"); ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1013006005, "满减送活动已结束,不能关闭");
// ========== Price 相关 1013007000 ============ // ========== TODO 空着 1013007000 ============
ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1013007000, "支付价格计算异常,原因:价格小于等于 0");
// ========== 秒杀活动 1013008000 ========== // ========== 秒杀活动 1013008000 ==========
ErrorCode SECKILL_ACTIVITY_NOT_EXISTS = new ErrorCode(1013008000, "秒杀活动不存在"); ErrorCode SECKILL_ACTIVITY_NOT_EXISTS = new ErrorCode(1013008000, "秒杀活动不存在");
@ -66,4 +65,5 @@ public interface ErrorCodeConstants {
ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013010002, "拼团活动已关闭不能修改"); ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013010002, "拼团活动已关闭不能修改");
ErrorCode COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013010003, "拼团活动未关闭或未结束,不能删除"); ErrorCode COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013010003, "拼团活动未关闭或未结束,不能删除");
ErrorCode COMBINATION_RECORD_NOT_EXISTS = new ErrorCode(1013010004, "拼团不存在"); ErrorCode COMBINATION_RECORD_NOT_EXISTS = new ErrorCode(1013010004, "拼团不存在");
} }

View File

@ -14,8 +14,9 @@ import java.util.Arrays;
@AllArgsConstructor @AllArgsConstructor
@Getter @Getter
public enum CombinationRecordStatusEnum implements IntArrayValuable { public enum CombinationRecordStatusEnum implements IntArrayValuable {
NOT_PAY(0, "未付款"),
ONGOING(1, "进行中"), WAITING(0, "未付款"),
IN_PROGRESS(1, "进行中"),
SUCCESS(2, "拼团成功"), SUCCESS(2, "拼团成功"),
FAILED(3, "拼团失败"); FAILED(3, "拼团失败");

View File

@ -14,6 +14,7 @@ import java.time.LocalDateTime;
*/ */
@Service @Service
public class CombinationApiImpl implements CombinationApi { public class CombinationApiImpl implements CombinationApi {
@Resource @Resource
private CombinationActivityService activityService; private CombinationActivityService activityService;

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.promotion.controller.admin.combination; package cn.iocoder.yudao.module.promotion.controller.admin.combination;
import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
@ -28,6 +27,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import static cn.hutool.core.collection.CollectionUtil.newArrayList;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@ -71,9 +71,9 @@ public class CombinationActivityController {
@Parameter(name = "id", description = "编号", required = true, example = "1024") @Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')") @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')")
public CommonResult<CombinationActivityRespVO> getCombinationActivity(@RequestParam("id") Long id) { public CommonResult<CombinationActivityRespVO> getCombinationActivity(@RequestParam("id") Long id) {
CombinationActivityDO combinationActivity = combinationActivityService.getCombinationActivity(id); CombinationActivityDO activity = combinationActivityService.getCombinationActivity(id);
List<CombinationProductDO> productDOs = combinationActivityService.getProductsByActivityIds(CollectionUtil.newArrayList(id)); List<CombinationProductDO> products = combinationActivityService.getProductsByActivityIds(newArrayList(id));
return success(CombinationActivityConvert.INSTANCE.convert(combinationActivity, productDOs)); return success(CombinationActivityConvert.INSTANCE.convert(activity, products));
} }
@GetMapping("/list") @GetMapping("/list")
@ -88,13 +88,15 @@ public class CombinationActivityController {
@GetMapping("/page") @GetMapping("/page")
@Operation(summary = "获得拼团活动分页") @Operation(summary = "获得拼团活动分页")
@PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')") @PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')")
public CommonResult<PageResult<CombinationActivityRespVO>> getCombinationActivityPage(@Valid CombinationActivityPageReqVO pageVO) { public CommonResult<PageResult<CombinationActivityRespVO>> getCombinationActivityPage(
@Valid CombinationActivityPageReqVO pageVO) {
PageResult<CombinationActivityDO> pageResult = combinationActivityService.getCombinationActivityPage(pageVO); PageResult<CombinationActivityDO> pageResult = combinationActivityService.getCombinationActivityPage(pageVO);
// TODO @puhui999可以不一定 aIds直接批量查询结果出来下面也是类似
Set<Long> aIds = CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getId); Set<Long> aIds = CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getId);
List<CombinationProductDO> productDOs = combinationActivityService.getProductsByActivityIds(aIds); List<CombinationProductDO> products = combinationActivityService.getProductsByActivityIds(aIds);
Set<Long> spuIds = CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getSpuId); Set<Long> spuIds = CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getSpuId);
List<ProductSpuRespDTO> spuList = spuApi.getSpuList(spuIds); List<ProductSpuRespDTO> spus = spuApi.getSpuList(spuIds);
return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult, productDOs, spuList)); return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult, products, spus));
} }
@GetMapping("/export-excel") @GetMapping("/export-excel")

View File

@ -20,7 +20,7 @@ public class CombinationActivityBaseVO {
@NotNull(message = "拼团名称不能为空") @NotNull(message = "拼团名称不能为空")
private String name; private String name;
@Schema(description = "商品 SPU 编号关联 ProductSpuDO 的 id", example = "[1,2,3]") @Schema(description = "商品 SPU 编号关联 ProductSpuDO 的 id", example = "[1,2,3]")
@NotNull(message = "拼团商品不能为空") @NotNull(message = "拼团商品不能为空")
private Long spuId; private Long spuId;
@ -32,6 +32,7 @@ public class CombinationActivityBaseVO {
@NotNull(message = "单次限购数量不能为空") @NotNull(message = "单次限购数量不能为空")
private Integer singleLimitCount; private Integer singleLimitCount;
// TODO @puhui999是不是弄成 2 个字段会好点哈。开始、结束
@Schema(description = "活动时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]") @Schema(description = "活动时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]")
@NotNull(message = "活动时间不能为空") @NotNull(message = "活动时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
@ -42,7 +43,7 @@ public class CombinationActivityBaseVO {
private Integer userSize; private Integer userSize;
@Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "限制时长(小时)", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "限制时长(小时)不能为空") @NotNull(message = "限制时长不能为空")
private Integer limitDuration; private Integer limitDuration;
} }

View File

@ -7,7 +7,7 @@ import lombok.Data;
import java.time.LocalDateTime; import java.time.LocalDateTime;
// TODO @puhui999如无必要导出都可以删除哈
/** /**
* 拼团活动 Excel VO * 拼团活动 Excel VO
* *

View File

@ -8,6 +8,7 @@ import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
// TODO @puhui999如无必要导出都可以删除哈
@Schema(description = "管理后台 - 拼团活动 Excel 导出 Request VO参数和 CombinationActivityPageReqVO 是一致的") @Schema(description = "管理后台 - 拼团活动 Excel 导出 Request VO参数和 CombinationActivityPageReqVO 是一致的")
@Data @Data
public class CombinationActivityExportReqVO { public class CombinationActivityExportReqVO {

View File

@ -45,7 +45,7 @@ public class CombinationActivityRespVO extends CombinationActivityBaseVO {
@NotNull(message = "虚拟成团不能为空") @NotNull(message = "虚拟成团不能为空")
private Integer virtualGroup; private Integer virtualGroup;
@Schema(description = "活动状态0开启 1关闭", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") @Schema(description = "活动状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "活动状态不能为空") @NotNull(message = "活动状态不能为空")
private Integer status; private Integer status;

View File

@ -5,6 +5,7 @@ import lombok.Data;
import java.time.LocalDateTime; import java.time.LocalDateTime;
// TODO @puhui999可以考虑删除 excel 导出哈
/** /**
* 拼团商品 Excel VO * 拼团商品 Excel VO
* *

View File

@ -8,6 +8,7 @@ import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
// TODO @puhui999可以考虑删除 excel 导出哈
@Schema(description = "管理后台 - 拼团商品 Excel 导出 Request VO参数和 CombinationProductPageReqVO 是一致的") @Schema(description = "管理后台 - 拼团商品 Excel 导出 Request VO参数和 CombinationProductPageReqVO 是一致的")
@Data @Data
public class CombinationProductExportReqVO { public class CombinationProductExportReqVO {

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.promotion.convert.combination;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO; import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO; import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
@ -26,6 +25,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
/** /**
* 拼团活动 Convert * 拼团活动 Convert
* *
@ -74,13 +75,17 @@ public interface CombinationActivityConvert {
PageResult<CombinationActivityRespVO> convertPage(PageResult<CombinationActivityDO> page); PageResult<CombinationActivityRespVO> convertPage(PageResult<CombinationActivityDO> page);
default PageResult<CombinationActivityRespVO> convertPage(PageResult<CombinationActivityDO> page, List<CombinationProductDO> productDOList, List<ProductSpuRespDTO> spuList) { default PageResult<CombinationActivityRespVO> convertPage(PageResult<CombinationActivityDO> page,
Map<Long, ProductSpuRespDTO> spuMap = CollectionUtils.convertMap(spuList, ProductSpuRespDTO::getId, c -> c); List<CombinationProductDO> productList,
List<ProductSpuRespDTO> spuList) {
// TODO @puhui999c -> c 可以去掉哈
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId, c -> c);
PageResult<CombinationActivityRespVO> pageResult = convertPage(page); PageResult<CombinationActivityRespVO> pageResult = convertPage(page);
pageResult.getList().forEach(item -> { pageResult.getList().forEach(item -> {
// TODO @puhui999最好 MapUtils.findAndThen万一没找到呢啊哈哈。
item.setSpuName(spuMap.get(item.getSpuId()).getName()); item.setSpuName(spuMap.get(item.getSpuId()).getName());
item.setPicUrl(spuMap.get(item.getSpuId()).getPicUrl()); item.setPicUrl(spuMap.get(item.getSpuId()).getPicUrl());
item.setProducts(convertList2(productDOList)); item.setProducts(convertList2(productList));
}); });
return pageResult; return pageResult;
} }
@ -111,12 +116,17 @@ public interface CombinationActivityConvert {
return list; return list;
} }
default List<CombinationProductDO> convertList1(CombinationActivityDO activityDO, List<CombinationProductUpdateReqVO> vos, List<CombinationProductDO> productDOs) { // TODO @puhui999这个方法的参数调整成 productDOs、vos、activityDO因为 productDOs 是主角;
Map<Long, Long> longMap = CollectionUtils.convertMap(productDOs, CombinationProductDO::getSkuId, CombinationProductDO::getId); // 然后,这个方法,感觉不是为了 convert而是为了补全
default List<CombinationProductDO> convertList1(CombinationActivityDO activityDO,
List<CombinationProductUpdateReqVO> vos,
List<CombinationProductDO> productDOs) {
Map<Long, Long> longMap = convertMap(productDOs, CombinationProductDO::getSkuId, CombinationProductDO::getId);
List<CombinationProductDO> list = new ArrayList<>(); List<CombinationProductDO> list = new ArrayList<>();
vos.forEach(sku -> { vos.forEach(sku -> {
CombinationProductDO productDO = convert(activityDO, sku); CombinationProductDO productDO = convert(activityDO, sku);
productDO.setId(longMap.get(sku.getSkuId())); productDO.setId(longMap.get(sku.getSkuId()));
// TODO @puhui999是是不是用 activityDO 的状态;
productDO.setActivityStatus(CommonStatusEnum.ENABLE.getStatus()); productDO.setActivityStatus(CommonStatusEnum.ENABLE.getStatus());
list.add(productDO); list.add(productDO);
}); });

View File

@ -84,6 +84,7 @@ public interface SeckillActivityConvert {
return list; return list;
} }
// TODO @puhui999同拼团那个 convert 想通的情况哈。
default List<SeckillProductDO> convertList1(SeckillActivityDO activityDO, List<SeckillProductUpdateReqVO> vos, List<SeckillProductDO> productDOs) { default List<SeckillProductDO> convertList1(SeckillActivityDO activityDO, List<SeckillProductUpdateReqVO> vos, List<SeckillProductDO> productDOs) {
Map<Long, Long> longMap = CollectionUtils.convertMap(productDOs, SeckillProductDO::getSkuId, SeckillProductDO::getId); Map<Long, Long> longMap = CollectionUtils.convertMap(productDOs, SeckillProductDO::getSkuId, SeckillProductDO::getId);
List<SeckillProductDO> list = new ArrayList<>(); List<SeckillProductDO> list = new ArrayList<>();

View File

@ -9,6 +9,7 @@ import lombok.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
// TODO @puhui999是不是应该在 combination 哈?
/** /**
* 拼团活动 DO * 拼团活动 DO
* *
@ -34,7 +35,9 @@ public class CombinationActivityDO extends BaseDO {
*/ */
private String name; private String name;
/** /**
* 商品 SPU 编号关联 ProductSpuDO 的 id * 商品 SPU 编号
*
* 关联 ProductSpuDO 的 id
*/ */
private Long spuId; private Long spuId;
/** /**
@ -75,7 +78,7 @@ public class CombinationActivityDO extends BaseDO {
private Integer virtualGroup; private Integer virtualGroup;
/** /**
* 活动状态0开启 1关闭 * 活动状态0开启 1关闭
* <p> *
* 枚举 {@link CommonStatusEnum} * 枚举 {@link CommonStatusEnum}
*/ */
private Integer status; private Integer status;

View File

@ -31,11 +31,11 @@ public class CombinationRecordDO extends BaseDO {
*/ */
private Long activityId; private Long activityId;
/** /**
* spu 编号 * SPU 编号
*/ */
private Long spuId; private Long spuId;
/** /**
* sku 编号 * SKU 编号
*/ */
private Long skuId; private Long skuId;
/** /**
@ -48,7 +48,7 @@ public class CombinationRecordDO extends BaseDO {
private Long orderId; private Long orderId;
/** /**
* 团长编号 * 团长编号
* <p> *
* 关联 {@link CombinationRecordDO#getUserId()} * 关联 {@link CombinationRecordDO#getUserId()}
*/ */
private Long headId; private Long headId;
@ -84,7 +84,8 @@ public class CombinationRecordDO extends BaseDO {
private Boolean virtualGroup; private Boolean virtualGroup;
/** /**
* 过期时间,单位:小时 * 过期时间,单位:小时
* 关联关联 {@link CombinationActivityDO#getLimitDuration()} *
* 关联 {@link CombinationActivityDO#getLimitDuration()}
*/ */
private Integer expireTime; private Integer expireTime;
/** /**
@ -97,6 +98,7 @@ public class CombinationRecordDO extends BaseDO {
private LocalDateTime endTime; private LocalDateTime endTime;
/** /**
* 开团需要人数 * 开团需要人数
*
* 关联 {@link CombinationActivityDO#getUserSize()} * 关联 {@link CombinationActivityDO#getUserSize()}
*/ */
private Integer userSize; private Integer userSize;

View File

@ -1 +0,0 @@
package cn.iocoder.yudao.module.promotion.dal.dataobject.combination;

View File

@ -47,11 +47,11 @@ public class SeckillProductDO extends BaseDO {
@TableField(typeHandler = LongListTypeHandler.class) @TableField(typeHandler = LongListTypeHandler.class)
private List<Long> configIds; private List<Long> configIds;
/** /**
* 商品 spu_id * 商品 SPU 编号
*/ */
private Long spuId; private Long spuId;
/** /**
* 商品 sku_id * 商品 SKU 编号
*/ */
private Long skuId; private Long skuId;
/** /**

View File

@ -50,7 +50,7 @@ public class SeckillConfigDO extends BaseDO {
private List<String> sliderPicUrls; private List<String> sliderPicUrls;
/** /**
* 状态 * 状态
* <p> *
* 枚举 {@link CommonStatusEnum 对应的类} * 枚举 {@link CommonStatusEnum 对应的类}
*/ */
private Integer status; private Integer status;

View File

@ -10,6 +10,7 @@ import org.apache.ibatis.annotations.Mapper;
import java.util.List; import java.util.List;
// TODO @puhui999是不是应该在 combination 哈?
/** /**
* 拼团活动 Mapper * 拼团活动 Mapper
* *

View File

@ -16,16 +16,18 @@ import java.util.List;
public interface CombinationRecordMapper extends BaseMapperX<CombinationRecordDO> { public interface CombinationRecordMapper extends BaseMapperX<CombinationRecordDO> {
default CombinationRecordDO selectRecord(Long userId, Long orderId) { default CombinationRecordDO selectRecord(Long userId, Long orderId) {
return selectOne(CombinationRecordDO::getUserId, userId, CombinationRecordDO::getOrderId, orderId); return selectOne(CombinationRecordDO::getUserId, userId,
CombinationRecordDO::getOrderId, orderId);
} }
default List<CombinationRecordDO> selectListByHeadIdAndStatus(Long headId, Integer status) { default List<CombinationRecordDO> selectListByHeadIdAndStatus(Long headId, Integer status) {
return selectList(new LambdaQueryWrapperX<CombinationRecordDO>().eq(CombinationRecordDO::getHeadId, headId) return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()
.eq(CombinationRecordDO::getHeadId, headId)
.eq(CombinationRecordDO::getStatus, status)); .eq(CombinationRecordDO::getStatus, status));
} }
default List<CombinationRecordDO> selectListByStatus(Integer status) { default List<CombinationRecordDO> selectListByStatus(Integer status) {
return selectList(new LambdaQueryWrapperX<CombinationRecordDO>().eq(CombinationRecordDO::getStatus, status)); return selectList(CombinationRecordDO::getStatus, status);
} }
} }

View File

@ -1 +0,0 @@
package cn.iocoder.yudao.module.promotion.dal.mysql.combination;

View File

@ -23,5 +23,4 @@ public interface SeckillConfigMapper extends BaseMapperX<SeckillConfigDO> {
return selectList(SeckillConfigDO::getStatus, status); return selectList(SeckillConfigDO::getStatus, status);
} }
} }

View File

@ -83,6 +83,7 @@ public interface CombinationActivityService {
*/ */
List<CombinationProductDO> getProductsByActivityIds(Collection<Long> ids); List<CombinationProductDO> getProductsByActivityIds(Collection<Long> ids);
// TODO @puhui999拆一个 CombinationRecordService 里,方法名可以简洁成 updateRecordStatusByOrderIdservice 方法可以稍微简单一点,如果是 update 方法
/** /**
* 更新拼团状态 * 更新拼团状态
* *
@ -99,10 +100,10 @@ public interface CombinationActivityService {
* @param orderId 订单编号 * @param orderId 订单编号
* @param status 状态 * @param status 状态
* @param startTime 开始时间 * @param startTime 开始时间
* @return
*/ */
void updateRecordStatusAndStartTimeByUserIdAndOrderId(Long userId, Long orderId, Integer status, LocalDateTime startTime); void updateRecordStatusAndStartTimeByUserIdAndOrderId(Long userId, Long orderId, Integer status, LocalDateTime startTime);
// TODO @puhui999拆一个 CombinationRecordService 里
/** /**
* 创建拼团记录 * 创建拼团记录
* *
@ -118,4 +119,5 @@ public interface CombinationActivityService {
* @return 拼团状态 * @return 拼团状态
*/ */
boolean validateRecordStatusIsSuccess(Long userId, Long orderId); boolean validateRecordStatusIsSuccess(Long userId, Long orderId);
} }

View File

@ -254,28 +254,33 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
@Override @Override
public void createRecord(CombinationRecordReqDTO reqDTO) { public void createRecord(CombinationRecordReqDTO reqDTO) {
// 校验拼团活动 // 校验拼团活动
CombinationActivityDO activityDO = validateCombinationActivityExists(reqDTO.getActivityId()); CombinationActivityDO activity = validateCombinationActivityExists(reqDTO.getActivityId());
// TODO @puhui999需要校验下它当前是不是已经参加了该拼团
// TODO @puhui999: 父拼团是否存在,是否已经满了
CombinationRecordDO recordDO = CombinationActivityConvert.INSTANCE.convert(reqDTO); CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO);
recordDO.setVirtualGroup(false); record.setVirtualGroup(false);
recordDO.setExpireTime(activityDO.getLimitDuration()); // TODO @puhui999过期时间应该是 Date 哈;
recordDO.setUserSize(activityDO.getUserSize()); record.setExpireTime(activity.getLimitDuration());
recordMapper.insert(recordDO); record.setUserSize(activity.getUserSize());
recordMapper.insert(record);
} }
@Override @Override
public boolean validateRecordStatusIsSuccess(Long userId, Long orderId) { public boolean validateRecordStatusIsSuccess(Long userId, Long orderId) {
CombinationRecordDO recordDO = validateCombinationRecord(userId, orderId); CombinationRecordDO record = validateCombinationRecord(userId, orderId);
return ObjectUtil.equal(recordDO.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus()); // TODO @puhui999可以搞个 getRecrod 方法,然后业务通过 CombinationRecordStatusEnum.isSuccess 方法校验
return ObjectUtil.equal(record.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus());
} }
// TODO @puhui999status 传入进来搞哈;
/** /**
* APP 端获取开团记录 * APP 端获取开团记录
* *
* @return 开团记录 * @return 开团记录
*/ */
public List<CombinationRecordDO> getRecordList() { public List<CombinationRecordDO> getRecordList() {
return recordMapper.selectListByStatus(CombinationRecordStatusEnum.ONGOING.getStatus()); return recordMapper.selectListByStatus(CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
} }
} }

View File

@ -60,9 +60,10 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
public Long createSeckillActivity(SeckillActivityCreateReqVO createReqVO) { public Long createSeckillActivity(SeckillActivityCreateReqVO createReqVO) {
// 校验商品秒秒杀时段是否冲突 // 校验商品秒秒杀时段是否冲突
validateProductSpuSeckillConflict(createReqVO.getConfigIds(), createReqVO.getSpuId(), null); validateProductSpuSeckillConflict(createReqVO.getConfigIds(), createReqVO.getSpuId(), null);
// 获取所选 spu下的所有 sku // 获取所选 spu 下的所有 sku
List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(CollUtil.newArrayList(createReqVO.getSpuId())); List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(CollUtil.newArrayList(createReqVO.getSpuId()));
// 校验商品 sku 是否存在 // 校验商品 sku 是否存在
// TODO @puhui999直接校验 sku 数量,是不是就完事啦,不需要校验的特别严谨哈;
validateProductSkuExistence(skus, createReqVO.getProducts(), SeckillProductCreateReqVO::getSkuId); validateProductSkuExistence(skus, createReqVO.getProducts(), SeckillProductCreateReqVO::getSkuId);
// 插入秒杀活动 // 插入秒杀活动
@ -71,6 +72,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
.setTotalStock(CollectionUtils.getSumValue(createReqVO.getProducts(), SeckillProductCreateReqVO::getStock, Integer::sum)); .setTotalStock(CollectionUtils.getSumValue(createReqVO.getProducts(), SeckillProductCreateReqVO::getStock, Integer::sum));
seckillActivityMapper.insert(activity); seckillActivityMapper.insert(activity);
// 插入商品 // 插入商品
// TODO @puhui999products 要注意复数哈
List<SeckillProductDO> product = SeckillActivityConvert.INSTANCE.convertList(activity, createReqVO.getProducts()); List<SeckillProductDO> product = SeckillActivityConvert.INSTANCE.convertList(activity, createReqVO.getProducts());
seckillProductMapper.insertBatch(product); seckillProductMapper.insertBatch(product);
return activity.getId(); return activity.getId();
@ -97,6 +99,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
} }
List<SeckillActivityDO> activityDOs2 = CollectionUtils.convertList(activityDOs, c -> c, s -> { List<SeckillActivityDO> activityDOs2 = CollectionUtils.convertList(activityDOs, c -> c, s -> {
// 判断秒杀时段是否有交集 // 判断秒杀时段是否有交集
// TODO @puhui999是不是 containsAny 就是有交集呀
List<Long> configIdsClone = CollUtil.newArrayList(s.getConfigIds()); List<Long> configIdsClone = CollUtil.newArrayList(s.getConfigIds());
configIdsClone.retainAll(configIds); configIdsClone.retainAll(configIds);
return CollUtil.isNotEmpty(configIdsClone); return CollUtil.isNotEmpty(configIdsClone);
@ -134,29 +137,32 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
/** /**
* 更新秒杀商品 * 更新秒杀商品
* *
* @param updateObj DO * @param updateObj 更新的活动
* @param products 商品配置 * @param products 商品配置
*/ */
// TODO @puhui999我在想我们是不是可以封装一个 CollUtil 的方法,传入两个数组,判断出哪些是新增、哪些是修改、哪些是删除;
// 例如说products 先转化成 SeckillProductDO然后基于一个 func(key1, key2) 做比对;
// 如果可以跑通,所有涉及到这种逻辑的,都可以服用哈。
private void updateSeckillProduct(SeckillActivityDO updateObj, List<SeckillProductUpdateReqVO> products) { private void updateSeckillProduct(SeckillActivityDO updateObj, List<SeckillProductUpdateReqVO> products) {
List<SeckillProductDO> seckillProductDOs = seckillProductMapper.selectListByActivityId(updateObj.getId());
// 数据库中的活动商品 // 数据库中的活动商品
Set<Long> convertSet = CollectionUtils.convertSet(seckillProductDOs, SeckillProductDO::getSkuId); List<SeckillProductDO> seckillProductDOs = seckillProductMapper.selectListByActivityId(updateObj.getId());
// 前端传过来的活动商品 Set<Long> dbSkuIds = CollectionUtils.convertSet(seckillProductDOs, SeckillProductDO::getSkuId);
Set<Long> convertSet1 = CollectionUtils.convertSet(products, SeckillProductUpdateReqVO::getSkuId); // 1. 删除后台存在的前端不存在的商品
// 删除后台存在的前端不存在的商品 // TODO @puhui999delete 应该是 id不是 skuId 哈
List<Long> d = CollectionUtils.filterList(convertSet, item -> !convertSet1.contains(item)); Set<Long> voSkuIds = CollectionUtils.convertSet(products, SeckillProductUpdateReqVO::getSkuId);
List<Long> d = CollectionUtils.filterList(dbSkuIds, item -> !voSkuIds.contains(item));
if (CollUtil.isNotEmpty(d)) { if (CollUtil.isNotEmpty(d)) {
seckillProductMapper.deleteBatchIds(d); seckillProductMapper.deleteBatchIds(d);
} }
// 前端存在的后端不存在的商品 // 2. 前端存在的后端不存在的商品
List<Long> c = CollectionUtils.filterList(convertSet1, item -> !convertSet.contains(item)); List<Long> c = CollectionUtils.filterList(voSkuIds, item -> !dbSkuIds.contains(item));
if (CollUtil.isNotEmpty(c)) { if (CollUtil.isNotEmpty(c)) {
List<SeckillProductUpdateReqVO> vos = CollectionUtils.filterList(products, item -> c.contains(item.getSkuId())); List<SeckillProductUpdateReqVO> vos = CollectionUtils.filterList(products, item -> c.contains(item.getSkuId()));
List<SeckillProductDO> productDOs = SeckillActivityConvert.INSTANCE.convertList(updateObj, vos); List<SeckillProductDO> productDOs = SeckillActivityConvert.INSTANCE.convertList(updateObj, vos);
seckillProductMapper.insertBatch(productDOs); seckillProductMapper.insertBatch(productDOs);
} }
// 更新已存在的商品 // 3. 更新已存在的商品
List<Long> u = CollectionUtils.filterList(convertSet1, convertSet::contains); List<Long> u = CollectionUtils.filterList(voSkuIds, dbSkuIds::contains);
if (CollUtil.isNotEmpty(u)) { if (CollUtil.isNotEmpty(u)) {
List<SeckillProductUpdateReqVO> vos = CollectionUtils.filterList(products, item -> u.contains(item.getSkuId())); List<SeckillProductUpdateReqVO> vos = CollectionUtils.filterList(products, item -> u.contains(item.getSkuId()));
List<SeckillProductDO> productDOs = SeckillActivityConvert.INSTANCE.convertList1(updateObj, vos, seckillProductDOs); List<SeckillProductDO> productDOs = SeckillActivityConvert.INSTANCE.convertList1(updateObj, vos, seckillProductDOs);
@ -165,12 +171,12 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
} }
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class) // TODO @puhui999这个不用加事务哈
public void closeSeckillActivity(Long id) { public void closeSeckillActivity(Long id) {
// TODO 待验证没使用过 // TODO 待验证没使用过
// 校验存在 // 校验存在
SeckillActivityDO seckillActivity = validateSeckillActivityExists(id); SeckillActivityDO activity = validateSeckillActivityExists(id);
if (CommonStatusEnum.DISABLE.getStatus().equals(seckillActivity.getStatus())) { if (CommonStatusEnum.DISABLE.getStatus().equals(activity.getStatus())) {
throw exception(SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED); throw exception(SECKILL_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED);
} }

View File

@ -151,6 +151,7 @@ public class SeckillConfigServiceImpl implements SeckillConfigService {
return seckillConfigMapper.selectPage(pageVO); return seckillConfigMapper.selectPage(pageVO);
} }
// TODO @puhui999改成传入 enable 状态哈。一个通用的 getSeckillConfigList 方法
@Override @Override
public List<SeckillConfigDO> getListAllSimple() { public List<SeckillConfigDO> getListAllSimple() {
return seckillConfigMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus()); return seckillConfigMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());

View File

@ -31,6 +31,7 @@ public class PromotionUtils {
return LocalDateTimeUtils.beforeNow(endTime) ? CommonStatusEnum.DISABLE.getStatus() : CommonStatusEnum.ENABLE.getStatus(); return LocalDateTimeUtils.beforeNow(endTime) ? CommonStatusEnum.DISABLE.getStatus() : CommonStatusEnum.ENABLE.getStatus();
} }
// TODO @puhui999写个注释哈。
public static <T> void validateProductSkuExistence(List<ProductSkuRespDTO> skus, List<T> products, Function<T, Long> func) { public static <T> void validateProductSkuExistence(List<ProductSkuRespDTO> skus, List<T> products, Function<T, Long> func) {
// 校验 sku 个数是否一致 // 校验 sku 个数是否一致
Set<Long> skuIdsSet = CollectionUtils.convertSet(products, func); Set<Long> skuIdsSet = CollectionUtils.convertSet(products, func);

View File

@ -24,6 +24,7 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINATION_ACTIVITY_NOT_EXISTS; import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COMBINATION_ACTIVITY_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
// TODO 芋艿:等完成后,在补全单测
/** /**
* {@link CombinationActivityServiceImpl} 的单元测试类 * {@link CombinationActivityServiceImpl} 的单元测试类
* *

View File

@ -1,16 +1,8 @@
DELETE DELETE FROM "market_activity";
FROM "market_activity"; DELETE FROM "promotion_coupon_template";
DELETE DELETE FROM "promotion_coupon";
FROM "promotion_coupon_template"; DELETE FROM "promotion_reward_activity";
DELETE DELETE FROM "promotion_discount_activity";
FROM "promotion_coupon"; DELETE FROM "promotion_discount_product";
DELETE DELETE FROM "promotion_seckill_config";
FROM "promotion_reward_activity"; DELETE FROM "promotion_combination_activity";
DELETE
FROM "promotion_discount_activity";
DELETE
FROM "promotion_discount_product";
DELETE
FROM "promotion_seckill_config";
DELETE
FROM "promotion_combination_activity";

View File

@ -70,7 +70,9 @@ public interface ErrorCodeConstants {
ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1011006000, "自提门店不存在"); ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1011006000, "自提门店不存在");
// ========== 物流 PICK_UP 模块 1011007000 ========== // ========== 物流 PICK_UP 模块 1011007000 ==========
// TODO @puhui999这几个错误码应该属于订单哈
ErrorCode ORDER_DELIVERY_FAILED_ITEMS_NOT_EMPTY = new ErrorCode(1011007000, "订单发货失败,请选择发货商品"); ErrorCode ORDER_DELIVERY_FAILED_ITEMS_NOT_EMPTY = new ErrorCode(1011007000, "订单发货失败,请选择发货商品");
ErrorCode ORDER_DELIVERY_FAILED_ITEM_NOT_EXISTS = new ErrorCode(1011007001, "订单发货失败,所选发货商品不存在"); ErrorCode ORDER_DELIVERY_FAILED_ITEM_NOT_EXISTS = new ErrorCode(1011007001, "订单发货失败,所选发货商品不存在");
ErrorCode ORDER_DELIVERY_FAILED_ITEM_ALREADY_DELIVERY = new ErrorCode(1011007001, "订单发货失败,所选商品已发货"); ErrorCode ORDER_DELIVERY_FAILED_ITEM_ALREADY_DELIVERY = new ErrorCode(1011007002, "订单发货失败,所选商品已发货");
} }

View File

@ -21,13 +21,15 @@ public class TradeOrderDeliveryReqVO {
@NotNull(message = "发货类型不能为空") @NotNull(message = "发货类型不能为空")
private Integer type; private Integer type;
// TODO @puhui999还是要校验下
@Schema(description = "发货物流公司编号", example = "1") @Schema(description = "发货物流公司编号", example = "1")
private Long logisticsId; private Long logisticsId;
@Schema(description = "发货物流单号", example = "SF123456789") @Schema(description = "发货物流单号", example = "SF123456789")
private String logisticsNo; private String logisticsNo;
// TODO 订单项商品单独发货 // TODO 订单项商品单独发货;不做单独发
@Schema(description = "发货订单项", example = "[1,2,3]") @Schema(description = "发货订单项", example = "[1,2,3]")
@NotNull(message = "发货订单项不能为空") @NotNull(message = "发货订单项不能为空")

View File

@ -108,13 +108,17 @@ public class AppTradeOrderController {
// 全部 // 全部
orderCount.put("allCount", tradeOrderService.getOrderCount(getLoginUserId(), null, null)); orderCount.put("allCount", tradeOrderService.getOrderCount(getLoginUserId(), null, null));
// 待付款(未支付) // 待付款(未支付)
orderCount.put("unpaidCount", tradeOrderService.getOrderCount(getLoginUserId(), TradeOrderStatusEnum.UNPAID.getStatus(), null)); orderCount.put("unpaidCount", tradeOrderService.getOrderCount(getLoginUserId(),
TradeOrderStatusEnum.UNPAID.getStatus(), null));
// 待发货 // 待发货
orderCount.put("undeliveredCount", tradeOrderService.getOrderCount(getLoginUserId(), TradeOrderStatusEnum.UNDELIVERED.getStatus(), null)); orderCount.put("undeliveredCount", tradeOrderService.getOrderCount(getLoginUserId(),
TradeOrderStatusEnum.UNDELIVERED.getStatus(), null));
// 待收货 // 待收货
orderCount.put("deliveredCount", tradeOrderService.getOrderCount(getLoginUserId(), TradeOrderStatusEnum.DELIVERED.getStatus(), null)); orderCount.put("deliveredCount", tradeOrderService.getOrderCount(getLoginUserId(),
TradeOrderStatusEnum.DELIVERED.getStatus(), null));
// 待评价 // 待评价
orderCount.put("uncommentedCount", tradeOrderService.getOrderCount(getLoginUserId(), TradeOrderStatusEnum.COMPLETED.getStatus(), false)); orderCount.put("uncommentedCount", tradeOrderService.getOrderCount(getLoginUserId(),
TradeOrderStatusEnum.COMPLETED.getStatus(), false));
return success(orderCount); return success(orderCount);
} }

View File

@ -6,7 +6,7 @@ import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*; import lombok.*;
// TODO @puhui999:
/** /**
* 交易订单发货记录 DO * 交易订单发货记录 DO
* *

View File

@ -7,6 +7,7 @@ import org.apache.ibatis.annotations.Mapper;
import java.util.List; import java.util.List;
// TODO @puhui999应该去掉啦
/** /**
* 交易订单发货记录 Mapper * 交易订单发货记录 Mapper
* *

View File

@ -180,8 +180,7 @@ public interface TradeOrderService {
TradeOrderDO getOrderByIdAndUserId(Long orderId, Long loginUserId); TradeOrderDO getOrderByIdAndUserId(Long orderId, Long loginUserId);
/** /**
* 创建订单项评论 * 创建订单项评论
* 创建交易订单项的评价
* *
* @param createReqVO 创建请求 * @param createReqVO 创建请求
* @return 得到评价 id * @return 得到评价 id

View File

@ -178,7 +178,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
MemberUserRespDTO user = memberUserApi.getUser(userId); MemberUserRespDTO user = memberUserApi.getUser(userId);
// TODO 拼团一次应该只能选择一种规格的商品 // TODO 拼团一次应该只能选择一种规格的商品
combinationApi.createRecord(TradeOrderConvert.INSTANCE.convert(order, orderItems.get(0), createReqVO, user) combinationApi.createRecord(TradeOrderConvert.INSTANCE.convert(order, orderItems.get(0), createReqVO, user)
.setStatus(CombinationRecordStatusEnum.NOT_PAY.getStatus())); .setStatus(CombinationRecordStatusEnum.WAITING.getStatus()));
} }
// TODO 秒杀扣减库存是下单就扣除还是等待订单支付成功再扣除 // TODO 秒杀扣减库存是下单就扣除还是等待订单支付成功再扣除
if (ObjectUtil.equal(TradeOrderTypeEnum.SECKILL.getType(), order.getType())) { if (ObjectUtil.equal(TradeOrderTypeEnum.SECKILL.getType(), order.getType())) {
@ -204,23 +204,6 @@ public class TradeOrderServiceImpl implements TradeOrderService {
return address; return address;
} }
/**
* 校验活动返回订单类型
*
* @param createReqVO 请求参数
* @return 订单类型
*/
private Integer validateActivity(AppTradeOrderCreateReqVO createReqVO) {
if (createReqVO.getSeckillActivityId() != null) {
return TradeOrderTypeEnum.SECKILL.getType();
}
if (createReqVO.getCombinationActivityId() != null) {
return TradeOrderTypeEnum.COMBINATION.getType();
}
// TODO 砍价敬请期待
return TradeOrderTypeEnum.NORMAL.getType();
}
private TradeOrderDO createTradeOrder(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO, private TradeOrderDO createTradeOrder(Long userId, String clientIp, AppTradeOrderCreateReqVO createReqVO,
TradePriceCalculateRespBO calculateRespBO) { TradePriceCalculateRespBO calculateRespBO) {
// 用户选择物流配送的时候才需要填写收货地址 // 用户选择物流配送的时候才需要填写收货地址
@ -246,6 +229,23 @@ public class TradeOrderServiceImpl implements TradeOrderService {
return order; return order;
} }
/**
* 校验活动,并返回订单类型
*
* @param createReqVO 请求参数
* @return 订单类型
*/
private Integer validateActivity(AppTradeOrderCreateReqVO createReqVO) {
if (createReqVO.getSeckillActivityId() != null) {
return TradeOrderTypeEnum.SECKILL.getType();
}
if (createReqVO.getCombinationActivityId() != null) {
return TradeOrderTypeEnum.COMBINATION.getType();
}
// TODO 砍价敬请期待
return TradeOrderTypeEnum.NORMAL.getType();
}
private List<TradeOrderItemDO> createTradeOrderItems(TradeOrderDO tradeOrderDO, TradePriceCalculateRespBO calculateRespBO) { private List<TradeOrderItemDO> createTradeOrderItems(TradeOrderDO tradeOrderDO, TradePriceCalculateRespBO calculateRespBO) {
List<TradeOrderItemDO> orderItems = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, calculateRespBO); List<TradeOrderItemDO> orderItems = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, calculateRespBO);
tradeOrderItemMapper.insertBatch(orderItems); tradeOrderItemMapper.insertBatch(orderItems);
@ -314,7 +314,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
// 1、拼团活动 // 1、拼团活动
if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) { if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
// 更新拼团状态 TODO puhui999订单支付失败或订单支付过期删除这条拼团记录 // 更新拼团状态 TODO puhui999订单支付失败或订单支付过期删除这条拼团记录
combinationApi.updateRecordStatusAndStartTime(order.getUserId(), order.getId(), CombinationRecordStatusEnum.ONGOING.getStatus()); combinationApi.updateRecordStatusAndStartTime(order.getUserId(), order.getId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
} }
// TODO 芋艿:发送订单变化的消息 // TODO 芋艿:发送订单变化的消息
@ -422,6 +422,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
orderDeliveryMapper.insertBatch(deliveryDOs); orderDeliveryMapper.insertBatch(deliveryDOs);
// TODO 芋艿:发送订单变化的消息 // TODO 芋艿:发送订单变化的消息
// TODO @puhui999可以抽个 message 包,里面是 Order 所有的 message类似工作流的
// 发送站内信 // 发送站内信
// 1、构造消息 // 1、构造消息
Map<String, Object> msgMap = new HashMap<>(); Map<String, Object> msgMap = new HashMap<>();
@ -684,24 +685,26 @@ public class TradeOrderServiceImpl implements TradeOrderService {
@Override @Override
public Long createOrderItemComment(AppTradeOrderItemCommentCreateReqVO createReqVO) { public Long createOrderItemComment(AppTradeOrderItemCommentCreateReqVO createReqVO) {
Long loginUserId = getLoginUserId(); Long loginUserId = getLoginUserId();
// 先通过订单项 ID 查询订单项是否存在 // 先通过订单项 ID查询订单项是否存在
TradeOrderItemDO orderItemDO = getOrderItemByIdAndUserId(createReqVO.getOrderItemId(), loginUserId); TradeOrderItemDO orderItem = getOrderItemByIdAndUserId(createReqVO.getOrderItemId(), loginUserId);
if (orderItemDO == null) { if (orderItem == null) {
throw exception(ORDER_ITEM_NOT_FOUND); throw exception(ORDER_ITEM_NOT_FOUND);
} }
// 校验订单 // 校验订单
TradeOrderDO orderDO = getOrderByIdAndUserId(orderItemDO.getOrderId(), loginUserId); TradeOrderDO order = getOrderByIdAndUserId(orderItem.getOrderId(), loginUserId);
if (orderDO == null) { if (order == null) {
throw exception(ORDER_NOT_FOUND); throw exception(ORDER_NOT_FOUND);
} }
if (ObjectUtil.notEqual(orderDO.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus())) { if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus())) {
throw exception(ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED); throw exception(ORDER_COMMENT_FAIL_STATUS_NOT_COMPLETED);
} }
if (ObjectUtil.notEqual(orderDO.getCommentStatus(), Boolean.FALSE)) { if (ObjectUtil.notEqual(order.getCommentStatus(), Boolean.FALSE)) {
throw exception(ORDER_COMMENT_STATUS_NOT_FALSE); throw exception(ORDER_COMMENT_STATUS_NOT_FALSE);
} }
// TODO @puhui999是不是评论完要更新 status、commentStatus另外是不是上面 order 可以不校验,直接只判断 orderItem 就够;
// 对于 order 来说,就是评论完,把 order 更新完合理的 status 等字段。
ProductCommentCreateReqDTO productCommentCreateReqDTO = TradeOrderConvert.INSTANCE.convert04(createReqVO, orderItemDO); ProductCommentCreateReqDTO productCommentCreateReqDTO = TradeOrderConvert.INSTANCE.convert04(createReqVO, orderItem);
return productCommentApi.createComment(productCommentCreateReqDTO); return productCommentApi.createComment(productCommentCreateReqDTO);
} }

View File

@ -21,7 +21,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL; import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL;
/** /**
* 价格计算 Service 实现类 * 价格计算 Service 实现类