fix:完善 review 拼团、秒杀活动的实现提到的问题

This commit is contained in:
puhui999 2023-07-31 18:34:56 +08:00
parent 446951bd11
commit d156d43d63
43 changed files with 391 additions and 695 deletions

View File

@ -27,6 +27,10 @@ public class CollectionUtils {
return Arrays.stream(collections).anyMatch(CollectionUtil::isEmpty);
}
public static <T> boolean isAny(Collection<T> from, Predicate<T> predicate) {
return from.stream().anyMatch(predicate);
}
public static <T> List<T> filterList(Collection<T> from, Predicate<T> predicate) {
if (CollUtil.isEmpty(from)) {
return new ArrayList<>();

View File

@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.common.util.date;
import cn.hutool.core.date.LocalDateTimeUtil;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
@ -70,7 +71,7 @@ public class LocalDateTimeUtils {
* @param endTime2 校验所需的结束时间
* @return 是否重叠
*/
// TODO @puhui999LocalDateTimeUtil.isOverlap() 是不是可以满足呀
@Deprecated
public static boolean checkTimeOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) {
// 判断时间是否重叠
// 开始时间在已配置时段的结束时间之前 结束时间在已配置时段的开始时间之后 []
@ -81,4 +82,14 @@ public class LocalDateTimeUtils {
|| startTime1.isBefore(endTime2) && endTime1.isAfter(endTime2);
}
public static boolean isOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) {
// 日期部分使用了当前日期LocalDate.now()
LocalDateTime startDateTime1 = LocalDateTime.of(LocalDate.now(), startTime1);
LocalDateTime endDateTime1 = LocalDateTime.of(LocalDate.now(), endTime1);
LocalDateTime startDateTime2 = LocalDateTime.of(LocalDate.now(), startTime2);
LocalDateTime endDateTime2 = LocalDateTime.of(LocalDate.now(), endTime2);
return LocalDateTimeUtil.isOverlap(startDateTime1, endDateTime1, startDateTime2, endDateTime2);
}
}

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.product.controller.app.comment;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
@ -9,10 +8,8 @@ import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppCommentStati
import cn.iocoder.yudao.module.product.controller.app.comment.vo.AppProductCommentRespVO;
import cn.iocoder.yudao.module.product.convert.comment.ProductCommentConvert;
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
import cn.iocoder.yudao.module.product.service.comment.ProductCommentService;
import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
import com.google.common.collect.Maps;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
@ -27,7 +24,6 @@ import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@ -60,15 +56,9 @@ public class AppProductCommentController {
@GetMapping("/page")
@Operation(summary = "获得商品评价分页")
public CommonResult<PageResult<AppProductCommentRespVO>> getCommentPage(@Valid AppCommentPageReqVO pageVO) {
// TODO @puhui999写到 convert 可以更简洁哈
PageResult<ProductCommentDO> commentDOPage = productCommentService.getCommentPage(pageVO, Boolean.TRUE);
Set<Long> skuIds = CollectionUtils.convertSet(commentDOPage.getList(), ProductCommentDO::getSkuId);
List<ProductSkuDO> skuList = productSkuService.getSkuList(skuIds);
Map<Long, ProductSkuDO> skuDOMap = Maps.newLinkedHashMapWithExpectedSize(skuIds.size());
if (CollUtil.isNotEmpty(skuList)) {
skuDOMap.putAll(CollectionUtils.convertMap(skuList, ProductSkuDO::getId, c -> c));
}
PageResult<AppProductCommentRespVO> page = ProductCommentConvert.INSTANCE.convertPage02(commentDOPage, skuDOMap);
PageResult<AppProductCommentRespVO> page = ProductCommentConvert.INSTANCE.convertPage02(commentDOPage, productSkuService.getSkuList(skuIds));
return success(page);
}

View File

@ -1,7 +1,9 @@
package cn.iocoder.yudao.module.product.convert.comment;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
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.MapUtils;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
@ -13,6 +15,7 @@ import cn.iocoder.yudao.module.product.controller.app.property.vo.value.AppProdu
import cn.iocoder.yudao.module.product.dal.dataobject.comment.ProductCommentDO;
import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
import com.google.common.collect.Maps;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
@ -37,6 +40,7 @@ public interface ProductCommentConvert {
@Mapping(target = "scores", expression = "java(calculateOverallScore(goodCount, mediocreCount, negativeCount))")
AppCommentStatisticsRespVO convert(Long goodCount, Long mediocreCount, Long negativeCount);
@Named("calculateOverallScore")
default double calculateOverallScore(long goodCount, long mediocreCount, long negativeCount) {
return (goodCount * 5 + mediocreCount * 3 + negativeCount) / (double) (goodCount + mediocreCount + negativeCount);
@ -48,8 +52,11 @@ public interface ProductCommentConvert {
PageResult<AppProductCommentRespVO> convertPage01(PageResult<ProductCommentDO> pageResult);
default PageResult<AppProductCommentRespVO> convertPage02(PageResult<ProductCommentDO> pageResult,
Map<Long, ProductSkuDO> skuMap) {
default PageResult<AppProductCommentRespVO> convertPage02(PageResult<ProductCommentDO> pageResult, List<ProductSkuDO> skuList) {
Map<Long, ProductSkuDO> skuMap = Maps.newLinkedHashMapWithExpectedSize(skuList.size());
if (CollUtil.isNotEmpty(skuList)) {
skuMap.putAll(CollectionUtils.convertMap(skuList, ProductSkuDO::getId));
}
PageResult<AppProductCommentRespVO> page = convertPage01(pageResult);
page.getList().forEach(item -> {
// 判断用户是否选择匿名
@ -61,6 +68,7 @@ public interface ProductCommentConvert {
});
return page;
}
List<AppProductPropertyValueDetailRespVO> convertList01(List<ProductSkuDO.Property> properties);
/**

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.product.convert.spu;
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.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.product.controller.admin.spu.vo.*;
@ -81,6 +82,7 @@ public interface ProductSpuConvert {
}
return voList;
}
@Named("convertListForGetSpuList0")
List<AppProductSpuPageRespVO> convertListForGetSpuList0(List<ProductSpuDO> list);
@ -112,12 +114,7 @@ public interface ProductSpuConvert {
default List<ProductSpuDetailRespVO> convertForSpuDetailRespListVO(List<ProductSpuDO> spus, List<ProductSkuDO> skus) {
List<ProductSpuDetailRespVO> vos = new ArrayList<>(spus.size());
Map<Long, List<ProductSkuDO>> skuMultiMap = convertMultiMap(skus, ProductSkuDO::getSpuId);
// TODO @puhui999可以直接使用 CollUtils.convertList
spus.forEach(spu -> {
ProductSpuDetailRespVO detailRespVO = convert03(spu);
detailRespVO.setSkus(ProductSkuConvert.INSTANCE.convertList(skuMultiMap.get(spu.getId())));
vos.add(detailRespVO);
});
CollectionUtils.convertList(spus, spu -> vos.add(convert03(spu).setSkus(ProductSkuConvert.INSTANCE.convertList(skuMultiMap.get(spu.getId())))));
return vos;
}

View File

@ -26,18 +26,19 @@ public interface ProductCommentMapper extends BaseMapperX<ProductCommentDO> {
}
static void appendTabQuery(LambdaQueryWrapperX<ProductCommentDO> queryWrapper, Integer type) {
// TODO @puhui999是不是不用 apply 直接用 mybatis 的方法就好啦
LambdaQueryWrapperX<ProductCommentDO> queryWrapperX = new LambdaQueryWrapperX<>();
// 构建好评查询语句好评计算 总评 >= 4
if (ObjectUtil.equal(type, AppCommentPageReqVO.GOOD_COMMENT)) {
queryWrapper.apply("scores >= 4");
queryWrapperX.ge(ProductCommentDO::getScores, 4);
}
// 构建中评查询语句中评计算 总评 >= 3 总评 < 4
if (ObjectUtil.equal(type, AppCommentPageReqVO.MEDIOCRE_COMMENT)) {
queryWrapper.apply("scores >=3 and scores < 4");
queryWrapperX.ge(ProductCommentDO::getScores, 3);
queryWrapperX.lt(ProductCommentDO::getScores, 4);
}
// 构建差评查询语句差评计算 总评 < 3
if (ObjectUtil.equal(type, AppCommentPageReqVO.NEGATIVE_COMMENT)) {
queryWrapper.apply("scores < 3");
queryWrapperX.lt(ProductCommentDO::getScores, 3);
}
}

View File

@ -0,0 +1,10 @@
package cn.iocoder.yudao.module.promotion.api.combination;
/**
* 拼团活动 API 接口
*
* @author HUIHUI
*/
public interface CombinationActivityApi {
}

View File

@ -1,30 +1,30 @@
package cn.iocoder.yudao.module.promotion.api.combination;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
import javax.validation.Valid;
// TODO @puhui999:CombinationRecordApi 分成活动记录哈
// TODO @芋艿后面也再撸撸这几个接口
/**
* 拼团活动 API 接口
* 拼团记录 API 接口
*
* @author HUIHUI
*/
public interface CombinationApi {
public interface CombinationRecordApi {
/**
* 创建开团记录
*
* @param reqDTO 请求 DTO
*/
void createRecord(@Valid CombinationRecordReqDTO reqDTO);
void createRecord(@Valid CombinationRecordCreateReqDTO reqDTO);
/**
* 获取开团记录状态
* 校验拼团是否成功
*
* @param userId 用户编号
* @param orderId 订单编号
* @return 拼团是否成功
*/
boolean validateRecordStatusIsSuccess(Long userId, Long orderId);

View File

@ -5,14 +5,13 @@ import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
// TODO @puhui999CombinationRecordCreateReqDTO这样更容易知道是创建噢
/**
* 拼团记录 Request DTO
*
* @author HUIHUI
*/
@Data
public class CombinationRecordReqDTO {
public class CombinationRecordCreateReqDTO {
/**
* 拼团活动编号

View File

@ -65,5 +65,8 @@ public interface ErrorCodeConstants {
ErrorCode COMBINATION_ACTIVITY_STATUS_DISABLE = new ErrorCode(1013010002, "拼团活动已关闭不能修改");
ErrorCode COMBINATION_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED_OR_END = new ErrorCode(1013010003, "拼团活动未关闭或未结束,不能删除");
ErrorCode COMBINATION_RECORD_NOT_EXISTS = new ErrorCode(1013010004, "拼团不存在");
ErrorCode COMBINATION_RECORD_EXISTS = new ErrorCode(1013010005, "拼团失败,已参与过该拼团");
ErrorCode COMBINATION_RECORD_HEAD_NOT_EXISTS = new ErrorCode(1013010006, "拼团失败,父拼团不存在");
ErrorCode COMBINATION_RECORD_USER_FULL = new ErrorCode(1013010006, "拼团失败,拼团人数已满");
}

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.promotion.enums.combination;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@ -36,4 +37,8 @@ public enum CombinationRecordStatusEnum implements IntArrayValuable {
return ARRAYS;
}
public static boolean isSuccess(Integer status) {
return ObjectUtil.equal(status, SUCCESS.getStatus());
}
}

View File

@ -1,7 +1,8 @@
package cn.iocoder.yudao.module.promotion.api.combination;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@ -13,30 +14,29 @@ import java.time.LocalDateTime;
* @author HUIHUI
*/
@Service
public class CombinationApiImpl implements CombinationApi {
public class CombinationRecordApiImpl implements CombinationRecordApi {
@Resource
private CombinationActivityService activityService;
private CombinationRecordService recordService;
@Override
public void createRecord(CombinationRecordReqDTO reqDTO) {
activityService.createRecord(reqDTO);
public void createRecord(CombinationRecordCreateReqDTO reqDTO) {
recordService.createRecord(reqDTO);
}
@Override
public boolean validateRecordStatusIsSuccess(Long userId, Long orderId) {
return activityService.validateRecordStatusIsSuccess(userId, orderId);
return CombinationRecordStatusEnum.isSuccess(recordService.getRecord(userId, orderId).getStatus());
}
@Override
public void updateRecordStatus(Long userId, Long orderId, Integer status) {
activityService.updateRecordStatusByUserIdAndOrderId(userId, orderId, status);
recordService.updateRecordStatusByUserIdAndOrderId(userId, orderId, status);
}
@Override
public void updateRecordStatusAndStartTime(Long userId, Long orderId, Integer status) {
activityService.updateRecordStatusAndStartTimeByUserIdAndOrderId(userId, orderId, status, LocalDateTime.now());
recordService.updateRecordStatusAndStartTimeByUserIdAndOrderId(userId, orderId, status, LocalDateTime.now());
}
}

View File

@ -3,14 +3,14 @@ package cn.iocoder.yudao.module.promotion.controller.admin.combination;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.*;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
import cn.iocoder.yudao.module.promotion.service.combination.CombinationActivityService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@ -20,16 +20,12 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
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.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Tag(name = "管理后台 - 拼团活动")
@RestController
@ -82,7 +78,7 @@ public class CombinationActivityController {
@PreAuthorize("@ss.hasPermission('promotion:combination-activity:query')")
public CommonResult<List<CombinationActivityRespVO>> getCombinationActivityList(@RequestParam("ids") Collection<Long> ids) {
List<CombinationActivityDO> list = combinationActivityService.getCombinationActivityList(ids);
return success(CombinationActivityConvert.INSTANCE.convertList(list));
return success(CombinationActivityConvert.INSTANCE.complementList(list));
}
@GetMapping("/page")
@ -91,24 +87,9 @@ public class CombinationActivityController {
public CommonResult<PageResult<CombinationActivityRespVO>> getCombinationActivityPage(
@Valid CombinationActivityPageReqVO pageVO) {
PageResult<CombinationActivityDO> pageResult = combinationActivityService.getCombinationActivityPage(pageVO);
// TODO @puhui999可以不一定 aIds直接批量查询结果出来下面也是类似
Set<Long> aIds = CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getId);
List<CombinationProductDO> products = combinationActivityService.getProductsByActivityIds(aIds);
Set<Long> spuIds = CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getSpuId);
List<ProductSpuRespDTO> spus = spuApi.getSpuList(spuIds);
return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult, products, spus));
}
@GetMapping("/export-excel")
@Operation(summary = "导出拼团活动 Excel")
@PreAuthorize("@ss.hasPermission('promotion:combination-activity:export')")
@OperateLog(type = EXPORT)
public void exportCombinationActivityExcel(@Valid CombinationActivityExportReqVO exportReqVO,
HttpServletResponse response) throws IOException {
List<CombinationActivityDO> list = combinationActivityService.getCombinationActivityList(exportReqVO);
// 导出 Excel
List<CombinationActivityExcelVO> datas = CombinationActivityConvert.INSTANCE.convertList02(list);
ExcelUtils.write(response, "拼团活动.xls", "数据", CombinationActivityExcelVO.class, datas);
return success(CombinationActivityConvert.INSTANCE.convertPage(pageResult,
combinationActivityService.getProductsByActivityIds(CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getId)),
spuApi.getSpuList(CollectionUtils.convertSet(pageResult.getList(), CombinationActivityDO::getSpuId))));
}
}

View File

@ -1,65 +0,0 @@
package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.time.LocalDateTime;
// TODO @puhui999如无必要导出都可以删除哈
/**
* 拼团活动 Excel VO
*
* @author HUIHUI
*/
@Data
public class CombinationActivityExcelVO {
@ExcelProperty("活动编号")
private Long id;
@ExcelProperty("拼团名称")
private String name;
@ExcelProperty("商品 SPU 编号关联 ProductSpuDO 的 id")
private Long spuId;
@ExcelProperty("总限购数量")
private Integer totalLimitCount;
@ExcelProperty("单次限购数量")
private Integer singleLimitCount;
@ExcelProperty("开始时间")
private LocalDateTime startTime;
@ExcelProperty("结束时间")
private LocalDateTime endTime;
@ExcelProperty("开团人数")
private Integer userSize;
@ExcelProperty("开团组数")
private Integer totalNum;
@ExcelProperty("成团组数")
private Integer successNum;
@ExcelProperty("参与人数")
private Integer orderUserCount;
@ExcelProperty("虚拟成团")
private Integer virtualGroup;
@ExcelProperty(value = "活动状态0开启 1关闭", converter = DictConvert.class)
@DictFormat("common_status") // TODO 代码优化建议设置到对应的 XXXDictTypeConstants 枚举类中
private Integer status;
@ExcelProperty("限制时长(小时)")
private Integer limitDuration;
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -1,61 +0,0 @@
package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
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 是一致的")
@Data
public class CombinationActivityExportReqVO {
@Schema(description = "拼团名称", example = "赵六")
private String name;
@Schema(description = "商品 SPU 编号关联 ProductSpuDO 的 id", example = "14016")
private Long spuId;
@Schema(description = "总限购数量", example = "16218")
private Integer totalLimitCount;
@Schema(description = "单次限购数量", example = "28265")
private Integer singleLimitCount;
@Schema(description = "开始时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] startTime;
@Schema(description = "结束时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] endTime;
@Schema(description = "开团人数")
private Integer userSize;
@Schema(description = "开团组数")
private Integer totalNum;
@Schema(description = "成团组数")
private Integer successNum;
@Schema(description = "参与人数", example = "25222")
private Integer orderUserCount;
@Schema(description = "虚拟成团")
private Integer virtualGroup;
@Schema(description = "活动状态0开启 1关闭", example = "0")
private Integer status;
@Schema(description = "限制时长(小时)")
private Integer limitDuration;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -1,44 +0,0 @@
package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.time.LocalDateTime;
// TODO @puhui999可以考虑删除 excel 导出哈
/**
* 拼团商品 Excel VO
*
* @author HUIHUI
*/
@Data
public class CombinationProductExcelVO {
@ExcelProperty("编号")
private Long id;
@ExcelProperty("拼团活动编号")
private Long activityId;
@ExcelProperty("商品 SPU 编号")
private Long spuId;
@ExcelProperty("商品 SKU 编号")
private Long skuId;
@ExcelProperty("拼团商品状态")
private Integer activityStatus;
@ExcelProperty("活动开始时间点")
private LocalDateTime activityStartTime;
@ExcelProperty("活动结束时间点")
private LocalDateTime activityEndTime;
@ExcelProperty("拼团价格,单位分")
private Integer activePrice;
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -1,43 +0,0 @@
package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
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 是一致的")
@Data
public class CombinationProductExportReqVO {
@Schema(description = "拼团活动编号", example = "6829")
private Long activityId;
@Schema(description = "商品 SPU 编号", example = "18731")
private Long spuId;
@Schema(description = "商品 SKU 编号", example = "31675")
private Long skuId;
@Schema(description = "拼团商品状态", example = "2")
private Integer activityStatus;
@Schema(description = "活动开始时间点")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] activityStartTime;
@Schema(description = "活动结束时间点")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] activityEndTime;
@Schema(description = "拼团价格,单位分", example = "27682")
private Integer activePrice;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -85,7 +85,7 @@ public class SeckillActivityController {
@PreAuthorize("@ss.hasPermission('promotion:seckill-activity:query')")
public CommonResult<List<SeckillActivityRespVO>> getSeckillActivityList(@RequestParam("ids") Collection<Long> ids) {
List<SeckillActivityDO> list = seckillActivityService.getSeckillActivityList(ids);
return success(SeckillActivityConvert.INSTANCE.convertList(list));
return success(SeckillActivityConvert.INSTANCE.complementList(list));
}
@GetMapping("/page")

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.promotion.controller.admin.seckill;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.config.*;
@ -80,7 +81,7 @@ public class SeckillConfigController {
@GetMapping("/list-all-simple")
@Operation(summary = "获得所有开启状态的秒杀时段精简列表", description = "主要用于前端的下拉选项")
public CommonResult<List<SeckillConfigSimpleRespVO>> getListAllSimple() {
List<SeckillConfigDO> list = seckillConfigService.getListAllSimple();
List<SeckillConfigDO> list = seckillConfigService.getSeckillConfigListByStatus(CommonStatusEnum.ENABLE.getStatus());
return success(SeckillConfigConvert.INSTANCE.convertList1(list));
}

View File

@ -1,19 +1,18 @@
package cn.iocoder.yudao.module.promotion.convert.combination;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
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.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExcelVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductBaseVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductRespVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductUpdateReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationRecordDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
@ -71,20 +70,18 @@ public interface CombinationActivityConvert {
return respVO;
}
List<CombinationActivityRespVO> convertList(List<CombinationActivityDO> list);
List<CombinationActivityRespVO> complementList(List<CombinationActivityDO> list);
PageResult<CombinationActivityRespVO> convertPage(PageResult<CombinationActivityDO> page);
default PageResult<CombinationActivityRespVO> convertPage(PageResult<CombinationActivityDO> page,
List<CombinationProductDO> productList,
List<ProductSpuRespDTO> spuList) {
// TODO @puhui999c -> c 可以去掉哈
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId, c -> c);
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
PageResult<CombinationActivityRespVO> pageResult = convertPage(page);
pageResult.getList().forEach(item -> {
// TODO @puhui999最好 MapUtils.findAndThen万一没找到呢啊哈哈
item.setSpuName(spuMap.get(item.getSpuId()).getName());
item.setPicUrl(spuMap.get(item.getSpuId()).getPicUrl());
MapUtils.findAndThen(spuMap, item.getSpuId(), spu -> item.setSpuName(spu.getName()));
MapUtils.findAndThen(spuMap, item.getSpuId(), spu -> item.setPicUrl(spu.getPicUrl()));
item.setProducts(convertList2(productList));
});
return pageResult;
@ -92,8 +89,6 @@ public interface CombinationActivityConvert {
List<CombinationProductRespVO> convertList2(List<CombinationProductDO> productDOs);
List<CombinationActivityExcelVO> convertList02(List<CombinationActivityDO> list);
@Mappings({
@Mapping(target = "id", ignore = true),
@Mapping(target = "activityId", source = "activityDO.id"),
@ -105,34 +100,28 @@ public interface CombinationActivityConvert {
})
CombinationProductDO convert(CombinationActivityDO activityDO, CombinationProductBaseVO vo);
default List<CombinationProductDO> convertList(CombinationActivityDO activityDO, List<? extends CombinationProductBaseVO> products) {
default List<CombinationProductDO> complementList(List<? extends CombinationProductBaseVO> products, CombinationActivityDO activityDO) {
List<CombinationProductDO> list = new ArrayList<>();
products.forEach(sku -> {
CombinationProductDO productDO = convert(activityDO, sku);
// TODO 状态设置
productDO.setActivityStatus(CommonStatusEnum.ENABLE.getStatus());
productDO.setActivityStatus(activityDO.getStatus());
list.add(productDO);
});
return list;
}
// TODO @puhui999这个方法的参数调整成 productDOsvosactivityDO因为 productDOs 是主角
// 然后这个方法感觉不是为了 convert而是为了补全
default List<CombinationProductDO> convertList1(CombinationActivityDO activityDO,
List<CombinationProductUpdateReqVO> vos,
List<CombinationProductDO> productDOs) {
default List<CombinationProductDO> complementList(List<CombinationProductDO> productDOs, List<CombinationProductUpdateReqVO> vos, CombinationActivityDO activityDO) {
Map<Long, Long> longMap = convertMap(productDOs, CombinationProductDO::getSkuId, CombinationProductDO::getId);
List<CombinationProductDO> list = new ArrayList<>();
vos.forEach(sku -> {
CombinationProductDO productDO = convert(activityDO, sku);
productDO.setId(longMap.get(sku.getSkuId()));
// TODO @puhui999是是不是用 activityDO 的状态
productDO.setActivityStatus(CommonStatusEnum.ENABLE.getStatus());
productDO.setActivityStatus(activityDO.getStatus());
list.add(productDO);
});
return list;
}
CombinationRecordDO convert(CombinationRecordReqDTO reqDTO);
CombinationRecordDO convert(CombinationRecordCreateReqDTO reqDTO);
}

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.promotion.convert.seckill.seckillactivity;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
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;
@ -38,7 +37,7 @@ public interface SeckillActivityConvert {
SeckillActivityRespVO convert(SeckillActivityDO bean);
List<SeckillActivityRespVO> convertList(List<SeckillActivityDO> list);
List<SeckillActivityRespVO> complementList(List<SeckillActivityDO> list);
PageResult<SeckillActivityRespVO> convertPage(PageResult<SeckillActivityDO> page);
@ -74,24 +73,23 @@ public interface SeckillActivityConvert {
})
SeckillProductDO convert(SeckillActivityDO activityDO, SeckillProductBaseVO vo);
default List<SeckillProductDO> convertList(SeckillActivityDO activityDO, List<? extends SeckillProductBaseVO> products) {
default List<SeckillProductDO> complementList(List<? extends SeckillProductBaseVO> products, SeckillActivityDO activityDO) {
List<SeckillProductDO> list = new ArrayList<>();
products.forEach(sku -> {
SeckillProductDO productDO = convert(activityDO, sku);
productDO.setActivityStatus(CommonStatusEnum.ENABLE.getStatus());
productDO.setActivityStatus(activityDO.getStatus());
list.add(productDO);
});
return list;
}
// TODO @puhui999同拼团那个 convert 想通的情况哈
default List<SeckillProductDO> convertList1(SeckillActivityDO activityDO, List<SeckillProductUpdateReqVO> vos, List<SeckillProductDO> productDOs) {
default List<SeckillProductDO> complementList(List<SeckillProductDO> productDOs, List<SeckillProductUpdateReqVO> vos, SeckillActivityDO activityDO) {
Map<Long, Long> longMap = CollectionUtils.convertMap(productDOs, SeckillProductDO::getSkuId, SeckillProductDO::getId);
List<SeckillProductDO> list = new ArrayList<>();
vos.forEach(sku -> {
SeckillProductDO productDO = convert(activityDO, sku);
productDO.setId(longMap.get(sku.getSkuId()));
productDO.setActivityStatus(CommonStatusEnum.ENABLE.getStatus());
productDO.setActivityStatus(activityDO.getStatus());
list.add(productDO);
});
return list;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity;
package cn.iocoder.yudao.module.promotion.dal.dataobject.combination;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@ -9,7 +9,6 @@ import lombok.*;
import java.time.LocalDateTime;
// TODO @puhui999是不是应该在 combination
/**
* 拼团活动 DO
*

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity;
package cn.iocoder.yudao.module.promotion.dal.dataobject.combination;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
@ -42,6 +42,8 @@ public class CombinationProductDO extends BaseDO {
private Long skuId;
/**
* 拼团商品状态
*
* 关联 {@link CombinationActivityDO#getStatus()}
*/
private Integer activityStatus;
/**

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity;
package cn.iocoder.yudao.module.promotion.dal.dataobject.combination;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;

View File

@ -1,16 +1,14 @@
package cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity;
package cn.iocoder.yudao.module.promotion.dal.mysql.combination;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExportReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
// TODO @puhui999是不是应该在 combination
/**
* 拼团活动 Mapper
*
@ -25,13 +23,6 @@ public interface CombinationActivityMapper extends BaseMapperX<CombinationActivi
.orderByDesc(CombinationActivityDO::getId));
}
default List<CombinationActivityDO> selectList(CombinationActivityExportReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<CombinationActivityDO>()
.likeIfPresent(CombinationActivityDO::getName, reqVO.getName())
.eqIfPresent(CombinationActivityDO::getStatus, reqVO.getStatus())
.orderByDesc(CombinationActivityDO::getId));
}
default List<CombinationActivityDO> selectListByStatus(Integer status) {
return selectList(CombinationActivityDO::getStatus, status);
}

View File

@ -1,11 +1,10 @@
package cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity;
package cn.iocoder.yudao.module.promotion.dal.mysql.combination;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductExportReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductPageReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
@ -32,19 +31,6 @@ public interface CombinationProductMapper extends BaseMapperX<CombinationProduct
.orderByDesc(CombinationProductDO::getId));
}
default List<CombinationProductDO> selectList(CombinationProductExportReqVO reqVO) {
return selectList(new LambdaQueryWrapperX<CombinationProductDO>()
.eqIfPresent(CombinationProductDO::getActivityId, reqVO.getActivityId())
.eqIfPresent(CombinationProductDO::getSpuId, reqVO.getSpuId())
.eqIfPresent(CombinationProductDO::getSkuId, reqVO.getSkuId())
.eqIfPresent(CombinationProductDO::getActivityStatus, reqVO.getActivityStatus())
.betweenIfPresent(CombinationProductDO::getActivityStartTime, reqVO.getActivityStartTime())
.betweenIfPresent(CombinationProductDO::getActivityEndTime, reqVO.getActivityEndTime())
.eqIfPresent(CombinationProductDO::getActivePrice, reqVO.getActivePrice())
.betweenIfPresent(CombinationProductDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(CombinationProductDO::getId));
}
default List<CombinationProductDO> selectListByActivityIds(Collection<Long> ids) {
return selectList(CombinationProductDO::getActivityId, ids);
}

View File

@ -1,8 +1,8 @@
package cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity;
package cn.iocoder.yudao.module.promotion.dal.mysql.combination;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationRecordDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@ -20,6 +20,18 @@ public interface CombinationRecordMapper extends BaseMapperX<CombinationRecordDO
CombinationRecordDO::getOrderId, orderId);
}
/**
* 查询拼团记录
*
* @param headId 团长编号
* @param activityId 活动编号
* @return 拼团记录
*/
default CombinationRecordDO selectRecordByHeadId(Long headId, Long activityId, Integer status) {
return selectOne(CombinationRecordDO::getUserId, headId, CombinationRecordDO::getActivityId, activityId,
CombinationRecordDO::getStatus, status);
}
default List<CombinationRecordDO> selectListByHeadIdAndStatus(Long headId, Integer status) {
return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()
.eq(CombinationRecordDO::getHeadId, headId)

View File

@ -1,16 +1,13 @@
package cn.iocoder.yudao.module.promotion.service.combination;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
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.CombinationActivityExportReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
@ -67,14 +64,6 @@ public interface CombinationActivityService {
*/
PageResult<CombinationActivityDO> getCombinationActivityPage(CombinationActivityPageReqVO pageReqVO);
/**
* 获得拼团活动列表, 用于 Excel 导出
*
* @param exportReqVO 查询条件
* @return 拼团活动列表
*/
List<CombinationActivityDO> getCombinationActivityList(CombinationActivityExportReqVO exportReqVO);
/**
* 获得拼团活动商品列表
*
@ -83,41 +72,4 @@ public interface CombinationActivityService {
*/
List<CombinationProductDO> getProductsByActivityIds(Collection<Long> ids);
// TODO @puhui999拆一个 CombinationRecordService 方法名可以简洁成 updateRecordStatusByOrderIdservice 方法可以稍微简单一点如果是 update 方法
/**
* 更新拼团状态
*
* @param userId 用户编号
* @param orderId 订单编号
* @param status 状态
*/
void updateRecordStatusByUserIdAndOrderId(Long userId, Long orderId, Integer status);
/**
* 更新拼团状态和开始时间
*
* @param userId 用户编号
* @param orderId 订单编号
* @param status 状态
* @param startTime 开始时间
*/
void updateRecordStatusAndStartTimeByUserIdAndOrderId(Long userId, Long orderId, Integer status, LocalDateTime startTime);
// TODO @puhui999拆一个 CombinationRecordService
/**
* 创建拼团记录
*
* @param reqDTO 创建信息
*/
void createRecord(CombinationRecordReqDTO reqDTO);
/**
* 获得拼团状态
*
* @param userId 用户编号
* @param orderId 订单编号
* @return 拼团状态
*/
boolean validateRecordStatusIsSuccess(Long userId, Long orderId);
}

View File

@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.promotion.service.combination;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import java.time.LocalDateTime;
/**
* 商品活动记录 service
*
* @author HUIHUI
*/
public interface CombinationRecordService {
/**
* 更新拼团状态
*
* @param userId 用户编号
* @param orderId 订单编号
* @param status 状态
*/
void updateRecordStatusByUserIdAndOrderId(Long userId, Long orderId, Integer status);
/**
* 创建拼团记录
*
* @param reqDTO 创建信息
*/
void createRecord(CombinationRecordCreateReqDTO reqDTO);
/**
* 更新拼团状态和开始时间
*
* @param userId 用户编号
* @param orderId 订单编号
* @param status 状态
* @param startTime 开始时间
*/
void updateRecordStatusAndStartTimeByUserIdAndOrderId(Long userId, Long orderId, Integer status, LocalDateTime startTime);
/**
* 获得拼团状态
*
* @param userId 用户编号
* @param orderId 订单编号
* @return 拼团状态
*/
CombinationRecordDO getRecord(Long userId, Long orderId);
}

View File

@ -12,20 +12,19 @@ import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
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.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExportReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.product.CombinationProductUpdateReqVO;
import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationProductDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationRecordDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationActivityMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationProductMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationRecordMapper;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationActivityMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationProductMapper;
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -47,7 +46,7 @@ import static cn.iocoder.yudao.module.promotion.util.PromotionUtils.validateProd
*/
@Service
@Validated
public class CombinationActivityServiceImpl implements CombinationActivityService {
public class CombinationServiceImpl implements CombinationActivityService, CombinationRecordService {
@Resource
private CombinationActivityMapper combinationActivityMapper;
@ -81,7 +80,7 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
activityDO.setStatus(CommonStatusEnum.ENABLE.getStatus());
combinationActivityMapper.insert(activityDO);
// 插入商品
List<CombinationProductDO> productDOs = CombinationActivityConvert.INSTANCE.convertList(activityDO, createReqVO.getProducts());
List<CombinationProductDO> productDOs = CombinationActivityConvert.INSTANCE.complementList(createReqVO.getProducts(), activityDO);
combinationProductMapper.insertBatch(productDOs);
// 返回
return activityDO.getId();
@ -144,15 +143,14 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
Map<String, List<CombinationProductDO>> data = CollectionUtils.convertCDUMap(convertSet1, convertSet, mapData -> {
HashMap<String, List<CombinationProductDO>> cdu = MapUtil.newHashMap(3);
MapUtils.findAndThen(mapData, "create", list -> {
cdu.put("create", CombinationActivityConvert.INSTANCE.convertList(updateObj,
CollectionUtils.filterList(products, item -> list.contains(item.getSkuId()))));
cdu.put("create", CombinationActivityConvert.INSTANCE.complementList(CollectionUtils.filterList(products, item -> list.contains(item.getSkuId())), updateObj));
});
MapUtils.findAndThen(mapData, "delete", list -> {
cdu.put("create", CollectionUtils.filterList(combinationProductDOs, item -> list.contains(item.getSkuId())));
});
MapUtils.findAndThen(mapData, "update", list -> {
cdu.put("update", CombinationActivityConvert.INSTANCE.convertList1(updateObj,
CollectionUtils.filterList(products, item -> list.contains(item.getSkuId())), combinationProductDOs));
cdu.put("update", CombinationActivityConvert.INSTANCE.complementList(combinationProductDOs,
CollectionUtils.filterList(products, item -> list.contains(item.getSkuId())), updateObj));
});
return cdu;
});
@ -200,11 +198,6 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
return combinationActivityMapper.selectPage(pageReqVO);
}
@Override
public List<CombinationActivityDO> getCombinationActivityList(CombinationActivityExportReqVO exportReqVO) {
return combinationActivityMapper.selectList(exportReqVO);
}
@Override
public List<CombinationProductDO> getProductsByActivityIds(Collection<Long> ids) {
return combinationProductMapper.selectListByActivityIds(ids);
@ -236,7 +229,7 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
recordDOs.forEach(item -> {
item.setUserCount(recordDOs.size());
// 校验拼团是否满足要求
if (recordDOs.size() >= recordDO.getUserSize()) {
if (ObjectUtil.equal(recordDOs.size(), recordDO.getUserSize())) {
item.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus());
}
});
@ -254,13 +247,30 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
}
@Override
public void createRecord(CombinationRecordReqDTO reqDTO) {
public void createRecord(CombinationRecordCreateReqDTO reqDTO) {
// 校验拼团活动
CombinationActivityDO activity = validateCombinationActivityExists(reqDTO.getActivityId());
// TODO @puhui999需要校验下它当前是不是已经参加了该拼团
// TODO @puhui999: 父拼团是否存在,是否已经满了
// 需要校验下它当前是不是已经参加了该拼团
CombinationRecordDO recordDO = recordMapper.selectRecord(reqDTO.getUserId(), reqDTO.getOrderId());
if (recordDO != null) {
throw exception(COMBINATION_RECORD_EXISTS);
}
// 父拼团是否存在,是否已经满了
if (reqDTO.getHeadId() != null) {
CombinationRecordDO recordDO1 = recordMapper.selectRecordByHeadId(reqDTO.getHeadId(), reqDTO.getActivityId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
if (recordDO1 == null) {
throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS);
}
// 校验拼团是否满足要求
if (ObjectUtil.equal(recordDO1.getUserCount(), recordDO1.getUserSize())) {
throw exception(COMBINATION_RECORD_USER_FULL);
}
}
CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO);
if (reqDTO.getHeadId() == null) {
record.setHeadId(reqDTO.getUserId());
}
record.setVirtualGroup(false);
// TODO @puhui999过期时间应该是 Date
record.setExpireTime(activity.getLimitDuration());
@ -269,21 +279,17 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
}
@Override
public boolean validateRecordStatusIsSuccess(Long userId, Long orderId) {
CombinationRecordDO record = validateCombinationRecord(userId, orderId);
// TODO @puhui999可以搞个 getRecrod 方法然后业务通过 CombinationRecordStatusEnum.isSuccess 方法校验
return ObjectUtil.equal(record.getStatus(), CombinationRecordStatusEnum.SUCCESS.getStatus());
public CombinationRecordDO getRecord(Long userId, Long orderId) {
return validateCombinationRecord(userId, orderId);
}
// TODO @puhui999status 传入进来搞哈
/**
* APP 端获取开团记录
*
* @return 开团记录
*/
public List<CombinationRecordDO> getRecordList() {
return recordMapper.selectListByStatus(CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
public List<CombinationRecordDO> getRecordListByStatus(Integer status) {
return recordMapper.selectListByStatus(status);
}
}

View File

@ -1,10 +1,12 @@
package cn.iocoder.yudao.module.promotion.service.seckill.seckillactivity;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
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.MapUtils;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
@ -26,11 +28,10 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.promotion.util.PromotionUtils.validateProductSkuExistence;
@ -63,8 +64,9 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
// 获取所选 spu 下的所有 sku
List<ProductSkuRespDTO> skus = productSkuApi.getSkuListBySpuId(CollUtil.newArrayList(createReqVO.getSpuId()));
// 校验商品 sku 是否存在
// TODO @puhui999直接校验 sku 数量是不是就完事啦不需要校验的特别严谨哈
validateProductSkuExistence(createReqVO.getProducts(), skus, SeckillProductCreateReqVO::getSkuId);
if (skus.size() != createReqVO.getProducts().size()) {
throw exception(SKU_NOT_EXISTS);
}
// 插入秒杀活动
SeckillActivityDO activity = SeckillActivityConvert.INSTANCE.convert(createReqVO)
@ -72,9 +74,8 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
.setTotalStock(CollectionUtils.getSumValue(createReqVO.getProducts(), SeckillProductCreateReqVO::getStock, Integer::sum));
seckillActivityMapper.insert(activity);
// 插入商品
// TODO @puhui999products 要注意复数哈
List<SeckillProductDO> product = SeckillActivityConvert.INSTANCE.convertList(activity, createReqVO.getProducts());
seckillProductMapper.insertBatch(product);
List<SeckillProductDO> products = SeckillActivityConvert.INSTANCE.complementList(createReqVO.getProducts(), activity);
seckillProductMapper.insertBatch(products);
return activity.getId();
}
@ -99,11 +100,9 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
}
List<SeckillActivityDO> activityDOs2 = CollectionUtils.convertList(activityDOs, c -> c, s -> {
// 判断秒杀时段是否有交集
// TODO @puhui999是不是 containsAny 就是有交集呀
List<Long> configIdsClone = CollUtil.newArrayList(s.getConfigIds());
configIdsClone.retainAll(configIds);
return CollUtil.isNotEmpty(configIdsClone);
return CollectionUtils.containsAny(s.getConfigIds(), configIds);
});
if (CollUtil.isNotEmpty(activityDOs2)) {
throw exception(SECKILL_TIME_CONFLICTS);
}
@ -147,31 +146,31 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
// 数据库中的活动商品
List<SeckillProductDO> seckillProductDOs = seckillProductMapper.selectListByActivityId(updateObj.getId());
Set<Long> dbSkuIds = CollectionUtils.convertSet(seckillProductDOs, SeckillProductDO::getSkuId);
// 1. 删除后台存在的前端不存在的商品
// TODO @puhui999delete 应该是 id不是 skuId
Set<Long> voSkuIds = CollectionUtils.convertSet(products, SeckillProductUpdateReqVO::getSkuId);
List<Long> d = CollectionUtils.filterList(dbSkuIds, item -> !voSkuIds.contains(item));
if (CollUtil.isNotEmpty(d)) {
seckillProductMapper.deleteBatchIds(d);
}
// 2. 前端存在的后端不存在的商品
List<Long> c = CollectionUtils.filterList(voSkuIds, item -> !dbSkuIds.contains(item));
if (CollUtil.isNotEmpty(c)) {
List<SeckillProductUpdateReqVO> vos = CollectionUtils.filterList(products, item -> c.contains(item.getSkuId()));
List<SeckillProductDO> productDOs = SeckillActivityConvert.INSTANCE.convertList(updateObj, vos);
seckillProductMapper.insertBatch(productDOs);
}
// 3. 更新已存在的商品
List<Long> u = CollectionUtils.filterList(voSkuIds, dbSkuIds::contains);
if (CollUtil.isNotEmpty(u)) {
List<SeckillProductUpdateReqVO> vos = CollectionUtils.filterList(products, item -> u.contains(item.getSkuId()));
List<SeckillProductDO> productDOs = SeckillActivityConvert.INSTANCE.convertList1(updateObj, vos, seckillProductDOs);
seckillProductMapper.updateBatch(productDOs);
}
Map<String, List<SeckillProductDO>> data = CollectionUtils.convertCDUMap(voSkuIds, dbSkuIds, mapData -> {
HashMap<String, List<SeckillProductDO>> cdu = MapUtil.newHashMap(3);
MapUtils.findAndThen(mapData, "create", list -> {
cdu.put("create", SeckillActivityConvert.INSTANCE.complementList(
CollectionUtils.filterList(products, item -> list.contains(item.getSkuId())), updateObj));
});
MapUtils.findAndThen(mapData, "delete", list -> {
cdu.put("create", CollectionUtils.filterList(seckillProductDOs, item -> list.contains(item.getSkuId())));
});
MapUtils.findAndThen(mapData, "update", list -> {
cdu.put("update", SeckillActivityConvert.INSTANCE.complementList(seckillProductDOs,
CollectionUtils.filterList(products, item -> list.contains(item.getSkuId())), updateObj));
});
return cdu;
});
// 执行增删改
MapUtils.findAndThen(data, "create", item -> seckillProductMapper.insertBatch(item));
MapUtils.findAndThen(data, "delete", item -> seckillProductMapper.deleteBatchIds(
CollectionUtils.convertSet(item, SeckillProductDO::getId)));
MapUtils.findAndThen(data, "update", item -> seckillProductMapper.updateBatch(item));
}
@Override
@Transactional(rollbackFor = Exception.class) // TODO @puhui999这个不用加事务哈
public void closeSeckillActivity(Long id) {
// TODO 待验证没使用过
// 校验存在

View File

@ -73,9 +73,10 @@ public interface SeckillConfigService {
/**
* 获得所有正常状态的时段配置列表
*
* @param status 状态
* @return 秒杀时段列表
*/
List<SeckillConfigDO> getListAllSimple();
List<SeckillConfigDO> getSeckillConfigListByStatus(Integer status);
/**
* 更新秒杀时段配置状态

View File

@ -62,7 +62,7 @@ public class SeckillConfigServiceImpl implements SeckillConfigService {
seckillConfigMapper.updateById(updateObj);
}
// TODO @puhui999: 这个要不合并到更新操作里? 不单独有个操作咧; 更新状态不用那么多必须的参数更新的时候需要校验时间段
// TODO @puhui999: 这个要不合并到更新操作里? 不单独有个操作咧; fix: 更新状态不用那么多必须的参数更新的时候需要校验时间段
@Override
public void updateSeckillConfigStatus(Long id, Integer status) {
// 校验秒杀时段是否存在
@ -108,7 +108,7 @@ public class SeckillConfigServiceImpl implements SeckillConfigService {
LocalTime startTime2 = LocalTime.parse(config.getStartTime());
LocalTime endTime2 = LocalTime.parse(config.getEndTime());
// 判断时间是否重叠
return LocalDateTimeUtils.checkTimeOverlap(startTime1, endTime1, startTime2, endTime2);
return LocalDateTimeUtils.isOverlap(startTime1, endTime1, startTime2, endTime2);
});
if (hasConflict) {
@ -151,10 +151,9 @@ public class SeckillConfigServiceImpl implements SeckillConfigService {
return seckillConfigMapper.selectPage(pageVO);
}
// TODO @puhui999改成传入 enable 状态哈一个通用的 getSeckillConfigList 方法
@Override
public List<SeckillConfigDO> getListAllSimple() {
return seckillConfigMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());
public List<SeckillConfigDO> getSeckillConfigListByStatus(Integer status) {
return seckillConfigMapper.selectListByStatus(status);
}
}

View File

@ -3,17 +3,15 @@ package cn.iocoder.yudao.module.promotion.service.combination;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityCreateReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityExportReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityUpdateReqVO;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.combinationactivity.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.combinationactivity.CombinationActivityMapper;
import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationActivityMapper;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
@ -26,15 +24,15 @@ import static org.junit.jupiter.api.Assertions.*;
// TODO 芋艿等完成后在补全单测
/**
* {@link CombinationActivityServiceImpl} 的单元测试类
* {@link CombinationServiceImpl} 的单元测试类
*
* @author HUIHUI
*/
@Import(CombinationActivityServiceImpl.class)
@Import(CombinationServiceImpl.class)
public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
@Resource
private CombinationActivityServiceImpl combinationActivityService;
private CombinationServiceImpl combinationActivityService;
@Resource
private CombinationActivityMapper combinationActivityMapper;
@ -225,28 +223,7 @@ public class CombinationActivityServiceImplTest extends BaseDbUnitTest {
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setLimitDuration(null)));
// 测试 createTime 不匹配
combinationActivityMapper.insert(cloneIgnoreId(dbCombinationActivity, o -> o.setCreateTime(null)));
// 准备参数
CombinationActivityExportReqVO reqVO = new CombinationActivityExportReqVO();
reqVO.setName(null);
reqVO.setSpuId(null);
reqVO.setTotalLimitCount(null);
reqVO.setSingleLimitCount(null);
reqVO.setStartTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
reqVO.setEndTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
reqVO.setUserSize(null);
reqVO.setTotalNum(null);
reqVO.setSuccessNum(null);
reqVO.setOrderUserCount(null);
reqVO.setVirtualGroup(null);
reqVO.setStatus(null);
reqVO.setLimitDuration(null);
reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
// 调用
List<CombinationActivityDO> list = combinationActivityService.getCombinationActivityList(reqVO);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbCombinationActivity, list.get(0));
}
}

View File

@ -31,6 +31,10 @@ public interface ErrorCodeConstants {
ErrorCode ORDER_COMMENT_STATUS_NOT_FALSE = new ErrorCode(1011000020, "创建交易订单项的评价失败,订单已评价");
ErrorCode ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE = new ErrorCode(1011000021, "交易订单发货失败,订单已退款或部分退款");
ErrorCode ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS = new ErrorCode(1011000022, "交易订单发货失败,拼团未成功");
// TODO 已移除订单单独发货
ErrorCode ORDER_DELIVERY_FAILED_ITEMS_NOT_EMPTY = new ErrorCode(1011000023, "订单发货失败,请选择发货商品");
ErrorCode ORDER_DELIVERY_FAILED_ITEM_NOT_EXISTS = new ErrorCode(1011000024, "订单发货失败,所选发货商品不存在");
ErrorCode ORDER_DELIVERY_FAILED_ITEM_ALREADY_DELIVERY = new ErrorCode(1011000025, "订单发货失败,所选商品已发货");
// ========== After Sale 模块 1011000100 ==========
ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1011000100, "售后单不存在");
@ -69,10 +73,4 @@ public interface ErrorCodeConstants {
// ========== 物流 PICK_UP 模块 1011006000 ==========
ErrorCode PICK_UP_STORE_NOT_EXISTS = new ErrorCode(1011006000, "自提门店不存在");
// ========== 物流 PICK_UP 模块 1011007000 ==========
// TODO @puhui999这几个错误码应该属于订单哈
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_ALREADY_DELIVERY = new ErrorCode(1011007002, "订单发货失败,所选商品已发货");
}

View File

@ -5,8 +5,8 @@ import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
@Schema(description = "管理后台 - 订单发货 Request VO")
@Data
@ -21,20 +21,14 @@ public class TradeOrderDeliveryReqVO {
@NotNull(message = "发货类型不能为空")
private Integer type;
// TODO @puhui999还是要校验下
@Schema(description = "发货物流公司编号", example = "1")
@NotNull(message = "发货物流公司不能为空")
private Long logisticsId;
@Schema(description = "发货物流单号", example = "SF123456789")
@NotEmpty(message = "发货物流单号不能为空")
private String logisticsNo;
// TODO 订单项商品单独发货不做单独发
@Schema(description = "发货订单项", example = "[1,2,3]")
@NotNull(message = "发货订单项不能为空")
private List<Long> orderItemIds;
// =============== 同城配送 ================
// TODO

View File

@ -13,12 +13,11 @@ import cn.iocoder.yudao.module.pay.enums.DictTypeConstants;
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordReqDTO;
import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
import cn.iocoder.yudao.module.promotion.api.price.dto.PriceCalculateReqDTO;
import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
import cn.iocoder.yudao.module.trade.controller.admin.base.member.user.MemberUserRespVO;
import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.ProductPropertyValueDetailRespVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDetailRespVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageItemRespVO;
import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
@ -27,7 +26,6 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderI
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDeliveryDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
@ -334,17 +332,6 @@ public interface TradeOrderConvert {
@Mapping(target = "avatar", source = "user.avatar"),
@Mapping(target = "status", ignore = true)
})
CombinationRecordReqDTO convert(TradeOrderDO order, TradeOrderItemDO orderItem, AppTradeOrderCreateReqVO createReqVO, MemberUserRespDTO user);
TradeOrderDeliveryDO covert(Long orderId, Long orderItemId, Long userId, Integer deliveryType, Long logisticsId, String logisticsNo);
default List<TradeOrderDeliveryDO> covert(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) {
ArrayList<TradeOrderDeliveryDO> arrayList = new ArrayList<>();
deliveryReqVO.getOrderItemIds().forEach(item -> {
arrayList.add(covert(order.getId(), item, order.getUserId(), deliveryReqVO.getType(),
deliveryReqVO.getLogisticsId(), deliveryReqVO.getLogisticsNo()));
});
return arrayList;
}
CombinationRecordCreateReqDTO convert(TradeOrderDO order, TradeOrderItemDO orderItem, AppTradeOrderCreateReqVO createReqVO, MemberUserRespDTO user);
}

View File

@ -1,60 +0,0 @@
package cn.iocoder.yudao.module.trade.dal.dataobject.order;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
// TODO @puhui999:
/**
* 交易订单发货记录 DO
*
* @author HUIHUI
*/
@TableName("trade_order_delivery")
@KeySequence("trade_order_delivery_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TradeOrderDeliveryDO extends BaseDO {
/**
* 订单发货记录 id
*/
private Long id;
/**
* 订单 id
*/
private Long orderId;
/**
* 订单项 id TODO 要不要一个发货记录可对应多个订单项
*/
private Long orderItemId;
/**
* 用户编号
*
* 关联 MemberUserDO id 编号
*/
private Long userId;
/**
* 配送方式
*
* 枚举 {@link DeliveryTypeEnum}
*/
private Integer deliveryType;
/**
* 发货物流公司编号
*/
private Long logisticsId;
/**
* 发货物流单号
*/
private String logisticsNo;
// TODO 同城配送
}

View File

@ -1,24 +0,0 @@
package cn.iocoder.yudao.module.trade.dal.mysql.order;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDeliveryDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
// TODO @puhui999应该去掉啦
/**
* 交易订单发货记录 Mapper
*
* @author HUIHUI
*/
@Mapper
public interface TradeOrderDeliveryMapper extends BaseMapperX<TradeOrderDeliveryDO> {
default List<TradeOrderDeliveryDO> selsectListByOrderIdAndItemIds(Long orderId, List<Long> orderItemIds) {
return selectList(new LambdaQueryWrapperX<TradeOrderDeliveryDO>()
.eq(TradeOrderDeliveryDO::getOrderId, orderId).in(TradeOrderDeliveryDO::getOrderItemId, orderItemIds));
}
}

View File

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.trade.service.message;
import cn.iocoder.yudao.module.trade.service.message.dto.TradeOrderMessageWhenDeliveryOrderReqDTO;
/**
* Trade 消息 service 接口
*
* @author HUIHUI
*/
public interface TradeMessageService {
/**
* 订单发货时发送消息
*
* @param reqDTO 发送消息
*/
void sendMessageWhenDeliveryOrder(TradeOrderMessageWhenDeliveryOrderReqDTO reqDTO);
}

View File

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.trade.service.message;
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import cn.iocoder.yudao.module.trade.service.message.dto.TradeOrderMessageWhenDeliveryOrderReqDTO;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
* Trade 消息 service 实现类
*
* @author HUIHUI
*/
@Service
@Validated
public class TradeMessageServiceImpl implements TradeMessageService {
@Resource
private NotifyMessageSendApi notifyMessageSendApi;
@Override
public void sendMessageWhenDeliveryOrder(TradeOrderMessageWhenDeliveryOrderReqDTO reqDTO) {
// 1构造消息
Map<String, Object> msgMap = new HashMap<>();
msgMap.put("orderId", reqDTO.getOrderId());
msgMap.put("msg", reqDTO.getMessage());
// 2发送站内信
notifyMessageSendApi.sendSingleMessageToMember(
new NotifySendSingleToUserReqDTO()
.setUserId(reqDTO.getUserId())
.setTemplateCode("order_delivery")
.setTemplateParams(msgMap));
}
}

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.trade.service.message.dto;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* 订单发货时 Req DTO
*
* @author HUIHUI
*/
@Data
public class TradeOrderMessageWhenDeliveryOrderReqDTO {
/**
* 订单编号
*/
@NotNull(message = "订单编号不能为空")
private Long orderId;
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Long userId;
/**
* 消息
*/
@NotEmpty(message = "发送消息不能为空")
private String message;
}

View File

@ -8,6 +8,7 @@ import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.member.api.address.AddressApi;
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
@ -21,12 +22,10 @@ import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi;
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
import cn.iocoder.yudao.module.promotion.api.combination.CombinationApi;
import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi;
import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
@ -38,9 +37,7 @@ import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDeliveryDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderDeliveryMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants;
@ -49,6 +46,8 @@ import cn.iocoder.yudao.module.trade.enums.order.*;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
import cn.iocoder.yudao.module.trade.service.cart.TradeCartService;
import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
import cn.iocoder.yudao.module.trade.service.message.TradeMessageService;
import cn.iocoder.yudao.module.trade.service.message.dto.TradeOrderMessageWhenDeliveryOrderReqDTO;
import cn.iocoder.yudao.module.trade.service.price.TradePriceService;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
@ -102,15 +101,13 @@ public class TradeOrderServiceImpl implements TradeOrderService {
@Resource
private ProductCommentApi productCommentApi;
@Resource
private NotifyMessageSendApi notifyMessageSendApi;
private TradeMessageService tradeMessageService;
@Resource
private TradeOrderProperties tradeOrderProperties;
@Resource
private CombinationApi combinationApi;
@Resource
private TradeOrderDeliveryMapper orderDeliveryMapper;
private CombinationRecordApi combinationRecordApi;
// =================== Order ===================
@Override
@ -177,7 +174,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
MemberUserRespDTO user = memberUserApi.getUser(userId);
// TODO 拼团一次应该只能选择一种规格的商品
combinationApi.createRecord(TradeOrderConvert.INSTANCE.convert(order, orderItems.get(0), createReqVO, user)
combinationRecordApi.createRecord(TradeOrderConvert.INSTANCE.convert(order, orderItems.get(0), createReqVO, user)
.setStatus(CombinationRecordStatusEnum.WAITING.getStatus()));
}
// TODO 秒杀扣减库存是下单就扣除还是等待订单支付成功再扣除
@ -314,7 +311,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
// 1拼团活动
if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
// 更新拼团状态 TODO puhui999订单支付失败或订单支付过期删除这条拼团记录
combinationApi.updateRecordStatusAndStartTime(order.getUserId(), order.getId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
combinationRecordApi.updateRecordStatusAndStartTime(order.getUserId(), order.getId(), CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
}
// TODO 芋艿发送订单变化的消息
@ -385,23 +382,26 @@ public class TradeOrderServiceImpl implements TradeOrderService {
// 校验并获得交易订单可发货
TradeOrderDO order = validateOrderDeliverable(deliveryReqVO.getId());
TradeOrderDO tradeOrderDO = new TradeOrderDO();
List<TradeOrderDeliveryDO> deliveryDOs = new ArrayList<>();
/* TODO
* fix: 首先需要店铺设置配送方式如 自提 配送物流-配送物流-配送-自提商家配送
* 1.如果店铺有设置配送方式用户只填写收货地址的情况下店家后台自己选择配送方式
* 2.如果店铺只支持到店自提那么下单后默认发货不需要物流
* 3.如果店铺支持 物流-配送-自提 的情况下后台不需要选择配送方式按前端用户选择的配送方式发货即可
*/
TradeOrderDO tradeOrderDO = new TradeOrderDO();
// 判断发货类型
// 快递发货
if (ObjectUtil.equal(deliveryReqVO.getType(), DeliveryTypeEnum.EXPRESS.getMode())) {
deliveryDOs = express(order, deliveryReqVO);
// 校验快递公司
DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId());
if (deliveryExpress == null) {
throw exception(EXPRESS_NOT_EXISTS);
}
tradeOrderDO.setLogisticsId(deliveryReqVO.getLogisticsId()).setLogisticsNo(deliveryReqVO.getLogisticsNo());
}
// 用户自提
if (ObjectUtil.equal(deliveryReqVO.getType(), DeliveryTypeEnum.PICK_UP.getMode())) {
deliveryDOs = pickUp(order, deliveryReqVO);
// TODO 校验自提门店是否存在
// 重置一下确保快递公司和快递单号为空
tradeOrderDO.setLogisticsId(null).setLogisticsNo("");
}
@ -409,6 +409,8 @@ public class TradeOrderServiceImpl implements TradeOrderService {
if (ObjectUtil.equal(deliveryReqVO.getType(), DeliveryTypeEnum.NULL.getMode())) {
// TODO 情况一正常走发货逻辑和用户自提有点像 不同点不需要自提门店只需要用户确认收货
// TODO 情况二用户下单付款后直接确认收货或等待用户确认收货
// 重置一下确保快递公司和快递单号为空
tradeOrderDO.setLogisticsId(null).setLogisticsNo("");
}
// 更新 TradeOrderDO 状态为已发货等待收货
@ -418,72 +420,16 @@ public class TradeOrderServiceImpl implements TradeOrderService {
if (updateCount == 0) {
throw exception(ORDER_DELIVERY_FAIL_STATUS_NOT_UNDELIVERED);
}
// 发货成功记录发货表
orderDeliveryMapper.insertBatch(deliveryDOs);
// TODO 芋艿发送订单变化的消息
// TODO @puhui999可以抽个 message 里面是 Order 所有的 message类似工作流的
// 发送站内信
// 1构造消息
Map<String, Object> msgMap = new HashMap<>();
msgMap.put("orderId", deliveryReqVO.getId());
msgMap.put("msg", TradeOrderStatusEnum.DELIVERED.getStatus());
// 2发送站内信
notifyMessageSendApi.sendSingleMessageToMember(
new NotifySendSingleToUserReqDTO()
.setUserId(userId)
.setTemplateCode("order_delivery")
.setTemplateParams(msgMap));
tradeMessageService.sendMessageWhenDeliveryOrder(new TradeOrderMessageWhenDeliveryOrderReqDTO().setOrderId(order.getId())
.setUserId(userId).setMessage(TradeOrderDeliveryStatusEnum.DELIVERED.getName()));
// TODO 芋艿OrderLog
// TODO 设计lili是不是发货后才支持售后
}
private List<TradeOrderDeliveryDO> express(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) {
// 校验快递公司
DeliveryExpressDO deliveryExpress = deliveryExpressService.getDeliveryExpress(deliveryReqVO.getLogisticsId());
if (deliveryExpress == null) {
throw exception(EXPRESS_NOT_EXISTS);
}
// 校验发货商品
validateDeliveryOrderItem(order, deliveryReqVO);
// 创建发货记录
return TradeOrderConvert.INSTANCE.covert(order, deliveryReqVO);
}
private List<TradeOrderDeliveryDO> pickUp(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) {
// TODO 校验自提门店是否存在
// 重置一下确保快递公司和快递单号为空
deliveryReqVO.setLogisticsId(null);
deliveryReqVO.setLogisticsNo("");
// 校验发货商品
validateDeliveryOrderItem(order, deliveryReqVO);
// 创建发货记录
return TradeOrderConvert.INSTANCE.covert(order, deliveryReqVO);
}
private void validateDeliveryOrderItem(TradeOrderDO order, TradeOrderDeliveryReqVO deliveryReqVO) {
// TODO 设计like是否要单独一个 delivery 发货单表 fix: 多商品可分开单独发货添加 trade_order_delivery 交易订单发货日志表关联发货所选订单项设置物流单号
// TODO 设计niu要不要支持一个订单下多个 order item 单独发货类似有赞 fix
// 校验发货商品
if (CollUtil.isEmpty(deliveryReqVO.getOrderItemIds())) {
throw exception(ORDER_DELIVERY_FAILED_ITEMS_NOT_EMPTY);
}
// 校验发货商品是否存在
List<TradeOrderItemDO> orderItemDOs = tradeOrderItemMapper.selectListByOrderId(order.getId());
Set<Long> itemIds = convertSet(orderItemDOs, TradeOrderItemDO::getId);
if (!itemIds.containsAll(deliveryReqVO.getOrderItemIds())) {
throw exception(ORDER_DELIVERY_FAILED_ITEM_NOT_EXISTS);
}
// 校验所选订单项是否存在有已发货的
List<TradeOrderDeliveryDO> deliveryDOList = orderDeliveryMapper.selsectListByOrderIdAndItemIds(order.getId(), deliveryReqVO.getOrderItemIds());
if (CollUtil.isNotEmpty(deliveryDOList)) {
HashSet<Long> hashSet = CollUtil.newHashSet(deliveryReqVO.getOrderItemIds());
hashSet.retainAll(convertSet(deliveryDOList, TradeOrderDeliveryDO::getOrderItemId));
throw exception(ORDER_DELIVERY_FAILED_ITEM_ALREADY_DELIVERY);
}
}
/**
* 校验交易订单满足被发货的条件
*
@ -511,7 +457,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
if (ObjectUtil.equal(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
// 校验订单拼团是否成功
// TODO 用户 ID 使用当前登录用户的还是订单保存的
if (combinationApi.validateRecordStatusIsSuccess(order.getUserId(), order.getId())) {
if (combinationRecordApi.validateRecordStatusIsSuccess(order.getUserId(), order.getId())) {
throw exception(ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS);
}
}
@ -532,7 +478,6 @@ public class TradeOrderServiceImpl implements TradeOrderService {
if (updateCount == 0) {
throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED);
}
// TODO 芋艿OrderLog
// TODO 芋艿lili 发送订单变化的消息
@ -683,6 +628,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
}
@Override
@Transactional(rollbackFor = Exception.class)
public Long createOrderItemComment(AppTradeOrderItemCommentCreateReqVO createReqVO) {
Long loginUserId = getLoginUserId();
// 先通过订单项 ID查询订单项是否存在
@ -690,7 +636,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
if (orderItem == null) {
throw exception(ORDER_ITEM_NOT_FOUND);
}
// 校验订单
// 校验订单相关状态
TradeOrderDO order = getOrderByIdAndUserId(orderItem.getOrderId(), loginUserId);
if (order == null) {
throw exception(ORDER_NOT_FOUND);
@ -701,11 +647,18 @@ public class TradeOrderServiceImpl implements TradeOrderService {
if (ObjectUtil.notEqual(order.getCommentStatus(), Boolean.FALSE)) {
throw exception(ORDER_COMMENT_STATUS_NOT_FALSE);
}
// TODO @puhui999是不是评论完要更新 statuscommentStatus另外是不是上面 order 可以不校验直接只判断 orderItem 就够
// 对于 order 来说就是评论完 order 更新完合理的 status 等字段
// 创建评价
ProductCommentCreateReqDTO productCommentCreateReqDTO = TradeOrderConvert.INSTANCE.convert04(createReqVO, orderItem);
return productCommentApi.createComment(productCommentCreateReqDTO);
Long comment = productCommentApi.createComment(productCommentCreateReqDTO);
// 更新订单项评价状态
tradeOrderItemMapper.updateById(new TradeOrderItemDO().setId(orderItem.getId()).setCommentStatus(Boolean.TRUE));
List<TradeOrderItemDO> orderItems = getOrderItemListByOrderId(CollUtil.newArrayList(order.getId()));
if (!CollectionUtils.isAny(orderItems, item -> ObjectUtil.equal(item.getCommentStatus(), Boolean.FALSE))) {
// 对于 order 来说就是评论完 order 更新完合理的 status 等字段
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE));
}
return comment;
}
/**