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 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
... ... @@ -25,7 +25,6 @@ public class BannerPageReqVO extends PageParam {
25 25 @Schema(description = "标题")
26 26 private String title;
27 27  
28   -
29 28 @Schema(description = "状态")
30 29 @InEnum(CommonStatusEnum.class)
31 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 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&lt;BannerDO&gt; {
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&lt;BargainActivityDO&gt; {
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&lt;BargainActivityDO&gt; {
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&lt;BargainActivityDO&gt; {
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&lt;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&lt;CouponTemplateDO&gt; {
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&lt;CouponTemplateDO&gt; {
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&lt;SeckillActivityDO&gt; {
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
... ... @@ -93,6 +93,6 @@ public interface ArticleService {
93 93 *
94 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 111 }
112 112  
113 113 @Override
114   - public void addBrowseCount(Long id) {
  114 + public void addArticleBrowseCount(Long id) {
115 115 // 校验文章是否存在
116 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 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&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 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  
... ...