Commit 3e570b8b89678653d2ab259f233689c57bb1975c
Committed by
Gitee
!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 | 34 | // ========== 商品 SPU 1-008-005-000 ========== |
| 35 | 35 | ErrorCode SPU_NOT_EXISTS = new ErrorCode(1_008_005_000, "商品 SPU 不存在"); |
| 36 | 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 | 41 | // ========== 商品 SKU 1-008-006-000 ========== |
| 41 | 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 | 28 | <artifactId>yudao-module-member-api</artifactId> |
| 29 | 29 | <version>${revision}</version> |
| 30 | 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 | 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 | 11 | import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; |
| 12 | 12 | import cn.iocoder.yudao.module.product.service.sku.ProductSkuService; |
| 13 | 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 | 16 | import io.swagger.v3.oas.annotations.Operation; |
| 15 | 17 | import io.swagger.v3.oas.annotations.Parameter; |
| 16 | 18 | import io.swagger.v3.oas.annotations.tags.Tag; |
| ... | ... | @@ -43,6 +45,9 @@ public class ProductSpuController { |
| 43 | 45 | @Resource |
| 44 | 46 | private ProductSkuService productSkuService; |
| 45 | 47 | |
| 48 | + @Resource | |
| 49 | + private CouponTemplateApi couponTemplateApi; | |
| 50 | + | |
| 46 | 51 | @PostMapping("/create") |
| 47 | 52 | @Operation(summary = "创建商品 SPU") |
| 48 | 53 | @PreAuthorize("@ss.hasPermission('product:spu:create')") |
| ... | ... | @@ -87,7 +92,10 @@ public class ProductSpuController { |
| 87 | 92 | } |
| 88 | 93 | // 查询商品 SKU |
| 89 | 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 | 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 | 96 | @NotNull(message = "商品赠送积分不能为空") |
| 97 | 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 | 102 | @Schema(description = "分销类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") |
| 103 | 103 | @NotNull(message = "商品分销类型不能为空") |
| 104 | 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 | 107 | private List<Integer> activityOrders; |
| 108 | 108 | |
| 109 | 109 | // ========== 统计相关字段 ========= |
| 110 | 110 | |
| 111 | - @Schema(description = "虚拟销量", example = "芋道") | |
| 111 | + @Schema(description = "虚拟销量", example = "66") | |
| 112 | 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 | 12 | import cn.iocoder.yudao.module.product.dal.dataobject.sku.ProductSkuDO; |
| 13 | 13 | import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; |
| 14 | 14 | import cn.iocoder.yudao.module.product.enums.DictTypeConstants; |
| 15 | +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponTemplateRespDTO; | |
| 15 | 16 | import org.mapstruct.Mapper; |
| 16 | 17 | import org.mapstruct.Mapping; |
| 17 | 18 | import org.mapstruct.Named; |
| ... | ... | @@ -100,10 +101,14 @@ public interface ProductSpuConvert { |
| 100 | 101 | |
| 101 | 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 | 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 | 194 | * 对应 PromotionTypeEnum 枚举 |
| 195 | 195 | */ |
| 196 | 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 | 16 | import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum; |
| 17 | 17 | import cn.iocoder.yudao.module.product.service.brand.ProductBrandService; |
| 18 | 18 | import cn.iocoder.yudao.module.product.service.category.ProductCategoryService; |
| 19 | -import cn.iocoder.yudao.module.product.service.property.ProductPropertyValueService; | |
| 20 | 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 | 23 | import com.google.common.collect.Maps; |
| 22 | 24 | import org.springframework.context.annotation.Lazy; |
| 23 | 25 | import org.springframework.stereotype.Service; |
| ... | ... | @@ -26,10 +28,10 @@ import org.springframework.validation.annotation.Validated; |
| 26 | 28 | |
| 27 | 29 | import javax.annotation.Resource; |
| 28 | 30 | import java.util.*; |
| 31 | +import java.util.stream.Collectors; | |
| 29 | 32 | |
| 30 | 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 | 35 | import static cn.iocoder.yudao.module.product.dal.dataobject.category.ProductCategoryDO.CATEGORY_LEVEL; |
| 34 | 36 | import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*; |
| 35 | 37 | |
| ... | ... | @@ -52,9 +54,10 @@ public class ProductSpuServiceImpl implements ProductSpuService { |
| 52 | 54 | private ProductBrandService brandService; |
| 53 | 55 | @Resource |
| 54 | 56 | private ProductCategoryService categoryService; |
| 57 | + | |
| 55 | 58 | @Resource |
| 56 | - @Lazy // 循环依赖,避免报错 | |
| 57 | - private ProductPropertyValueService productPropertyValueService; | |
| 59 | + @Lazy | |
| 60 | + private CouponTemplateApi couponTemplateApi; | |
| 58 | 61 | |
| 59 | 62 | @Override |
| 60 | 63 | @Transactional(rollbackFor = Exception.class) |
| ... | ... | @@ -62,6 +65,9 @@ public class ProductSpuServiceImpl implements ProductSpuService { |
| 62 | 65 | // 校验分类、品牌 |
| 63 | 66 | validateCategory(createReqVO.getCategoryId()); |
| 64 | 67 | brandService.validateProductBrand(createReqVO.getBrandId()); |
| 68 | + // 校验优惠券 | |
| 69 | + Set<Long> giveCouponTemplateIds = convertSet(createReqVO.getGiveCouponTemplates(), ProductSpuCreateReqVO.GiveCouponTemplate::getId); | |
| 70 | + validateCouponTemplate(giveCouponTemplateIds); | |
| 65 | 71 | // 校验 SKU |
| 66 | 72 | List<ProductSkuCreateOrUpdateReqVO> skuSaveReqList = createReqVO.getSkus(); |
| 67 | 73 | productSkuService.validateSkuList(skuSaveReqList, createReqVO.getSpecType()); |
| ... | ... | @@ -69,6 +75,8 @@ public class ProductSpuServiceImpl implements ProductSpuService { |
| 69 | 75 | ProductSpuDO spu = ProductSpuConvert.INSTANCE.convert(createReqVO); |
| 70 | 76 | // 初始化 SPU 中 SKU 相关属性 |
| 71 | 77 | initSpuFromSkus(spu, skuSaveReqList); |
| 78 | + // 设置优惠券 | |
| 79 | + spu.setGiveCouponTemplateIds(CollUtil.newArrayList(giveCouponTemplateIds)); | |
| 72 | 80 | // 插入 SPU |
| 73 | 81 | productSpuMapper.insert(spu); |
| 74 | 82 | // 插入 SKU |
| ... | ... | @@ -85,6 +93,9 @@ public class ProductSpuServiceImpl implements ProductSpuService { |
| 85 | 93 | // 校验分类、品牌 |
| 86 | 94 | validateCategory(updateReqVO.getCategoryId()); |
| 87 | 95 | brandService.validateProductBrand(updateReqVO.getBrandId()); |
| 96 | + // 校验优惠券 | |
| 97 | + Set<Long> giveCouponTemplateIds = convertSet(updateReqVO.getGiveCouponTemplates(), ProductSpuUpdateReqVO.GiveCouponTemplate::getId); | |
| 98 | + validateCouponTemplate(giveCouponTemplateIds); | |
| 88 | 99 | // 校验SKU |
| 89 | 100 | List<ProductSkuCreateOrUpdateReqVO> skuSaveReqList = updateReqVO.getSkus(); |
| 90 | 101 | productSkuService.validateSkuList(skuSaveReqList, updateReqVO.getSpecType()); |
| ... | ... | @@ -92,6 +103,8 @@ public class ProductSpuServiceImpl implements ProductSpuService { |
| 92 | 103 | // 更新 SPU |
| 93 | 104 | ProductSpuDO updateObj = ProductSpuConvert.INSTANCE.convert(updateReqVO); |
| 94 | 105 | initSpuFromSkus(updateObj, skuSaveReqList); |
| 106 | + // 设置优惠券 | |
| 107 | + updateObj.setGiveCouponTemplateIds(CollUtil.newArrayList(giveCouponTemplateIds)); | |
| 95 | 108 | productSpuMapper.updateById(updateObj); |
| 96 | 109 | // 批量更新 SKU |
| 97 | 110 | productSkuService.updateSkuList(updateObj.getId(), updateReqVO.getSkus()); |
| ... | ... | @@ -125,6 +138,10 @@ public class ProductSpuServiceImpl implements ProductSpuService { |
| 125 | 138 | // 默认商品浏览量 |
| 126 | 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 | 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 | 167 | @Override |
| 144 | 168 | public List<ProductSpuDO> validateSpuList(Collection<Long> ids) { |
| 145 | 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 | 27 | @NotNull(message = "图片地址不能为空") |
| 28 | 28 | private String picUrl; |
| 29 | 29 | |
| 30 | + @Schema(description = "position", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 31 | + @NotNull(message = "position 不能为空") | |
| 32 | + private Integer position; | |
| 33 | + | |
| 30 | 34 | @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED) |
| 31 | 35 | @NotNull(message = "排序不能为空") |
| 32 | 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
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 | 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 | 60 | @Operation(summary = "增加文章浏览量") |
| 62 | 61 | @Parameter(name = "id", description = "文章编号", example = "1024") |
| 63 | 62 | public CommonResult<Boolean> addBrowseCount(@RequestParam("id") Long id) { |
| 64 | - // TODO @puhui999:addArticleBrowseCount | |
| 65 | - articleService.addBrowseCount(id); | |
| 63 | + articleService.addArticleBrowseCount(id); | |
| 66 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | 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 | 10 | import io.swagger.v3.oas.annotations.Operation; |
| 11 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 6 | 12 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 7 | 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 | 18 | import java.util.List; |
| 15 | 19 | |
| 16 | 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 | 23 | @RestController |
| 19 | 24 | @RequestMapping("/promotion/banner") |
| ... | ... | @@ -21,22 +26,39 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 21 | 26 | @Validated |
| 22 | 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 | 44 | @GetMapping("/list") |
| 25 | 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 | 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 | 9 | @Data |
| 10 | 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 | 19 | @Schema(description = "跳转链接", requiredMode = Schema.RequiredMode.REQUIRED) |
| 13 | 20 | @NotNull(message = "跳转链接不能为空") |
| 14 | 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 | 4 | import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerCreateReqVO; |
| 5 | 5 | import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerRespVO; |
| 6 | 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 | 8 | import cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO; |
| 8 | 9 | import org.mapstruct.Mapper; |
| 9 | 10 | import org.mapstruct.factory.Mappers; |
| ... | ... | @@ -25,4 +26,6 @@ public interface BannerConvert { |
| 25 | 26 | |
| 26 | 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 | 2 | |
| 3 | 3 | import cn.hutool.core.map.MapUtil; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponTemplateRespDTO; | |
| 5 | 6 | import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateCreateReqVO; |
| 6 | 7 | import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO; |
| 7 | 8 | import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplateRespVO; |
| ... | ... | @@ -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 | 1 | package cn.iocoder.yudao.module.promotion.dal.dataobject.banner; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 3 | 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 5 | +import cn.iocoder.yudao.module.promotion.enums.banner.BannerPositionEnum; | |
| 4 | 6 | import com.baomidou.mybatisplus.annotation.TableName; |
| 5 | 7 | import lombok.*; |
| 6 | 8 | |
| ... | ... | @@ -40,14 +42,25 @@ public class BannerDO extends BaseDO { |
| 40 | 42 | private Integer sort; |
| 41 | 43 | |
| 42 | 44 | /** |
| 43 | - * 状态 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum} | |
| 45 | + * 状态 {@link CommonStatusEnum} | |
| 44 | 46 | */ |
| 45 | 47 | private Integer status; |
| 48 | + | |
| 49 | + /** | |
| 50 | + * 定位 {@link BannerPositionEnum} | |
| 51 | + */ | |
| 52 | + private Integer position; | |
| 53 | + | |
| 46 | 54 | /** |
| 47 | 55 | * 备注 |
| 48 | 56 | */ |
| 49 | 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 | 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 6 | 6 | import cn.iocoder.yudao.module.promotion.controller.admin.banner.vo.BannerPageReqVO; |
| 7 | 7 | import cn.iocoder.yudao.module.promotion.dal.dataobject.banner.BannerDO; |
| 8 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | |
| 8 | 9 | import org.apache.ibatis.annotations.Mapper; |
| 9 | 10 | |
| 11 | +import java.util.List; | |
| 12 | + | |
| 10 | 13 | /** |
| 11 | 14 | * Banner Mapper |
| 12 | 15 | * |
| ... | ... | @@ -23,4 +26,14 @@ public interface BannerMapper extends BaseMapperX<BannerDO> { |
| 23 | 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<BargainActivityDO> { |
| 86 | 86 | .last("LIMIT " + count)); |
| 87 | 87 | } |
| 88 | 88 | |
| 89 | - // TODO @puhui999:是不是返回 BargainActivityDO 更干净哈?分组后返回 DO 的话需要联表查询 | |
| 90 | 89 | /** |
| 91 | 90 | * 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号 |
| 92 | 91 | * |
| ... | ... | @@ -102,7 +101,6 @@ public interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> { |
| 102 | 101 | .groupBy("spu_id")); |
| 103 | 102 | } |
| 104 | 103 | |
| 105 | - // TODO @puhui999:是不是只要 endTime 小于就可以啦; | |
| 106 | 104 | /** |
| 107 | 105 | * 获取指定活动编号的活动列表且 |
| 108 | 106 | * 开始时间和结束时间小于给定时间 dateTime 的活动列表 |
| ... | ... | @@ -115,7 +113,7 @@ public interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> { |
| 115 | 113 | return selectList(new LambdaQueryWrapperX<BargainActivityDO>() |
| 116 | 114 | .in(BargainActivityDO::getId, ids) |
| 117 | 115 | .lt(BargainActivityDO::getStartTime, dateTime) |
| 118 | - .lt(BargainActivityDO::getEndTime, dateTime) | |
| 116 | + .gt(BargainActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动 | |
| 119 | 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<CombinationActivi |
| 71 | 71 | return selectList(new LambdaQueryWrapperX<CombinationActivityDO>() |
| 72 | 72 | .in(CombinationActivityDO::getId, ids) |
| 73 | 73 | .lt(CombinationActivityDO::getStartTime, dateTime) |
| 74 | - .lt(CombinationActivityDO::getEndTime, dateTime) | |
| 74 | + .gt(CombinationActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动 | |
| 75 | 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 | 8 | import cn.iocoder.yudao.module.promotion.controller.admin.coupon.vo.template.CouponTemplatePageReqVO; |
| 9 | 9 | import cn.iocoder.yudao.module.promotion.dal.dataobject.coupon.CouponTemplateDO; |
| 10 | 10 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| 11 | +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | |
| 11 | 12 | import org.apache.ibatis.annotations.Mapper; |
| 12 | -import org.apache.ibatis.annotations.Param; | |
| 13 | 13 | |
| 14 | 14 | import java.time.LocalDateTime; |
| 15 | +import java.util.Collection; | |
| 15 | 16 | import java.util.List; |
| 16 | 17 | import java.util.function.Consumer; |
| 17 | 18 | |
| ... | ... | @@ -39,7 +40,11 @@ public interface CouponTemplateMapper extends BaseMapperX<CouponTemplateDO> { |
| 39 | 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 | 49 | default List<CouponTemplateDO> selectListByTakeType(Integer takeType) { |
| 45 | 50 | return selectList(CouponTemplateDO::getTakeType, takeType); |
| ... | ... | @@ -70,4 +75,8 @@ public interface CouponTemplateMapper extends BaseMapperX<CouponTemplateDO> { |
| 70 | 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<SeckillActivityDO> { |
| 103 | 103 | return selectList(new LambdaQueryWrapperX<SeckillActivityDO>() |
| 104 | 104 | .in(SeckillActivityDO::getId, ids) |
| 105 | 105 | .lt(SeckillActivityDO::getStartTime, dateTime) |
| 106 | - .lt(SeckillActivityDO::getEndTime, dateTime) | |
| 106 | + .gt(SeckillActivityDO::getEndTime, dateTime)// 开始时间 < 指定时间 < 结束时间,也就是说获取指定时间段的活动 | |
| 107 | 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
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/article/ArticleServiceImpl.java
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 | 47 | BannerDO getBanner(Long id); |
| 48 | 48 | |
| 49 | 49 | /** |
| 50 | - * 获得所有 Banner列表 | |
| 51 | - * @return Banner列表 | |
| 52 | - */ | |
| 53 | - List<BannerDO> getBannerList(); | |
| 54 | - | |
| 55 | - /** | |
| 56 | 50 | * 获得 Banner 分页 |
| 57 | 51 | * |
| 58 | 52 | * @param pageReqVO 分页查询 |
| ... | ... | @@ -60,4 +54,19 @@ public interface BannerService { |
| 60 | 54 | */ |
| 61 | 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 | 66 | } |
| 67 | 67 | |
| 68 | 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 | 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 | 369 | keyValue.setValue(keyValue.getValue() + 1); |
| 370 | 370 | } |
| 371 | 371 | } catch (Exception ignored) { // 处理异常继续循环 |
| 372 | - // TODO @puhui999:拼团过期 or 虚拟成团 可以改成 expireCombinationRecord;因为找方法更容易一些; | |
| 373 | - log.error("[拼团过期 or 虚拟成团][record({}) 处理异常,请进行处理!record 数据是:{}]", | |
| 372 | + log.error("[expireCombinationRecord][record({}) 处理异常,请进行处理!record 数据是:{}]", | |
| 374 | 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 | 8 | import cn.iocoder.yudao.module.promotion.enums.coupon.CouponTakeTypeEnum; |
| 9 | 9 | |
| 10 | 10 | import javax.validation.Valid; |
| 11 | +import java.util.Collection; | |
| 11 | 12 | import java.util.List; |
| 12 | 13 | |
| 13 | 14 | /** |
| ... | ... | @@ -91,4 +92,12 @@ public interface CouponTemplateService { |
| 91 | 92 | List<CouponTemplateDO> getCouponTemplateList(List<Integer> canTakeTypes, Integer productScope, |
| 92 | 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 | 16 | import org.springframework.validation.annotation.Validated; |
| 17 | 17 | |
| 18 | 18 | import javax.annotation.Resource; |
| 19 | +import java.util.Collection; | |
| 19 | 20 | import java.util.List; |
| 20 | 21 | import java.util.Objects; |
| 21 | 22 | |
| ... | ... | @@ -126,4 +127,9 @@ public class CouponTemplateServiceImpl implements CouponTemplateService { |
| 126 | 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 | 31 | @Resource |
| 32 | 32 | private MemberSignInRecordService signInRecordService; |
| 33 | 33 | |
| 34 | - // TODO 芋艿:临时 mock => UserSignController.getUserInfo | |
| 35 | 34 | @GetMapping("/get-summary") |
| 36 | 35 | @Operation(summary = "获得个人签到统计") |
| 37 | 36 | @PreAuthenticated |
| 38 | 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 | 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 | 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 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 6 | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| 5 | 7 | import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; |
| 6 | 8 | import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordRespVO; |
| 7 | 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 | 11 | import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO; |
| 9 | 12 | import org.mapstruct.Mapper; |
| 10 | 13 | import org.mapstruct.factory.Mappers; |
| 11 | 14 | |
| 15 | +import java.time.LocalDateTime; | |
| 16 | +import java.time.temporal.ChronoUnit; | |
| 12 | 17 | import java.util.List; |
| 13 | 18 | import java.util.Map; |
| 14 | 19 | |
| ... | ... | @@ -32,10 +37,37 @@ public interface MemberSignInRecordConvert { |
| 32 | 37 | memberUserRespDTO -> record.setNickname(memberUserRespDTO.getNickname()))); |
| 33 | 38 | return voPageResult; |
| 34 | 39 | } |
| 40 | + | |
| 35 | 41 | PageResult<MemberSignInRecordRespVO> convertPage(PageResult<MemberSignInRecordDO> pageResult); |
| 36 | 42 | |
| 37 | 43 | PageResult<AppMemberSignInRecordRespVO> convertPage02(PageResult<MemberSignInRecordDO> pageResult); |
| 38 | 44 | |
| 39 | 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 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 7 | 7 | import cn.iocoder.yudao.module.member.controller.admin.signin.vo.record.MemberSignInRecordPageReqVO; |
| 8 | 8 | import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO; |
| 9 | +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | |
| 9 | 10 | import org.apache.ibatis.annotations.Mapper; |
| 10 | 11 | |
| 11 | 12 | import java.util.List; |
| ... | ... | @@ -35,9 +36,45 @@ public interface MemberSignInRecordMapper extends BaseMapperX<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 | 78 | .eq(MemberSignInRecordDO::getUserId, userId) |
| 42 | 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 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 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 | 7 | import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO; |
| 8 | 8 | |
| 9 | 9 | /** |
| ... | ... | @@ -24,7 +24,7 @@ public interface MemberSignInRecordService { |
| 24 | 24 | /** |
| 25 | 25 | * 【会员】获得签到记录分页 |
| 26 | 26 | * |
| 27 | - * @param userId 用户编号 | |
| 27 | + * @param userId 用户编号 | |
| 28 | 28 | * @param pageParam 分页查询 |
| 29 | 29 | * @return 签到记录分页 |
| 30 | 30 | */ |
| ... | ... | @@ -44,7 +44,7 @@ public interface MemberSignInRecordService { |
| 44 | 44 | * @param userId 用户编号 |
| 45 | 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 | 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 | 5 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 4 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 5 | 7 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 8 | +import cn.iocoder.yudao.framework.common.util.date.DateUtils; | |
| 6 | 9 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| 7 | -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | |
| 8 | 10 | import cn.iocoder.yudao.module.member.api.user.MemberUserApi; |
| 9 | 11 | import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; |
| 10 | 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 | 15 | import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInConfigDO; |
| 13 | 16 | import cn.iocoder.yudao.module.member.dal.dataobject.signin.MemberSignInRecordDO; |
| 14 | -import cn.iocoder.yudao.module.member.dal.mysql.signin.MemberSignInConfigMapper; | |
| 15 | 17 | import cn.iocoder.yudao.module.member.dal.mysql.signin.MemberSignInRecordMapper; |
| 16 | -import cn.iocoder.yudao.module.member.enums.ErrorCodeConstants; | |
| 17 | 18 | import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum; |
| 18 | 19 | import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; |
| 19 | 20 | import cn.iocoder.yudao.module.member.service.level.MemberLevelService; |
| ... | ... | @@ -21,17 +22,17 @@ import cn.iocoder.yudao.module.member.service.point.MemberPointRecordService; |
| 21 | 22 | import org.apache.commons.lang3.StringUtils; |
| 22 | 23 | import org.springframework.stereotype.Service; |
| 23 | 24 | import org.springframework.transaction.annotation.Transactional; |
| 24 | -import org.springframework.util.CollectionUtils; | |
| 25 | 25 | import org.springframework.validation.annotation.Validated; |
| 26 | 26 | |
| 27 | 27 | import javax.annotation.Resource; |
| 28 | 28 | import java.time.LocalDate; |
| 29 | -import java.time.temporal.ChronoUnit; | |
| 29 | +import java.util.Comparator; | |
| 30 | 30 | import java.util.List; |
| 31 | 31 | import java.util.Set; |
| 32 | 32 | |
| 33 | 33 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 34 | 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 | 38 | * 签到记录 Service 实现类 |
| ... | ... | @@ -45,7 +46,7 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService |
| 45 | 46 | @Resource |
| 46 | 47 | private MemberSignInRecordMapper signInRecordMapper; |
| 47 | 48 | @Resource |
| 48 | - private MemberSignInConfigMapper signInConfigMapper; | |
| 49 | + private MemberSignInConfigService signInConfigService; | |
| 49 | 50 | @Resource |
| 50 | 51 | private MemberPointRecordService pointRecordService; |
| 51 | 52 | @Resource |
| ... | ... | @@ -55,50 +56,64 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService |
| 55 | 56 | private MemberUserApi memberUserApi; |
| 56 | 57 | |
| 57 | 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 | 62 | vo.setTotalDay(0); |
| 61 | 63 | vo.setContinuousDay(0); |
| 62 | 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 | 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 | 119 | @Override |
| ... | ... | @@ -108,7 +123,7 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService |
| 108 | 123 | if (StringUtils.isNotBlank(pageReqVO.getNickname())) { |
| 109 | 124 | List<MemberUserRespDTO> users = memberUserApi.getUserListByNickname(pageReqVO.getNickname()); |
| 110 | 125 | // 如果查询用户结果为空直接返回无需继续查询 |
| 111 | - if (CollectionUtils.isEmpty(users)) { | |
| 126 | + if (CollUtil.isEmpty(users)) { | |
| 112 | 127 | return PageResult.empty(); |
| 113 | 128 | } |
| 114 | 129 | userIds = convertSet(users, MemberUserRespDTO::getId); |
| ... | ... | @@ -125,73 +140,40 @@ public class MemberSignInRecordServiceImpl implements MemberSignInRecordService |
| 125 | 140 | @Override |
| 126 | 141 | @Transactional(rollbackFor = Exception.class) |
| 127 | 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 | 156 | // 3. 插入签到记录 |
| 172 | - signInRecordMapper.insert(sign); | |
| 157 | + signInRecordMapper.insert(record); | |
| 173 | 158 | |
| 174 | 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 | 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 | 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 | ... | ... |