mirror of
				https://gitee.com/hhyykk/ipms-sjy.git
				synced 2025-11-04 04:08:43 +08:00 
			
		
		
		
	营销活动:完善 review 提到的问题
This commit is contained in:
		@@ -111,9 +111,10 @@ public interface ErrorCodeConstants {
 | 
			
		||||
    ErrorCode BARGAIN_HELP_CREATE_FAIL_HELP_EXISTS = new ErrorCode(1_013_014_004, "助力失败,您已经助力过了");
 | 
			
		||||
 | 
			
		||||
    // ========== 文章分类 1-013-015-000 ==========
 | 
			
		||||
    ErrorCode ARTICLE_CATEGORY_NOT_EXISTS = new ErrorCode(1_013_015_000, "分类不存在");
 | 
			
		||||
    ErrorCode ARTICLE_CATEGORY_NOT_EXISTS = new ErrorCode(1_013_015_000, "文章分类不存在");
 | 
			
		||||
    ErrorCode ARTICLE_CATEGORY_DELETE_FAIL_HAVE_ARTICLES = new ErrorCode(1_013_015_001, "文章分类删除失败,存在关联文章");
 | 
			
		||||
 | 
			
		||||
    // ========== 文章管理 1-013-016-000 ==========
 | 
			
		||||
    ErrorCode ARTICLE_NOT_EXISTS = new ErrorCode(1_013_016_000, "文章管理不存在");
 | 
			
		||||
    ErrorCode ARTICLE_NOT_EXISTS = new ErrorCode(1_013_016_000, "文章不存在");
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleCreateReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleRespVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleUpdateReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.app.article.vo.article.AppArticleRespVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleDO;
 | 
			
		||||
import org.mapstruct.Mapper;
 | 
			
		||||
import org.mapstruct.factory.Mappers;
 | 
			
		||||
@@ -30,4 +31,10 @@ public interface ArticleConvert {
 | 
			
		||||
 | 
			
		||||
    PageResult<ArticleRespVO> convertPage(PageResult<ArticleDO> page);
 | 
			
		||||
 | 
			
		||||
    AppArticleRespVO convert01(ArticleDO article);
 | 
			
		||||
 | 
			
		||||
    PageResult<AppArticleRespVO> convertPage02(PageResult<ArticleDO> articlePage);
 | 
			
		||||
 | 
			
		||||
    List<AppArticleRespVO> convertList03(List<ArticleDO> articleCategoryListByRecommendHotAndRecommendBanner);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -207,22 +207,21 @@ public interface CombinationActivityConvert {
 | 
			
		||||
    /**
 | 
			
		||||
     * 转换生成虚拟成团虚拟记录
 | 
			
		||||
     *
 | 
			
		||||
     * @param virtualGroupHeadRecords 虚拟成团团长记录列表
 | 
			
		||||
     * @param virtualGroupHeadRecord 虚拟成团团长记录
 | 
			
		||||
     * @return 虚拟记录列表
 | 
			
		||||
     */
 | 
			
		||||
    default List<CombinationRecordDO> convertVirtualGroupList(List<CombinationRecordDO> virtualGroupHeadRecords) {
 | 
			
		||||
    default List<CombinationRecordDO> convertVirtualGroupList(CombinationRecordDO virtualGroupHeadRecord) {
 | 
			
		||||
        List<CombinationRecordDO> createRecords = new ArrayList<>();
 | 
			
		||||
        virtualGroupHeadRecords.forEach(headRecord -> {
 | 
			
		||||
        // 计算需要创建的虚拟成团记录数量
 | 
			
		||||
            int count = headRecord.getUserSize() - headRecord.getUserCount();
 | 
			
		||||
        int count = virtualGroupHeadRecord.getUserSize() - virtualGroupHeadRecord.getUserCount();
 | 
			
		||||
        for (int i = 0; i < count; i++) {
 | 
			
		||||
            // 基础信息和团长保持一致
 | 
			
		||||
                CombinationRecordDO newRecord = new CombinationRecordDO().setActivityId(headRecord.getActivityId())
 | 
			
		||||
                        .setCombinationPrice(headRecord.getCombinationPrice()).setSpuId(headRecord.getSpuId()).setSpuName(headRecord.getSpuName())
 | 
			
		||||
                        .setPicUrl(headRecord.getPicUrl()).setSkuId(headRecord.getSkuId()).setHeadId(headRecord.getId())
 | 
			
		||||
                        .setStatus(headRecord.getStatus()) // 状态保持和创建时一致,创建完成后会接着处理
 | 
			
		||||
                        .setVirtualGroup(headRecord.getVirtualGroup()).setExpireTime(headRecord.getExpireTime())
 | 
			
		||||
                        .setStartTime(headRecord.getStartTime()).setUserSize(headRecord.getUserSize()).setUserCount(headRecord.getUserCount());
 | 
			
		||||
            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());
 | 
			
		||||
            // 虚拟信息
 | 
			
		||||
            newRecord.setCount(0);
 | 
			
		||||
            newRecord.setUserId(0L);
 | 
			
		||||
@@ -231,7 +230,6 @@ public interface CombinationActivityConvert {
 | 
			
		||||
            newRecord.setOrderId(0L);
 | 
			
		||||
            createRecords.add(newRecord);
 | 
			
		||||
        }
 | 
			
		||||
        });
 | 
			
		||||
        return createRecords;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,9 +4,12 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
 | 
			
		||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.admin.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 org.apache.ibatis.annotations.Mapper;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 文章管理 Mapper
 | 
			
		||||
 *
 | 
			
		||||
@@ -28,4 +31,16 @@ public interface ArticleMapper extends BaseMapperX<ArticleDO> {
 | 
			
		||||
                .orderByDesc(ArticleDO::getId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default List<ArticleDO> selectList(Boolean recommendHot, Boolean recommendBanner) {
 | 
			
		||||
        return selectList(new LambdaQueryWrapperX<ArticleDO>()
 | 
			
		||||
                .eqIfPresent(ArticleDO::getRecommendHot, recommendHot)
 | 
			
		||||
                .eqIfPresent(ArticleDO::getRecommendBanner, recommendBanner));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default PageResult<ArticleDO> selectPage(AppArticlePageReqVO pageReqVO) {
 | 
			
		||||
        return selectPage(pageReqVO, new LambdaQueryWrapperX<ArticleDO>()
 | 
			
		||||
                .eqIfPresent(ArticleDO::getCategoryId, pageReqVO.getCategoryId()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -127,11 +127,9 @@ public interface CombinationRecordMapper extends BaseMapperX<CombinationRecordDO
 | 
			
		||||
     *
 | 
			
		||||
     * @return 参加过拼团的用户数
 | 
			
		||||
     */
 | 
			
		||||
    // TODO @puhui999:1)方法名,直接 selectUserCount;2)COUNT(DISTINCT(user_id)) 就可以啦,不用 group by 哈
 | 
			
		||||
    default Long selectUserDistinctCount() {
 | 
			
		||||
    default Long selectUserCount() {
 | 
			
		||||
        return selectCount(new QueryWrapper<CombinationRecordDO>()
 | 
			
		||||
                .select("DISTINCT (user_id)")
 | 
			
		||||
                .groupBy("user_id"));
 | 
			
		||||
                .select("DISTINCT (user_id)"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default List<CombinationRecordDO> selectListByHeadIdAndStatusAndExpireTimeLt(Long headId, Integer status, LocalDateTime dateTime) {
 | 
			
		||||
@@ -141,8 +139,8 @@ public interface CombinationRecordMapper extends BaseMapperX<CombinationRecordDO
 | 
			
		||||
                .lt(CombinationRecordDO::getExpireTime, dateTime));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    default List<CombinationRecordDO> selectListByHeadIds(Collection<Long> headIds) {
 | 
			
		||||
        return selectList(new LambdaQueryWrapperX<CombinationRecordDO>().in(CombinationRecordDO::getHeadId, headIds));
 | 
			
		||||
    default List<CombinationRecordDO> selectListByHeadId(Long headId) {
 | 
			
		||||
        return selectList(new LambdaQueryWrapperX<CombinationRecordDO>().eq(CombinationRecordDO::getHeadId, headId));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,13 @@
 | 
			
		||||
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.stereotype.Service;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
@@ -14,6 +16,7 @@ import javax.annotation.Resource;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 | 
			
		||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.ARTICLE_CATEGORY_DELETE_FAIL_HAVE_ARTICLES;
 | 
			
		||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.ARTICLE_CATEGORY_NOT_EXISTS;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -27,6 +30,8 @@ public class ArticleCategoryServiceImpl implements ArticleCategoryService {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private ArticleCategoryMapper articleCategoryMapper;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private ArticleService articleService;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Long createArticleCategory(ArticleCategoryCreateReqVO createReqVO) {
 | 
			
		||||
@@ -50,7 +55,11 @@ public class ArticleCategoryServiceImpl implements ArticleCategoryService {
 | 
			
		||||
    public void deleteArticleCategory(Long id) {
 | 
			
		||||
        // 校验存在
 | 
			
		||||
        validateArticleCategoryExists(id);
 | 
			
		||||
        // TODO @puhui999:需要校验下,是不是存在文章
 | 
			
		||||
        // 校验是不是存在关联文章
 | 
			
		||||
        List<ArticleDO> articleList = articleService.getArticleByCategoryId(id);
 | 
			
		||||
        if (CollUtil.isNotEmpty(articleList)) {
 | 
			
		||||
            throw exception(ARTICLE_CATEGORY_DELETE_FAIL_HAVE_ARTICLES);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 删除
 | 
			
		||||
        articleCategoryMapper.deleteById(id);
 | 
			
		||||
 
 | 
			
		||||
@@ -4,19 +4,21 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleCreateReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticlePageReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleUpdateReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.app.article.vo.article.AppArticlePageReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleDO;
 | 
			
		||||
 | 
			
		||||
import javax.validation.Valid;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 文章管理 Service 接口
 | 
			
		||||
 * 文章详情 Service 接口
 | 
			
		||||
 *
 | 
			
		||||
 * @author HUIHUI
 | 
			
		||||
 */
 | 
			
		||||
public interface ArticleService {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 创建文章管理
 | 
			
		||||
     * 创建文章详情
 | 
			
		||||
     *
 | 
			
		||||
     * @param createReqVO 创建信息
 | 
			
		||||
     * @return 编号
 | 
			
		||||
@@ -24,33 +26,58 @@ public interface ArticleService {
 | 
			
		||||
    Long createArticle(@Valid ArticleCreateReqVO createReqVO);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 更新文章管理
 | 
			
		||||
     * 更新文章详情
 | 
			
		||||
     *
 | 
			
		||||
     * @param updateReqVO 更新信息
 | 
			
		||||
     */
 | 
			
		||||
    void updateArticle(@Valid ArticleUpdateReqVO updateReqVO);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 删除文章管理
 | 
			
		||||
     * 删除文章详情
 | 
			
		||||
     *
 | 
			
		||||
     * @param id 编号
 | 
			
		||||
     */
 | 
			
		||||
    void deleteArticle(Long id);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得文章管理
 | 
			
		||||
     * 获得文章详情
 | 
			
		||||
     *
 | 
			
		||||
     * @param id 编号
 | 
			
		||||
     * @return 文章管理
 | 
			
		||||
     * @return 文章详情
 | 
			
		||||
     */
 | 
			
		||||
    ArticleDO getArticle(Long id);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得文章管理分页
 | 
			
		||||
     * 获得文章详情分页
 | 
			
		||||
     *
 | 
			
		||||
     * @param pageReqVO 分页查询
 | 
			
		||||
     * @return 文章管理分页
 | 
			
		||||
     * @return 文章详情分页
 | 
			
		||||
     */
 | 
			
		||||
    PageResult<ArticleDO> getArticlePage(ArticlePageReqVO pageReqVO);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得文章详情列表
 | 
			
		||||
     *
 | 
			
		||||
     * @param recommendHot    是否热门
 | 
			
		||||
     * @param recommendBanner 是否轮播图
 | 
			
		||||
     * @return 文章详情列表
 | 
			
		||||
     */
 | 
			
		||||
    List<ArticleDO> getArticleCategoryListByRecommendHotAndRecommendBanner(Boolean recommendHot, Boolean recommendBanner);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得文章详情分页
 | 
			
		||||
     *
 | 
			
		||||
     * @param pageReqVO 分页查询
 | 
			
		||||
     * @return 文章详情分页
 | 
			
		||||
     */
 | 
			
		||||
    PageResult<ArticleDO> getArticlePage(AppArticlePageReqVO pageReqVO);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得指定分类的文章列表
 | 
			
		||||
     *
 | 
			
		||||
     * @param categoryId 文章分类编号
 | 
			
		||||
     * @return 文章列表
 | 
			
		||||
     */
 | 
			
		||||
    List<ArticleDO> getArticleByCategoryId(Long categoryId);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,15 +4,19 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleCreateReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticlePageReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleUpdateReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.app.article.vo.article.AppArticlePageReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.convert.article.ArticleConvert;
 | 
			
		||||
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.ArticleMapper;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.validation.annotation.Validated;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
 | 
			
		||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.ARTICLE_CATEGORY_NOT_EXISTS;
 | 
			
		||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.ARTICLE_NOT_EXISTS;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -26,10 +30,14 @@ public class ArticleServiceImpl implements ArticleService {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private ArticleMapper articleMapper;
 | 
			
		||||
    @Resource
 | 
			
		||||
    private ArticleCategoryService articleCategoryService;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Long createArticle(ArticleCreateReqVO createReqVO) {
 | 
			
		||||
        // TODO @puhui999:需要校验分类存在
 | 
			
		||||
        // 校验分类存在
 | 
			
		||||
        validateArticleCategoryExists(createReqVO.getCategoryId());
 | 
			
		||||
 | 
			
		||||
        // 插入
 | 
			
		||||
        ArticleDO article = ArticleConvert.INSTANCE.convert(createReqVO);
 | 
			
		||||
        articleMapper.insert(article);
 | 
			
		||||
@@ -41,7 +49,8 @@ public class ArticleServiceImpl implements ArticleService {
 | 
			
		||||
    public void updateArticle(ArticleUpdateReqVO updateReqVO) {
 | 
			
		||||
        // 校验存在
 | 
			
		||||
        validateArticleExists(updateReqVO.getId());
 | 
			
		||||
        // TODO @puhui999:需要校验分类存在
 | 
			
		||||
        // 校验分类存在
 | 
			
		||||
        validateArticleCategoryExists(updateReqVO.getCategoryId());
 | 
			
		||||
 | 
			
		||||
        // 更新
 | 
			
		||||
        ArticleDO updateObj = ArticleConvert.INSTANCE.convert(updateReqVO);
 | 
			
		||||
@@ -62,6 +71,13 @@ public class ArticleServiceImpl implements ArticleService {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void validateArticleCategoryExists(Long categoryId) {
 | 
			
		||||
        ArticleCategoryDO articleCategory = articleCategoryService.getArticleCategory(categoryId);
 | 
			
		||||
        if (articleCategory == null) {
 | 
			
		||||
            throw exception(ARTICLE_CATEGORY_NOT_EXISTS);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public ArticleDO getArticle(Long id) {
 | 
			
		||||
        return articleMapper.selectById(id);
 | 
			
		||||
@@ -72,4 +88,19 @@ public class ArticleServiceImpl implements ArticleService {
 | 
			
		||||
        return articleMapper.selectPage(pageReqVO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<ArticleDO> getArticleCategoryListByRecommendHotAndRecommendBanner(Boolean recommendHot, Boolean recommendBanner) {
 | 
			
		||||
        return articleMapper.selectList(recommendHot, recommendBanner);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public PageResult<ArticleDO> getArticlePage(AppArticlePageReqVO pageReqVO) {
 | 
			
		||||
        return articleMapper.selectPage(pageReqVO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<ArticleDO> getArticleByCategoryId(Long categoryId) {
 | 
			
		||||
        return articleMapper.selectList(ArticleDO::getCategoryId, categoryId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -247,7 +247,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Long getCombinationUserCount() {
 | 
			
		||||
        return combinationRecordMapper.selectUserDistinctCount();
 | 
			
		||||
        return combinationRecordMapper.selectUserCount();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@@ -351,48 +351,46 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
 | 
			
		||||
                convertSet(headExpireRecords, CombinationRecordDO::getActivityId));
 | 
			
		||||
        Map<Long, CombinationActivityDO> activityMap = convertMap(activities, CombinationActivityDO::getId);
 | 
			
		||||
 | 
			
		||||
        // TODO @puhui999:这里可以改成“每个团”,处理一次哈;这样 handleExpireRecord、handleVirtualGroupRecord 都改成按团处理,每个是一个小事务;
 | 
			
		||||
        // 3. 校验是否虚拟成团
 | 
			
		||||
        List<CombinationRecordDO> virtualGroupHeadRecords = new ArrayList<>(); // 虚拟成团
 | 
			
		||||
        for (Iterator<CombinationRecordDO> iterator = headExpireRecords.iterator(); iterator.hasNext(); ) {
 | 
			
		||||
            CombinationRecordDO record = iterator.next();
 | 
			
		||||
            // 3.1.不匹配,则直接跳过
 | 
			
		||||
            CombinationActivityDO activity = activityMap.get(record.getActivityId());
 | 
			
		||||
        // 3. 改成“每个团”,处理一次哈;这样 handleExpireRecord、handleVirtualGroupRecord 都改成按团处理,每个是一个小事务;
 | 
			
		||||
        KeyValue<Integer, Integer> keyValue = new KeyValue<>(0, 0); // 统计过期拼团和虚拟成团
 | 
			
		||||
        for (CombinationRecordDO recordDO : headExpireRecords) {
 | 
			
		||||
            try {
 | 
			
		||||
                CombinationActivityDO activity = activityMap.get(recordDO.getActivityId());
 | 
			
		||||
                if (activity == null || !activity.getVirtualGroup()) { // 取不到活动的或者不是虚拟拼团的
 | 
			
		||||
                continue;
 | 
			
		||||
                    // 3.1. 处理过期的拼团
 | 
			
		||||
                    getSelf().handleExpireRecord(recordDO);
 | 
			
		||||
                    keyValue.setKey(keyValue.getKey() + 1);
 | 
			
		||||
                } else {
 | 
			
		||||
                    // 3.2. 处理虚拟成团
 | 
			
		||||
                    getSelf().handleVirtualGroupRecord(recordDO);
 | 
			
		||||
                    keyValue.setValue(keyValue.getValue() + 1);
 | 
			
		||||
                }
 | 
			
		||||
            } catch (Exception ignored) { // 处理异常继续循环
 | 
			
		||||
            }
 | 
			
		||||
            // 3.2.匹配,则移除,添加到虚拟成团中,并结束寻找
 | 
			
		||||
            virtualGroupHeadRecords.add(record);
 | 
			
		||||
            iterator.remove();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 4.处理过期的拼团
 | 
			
		||||
        getSelf().handleExpireRecord(headExpireRecords);
 | 
			
		||||
        // 5.虚拟成团
 | 
			
		||||
        getSelf().handleVirtualGroupRecord(virtualGroupHeadRecords);
 | 
			
		||||
        return new KeyValue<>(headExpireRecords.size(), virtualGroupHeadRecords.size());
 | 
			
		||||
        return keyValue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 处理过期拼团
 | 
			
		||||
     *
 | 
			
		||||
     * @param headExpireRecords 过期拼团团长记录列表
 | 
			
		||||
     * @param headExpireRecord 过期拼团团长记录列表
 | 
			
		||||
     */
 | 
			
		||||
    @Transactional(rollbackFor = Exception.class)
 | 
			
		||||
    public void handleExpireRecord(List<CombinationRecordDO> headExpireRecords) {
 | 
			
		||||
        if (CollUtil.isEmpty(headExpireRecords)) {
 | 
			
		||||
    public void handleExpireRecord(CombinationRecordDO headExpireRecord) {
 | 
			
		||||
        if (headExpireRecord == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 1.更新拼团记录
 | 
			
		||||
        List<CombinationRecordDO> headsAndRecords = updateBatchCombinationRecords(headExpireRecords,
 | 
			
		||||
        List<CombinationRecordDO> headsAndRecords = updateBatchCombinationRecords(headExpireRecord,
 | 
			
		||||
                CombinationRecordStatusEnum.FAILED);
 | 
			
		||||
        if (headsAndRecords == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 2.订单取消 TODO 以现在的取消回滚逻辑好像只能循环了
 | 
			
		||||
        // 2.订单取消
 | 
			
		||||
        headsAndRecords.forEach(item -> {
 | 
			
		||||
            tradeOrderApi.cancelPaidOrder(item.getUserId(), item.getOrderId());
 | 
			
		||||
        });
 | 
			
		||||
@@ -401,36 +399,32 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
 | 
			
		||||
    /**
 | 
			
		||||
     * 处理虚拟拼团
 | 
			
		||||
     *
 | 
			
		||||
     * @param virtualGroupHeadRecords 虚拟成团团长记录列表
 | 
			
		||||
     * @param virtualGroupHeadRecord 虚拟成团团长记录列表
 | 
			
		||||
     */
 | 
			
		||||
    @Transactional(rollbackFor = Exception.class)
 | 
			
		||||
    public void handleVirtualGroupRecord(List<CombinationRecordDO> virtualGroupHeadRecords) {
 | 
			
		||||
        if (CollUtil.isEmpty(virtualGroupHeadRecords)) {
 | 
			
		||||
    public void handleVirtualGroupRecord(CombinationRecordDO virtualGroupHeadRecord) {
 | 
			
		||||
        if (virtualGroupHeadRecord == null) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 1. 团员补齐
 | 
			
		||||
        combinationRecordMapper.insertBatch(CombinationActivityConvert.INSTANCE.convertVirtualGroupList(virtualGroupHeadRecords));
 | 
			
		||||
        combinationRecordMapper.insertBatch(CombinationActivityConvert.INSTANCE.convertVirtualGroupList(virtualGroupHeadRecord));
 | 
			
		||||
        // 2. 更新拼团记录
 | 
			
		||||
        updateBatchCombinationRecords(virtualGroupHeadRecords, CombinationRecordStatusEnum.SUCCESS);
 | 
			
		||||
        updateBatchCombinationRecords(virtualGroupHeadRecord, CombinationRecordStatusEnum.SUCCESS);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private List<CombinationRecordDO> updateBatchCombinationRecords(List<CombinationRecordDO> headRecords, CombinationRecordStatusEnum status) {
 | 
			
		||||
        // 1. 查询团成员
 | 
			
		||||
        List<CombinationRecordDO> records = combinationRecordMapper.selectListByHeadIds(
 | 
			
		||||
                convertSet(headRecords, CombinationRecordDO::getId));
 | 
			
		||||
    private List<CombinationRecordDO> updateBatchCombinationRecords(CombinationRecordDO headRecord, CombinationRecordStatusEnum status) {
 | 
			
		||||
        // 1. 查询团成员(包含团长)
 | 
			
		||||
        List<CombinationRecordDO> records = combinationRecordMapper.selectListByHeadId(headRecord.getId());
 | 
			
		||||
        if (CollUtil.isEmpty(records)) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        Map<Long, List<CombinationRecordDO>> recordsMap = convertMultiMap(records, CombinationRecordDO::getHeadId);
 | 
			
		||||
        headRecords.forEach(item -> {
 | 
			
		||||
            recordsMap.get(item.getId()).add(item); // 把团长加进团里
 | 
			
		||||
        });
 | 
			
		||||
        records.add(headRecord);// 把团长加进去
 | 
			
		||||
 | 
			
		||||
        // 2.批量更新拼团记录 status 和 失败/成团时间
 | 
			
		||||
        List<CombinationRecordDO> headsAndRecords = mergeValuesFromMap(recordsMap);
 | 
			
		||||
        List<CombinationRecordDO> updateRecords = new ArrayList<>(headsAndRecords.size());
 | 
			
		||||
        List<CombinationRecordDO> updateRecords = new ArrayList<>(records.size());
 | 
			
		||||
        LocalDateTime now = LocalDateTime.now();
 | 
			
		||||
        headsAndRecords.forEach(item -> {
 | 
			
		||||
        records.forEach(item -> {
 | 
			
		||||
            CombinationRecordDO record = new CombinationRecordDO().setId(item.getId())
 | 
			
		||||
                    .setStatus(status.getStatus()).setEndTime(now);
 | 
			
		||||
            if (CombinationRecordStatusEnum.isSuccess(status.getStatus())) { // 虚拟成团完事更改状态成功后还需要把参与人数修改为成团需要人数
 | 
			
		||||
@@ -439,7 +433,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
 | 
			
		||||
            updateRecords.add(record);
 | 
			
		||||
        });
 | 
			
		||||
        combinationRecordMapper.updateBatch(updateRecords);
 | 
			
		||||
        return headsAndRecords;
 | 
			
		||||
        return records;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.promotion.service.article;
 | 
			
		||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
 | 
			
		||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleCreateReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.ArticleExportReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticlePageReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.controller.admin.article.vo.article.ArticleUpdateReqVO;
 | 
			
		||||
import cn.iocoder.yudao.module.promotion.dal.dataobject.article.ArticleDO;
 | 
			
		||||
@@ -13,7 +12,6 @@ import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.springframework.context.annotation.Import;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
 | 
			
		||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
 | 
			
		||||
@@ -166,68 +164,4 @@ public class ArticleServiceImplTest extends BaseDbUnitTest {
 | 
			
		||||
        assertPojoEquals(dbArticle, pageResult.getList().get(0));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    @Disabled  // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
 | 
			
		||||
    public void testGetArticleList() {
 | 
			
		||||
        // mock 数据
 | 
			
		||||
        ArticleDO dbArticle = randomPojo(ArticleDO.class, o -> { // 等会查询到
 | 
			
		||||
            o.setCategoryId(null);
 | 
			
		||||
            o.setTitle(null);
 | 
			
		||||
            o.setAuthor(null);
 | 
			
		||||
            o.setPicUrl(null);
 | 
			
		||||
            o.setIntroduction(null);
 | 
			
		||||
            o.setBrowseCount(null);
 | 
			
		||||
            o.setSort(null);
 | 
			
		||||
            o.setStatus(null);
 | 
			
		||||
            o.setSpuId(null);
 | 
			
		||||
            o.setRecommendHot(null);
 | 
			
		||||
            o.setRecommendBanner(null);
 | 
			
		||||
            o.setContent(null);
 | 
			
		||||
            o.setCreateTime(null);
 | 
			
		||||
        });
 | 
			
		||||
        articleMapper.insert(dbArticle);
 | 
			
		||||
        // 测试 categoryId 不匹配
 | 
			
		||||
        articleMapper.insert(cloneIgnoreId(dbArticle, o -> o.setCategoryId(null)));
 | 
			
		||||
        // 测试 title 不匹配
 | 
			
		||||
        articleMapper.insert(cloneIgnoreId(dbArticle, o -> o.setTitle(null)));
 | 
			
		||||
        // 测试 author 不匹配
 | 
			
		||||
        articleMapper.insert(cloneIgnoreId(dbArticle, o -> o.setAuthor(null)));
 | 
			
		||||
        // 测试 picUrl 不匹配
 | 
			
		||||
        articleMapper.insert(cloneIgnoreId(dbArticle, o -> o.setPicUrl(null)));
 | 
			
		||||
        // 测试 introduction 不匹配
 | 
			
		||||
        articleMapper.insert(cloneIgnoreId(dbArticle, o -> o.setIntroduction(null)));
 | 
			
		||||
        // 测试 browseCount 不匹配
 | 
			
		||||
        articleMapper.insert(cloneIgnoreId(dbArticle, o -> o.setBrowseCount(null)));
 | 
			
		||||
        // 测试 sort 不匹配
 | 
			
		||||
        articleMapper.insert(cloneIgnoreId(dbArticle, o -> o.setSort(null)));
 | 
			
		||||
        // 测试 status 不匹配
 | 
			
		||||
        articleMapper.insert(cloneIgnoreId(dbArticle, o -> o.setStatus(null)));
 | 
			
		||||
        // 测试 spuId 不匹配
 | 
			
		||||
        articleMapper.insert(cloneIgnoreId(dbArticle, o -> o.setSpuId(null)));
 | 
			
		||||
        // 测试 recommendHot 不匹配
 | 
			
		||||
        articleMapper.insert(cloneIgnoreId(dbArticle, o -> o.setRecommendHot(null)));
 | 
			
		||||
        // 测试 recommendBanner 不匹配
 | 
			
		||||
        articleMapper.insert(cloneIgnoreId(dbArticle, o -> o.setRecommendBanner(null)));
 | 
			
		||||
        // 测试 content 不匹配
 | 
			
		||||
        articleMapper.insert(cloneIgnoreId(dbArticle, o -> o.setContent(null)));
 | 
			
		||||
        // 测试 createTime 不匹配
 | 
			
		||||
        articleMapper.insert(cloneIgnoreId(dbArticle, o -> o.setCreateTime(null)));
 | 
			
		||||
        // 准备参数
 | 
			
		||||
        ArticleExportReqVO reqVO = new ArticleExportReqVO();
 | 
			
		||||
        reqVO.setCategoryId(null);
 | 
			
		||||
        reqVO.setTitle(null);
 | 
			
		||||
        reqVO.setAuthor(null);
 | 
			
		||||
        reqVO.setStatus(null);
 | 
			
		||||
        reqVO.setSpuId(null);
 | 
			
		||||
        reqVO.setRecommendHot(null);
 | 
			
		||||
        reqVO.setRecommendBanner(null);
 | 
			
		||||
        reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
 | 
			
		||||
 | 
			
		||||
        // 调用
 | 
			
		||||
        List<ArticleDO> list = articleService.getArticleList(reqVO);
 | 
			
		||||
        // 断言
 | 
			
		||||
        assertEquals(1, list.size());
 | 
			
		||||
        assertPojoEquals(dbArticle, list.get(0));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -93,7 +93,6 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
 | 
			
		||||
        return selectOne(TradeOrderDO::getPickUpVerifyCode, pickUpVerifyCode);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO @puhui999:selectByUserIdAndCombinationActivityIdAndStatus,这样更容易理解哈。
 | 
			
		||||
    /**
 | 
			
		||||
     * 只针对 combinationActivityId 的查询
 | 
			
		||||
     *
 | 
			
		||||
@@ -102,7 +101,7 @@ public interface TradeOrderMapper extends BaseMapperX<TradeOrderDO> {
 | 
			
		||||
     * @param status     订单状态
 | 
			
		||||
     * @return 交易订单
 | 
			
		||||
     */
 | 
			
		||||
    default TradeOrderDO selectByUserIdAndActivityIdAndStatus(Long userId, Long activityId, Integer status) {
 | 
			
		||||
    default TradeOrderDO selectByUserIdAndCombinationActivityIdAndStatus(Long userId, Long activityId, Integer status) {
 | 
			
		||||
        return selectOne(new LambdaQueryWrapperX<TradeOrderDO>()
 | 
			
		||||
                .eq(TradeOrderDO::getUserId, userId)
 | 
			
		||||
                .eq(TradeOrderDO::getStatus, status)
 | 
			
		||||
 
 | 
			
		||||
@@ -46,7 +46,7 @@ public interface TradeOrderQueryService {
 | 
			
		||||
     * @param status     订单状态
 | 
			
		||||
     * @return 交易订单
 | 
			
		||||
     */
 | 
			
		||||
    TradeOrderDO getActivityOrderByUserIdAndActivityIdAndStatus(Long userId, Long activityId, Integer status);
 | 
			
		||||
    TradeOrderDO getCombinationActivityOrderByUserIdAndCombinationActivityIdAndStatus(Long userId, Long activityId, Integer status);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得订单列表
 | 
			
		||||
 
 | 
			
		||||
@@ -71,8 +71,8 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public TradeOrderDO getActivityOrderByUserIdAndActivityIdAndStatus(Long userId, Long activityId, Integer status) {
 | 
			
		||||
        return tradeOrderMapper.selectByUserIdAndActivityIdAndStatus(userId, activityId, status);
 | 
			
		||||
    public TradeOrderDO getCombinationActivityOrderByUserIdAndCombinationActivityIdAndStatus(Long userId, Long activityId, Integer status) {
 | 
			
		||||
        return tradeOrderMapper.selectByUserIdAndCombinationActivityIdAndStatus(userId, activityId, status);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -49,7 +49,7 @@ public class TradeCombinationOrderHandler implements TradeOrderHandler {
 | 
			
		||||
                order.getCombinationHeadId(), item.getSkuId(), item.getCount());
 | 
			
		||||
 | 
			
		||||
        // 2. 校验该用户是否存在未支付的拼团活动订单,避免一个拼团可以下多个单子了
 | 
			
		||||
        TradeOrderDO activityOrder = orderQueryService.getActivityOrderByUserIdAndActivityIdAndStatus(
 | 
			
		||||
        TradeOrderDO activityOrder = orderQueryService.getCombinationActivityOrderByUserIdAndCombinationActivityIdAndStatus(
 | 
			
		||||
                order.getUserId(), order.getCombinationActivityId(), TradeOrderStatusEnum.UNPAID.getStatus());
 | 
			
		||||
        if (activityOrder != null) {
 | 
			
		||||
            throw exception(ORDER_CREATE_FAIL_EXIST_UNPAID);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user