Commit 3e570b8b89678653d2ab259f233689c57bb1975c

Authored by 芋道源码
Committed by Gitee
2 parents 785fbeeb 0e754a99

!685 完善遗留的问题

Merge pull request !685 from puhui999/feature/mall_product
Showing 40 changed files with 501 additions and 269 deletions
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/enums/ErrorCodeConstants.java
@@ -34,8 +34,9 @@ public interface ErrorCodeConstants { @@ -34,8 +34,9 @@ public interface ErrorCodeConstants {
34 // ========== 商品 SPU 1-008-005-000 ========== 34 // ========== 商品 SPU 1-008-005-000 ==========
35 ErrorCode SPU_NOT_EXISTS = new ErrorCode(1_008_005_000, "商品 SPU 不存在"); 35 ErrorCode SPU_NOT_EXISTS = new ErrorCode(1_008_005_000, "商品 SPU 不存在");
36 ErrorCode SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR = new ErrorCode(1_008_005_001, "商品分类不正确,原因:必须使用第二级的商品分类及以下"); 36 ErrorCode SPU_SAVE_FAIL_CATEGORY_LEVEL_ERROR = new ErrorCode(1_008_005_001, "商品分类不正确,原因:必须使用第二级的商品分类及以下");
37 - ErrorCode SPU_NOT_ENABLE = new ErrorCode(1_008_005_002, "商品 SPU【{}】不处于上架状态");  
38 - ErrorCode SPU_NOT_RECYCLE = new ErrorCode(1_008_005_003, "商品 SPU 不处于回收站状态"); 37 + ErrorCode SPU_SAVE_FAIL_COUPON_TEMPLATE_NOT_EXISTS = new ErrorCode(1_008_005_002, "商品 SPU 保存失败,原因:优惠卷不存在");
  38 + ErrorCode SPU_NOT_ENABLE = new ErrorCode(1_008_005_003, "商品 SPU【{}】不处于上架状态");
  39 + ErrorCode SPU_NOT_RECYCLE = new ErrorCode(1_008_005_004, "商品 SPU 不处于回收站状态");
39 40
40 // ========== 商品 SKU 1-008-006-000 ========== 41 // ========== 商品 SKU 1-008-006-000 ==========
41 ErrorCode SKU_NOT_EXISTS = new ErrorCode(1_008_006_000, "商品 SKU 不存在"); 42 ErrorCode SKU_NOT_EXISTS = new ErrorCode(1_008_006_000, "商品 SKU 不存在");
yudao-module-mall/yudao-module-product-biz/pom.xml
@@ -28,6 +28,11 @@ @@ -28,6 +28,11 @@
28 <artifactId>yudao-module-member-api</artifactId> 28 <artifactId>yudao-module-member-api</artifactId>
29 <version>${revision}</version> 29 <version>${revision}</version>
30 </dependency> 30 </dependency>
  31 + <dependency>
  32 + <groupId>cn.iocoder.boot</groupId>
  33 + <artifactId>yudao-module-promotion-api</artifactId>
  34 + <version>${revision}</version>
  35 + </dependency>
31 36
32 <!-- 业务组件 --> 37 <!-- 业务组件 -->
33 <dependency> 38 <dependency>
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java
@@ -11,6 +11,8 @@ import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; @@ -11,6 +11,8 @@ import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
11 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; 11 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
12 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; 12 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
13 import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; 13 import cn.iocoder.yudao.module.product.service.spu.ProductSpuService;
  14 +import cn.iocoder.yudao.module.promotion.api.coupon.CouponTemplateApi;
  15 +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponTemplateRespDTO;
14 import io.swagger.v3.oas.annotations.Operation; 16 import io.swagger.v3.oas.annotations.Operation;
15 import io.swagger.v3.oas.annotations.Parameter; 17 import io.swagger.v3.oas.annotations.Parameter;
16 import io.swagger.v3.oas.annotations.tags.Tag; 18 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -43,6 +45,9 @@ public class ProductSpuController { @@ -43,6 +45,9 @@ public class ProductSpuController {
43 @Resource 45 @Resource
44 private ProductSkuService productSkuService; 46 private ProductSkuService productSkuService;
45 47
  48 + @Resource
  49 + private CouponTemplateApi couponTemplateApi;
  50 +
46 @PostMapping("/create") 51 @PostMapping("/create")
47 @Operation(summary = "创建商品 SPU") 52 @Operation(summary = "创建商品 SPU")
48 @PreAuthorize("@ss.hasPermission('product:spu:create')") 53 @PreAuthorize("@ss.hasPermission('product:spu:create')")
@@ -87,7 +92,10 @@ public class ProductSpuController { @@ -87,7 +92,10 @@ public class ProductSpuController {
87 } 92 }
88 // 查询商品 SKU 93 // 查询商品 SKU
89 List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId()); 94 List<ProductSkuDO> skus = productSkuService.getSkuListBySpuId(spu.getId());
90 - return success(ProductSpuConvert.INSTANCE.convertForSpuDetailRespVO(spu, skus)); 95 + // 查询优惠卷
  96 + List<CouponTemplateRespDTO> couponTemplateList = couponTemplateApi.getCouponTemplateListByIds(
  97 + spu.getGiveCouponTemplateIds());
  98 + return success(ProductSpuConvert.INSTANCE.convertForSpuDetailRespVO(spu, skus, couponTemplateList));
91 } 99 }
92 100
93 @GetMapping("/list-all-simple") 101 @GetMapping("/list-all-simple")
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuBaseVO.java
@@ -96,19 +96,31 @@ public class ProductSpuBaseVO { @@ -96,19 +96,31 @@ public class ProductSpuBaseVO {
96 @NotNull(message = "商品赠送积分不能为空") 96 @NotNull(message = "商品赠送积分不能为空")
97 private Integer giveIntegral; 97 private Integer giveIntegral;
98 98
99 - @Schema(description = "赠送的优惠劵编号的数组", example = "[1, 10]") // TODO 这块前端还未实现  
100 - private List<Long> giveCouponTemplateIds; 99 + @Schema(description = "赠送的优惠劵数组包含优惠券编号和名称")
  100 + private List<GiveCouponTemplate> giveCouponTemplates;
101 101
102 @Schema(description = "分销类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") 102 @Schema(description = "分销类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
103 @NotNull(message = "商品分销类型不能为空") 103 @NotNull(message = "商品分销类型不能为空")
104 private Boolean subCommissionType; 104 private Boolean subCommissionType;
105 105
106 - @Schema(description = "活动展示顺序", example = "[1, 3, 2, 4, 5]") // TODO 这块前端还未实现 106 + @Schema(description = "活动展示顺序", example = "[1, 3, 2, 4, 5]")
107 private List<Integer> activityOrders; 107 private List<Integer> activityOrders;
108 108
109 // ========== 统计相关字段 ========= 109 // ========== 统计相关字段 =========
110 110
111 - @Schema(description = "虚拟销量", example = "芋道") 111 + @Schema(description = "虚拟销量", example = "66")
112 private Integer virtualSalesCount; 112 private Integer virtualSalesCount;
113 113
  114 + @Schema(description = "管理后台 - 商品 SPU 赠送的优惠卷")
  115 + @Data
  116 + public static class GiveCouponTemplate {
  117 +
  118 + @Schema(description = "模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  119 + private Long id;
  120 +
  121 + @Schema(description = "优惠劵名", requiredMode = Schema.RequiredMode.REQUIRED, example = "春节送送送")
  122 + private String name;
  123 +
  124 + }
  125 +
114 } 126 }
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/spu/ProductSpuConvert.java
@@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert; @@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.product.convert.sku.ProductSkuConvert;
12 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; 12 import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO;
13 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; 13 import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO;
14 import cn.iocoder.yudao.module.product.enums.DictTypeConstants; 14 import cn.iocoder.yudao.module.product.enums.DictTypeConstants;
  15 +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponTemplateRespDTO;
15 import org.mapstruct.Mapper; 16 import org.mapstruct.Mapper;
16 import org.mapstruct.Mapping; 17 import org.mapstruct.Mapping;
17 import org.mapstruct.Named; 18 import org.mapstruct.Named;
@@ -100,10 +101,14 @@ public interface ProductSpuConvert { @@ -100,10 +101,14 @@ public interface ProductSpuConvert {
100 101
101 List<AppProductSpuDetailRespVO.Sku> convertListForGetSpuDetail(List<ProductSkuDO> skus); 102 List<AppProductSpuDetailRespVO.Sku> convertListForGetSpuDetail(List<ProductSkuDO> skus);
102 103
103 - default ProductSpuDetailRespVO convertForSpuDetailRespVO(ProductSpuDO spu, List<ProductSkuDO> skus) {  
104 - ProductSpuDetailRespVO detailRespVO = convert03(spu);  
105 - detailRespVO.setSkus(ProductSkuConvert.INSTANCE.convertList(skus));  
106 - return detailRespVO; 104 + List<ProductSpuDetailRespVO.GiveCouponTemplate> convertList04(List<CouponTemplateRespDTO> couponTemplateList);
  105 +
  106 + default ProductSpuDetailRespVO convertForSpuDetailRespVO(ProductSpuDO spu, List<ProductSkuDO> skus,
  107 + List<CouponTemplateRespDTO> couponTemplateList) {
  108 + ProductSpuDetailRespVO respVO = convert03(spu);
  109 + respVO.setSkus(ProductSkuConvert.INSTANCE.convertList(skus));
  110 + respVO.setGiveCouponTemplates(convertList04(couponTemplateList));
  111 + return respVO;
107 } 112 }
108 113
109 default List<ProductSpuDetailRespVO> convertForSpuDetailRespListVO(List<ProductSpuDO> spus, List<ProductSkuDO> skus) { 114 default List<ProductSpuDetailRespVO> convertForSpuDetailRespListVO(List<ProductSpuDO> spus, List<ProductSkuDO> skus) {
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/dataobject/spu/ProductSpuDO.java
@@ -194,7 +194,7 @@ public class ProductSpuDO extends BaseDO { @@ -194,7 +194,7 @@ public class ProductSpuDO extends BaseDO {
194 * 对应 PromotionTypeEnum 枚举 194 * 对应 PromotionTypeEnum 枚举
195 */ 195 */
196 @TableField(typeHandler = JacksonTypeHandler.class) 196 @TableField(typeHandler = JacksonTypeHandler.class)
197 - private List<Integer> activityOrders; 197 + private List<Integer> activityOrders; // TODO @芋艿: 活动顺序字段长度需要增加
198 198
199 // ========== 统计相关字段 ========= 199 // ========== 统计相关字段 =========
200 200
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java
@@ -16,8 +16,10 @@ import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper; @@ -16,8 +16,10 @@ import cn.iocoder.yudao.module.product.dal.mysql.spu.ProductSpuMapper;
16 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; 16 import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
17 import cn.iocoder.yudao.module.product.service.brand.ProductBrandService; 17 import cn.iocoder.yudao.module.product.service.brand.ProductBrandService;
18 import cn.iocoder.yudao.module.product.service.category.ProductCategoryService; 18 import cn.iocoder.yudao.module.product.service.category.ProductCategoryService;
19 -import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService;  
20 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; 19 import cn.iocoder.yudao.module.product.service.sku.ProductSkuService;
  20 +import cn.iocoder.yudao.module.promotion.api.coupon.CouponTemplateApi;
  21 +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponTemplateRespDTO;
  22 +import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
21 import com.google.common.collect.Maps; 23 import com.google.common.collect.Maps;
22 import org.springframework.context.annotation.Lazy; 24 import org.springframework.context.annotation.Lazy;
23 import org.springframework.stereotype.Service; 25 import org.springframework.stereotype.Service;
@@ -26,10 +28,10 @@ import org.springframework.validation.annotation.Validated; @@ -26,10 +28,10 @@ import org.springframework.validation.annotation.Validated;
26 28
27 import javax.annotation.Resource; 29 import javax.annotation.Resource;
28 import java.util.*; 30 import java.util.*;
  31 +import java.util.stream.Collectors;
29 32
30 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
31 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMinValue;  
32 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; 34 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
33 import static cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO.CATEGORY_LEVEL; 35 import static cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO.CATEGORY_LEVEL;
34 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; 36 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;
35 37
@@ -52,9 +54,10 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -52,9 +54,10 @@ public class ProductSpuServiceImpl implements ProductSpuService {
52 private ProductBrandService brandService; 54 private ProductBrandService brandService;
53 @Resource 55 @Resource
54 private ProductCategoryService categoryService; 56 private ProductCategoryService categoryService;
  57 +
55 @Resource 58 @Resource
56 - @Lazy // 循环依赖,避免报错  
57 - private ProductPropertyValueService productPropertyValueService; 59 + @Lazy
  60 + private CouponTemplateApi couponTemplateApi;
58 61
59 @Override 62 @Override
60 @Transactional(rollbackFor = Exception.class) 63 @Transactional(rollbackFor = Exception.class)
@@ -62,6 +65,9 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -62,6 +65,9 @@ public class ProductSpuServiceImpl implements ProductSpuService {
62 // 校验分类、品牌 65 // 校验分类、品牌
63 validateCategory(createReqVO.getCategoryId()); 66 validateCategory(createReqVO.getCategoryId());
64 brandService.validateProductBrand(createReqVO.getBrandId()); 67 brandService.validateProductBrand(createReqVO.getBrandId());
  68 + // 校验优惠券
  69 + Set<Long> giveCouponTemplateIds = convertSet(createReqVO.getGiveCouponTemplates(), ProductSpuCreateReqVO.GiveCouponTemplate::getId);
  70 + validateCouponTemplate(giveCouponTemplateIds);
65 // 校验 SKU 71 // 校验 SKU
66 List<ProductSkuCreateOrUpdateReqVO> skuSaveReqList = createReqVO.getSkus(); 72 List<ProductSkuCreateOrUpdateReqVO> skuSaveReqList = createReqVO.getSkus();
67 productSkuService.validateSkuList(skuSaveReqList, createReqVO.getSpecType()); 73 productSkuService.validateSkuList(skuSaveReqList, createReqVO.getSpecType());
@@ -69,6 +75,8 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -69,6 +75,8 @@ public class ProductSpuServiceImpl implements ProductSpuService {
69 ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO); 75 ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO);
70 // 初始化 SPU 中 SKU 相关属性 76 // 初始化 SPU 中 SKU 相关属性
71 initSpuFromSkus(spu, skuSaveReqList); 77 initSpuFromSkus(spu, skuSaveReqList);
  78 + // 设置优惠券
  79 + spu.setGiveCouponTemplateIds(CollUtil.newArrayList(giveCouponTemplateIds));
72 // 插入 SPU 80 // 插入 SPU
73 productSpuMapper.insert(spu); 81 productSpuMapper.insert(spu);
74 // 插入 SKU 82 // 插入 SKU
@@ -85,6 +93,9 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -85,6 +93,9 @@ public class ProductSpuServiceImpl implements ProductSpuService {
85 // 校验分类、品牌 93 // 校验分类、品牌
86 validateCategory(updateReqVO.getCategoryId()); 94 validateCategory(updateReqVO.getCategoryId());
87 brandService.validateProductBrand(updateReqVO.getBrandId()); 95 brandService.validateProductBrand(updateReqVO.getBrandId());
  96 + // 校验优惠券
  97 + Set<Long> giveCouponTemplateIds = convertSet(updateReqVO.getGiveCouponTemplates(), ProductSpuUpdateReqVO.GiveCouponTemplate::getId);
  98 + validateCouponTemplate(giveCouponTemplateIds);
88 // 校验SKU 99 // 校验SKU
89 List<ProductSkuCreateOrUpdateReqVO> skuSaveReqList = updateReqVO.getSkus(); 100 List<ProductSkuCreateOrUpdateReqVO> skuSaveReqList = updateReqVO.getSkus();
90 productSkuService.validateSkuList(skuSaveReqList, updateReqVO.getSpecType()); 101 productSkuService.validateSkuList(skuSaveReqList, updateReqVO.getSpecType());
@@ -92,6 +103,8 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -92,6 +103,8 @@ public class ProductSpuServiceImpl implements ProductSpuService {
92 // 更新 SPU 103 // 更新 SPU
93 ProductSpuDO updateObj = ProductSpuConvert.INSTANCE.convert(updateReqVO); 104 ProductSpuDO updateObj = ProductSpuConvert.INSTANCE.convert(updateReqVO);
94 initSpuFromSkus(updateObj, skuSaveReqList); 105 initSpuFromSkus(updateObj, skuSaveReqList);
  106 + // 设置优惠券
  107 + updateObj.setGiveCouponTemplateIds(CollUtil.newArrayList(giveCouponTemplateIds));
95 productSpuMapper.updateById(updateObj); 108 productSpuMapper.updateById(updateObj);
96 // 批量更新 SKU 109 // 批量更新 SKU
97 productSkuService.updateSkuList(updateObj.getId(), updateReqVO.getSkus()); 110 productSkuService.updateSkuList(updateObj.getId(), updateReqVO.getSkus());
@@ -125,6 +138,10 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -125,6 +138,10 @@ public class ProductSpuServiceImpl implements ProductSpuService {
125 // 默认商品浏览量 138 // 默认商品浏览量
126 spu.setBrowseCount(0); 139 spu.setBrowseCount(0);
127 } 140 }
  141 + // 如果活动顺序为空则默认初始化
  142 + if (CollUtil.isEmpty(spu.getActivityOrders())) {
  143 + spu.setActivityOrders(Arrays.stream(PromotionTypeEnum.ARRAYS).boxed().collect(Collectors.toList()));
  144 + }
128 } 145 }
129 146
130 /** 147 /**
@@ -140,6 +157,13 @@ public class ProductSpuServiceImpl implements ProductSpuService { @@ -140,6 +157,13 @@ public class ProductSpuServiceImpl implements ProductSpuService {
140 } 157 }
141 } 158 }
142 159
  160 + private void validateCouponTemplate(Collection<Long> ids) {
  161 + List<CouponTemplateRespDTO> couponTemplateList = couponTemplateApi.getCouponTemplateListByIds(ids);
  162 + if (couponTemplateList.size() != ids.size()) {
  163 + throw exception(SPU_SAVE_FAIL_COUPON_TEMPLATE_NOT_EXISTS);
  164 + }
  165 + }
  166 +
143 @Override 167 @Override
144 public List<ProductSpuDO> validateSpuList(Collection<Long> ids) { 168 public List<ProductSpuDO> validateSpuList(Collection<Long> ids) {
145 if (CollUtil.isEmpty(ids)) { 169 if (CollUtil.isEmpty(ids)) {
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponTemplateApi.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.api.coupon;
  2 +
  3 +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponTemplateRespDTO;
  4 +
  5 +import java.util.Collection;
  6 +import java.util.List;
  7 +
  8 +/**
  9 + * 优惠劵模版 API 接口
  10 + *
  11 + * @author HUIHUI
  12 + */
  13 +public interface CouponTemplateApi {
  14 +
  15 + /**
  16 + * 获得优惠券模版的精简信息列表
  17 + *
  18 + * @param ids 优惠券模版编号
  19 + * @return 优惠券模版的精简信息列表
  20 + */
  21 + List<CouponTemplateRespDTO> getCouponTemplateListByIds(Collection<Long> ids);
  22 +
  23 +}
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/dto/CouponTemplateRespDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.api.coupon.dto;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import lombok.Data;
  5 +
  6 +/**
  7 + * 优惠券模版 Response DTO
  8 + *
  9 + * @author HUIHUI
  10 + */
  11 +@Data
  12 +public class CouponTemplateRespDTO {
  13 + /**
  14 + * 模板编号,自增唯一
  15 + */
  16 +
  17 + private Long id;
  18 + /**
  19 + * 优惠劵名
  20 + */
  21 + private String name;
  22 +
  23 + /**
  24 + * 状态
  25 + *
  26 + * 枚举 {@link CommonStatusEnum}
  27 + */
  28 + private Integer status;
  29 +
  30 +}
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/banner/BannerPositionEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.enums.banner;
  2 +
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
  4 +import cn.iocoder.yudao.module.promotion.enums.bargain.BargainRecordStatusEnum;
  5 +import lombok.AllArgsConstructor;
  6 +import lombok.Getter;
  7 +
  8 +import java.util.Arrays;
  9 +
  10 +/**
  11 + * Banner Position 枚举
  12 + *
  13 + * @author HUIHUI
  14 + */
  15 +@AllArgsConstructor
  16 +@Getter
  17 +public enum BannerPositionEnum implements IntArrayValuable {
  18 +
  19 + HOME_POSITION(1, "首页"),
  20 + SECKILL_POSITION(2, "秒杀活动页"),
  21 + COMBINATION_POSITION(3, "砍价活动页"),
  22 + DISCOUNT_POSITION(4, "限时折扣页"),
  23 + REWARD_POSITION(5, "满减送页");
  24 +
  25 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BargainRecordStatusEnum::getStatus).toArray();
  26 + /**
  27 + * 值
  28 + */
  29 + private final Integer position;
  30 + /**
  31 + * 名字
  32 + */
  33 + private final String name;
  34 +
  35 + @Override
  36 + public int[] array() {
  37 + return ARRAYS;
  38 + }
  39 +
  40 +}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/coupon/CouponTemplateApiImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.api.coupon;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponTemplateRespDTO;
  5 +import cn.iocoder.yudao.module.promotion.convert.coupon.CouponTemplateConvert;
  6 +import cn.iocoder.yudao.module.promotion.service.coupon.CouponTemplateService;
  7 +import org.springframework.stereotype.Service;
  8 +
  9 +import javax.annotation.Resource;
  10 +import java.util.Collection;
  11 +import java.util.Collections;
  12 +import java.util.List;
  13 +
  14 +/**
  15 + * 优惠劵模版 API 接口实现类
  16 + *
  17 + * @author HUIHUI
  18 + */
  19 +@Service
  20 +public class CouponTemplateApiImpl implements CouponTemplateApi {
  21 +
  22 + @Resource
  23 + private CouponTemplateService couponTemplateService;
  24 +
  25 + @Override
  26 + public List<CouponTemplateRespDTO> getCouponTemplateListByIds(Collection<Long> ids) {
  27 + if (CollUtil.isEmpty(ids)) { // 防御一下
  28 + return Collections.emptyList();
  29 + }
  30 + return CouponTemplateConvert.INSTANCE.convertList(couponTemplateService.getCouponTemplateListByIds(ids));
  31 + }
  32 +
  33 +}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerBaseVO.java
@@ -27,6 +27,10 @@ public class BannerBaseVO { @@ -27,6 +27,10 @@ public class BannerBaseVO {
27 @NotNull(message = "图片地址不能为空") 27 @NotNull(message = "图片地址不能为空")
28 private String picUrl; 28 private String picUrl;
29 29
  30 + @Schema(description = "position", requiredMode = Schema.RequiredMode.REQUIRED)
  31 + @NotNull(message = "position 不能为空")
  32 + private Integer position;
  33 +
30 @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED) 34 @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED)
31 @NotNull(message = "排序不能为空") 35 @NotNull(message = "排序不能为空")
32 private Integer sort; 36 private Integer sort;
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/banner/vo/BannerPageReqVO.java
@@ -25,7 +25,6 @@ public class BannerPageReqVO extends PageParam { @@ -25,7 +25,6 @@ public class BannerPageReqVO extends PageParam {
25 @Schema(description = "标题") 25 @Schema(description = "标题")
26 private String title; 26 private String title;
27 27
28 -  
29 @Schema(description = "状态") 28 @Schema(description = "状态")
30 @InEnum(CommonStatusEnum.class) 29 @InEnum(CommonStatusEnum.class)
31 private Integer status; 30 private Integer status;
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/article/AppArticleController.java
@@ -56,13 +56,11 @@ public class AppArticleController { @@ -56,13 +56,11 @@ public class AppArticleController {
56 return success(ArticleConvert.INSTANCE.convert01(articleService.getArticle(id))); 56 return success(ArticleConvert.INSTANCE.convert01(articleService.getArticle(id)));
57 } 57 }
58 58
59 - // TODO @puhui999:add-browse-count 噢;前端 uniapp 也要接下;就是打开文章的时候,调用下这个接口;  
60 - @PutMapping("/add-browseCount") 59 + @PutMapping("/add-browse-count")
61 @Operation(summary = "增加文章浏览量") 60 @Operation(summary = "增加文章浏览量")
62 @Parameter(name = "id", description = "文章编号", example = "1024") 61 @Parameter(name = "id", description = "文章编号", example = "1024")
63 public CommonResult<Boolean> addBrowseCount(@RequestParam("id") Long id) { 62 public CommonResult<Boolean> addBrowseCount(@RequestParam("id") Long id) {
64 - // TODO @puhui999:addArticleBrowseCount  
65 - articleService.addBrowseCount(id); 63 + articleService.addArticleBrowseCount(id);
66 return success(true); 64 return success(true);
67 } 65 }
68 66
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/banner/AppBannerController.java
@@ -2,18 +2,23 @@ package cn.iocoder.yudao.module.promotion.controller.app.banner; @@ -2,18 +2,23 @@ package cn.iocoder.yudao.module.promotion.controller.app.banner;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.module.promotion.controller.app.banner.vo.AppBannerRespVO; 4 import cn.iocoder.yudao.module.promotion.controller.app.banner.vo.AppBannerRespVO;
  5 +import cn.iocoder.yudao.module.promotion.convert.banner.BannerConvert;
  6 +import cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO;
  7 +import cn.iocoder.yudao.module.promotion.service.banner.BannerService;
  8 +import com.google.common.cache.CacheLoader;
  9 +import com.google.common.cache.LoadingCache;
5 import io.swagger.v3.oas.annotations.Operation; 10 import io.swagger.v3.oas.annotations.Operation;
  11 +import io.swagger.v3.oas.annotations.Parameter;
6 import io.swagger.v3.oas.annotations.tags.Tag; 12 import io.swagger.v3.oas.annotations.tags.Tag;
7 import org.springframework.validation.annotation.Validated; 13 import org.springframework.validation.annotation.Validated;
8 -import org.springframework.web.bind.annotation.GetMapping;  
9 -import org.springframework.web.bind.annotation.RequestMapping;  
10 -import org.springframework.web.bind.annotation.RequestParam;  
11 -import org.springframework.web.bind.annotation.RestController; 14 +import org.springframework.web.bind.annotation.*;
12 15
13 -import java.util.ArrayList; 16 +import javax.annotation.Resource;
  17 +import java.time.Duration;
14 import java.util.List; 18 import java.util.List;
15 19
16 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 20 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  21 +import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;
17 22
18 @RestController 23 @RestController
19 @RequestMapping("/promotion/banner") 24 @RequestMapping("/promotion/banner")
@@ -21,22 +26,39 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -21,22 +26,39 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
21 @Validated 26 @Validated
22 public class AppBannerController { 27 public class AppBannerController {
23 28
  29 + @Resource
  30 + private BannerService bannerService;
  31 + /**
  32 + * {@link AppBannerRespVO} 缓存,通过它异步刷新 {@link #getBannerList0(Integer)} 所要的首页数据
  33 + */
  34 + private final LoadingCache<Integer, List<AppBannerRespVO>> bannerListCache = buildAsyncReloadingCache(Duration.ofSeconds(10L),
  35 + new CacheLoader<Integer, List<AppBannerRespVO>>() {
  36 +
  37 + @Override
  38 + public List<AppBannerRespVO> load(Integer position) {
  39 + return getBannerList0(position);
  40 + }
  41 +
  42 + });
  43 +
24 @GetMapping("/list") 44 @GetMapping("/list")
25 @Operation(summary = "获得 banner 列表") 45 @Operation(summary = "获得 banner 列表")
26 - // todo @芋艿:swagger 注解,待补全  
27 - // TODO @芋艿:可以增加缓存,提升性能  
28 - // TODO @芋艿:position = 1 时,首页;position = 10 时,拼团活动页 46 + @Parameter(name = "position", description = "Banner position", example = "1")
29 public CommonResult<List<AppBannerRespVO>> getBannerList(@RequestParam("position") Integer position) { 47 public CommonResult<List<AppBannerRespVO>> getBannerList(@RequestParam("position") Integer position) {
30 - List<AppBannerRespVO> bannerList = new ArrayList<>();  
31 - AppBannerRespVO banner1 = new AppBannerRespVO();  
32 - banner1.setUrl("https://www.example.com/link1");  
33 - banner1.setPicUrl("https://api.java.crmeb.net/crmebimage/public/content/2022/08/04/0f78716213f64bfa83f191d51a832cbf73f6axavoy.jpg");  
34 - bannerList.add(banner1);  
35 - AppBannerRespVO banner2 = new AppBannerRespVO();  
36 - banner2.setUrl("https://www.example.com/link2");  
37 - banner2.setPicUrl("https://api.java.crmeb.net/crmebimage/public/content/2023/01/11/be09e755268b43ee90b0db3a3e1b7132r7a6t2wvsm.jpg");  
38 - bannerList.add(banner2);  
39 - return success(bannerList); 48 + return success(bannerListCache.getUnchecked(position));
  49 + }
  50 +
  51 + private List<AppBannerRespVO> getBannerList0(Integer position) {
  52 + List<BannerDO> bannerList = bannerService.getBannerListByPosition(position);
  53 + return BannerConvert.INSTANCE.convertList01(bannerList);
  54 + }
  55 +
  56 + @PutMapping("/add-browse-count")
  57 + @Operation(summary = "增加 Banner 点击量")
  58 + @Parameter(name = "id", description = "Banner 编号", example = "1024")
  59 + public CommonResult<Boolean> addBrowseCount(@RequestParam("id") Long id) {
  60 + bannerService.addBannerBrowseCount(id);
  61 + return success(true);
40 } 62 }
41 63
42 } 64 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/banner/vo/AppBannerRespVO.java
@@ -9,6 +9,13 @@ import javax.validation.constraints.NotNull; @@ -9,6 +9,13 @@ import javax.validation.constraints.NotNull;
9 @Data 9 @Data
10 public class AppBannerRespVO { 10 public class AppBannerRespVO {
11 11
  12 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED)
  13 + private Long id;
  14 +
  15 + @Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED)
  16 + @NotNull(message = "标题不能为空")
  17 + private String title;
  18 +
12 @Schema(description = "跳转链接", requiredMode = Schema.RequiredMode.REQUIRED) 19 @Schema(description = "跳转链接", requiredMode = Schema.RequiredMode.REQUIRED)
13 @NotNull(message = "跳转链接不能为空") 20 @NotNull(message = "跳转链接不能为空")
14 private String url; 21 private String url;
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/banner/BannerConvert.java
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerCreateReqVO; 4 import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerCreateReqVO;
5 import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerRespVO; 5 import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerRespVO;
6 import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerUpdateReqVO; 6 import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerUpdateReqVO;
  7 +import cn.iocoder.yudao.module.promotion.controller.app.banner.vo.AppBannerRespVO;
7 import cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO;
8 import org.mapstruct.Mapper; 9 import org.mapstruct.Mapper;
9 import org.mapstruct.factory.Mappers; 10 import org.mapstruct.factory.Mappers;
@@ -25,4 +26,6 @@ public interface BannerConvert { @@ -25,4 +26,6 @@ public interface BannerConvert {
25 26
26 BannerDO convert(BannerUpdateReqVO updateReqVO); 27 BannerDO convert(BannerUpdateReqVO updateReqVO);
27 28
  29 + List<AppBannerRespVO> convertList01(List<BannerDO> bannerList);
  30 +
28 } 31 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/coupon/CouponTemplateConvert.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.convert.coupon; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.convert.coupon;
2 2
3 import cn.hutool.core.map.MapUtil; 3 import cn.hutool.core.map.MapUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponTemplateRespDTO;
5 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO; 6 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO;
6 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;
7 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateRespVO; 8 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateRespVO;
@@ -58,4 +59,6 @@ public interface CouponTemplateConvert { @@ -58,4 +59,6 @@ public interface CouponTemplateConvert {
58 } 59 }
59 } 60 }
60 61
  62 + List<CouponTemplateRespDTO> convertList(List<CouponTemplateDO> list);
  63 +
61 } 64 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/banner/BannerDO.java
1 package cn.iocoder.yudao.module.promotion.dal.dataobject.banner; 1 package cn.iocoder.yudao.module.promotion.dal.dataobject.banner;
2 2
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.module.promotion.enums.banner.BannerPositionEnum;
4 import com.baomidou.mybatisplus.annotation.TableName; 6 import com.baomidou.mybatisplus.annotation.TableName;
5 import lombok.*; 7 import lombok.*;
6 8
@@ -40,14 +42,25 @@ public class BannerDO extends BaseDO { @@ -40,14 +42,25 @@ public class BannerDO extends BaseDO {
40 private Integer sort; 42 private Integer sort;
41 43
42 /** 44 /**
43 - * 状态 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} 45 + * 状态 {@link CommonStatusEnum}
44 */ 46 */
45 private Integer status; 47 private Integer status;
  48 +
  49 + /**
  50 + * 定位 {@link BannerPositionEnum}
  51 + */
  52 + private Integer position;
  53 +
46 /** 54 /**
47 * 备注 55 * 备注
48 */ 56 */
49 private String memo; 57 private String memo;
50 58
51 - // TODO 芋艿 点击次数。&& 其他数据相关 59 + /**
  60 + * 点击次数
  61 + */
  62 + private Integer browseCount;
  63 +
  64 + // TODO 芋艿 其他数据相关
52 65
53 } 66 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/banner/BannerMapper.java
@@ -5,8 +5,11 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; @@ -5,8 +5,11 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerPageReqVO; 6 import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerPageReqVO;
7 import cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO; 7 import cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO;
  8 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
8 import org.apache.ibatis.annotations.Mapper; 9 import org.apache.ibatis.annotations.Mapper;
9 10
  11 +import java.util.List;
  12 +
10 /** 13 /**
11 * Banner Mapper 14 * Banner Mapper
12 * 15 *
@@ -23,4 +26,14 @@ public interface BannerMapper extends BaseMapperX&lt;BannerDO&gt; { @@ -23,4 +26,14 @@ public interface BannerMapper extends BaseMapperX&lt;BannerDO&gt; {
23 .orderByDesc(BannerDO::getSort)); 26 .orderByDesc(BannerDO::getSort));
24 } 27 }
25 28
  29 + default void updateBrowseCount(Long id) {
  30 + update(null, new LambdaUpdateWrapper<BannerDO>()
  31 + .eq(BannerDO::getId, id)
  32 + .setSql("browse_count = browse_count + 1"));
  33 + }
  34 +
  35 + default List<BannerDO> selectBannerListByPosition(Integer position) {
  36 + return selectList(new LambdaQueryWrapperX<BannerDO>().eq(BannerDO::getPosition, position));
  37 + }
  38 +
26 } 39 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainActivityMapper.java
@@ -86,7 +86,6 @@ public interface BargainActivityMapper extends BaseMapperX&lt;BargainActivityDO&gt; { @@ -86,7 +86,6 @@ public interface BargainActivityMapper extends BaseMapperX&lt;BargainActivityDO&gt; {
86 .last("LIMIT " + count)); 86 .last("LIMIT " + count));
87 } 87 }
88 88
89 - // TODO @puhui999:是不是返回 BargainActivityDO 更干净哈?分组后返回 DO 的话需要联表查询  
90 /** 89 /**
91 * 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号 90 * 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
92 * 91 *
@@ -102,7 +101,6 @@ public interface BargainActivityMapper extends BaseMapperX&lt;BargainActivityDO&gt; { @@ -102,7 +101,6 @@ public interface BargainActivityMapper extends BaseMapperX&lt;BargainActivityDO&gt; {
102 .groupBy("spu_id")); 101 .groupBy("spu_id"));
103 } 102 }
104 103
105 - // TODO @puhui999:是不是只要 endTime 小于就可以啦;  
106 /** 104 /**
107 * 获取指定活动编号的活动列表且 105 * 获取指定活动编号的活动列表且
108 * 开始时间和结束时间小于给定时间 dateTime 的活动列表 106 * 开始时间和结束时间小于给定时间 dateTime 的活动列表
@@ -115,7 +113,7 @@ public interface BargainActivityMapper extends BaseMapperX&lt;BargainActivityDO&gt; { @@ -115,7 +113,7 @@ public interface BargainActivityMapper extends BaseMapperX&lt;BargainActivityDO&gt; {
115 return selectList(new LambdaQueryWrapperX<BargainActivityDO>() 113 return selectList(new LambdaQueryWrapperX<BargainActivityDO>()
116 .in(BargainActivityDO::getId, ids) 114 .in(BargainActivityDO::getId, ids)
117 .lt(BargainActivityDO::getStartTime, dateTime) 115 .lt(BargainActivityDO::getStartTime, dateTime)
118 - .lt(BargainActivityDO::getEndTime, dateTime) 116 + .gt(BargainActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
119 .orderByDesc(BargainActivityDO::getCreateTime)); 117 .orderByDesc(BargainActivityDO::getCreateTime));
120 } 118 }
121 119
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationActivityMapper.java
@@ -71,7 +71,7 @@ public interface CombinationActivityMapper extends BaseMapperX&lt;CombinationActivi @@ -71,7 +71,7 @@ public interface CombinationActivityMapper extends BaseMapperX&lt;CombinationActivi
71 return selectList(new LambdaQueryWrapperX<CombinationActivityDO>() 71 return selectList(new LambdaQueryWrapperX<CombinationActivityDO>()
72 .in(CombinationActivityDO::getId, ids) 72 .in(CombinationActivityDO::getId, ids)
73 .lt(CombinationActivityDO::getStartTime, dateTime) 73 .lt(CombinationActivityDO::getStartTime, dateTime)
74 - .lt(CombinationActivityDO::getEndTime, dateTime) 74 + .gt(CombinationActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
75 .orderByDesc(CombinationActivityDO::getCreateTime)); 75 .orderByDesc(CombinationActivityDO::getCreateTime));
76 } 76 }
77 77
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java
@@ -8,10 +8,11 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; @@ -8,10 +8,11 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
8 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO; 8 import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO;
9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
10 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 10 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  11 +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
11 import org.apache.ibatis.annotations.Mapper; 12 import org.apache.ibatis.annotations.Mapper;
12 -import org.apache.ibatis.annotations.Param;  
13 13
14 import java.time.LocalDateTime; 14 import java.time.LocalDateTime;
  15 +import java.util.Collection;
15 import java.util.List; 16 import java.util.List;
16 import java.util.function.Consumer; 17 import java.util.function.Consumer;
17 18
@@ -39,7 +40,11 @@ public interface CouponTemplateMapper extends BaseMapperX&lt;CouponTemplateDO&gt; { @@ -39,7 +40,11 @@ public interface CouponTemplateMapper extends BaseMapperX&lt;CouponTemplateDO&gt; {
39 .orderByDesc(CouponTemplateDO::getId)); 40 .orderByDesc(CouponTemplateDO::getId));
40 } 41 }
41 42
42 - void updateTakeCount(@Param("id") Long id, @Param("incrCount") Integer incrCount); 43 + default void updateTakeCount(Long id, Integer incrCount) {
  44 + update(null, new LambdaUpdateWrapper<CouponTemplateDO>()
  45 + .eq(CouponTemplateDO::getId, id)
  46 + .setSql("take_count = take_count + " + incrCount));
  47 + }
43 48
44 default List<CouponTemplateDO> selectListByTakeType(Integer takeType) { 49 default List<CouponTemplateDO> selectListByTakeType(Integer takeType) {
45 return selectList(CouponTemplateDO::getTakeType, takeType); 50 return selectList(CouponTemplateDO::getTakeType, takeType);
@@ -70,4 +75,8 @@ public interface CouponTemplateMapper extends BaseMapperX&lt;CouponTemplateDO&gt; { @@ -70,4 +75,8 @@ public interface CouponTemplateMapper extends BaseMapperX&lt;CouponTemplateDO&gt; {
70 return canTakeConsumer; 75 return canTakeConsumer;
71 } 76 }
72 77
  78 + default List<CouponTemplateDO> selectListByIds(Collection<Long> ids) {
  79 + return selectList(new LambdaQueryWrapperX<CouponTemplateDO>().in(CouponTemplateDO::getId, ids));
  80 + }
  81 +
73 } 82 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java
@@ -103,7 +103,7 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; { @@ -103,7 +103,7 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; {
103 return selectList(new LambdaQueryWrapperX<SeckillActivityDO>() 103 return selectList(new LambdaQueryWrapperX<SeckillActivityDO>()
104 .in(SeckillActivityDO::getId, ids) 104 .in(SeckillActivityDO::getId, ids)
105 .lt(SeckillActivityDO::getStartTime, dateTime) 105 .lt(SeckillActivityDO::getStartTime, dateTime)
106 - .lt(SeckillActivityDO::getEndTime, dateTime) 106 + .gt(SeckillActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动
107 .orderByDesc(SeckillActivityDO::getCreateTime)); 107 .orderByDesc(SeckillActivityDO::getCreateTime));
108 } 108 }
109 109
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/article/ArticleService.java
@@ -93,6 +93,6 @@ public interface ArticleService { @@ -93,6 +93,6 @@ public interface ArticleService {
93 * 93 *
94 * @param id 文章编号 94 * @param id 文章编号
95 */ 95 */
96 - void addBrowseCount(Long id); 96 + void addArticleBrowseCount(Long id);
97 97
98 } 98 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/article/ArticleServiceImpl.java
@@ -111,7 +111,7 @@ public class ArticleServiceImpl implements ArticleService { @@ -111,7 +111,7 @@ public class ArticleServiceImpl implements ArticleService {
111 } 111 }
112 112
113 @Override 113 @Override
114 - public void addBrowseCount(Long id) { 114 + public void addArticleBrowseCount(Long id) {
115 // 校验文章是否存在 115 // 校验文章是否存在
116 validateArticleExists(id); 116 validateArticleExists(id);
117 // 增加浏览次数 117 // 增加浏览次数
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/banner/BannerService.java
@@ -47,12 +47,6 @@ public interface BannerService { @@ -47,12 +47,6 @@ public interface BannerService {
47 BannerDO getBanner(Long id); 47 BannerDO getBanner(Long id);
48 48
49 /** 49 /**
50 - * 获得所有 Banner列表  
51 - * @return Banner列表  
52 - */  
53 - List<BannerDO> getBannerList();  
54 -  
55 - /**  
56 * 获得 Banner 分页 50 * 获得 Banner 分页
57 * 51 *
58 * @param pageReqVO 分页查询 52 * @param pageReqVO 分页查询
@@ -60,4 +54,19 @@ public interface BannerService { @@ -60,4 +54,19 @@ public interface BannerService {
60 */ 54 */
61 PageResult<BannerDO> getBannerPage(BannerPageReqVO pageReqVO); 55 PageResult<BannerDO> getBannerPage(BannerPageReqVO pageReqVO);
62 56
  57 + /**
  58 + * 增加 Banner 点击量
  59 + *
  60 + * @param id Banner编号
  61 + */
  62 + void addBannerBrowseCount(Long id);
  63 +
  64 + /**
  65 + * 获得 Banner 列表
  66 + *
  67 + * @param position 定位
  68 + * @return Banner 列表
  69 + */
  70 + List<BannerDO> getBannerListByPosition(Integer position);
  71 +
63 } 72 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/banner/BannerServiceImpl.java
@@ -66,13 +66,21 @@ public class BannerServiceImpl implements BannerService { @@ -66,13 +66,21 @@ public class BannerServiceImpl implements BannerService {
66 } 66 }
67 67
68 @Override 68 @Override
69 - public List<BannerDO> getBannerList() {  
70 - return bannerMapper.selectList(); 69 + public PageResult<BannerDO> getBannerPage(BannerPageReqVO pageReqVO) {
  70 + return bannerMapper.selectPage(pageReqVO);
71 } 71 }
72 72
73 @Override 73 @Override
74 - public PageResult<BannerDO> getBannerPage(BannerPageReqVO pageReqVO) {  
75 - return bannerMapper.selectPage(pageReqVO); 74 + public void addBannerBrowseCount(Long id) {
  75 + // 校验 Banner 是否存在
  76 + validateBannerExists(id);
  77 + // 增加点击次数
  78 + bannerMapper.updateBrowseCount(id);
  79 + }
  80 +
  81 + @Override
  82 + public List<BannerDO> getBannerListByPosition(Integer position) {
  83 + return bannerMapper.selectBannerListByPosition(position);
76 } 84 }
77 85
78 } 86 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java
@@ -369,8 +369,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -369,8 +369,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
369 keyValue.setValue(keyValue.getValue() + 1); 369 keyValue.setValue(keyValue.getValue() + 1);
370 } 370 }
371 } catch (Exception ignored) { // 处理异常继续循环 371 } catch (Exception ignored) { // 处理异常继续循环
372 - // TODO @puhui999:拼团过期 or 虚拟成团 可以改成 expireCombinationRecord;因为找方法更容易一些;  
373 - log.error("[拼团过期 or 虚拟成团][record({}) 处理异常,请进行处理!record 数据是:{}]", 372 + log.error("[expireCombinationRecord][record({}) 处理异常,请进行处理!record 数据是:{}]",
374 record.getId(), JsonUtils.toJsonString(record)); 373 record.getId(), JsonUtils.toJsonString(record));
375 } 374 }
376 } 375 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateService.java
@@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO;
8 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum; 8 import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum;
9 9
10 import javax.validation.Valid; 10 import javax.validation.Valid;
  11 +import java.util.Collection;
11 import java.util.List; 12 import java.util.List;
12 13
13 /** 14 /**
@@ -91,4 +92,12 @@ public interface CouponTemplateService { @@ -91,4 +92,12 @@ public interface CouponTemplateService {
91 List<CouponTemplateDO> getCouponTemplateList(List<Integer> canTakeTypes, Integer productScope, 92 List<CouponTemplateDO> getCouponTemplateList(List<Integer> canTakeTypes, Integer productScope,
92 Long productScopeValue, Integer count); 93 Long productScopeValue, Integer count);
93 94
  95 + /**
  96 + * 获得优惠券模版列表
  97 + *
  98 + * @param ids 优惠券模版编号
  99 + * @return 优惠券模版列表
  100 + */
  101 + List<CouponTemplateDO> getCouponTemplateListByIds(Collection<Long> ids);
  102 +
94 } 103 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java
@@ -16,6 +16,7 @@ import org.springframework.stereotype.Service; @@ -16,6 +16,7 @@ import org.springframework.stereotype.Service;
16 import org.springframework.validation.annotation.Validated; 16 import org.springframework.validation.annotation.Validated;
17 17
18 import javax.annotation.Resource; 18 import javax.annotation.Resource;
  19 +import java.util.Collection;
19 import java.util.List; 20 import java.util.List;
20 import java.util.Objects; 21 import java.util.Objects;
21 22
@@ -126,4 +127,9 @@ public class CouponTemplateServiceImpl implements CouponTemplateService { @@ -126,4 +127,9 @@ public class CouponTemplateServiceImpl implements CouponTemplateService {
126 return couponTemplateMapper.selectList(canTakeTypes, productScope, productScopeValue, count); 127 return couponTemplateMapper.selectList(canTakeTypes, productScope, productScopeValue, count);
127 } 128 }
128 129
  130 + @Override
  131 + public List<CouponTemplateDO> getCouponTemplateListByIds(Collection<Long> ids) {
  132 + return couponTemplateMapper.selectListByIds(ids);
  133 + }
  134 +
129 } 135 }
yudao-module-mall/yudao-module-promotion-biz/src/main/resources/mapper/coupon/CouponTemplateMapper.xml deleted
1 -<?xml version="1.0" encoding="UTF-8"?>  
2 -<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
3 -<mapper namespace="cn.iocoder.yudao.module.promotion.dal.mysql.coupon.CouponTemplateMapper">  
4 -  
5 - <update id="updateTakeCount">  
6 - UPDATE promotion_coupon_template  
7 - SET take_count = take_count + #{incrCount}  
8 - WHERE id = #{id}  
9 - </update>  
10 -  
11 -</mapper>  
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/AppMemberSignInController.java deleted
1 -package cn.iocoder.yudao.module.member.controller.app.signin;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.CommonResult;  
4 -import cn.iocoder.yudao.module.member.service.signin.MemberSignInRecordService;  
5 -import io.swagger.v3.oas.annotations.Operation;  
6 -import io.swagger.v3.oas.annotations.tags.Tag;  
7 -import org.springframework.web.bind.annotation.GetMapping;  
8 -import org.springframework.web.bind.annotation.RequestMapping;  
9 -import org.springframework.web.bind.annotation.RestController;  
10 -  
11 -import javax.annotation.Resource;  
12 -  
13 -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;  
14 -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;  
15 -  
16 -// TODO @xiaqing:sign-in  
17 -@Tag(name = "签到APP - 签到")  
18 -@RestController  
19 -@RequestMapping("/member/signin")  
20 -public class AppMemberSignInController {  
21 -  
22 - @Resource  
23 - private MemberSignInRecordService signInRecordService;  
24 -  
25 - // TODO @xiaqing:泛型:  
26 - // TODO @xiaqing:合并到 AppMemberSignInRecordController 的 getSignInRecordSummary 里哈。  
27 - @Operation(summary = "个人签到信息")  
28 - @GetMapping("/get-summary")  
29 - public CommonResult getUserSummary() {  
30 - return success(signInRecordService.getSignInRecordSummary(getLoginUserId()));  
31 - }  
32 -  
33 -}  
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/AppMemberSignInRecordController.java
@@ -31,22 +31,11 @@ public class AppMemberSignInRecordController { @@ -31,22 +31,11 @@ public class AppMemberSignInRecordController {
31 @Resource 31 @Resource
32 private MemberSignInRecordService signInRecordService; 32 private MemberSignInRecordService signInRecordService;
33 33
34 - // TODO 芋艿:临时 mock => UserSignController.getUserInfo  
35 @GetMapping("/get-summary") 34 @GetMapping("/get-summary")
36 @Operation(summary = "获得个人签到统计") 35 @Operation(summary = "获得个人签到统计")
37 @PreAuthenticated 36 @PreAuthenticated
38 public CommonResult<AppMemberSignInRecordSummaryRespVO> getSignInRecordSummary() { 37 public CommonResult<AppMemberSignInRecordSummaryRespVO> getSignInRecordSummary() {
39 - AppMemberSignInRecordSummaryRespVO respVO = new AppMemberSignInRecordSummaryRespVO();  
40 - if (false) {  
41 - respVO.setTotalDay(100);  
42 - respVO.setContinuousDay(5);  
43 - respVO.setTodaySignIn(true);  
44 - } else {  
45 - respVO.setTotalDay(100);  
46 - respVO.setContinuousDay(10);  
47 - respVO.setTodaySignIn(false);  
48 - }  
49 - return success(respVO); 38 + return success(signInRecordService.getSignInRecordSummary(getLoginUserId()));
50 } 39 }
51 40
52 @PostMapping("/create") 41 @PostMapping("/create")
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/vo/AppMemberSignInRecordRespVO.java deleted
1 -package cn.iocoder.yudao.module.member.controller.app.signin.vo;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import lombok.Data;  
5 -  
6 -import java.time.LocalDateTime;  
7 -  
8 -@Schema(description = "用户签到积分 Response VO")  
9 -@Data  
10 -public class AppMemberSignInRecordRespVO {  
11 -  
12 - @Schema(description = "第几天签到", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")  
13 - private Integer day;  
14 -  
15 - @Schema(description = "签到的积分", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")  
16 - private Integer point;  
17 -  
18 - @Schema(description = "签到的经验", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")  
19 - private Integer experience;  
20 -  
21 - @Schema(description = "签到时间", requiredMode = Schema.RequiredMode.REQUIRED)  
22 - private LocalDateTime createTime;  
23 -  
24 -}  
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/signin/vo/AppMemberSignInSummaryRespVO.java deleted
1 -package cn.iocoder.yudao.module.member.controller.app.signin.vo;  
2 -  
3 -import io.swagger.v3.oas.annotations.media.Schema;  
4 -import lombok.Data;  
5 -  
6 -import java.time.LocalDateTime;  
7 -  
8 -@Schema(description = "用户签到统计信息 Response VO")  
9 -@Data  
10 -public class AppMemberSignInSummaryRespVO {  
11 -  
12 - @Schema(description = "持续签到天数", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")  
13 - private Integer continuousDay;  
14 -  
15 - @Schema(description = "总签到天数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")  
16 - private Integer totalDay;  
17 -  
18 - @Schema(description = "当天是否签到", requiredMode = Schema.RequiredMode.REQUIRED,example = "true")  
19 - private Boolean todaySignIn ;  
20 -  
21 -}  
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/convert/signin/MemberSignInRecordConvert.java
1 package cn.iocoder.yudao.module.member.convert.signin; 1 package cn.iocoder.yudao.module.member.convert.signin;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 6 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
5 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 7 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
6 import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordRespVO; 8 import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordRespVO;
7 import cn.iocoder.yudao.module.member.controller.app.signin.vo.record.AppMemberSignInRecordRespVO; 9 import cn.iocoder.yudao.module.member.controller.app.signin.vo.record.AppMemberSignInRecordRespVO;
  10 +import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO;
8 import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO; 11 import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO;
9 import org.mapstruct.Mapper; 12 import org.mapstruct.Mapper;
10 import org.mapstruct.factory.Mappers; 13 import org.mapstruct.factory.Mappers;
11 14
  15 +import java.time.LocalDateTime;
  16 +import java.time.temporal.ChronoUnit;
12 import java.util.List; 17 import java.util.List;
13 import java.util.Map; 18 import java.util.Map;
14 19
@@ -32,10 +37,37 @@ public interface MemberSignInRecordConvert { @@ -32,10 +37,37 @@ public interface MemberSignInRecordConvert {
32 memberUserRespDTO -> record.setNickname(memberUserRespDTO.getNickname()))); 37 memberUserRespDTO -> record.setNickname(memberUserRespDTO.getNickname())));
33 return voPageResult; 38 return voPageResult;
34 } 39 }
  40 +
35 PageResult<MemberSignInRecordRespVO> convertPage(PageResult<MemberSignInRecordDO> pageResult); 41 PageResult<MemberSignInRecordRespVO> convertPage(PageResult<MemberSignInRecordDO> pageResult);
36 42
37 PageResult<AppMemberSignInRecordRespVO> convertPage02(PageResult<MemberSignInRecordDO> pageResult); 43 PageResult<AppMemberSignInRecordRespVO> convertPage02(PageResult<MemberSignInRecordDO> pageResult);
38 44
39 AppMemberSignInRecordRespVO coverRecordToAppRecordVo(MemberSignInRecordDO memberSignInRecordDO); 45 AppMemberSignInRecordRespVO coverRecordToAppRecordVo(MemberSignInRecordDO memberSignInRecordDO);
40 46
  47 + default MemberSignInRecordDO convert(Long userId, MemberSignInRecordDO firstRecord, List<MemberSignInConfigDO> signInConfigs) {
  48 + // 1. 计算今天是第几天签到
  49 + long day = ChronoUnit.DAYS.between(firstRecord.getCreateTime(), LocalDateTime.now());
  50 + // 2. 初始化签到信息
  51 + MemberSignInRecordDO signInRecord = new MemberSignInRecordDO().setUserId(userId)
  52 + .setDay(Integer.parseInt(Long.toString(day))) // 设置签到天数
  53 + .setPoint(0) // 设置签到积分默认为 0
  54 + .setExperience(0); // 设置签到经验默认为 0
  55 +
  56 +
  57 + // 3. 获取签到对应的积分数
  58 + MemberSignInConfigDO lastConfig = signInConfigs.get(signInConfigs.size() - 1); // 最大签到天数
  59 + if (day > lastConfig.getDay()) { // 超出范围按第一天的经验计算
  60 + signInRecord.setPoint(signInConfigs.get(0).getPoint());
  61 + signInRecord.setExperience(signInConfigs.get(0).getExperience());
  62 + return signInRecord;
  63 + }
  64 + MemberSignInConfigDO signInConfig = CollUtil.findOne(signInConfigs, config -> ObjUtil.equal(config.getDay(), day));
  65 + if (signInConfig == null) {
  66 + return signInRecord;
  67 + }
  68 + signInRecord.setPoint(signInConfig.getPoint());
  69 + signInRecord.setExperience(signInConfig.getExperience());
  70 + return signInRecord;
  71 + }
  72 +
41 } 73 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/signin/MemberSignInRecordMapper.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7 import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordPageReqVO; 7 import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordPageReqVO;
8 import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO; 8 import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO;
  9 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
9 import org.apache.ibatis.annotations.Mapper; 10 import org.apache.ibatis.annotations.Mapper;
10 11
11 import java.util.List; 12 import java.util.List;
@@ -35,9 +36,45 @@ public interface MemberSignInRecordMapper extends BaseMapperX&lt;MemberSignInRecord @@ -35,9 +36,45 @@ public interface MemberSignInRecordMapper extends BaseMapperX&lt;MemberSignInRecord
35 } 36 }
36 37
37 38
38 - //获取用户的签到记录列表信息,根据签到时间倒序  
39 - default List<MemberSignInRecordDO> selectListByUserId(Long userId){  
40 - return selectList(new LambdaQueryWrapperX <MemberSignInRecordDO>() 39 + /**
  40 + * 获取用户最近的签到记录信息,根据签到时间倒序
  41 + *
  42 + * @param userId 用户编号
  43 + * @return 签到记录列表
  44 + */
  45 + default MemberSignInRecordDO selectLastRecordByUserIdDesc(Long userId) {
  46 + return selectOne(new QueryWrapper<MemberSignInRecordDO>()
  47 + .eq("user_id", userId)
  48 + .orderByDesc("create_time")
  49 + .last("limit 1"));
  50 + }
  51 +
  52 + /**
  53 + * 获取用户最早的签到记录信息,根据签到时间倒序
  54 + *
  55 + * @param userId 用户编号
  56 + * @return 签到记录列表
  57 + */
  58 + default MemberSignInRecordDO selectLastRecordByUserIdAsc(Long userId) {
  59 + return selectOne(new QueryWrapper<MemberSignInRecordDO>()
  60 + .eq("user_id", userId)
  61 + .orderByAsc("create_time")
  62 + .last("limit 1"));
  63 + }
  64 +
  65 + default Long selectCountByUserId(Long userId) {
  66 + return selectCount(new LambdaQueryWrapperX<MemberSignInRecordDO>()
  67 + .eq(MemberSignInRecordDO::getUserId, userId));
  68 + }
  69 +
  70 + /**
  71 + * 获取用户的签到记录列表信息,根据签到时间倒序
  72 + *
  73 + * @param userId 用户编号
  74 + * @return 签到记录信息
  75 + */
  76 + default List<MemberSignInRecordDO> selectListByUserId(Long userId) {
  77 + return selectList(new LambdaQueryWrapperX<MemberSignInRecordDO>()
41 .eq(MemberSignInRecordDO::getUserId, userId) 78 .eq(MemberSignInRecordDO::getUserId, userId)
42 .orderByDesc(MemberSignInRecordDO::getCreateTime)); 79 .orderByDesc(MemberSignInRecordDO::getCreateTime));
43 } 80 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInRecordService.java
@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.member.service.signin; @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.member.service.signin;
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordPageReqVO; 5 import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordPageReqVO;
6 -import cn.iocoder.yudao.module.member.controller.app.signin.vo.AppMemberSignInSummaryRespVO; 6 +import cn.iocoder.yudao.module.member.controller.app.signin.vo.record.AppMemberSignInRecordSummaryRespVO;
7 import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO; 7 import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO;
8 8
9 /** 9 /**
@@ -24,7 +24,7 @@ public interface MemberSignInRecordService { @@ -24,7 +24,7 @@ public interface MemberSignInRecordService {
24 /** 24 /**
25 * 【会员】获得签到记录分页 25 * 【会员】获得签到记录分页
26 * 26 *
27 - * @param userId 用户编号 27 + * @param userId 用户编号
28 * @param pageParam 分页查询 28 * @param pageParam 分页查询
29 * @return 签到记录分页 29 * @return 签到记录分页
30 */ 30 */
@@ -44,7 +44,7 @@ public interface MemberSignInRecordService { @@ -44,7 +44,7 @@ public interface MemberSignInRecordService {
44 * @param userId 用户编号 44 * @param userId 用户编号
45 * @return 个人签到统计信息 45 * @return 个人签到统计信息
46 */ 46 */
47 - AppMemberSignInSummaryRespVO getSignInRecordSummary(Long userId); 47 + AppMemberSignInRecordSummaryRespVO getSignInRecordSummary(Long userId);
48 48
49 49
50 } 50 }
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/signin/MemberSignInRecordServiceImpl.java
1 package cn.iocoder.yudao.module.member.service.signin; 1 package cn.iocoder.yudao.module.member.service.signin;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.common.pojo.PageParam; 6 import cn.iocoder.yudao.framework.common.pojo.PageParam;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  8 +import cn.iocoder.yudao.framework.common.util.date.DateUtils;
6 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 9 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
7 -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;  
8 import cn.iocoder.yudao.module.member.api.user.MemberUserApi; 10 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
9 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 11 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
10 import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordPageReqVO; 12 import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordPageReqVO;
11 -import cn.iocoder.yudao.module.member.controller.app.signin.vo.AppMemberSignInSummaryRespVO; 13 +import cn.iocoder.yudao.module.member.controller.app.signin.vo.record.AppMemberSignInRecordSummaryRespVO;
  14 +import cn.iocoder.yudao.module.member.convert.signin.MemberSignInRecordConvert;
12 import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO; 15 import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO;
13 import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO; 16 import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO;
14 -import cn.iocoder.yudao.module.member.dal.mysql.signin.MemberSignInConfigMapper;  
15 import cn.iocoder.yudao.module.member.dal.mysql.signin.MemberSignInRecordMapper; 17 import cn.iocoder.yudao.module.member.dal.mysql.signin.MemberSignInRecordMapper;
16 -import cn.iocoder.yudao.module.member.enums.ErrorCodeConstants;  
17 import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum; 18 import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
18 import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; 19 import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;
19 import cn.iocoder.yudao.module.member.service.level.MemberLevelService; 20 import cn.iocoder.yudao.module.member.service.level.MemberLevelService;
@@ -21,17 +22,17 @@ import cn.iocoder.yudao.module.member.service.point.MemberPointRecordService; @@ -21,17 +22,17 @@ import cn.iocoder.yudao.module.member.service.point.MemberPointRecordService;
21 import org.apache.commons.lang3.StringUtils; 22 import org.apache.commons.lang3.StringUtils;
22 import org.springframework.stereotype.Service; 23 import org.springframework.stereotype.Service;
23 import org.springframework.transaction.annotation.Transactional; 24 import org.springframework.transaction.annotation.Transactional;
24 -import org.springframework.util.CollectionUtils;  
25 import org.springframework.validation.annotation.Validated; 25 import org.springframework.validation.annotation.Validated;
26 26
27 import javax.annotation.Resource; 27 import javax.annotation.Resource;
28 import java.time.LocalDate; 28 import java.time.LocalDate;
29 -import java.time.temporal.ChronoUnit; 29 +import java.util.Comparator;
30 import java.util.List; 30 import java.util.List;
31 import java.util.Set; 31 import java.util.Set;
32 32
33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
34 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; 34 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  35 +import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.SIGN_IN_RECORD_TODAY_EXISTS;
35 36
36 /** 37 /**
37 * 签到记录 Service 实现类 38 * 签到记录 Service 实现类
@@ -45,7 +46,7 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService @@ -45,7 +46,7 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService
45 @Resource 46 @Resource
46 private MemberSignInRecordMapper signInRecordMapper; 47 private MemberSignInRecordMapper signInRecordMapper;
47 @Resource 48 @Resource
48 - private MemberSignInConfigMapper signInConfigMapper; 49 + private MemberSignInConfigService signInConfigService;
49 @Resource 50 @Resource
50 private MemberPointRecordService pointRecordService; 51 private MemberPointRecordService pointRecordService;
51 @Resource 52 @Resource
@@ -55,50 +56,64 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService @@ -55,50 +56,64 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService
55 private MemberUserApi memberUserApi; 56 private MemberUserApi memberUserApi;
56 57
57 @Override 58 @Override
58 - public AppMemberSignInSummaryRespVO getSignInRecordSummary(Long userId) {  
59 - AppMemberSignInSummaryRespVO vo = new AppMemberSignInSummaryRespVO(); 59 + public AppMemberSignInRecordSummaryRespVO getSignInRecordSummary(Long userId) {
  60 + // 1. 初始化默认返回信息
  61 + AppMemberSignInRecordSummaryRespVO vo = new AppMemberSignInRecordSummaryRespVO();
60 vo.setTotalDay(0); 62 vo.setTotalDay(0);
61 vo.setContinuousDay(0); 63 vo.setContinuousDay(0);
62 vo.setTodaySignIn(false); 64 vo.setTodaySignIn(false);
63 - //获取用户签到的记录,按照天数倒序获取  
64 - List<MemberSignInRecordDO> signInRecordDOList = signInRecordMapper.selectListByUserId(userId);  
65 - // TODO @xiaqing:if 空的时候,直接 return;这样括号少,逻辑更简洁;  
66 - if (!CollectionUtils.isEmpty(signInRecordDOList)) {  
67 - //设置总签到天数  
68 - vo.setTotalDay(signInRecordDOList.size()); // TODO @xiaqing:是不是不用读取 signInRecordDOList 所有的,而是 count下,然后另外再读取一条最后一条;  
69 - //判断当天是否有签到复用校验方法  
70 - // TODO @xiaqing:不要用异常实现逻辑;还是判断哈;  
71 - try {  
72 - validSignDay(signInRecordDOList.get(0));  
73 - vo.setTodaySignIn(false);  
74 - } catch (Exception e) {  
75 - vo.setTodaySignIn(true);  
76 - }  
77 - //如果当天签到了则说明连续签到天数有意义,否则直接用默认值0  
78 - if (vo.getTodaySignIn()) {  
79 - //下方计算连续签到从2天开始,此处直接设置一天连续签到  
80 - vo.setContinuousDay(1);  
81 - //判断连续签到天数  
82 - // TODO @xiaqing:这里逻辑,想想怎么在简化下,可读性可以在提升下哈;  
83 - for (int i = 1; i < signInRecordDOList.size(); i++) {  
84 - //前一天减1等于当前天数则说明连续,继续循环  
85 - LocalDate cur = signInRecordDOList.get(i).getCreateTime().toLocalDate();  
86 - LocalDate pre = signInRecordDOList.get(i - 1).getCreateTime().toLocalDate();  
87 - if (1 == daysBetween(cur, pre)) {  
88 - vo.setContinuousDay(i + 1);  
89 - } else {  
90 - break;  
91 - }  
92 - }  
93 - }  
94 65
  66 + // 2. 获取用户签到的记录数
  67 + Long signCount = signInRecordMapper.selectCountByUserId(userId);
  68 + if (ObjUtil.equal(signCount, 0L)) {
  69 + return vo;
  70 + }
  71 + vo.setTotalDay(signCount.intValue()); // 设置总签到天数
  72 +
  73 + // 3. 校验当天是否有签到
  74 + MemberSignInRecordDO signInRecord = signInRecordMapper.selectLastRecordByUserIdDesc(userId);
  75 + if (signInRecord == null) {
  76 + return vo;
  77 + }
  78 + vo.setTodaySignIn(DateUtils.isToday(signInRecord.getCreateTime()));
95 79
  80 + // 4. 校验今天是否签到,没有签到则直接返回
  81 + if (!vo.getTodaySignIn()) {
  82 + return vo;
96 } 83 }
  84 + // 4.1. 判断连续签到天数
  85 + List<MemberSignInRecordDO> signInRecords = signInRecordMapper.selectListByUserId(userId);
  86 + vo.setContinuousDay(calculateConsecutiveDays(signInRecords));
97 return vo; 87 return vo;
98 } 88 }
99 89
100 - private long daysBetween(LocalDate date1, LocalDate date2) {  
101 - return ChronoUnit.DAYS.between(date1, date2); 90 + /**
  91 + * 计算连续签到天数
  92 + *
  93 + * @param signInRecords 签到记录列表
  94 + * @return int 连续签到天数
  95 + */
  96 + public int calculateConsecutiveDays(List<MemberSignInRecordDO> signInRecords) {
  97 + int consecutiveDays = 1; // 初始连续天数为1
  98 + LocalDate previousDate = null;
  99 +
  100 + for (MemberSignInRecordDO record : signInRecords) {
  101 + LocalDate currentDate = record.getCreateTime().toLocalDate();
  102 +
  103 + if (previousDate != null) {
  104 + // 检查相邻两个日期是否连续
  105 + if (currentDate.minusDays(1).isEqual(previousDate)) {
  106 + consecutiveDays++;
  107 + } else {
  108 + // 如果日期不连续,停止遍历
  109 + break;
  110 + }
  111 + }
  112 +
  113 + previousDate = currentDate;
  114 + }
  115 +
  116 + return consecutiveDays;
102 } 117 }
103 118
104 @Override 119 @Override
@@ -108,7 +123,7 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService @@ -108,7 +123,7 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService
108 if (StringUtils.isNotBlank(pageReqVO.getNickname())) { 123 if (StringUtils.isNotBlank(pageReqVO.getNickname())) {
109 List<MemberUserRespDTO> users = memberUserApi.getUserListByNickname(pageReqVO.getNickname()); 124 List<MemberUserRespDTO> users = memberUserApi.getUserListByNickname(pageReqVO.getNickname());
110 // 如果查询用户结果为空直接返回无需继续查询 125 // 如果查询用户结果为空直接返回无需继续查询
111 - if (CollectionUtils.isEmpty(users)) { 126 + if (CollUtil.isEmpty(users)) {
112 return PageResult.empty(); 127 return PageResult.empty();
113 } 128 }
114 userIds = convertSet(users, MemberUserRespDTO::getId); 129 userIds = convertSet(users, MemberUserRespDTO::getId);
@@ -125,73 +140,40 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService @@ -125,73 +140,40 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService
125 @Override 140 @Override
126 @Transactional(rollbackFor = Exception.class) 141 @Transactional(rollbackFor = Exception.class)
127 public MemberSignInRecordDO createSignRecord(Long userId) { 142 public MemberSignInRecordDO createSignRecord(Long userId) {
128 - // 获取当前用户签到的最大天数  
129 - // TODO @xiaqing:db 操作,dou封装到 mapper 中;  
130 - // TODO @xiaqing:maxSignDay,是不是变量叫 lastRecord 会更容易理解哈;  
131 - MemberSignInRecordDO maxSignDay = signInRecordMapper.selectOne(new LambdaQueryWrapperX<MemberSignInRecordDO>()  
132 - .eq(MemberSignInRecordDO::getUserId, userId)  
133 - .orderByDesc(MemberSignInRecordDO::getDay)  
134 - .last("limit 1"));  
135 - // 判断是否重复签到  
136 - validSignDay(maxSignDay);  
137 -  
138 - // 1. 查询出当前签到的天数  
139 - MemberSignInRecordDO sign = new MemberSignInRecordDO().setUserId(userId); // TODO @xiaqing:应该使用 record 变量,会更合适  
140 - sign.setDay(1); // 设置签到初始化天数  
141 - sign.setPoint(0); // 设置签到积分默认为 0  
142 - sign.setExperience(0); // 设置签到经验默认为 0  
143 - // 如果不为空则修改当前签到对应的天数  
144 - // TODO @xiaqing:应该是要判断连续哈,就是昨天;  
145 - if (maxSignDay != null) {  
146 - sign.setDay(maxSignDay.getDay() + 1);  
147 - }  
148 - // 2. 获取签到对应的积分数  
149 - // 获取所有的签到规则,按照天数排序,只获取启用的 TODO @xiaqing:不要使用 signInConfigMapper 直接查询,而是要通过 SigninConfigService;  
150 - List<MemberSignInConfigDO> configDOList = signInConfigMapper.selectList(new LambdaQueryWrapperX<MemberSignInConfigDO>()  
151 - .eq(MemberSignInConfigDO::getStatus, CommonStatusEnum.ENABLE.getStatus())  
152 - .orderByAsc(MemberSignInConfigDO::getDay));  
153 - // 如果签到的天数大于最大启用的规则天数,直接给最大签到的积分数  
154 - // TODO @xiaqing:超过最大配置的天数,应该直接重置到第一天哈;  
155 - MemberSignInConfigDO lastConfig = configDOList.get(configDOList.size() - 1);  
156 - if (sign.getDay() > lastConfig.getDay()) {  
157 - sign.setPoint(lastConfig.getPoint());  
158 - sign.setExperience(lastConfig.getExperience());  
159 - } else {  
160 - configDOList.forEach(el -> {  
161 - // 循环匹配对应天数,设置对应积分数  
162 - // TODO @xiaqing:使用 equals;另外,这种不应该去遍历比较,从可读性来说,应该 CollUtil.findOne()  
163 - if (el.getDay() == sign.getDay()) {  
164 - sign.setPoint(el.getPoint());  
165 - sign.setExperience(el.getExperience());  
166 - }  
167 -  
168 - });  
169 - } 143 + // 1. 获取当前用户最近的签到
  144 + MemberSignInRecordDO lastRecord = signInRecordMapper.selectLastRecordByUserIdDesc(userId);
  145 + // 1.1. 判断是否重复签到
  146 + validateSigned(lastRecord);
  147 +
  148 + // 2. 获取当前用户最早的一次前端记录,用于计算今天是第几天签到
  149 + MemberSignInRecordDO firstRecord = signInRecordMapper.selectLastRecordByUserIdAsc(userId);
  150 + // 2.1. 获取所有的签到规则
  151 + List<MemberSignInConfigDO> signInConfigs = signInConfigService.getSignInConfigList(CommonStatusEnum.ENABLE.getStatus());
  152 + signInConfigs.sort(Comparator.comparing(MemberSignInConfigDO::getDay));
  153 + // 2.2. 组合数据
  154 + MemberSignInRecordDO record = MemberSignInRecordConvert.INSTANCE.convert(userId, firstRecord, signInConfigs);
170 155
171 // 3. 插入签到记录 156 // 3. 插入签到记录
172 - signInRecordMapper.insert(sign); 157 + signInRecordMapper.insert(record);
173 158
174 // 4. 增加积分 159 // 4. 增加积分
175 - if (!ObjectUtils.equalsAny(sign.getPoint(), null, 0)) {  
176 - pointRecordService.createPointRecord(userId, sign.getPoint(), MemberPointBizTypeEnum.SIGN, String.valueOf(sign.getId())); 160 + if (!ObjectUtils.equalsAny(record.getPoint(), null, 0)) {
  161 + pointRecordService.createPointRecord(userId, record.getPoint(), MemberPointBizTypeEnum.SIGN, String.valueOf(record.getId()));
177 } 162 }
178 // 5. 增加经验 163 // 5. 增加经验
179 - if (!ObjectUtils.equalsAny(sign.getPoint(), null, 0)) {  
180 - memberLevelService.addExperience(userId, sign.getExperience(), MemberExperienceBizTypeEnum.SIGN_IN, String.valueOf(sign.getId())); 164 + if (!ObjectUtils.equalsAny(record.getExperience(), null, 0)) {
  165 + memberLevelService.addExperience(userId, record.getExperience(), MemberExperienceBizTypeEnum.SIGN_IN, String.valueOf(record.getId()));
181 } 166 }
182 167
183 - return sign; 168 + return record;
184 } 169 }
185 170
186 - // TODO @xiaqing:校验使用 validate 动词哈;可以改成 validateSigned  
187 - private void validSignDay(MemberSignInRecordDO signInRecordDO) {  
188 - // TODO @xiaqing:代码格式:if () {} 要有括号哈  
189 - if (signInRecordDO == null) 171 + private void validateSigned(MemberSignInRecordDO signInRecordDO) {
  172 + if (signInRecordDO == null) {
190 return; 173 return;
191 - // TODO @xiaqing:可以直接使用 DateUtils.isToday()  
192 - LocalDate today = LocalDate.now();  
193 - if (today.equals(signInRecordDO.getCreateTime().toLocalDate())) {  
194 - throw exception(ErrorCodeConstants.SIGN_IN_RECORD_TODAY_EXISTS); 174 + }
  175 + if (DateUtils.isToday(signInRecordDO.getCreateTime())) {
  176 + throw exception(SIGN_IN_RECORD_TODAY_EXISTS);
195 } 177 }
196 } 178 }
197 179