Bläddra i källkod

Merge branch 'feature/mall_product' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/mall_product

YunaiV 1 år sedan
förälder
incheckning
64b842ed94
22 ändrade filer med 168 tillägg och 92 borttagningar
  1. 0 4
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/vo/article/ArticleBaseVO.java
  2. 3 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/vo/article/ArticleRespVO.java
  3. 11 9
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.java
  4. 2 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/article/AppArticleCategoryController.java
  5. 9 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/article/AppArticleController.java
  6. 7 10
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java
  7. 1 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/article/ArticleDO.java
  8. 6 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/article/ArticleCategoryMapper.java
  9. 6 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/article/ArticleMapper.java
  10. 11 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainActivityMapper.java
  11. 12 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationActivityMapper.java
  12. 12 1
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java
  13. 3 7
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/article/ArticleCategoryServiceImpl.java
  14. 15 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/article/ArticleService.java
  15. 17 0
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/article/ArticleServiceImpl.java
  16. 5 3
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityService.java
  17. 3 3
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityServiceImpl.java
  18. 5 3
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java
  19. 4 2
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java
  20. 27 40
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java
  21. 5 3
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java
  22. 4 2
      yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java

+ 0 - 4
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/vo/article/ArticleBaseVO.java

@@ -34,10 +34,6 @@ public class ArticleBaseVO {
     @Schema(description = "文章简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "这是一个简介")
     private String introduction;
 
-    // TODO @puhui999:浏览量的字段,应该不是后端新增设置的哈;
-    @Schema(description = "浏览次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "111")
-    private String browseCount;
-
     @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
     @NotNull(message = "排序不能为空")
     private Integer sort;

+ 3 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/article/vo/article/ArticleRespVO.java

@@ -16,6 +16,9 @@ public class ArticleRespVO extends ArticleBaseVO {
     @Schema(description = "文章编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8606")
     private Long id;
 
+    @Schema(description = "浏览量", requiredMode = Schema.RequiredMode.REQUIRED, example = "99999")
+    private Integer browseCount;
+
     @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
     private LocalDateTime createTime;
 

+ 11 - 9
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.java

@@ -22,6 +22,7 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.Resource;
+import java.time.LocalDateTime;
 import java.util.*;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -63,10 +64,11 @@ public class AppActivityController {
         if (CollUtil.isEmpty(spuIds)) {
             return new ArrayList<>();
         }
+        LocalDateTime now = LocalDateTime.now();
         List<AppActivityRespVO> activityList = new ArrayList<>();
-        // 拼团活动
-        List<CombinationActivityDO> combinationActivities = combinationActivityService.getCombinationActivityBySpuIdsAndStatus(
-                spuIds, CommonStatusEnum.ENABLE.getStatus());
+        // 拼团活动-获取开启的且开始的且没有结束的活动
+        List<CombinationActivityDO> combinationActivities = combinationActivityService.getCombinationActivityBySpuIdsAndStatusAndDateTimeLt(
+                spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
         if (CollUtil.isNotEmpty(combinationActivities)) {
             combinationActivities.forEach(item -> {
                 activityList.add(new AppActivityRespVO().setId(item.getId())
@@ -74,9 +76,9 @@ public class AppActivityController {
                         .setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
             });
         }
-        // 秒杀活动
-        List<SeckillActivityDO> seckillActivities = seckillActivityService.getSeckillActivityBySpuIdsAndStatus(
-                spuIds, CommonStatusEnum.ENABLE.getStatus());
+        // 秒杀活动-获取开启的且开始的且没有结束的活动
+        List<SeckillActivityDO> seckillActivities = seckillActivityService.getSeckillActivityBySpuIdsAndStatusAndDateTimeLt(
+                spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
         if (CollUtil.isNotEmpty(seckillActivities)) {
             seckillActivities.forEach(item -> {
                 activityList.add(new AppActivityRespVO().setId(item.getId())
@@ -84,9 +86,9 @@ public class AppActivityController {
                         .setSpuId(item.getSpuId()).setStartTime(item.getStartTime()).setEndTime(item.getEndTime()));
             });
         }
-        // 砍价活动
-        List<BargainActivityDO> bargainActivities = bargainActivityService.getBargainActivityBySpuIdsAndStatus(
-                spuIds, CommonStatusEnum.ENABLE.getStatus());
+        // 砍价活动-获取开启的且开始的且没有结束的活动
+        List<BargainActivityDO> bargainActivities = bargainActivityService.getBargainActivityBySpuIdsAndStatusAndDateTimeLt(
+                spuIds, CommonStatusEnum.ENABLE.getStatus(), now);
         if (CollUtil.isNotEmpty(bargainActivities)) {
             bargainActivities.forEach(item -> {
                 activityList.add(new AppActivityRespVO().setId(item.getId())

+ 2 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/article/AppArticleCategoryController.java

@@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.Resource;
+import java.util.Comparator;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -31,7 +32,7 @@ public class AppArticleCategoryController {
     public CommonResult<List<AppArticleCategoryRespVO>> getArticleCategoryList() {
         List<ArticleCategoryDO> categoryList = articleCategoryService.getArticleCategoryListByStatus(
                 CommonStatusEnum.ENABLE.getStatus());
-        // TODO @puhui999:排序下
+        categoryList.sort(Comparator.comparing(ArticleCategoryDO::getSort).reversed()); // 按 sort 降序排列
         return success(ArticleCategoryConvert.INSTANCE.convertList04(categoryList));
     }
 

+ 9 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/article/AppArticleController.java

@@ -11,6 +11,7 @@ import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.Parameters;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PutMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
@@ -55,5 +56,12 @@ public class AppArticleController {
         return success(ArticleConvert.INSTANCE.convert01(articleService.getArticle(id)));
     }
 
-    // TODO @puhui999:增加浏览量,实现一个接口;先简单做,用户规模不大,只 +1 即可;ps:uniapp 那边也要接下噢
+    @PutMapping("/add-browseCount")
+    @Operation(summary = "增加文章浏览量")
+    @Parameter(name = "id", description = "文章编号", example = "1024")
+    public CommonResult<Boolean> addBrowseCount(@RequestParam("id") Long id) {
+        articleService.addBrowseCount(id);
+        return success(true);
+    }
+
 }

+ 7 - 10
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java

@@ -204,25 +204,22 @@ public interface CombinationActivityConvert {
         return respVO;
     }
 
+    @Mapping(target = "id", ignore = true)
+    CombinationRecordDO convert5(CombinationRecordDO headRecord);
+
     /**
      * 转换生成虚拟成团虚拟记录
      *
-     * @param virtualGroupHeadRecord 虚拟成团团长记录
+     * @param headRecord 虚拟成团团长记录
      * @return 虚拟记录列表
      */
-    // TODO @puhui999:1)方法名,建议改成 convertVirtualRecordList(CombinationRecordDO headRecord);2)第 220 到 225 可以搞成 mapstruct 一个方法,默认都 copy 进去,然后 set 第 226 到 232 的字段
-    default List<CombinationRecordDO> convertVirtualGroupList(CombinationRecordDO virtualGroupHeadRecord) {
+    default List<CombinationRecordDO> convertVirtualRecordList(CombinationRecordDO headRecord) {
         List<CombinationRecordDO> createRecords = new ArrayList<>();
         // 计算需要创建的虚拟成团记录数量
-        int count = virtualGroupHeadRecord.getUserSize() - virtualGroupHeadRecord.getUserCount();
+        int count = headRecord.getUserSize() - headRecord.getUserCount();
         for (int i = 0; i < count; i++) {
             // 基础信息和团长保持一致
-            CombinationRecordDO newRecord = new CombinationRecordDO().setActivityId(virtualGroupHeadRecord.getActivityId())
-                    .setCombinationPrice(virtualGroupHeadRecord.getCombinationPrice()).setSpuId(virtualGroupHeadRecord.getSpuId()).setSpuName(virtualGroupHeadRecord.getSpuName())
-                    .setPicUrl(virtualGroupHeadRecord.getPicUrl()).setSkuId(virtualGroupHeadRecord.getSkuId()).setHeadId(virtualGroupHeadRecord.getId())
-                    .setStatus(virtualGroupHeadRecord.getStatus()) // 状态保持和创建时一致,创建完成后会接着处理
-                    .setVirtualGroup(virtualGroupHeadRecord.getVirtualGroup()).setExpireTime(virtualGroupHeadRecord.getExpireTime())
-                    .setStartTime(virtualGroupHeadRecord.getStartTime()).setUserSize(virtualGroupHeadRecord.getUserSize()).setUserCount(virtualGroupHeadRecord.getUserCount());
+            CombinationRecordDO newRecord = convert5(headRecord);
             // 虚拟信息
             newRecord.setCount(0);
             newRecord.setUserId(0L);

+ 1 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/article/ArticleDO.java

@@ -54,7 +54,7 @@ public class ArticleDO extends BaseDO {
     /**
      * 浏览次数
      */
-    private String browseCount;
+    private Integer browseCount;
     /**
      * 排序
      */

+ 6 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/article/ArticleCategoryMapper.java

@@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.Ar
 import cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleCategoryDO;
 import org.apache.ibatis.annotations.Mapper;
 
+import java.util.List;
+
 /**
  * 文章分类 Mapper
  *
@@ -23,4 +25,8 @@ public interface ArticleCategoryMapper extends BaseMapperX<ArticleCategoryDO> {
                 .orderByDesc(ArticleCategoryDO::getSort));
     }
 
+    default List<ArticleCategoryDO> selectListByStatus(Integer status) {
+        return selectList(ArticleCategoryDO::getStatus, status);
+    }
+
 }

+ 6 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/article/ArticleMapper.java

@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticlePageReqVO;
 import cn.iocoder.yudao.module.promotion.controller.app.article.vo.article.AppArticlePageReqVO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleDO;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 
 import java.util.List;
@@ -42,5 +43,10 @@ public interface ArticleMapper extends BaseMapperX<ArticleDO> {
                 .eqIfPresent(ArticleDO::getCategoryId, pageReqVO.getCategoryId()));
     }
 
+    default void updateBrowseCount(Long id) {
+        update(null, new LambdaUpdateWrapper<ArticleDO>()
+                .eq(ArticleDO::getId, id)
+                .setSql("browse_count = browse_count + 1"));
+    }
 
 }

+ 11 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainActivityMapper.java

@@ -102,9 +102,19 @@ public interface BargainActivityMapper extends BaseMapperX<BargainActivityDO> {
                 .groupBy("spu_id"));
     }
 
-    default List<BargainActivityDO> selectListByIds(Collection<Long> ids) {
+    /**
+     * 获取指定活动编号的活动列表且
+     * 开始时间和结束时间小于给定时间 dateTime 的活动列表
+     *
+     * @param ids      活动编号
+     * @param dateTime 指定日期
+     * @return 活动列表
+     */
+    default List<BargainActivityDO> selectListByIdsAndDateTimeLt(Collection<Long> ids, LocalDateTime dateTime) {
         return selectList(new LambdaQueryWrapperX<BargainActivityDO>()
                 .in(BargainActivityDO::getId, ids)
+                .lt(BargainActivityDO::getStartTime, dateTime)
+                .lt(BargainActivityDO::getEndTime, dateTime)
                 .orderByDesc(BargainActivityDO::getCreateTime));
     }
 

+ 12 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationActivityMapper.java

@@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
+import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -58,9 +59,19 @@ public interface CombinationActivityMapper extends BaseMapperX<CombinationActivi
                 .groupBy("spu_id"));
     }
 
-    default List<CombinationActivityDO> selectListByIds(Collection<Long> ids) {
+    /**
+     * 获取指定活动编号的活动列表且
+     * 开始时间和结束时间小于给定时间 dateTime 的活动列表
+     *
+     * @param ids      活动编号
+     * @param dateTime 指定日期
+     * @return 活动列表
+     */
+    default List<CombinationActivityDO> selectListByIdsAndDateTimeLt(Collection<Long> ids, LocalDateTime dateTime) {
         return selectList(new LambdaQueryWrapperX<CombinationActivityDO>()
                 .in(CombinationActivityDO::getId, ids)
+                .lt(CombinationActivityDO::getStartTime, dateTime)
+                .lt(CombinationActivityDO::getEndTime, dateTime)
                 .orderByDesc(CombinationActivityDO::getCreateTime));
     }
 

+ 12 - 1
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java

@@ -13,6 +13,7 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
+import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -90,9 +91,19 @@ public interface SeckillActivityMapper extends BaseMapperX<SeckillActivityDO> {
                 .groupBy("spu_id"));
     }
 
-    default List<SeckillActivityDO> selectListByIds(Collection<Long> ids) {
+    /**
+     * 获取指定活动编号的活动列表且
+     * 开始时间和结束时间小于给定时间 dateTime 的活动列表
+     *
+     * @param ids      活动编号
+     * @param dateTime 指定日期
+     * @return 活动列表
+     */
+    default List<SeckillActivityDO> selectListByIdsAndDateTimeLt(Collection<Long> ids, LocalDateTime dateTime) {
         return selectList(new LambdaQueryWrapperX<SeckillActivityDO>()
                 .in(SeckillActivityDO::getId, ids)
+                .lt(SeckillActivityDO::getStartTime, dateTime)
+                .lt(SeckillActivityDO::getEndTime, dateTime)
                 .orderByDesc(SeckillActivityDO::getCreateTime));
     }
 

+ 3 - 7
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/article/ArticleCategoryServiceImpl.java

@@ -1,13 +1,11 @@
 package cn.iocoder.yudao.module.promotion.service.article;
 
-import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.ArticleCategoryCreateReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.ArticleCategoryPageReqVO;
 import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.category.ArticleCategoryUpdateReqVO;
 import cn.iocoder.yudao.module.promotion.convert.article.ArticleCategoryConvert;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleCategoryDO;
-import cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleDO;
 import cn.iocoder.yudao.module.promotion.dal.mysql.article.ArticleCategoryMapper;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
@@ -59,9 +57,8 @@ public class ArticleCategoryServiceImpl implements ArticleCategoryService {
         // 校验存在
         validateArticleCategoryExists(id);
         // 校验是不是存在关联文章
-        // TODO @puhui999:最好获得数量哈;
-        List<ArticleDO> articleList = articleService.getArticleByCategoryId(id);
-        if (CollUtil.isNotEmpty(articleList)) {
+        Long count = articleService.getArticleCountByCategoryId(id);
+        if (count > 0) {
             throw exception(ARTICLE_CATEGORY_DELETE_FAIL_HAVE_ARTICLES);
         }
 
@@ -87,8 +84,7 @@ public class ArticleCategoryServiceImpl implements ArticleCategoryService {
 
     @Override
     public List<ArticleCategoryDO> getArticleCategoryListByStatus(Integer status) {
-        // TODO @puhui999:selectListByStatus
-        return articleCategoryMapper.selectList(ArticleCategoryDO::getStatus, status);
+        return articleCategoryMapper.selectListByStatus(status);
     }
 
 }

+ 15 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/article/ArticleService.java

@@ -80,4 +80,19 @@ public interface ArticleService {
      */
     List<ArticleDO> getArticleByCategoryId(Long categoryId);
 
+    /**
+     * 获得指定分类的文章数量
+     *
+     * @param categoryId 文章分类编号
+     * @return 文章数量
+     */
+    Long getArticleCountByCategoryId(Long categoryId);
+
+    /**
+     * 增加文章浏览量
+     *
+     * @param id 文章编号
+     */
+    void addBrowseCount(Long id);
+
 }

+ 17 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/article/ArticleServiceImpl.java

@@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleCategoryD
 import cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleDO;
 import cn.iocoder.yudao.module.promotion.dal.mysql.article.ArticleMapper;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
@@ -41,6 +42,7 @@ public class ArticleServiceImpl implements ArticleService {
 
         // 插入
         ArticleDO article = ArticleConvert.INSTANCE.convert(createReqVO);
+        article.setBrowseCount(0); // 初始浏览量
         articleMapper.insert(article);
         // 返回
         return article.getId();
@@ -104,4 +106,19 @@ public class ArticleServiceImpl implements ArticleService {
         return articleMapper.selectList(ArticleDO::getCategoryId, categoryId);
     }
 
+    @Override
+    public Long getArticleCountByCategoryId(Long categoryId) {
+        return articleMapper.selectCount(ArticleDO::getCategoryId, categoryId);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void addBrowseCount(Long id) {
+        // 校验文章是否存在
+        validateArticleExists(id);
+
+        // 增加浏览次数 TODO 先简单做,用户规模不大,只 +1
+        articleMapper.updateBrowseCount(id);
+    }
+
 }

+ 5 - 3
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityService.java

@@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.Ba
 import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
 
 import javax.validation.Valid;
+import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.List;
 import java.util.Set;
@@ -102,10 +103,11 @@ public interface BargainActivityService {
     /**
      * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
      *
-     * @param spuIds spu 编号
-     * @param status 状态
+     * @param spuIds   spu 编号
+     * @param status   状态
+     * @param dateTime 日期时间
      * @return 砍价活动列表
      */
-    List<BargainActivityDO> getBargainActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status);
+    List<BargainActivityDO> getBargainActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
 
 }

+ 3 - 3
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityServiceImpl.java

@@ -182,15 +182,15 @@ public class BargainActivityServiceImpl implements BargainActivityService {
     }
 
     @Override
-    public List<BargainActivityDO> getBargainActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
+    public List<BargainActivityDO> getBargainActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime) {
         // 1. 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
-        // TODO @puhui999:我想了下,这种是不是只展示当前正在进行中的。已经结束、或者未开始的,可能没啥意义?
         List<Map<String, Object>> spuIdAndActivityIdMaps = bargainActivityMapper.selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(spuIds, status);
         if (CollUtil.isEmpty(spuIdAndActivityIdMaps)) {
             return Collections.emptyList();
         }
         // 2. 查询活动详情
-        return bargainActivityMapper.selectListByIds(convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")));
+        return bargainActivityMapper.selectListByIdsAndDateTimeLt(
+                convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")), dateTime);
     }
 
 }

+ 5 - 3
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityService.java

@@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationA
 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
 
 import javax.validation.Valid;
+import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -120,10 +121,11 @@ public interface CombinationActivityService {
     /**
      * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
      *
-     * @param spuIds spu 编号
-     * @param status 状态
+     * @param spuIds   spu 编号
+     * @param status   状态
+     * @param dateTime 日期时间
      * @return 拼团活动列表
      */
-    List<CombinationActivityDO> getCombinationActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status);
+    List<CombinationActivityDO> getCombinationActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
 
 }

+ 4 - 2
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java

@@ -25,6 +25,7 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
+import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -228,14 +229,15 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
     }
 
     @Override
-    public List<CombinationActivityDO> getCombinationActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
+    public List<CombinationActivityDO> getCombinationActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime) {
         // 1.查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
         List<Map<String, Object>> spuIdAndActivityIdMaps = combinationActivityMapper.selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(spuIds, status);
         if (CollUtil.isEmpty(spuIdAndActivityIdMaps)) {
             return Collections.emptyList();
         }
         // 2.查询活动详情
-        return combinationActivityMapper.selectListByIds(convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")));
+        return combinationActivityMapper.selectListByIdsAndDateTimeLt(
+                convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")), dateTime);
     }
 
 }

+ 27 - 40
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java

@@ -6,6 +6,7 @@ import cn.hutool.extra.spring.SpringUtil;
 import cn.iocoder.yudao.framework.common.core.KeyValue;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
 import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
@@ -22,6 +23,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationR
 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper;
 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
 import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
@@ -45,6 +47,7 @@ import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
  * @author HUIHUI
  */
 @Service
+@Slf4j
 @Validated
 public class CombinationRecordServiceImpl implements CombinationRecordService {
 
@@ -353,21 +356,21 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
 
         // 3. 逐个处理拼团,过期 or 虚拟成团
         KeyValue<Integer, Integer> keyValue = new KeyValue<>(0, 0); // 统计过期拼团和虚拟成团
-        for (CombinationRecordDO recordDO : headExpireRecords) {
-            // TODO @puhui999:recordDO 非必要的情况下,不用带 DO;直接 record;
+        for (CombinationRecordDO record : headExpireRecords) {
             try {
-                CombinationActivityDO activity = activityMap.get(recordDO.getActivityId());
+                CombinationActivityDO activity = activityMap.get(record.getActivityId());
                 if (activity == null || !activity.getVirtualGroup()) { // 取不到活动的或者不是虚拟拼团的
                     // 3.1. 处理过期的拼团
-                    getSelf().handleExpireRecord(recordDO);
+                    getSelf().handleExpireRecord(record);
                     keyValue.setKey(keyValue.getKey() + 1);
                 } else {
                     // 3.2. 处理虚拟成团
-                    getSelf().handleVirtualGroupRecord(recordDO);
+                    getSelf().handleVirtualGroupRecord(record);
                     keyValue.setValue(keyValue.getValue() + 1);
                 }
             } catch (Exception ignored) { // 处理异常继续循环
-                // TODO @puhui999:需要打印异常日志
+                log.error("[拼团过期 or 虚拟成团][record({}) 处理异常,请进行处理!record 数据是:{}]",
+                        record.getId(), JsonUtils.toJsonString(record));
             }
         }
         return keyValue;
@@ -376,68 +379,52 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
     /**
      * 处理过期拼团
      *
-     * @param headExpireRecord 过期拼团团长记录列表
+     * @param headRecord 过期拼团团长记录
      */
     @Transactional(rollbackFor = Exception.class)
-    public void handleExpireRecord(CombinationRecordDO headExpireRecord) {
-        // TODO @puhui999:这里的 null 其实不用判断。真出现,应该要处个 npe,因为就是要错哈;
-        // TODO @puhui999:headExpireRecord 可以简化成 headRecord
-        if (headExpireRecord == null) {
-            return;
-        }
-
+    public void handleExpireRecord(CombinationRecordDO headRecord) {
         // 1.更新拼团记录
-        List<CombinationRecordDO> headsAndRecords = updateBatchCombinationRecords(headExpireRecord,
+        List<CombinationRecordDO> headAndRecords = updateBatchCombinationRecords(headRecord,
                 CombinationRecordStatusEnum.FAILED);
-        // TODO @puhui999:这里的 null 其实不用判断。真出现,应该要处个 npe,因为就是要错哈;
-        if (headsAndRecords == null) {
-            return;
-        }
-
         // 2.订单取消
-        headsAndRecords.forEach(item -> tradeOrderApi.cancelPaidOrder(item.getUserId(), item.getOrderId()));
+        headAndRecords.forEach(item -> tradeOrderApi.cancelPaidOrder(item.getUserId(), item.getOrderId()));
     }
 
     /**
      * 处理虚拟拼团
      *
-     * @param virtualGroupHeadRecord 虚拟成团团长记录列表
+     * @param headRecord 虚拟成团团长记录
      */
     @Transactional(rollbackFor = Exception.class)
-    public void handleVirtualGroupRecord(CombinationRecordDO virtualGroupHeadRecord) {
-        // TODO @puhui999:这里的 null 其实不用判断。真出现,应该要处个 npe,因为就是要错哈;
-        // TODO @puhui999:headExpireRecord 可以简化成 headRecord
-        if (virtualGroupHeadRecord == null) {
-            return;
-        }
-
+    public void handleVirtualGroupRecord(CombinationRecordDO headRecord) {
         // 1. 团员补齐
-        combinationRecordMapper.insertBatch(CombinationActivityConvert.INSTANCE.convertVirtualGroupList(virtualGroupHeadRecord));
+        combinationRecordMapper.insertBatch(CombinationActivityConvert.INSTANCE.convertVirtualRecordList(headRecord));
         // 2. 更新拼团记录
-        updateBatchCombinationRecords(virtualGroupHeadRecord, CombinationRecordStatusEnum.SUCCESS);
+        updateBatchCombinationRecords(headRecord, CombinationRecordStatusEnum.SUCCESS);
     }
 
-    // TODO @puhui999:写下方法注释;
+    /**
+     * 更新拼团记录
+     *
+     * @param headRecord 团长记录
+     * @param status     状态-拼团失败 FAILED 成功 SUCCESS
+     * @return 整团记录(包含团长和团成员)
+     */
     private List<CombinationRecordDO> updateBatchCombinationRecords(CombinationRecordDO headRecord, CombinationRecordStatusEnum status) {
         // 1. 查询团成员(包含团长)
         List<CombinationRecordDO> records = combinationRecordMapper.selectListByHeadId(headRecord.getId());
-        // TODO @puhui999:是不是不用判断空哈;例如说,就一个团长,然后过期。
-        if (CollUtil.isEmpty(records)) {
-            return null;
-        }
         records.add(headRecord);// 把团长加进去
 
         // 2. 批量更新拼团记录 status 和 endTime
         List<CombinationRecordDO> updateRecords = new ArrayList<>(records.size());
         LocalDateTime now = LocalDateTime.now();
         records.forEach(item -> {
-            // TODO @puhui999:record 改成 updateRecord
-            CombinationRecordDO record = new CombinationRecordDO().setId(item.getId())
+            CombinationRecordDO updateRecord = new CombinationRecordDO().setId(item.getId())
                     .setStatus(status.getStatus()).setEndTime(now);
             if (CombinationRecordStatusEnum.isSuccess(status.getStatus())) { // 虚拟成团完事更改状态成功后还需要把参与人数修改为成团需要人数
-                record.setUserCount(record.getUserSize());
+                updateRecord.setUserCount(updateRecord.getUserSize());
             }
-            updateRecords.add(record);
+            updateRecords.add(updateRecord);
         });
         combinationRecordMapper.updateBatch(updateRecords);
         return records;

+ 5 - 3
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java

@@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityD
 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;
 
 import javax.validation.Valid;
+import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.List;
 
@@ -131,10 +132,11 @@ public interface SeckillActivityService {
     /**
      * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录
      *
-     * @param spuIds spu 编号
-     * @param status 状态
+     * @param spuIds   spu 编号
+     * @param status   状态
+     * @param dateTime 日期时间
      * @return 秒杀活动列表
      */
-    List<SeckillActivityDO> getSeckillActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status);
+    List<SeckillActivityDO> getSeckillActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime);
 
 }

+ 4 - 2
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java

@@ -28,6 +28,7 @@ import org.springframework.transaction.annotation.Transactional;
 import org.springframework.validation.annotation.Validated;
 
 import javax.annotation.Resource;
+import java.time.LocalDateTime;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -324,14 +325,15 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
     }
 
     @Override
-    public List<SeckillActivityDO> getSeckillActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
+    public List<SeckillActivityDO> getSeckillActivityBySpuIdsAndStatusAndDateTimeLt(Collection<Long> spuIds, Integer status, LocalDateTime dateTime) {
         // 1.查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
         List<Map<String, Object>> spuIdAndActivityIdMaps = seckillActivityMapper.selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(spuIds, status);
         if (CollUtil.isEmpty(spuIdAndActivityIdMaps)) {
             return Collections.emptyList();
         }
         // 2.查询活动详情
-        return seckillActivityMapper.selectListByIds(convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")));
+        return seckillActivityMapper.selectListByIdsAndDateTimeLt(
+                convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")), dateTime);
     }
 
 }