Commit 2e207019b3582e13d56e4c1aa2a169a97f471a18

Authored by jason
2 parents 571961b4 a72869b9

Merge remote-tracking branch 'origin/feature/mall_product' into feature/mall_product

Showing 102 changed files with 2146 additions and 770 deletions
sql/mysql/statistics.sql
@@ -28,16 +28,22 @@ CREATE INDEX trade_statistics_time_index @@ -28,16 +28,22 @@ CREATE INDEX trade_statistics_time_index
28 ON trade_statistics (time); 28 ON trade_statistics (time);
29 29
30 -- 菜单 30 -- 菜单
31 -INSERT INTO `ruoyi-vue-pro`.system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name) 31 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name)
32 VALUES ('统计管理', '', 1, 4, 0, '/statistics', 'ep:data-line', '', ''); 32 VALUES ('统计管理', '', 1, 4, 0, '/statistics', 'ep:data-line', '', '');
33 SELECT @parentId := LAST_INSERT_ID(); 33 SELECT @parentId := LAST_INSERT_ID();
34 -INSERT INTO `ruoyi-vue-pro`.system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name)  
35 -VALUES ('交易统计', '', 2, 4, @parentId, 'trade', 'fa-solid:credit-card', 'statistics/trade/index', 'TradeStatistics'); 34 +-- 交易统计
  35 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name)
  36 +VALUES ('交易统计', '', 2, 1, @parentId, 'trade', 'fa-solid:credit-card', 'statistics/trade/index', 'TradeStatistics');
36 SELECT @parentId := LAST_INSERT_ID(); 37 SELECT @parentId := LAST_INSERT_ID();
37 --- 按钮  
38 INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) 38 INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
39 VALUES ('交易统计查询', 'statistics:trade:query', 3, 1, @parentId, '', '', '', 0); 39 VALUES ('交易统计查询', 'statistics:trade:query', 3, 1, @parentId, '', '', '', 0);
40 INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status) 40 INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
41 VALUES ('交易统计导出', 'statistics:trade:export', 3, 2, @parentId, '', '', '', 0); 41 VALUES ('交易统计导出', 'statistics:trade:export', 3, 2, @parentId, '', '', '', 0);
  42 +-- 会员统计
  43 +INSERT INTO system_menu (name, permission, type, sort, parent_id, path, icon, component, component_name)
  44 +VALUES ('会员统计', '', 2, 2, @parentId, 'member', 'ep:avatar', 'statistics/member/index', 'MemberStatistics');
  45 +SELECT @parentId := LAST_INSERT_ID();
  46 +INSERT INTO system_menu(name, permission, type, sort, parent_id, path, icon, component, status)
  47 +VALUES ('会员统计查询', 'statistics:member:query', 3, 1, @parentId, '', '', '', 0);
42 48
43 49
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.common.util.collection; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.common.util.collection;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.CollectionUtil; 4 import cn.hutool.core.collection.CollectionUtil;
  5 +import cn.hutool.core.util.ArrayUtil;
5 import com.google.common.collect.ImmutableMap; 6 import com.google.common.collect.ImmutableMap;
6 7
7 import java.util.*; 8 import java.util.*;
@@ -50,6 +51,13 @@ public class CollectionUtils { @@ -50,6 +51,13 @@ public class CollectionUtils {
50 return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values()); 51 return new ArrayList<>(convertMap(from, keyMapper, Function.identity(), cover).values());
51 } 52 }
52 53
  54 + public static <T, U> List<U> convertList(T[] from, Function<T, U> func) {
  55 + if (ArrayUtil.isEmpty(from)) {
  56 + return new ArrayList<>();
  57 + }
  58 + return convertList(Arrays.asList(from), func);
  59 + }
  60 +
53 public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) { 61 public static <T, U> List<U> convertList(Collection<T> from, Function<T, U> func) {
54 if (CollUtil.isEmpty(from)) { 62 if (CollUtil.isEmpty(from)) {
55 return new ArrayList<>(); 63 return new ArrayList<>();
@@ -64,6 +72,13 @@ public class CollectionUtils { @@ -64,6 +72,13 @@ public class CollectionUtils {
64 return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList()); 72 return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList());
65 } 73 }
66 74
  75 + public static <K, V> List<V> mergeValuesFromMap(Map<K, List<V>> map) {
  76 + return map.values()
  77 + .stream()
  78 + .flatMap(List::stream)
  79 + .collect(Collectors.toList());
  80 + }
  81 +
67 public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) { 82 public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
68 if (CollUtil.isEmpty(from)) { 83 if (CollUtil.isEmpty(from)) {
69 return new HashSet<>(); 84 return new HashSet<>();
@@ -162,8 +177,8 @@ public class CollectionUtils { @@ -162,8 +177,8 @@ public class CollectionUtils {
162 /** 177 /**
163 * 对比老、新两个列表,找出新增、修改、删除的数据 178 * 对比老、新两个列表,找出新增、修改、删除的数据
164 * 179 *
165 - * @param oldList 老列表  
166 - * @param newList 新列表 180 + * @param oldList 老列表
  181 + * @param newList 新列表
167 * @param sameFunc 对比函数,返回 true 表示相同,返回 false 表示不同 182 * @param sameFunc 对比函数,返回 true 表示相同,返回 false 表示不同
168 * 注意,same 是通过每个元素的“标识”,判断它们是不是同一个数据 183 * 注意,same 是通过每个元素的“标识”,判断它们是不是同一个数据
169 * @return [新增列表、修改列表、删除列表] 184 * @return [新增列表、修改列表、删除列表]
yudao-framework/yudao-spring-boot-starter-biz-ip/src/main/java/cn/iocoder/yudao/framework/ip/core/utils/AreaUtils.java
@@ -7,12 +7,16 @@ import cn.hutool.core.text.csv.CsvUtil; @@ -7,12 +7,16 @@ import cn.hutool.core.text.csv.CsvUtil;
7 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 7 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
8 import cn.iocoder.yudao.framework.ip.core.Area; 8 import cn.iocoder.yudao.framework.ip.core.Area;
9 import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum; 9 import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
  10 +import lombok.NonNull;
10 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
11 12
12 import java.util.ArrayList; 13 import java.util.ArrayList;
13 import java.util.HashMap; 14 import java.util.HashMap;
14 import java.util.List; 15 import java.util.List;
15 import java.util.Map; 16 import java.util.Map;
  17 +import java.util.function.Function;
  18 +
  19 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
16 20
17 /** 21 /**
18 * 区域工具类 22 * 区域工具类
@@ -108,7 +112,7 @@ public class AreaUtils { @@ -108,7 +112,7 @@ public class AreaUtils {
108 // “递归”父节点 112 // “递归”父节点
109 area = area.getParent(); 113 area = area.getParent();
110 if (area == null 114 if (area == null
111 - || ObjectUtils.equalsAny(area.getId(), Area.ID_GLOBAL, Area.ID_CHINA)) { // 跳过父节点为中国的情况 115 + || ObjectUtils.equalsAny(area.getId(), Area.ID_GLOBAL, Area.ID_CHINA)) { // 跳过父节点为中国的情况
112 break; 116 break;
113 } 117 }
114 sb.insert(0, separator); 118 sb.insert(0, separator);
@@ -116,4 +120,37 @@ public class AreaUtils { @@ -116,4 +120,37 @@ public class AreaUtils {
116 return sb.toString(); 120 return sb.toString();
117 } 121 }
118 122
  123 + /**
  124 + * 获取指定类型的区域列表
  125 + *
  126 + * @param type 区域类型
  127 + * @param func 转换函数
  128 + * @param <T> 结果类型
  129 + * @return 区域列表
  130 + */
  131 + public static <T> List<T> getByType(AreaTypeEnum type, Function<Area, T> func) {
  132 + return convertList(areas.values(), func, area -> type.getType().equals(area.getType()));
  133 + }
  134 +
  135 + // TODO @疯狂:注释写下;
  136 + public static Integer getParentIdByType(Integer id, @NonNull AreaTypeEnum type) {
  137 + // TODO @疯狂:这种不要用 while true;因为万一脏数据,可能会死循环;可以转换成 for (int i = 0; i < Byte.MAX; i++) 一般是优先层级;
  138 + do {
  139 + Area area = AreaUtils.getArea(id);
  140 + if (area == null) {
  141 + return null;
  142 + }
  143 +
  144 + if (type.getType().equals(area.getType())) {
  145 + return area.getId();
  146 + }
  147 +
  148 + if (area.getParent() == null || area.getParent().getId() == null) {
  149 + return null;
  150 + }
  151 +
  152 + id = area.getParent().getId();
  153 + } while (true);
  154 + }
  155 +
119 } 156 }
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApi.java
1 package cn.iocoder.yudao.module.promotion.api.combination; 1 package cn.iocoder.yudao.module.promotion.api.combination;
2 2
  3 +import cn.iocoder.yudao.framework.common.core.KeyValue;
3 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; 4 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
4 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO; 5 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
5 6
6 import javax.validation.Valid; 7 import javax.validation.Valid;
7 8
8 -// TODO @芋艿:后面也再撸撸这几个接口  
9 -  
10 /** 9 /**
11 * 拼团记录 API 接口 10 * 拼团记录 API 接口
12 * 11 *
@@ -29,9 +28,9 @@ public interface CombinationRecordApi { @@ -29,9 +28,9 @@ public interface CombinationRecordApi {
29 * 创建开团记录 28 * 创建开团记录
30 * 29 *
31 * @param reqDTO 请求 DTO 30 * @param reqDTO 请求 DTO
32 - * @return 开团记录编号 31 + * @return key 开团记录编号、value 团长编号
33 */ 32 */
34 - Long createCombinationRecord(@Valid CombinationRecordCreateReqDTO reqDTO); 33 + KeyValue<Long, Long> createCombinationRecord(@Valid CombinationRecordCreateReqDTO reqDTO);
35 34
36 /** 35 /**
37 * 查询拼团记录是否成功 36 * 查询拼团记录是否成功
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/SeckillActivityApi.java
@@ -10,13 +10,22 @@ import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinResp @@ -10,13 +10,22 @@ import cn.iocoder.yudao.module.promotion.api.seckill.dto.SeckillValidateJoinResp
10 public interface SeckillActivityApi { 10 public interface SeckillActivityApi {
11 11
12 /** 12 /**
13 - * 更新秒杀库存 13 + * 更新秒杀库存(减少)
14 * 14 *
15 - * @param id 活动编号  
16 - * @param skuId sku 编号  
17 - * @param count 数量 15 + * @param id 活动编号
  16 + * @param skuId sku 编号
  17 + * @param count 数量(正数)
  18 + */
  19 + void updateSeckillStockDecr(Long id, Long skuId, Integer count);
  20 +
  21 + /**
  22 + * 更新秒杀库存(增加)
  23 + *
  24 + * @param id 活动编号
  25 + * @param skuId sku 编号
  26 + * @param count 数量(正数)
18 */ 27 */
19 - void updateSeckillStock(Long id, Long skuId, Integer count); 28 + void updateSeckillStockIncr(Long id, Long skuId, Integer count);
20 29
21 /** 30 /**
22 * 【下单前】校验是否参与秒杀活动 31 * 【下单前】校验是否参与秒杀活动
@@ -24,8 +33,8 @@ public interface SeckillActivityApi { @@ -24,8 +33,8 @@ public interface SeckillActivityApi {
24 * 如果校验失败,则抛出业务异常 33 * 如果校验失败,则抛出业务异常
25 * 34 *
26 * @param activityId 活动编号 35 * @param activityId 活动编号
27 - * @param skuId SKU 编号  
28 - * @param count 数量 36 + * @param skuId SKU 编号
  37 + * @param count 数量
29 * @return 秒杀信息 38 * @return 秒杀信息
30 */ 39 */
31 SeckillValidateJoinRespDTO validateJoinSeckill(Long activityId, Long skuId, Integer count); 40 SeckillValidateJoinRespDTO validateJoinSeckill(Long activityId, Long skuId, Integer count);
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/combination/CombinationRecordApiImpl.java
1 package cn.iocoder.yudao.module.promotion.api.combination; 1 package cn.iocoder.yudao.module.promotion.api.combination;
2 2
  3 +import cn.iocoder.yudao.framework.common.core.KeyValue;
3 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; 4 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
4 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO; 5 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
5 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; 6 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
@@ -28,8 +29,9 @@ public class CombinationRecordApiImpl implements CombinationRecordApi { @@ -28,8 +29,9 @@ public class CombinationRecordApiImpl implements CombinationRecordApi {
28 recordService.validateCombinationRecord(userId, activityId, headId, skuId, count); 29 recordService.validateCombinationRecord(userId, activityId, headId, skuId, count);
29 } 30 }
30 31
  32 + // TODO @puhui999:搞个创建的 RespDTO 哈;
31 @Override 33 @Override
32 - public Long createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) { 34 + public KeyValue<Long, Long> createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
33 return recordService.createCombinationRecord(reqDTO); 35 return recordService.createCombinationRecord(reqDTO);
34 } 36 }
35 37
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/api/seckill/SeckillActivityApiImpl.java
@@ -18,8 +18,13 @@ public class SeckillActivityApiImpl implements SeckillActivityApi { @@ -18,8 +18,13 @@ public class SeckillActivityApiImpl implements SeckillActivityApi {
18 private SeckillActivityService activityService; 18 private SeckillActivityService activityService;
19 19
20 @Override 20 @Override
21 - public void updateSeckillStock(Long id, Long skuId, Integer count) {  
22 - activityService.updateSeckillStock(id, skuId, count); 21 + public void updateSeckillStockDecr(Long id, Long skuId, Integer count) {
  22 + activityService.updateSeckillStockDecr(id, skuId, count);
  23 + }
  24 +
  25 + @Override
  26 + public void updateSeckillStockIncr(Long id, Long skuId, Integer count) {
  27 + activityService.updateSeckillStockIncr(id, skuId, count);
23 } 28 }
24 29
25 @Override 30 @Override
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/CombinationRecordController.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.controller.admin.combination; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.promotion.controller.admin.combination;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordPageItemRespVO; 5 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordPageItemRespVO;
  6 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPage2VO;
6 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;
7 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordSummaryVO; 8 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordSummaryVO;
8 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; 9 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
@@ -39,6 +40,9 @@ public class CombinationRecordController { @@ -39,6 +40,9 @@ public class CombinationRecordController {
39 @Lazy 40 @Lazy
40 private CombinationRecordService combinationRecordService; 41 private CombinationRecordService combinationRecordService;
41 42
  43 + // TODO @puhui999:getBargainRecordPage 和 getBargainRecordPage 是不是可以合并;然后 CombinationRecordReqPageVO 加一个 headId;
  44 + // 然后如果 headId 非空,并且第一页,单独多查询一条 head ;放到第 0 个位置;相当于说,第一页特殊一点;
  45 +
42 @GetMapping("/page") 46 @GetMapping("/page")
43 @Operation(summary = "获得拼团记录分页") 47 @Operation(summary = "获得拼团记录分页")
44 @PreAuthorize("@ss.hasPermission('promotion:combination-record:query')") 48 @PreAuthorize("@ss.hasPermission('promotion:combination-record:query')")
@@ -46,6 +50,18 @@ public class CombinationRecordController { @@ -46,6 +50,18 @@ public class CombinationRecordController {
46 PageResult<CombinationRecordDO> recordPage = combinationRecordService.getCombinationRecordPage(pageVO); 50 PageResult<CombinationRecordDO> recordPage = combinationRecordService.getCombinationRecordPage(pageVO);
47 List<CombinationActivityDO> activities = combinationActivityService.getCombinationActivityListByIds( 51 List<CombinationActivityDO> activities = combinationActivityService.getCombinationActivityListByIds(
48 convertSet(recordPage.getList(), CombinationRecordDO::getActivityId)); 52 convertSet(recordPage.getList(), CombinationRecordDO::getActivityId));
  53 + // TODO @puhui999:商品没读取
  54 + return success(CombinationActivityConvert.INSTANCE.convert(recordPage, activities));
  55 + }
  56 +
  57 + @GetMapping("/page-by-headId")
  58 + @Operation(summary = "获得拼团记录分页")
  59 + @PreAuthorize("@ss.hasPermission('promotion:combination-record:query')")
  60 + public CommonResult<PageResult<CombinationRecordPageItemRespVO>> getBargainRecordPage(@Valid CombinationRecordReqPage2VO pageVO) {
  61 + // 包含团长和团员的分页记录
  62 + PageResult<CombinationRecordDO> recordPage = combinationRecordService.getCombinationRecordPage2(pageVO);
  63 + List<CombinationActivityDO> activities = combinationActivityService.getCombinationActivityListByIds(
  64 + convertSet(recordPage.getList(), CombinationRecordDO::getActivityId));
49 return success(CombinationActivityConvert.INSTANCE.convert(recordPage, activities)); 65 return success(CombinationActivityConvert.INSTANCE.convert(recordPage, activities));
50 } 66 }
51 67
@@ -54,10 +70,11 @@ public class CombinationRecordController { @@ -54,10 +70,11 @@ public class CombinationRecordController {
54 @PreAuthorize("@ss.hasPermission('promotion:combination-record:query')") 70 @PreAuthorize("@ss.hasPermission('promotion:combination-record:query')")
55 public CommonResult<CombinationRecordSummaryVO> getCombinationRecordSummary() { 71 public CommonResult<CombinationRecordSummaryVO> getCombinationRecordSummary() {
56 CombinationRecordSummaryVO summaryVO = new CombinationRecordSummaryVO(); 72 CombinationRecordSummaryVO summaryVO = new CombinationRecordSummaryVO();
57 - summaryVO.setUserCount(combinationRecordService.getCombinationRecordCount(null, null)); // 获取所有拼团记录 73 + summaryVO.setUserCount(combinationRecordService.getCombinationRecordCount(null, null, null)); // 获取拼团用户参与数量
58 summaryVO.setSuccessCount(combinationRecordService.getCombinationRecordCount( // 获取成团记录 74 summaryVO.setSuccessCount(combinationRecordService.getCombinationRecordCount( // 获取成团记录
59 - CombinationRecordStatusEnum.SUCCESS.getStatus(), null));  
60 - summaryVO.setVirtualGroupCount(combinationRecordService.getCombinationRecordCount(null, Boolean.TRUE));// 获取虚拟成团记录 75 + CombinationRecordStatusEnum.SUCCESS.getStatus(), null, CombinationRecordDO.HEAD_ID_GROUP));
  76 + summaryVO.setVirtualGroupCount(combinationRecordService.getCombinationRecordCount(// 获取虚拟成团记录
  77 + null, Boolean.TRUE, CombinationRecordDO.HEAD_ID_GROUP));
61 return success(summaryVO); 78 return success(summaryVO);
62 } 79 }
63 80
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/combination/vo/recrod/CombinationRecordReqPage2VO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import javax.validation.constraints.NotNull;
  10 +
  11 +@Schema(description = "管理后台 - 拼团记录分页 Request VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class CombinationRecordReqPage2VO extends PageParam {
  16 +
  17 + @Schema(description = "团长编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  18 + @NotNull(message = "团长编号不能为空")
  19 + private Long headId;
  20 +
  21 +}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/activity/AppActivityController.http 0 → 100644
  1 +### /promotion/activity/list-by-spu-ids 获得多个商品,近期参与的每个活动
  2 +GET {{appApi}}/promotion/activity/list-by-spu-ids?spuIds=222&spuIds=633
  3 +Authorization: Bearer {{appToken}}
  4 +Content-Type: application/json
  5 +tenant-id: {{appTenentId}}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/bargain/AppBargainRecordController.java
@@ -46,18 +46,18 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti @@ -46,18 +46,18 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti
46 public class AppBargainRecordController { 46 public class AppBargainRecordController {
47 47
48 @Resource 48 @Resource
  49 + private BargainHelpService bargainHelpService;
  50 + @Resource
49 private BargainRecordService bargainRecordService; 51 private BargainRecordService bargainRecordService;
50 @Resource 52 @Resource
51 private BargainActivityService bargainActivityService; 53 private BargainActivityService bargainActivityService;
52 - @Resource  
53 - private BargainHelpService bargainHelpService;  
54 54
55 @Resource 55 @Resource
  56 + private TradeOrderApi tradeOrderApi;
  57 + @Resource
56 private MemberUserApi memberUserApi; 58 private MemberUserApi memberUserApi;
57 @Resource 59 @Resource
58 private ProductSpuApi productSpuApi; 60 private ProductSpuApi productSpuApi;
59 - @Resource  
60 - private TradeOrderApi tradeOrderApi;  
61 61
62 @GetMapping("/get-summary") 62 @GetMapping("/get-summary")
63 @Operation(summary = "获得砍价记录的概要信息", description = "用于小程序首页") 63 @Operation(summary = "获得砍价记录的概要信息", description = "用于小程序首页")
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/app/combination/AppCombinationRecordController.java
@@ -46,8 +46,8 @@ public class AppCombinationRecordController { @@ -46,8 +46,8 @@ public class AppCombinationRecordController {
46 @Operation(summary = "获得拼团记录的概要信息", description = "用于小程序首页") 46 @Operation(summary = "获得拼团记录的概要信息", description = "用于小程序首页")
47 public CommonResult<AppCombinationRecordSummaryRespVO> getCombinationRecordSummary() { 47 public CommonResult<AppCombinationRecordSummaryRespVO> getCombinationRecordSummary() {
48 AppCombinationRecordSummaryRespVO summary = new AppCombinationRecordSummaryRespVO(); 48 AppCombinationRecordSummaryRespVO summary = new AppCombinationRecordSummaryRespVO();
49 - // 1. 获得拼团记录数量  
50 - Long count = combinationRecordService.getCombinationRecordCount(null, null); 49 + // 1. 获得拼团参与用户数量
  50 + Long count = combinationRecordService.getCombinationRecordCount(null, null, null);
51 if (count == 0) { 51 if (count == 0) {
52 summary.setAvatars(Collections.emptyList()); 52 summary.setAvatars(Collections.emptyList());
53 summary.setUserCount(count); 53 summary.setUserCount(count);
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java
@@ -28,7 +28,7 @@ import org.mapstruct.Mapping; @@ -28,7 +28,7 @@ import org.mapstruct.Mapping;
28 import org.mapstruct.Mappings; 28 import org.mapstruct.Mappings;
29 import org.mapstruct.factory.Mappers; 29 import org.mapstruct.factory.Mappers;
30 30
31 -import java.time.LocalDateTime; 31 +import java.util.ArrayList;
32 import java.util.List; 32 import java.util.List;
33 import java.util.Map; 33 import java.util.Map;
34 34
@@ -114,9 +114,6 @@ public interface CombinationActivityConvert { @@ -114,9 +114,6 @@ public interface CombinationActivityConvert {
114 ProductSpuRespDTO spu, ProductSkuRespDTO sku) { 114 ProductSpuRespDTO spu, ProductSkuRespDTO sku) {
115 return convert(reqDTO).setVirtualGroup(false) 115 return convert(reqDTO).setVirtualGroup(false)
116 .setStatus(CombinationRecordStatusEnum.IN_PROGRESS.getStatus()) // 创建后默认状态为进行中 116 .setStatus(CombinationRecordStatusEnum.IN_PROGRESS.getStatus()) // 创建后默认状态为进行中
117 - .setStartTime(LocalDateTime.now()) // TODO @puhui999:想了下,这个 startTime 应该是团长的;  
118 - // TODO @puhui999:有团长的情况下,expireTime 应该是团长的;  
119 - .setExpireTime(activity.getStartTime().plusHours(activity.getLimitDuration()))  
120 .setUserSize(activity.getUserSize()).setUserCount(1) // 默认就是 1 插入后会接着更新一次所有的拼团记录 117 .setUserSize(activity.getUserSize()).setUserCount(1) // 默认就是 1 插入后会接着更新一次所有的拼团记录
121 // 用户信息 118 // 用户信息
122 .setNickname(user.getNickname()).setAvatar(user.getAvatar()) 119 .setNickname(user.getNickname()).setAvatar(user.getAvatar())
@@ -200,4 +197,35 @@ public interface CombinationActivityConvert { @@ -200,4 +197,35 @@ public interface CombinationActivityConvert {
200 return respVO; 197 return respVO;
201 } 198 }
202 199
  200 + /**
  201 + * 转换生成虚拟成团虚拟记录
  202 + *
  203 + * @param virtualGroupHeadRecords 虚拟成团团长记录列表
  204 + * @return 虚拟记录列表
  205 + */
  206 + default List<CombinationRecordDO> convertVirtualGroupList(List<CombinationRecordDO> virtualGroupHeadRecords) {
  207 + List<CombinationRecordDO> createRecords = new ArrayList<>();
  208 + virtualGroupHeadRecords.forEach(headRecord -> {
  209 + // 计算需要创建的虚拟成团记录数量
  210 + int count = headRecord.getUserSize() - headRecord.getUserCount();
  211 + for (int i = 0; i < count; i++) {
  212 + // 基础信息和团长保持一致
  213 + CombinationRecordDO newRecord = new CombinationRecordDO().setActivityId(headRecord.getActivityId())
  214 + .setCombinationPrice(headRecord.getCombinationPrice()).setSpuId(headRecord.getSpuId()).setSpuName(headRecord.getSpuName())
  215 + .setPicUrl(headRecord.getPicUrl()).setSkuId(headRecord.getSkuId()).setHeadId(headRecord.getId())
  216 + .setStatus(headRecord.getStatus()) // 状态保持和创建时一致,创建完成后会接着处理
  217 + .setVirtualGroup(headRecord.getVirtualGroup()).setExpireTime(headRecord.getExpireTime())
  218 + .setStartTime(headRecord.getStartTime()).setUserSize(headRecord.getUserSize()).setUserCount(headRecord.getUserCount());
  219 + // 虚拟信息
  220 + newRecord.setCount(0);
  221 + newRecord.setUserId(0L);
  222 + newRecord.setNickname("");
  223 + newRecord.setAvatar("");
  224 + newRecord.setOrderId(0L);
  225 + createRecords.add(newRecord);
  226 + }
  227 + });
  228 + return createRecords;
  229 + }
  230 +
203 } 231 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/bargain/BargainActivityMapper.java
@@ -6,13 +6,14 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; @@ -6,13 +6,14 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7 import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityPageReqVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.bargain.vo.activity.BargainActivityPageReqVO;
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.bargain.BargainActivityDO;
  9 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
9 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 10 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
10 import org.apache.ibatis.annotations.Mapper; 11 import org.apache.ibatis.annotations.Mapper;
11 -import org.apache.ibatis.annotations.Select;  
12 12
13 import java.time.LocalDateTime; 13 import java.time.LocalDateTime;
14 import java.util.Collection; 14 import java.util.Collection;
15 import java.util.List; 15 import java.util.List;
  16 +import java.util.Map;
16 17
17 /** 18 /**
18 * 砍价活动 Mapper 19 * 砍价活动 Mapper
@@ -85,25 +86,26 @@ public interface BargainActivityMapper extends BaseMapperX&lt;BargainActivityDO&gt; { @@ -85,25 +86,26 @@ public interface BargainActivityMapper extends BaseMapperX&lt;BargainActivityDO&gt; {
85 .last("LIMIT " + count)); 86 .last("LIMIT " + count));
86 } 87 }
87 88
88 - // TODO @puhui999:一个商品,在统一时间,不会参与多个活动;so 是不是不用 inner join 哈?  
89 - // PS:如果可以参与多个,其实可以这样写 select * from promotion_bargain_activity group by spu_id ORDER BY create_time DESC;通过 group 来过滤 89 + // TODO @puhui999:是不是返回 BargainActivityDO 更干净哈?
90 /** 90 /**
91 - * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录 91 + * 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
92 * 92 *
93 * @param spuIds spu 编号 93 * @param spuIds spu 编号
94 * @param status 状态 94 * @param status 状态
95 - * @return 砍价活动列表 95 + * @return 包含 spuId 和 activityId 的 map 对象列表
96 */ 96 */
97 - @Select("SELECT p1.* " +  
98 - "FROM promotion_bargain_activity p1 " +  
99 - "INNER JOIN ( " +  
100 - " SELECT spu_id, MAX(DISTINCT(create_time)) AS max_create_time " +  
101 - " FROM promotion_bargain_activity " +  
102 - " WHERE spu_id IN #{spuIds} " +  
103 - " GROUP BY spu_id " +  
104 - ") p2 " +  
105 - "ON p1.spu_id = p2.spu_id AND p1.create_time = p2.max_create_time AND p1.status = #{status} " +  
106 - "ORDER BY p1.create_time DESC;")  
107 - List<BargainActivityDO> selectListBySpuIds(Collection<Long> spuIds, Integer status); 97 + default List<Map<String, Object>> selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
  98 + return selectMaps(new QueryWrapper<BargainActivityDO>()
  99 + .select("spu_id AS spuId, MAX(DISTINCT(id)) AS activityId") // 时间越大 id 也越大 直接用 id
  100 + .in("spu_id", spuIds)
  101 + .eq("status", status)
  102 + .groupBy("spu_id"));
  103 + }
  104 +
  105 + default List<BargainActivityDO> selectListByIds(Collection<Long> ids) {
  106 + return selectList(new LambdaQueryWrapperX<BargainActivityDO>()
  107 + .in(BargainActivityDO::getId, ids)
  108 + .orderByDesc(BargainActivityDO::getCreateTime));
  109 + }
108 110
109 } 111 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationActivityMapper.java
@@ -6,12 +6,13 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; @@ -6,12 +6,13 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO; 7 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.activity.CombinationActivityPageReqVO;
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; 8 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
  9 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
9 import org.apache.ibatis.annotations.Mapper; 10 import org.apache.ibatis.annotations.Mapper;
10 import org.apache.ibatis.annotations.Param; 11 import org.apache.ibatis.annotations.Param;
11 -import org.apache.ibatis.annotations.Select;  
12 12
13 import java.util.Collection; 13 import java.util.Collection;
14 import java.util.List; 14 import java.util.List;
  15 +import java.util.Map;
15 16
16 /** 17 /**
17 * 拼团活动 Mapper 18 * 拼团活动 Mapper
@@ -43,24 +44,24 @@ public interface CombinationActivityMapper extends BaseMapperX&lt;CombinationActivi @@ -43,24 +44,24 @@ public interface CombinationActivityMapper extends BaseMapperX&lt;CombinationActivi
43 .last("LIMIT " + count)); 44 .last("LIMIT " + count));
44 } 45 }
45 46
46 - // TODO @puhui999:类似 BargainActivityMapper  
47 /** 47 /**
48 - * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录  
49 - * 48 + * 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
50 * @param spuIds spu 编号 49 * @param spuIds spu 编号
51 * @param status 状态 50 * @param status 状态
52 - * @return 拼团活动列表 51 + * @return 包含 spuId 和 activityId 的 map 对象列表
53 */ 52 */
54 - @Select("SELECT p1.* " +  
55 - "FROM promotion_combination_activity p1 " +  
56 - "INNER JOIN ( " +  
57 - " SELECT spu_id, MAX(DISTINCT(create_time)) AS max_create_time " +  
58 - " FROM promotion_combination_activity " +  
59 - " WHERE spu_id IN #{spuIds} " +  
60 - " GROUP BY spu_id " +  
61 - ") p2 " +  
62 - "ON p1.spu_id = p2.spu_id AND p1.create_time = p2.max_create_time AND p1.status = #{status} " +  
63 - "ORDER BY p1.create_time DESC;")  
64 - List<CombinationActivityDO> selectListBySpuIds(@Param("spuIds") Collection<Long> spuIds, @Param("status") Integer status); 53 + default List<Map<String, Object>> selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(@Param("spuIds") Collection<Long> spuIds, @Param("status") Integer status) {
  54 + return selectMaps(new QueryWrapper<CombinationActivityDO>()
  55 + .select("spu_id AS spuId, MAX(DISTINCT(id)) AS activityId") // 时间越大 id 也越大 直接用 id
  56 + .in("spu_id", spuIds)
  57 + .eq("status", status)
  58 + .groupBy("spu_id"));
  59 + }
  60 +
  61 + default List<CombinationActivityDO> selectListByIds(Collection<Long> ids) {
  62 + return selectList(new LambdaQueryWrapperX<CombinationActivityDO>()
  63 + .in(CombinationActivityDO::getId, ids)
  64 + .orderByDesc(CombinationActivityDO::getCreateTime));
  65 + }
65 66
66 } 67 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationRecordMapper.java
@@ -6,11 +6,13 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -6,11 +6,13 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 6 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
7 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 7 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
8 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 8 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  9 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPage2VO;
9 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO; 10 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;
10 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO; 11 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
11 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 12 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
12 import org.apache.ibatis.annotations.Mapper; 13 import org.apache.ibatis.annotations.Mapper;
13 14
  15 +import java.time.LocalDateTime;
14 import java.util.Collection; 16 import java.util.Collection;
15 import java.util.Collections; 17 import java.util.Collections;
16 import java.util.List; 18 import java.util.List;
@@ -100,13 +102,41 @@ public interface CombinationRecordMapper extends BaseMapperX&lt;CombinationRecordDO @@ -100,13 +102,41 @@ public interface CombinationRecordMapper extends BaseMapperX&lt;CombinationRecordDO
100 .betweenIfPresent(CombinationRecordDO::getCreateTime, pageVO.getCreateTime())); 102 .betweenIfPresent(CombinationRecordDO::getCreateTime, pageVO.getCreateTime()));
101 } 103 }
102 104
103 - // TODO @puhui999:这个最好把 headId 也作为一个参数;因为有个要求 userCount,它要 DISTINCT 下;整体可以参考 selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId  
104 - default Long selectCountByHeadAndStatusAndVirtualGroup(Integer status, Boolean virtualGroup) {  
105 - return selectCount(new LambdaQueryWrapperX<CombinationRecordDO>()  
106 - .eq(status != null || virtualGroup != null,  
107 - CombinationRecordDO::getHeadId, CombinationRecordDO.HEAD_ID_GROUP) // 统计团信息则指定团长  
108 - .eqIfPresent(CombinationRecordDO::getStatus, status)  
109 - .eqIfPresent(CombinationRecordDO::getVirtualGroup, virtualGroup)); 105 + default PageResult<CombinationRecordDO> selectPage(CombinationRecordReqPage2VO pageVO) {
  106 + return selectPage(pageVO, new LambdaQueryWrapperX<CombinationRecordDO>()
  107 + .eq(CombinationRecordDO::getId, pageVO.getHeadId())
  108 + .or()
  109 + .eq(CombinationRecordDO::getHeadId, pageVO.getHeadId()));
  110 + }
  111 +
  112 + /**
  113 + * 查询指定条件的记录数
  114 + * 如果参数都为 null 时则查询用户拼团记录(DISTINCT 去重),也就是说查询会员表中的用户有多少人参与过拼团活动每个人只统计一次
  115 + *
  116 + * @param status 状态,可为 null
  117 + * @param virtualGroup 是否虚拟成团,可为 null
  118 + * @param headId 团长编号,可为 null
  119 + * @return 记录数
  120 + */
  121 + default Long selectCountByHeadAndStatusAndVirtualGroup(Integer status, Boolean virtualGroup, Long headId) {
  122 + return selectCount(new QueryWrapper<CombinationRecordDO>()
  123 + // TODO @puhui999:这种偏逻辑性的,不要给 mapper 哈;可以考虑拆成 2 个 mapper,上层也是 2 个 service;
  124 + .select(status == null && virtualGroup == null && headId == null, "DISTINCT (user_id)")
  125 + .eq(status != null, "status", status)
  126 + .eq(virtualGroup != null, "virtual_group", virtualGroup)
  127 + .eq(headId != null, "head_id", headId)
  128 + .groupBy("user_id"));
  129 + }
  130 +
  131 + default List<CombinationRecordDO> selectListByHeadIdAndStatusAndExpireTimeLt(Long headId, Integer status, LocalDateTime dateTime) {
  132 + return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()
  133 + .eq(CombinationRecordDO::getHeadId, headId)
  134 + .eq(CombinationRecordDO::getStatus, status)
  135 + .lt(CombinationRecordDO::getExpireTime, dateTime));
  136 + }
  137 +
  138 + default List<CombinationRecordDO> selectListByHeadIds(Collection<Long> headIds) {
  139 + return selectList(new LambdaQueryWrapperX<CombinationRecordDO>().in(CombinationRecordDO::getHeadId, headIds));
110 } 140 }
111 141
112 } 142 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java
1 package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity; 1 package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity;
2 2
  3 +import cn.hutool.core.lang.Assert;
3 import cn.hutool.core.util.ObjectUtil; 4 import cn.hutool.core.util.ObjectUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
@@ -7,12 +8,14 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; @@ -7,12 +8,14 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO; 8 import cn.iocoder.yudao.module.promotion.controller.admin.seckill.vo.activity.SeckillActivityPageReqVO;
8 import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO; 9 import cn.iocoder.yudao.module.promotion.controller.app.seckill.vo.activity.AppSeckillActivityPageReqVO;
9 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO; 10 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillActivityDO;
  11 +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
10 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 12 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
11 import org.apache.ibatis.annotations.Mapper; 13 import org.apache.ibatis.annotations.Mapper;
12 -import org.apache.ibatis.annotations.Select; 14 +import org.apache.ibatis.annotations.Param;
13 15
14 import java.util.Collection; 16 import java.util.Collection;
15 import java.util.List; 17 import java.util.List;
  18 +import java.util.Map;
16 19
17 /** 20 /**
18 * 秒杀活动 Mapper 21 * 秒杀活动 Mapper
@@ -37,18 +40,32 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; { @@ -37,18 +40,32 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; {
37 } 40 }
38 41
39 /** 42 /**
40 - * 更新活动库存 43 + * 更新活动库存(减少)
41 * 44 *
42 * @param id 活动编号 45 * @param id 活动编号
43 - * @param count 扣减的库存数量 46 + * @param count 扣减的库存数量(正数)
44 * @return 影响的行数 47 * @return 影响的行数
45 */ 48 */
46 - default int updateStock(Long id, int count) { 49 + default int updateStockDecr(Long id, int count) {
  50 + Assert.isTrue(count > 0);
47 return update(null, new LambdaUpdateWrapper<SeckillActivityDO>() 51 return update(null, new LambdaUpdateWrapper<SeckillActivityDO>()
48 .eq(SeckillActivityDO::getId, id) 52 .eq(SeckillActivityDO::getId, id)
49 - .gt(SeckillActivityDO::getTotalStock, 0)  
50 - .setSql("stock = stock + " + count)  
51 - .setSql("total_stock = total_stock - " + count)); 53 + .gt(SeckillActivityDO::getStock, count)
  54 + .setSql("stock = stock - " + count));
  55 + }
  56 +
  57 + /**
  58 + * 更新活动库存(增加)
  59 + *
  60 + * @param id 活动编号
  61 + * @param count 增加的库存数量(正数)
  62 + * @return 影响的行数
  63 + */
  64 + default int updateStockIncr(Long id, int count) {
  65 + Assert.isTrue(count > 0);
  66 + return update(null, new LambdaUpdateWrapper<SeckillActivityDO>()
  67 + .eq(SeckillActivityDO::getId, id)
  68 + .setSql("stock = stock + " + count));
52 } 69 }
53 70
54 default PageResult<SeckillActivityDO> selectPage(AppSeckillActivityPageReqVO pageReqVO, Integer status) { 71 default PageResult<SeckillActivityDO> selectPage(AppSeckillActivityPageReqVO pageReqVO, Integer status) {
@@ -58,24 +75,25 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; { @@ -58,24 +75,25 @@ public interface SeckillActivityMapper extends BaseMapperX&lt;SeckillActivityDO&gt; {
58 .apply(ObjectUtil.isNotNull(pageReqVO.getConfigId()), "FIND_IN_SET(" + pageReqVO.getConfigId() + ",config_ids) > 0")); 75 .apply(ObjectUtil.isNotNull(pageReqVO.getConfigId()), "FIND_IN_SET(" + pageReqVO.getConfigId() + ",config_ids) > 0"));
59 } 76 }
60 77
61 - // TODO @puhui999:类似 BargainActivityMapper  
62 /** 78 /**
63 - * 获取指定 spu 编号最近参加的活动,每个 spuId 只返回一条记录 79 + * 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
64 * 80 *
65 * @param spuIds spu 编号 81 * @param spuIds spu 编号
66 * @param status 状态 82 * @param status 状态
67 - * @return 秒杀活动列表 83 + * @return 包含 spuId 和 activityId 的 map 对象列表
68 */ 84 */
69 - @Select("SELECT p1.* " +  
70 - "FROM promotion_seckill_activity p1 " +  
71 - "INNER JOIN ( " +  
72 - " SELECT spu_id, MAX(DISTINCT(create_time)) AS max_create_time " +  
73 - " FROM promotion_seckill_activity " +  
74 - " WHERE spu_id IN #{spuIds} " +  
75 - " GROUP BY spu_id " +  
76 - ") p2 " +  
77 - "ON p1.spu_id = p2.spu_id AND p1.create_time = p2.max_create_time AND p1.status = #{status} " +  
78 - "ORDER BY p1.create_time DESC;")  
79 - List<SeckillActivityDO> selectListBySpuIds(Collection<Long> spuIds, Integer status); 85 + default List<Map<String, Object>> selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(@Param("spuIds") Collection<Long> spuIds, @Param("status") Integer status) {
  86 + return selectMaps(new QueryWrapper<SeckillActivityDO>()
  87 + .select("spu_id AS spuId, MAX(DISTINCT(id)) AS activityId") // 时间越大 id 也越大 直接用 id
  88 + .in("spu_id", spuIds)
  89 + .eq("status", status)
  90 + .groupBy("spu_id"));
  91 + }
  92 +
  93 + default List<SeckillActivityDO> selectListByIds(Collection<Long> ids) {
  94 + return selectList(new LambdaQueryWrapperX<SeckillActivityDO>()
  95 + .in(SeckillActivityDO::getId, ids)
  96 + .orderByDesc(SeckillActivityDO::getCreateTime));
  97 + }
80 98
81 } 99 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillProductMapper.java
1 package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity; 1 package cn.iocoder.yudao.module.promotion.dal.mysql.seckill.seckillactivity;
2 2
  3 +import cn.hutool.core.lang.Assert;
3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO; 5 import cn.iocoder.yudao.module.promotion.dal.dataobject.seckill.SeckillProductDO;
5 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 6 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@@ -30,17 +31,32 @@ public interface SeckillProductMapper extends BaseMapperX&lt;SeckillProductDO&gt; { @@ -30,17 +31,32 @@ public interface SeckillProductMapper extends BaseMapperX&lt;SeckillProductDO&gt; {
30 } 31 }
31 32
32 /** 33 /**
33 - * 更新活动库存 34 + * 更新活动库存(减少)
34 * 35 *
35 * @param id 活动编号 36 * @param id 活动编号
36 - * @param count 扣减的库存数量 37 + * @param count 扣减的库存数量(减少库存)
37 * @return 影响的行数 38 * @return 影响的行数
38 */ 39 */
39 - default int updateStock(Long id, int count) { 40 + default int updateStockDecr(Long id, int count) {
  41 + Assert.isTrue(count > 0);
40 return update(null, new LambdaUpdateWrapper<SeckillProductDO>() 42 return update(null, new LambdaUpdateWrapper<SeckillProductDO>()
41 .eq(SeckillProductDO::getId, id) 43 .eq(SeckillProductDO::getId, id)
42 - .gt(SeckillProductDO::getStock, count) 44 + .ge(SeckillProductDO::getStock, count)
43 .setSql("stock = stock - " + count)); 45 .setSql("stock = stock - " + count));
44 } 46 }
45 47
  48 + /**
  49 + * 更新活动库存(增加)
  50 + *
  51 + * @param id 活动编号
  52 + * @param count 需要增加的库存(增加库存)
  53 + * @return 影响的行数
  54 + */
  55 + default int updateStockIncr(Long id, int count) {
  56 + Assert.isTrue(count > 0);
  57 + return update(null, new LambdaUpdateWrapper<SeckillProductDO>()
  58 + .eq(SeckillProductDO::getId, id)
  59 + .setSql("stock = stock + " + count));
  60 + }
  61 +
46 } 62 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/job/combination/CombinationRecordExpireJob.java 0 → 100644
  1 +package cn.iocoder.yudao.module.promotion.job.combination;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.iocoder.yudao.framework.common.core.KeyValue;
  5 +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
  6 +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
  7 +import cn.iocoder.yudao.module.promotion.service.combination.CombinationRecordService;
  8 +import org.springframework.stereotype.Component;
  9 +
  10 +import javax.annotation.Resource;
  11 +
  12 +/**
  13 + * 拼团过期 Job
  14 + *
  15 + * @author HUIHUI
  16 + */
  17 +@Component
  18 +public class CombinationRecordExpireJob implements JobHandler {
  19 +
  20 + @Resource
  21 + private CombinationRecordService combinationRecordService;
  22 +
  23 + @Override
  24 + @TenantJob
  25 + public String execute(String param) {
  26 + KeyValue<Integer, Integer> keyValue = combinationRecordService.expireCombinationRecord();
  27 + return StrUtil.format("过期拼团 {} 个, 虚拟成团 {} 个", keyValue.getKey(), keyValue.getValue());
  28 + }
  29 +
  30 +}
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/bargain/BargainActivityServiceImpl.java
1 package cn.iocoder.yudao.module.promotion.service.bargain; 1 package cn.iocoder.yudao.module.promotion.service.bargain;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.map.MapUtil;
3 import cn.hutool.core.util.ObjUtil; 5 import cn.hutool.core.util.ObjUtil;
4 import cn.hutool.core.util.ObjectUtil; 6 import cn.hutool.core.util.ObjectUtil;
5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
@@ -20,12 +22,11 @@ import org.springframework.validation.annotation.Validated; @@ -20,12 +22,11 @@ import org.springframework.validation.annotation.Validated;
20 22
21 import javax.annotation.Resource; 23 import javax.annotation.Resource;
22 import java.time.LocalDateTime; 24 import java.time.LocalDateTime;
23 -import java.util.Collection;  
24 -import java.util.List;  
25 -import java.util.Set; 25 +import java.util.*;
26 26
27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
28 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch; 28 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;
  29 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
29 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; 30 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
30 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; 31 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
31 32
@@ -84,10 +85,14 @@ public class BargainActivityServiceImpl implements BargainActivityService { @@ -84,10 +85,14 @@ public class BargainActivityServiceImpl implements BargainActivityService {
84 85
85 @Override 86 @Override
86 public void updateBargainActivityStock(Long id, Integer count) { 87 public void updateBargainActivityStock(Long id, Integer count) {
87 - // 更新库存。如果更新失败,则抛出异常  
88 - int updateCount = bargainActivityMapper.updateStock(id, count);  
89 - if (updateCount == 0) {  
90 - throw exception(BARGAIN_ACTIVITY_STOCK_NOT_ENOUGH); 88 + if (count < 0) {
  89 + // 更新库存。如果更新失败,则抛出异常
  90 + int updateCount = bargainActivityMapper.updateStock(id, count);
  91 + if (updateCount == 0) {
  92 + throw exception(BARGAIN_ACTIVITY_STOCK_NOT_ENOUGH);
  93 + }
  94 + } else if (count > 0) {
  95 + bargainActivityMapper.updateStock(id, count);
91 } 96 }
92 } 97 }
93 98
@@ -139,7 +144,7 @@ public class BargainActivityServiceImpl implements BargainActivityService { @@ -139,7 +144,7 @@ public class BargainActivityServiceImpl implements BargainActivityService {
139 144
140 @Override 145 @Override
141 public List<BargainActivityDO> getBargainActivityList(Set<Long> ids) { 146 public List<BargainActivityDO> getBargainActivityList(Set<Long> ids) {
142 - return bargainActivityMapper.selectBatchIds(ids); 147 + return bargainActivityMapper.selectBatchIds(ids);
143 } 148 }
144 149
145 @Override 150 @Override
@@ -178,7 +183,14 @@ public class BargainActivityServiceImpl implements BargainActivityService { @@ -178,7 +183,14 @@ public class BargainActivityServiceImpl implements BargainActivityService {
178 183
179 @Override 184 @Override
180 public List<BargainActivityDO> getBargainActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) { 185 public List<BargainActivityDO> getBargainActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
181 - return bargainActivityMapper.selectListBySpuIds(spuIds, status); 186 + // 1. 查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
  187 + // TODO @puhui999:我想了下,这种是不是只展示当前正在进行中的。已经结束、或者未开始的,可能没啥意义?
  188 + List<Map<String, Object>> spuIdAndActivityIdMaps = bargainActivityMapper.selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(spuIds, status);
  189 + if (CollUtil.isEmpty(spuIdAndActivityIdMaps)) {
  190 + return Collections.emptyList();
  191 + }
  192 + // 2. 查询活动详情
  193 + return bargainActivityMapper.selectListByIds(convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")));
182 } 194 }
183 195
184 } 196 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationActivityServiceImpl.java
1 package cn.iocoder.yudao.module.promotion.service.combination; 1 package cn.iocoder.yudao.module.promotion.service.combination;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.map.MapUtil;
4 import cn.hutool.core.util.ObjectUtil; 5 import cn.hutool.core.util.ObjectUtil;
5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 import cn.iocoder.yudao.framework.common.pojo.PageParam; 7 import cn.iocoder.yudao.framework.common.pojo.PageParam;
@@ -25,12 +26,12 @@ import org.springframework.validation.annotation.Validated; @@ -25,12 +26,12 @@ import org.springframework.validation.annotation.Validated;
25 26
26 import javax.annotation.Resource; 27 import javax.annotation.Resource;
27 import java.util.Collection; 28 import java.util.Collection;
  29 +import java.util.Collections;
28 import java.util.List; 30 import java.util.List;
29 import java.util.Map; 31 import java.util.Map;
30 32
31 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
32 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;  
33 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; 34 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
34 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS; 35 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
35 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS; 36 import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SPU_NOT_EXISTS;
36 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; 37 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
@@ -228,7 +229,13 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic @@ -228,7 +229,13 @@ public class CombinationActivityServiceImpl implements CombinationActivityServic
228 229
229 @Override 230 @Override
230 public List<CombinationActivityDO> getCombinationActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) { 231 public List<CombinationActivityDO> getCombinationActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
231 - return combinationActivityMapper.selectListBySpuIds(spuIds, status); 232 + // 1.查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
  233 + List<Map<String, Object>> spuIdAndActivityIdMaps = combinationActivityMapper.selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(spuIds, status);
  234 + if (CollUtil.isEmpty(spuIdAndActivityIdMaps)) {
  235 + return Collections.emptyList();
  236 + }
  237 + // 2.查询活动详情
  238 + return combinationActivityMapper.selectListByIds(convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")));
232 } 239 }
233 240
234 } 241 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordService.java
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.core.KeyValue; @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.core.KeyValue;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; 5 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
6 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO; 6 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
  7 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPage2VO;
7 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO; 8 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;
8 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; 9 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
9 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO; 10 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationProductDO;
@@ -49,9 +50,9 @@ public interface CombinationRecordService { @@ -49,9 +50,9 @@ public interface CombinationRecordService {
49 * 创建拼团记录 50 * 创建拼团记录
50 * 51 *
51 * @param reqDTO 创建信息 52 * @param reqDTO 创建信息
52 - * @return 开团记录编号 53 + * @return key 开团记录编号 value 团长编号
53 */ 54 */
54 - Long createCombinationRecord(CombinationRecordCreateReqDTO reqDTO); 55 + KeyValue<Long, Long> createCombinationRecord(CombinationRecordCreateReqDTO reqDTO);
55 56
56 /** 57 /**
57 * 获得拼团记录 58 * 获得拼团记录
@@ -90,9 +91,10 @@ public interface CombinationRecordService { @@ -90,9 +91,10 @@ public interface CombinationRecordService {
90 * 91 *
91 * @param status 状态-允许为空 92 * @param status 状态-允许为空
92 * @param virtualGroup 是否虚拟成团-允许为空 93 * @param virtualGroup 是否虚拟成团-允许为空
  94 + * @param headId 团长编号,允许空。目的 headId 设置为 {@link CombinationRecordDO#HEAD_ID_GROUP} 时,可以设置
93 * @return 记录数 95 * @return 记录数
94 */ 96 */
95 - Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup); 97 + Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup, @Nullable Long headId);
96 98
97 /** 99 /**
98 * 获取最近的 count 条拼团记录 100 * 获取最近的 count 条拼团记录
@@ -137,6 +139,14 @@ public interface CombinationRecordService { @@ -137,6 +139,14 @@ public interface CombinationRecordService {
137 PageResult<CombinationRecordDO> getCombinationRecordPage(CombinationRecordReqPageVO pageVO); 139 PageResult<CombinationRecordDO> getCombinationRecordPage(CombinationRecordReqPageVO pageVO);
138 140
139 /** 141 /**
  142 + * 获取拼团记录分页数据(通过团长查询)
  143 + *
  144 + * @param pageVO 分页请求
  145 + * @return 拼团记录分页数据(包括团长的)
  146 + */
  147 + PageResult<CombinationRecordDO> getCombinationRecordPage2(CombinationRecordReqPage2VO pageVO);
  148 +
  149 + /**
140 * 【拼团活动】获得拼团记录数量 Map 150 * 【拼团活动】获得拼团记录数量 Map
141 * 151 *
142 * @param activityIds 活动记录编号数组 152 * @param activityIds 活动记录编号数组
@@ -148,7 +158,6 @@ public interface CombinationRecordService { @@ -148,7 +158,6 @@ public interface CombinationRecordService {
148 @Nullable Integer status, 158 @Nullable Integer status,
149 @Nullable Long headId); 159 @Nullable Long headId);
150 160
151 -  
152 /** 161 /**
153 * 获取拼团记录 162 * 获取拼团记录
154 * 163 *
@@ -167,5 +176,11 @@ public interface CombinationRecordService { @@ -167,5 +176,11 @@ public interface CombinationRecordService {
167 */ 176 */
168 void cancelCombinationRecord(Long userId, Long id, Long headId); 177 void cancelCombinationRecord(Long userId, Long id, Long headId);
169 178
  179 + /**
  180 + * 处理过期拼团
  181 + *
  182 + * @return key 过期拼团数量, value 虚拟成团数量
  183 + */
  184 + KeyValue<Integer, Integer> expireCombinationRecord();
170 185
171 } 186 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.service.combination; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.promotion.service.combination;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
  5 +import cn.hutool.extra.spring.SpringUtil;
5 import cn.iocoder.yudao.framework.common.core.KeyValue; 6 import cn.iocoder.yudao.framework.common.core.KeyValue;
6 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -13,6 +14,7 @@ import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi; @@ -13,6 +14,7 @@ import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
13 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; 14 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
14 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO; 15 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationRecordCreateReqDTO;
15 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO; 16 import cn.iocoder.yudao.module.promotion.api.combination.dto.CombinationValidateJoinRespDTO;
  17 +import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPage2VO;
16 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO; 18 import cn.iocoder.yudao.module.promotion.controller.admin.combination.vo.recrod.CombinationRecordReqPageVO;
17 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert; 19 import cn.iocoder.yudao.module.promotion.convert.combination.CombinationActivityConvert;
18 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO; 20 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationActivityDO;
@@ -21,18 +23,18 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationR @@ -21,18 +23,18 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationR
21 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper; 23 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper;
22 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum; 24 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
23 import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi; 25 import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
24 -import org.springframework.context.annotation.Lazy; 26 +import org.springframework.scheduling.annotation.Async;
25 import org.springframework.stereotype.Service; 27 import org.springframework.stereotype.Service;
26 import org.springframework.transaction.annotation.Transactional; 28 import org.springframework.transaction.annotation.Transactional;
27 import org.springframework.validation.annotation.Validated; 29 import org.springframework.validation.annotation.Validated;
28 30
29 import javax.annotation.Nullable; 31 import javax.annotation.Nullable;
30 import javax.annotation.Resource; 32 import javax.annotation.Resource;
  33 +import java.time.LocalDateTime;
31 import java.util.*; 34 import java.util.*;
32 35
33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 36 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
34 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.findFirst;  
35 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; 37 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
36 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.afterNow; 38 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.afterNow;
37 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.beforeNow; 39 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.beforeNow;
38 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; 40 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
@@ -49,23 +51,20 @@ import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*; @@ -49,23 +51,20 @@ import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
49 public class CombinationRecordServiceImpl implements CombinationRecordService { 51 public class CombinationRecordServiceImpl implements CombinationRecordService {
50 52
51 @Resource 53 @Resource
52 - @Lazy  
53 private CombinationActivityService combinationActivityService; 54 private CombinationActivityService combinationActivityService;
54 @Resource 55 @Resource
55 - private CombinationRecordMapper recordMapper; 56 + private CombinationRecordMapper combinationRecordMapper;
56 57
57 @Resource 58 @Resource
58 private MemberUserApi memberUserApi; 59 private MemberUserApi memberUserApi;
59 @Resource 60 @Resource
60 - @Lazy  
61 private ProductSpuApi productSpuApi; 61 private ProductSpuApi productSpuApi;
62 @Resource 62 @Resource
63 - @Lazy  
64 private ProductSkuApi productSkuApi; 63 private ProductSkuApi productSkuApi;
  64 +
65 @Resource 65 @Resource
66 private TradeOrderApi tradeOrderApi; 66 private TradeOrderApi tradeOrderApi;
67 67
68 - // TODO @芋艿:在详细预览下;  
69 @Override 68 @Override
70 @Transactional(rollbackFor = Exception.class) 69 @Transactional(rollbackFor = Exception.class)
71 public void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId) { 70 public void updateCombinationRecordStatusByUserIdAndOrderId(Integer status, Long userId, Long orderId) {
@@ -73,13 +72,14 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -73,13 +72,14 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
73 CombinationRecordDO record = validateCombinationRecord(userId, orderId); 72 CombinationRecordDO record = validateCombinationRecord(userId, orderId);
74 73
75 // 更新状态 74 // 更新状态
  75 + // TODO @puhui999:不要整个更新,new 一个出来;why?例如说,两个线程都去更新,这样存在相互覆盖的问题
76 record.setStatus(status); 76 record.setStatus(status);
77 - recordMapper.updateById(record); 77 + combinationRecordMapper.updateById(record);
78 } 78 }
79 79
80 private CombinationRecordDO validateCombinationRecord(Long userId, Long orderId) { 80 private CombinationRecordDO validateCombinationRecord(Long userId, Long orderId) {
81 // 校验拼团是否存在 81 // 校验拼团是否存在
82 - CombinationRecordDO recordDO = recordMapper.selectByUserIdAndOrderId(userId, orderId); 82 + CombinationRecordDO recordDO = combinationRecordMapper.selectByUserIdAndOrderId(userId, orderId);
83 if (recordDO == null) { 83 if (recordDO == null) {
84 throw exception(COMBINATION_RECORD_NOT_EXISTS); 84 throw exception(COMBINATION_RECORD_NOT_EXISTS);
85 } 85 }
@@ -108,7 +108,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -108,7 +108,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
108 // 2. 父拼团是否存在,是否已经满了 108 // 2. 父拼团是否存在,是否已经满了
109 if (headId != null) { 109 if (headId != null) {
110 // 2.1. 查询进行中的父拼团 110 // 2.1. 查询进行中的父拼团
111 - CombinationRecordDO record = recordMapper.selectByHeadId(headId, CombinationRecordStatusEnum.IN_PROGRESS.getStatus()); 111 + CombinationRecordDO record = combinationRecordMapper.selectByHeadId(headId, CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
112 if (record == null) { 112 if (record == null) {
113 throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS); 113 throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS);
114 } 114 }
@@ -143,7 +143,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -143,7 +143,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
143 } 143 }
144 144
145 // 6.1 校验是否有拼团记录 145 // 6.1 校验是否有拼团记录
146 - List<CombinationRecordDO> recordList = recordMapper.selectListByUserIdAndActivityId(userId, activityId); 146 + List<CombinationRecordDO> recordList = combinationRecordMapper.selectListByUserIdAndActivityId(userId, activityId);
147 recordList.removeIf(record -> CombinationRecordStatusEnum.isFailed(record.getStatus())); // 取消的订单,不算数 147 recordList.removeIf(record -> CombinationRecordStatusEnum.isFailed(record.getStatus())); // 取消的订单,不算数
148 if (CollUtil.isEmpty(recordList)) { // 如果为空,说明可以参与,直接返回 148 if (CollUtil.isEmpty(recordList)) { // 如果为空,说明可以参与,直接返回
149 return new KeyValue<>(activity, product); 149 return new KeyValue<>(activity, product);
@@ -164,32 +164,34 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -164,32 +164,34 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
164 164
165 @Override 165 @Override
166 @Transactional(rollbackFor = Exception.class) 166 @Transactional(rollbackFor = Exception.class)
167 - public Long createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) { 167 + public KeyValue<Long, Long> createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
168 // 1. 校验拼团活动 168 // 1. 校验拼团活动
169 KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(reqDTO.getUserId(), 169 KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(reqDTO.getUserId(),
170 reqDTO.getActivityId(), reqDTO.getHeadId(), reqDTO.getSkuId(), reqDTO.getCount()); 170 reqDTO.getActivityId(), reqDTO.getHeadId(), reqDTO.getSkuId(), reqDTO.getCount());
171 171
172 - // 2.1 组合数据创建拼团记录 172 + // 2. 组合数据创建拼团记录
173 MemberUserRespDTO user = memberUserApi.getUser(reqDTO.getUserId()); 173 MemberUserRespDTO user = memberUserApi.getUser(reqDTO.getUserId());
174 ProductSpuRespDTO spu = productSpuApi.getSpu(reqDTO.getSpuId()); 174 ProductSpuRespDTO spu = productSpuApi.getSpu(reqDTO.getSpuId());
175 ProductSkuRespDTO sku = productSkuApi.getSku(reqDTO.getSkuId()); 175 ProductSkuRespDTO sku = productSkuApi.getSku(reqDTO.getSkuId());
176 CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO, keyValue.getKey(), user, spu, sku); 176 CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO, keyValue.getKey(), user, spu, sku);
177 - // 2.2 如果是团长需要设置 headId 为 CombinationRecordDO#HEAD_ID_GROUP 177 + // 2.1. 如果是团长需要设置 headId 为 CombinationRecordDO#HEAD_ID_GROUP
178 if (record.getHeadId() == null) { 178 if (record.getHeadId() == null) {
179 - record.setHeadId(CombinationRecordDO.HEAD_ID_GROUP); 179 + record.setStartTime(LocalDateTime.now())
  180 + .setExpireTime(keyValue.getKey().getStartTime().plusHours(keyValue.getKey().getLimitDuration()))
  181 + .setHeadId(CombinationRecordDO.HEAD_ID_GROUP);
  182 + } else {
  183 + // 2.2.有团长的情况下需要设置开始时间和过期时间为团长的
  184 + CombinationRecordDO headRecord = combinationRecordMapper.selectByHeadId(record.getHeadId(),
  185 + CombinationRecordStatusEnum.IN_PROGRESS.getStatus()); // 查询进行中的父拼团
  186 + record.setStartTime(headRecord.getStartTime()).setExpireTime(headRecord.getExpireTime());
180 } 187 }
181 - recordMapper.insert(record); 188 + combinationRecordMapper.insert(record);
182 189
183 - if (ObjUtil.equal(CombinationRecordDO.HEAD_ID_GROUP, record.getHeadId())) {  
184 - return record.getId(); 190 + // 3. 更新拼团记录
  191 + if (ObjUtil.notEqual(CombinationRecordDO.HEAD_ID_GROUP, record.getHeadId())) {
  192 + updateCombinationRecordWhenCreate(reqDTO.getHeadId(), keyValue.getKey());
185 } 193 }
186 -  
187 - // TODO @puhui:是不是这里的更新,放到 order 模块那;支付完成后;  
188 - // 4、更新拼团相关信息到订单  
189 - tradeOrderApi.updateOrderCombinationInfo(record.getOrderId(), record.getActivityId(), record.getId(), record.getHeadId());  
190 - // 4、更新拼团记录  
191 - updateCombinationRecordWhenCreate(reqDTO.getHeadId(), keyValue.getKey());  
192 - return record.getId(); 194 + return new KeyValue<>(record.getId(), record.getHeadId());
193 } 195 }
194 196
195 /** 197 /**
@@ -204,31 +206,33 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -204,31 +206,33 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
204 if (CollUtil.isEmpty(records)) { 206 if (CollUtil.isEmpty(records)) {
205 return; 207 return;
206 } 208 }
207 - CombinationRecordDO headRecord = recordMapper.selectById(headId); 209 + CombinationRecordDO headRecord = combinationRecordMapper.selectById(headId);
208 210
209 // 2. 批量更新记录 211 // 2. 批量更新记录
210 List<CombinationRecordDO> updateRecords = new ArrayList<>(); 212 List<CombinationRecordDO> updateRecords = new ArrayList<>();
211 records.add(headRecord); // 加入团长,团长也需要更新 213 records.add(headRecord); // 加入团长,团长也需要更新
212 boolean isFull = records.size() >= activity.getUserSize(); 214 boolean isFull = records.size() >= activity.getUserSize();
  215 + LocalDateTime now = LocalDateTime.now();
213 records.forEach(item -> { 216 records.forEach(item -> {
214 CombinationRecordDO updateRecord = new CombinationRecordDO(); 217 CombinationRecordDO updateRecord = new CombinationRecordDO();
215 updateRecord.setId(item.getId()).setUserCount(records.size()); 218 updateRecord.setId(item.getId()).setUserCount(records.size());
216 if (isFull) { 219 if (isFull) {
217 updateRecord.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus()); 220 updateRecord.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus());
  221 + updateRecord.setEndTime(now);
218 } 222 }
219 updateRecords.add(updateRecord); 223 updateRecords.add(updateRecord);
220 }); 224 });
221 - recordMapper.updateBatch(updateRecords); 225 + combinationRecordMapper.updateBatch(updateRecords);
222 } 226 }
223 227
224 @Override 228 @Override
225 public CombinationRecordDO getCombinationRecord(Long userId, Long orderId) { 229 public CombinationRecordDO getCombinationRecord(Long userId, Long orderId) {
226 - return recordMapper.selectByUserIdAndOrderId(userId, orderId); 230 + return combinationRecordMapper.selectByUserIdAndOrderId(userId, orderId);
227 } 231 }
228 232
229 @Override 233 @Override
230 public List<CombinationRecordDO> getCombinationRecordListByUserIdAndActivityId(Long userId, Long activityId) { 234 public List<CombinationRecordDO> getCombinationRecordListByUserIdAndActivityId(Long userId, Long activityId) {
231 - return recordMapper.selectListByUserIdAndActivityId(userId, activityId); 235 + return combinationRecordMapper.selectListByUserIdAndActivityId(userId, activityId);
232 } 236 }
233 237
234 @Override 238 @Override
@@ -241,52 +245,57 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -241,52 +245,57 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
241 } 245 }
242 246
243 @Override 247 @Override
244 - public Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup) {  
245 - return recordMapper.selectCountByHeadAndStatusAndVirtualGroup(status, virtualGroup); 248 + public Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup, @Nullable Long headId) {
  249 + return combinationRecordMapper.selectCountByHeadAndStatusAndVirtualGroup(status, virtualGroup, headId);
246 } 250 }
247 251
248 @Override 252 @Override
249 public List<CombinationRecordDO> getLatestCombinationRecordList(int count) { 253 public List<CombinationRecordDO> getLatestCombinationRecordList(int count) {
250 - return recordMapper.selectLatestList(count); 254 + return combinationRecordMapper.selectLatestList(count);
251 } 255 }
252 256
253 @Override 257 @Override
254 public List<CombinationRecordDO> getHeadCombinationRecordList(Long activityId, Integer status, Integer count) { 258 public List<CombinationRecordDO> getHeadCombinationRecordList(Long activityId, Integer status, Integer count) {
255 - return recordMapper.selectListByActivityIdAndStatusAndHeadId(activityId, status, 259 + return combinationRecordMapper.selectListByActivityIdAndStatusAndHeadId(activityId, status,
256 CombinationRecordDO.HEAD_ID_GROUP, count); 260 CombinationRecordDO.HEAD_ID_GROUP, count);
257 } 261 }
258 262
259 @Override 263 @Override
260 public CombinationRecordDO getCombinationRecordById(Long id) { 264 public CombinationRecordDO getCombinationRecordById(Long id) {
261 - return recordMapper.selectById(id); 265 + return combinationRecordMapper.selectById(id);
262 } 266 }
263 267
264 @Override 268 @Override
265 public List<CombinationRecordDO> getCombinationRecordListByHeadId(Long headId) { 269 public List<CombinationRecordDO> getCombinationRecordListByHeadId(Long headId) {
266 - return recordMapper.selectList(CombinationRecordDO::getHeadId, headId); 270 + return combinationRecordMapper.selectList(CombinationRecordDO::getHeadId, headId);
267 } 271 }
268 272
269 @Override 273 @Override
270 public PageResult<CombinationRecordDO> getCombinationRecordPage(CombinationRecordReqPageVO pageVO) { 274 public PageResult<CombinationRecordDO> getCombinationRecordPage(CombinationRecordReqPageVO pageVO) {
271 - return recordMapper.selectPage(pageVO); 275 + return combinationRecordMapper.selectPage(pageVO);
  276 + }
  277 +
  278 + @Override
  279 + public PageResult<CombinationRecordDO> getCombinationRecordPage2(CombinationRecordReqPage2VO pageVO) {
  280 + return combinationRecordMapper.selectPage(pageVO);
272 } 281 }
273 282
274 @Override 283 @Override
275 public Map<Long, Integer> getCombinationRecordCountMapByActivity(Collection<Long> activityIds, 284 public Map<Long, Integer> getCombinationRecordCountMapByActivity(Collection<Long> activityIds,
276 @Nullable Integer status, @Nullable Long headId) { 285 @Nullable Integer status, @Nullable Long headId) {
277 - return recordMapper.selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId(activityIds, status, headId); 286 + return combinationRecordMapper.selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId(activityIds, status, headId);
278 } 287 }
279 288
280 @Override 289 @Override
281 public CombinationRecordDO getCombinationRecordByIdAndUser(Long userId, Long id) { 290 public CombinationRecordDO getCombinationRecordByIdAndUser(Long userId, Long id) {
282 - return recordMapper.selectOne(CombinationRecordDO::getUserId, userId, CombinationRecordDO::getId, id); 291 + return combinationRecordMapper.selectOne(CombinationRecordDO::getUserId, userId, CombinationRecordDO::getId, id);
283 } 292 }
284 293
285 @Override 294 @Override
286 @Transactional(rollbackFor = Exception.class) 295 @Transactional(rollbackFor = Exception.class)
287 public void cancelCombinationRecord(Long userId, Long id, Long headId) { 296 public void cancelCombinationRecord(Long userId, Long id, Long headId) {
288 // 删除记录 297 // 删除记录
289 - recordMapper.deleteById(id); 298 + combinationRecordMapper.deleteById(id);
290 299
291 // 需要更新的记录 300 // 需要更新的记录
292 List<CombinationRecordDO> updateRecords = new ArrayList<>(); 301 List<CombinationRecordDO> updateRecords = new ArrayList<>();
@@ -313,7 +322,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -313,7 +322,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
313 }); 322 });
314 } else { // 情况二:团员 323 } else { // 情况二:团员
315 // 团长 324 // 团长
316 - CombinationRecordDO recordHead = recordMapper.selectById(headId); 325 + CombinationRecordDO recordHead = combinationRecordMapper.selectById(headId);
317 // 团员 326 // 团员
318 List<CombinationRecordDO> records = getCombinationRecordListByHeadId(headId); 327 List<CombinationRecordDO> records = getCombinationRecordListByHeadId(headId);
319 if (CollUtil.isEmpty(records)) { 328 if (CollUtil.isEmpty(records)) {
@@ -329,7 +338,114 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -329,7 +338,114 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
329 } 338 }
330 339
331 // 更新拼团记录 340 // 更新拼团记录
332 - recordMapper.updateBatch(updateRecords); 341 + combinationRecordMapper.updateBatch(updateRecords);
  342 + }
  343 +
  344 + @Override
  345 + public KeyValue<Integer, Integer> expireCombinationRecord() {
  346 + // TODO @puhui999:数字一般是 1. 2. 这种格式哈
  347 + // 1。获取所有正在进行中的过期的父拼团
  348 + List<CombinationRecordDO> headExpireRecords = combinationRecordMapper.selectListByHeadIdAndStatusAndExpireTimeLt(
  349 + CombinationRecordDO.HEAD_ID_GROUP, CombinationRecordStatusEnum.IN_PROGRESS.getStatus(), LocalDateTime.now());
  350 + if (CollUtil.isEmpty(headExpireRecords)) {
  351 + return new KeyValue<>(0, 0);
  352 + }
  353 +
  354 + // 2. 获取拼团活动
  355 + // TODO @puhui999:在自己模块里,变量可以简略点;例如说 activityList
  356 + List<CombinationActivityDO> combinationActivities = combinationActivityService.getCombinationActivityListByIds(
  357 + convertSet(headExpireRecords, CombinationRecordDO::getActivityId));
  358 + Map<Long, CombinationActivityDO> activityMap = convertMap(combinationActivities, CombinationActivityDO::getId);
  359 +
  360 + // TODO @puhui999:job 一般不建议异步跑;因为可能下次跑,结果上次还没跑完;
  361 + // TODO 这里,我们可以每个 record 处理下;然后按照是否需要虚拟拼团,各搞一个方法逻辑 + 事务;这样,保证 job 里面尽量不要大事务,而是 n 个独立小事务的处理。
  362 + // 3. 校验是否虚拟成团
  363 + List<CombinationRecordDO> virtualGroupHeadRecords = new ArrayList<>(); // 虚拟成团
  364 + for (Iterator<CombinationRecordDO> iterator = headExpireRecords.iterator(); iterator.hasNext(); ) {
  365 + CombinationRecordDO record = iterator.next();
  366 + // 3.1 不匹配,则直接跳过
  367 + CombinationActivityDO activity = activityMap.get(record.getActivityId());
  368 + if (activity == null || !activity.getVirtualGroup()) { // 取不到活动的或者不是虚拟拼团的
  369 + continue;
  370 + }
  371 + // 3.2 匹配,则移除,添加到虚拟成团中,并结束寻找
  372 + virtualGroupHeadRecords.add(record);
  373 + iterator.remove();
  374 + break;
  375 + }
  376 +
  377 + // 4.处理过期的拼团
  378 + getSelf().handleExpireRecord(headExpireRecords);
  379 + // 5.虚拟成团
  380 + getSelf().handleVirtualGroupRecord(virtualGroupHeadRecords);
  381 + return new KeyValue<>(headExpireRecords.size(), virtualGroupHeadRecords.size());
  382 + }
  383 +
  384 + @Async
  385 + protected void handleExpireRecord(List<CombinationRecordDO> headExpireRecords) {
  386 + if (CollUtil.isEmpty(headExpireRecords)) {
  387 + return;
  388 + }
  389 +
  390 + // 1.更新拼团记录
  391 + List<CombinationRecordDO> headsAndRecords = updateBatchCombinationRecords(headExpireRecords,
  392 + CombinationRecordStatusEnum.FAILED);
  393 + if (headsAndRecords == null) {
  394 + return;
  395 + }
  396 +
  397 + // 2.订单取消 TODO 以现在的取消回滚逻辑好像只能循环了
  398 + headsAndRecords.forEach(item -> {
  399 + tradeOrderApi.cancelPaidOrder(item.getUserId(), item.getOrderId());
  400 + });
  401 + }
  402 +
  403 + @Async
  404 + protected void handleVirtualGroupRecord(List<CombinationRecordDO> virtualGroupHeadRecords) {
  405 + if (CollUtil.isEmpty(virtualGroupHeadRecords)) {
  406 + return;
  407 + }
  408 +
  409 + // 1. 团员补齐
  410 + combinationRecordMapper.insertBatch(CombinationActivityConvert.INSTANCE.convertVirtualGroupList(virtualGroupHeadRecords));
  411 + // 2. 更新拼团记录
  412 + updateBatchCombinationRecords(virtualGroupHeadRecords, CombinationRecordStatusEnum.SUCCESS);
  413 + }
  414 +
  415 + private List<CombinationRecordDO> updateBatchCombinationRecords(List<CombinationRecordDO> headRecords, CombinationRecordStatusEnum status) {
  416 + // 1. 查询团成员
  417 + List<CombinationRecordDO> records = combinationRecordMapper.selectListByHeadIds(
  418 + convertSet(headRecords, CombinationRecordDO::getId));
  419 + if (CollUtil.isEmpty(records)) {
  420 + return null;
  421 + }
  422 + Map<Long, List<CombinationRecordDO>> recordsMap = convertMultiMap(records, CombinationRecordDO::getHeadId);
  423 + headRecords.forEach(item -> {
  424 + recordsMap.get(item.getId()).add(item); // 把团长加进团里
  425 + });
  426 + // 2.批量更新拼团记录 status 和 失败/成团时间
  427 + List<CombinationRecordDO> headsAndRecords = mergeValuesFromMap(recordsMap);
  428 + List<CombinationRecordDO> updateRecords = new ArrayList<>(headsAndRecords.size());
  429 + LocalDateTime now = LocalDateTime.now();
  430 + headsAndRecords.forEach(item -> {
  431 + CombinationRecordDO record = new CombinationRecordDO().setId(item.getId())
  432 + .setStatus(status.getStatus()).setEndTime(now);
  433 + if (CombinationRecordStatusEnum.isSuccess(status.getStatus())) { // 虚拟成团完事更改状态成功后还需要把参与人数修改为成团需要人数
  434 + record.setUserCount(record.getUserSize());
  435 + }
  436 + updateRecords.add(record);
  437 + });
  438 + combinationRecordMapper.updateBatch(updateRecords);
  439 + return headsAndRecords;
  440 + }
  441 +
  442 + /**
  443 + * 获得自身的代理对象,解决 AOP 生效问题
  444 + *
  445 + * @return 自己
  446 + */
  447 + private CombinationRecordServiceImpl getSelf() {
  448 + return SpringUtil.getBean(getClass());
333 } 449 }
334 450
335 } 451 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityService.java
@@ -36,13 +36,22 @@ public interface SeckillActivityService { @@ -36,13 +36,22 @@ public interface SeckillActivityService {
36 void updateSeckillActivity(@Valid SeckillActivityUpdateReqVO updateReqVO); 36 void updateSeckillActivity(@Valid SeckillActivityUpdateReqVO updateReqVO);
37 37
38 /** 38 /**
39 - * 更新秒杀库存 39 + * 更新秒杀库存(减少)
40 * 40 *
41 * @param id 活动编号 41 * @param id 活动编号
42 * @param skuId sku 编号 42 * @param skuId sku 编号
43 - * @param count 数量 43 + * @param count 数量(正数)
44 */ 44 */
45 - void updateSeckillStock(Long id, Long skuId, Integer count); 45 + void updateSeckillStockDecr(Long id, Long skuId, Integer count);
  46 +
  47 + /**
  48 + * 更新秒杀库存(增加)
  49 + *
  50 + * @param id 活动编号
  51 + * @param skuId sku 编号
  52 + * @param count 数量(正数)
  53 + */
  54 + void updateSeckillStockIncr(Long id, Long skuId, Integer count);
46 55
47 /** 56 /**
48 * 关闭秒杀活动 57 * 关闭秒杀活动
@@ -113,8 +122,8 @@ public interface SeckillActivityService { @@ -113,8 +122,8 @@ public interface SeckillActivityService {
113 * 如果校验失败,则抛出业务异常 122 * 如果校验失败,则抛出业务异常
114 * 123 *
115 * @param activityId 活动编号 124 * @param activityId 活动编号
116 - * @param skuId SKU 编号  
117 - * @param count 数量 125 + * @param skuId SKU 编号
  126 + * @param count 数量
118 * @return 秒杀信息 127 * @return 秒杀信息
119 */ 128 */
120 SeckillValidateJoinRespDTO validateJoinSeckill(Long activityId, Long skuId, Integer count); 129 SeckillValidateJoinRespDTO validateJoinSeckill(Long activityId, Long skuId, Integer count);
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/seckill/SeckillActivityServiceImpl.java
1 package cn.iocoder.yudao.module.promotion.service.seckill; 1 package cn.iocoder.yudao.module.promotion.service.seckill;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.hutool.core.collection.CollectionUtil; 4 import cn.hutool.core.collection.CollectionUtil;
  5 +import cn.hutool.core.map.MapUtil;
4 import cn.hutool.core.util.ObjectUtil; 6 import cn.hutool.core.util.ObjectUtil;
5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -27,6 +29,7 @@ import org.springframework.validation.annotation.Validated; @@ -27,6 +29,7 @@ import org.springframework.validation.annotation.Validated;
27 29
28 import javax.annotation.Resource; 30 import javax.annotation.Resource;
29 import java.util.Collection; 31 import java.util.Collection;
  32 +import java.util.Collections;
30 import java.util.List; 33 import java.util.List;
31 import java.util.Map; 34 import java.util.Map;
32 35
@@ -154,7 +157,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { @@ -154,7 +157,7 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
154 157
155 @Override 158 @Override
156 @Transactional(rollbackFor = Exception.class) 159 @Transactional(rollbackFor = Exception.class)
157 - public void updateSeckillStock(Long id, Long skuId, Integer count) { 160 + public void updateSeckillStockDecr(Long id, Long skuId, Integer count) {
158 // 1.1 校验活动库存是否充足 161 // 1.1 校验活动库存是否充足
159 SeckillActivityDO seckillActivity = validateSeckillActivityExists(id); 162 SeckillActivityDO seckillActivity = validateSeckillActivityExists(id);
160 if (count > seckillActivity.getTotalStock()) { 163 if (count > seckillActivity.getTotalStock()) {
@@ -167,18 +170,28 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { @@ -167,18 +170,28 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
167 } 170 }
168 171
169 // 2.1 更新活动商品库存 172 // 2.1 更新活动商品库存
170 - int updateCount = seckillProductMapper.updateStock(product.getId(), count); 173 + int updateCount = seckillProductMapper.updateStockDecr(product.getId(), count);
171 if (updateCount == 0) { 174 if (updateCount == 0) {
172 throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL); 175 throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
173 } 176 }
174 177
175 // 2.2 更新活动库存 178 // 2.2 更新活动库存
176 - updateCount = seckillActivityMapper.updateStock(seckillActivity.getId(), count); 179 + updateCount = seckillActivityMapper.updateStockDecr(seckillActivity.getId(), count);
177 if (updateCount == 0) { 180 if (updateCount == 0) {
178 throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL); 181 throw exception(SECKILL_ACTIVITY_UPDATE_STOCK_FAIL);
179 } 182 }
180 } 183 }
181 184
  185 + @Override
  186 + @Transactional(rollbackFor = Exception.class)
  187 + public void updateSeckillStockIncr(Long id, Long skuId, Integer count) {
  188 + SeckillProductDO product = seckillProductMapper.selectByActivityIdAndSkuId(id, skuId);
  189 + // 更新活动商品库存
  190 + seckillProductMapper.updateStockIncr(product.getId(), count);
  191 + // 更新活动库存
  192 + seckillActivityMapper.updateStockIncr(id, count);
  193 + }
  194 +
182 /** 195 /**
183 * 更新秒杀商品 196 * 更新秒杀商品
184 * 197 *
@@ -312,7 +325,13 @@ public class SeckillActivityServiceImpl implements SeckillActivityService { @@ -312,7 +325,13 @@ public class SeckillActivityServiceImpl implements SeckillActivityService {
312 325
313 @Override 326 @Override
314 public List<SeckillActivityDO> getSeckillActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) { 327 public List<SeckillActivityDO> getSeckillActivityBySpuIdsAndStatus(Collection<Long> spuIds, Integer status) {
315 - return seckillActivityMapper.selectListBySpuIds(spuIds, status); 328 + // 1.查询出指定 spuId 的 spu 参加的活动最接近现在的一条记录。多个的话,一个 spuId 对应一个最近的活动编号
  329 + List<Map<String, Object>> spuIdAndActivityIdMaps = seckillActivityMapper.selectSpuIdAndActivityIdMapsBySpuIdsAndStatus(spuIds, status);
  330 + if (CollUtil.isEmpty(spuIdAndActivityIdMaps)) {
  331 + return Collections.emptyList();
  332 + }
  333 + // 2.查询活动详情
  334 + return seckillActivityMapper.selectListByIds(convertSet(spuIdAndActivityIdMaps, map -> MapUtil.getLong(map, "activityId")));
316 } 335 }
317 336
318 } 337 }
yudao-module-mall/yudao-module-statistics-biz/pom.xml
@@ -59,6 +59,11 @@ @@ -59,6 +59,11 @@
59 <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId> 59 <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
60 </dependency> 60 </dependency>
61 61
  62 + <dependency>
  63 + <groupId>cn.iocoder.boot</groupId>
  64 + <artifactId>yudao-spring-boot-starter-biz-ip</artifactId>
  65 + </dependency>
  66 +
62 <!-- Web 相关 --> 67 <!-- Web 相关 -->
63 <dependency> 68 <dependency>
64 <groupId>cn.iocoder.boot</groupId> 69 <groupId>cn.iocoder.boot</groupId>
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/MemberStatisticsController.java
1 package cn.iocoder.yudao.module.statistics.controller.admin.member; 1 package cn.iocoder.yudao.module.statistics.controller.admin.member;
2 2
  3 +import cn.hutool.core.util.ArrayUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 -import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAreaStatisticsRespVO;  
5 -import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSexStatisticsRespVO;  
6 -import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSummaryRespVO;  
7 -import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberTerminalStatisticsRespVO;  
8 -import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeStatisticsComparisonRespVO; 6 +import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.*;
  7 +import cn.iocoder.yudao.module.statistics.service.member.MemberStatisticsService;
9 import io.swagger.v3.oas.annotations.Operation; 8 import io.swagger.v3.oas.annotations.Operation;
10 import io.swagger.v3.oas.annotations.tags.Tag; 9 import io.swagger.v3.oas.annotations.tags.Tag;
11 import lombok.extern.slf4j.Slf4j; 10 import lombok.extern.slf4j.Slf4j;
  11 +import org.springframework.security.access.prepost.PreAuthorize;
12 import org.springframework.validation.annotation.Validated; 12 import org.springframework.validation.annotation.Validated;
13 import org.springframework.web.bind.annotation.GetMapping; 13 import org.springframework.web.bind.annotation.GetMapping;
14 import org.springframework.web.bind.annotation.RequestMapping; 14 import org.springframework.web.bind.annotation.RequestMapping;
15 import org.springframework.web.bind.annotation.RestController; 15 import org.springframework.web.bind.annotation.RestController;
16 16
  17 +import javax.annotation.Resource;
17 import java.util.List; 18 import java.util.List;
18 19
  20 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  21 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  22 +
19 @Tag(name = "管理后台 - 会员统计") 23 @Tag(name = "管理后台 - 会员统计")
20 @RestController 24 @RestController
21 @RequestMapping("/statistics/member") 25 @RequestMapping("/statistics/member")
@@ -23,36 +27,47 @@ import java.util.List; @@ -23,36 +27,47 @@ import java.util.List;
23 @Slf4j 27 @Slf4j
24 public class MemberStatisticsController { 28 public class MemberStatisticsController {
25 29
26 - // TODO @疯狂:一个类似 getTradeTrendSummaryComparison 的接口  
27 - // TODO @疯狂:一个类似 getTradeStatisticsList 的接口 30 + @Resource
  31 + private MemberStatisticsService memberStatisticsService;
28 32
29 @GetMapping("/summary") 33 @GetMapping("/summary")
30 @Operation(summary = "获得会员统计") 34 @Operation(summary = "获得会员统计")
31 - public CommonResult<TradeStatisticsComparisonRespVO<MemberSummaryRespVO>> getMemberSummary() {  
32 - // TODO 疯狂:目前先直接计算;  
33 - return null; 35 + @PreAuthorize("@ss.hasPermission('statistics:member:query')")
  36 + public CommonResult<MemberSummaryRespVO> getMemberSummary() {
  37 + return success(memberStatisticsService.getMemberSummary());
  38 + }
  39 +
  40 + @GetMapping("/analyse")
  41 + @Operation(summary = "获得会员分析数据")
  42 + @PreAuthorize("@ss.hasPermission('statistics:member:query')")
  43 + public CommonResult<MemberAnalyseRespVO> getMemberAnalyse(MemberAnalyseReqVO reqVO) {
  44 + return success(memberStatisticsService.getMemberAnalyse(
  45 + ArrayUtil.get(reqVO.getTimes(), 0), ArrayUtil.get(reqVO.getTimes(), 1)));
34 } 46 }
35 47
36 @GetMapping("/get-area-statistics-list") 48 @GetMapping("/get-area-statistics-list")
37 @Operation(summary = "按照省份,获得会员统计列表") 49 @Operation(summary = "按照省份,获得会员统计列表")
  50 + @PreAuthorize("@ss.hasPermission('statistics:member:query')")
38 public CommonResult<List<MemberAreaStatisticsRespVO>> getMemberAreaStatisticsList() { 51 public CommonResult<List<MemberAreaStatisticsRespVO>> getMemberAreaStatisticsList() {
39 - // TODO 疯狂:目前先直接计算,进行统计;后续再考虑优化  
40 - return null; 52 + return success(memberStatisticsService.getMemberAreaStatisticsList());
41 } 53 }
42 54
43 @GetMapping("/get-sex-statistics-list") 55 @GetMapping("/get-sex-statistics-list")
44 @Operation(summary = "按照性别,获得会员统计列表") 56 @Operation(summary = "按照性别,获得会员统计列表")
  57 + @PreAuthorize("@ss.hasPermission('statistics:member:query')")
45 public CommonResult<List<MemberSexStatisticsRespVO>> getMemberSexStatisticsList() { 58 public CommonResult<List<MemberSexStatisticsRespVO>> getMemberSexStatisticsList() {
46 - // TODO 疯狂:目前先直接计算,进行统计;后续再考虑优化  
47 - return null; 59 + return success(memberStatisticsService.getMemberSexStatisticsList());
48 } 60 }
49 61
50 @GetMapping("/get-terminal-statistics-list") 62 @GetMapping("/get-terminal-statistics-list")
51 @Operation(summary = "按照终端,获得会员统计列表") 63 @Operation(summary = "按照终端,获得会员统计列表")
  64 + @PreAuthorize("@ss.hasPermission('statistics:member:query')")
52 public CommonResult<List<MemberTerminalStatisticsRespVO>> getMemberTerminalStatisticsList() { 65 public CommonResult<List<MemberTerminalStatisticsRespVO>> getMemberTerminalStatisticsList() {
53 - // TODO 疯狂:目前先直接计算,进行统计;后续再考虑优化  
54 // TODO 疯狂:这个可以晚点写,因为 user = = 上还没记录 terminal 66 // TODO 疯狂:这个可以晚点写,因为 user = = 上还没记录 terminal
55 - return null; 67 + List<MemberTerminalStatisticsRespVO> list = convertList(TerminalEnum.values(),
  68 + t -> new MemberTerminalStatisticsRespVO()
  69 + .setTerminal(t.getTerminal()).setUserCount(t.getTerminal() * 100));
  70 + return success(list);
56 } 71 }
57 72
58 } 73 }
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/package-info.java deleted
1 -/**  
2 - * TODO  
3 - * 1. 会员总数据  
4 - * 2. 性别统计  
5 - * 3. 渠道统计  
6 - * 4. 地域统计  
7 - * 5. 会员概览  
8 - */  
9 -package cn.iocoder.yudao.module.statistics.controller.admin.member;  
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/vo/MemberAnalyseComparisonRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +
  6 +@Schema(description = "管理后台 - 会员分析对照数据 Response VO")
  7 +@Data
  8 +public class MemberAnalyseComparisonRespVO {
  9 +
  10 + @Schema(description = "会员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  11 + private Integer userCount;
  12 +
  13 + @Schema(description = "活跃用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  14 + private Integer activeUserCount;
  15 +
  16 + @Schema(description = "充值会员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "221")
  17 + private Integer rechargeUserCount;
  18 +
  19 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/vo/MemberAnalyseReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.Data;
  5 +import org.springframework.format.annotation.DateTimeFormat;
  6 +
  7 +import java.time.LocalDateTime;
  8 +
  9 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  10 +
  11 +@Schema(description = "管理后台 - 会员分析 Request VO")
  12 +@Data
  13 +public class MemberAnalyseReqVO {
  14 +
  15 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  16 + @Schema(description = "时间范围")
  17 + private LocalDateTime[] times;
  18 +
  19 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/vo/MemberAnalyseRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;
  2 +
  3 +import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeStatisticsComparisonRespVO;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +@Schema(description = "管理后台 - 会员分析 Response VO")
  8 +@Data
  9 +public class MemberAnalyseRespVO {
  10 +
  11 + @Schema(description = "访客数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  12 + private Integer visitorCount;
  13 +
  14 + @Schema(description = "下单用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  15 + private Integer orderUserCount;
  16 +
  17 + @Schema(description = "成交用户数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  18 + private Integer payUserCount;
  19 +
  20 + @Schema(description = "客单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  21 + private Integer atv;
  22 +
  23 + @Schema(description = "对照数据", requiredMode = Schema.RequiredMode.REQUIRED)
  24 + private TradeStatisticsComparisonRespVO<MemberAnalyseComparisonRespVO> comparison;
  25 +
  26 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/vo/MemberSexStatisticsRespVO.java
@@ -13,11 +13,4 @@ public class MemberSexStatisticsRespVO { @@ -13,11 +13,4 @@ public class MemberSexStatisticsRespVO {
13 @Schema(description = "会员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 13 @Schema(description = "会员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
14 private Integer userCount; 14 private Integer userCount;
15 15
16 - @Schema(description = "订单创建数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
17 - private Integer orderCreateCount;  
18 - @Schema(description = "订单支付数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")  
19 - private Integer orderPayCount;  
20 - @Schema(description = "订单支付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "622")  
21 - private Integer orderPayPrice;  
22 -  
23 } 16 }
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/vo/MemberSummaryRespVO.java
@@ -15,6 +15,7 @@ public class MemberSummaryRespVO { @@ -15,6 +15,7 @@ public class MemberSummaryRespVO {
15 15
16 @Schema(description = "充值金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 16 @Schema(description = "充值金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
17 private Integer rechargePrice; 17 private Integer rechargePrice;
  18 +
18 @Schema(description = "支出金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 19 @Schema(description = "支出金额", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
19 private Integer expensePrice; 20 private Integer expensePrice;
20 21
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/controller/admin/member/vo/MemberTerminalStatisticsRespVO.java
@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.statistics.controller.admin.member.vo; @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.statistics.controller.admin.member.vo;
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 4 import lombok.Data;
5 5
6 -@Schema(description = "管理后台 - 终端性别统计 Response VO") 6 +@Schema(description = "管理后台 - 会员终端统计 Response VO")
7 @Data 7 @Data
8 public class MemberTerminalStatisticsRespVO { 8 public class MemberTerminalStatisticsRespVO {
9 9
@@ -13,11 +13,4 @@ public class MemberTerminalStatisticsRespVO { @@ -13,11 +13,4 @@ public class MemberTerminalStatisticsRespVO {
13 @Schema(description = "会员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") 13 @Schema(description = "会员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
14 private Integer userCount; 14 private Integer userCount;
15 15
16 - @Schema(description = "订单创建数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")  
17 - private Integer orderCreateCount;  
18 - @Schema(description = "订单支付数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")  
19 - private Integer orderPayCount;  
20 - @Schema(description = "订单支付金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "622")  
21 - private Integer orderPayPrice;  
22 -  
23 } 16 }
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/convert/member/MemberStatisticsConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.convert.member;
  2 +
  3 +import cn.hutool.core.map.MapUtil;
  4 +import cn.hutool.core.util.ObjUtil;
  5 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  6 +import cn.iocoder.yudao.framework.ip.core.Area;
  7 +import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAreaStatisticsRespVO;
  8 +import org.mapstruct.Mapper;
  9 +import org.mapstruct.factory.Mappers;
  10 +
  11 +import java.util.List;
  12 +import java.util.Map;
  13 +import java.util.Optional;
  14 +
  15 +/**
  16 + * 会员统计 Convert
  17 + *
  18 + * @author owen
  19 + */
  20 +@Mapper
  21 +public interface MemberStatisticsConvert {
  22 +
  23 + MemberStatisticsConvert INSTANCE = Mappers.getMapper(MemberStatisticsConvert.class);
  24 +
  25 + default List<MemberAreaStatisticsRespVO> convertList(List<Area> areaList,
  26 + Map<Integer, Integer> userCountMap,
  27 + Map<Integer, MemberAreaStatisticsRespVO> orderMap) {
  28 + return CollectionUtils.convertList(areaList, area -> {
  29 + MemberAreaStatisticsRespVO orderVo = Optional.ofNullable(orderMap.get(area.getId())).orElseGet(MemberAreaStatisticsRespVO::new);
  30 + return new MemberAreaStatisticsRespVO()
  31 + .setAreaId(area.getId()).setAreaName(area.getName())
  32 + .setUserCount(MapUtil.getInt(userCountMap, area.getId(), 0))
  33 + .setOrderCreateCount(ObjUtil.defaultIfNull(orderVo.getOrderCreateCount(), 0))
  34 + .setOrderPayCount(ObjUtil.defaultIfNull(orderVo.getOrderPayCount(), 0))
  35 + .setOrderPayPrice(ObjUtil.defaultIfNull(orderVo.getOrderPayPrice(), 0));
  36 + });
  37 + }
  38 +
  39 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/convert/trade/TradeStatisticsConvert.java
1 package cn.iocoder.yudao.module.statistics.convert.trade; 1 package cn.iocoder.yudao.module.statistics.convert.trade;
2 2
3 -import cn.iocoder.yudao.module.pay.api.wallet.dto.WalletSummaryRespDTO; 3 +import cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO;
4 import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeStatisticsComparisonRespVO; 4 import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeStatisticsComparisonRespVO;
5 import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeSummaryRespVO; 5 import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeSummaryRespVO;
6 import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryExcelVO; 6 import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryExcelVO;
7 import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryRespVO; 7 import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryRespVO;
8 import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO; 8 import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;
  9 +import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeOrderSummaryRespBO;
9 import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO; 10 import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO;
10 -import cn.iocoder.yudao.module.trade.api.aftersale.dto.AfterSaleSummaryRespDTO;  
11 -import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderSummaryRespDTO; 11 +import cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO;
12 import org.mapstruct.Mapper; 12 import org.mapstruct.Mapper;
13 import org.mapstruct.factory.Mappers; 13 import org.mapstruct.factory.Mappers;
14 14
@@ -46,8 +46,8 @@ public interface TradeStatisticsConvert { @@ -46,8 +46,8 @@ public interface TradeStatisticsConvert {
46 46
47 List<TradeTrendSummaryExcelVO> convertList02(List<TradeTrendSummaryRespVO> list); 47 List<TradeTrendSummaryExcelVO> convertList02(List<TradeTrendSummaryRespVO> list);
48 48
49 - TradeStatisticsDO convert(LocalDateTime time, TradeOrderSummaryRespDTO orderSummary,  
50 - AfterSaleSummaryRespDTO afterSaleSummary, Integer brokerageSettlementPrice,  
51 - WalletSummaryRespDTO walletSummary); 49 + TradeStatisticsDO convert(LocalDateTime time, TradeOrderSummaryRespBO orderSummary,
  50 + AfterSaleSummaryRespBO afterSaleSummary, Integer brokerageSettlementPrice,
  51 + WalletSummaryRespBO walletSummary);
52 52
53 } 53 }
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/infra/ApiAccessLogStatisticsMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.dal.mysql.infra;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import org.apache.ibatis.annotations.Mapper;
  5 +import org.apache.ibatis.annotations.Param;
  6 +
  7 +import java.time.LocalDateTime;
  8 +
  9 +/**
  10 + * API 访问日志的统计 Mapper
  11 + *
  12 + * @author owen
  13 + */
  14 +@Mapper
  15 +public interface ApiAccessLogStatisticsMapper extends BaseMapperX<Object> {
  16 +
  17 + Integer selectCountByIp(@Param("beginTime") LocalDateTime beginTime,
  18 + @Param("endTime") LocalDateTime endTime);
  19 +
  20 + Integer selectCountByUserId(@Param("beginTime") LocalDateTime beginTime,
  21 + @Param("endTime") LocalDateTime endTime);
  22 +
  23 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/member/MemberStatisticsMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.dal.mysql.member;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAreaStatisticsRespVO;
  5 +import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSexStatisticsRespVO;
  6 +import org.apache.ibatis.annotations.Mapper;
  7 +import org.apache.ibatis.annotations.Param;
  8 +
  9 +import java.time.LocalDateTime;
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * 会员信息的统计 Mapper
  14 + *
  15 + * @author owen
  16 + */
  17 +@Mapper
  18 +public interface MemberStatisticsMapper extends BaseMapperX<Object> {
  19 +
  20 + List<MemberAreaStatisticsRespVO> selectSummaryListByAreaId();
  21 +
  22 + List<MemberSexStatisticsRespVO> selectSummaryListBySex();
  23 +
  24 + Integer selectUserCount(@Param("beginTime") LocalDateTime beginTime,
  25 + @Param("endTime") LocalDateTime endTime);
  26 +
  27 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/pay/PayWalletStatisticsMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.dal.mysql.pay;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSummaryRespVO;
  5 +import cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO;
  6 +import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;
  7 +import org.apache.ibatis.annotations.Mapper;
  8 +import org.apache.ibatis.annotations.Param;
  9 +
  10 +import java.time.LocalDateTime;
  11 +
  12 +/**
  13 + * 支付钱包的统计 Mapper
  14 + *
  15 + * @author owen
  16 + */
  17 +@Mapper
  18 +public interface PayWalletStatisticsMapper extends BaseMapperX<TradeStatisticsDO> {
  19 +
  20 + WalletSummaryRespBO selectRechargeSummaryByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime,
  21 + @Param("endTime") LocalDateTime endTime,
  22 + @Param("payStatus") Boolean payStatus);
  23 +
  24 + WalletSummaryRespBO selectRechargeSummaryByRefundTimeBetween(@Param("beginTime") LocalDateTime beginTime,
  25 + @Param("endTime") LocalDateTime endTime,
  26 + @Param("refundStatus") Integer refundStatus);
  27 +
  28 + Integer selectPriceSummaryByBizTypeAndCreateTimeBetween(@Param("beginTime") LocalDateTime beginTime,
  29 + @Param("endTime") LocalDateTime endTime,
  30 + @Param("bizType") Integer bizType);
  31 +
  32 + // TODO @疯狂:是不是搞个单独的 BO 呀;
  33 + MemberSummaryRespVO selectRechargeSummaryGroupByWalletId(@Param("beginTime") LocalDateTime beginTime,
  34 + @Param("endTime") LocalDateTime endTime,
  35 + @Param("payStatus") Boolean payStatus);
  36 +
  37 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/trade/AfterSaleStatisticsMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.dal.mysql.trade;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;
  5 +import cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO;
  6 +import org.apache.ibatis.annotations.Mapper;
  7 +import org.apache.ibatis.annotations.Param;
  8 +
  9 +import java.time.LocalDateTime;
  10 +
  11 +/**
  12 + * 售后订单的统计 Mapper
  13 + *
  14 + * @author owen
  15 + */
  16 +@Mapper
  17 +public interface AfterSaleStatisticsMapper extends BaseMapperX<TradeStatisticsDO> {
  18 +
  19 + AfterSaleSummaryRespBO selectSummaryByRefundTimeBetween(@Param("beginTime") LocalDateTime beginTime,
  20 + @Param("endTime") LocalDateTime endTime);
  21 +
  22 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/trade/BrokerageStatisticsMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.dal.mysql.trade;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;
  5 +import org.apache.ibatis.annotations.Mapper;
  6 +import org.apache.ibatis.annotations.Param;
  7 +
  8 +import java.time.LocalDateTime;
  9 +
  10 +/**
  11 + * 订单分销的统计 Mapper
  12 + *
  13 + * @author owen
  14 + */
  15 +@Mapper
  16 +public interface BrokerageStatisticsMapper extends BaseMapperX<TradeStatisticsDO> {
  17 +
  18 + Integer selectSummaryPriceByStatusAndUnfreezeTimeBetween(@Param("bizType") Integer bizType,
  19 + @Param("status") Integer status,
  20 + @Param("beginTime") LocalDateTime beginTime,
  21 + @Param("endTime") LocalDateTime endTime);
  22 +
  23 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/trade/TradeOrderStatisticsMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.dal.mysql.trade;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAreaStatisticsRespVO;
  5 +import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;
  6 +import org.apache.ibatis.annotations.Mapper;
  7 +import org.apache.ibatis.annotations.Param;
  8 +
  9 +import java.time.LocalDateTime;
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * 交易订单的统计 Mapper
  14 + *
  15 + * @author owen
  16 + */
  17 +@Mapper
  18 +public interface TradeOrderStatisticsMapper extends BaseMapperX<TradeStatisticsDO> {
  19 +
  20 + List<MemberAreaStatisticsRespVO> selectSummaryListByAreaId();
  21 +
  22 + Integer selectCountByCreateTimeBetween(@Param("beginTime") LocalDateTime beginTime,
  23 + @Param("endTime") LocalDateTime endTime);
  24 +
  25 + Integer selectCountByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime,
  26 + @Param("endTime") LocalDateTime endTime);
  27 +
  28 + Integer selectSummaryPriceByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime,
  29 + @Param("endTime") LocalDateTime endTime);
  30 +
  31 + Integer selectUserCountByCreateTimeBetween(@Param("beginTime") LocalDateTime beginTime,
  32 + @Param("endTime") LocalDateTime endTime);
  33 +
  34 + Integer selectUserCountByPayTimeBetween(@Param("beginTime") LocalDateTime beginTime,
  35 + @Param("endTime") LocalDateTime endTime);
  36 +
  37 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/dal/mysql/trade/TradeStatisticsMapper.java
@@ -19,6 +19,7 @@ import java.util.List; @@ -19,6 +19,7 @@ import java.util.List;
19 @Mapper 19 @Mapper
20 public interface TradeStatisticsMapper extends BaseMapperX<TradeStatisticsDO> { 20 public interface TradeStatisticsMapper extends BaseMapperX<TradeStatisticsDO> {
21 21
  22 + // TODO @疯狂:这个要不要也挪到 xml 里,保持统一?
22 @Select("SELECT IFNULL(SUM(order_create_count), 0) AS count, IFNULL(SUM(order_pay_price), 0) AS summary " + 23 @Select("SELECT IFNULL(SUM(order_create_count), 0) AS count, IFNULL(SUM(order_pay_price), 0) AS summary " +
23 "FROM trade_statistics " + 24 "FROM trade_statistics " +
24 "WHERE time BETWEEN #{beginTime} AND #{endTime} AND deleted = FALSE") 25 "WHERE time BETWEEN #{beginTime} AND #{endTime} AND deleted = FALSE")
@@ -30,4 +31,8 @@ public interface TradeStatisticsMapper extends BaseMapperX&lt;TradeStatisticsDO&gt; { @@ -30,4 +31,8 @@ public interface TradeStatisticsMapper extends BaseMapperX&lt;TradeStatisticsDO&gt; {
30 31
31 List<TradeTrendSummaryRespVO> selectListByTimeBetween(@Param("beginTime") LocalDateTime beginTime, 32 List<TradeTrendSummaryRespVO> selectListByTimeBetween(@Param("beginTime") LocalDateTime beginTime,
32 @Param("endTime") LocalDateTime endTime); 33 @Param("endTime") LocalDateTime endTime);
  34 +
  35 + Integer selectExpensePriceByTimeBetween(@Param("beginTime") LocalDateTime beginTime,
  36 + @Param("endTime") LocalDateTime endTime);
  37 +
33 } 38 }
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/infra/ApiAccessLogStatisticsService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.service.infra;
  2 +
  3 +import java.time.LocalDateTime;
  4 +
  5 +/**
  6 + * API 访问日志的统计 Service 接口
  7 + *
  8 + * @author owen
  9 + */
  10 +public interface ApiAccessLogStatisticsService {
  11 +
  12 + /**
  13 + * 获取活跃用户数量
  14 + *
  15 + * @param beginTime 起始时间
  16 + * @param endTime 截止时间
  17 + * @return 活跃用户数量
  18 + */
  19 + Integer getActiveUserCount(LocalDateTime beginTime, LocalDateTime endTime);
  20 +
  21 + /**
  22 + * 获取访问用户数量
  23 + *
  24 + * @param beginTime 起始时间
  25 + * @param endTime 截止时间
  26 + * @return 访问用户数量
  27 + */
  28 + Integer getVisitorUserCount(LocalDateTime beginTime, LocalDateTime endTime);
  29 +
  30 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/infra/ApiAccessLogStatisticsServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.service.infra;
  2 +
  3 +import cn.iocoder.yudao.module.statistics.dal.mysql.infra.ApiAccessLogStatisticsMapper;
  4 +import org.springframework.stereotype.Service;
  5 +import org.springframework.validation.annotation.Validated;
  6 +
  7 +import javax.annotation.Resource;
  8 +import java.time.LocalDateTime;
  9 +
  10 +/**
  11 + * API 访问日志的统计 Service 实现类
  12 + *
  13 + * @author owen
  14 + */
  15 +@Service
  16 +@Validated
  17 +public class ApiAccessLogStatisticsServiceImpl implements ApiAccessLogStatisticsService {
  18 +
  19 + @Resource
  20 + private ApiAccessLogStatisticsMapper apiAccessLogStatisticsMapper;
  21 +
  22 + @Override
  23 + public Integer getActiveUserCount(LocalDateTime beginTime, LocalDateTime endTime) {
  24 + return apiAccessLogStatisticsMapper.selectCountByUserId(beginTime, endTime);
  25 + }
  26 +
  27 + @Override
  28 + public Integer getVisitorUserCount(LocalDateTime beginTime, LocalDateTime endTime) {
  29 + return apiAccessLogStatisticsMapper.selectCountByIp(beginTime, endTime);
  30 + }
  31 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/member/MemberStatisticsService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.service.member;
  2 +
  3 +import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAnalyseRespVO;
  4 +import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAreaStatisticsRespVO;
  5 +import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSexStatisticsRespVO;
  6 +import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSummaryRespVO;
  7 +
  8 +import java.time.LocalDateTime;
  9 +import java.util.List;
  10 +
  11 +/**
  12 + * 会员信息的统计 Service 接口
  13 + *
  14 + * @author owen
  15 + */
  16 +public interface MemberStatisticsService {
  17 +
  18 + /**
  19 + * 获取会员统计
  20 + *
  21 + * @return 会员统计
  22 + */
  23 + MemberSummaryRespVO getMemberSummary();
  24 +
  25 + /**
  26 + * 按照省份,获得会员统计列表
  27 + *
  28 + * @return 会员统计列表
  29 + */
  30 + List<MemberAreaStatisticsRespVO> getMemberAreaStatisticsList();
  31 +
  32 + /**
  33 + * 按照性别,获得会员统计列表
  34 + *
  35 + * @return 会员统计列表
  36 + */
  37 + List<MemberSexStatisticsRespVO> getMemberSexStatisticsList();
  38 +
  39 + /**
  40 + * 获取用户分析数据
  41 + *
  42 + * @param beginTime 起始时间
  43 + * @param endTime 截止时间
  44 + * @return 用户分析数据
  45 + */
  46 + MemberAnalyseRespVO getMemberAnalyse(LocalDateTime beginTime, LocalDateTime endTime);
  47 +
  48 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/member/MemberStatisticsServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.service.member;
  2 +
  3 +import cn.hutool.core.util.NumberUtil;
  4 +import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
  5 +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
  6 +import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.*;
  7 +import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeStatisticsComparisonRespVO;
  8 +import cn.iocoder.yudao.module.statistics.convert.member.MemberStatisticsConvert;
  9 +import cn.iocoder.yudao.module.statistics.dal.mysql.member.MemberStatisticsMapper;
  10 +import cn.iocoder.yudao.module.statistics.service.infra.ApiAccessLogStatisticsService;
  11 +import cn.iocoder.yudao.module.statistics.service.pay.PayWalletStatisticsService;
  12 +import cn.iocoder.yudao.module.statistics.service.trade.TradeOrderStatisticsService;
  13 +import cn.iocoder.yudao.module.statistics.service.trade.TradeStatisticsService;
  14 +import org.springframework.stereotype.Service;
  15 +import org.springframework.validation.annotation.Validated;
  16 +
  17 +import javax.annotation.Resource;
  18 +import java.time.Duration;
  19 +import java.time.LocalDateTime;
  20 +import java.util.List;
  21 +import java.util.Map;
  22 +import java.util.Optional;
  23 +
  24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  25 +
  26 +/**
  27 + * 会员信息的统计 Service 实现类
  28 + *
  29 + * @author owen
  30 + */
  31 +@Service
  32 +@Validated
  33 +public class MemberStatisticsServiceImpl implements MemberStatisticsService {
  34 +
  35 + @Resource
  36 + private MemberStatisticsMapper memberStatisticsMapper;
  37 +
  38 + @Resource
  39 + private PayWalletStatisticsService payWalletStatisticsService;
  40 + @Resource
  41 + private TradeStatisticsService tradeStatisticsService;
  42 + @Resource
  43 + private TradeOrderStatisticsService tradeOrderStatisticsService;
  44 + @Resource
  45 + private ApiAccessLogStatisticsService apiAccessLogStatisticsService;
  46 +
  47 + @Override
  48 + public MemberSummaryRespVO getMemberSummary() {
  49 + MemberSummaryRespVO vo = payWalletStatisticsService.getUserRechargeSummary(null, null);
  50 + Integer expensePrice = tradeStatisticsService.getExpensePrice(null, null);
  51 + Integer userCount = memberStatisticsMapper.selectUserCount(null, null);
  52 + // 拼接数据
  53 + if (vo == null) {
  54 + vo = new MemberSummaryRespVO().setRechargeUserCount(0).setRechargePrice(0);
  55 + }
  56 + return vo.setUserCount(userCount).setExpensePrice(expensePrice);
  57 + }
  58 +
  59 + @Override
  60 + public List<MemberAreaStatisticsRespVO> getMemberAreaStatisticsList() {
  61 + // 统计用户
  62 + Map<Integer, Integer> userCountMap = convertMap(memberStatisticsMapper.selectSummaryListByAreaId(),
  63 + vo -> AreaUtils.getParentIdByType(vo.getAreaId(), AreaTypeEnum.PROVINCE),
  64 + MemberAreaStatisticsRespVO::getUserCount, Integer::sum);
  65 + // 统计订单
  66 + Map<Integer, MemberAreaStatisticsRespVO> orderMap = convertMap(tradeOrderStatisticsService.getSummaryListByAreaId(),
  67 + vo -> AreaUtils.getParentIdByType(vo.getAreaId(), AreaTypeEnum.PROVINCE),
  68 + vo -> vo,
  69 + (a, b) -> new MemberAreaStatisticsRespVO()
  70 + .setOrderCreateCount(a.getOrderCreateCount() + b.getOrderCreateCount())
  71 + .setOrderPayCount(a.getOrderPayCount() + b.getOrderPayCount())
  72 + .setOrderPayPrice(a.getOrderPayPrice() + b.getOrderPayPrice()));
  73 + // 拼接数据
  74 + return MemberStatisticsConvert.INSTANCE.convertList(AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area), userCountMap, orderMap);
  75 + }
  76 +
  77 + @Override
  78 + public List<MemberSexStatisticsRespVO> getMemberSexStatisticsList() {
  79 + return memberStatisticsMapper.selectSummaryListBySex();
  80 + }
  81 +
  82 + @Override
  83 + public MemberAnalyseRespVO getMemberAnalyse(LocalDateTime beginTime, LocalDateTime endTime) {
  84 + // 对照数据
  85 + MemberAnalyseComparisonRespVO vo = getMemberAnalyseComparisonData(beginTime, endTime);
  86 + LocalDateTime referenceBeginTime = beginTime.minus(Duration.between(beginTime, endTime));
  87 + MemberAnalyseComparisonRespVO reference = getMemberAnalyseComparisonData(referenceBeginTime, beginTime);
  88 +
  89 + Integer payUserCount = tradeOrderStatisticsService.getPayUserCount(beginTime, endTime);
  90 + // 计算客单价
  91 + int atv = 0;
  92 + if (payUserCount != null && payUserCount > 0) {
  93 + Integer payPrice = tradeOrderStatisticsService.getOrderPayPrice(beginTime, endTime);
  94 + atv = NumberUtil.div(payPrice, payUserCount).intValue();
  95 + }
  96 + return new MemberAnalyseRespVO()
  97 + .setVisitorCount(apiAccessLogStatisticsService.getVisitorUserCount(beginTime, endTime))
  98 + .setOrderUserCount(tradeOrderStatisticsService.getOrderUserCount(beginTime, endTime))
  99 + .setPayUserCount(payUserCount)
  100 + .setAtv(atv)
  101 + .setComparison(new TradeStatisticsComparisonRespVO<>(vo, reference));
  102 + }
  103 +
  104 + private MemberAnalyseComparisonRespVO getMemberAnalyseComparisonData(LocalDateTime beginTime, LocalDateTime endTime) {
  105 + Integer rechargeUserCount = Optional.ofNullable(payWalletStatisticsService.getUserRechargeSummary(beginTime, endTime))
  106 + .map(MemberSummaryRespVO::getRechargeUserCount).orElse(0);
  107 + return new MemberAnalyseComparisonRespVO()
  108 + .setUserCount(memberStatisticsMapper.selectUserCount(beginTime, endTime))
  109 + .setActiveUserCount(apiAccessLogStatisticsService.getActiveUserCount(beginTime, endTime))
  110 + .setRechargeUserCount(rechargeUserCount);
  111 + }
  112 +
  113 +}
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/wallet/PayWalletApi.java renamed to yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/pay/PayWalletStatisticsService.java
1 -package cn.iocoder.yudao.module.pay.api.wallet; 1 +package cn.iocoder.yudao.module.statistics.service.pay;
2 2
3 -import cn.iocoder.yudao.module.pay.api.wallet.dto.WalletSummaryRespDTO; 3 +import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSummaryRespVO;
  4 +import cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO;
4 5
5 import java.time.LocalDateTime; 6 import java.time.LocalDateTime;
6 7
7 /** 8 /**
8 - * 钱包 API 接口 9 + * 钱包的统计 Service 接口
9 * 10 *
10 * @author owen 11 * @author owen
11 */ 12 */
12 -public interface PayWalletApi { 13 +public interface PayWalletStatisticsService {
13 14
14 /** 15 /**
15 * 获取钱包统计 16 * 获取钱包统计
@@ -18,6 +19,15 @@ public interface PayWalletApi { @@ -18,6 +19,15 @@ public interface PayWalletApi {
18 * @param endTime 截止时间 19 * @param endTime 截止时间
19 * @return 钱包统计 20 * @return 钱包统计
20 */ 21 */
21 - WalletSummaryRespDTO getWalletSummary(LocalDateTime beginTime, LocalDateTime endTime); 22 + WalletSummaryRespBO getWalletSummary(LocalDateTime beginTime, LocalDateTime endTime);
  23 +
  24 + /**
  25 + * 获取钱包充值统计
  26 + *
  27 + * @param beginTime 起始时间
  28 + * @param endTime 截止时间
  29 + * @return 钱包充值统计
  30 + */
  31 + MemberSummaryRespVO getUserRechargeSummary(LocalDateTime beginTime, LocalDateTime endTime);
22 32
23 } 33 }
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/pay/PayWalletStatisticsServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.service.pay;
  2 +
  3 +import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
  4 +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
  5 +import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSummaryRespVO;
  6 +import cn.iocoder.yudao.module.statistics.dal.mysql.pay.PayWalletStatisticsMapper;
  7 +import cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO;
  8 +import org.springframework.stereotype.Service;
  9 +import org.springframework.validation.annotation.Validated;
  10 +
  11 +import javax.annotation.Resource;
  12 +import java.time.LocalDateTime;
  13 +
  14 +/**
  15 + * 钱包的统计 Service 实现类
  16 + *
  17 + * @author owen
  18 + */
  19 +@Service
  20 +@Validated
  21 +public class PayWalletStatisticsServiceImpl implements PayWalletStatisticsService {
  22 +
  23 + @Resource
  24 + private PayWalletStatisticsMapper payWalletStatisticsMapper;
  25 +
  26 + @Override
  27 + public WalletSummaryRespBO getWalletSummary(LocalDateTime beginTime, LocalDateTime endTime) {
  28 + WalletSummaryRespBO paySummary = payWalletStatisticsMapper.selectRechargeSummaryByPayTimeBetween(
  29 + beginTime, endTime, true);
  30 + WalletSummaryRespBO refundSummary = payWalletStatisticsMapper.selectRechargeSummaryByRefundTimeBetween(
  31 + beginTime, endTime, PayRefundStatusEnum.SUCCESS.getStatus());
  32 + Integer walletPayPrice = payWalletStatisticsMapper.selectPriceSummaryByBizTypeAndCreateTimeBetween(
  33 + beginTime, endTime, PayWalletBizTypeEnum.PAYMENT.getType());
  34 + // 拼接
  35 + paySummary.setOrderWalletPayPrice(walletPayPrice)
  36 + .setRechargeRefundCount(refundSummary.getRechargeRefundCount())
  37 + .setRechargeRefundPrice(refundSummary.getRechargeRefundPrice());
  38 + return paySummary;
  39 + }
  40 +
  41 + @Override
  42 + public MemberSummaryRespVO getUserRechargeSummary(LocalDateTime beginTime, LocalDateTime endTime) {
  43 + return payWalletStatisticsMapper.selectRechargeSummaryGroupByWalletId(beginTime, endTime, true);
  44 + }
  45 +
  46 +}
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/aftersale/TradeAfterSaleApi.java renamed to yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/AfterSaleStatisticsService.java
1 -package cn.iocoder.yudao.module.trade.api.aftersale; 1 +package cn.iocoder.yudao.module.statistics.service.trade;
2 2
3 -import cn.iocoder.yudao.module.trade.api.aftersale.dto.AfterSaleSummaryRespDTO; 3 +import cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO;
4 4
5 import java.time.LocalDateTime; 5 import java.time.LocalDateTime;
6 6
7 /** 7 /**
8 - * 售后 API 接口 8 + * 售后统计 Service 接口
9 * 9 *
10 * @author owen 10 * @author owen
11 */ 11 */
12 -public interface TradeAfterSaleApi { 12 +public interface AfterSaleStatisticsService {
13 13
14 /** 14 /**
15 * 获取售后单统计 15 * 获取售后单统计
@@ -18,6 +18,6 @@ public interface TradeAfterSaleApi { @@ -18,6 +18,6 @@ public interface TradeAfterSaleApi {
18 * @param endTime 截止时间 18 * @param endTime 截止时间
19 * @return 售后统计结果 19 * @return 售后统计结果
20 */ 20 */
21 - AfterSaleSummaryRespDTO getAfterSaleSummary(LocalDateTime beginTime, LocalDateTime endTime); 21 + AfterSaleSummaryRespBO getAfterSaleSummary(LocalDateTime beginTime, LocalDateTime endTime);
22 22
23 } 23 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/aftersale/TradeAfterSaleApiImpl.java renamed to yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/AfterSaleStatisticsServiceImpl.java
1 -package cn.iocoder.yudao.module.trade.api.aftersale; 1 +package cn.iocoder.yudao.module.statistics.service.trade;
2 2
3 -import cn.iocoder.yudao.module.trade.api.aftersale.dto.AfterSaleSummaryRespDTO;  
4 -import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService; 3 +import cn.iocoder.yudao.module.statistics.dal.mysql.trade.AfterSaleStatisticsMapper;
  4 +import cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO;
5 import org.springframework.stereotype.Service; 5 import org.springframework.stereotype.Service;
6 import org.springframework.validation.annotation.Validated; 6 import org.springframework.validation.annotation.Validated;
7 7
@@ -9,20 +9,20 @@ import javax.annotation.Resource; @@ -9,20 +9,20 @@ import javax.annotation.Resource;
9 import java.time.LocalDateTime; 9 import java.time.LocalDateTime;
10 10
11 /** 11 /**
12 - * 售后 API 接口实现类 12 + * 售后统计 Service 实现类
13 * 13 *
14 * @author owen 14 * @author owen
15 */ 15 */
16 @Service 16 @Service
17 @Validated 17 @Validated
18 -public class TradeAfterSaleApiImpl implements TradeAfterSaleApi { 18 +public class AfterSaleStatisticsServiceImpl implements AfterSaleStatisticsService {
19 19
20 @Resource 20 @Resource
21 - private AfterSaleService afterSaleService; 21 + private AfterSaleStatisticsMapper afterSaleStatisticsMapper;
22 22
23 @Override 23 @Override
24 - public AfterSaleSummaryRespDTO getAfterSaleSummary(LocalDateTime beginTime, LocalDateTime endTime) {  
25 - return afterSaleService.getAfterSaleSummary(beginTime, endTime); 24 + public AfterSaleSummaryRespBO getAfterSaleSummary(LocalDateTime beginTime, LocalDateTime endTime) {
  25 + return afterSaleStatisticsMapper.selectSummaryByRefundTimeBetween(beginTime, endTime);
26 } 26 }
27 27
28 } 28 }
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/TradeBrokerageApi.java renamed to yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/BrokerageStatisticsService.java
1 -package cn.iocoder.yudao.module.trade.api.brokerage; 1 +package cn.iocoder.yudao.module.statistics.service.trade;
2 2
3 import java.time.LocalDateTime; 3 import java.time.LocalDateTime;
4 4
5 /** 5 /**
6 - * 分销 API 接口 6 + * 分销统计 Service 接口
7 * 7 *
8 * @author owen 8 * @author owen
9 */ 9 */
10 -public interface TradeBrokerageApi { 10 +public interface BrokerageStatisticsService {
11 11
12 /** 12 /**
13 * 获取已结算的佣金金额 13 * 获取已结算的佣金金额
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/brokerage/TradeBrokerageApiImpl.java renamed to yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/BrokerageStatisticsServiceImpl.java
1 -package cn.iocoder.yudao.module.trade.api.brokerage; 1 +package cn.iocoder.yudao.module.statistics.service.trade;
2 2
3 -import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService; 3 +import cn.iocoder.yudao.module.statistics.dal.mysql.trade.BrokerageStatisticsMapper;
  4 +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
  5 +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordStatusEnum;
4 import org.springframework.stereotype.Service; 6 import org.springframework.stereotype.Service;
5 import org.springframework.validation.annotation.Validated; 7 import org.springframework.validation.annotation.Validated;
6 8
@@ -8,20 +10,22 @@ import javax.annotation.Resource; @@ -8,20 +10,22 @@ import javax.annotation.Resource;
8 import java.time.LocalDateTime; 10 import java.time.LocalDateTime;
9 11
10 /** 12 /**
11 - * 订单 API 接口实现类 13 + * 分销统计 Service 实现类
12 * 14 *
13 - * @author HUIHUI 15 + * @author owen
14 */ 16 */
15 @Service 17 @Service
16 @Validated 18 @Validated
17 -public class TradeBrokerageApiImpl implements TradeBrokerageApi { 19 +public class BrokerageStatisticsServiceImpl implements BrokerageStatisticsService {
18 20
19 @Resource 21 @Resource
20 - private BrokerageRecordService brokerageRecordService; 22 + private BrokerageStatisticsMapper brokerageStatisticsMapper;
21 23
22 @Override 24 @Override
23 public Integer getBrokerageSettlementPriceSummary(LocalDateTime beginTime, LocalDateTime endTime) { 25 public Integer getBrokerageSettlementPriceSummary(LocalDateTime beginTime, LocalDateTime endTime) {
24 - return brokerageRecordService.getBrokerageSettlementPriceSummary(beginTime, endTime); 26 + return brokerageStatisticsMapper.selectSummaryPriceByStatusAndUnfreezeTimeBetween(
  27 + BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus(),
  28 + beginTime, endTime);
25 } 29 }
26 30
27 } 31 }
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/TradeOrderStatisticsService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.service.trade;
  2 +
  3 +import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAreaStatisticsRespVO;
  4 +import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeOrderSummaryRespBO;
  5 +
  6 +import java.time.LocalDateTime;
  7 +import java.util.List;
  8 +
  9 +/**
  10 + * 交易订单的统计 Service 接口
  11 + *
  12 + * @author owen
  13 + */
  14 +public interface TradeOrderStatisticsService {
  15 +
  16 + /**
  17 + * 获取订单统计
  18 + *
  19 + * @param beginTime 起始时间
  20 + * @param endTime 截止时间
  21 + * @return 订单统计结果
  22 + */
  23 + TradeOrderSummaryRespBO getOrderSummary(LocalDateTime beginTime, LocalDateTime endTime);
  24 +
  25 + /**
  26 + * 获取地区订单统计
  27 + *
  28 + * @return 订单统计结果
  29 + */
  30 + List<MemberAreaStatisticsRespVO> getSummaryListByAreaId();
  31 +
  32 + /**
  33 + * 获取下单用户数量
  34 + *
  35 + * @param beginTime 起始时间
  36 + * @param endTime 截止时间
  37 + * @return 支付下单数量
  38 + */
  39 + Integer getOrderUserCount(LocalDateTime beginTime, LocalDateTime endTime);
  40 +
  41 + /**
  42 + * 获取支付用户数量
  43 + *
  44 + * @param beginTime 起始时间
  45 + * @param endTime 截止时间
  46 + * @return 支付用户数量
  47 + */
  48 + Integer getPayUserCount(LocalDateTime beginTime, LocalDateTime endTime);
  49 +
  50 + /**
  51 + * 获取支付金额
  52 + *
  53 + * @param beginTime 起始时间
  54 + * @param endTime 截止时间
  55 + * @return 支付用户金额
  56 + */
  57 + Integer getOrderPayPrice(LocalDateTime beginTime, LocalDateTime endTime);
  58 +
  59 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/TradeOrderStatisticsServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.statistics.service.trade;
  2 +
  3 +import cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAreaStatisticsRespVO;
  4 +import cn.iocoder.yudao.module.statistics.dal.mysql.trade.TradeOrderStatisticsMapper;
  5 +import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeOrderSummaryRespBO;
  6 +import org.springframework.stereotype.Service;
  7 +import org.springframework.validation.annotation.Validated;
  8 +
  9 +import javax.annotation.Resource;
  10 +import java.time.LocalDateTime;
  11 +import java.util.List;
  12 +
  13 +/**
  14 + * 交易订单统计 Service 实现类
  15 + *
  16 + * @author owen
  17 + */
  18 +@Service
  19 +@Validated
  20 +public class TradeOrderStatisticsServiceImpl implements TradeOrderStatisticsService {
  21 +
  22 + @Resource
  23 + private TradeOrderStatisticsMapper tradeOrderStatisticsMapper;
  24 +
  25 + @Override
  26 + public TradeOrderSummaryRespBO getOrderSummary(LocalDateTime beginTime, LocalDateTime endTime) {
  27 + return new TradeOrderSummaryRespBO()
  28 + .setOrderCreateCount(tradeOrderStatisticsMapper.selectCountByCreateTimeBetween(beginTime, endTime))
  29 + .setOrderPayCount(tradeOrderStatisticsMapper.selectCountByPayTimeBetween(beginTime, endTime))
  30 + .setOrderPayPrice(tradeOrderStatisticsMapper.selectSummaryPriceByPayTimeBetween(beginTime, endTime));
  31 + }
  32 +
  33 + @Override
  34 + public List<MemberAreaStatisticsRespVO> getSummaryListByAreaId() {
  35 + return tradeOrderStatisticsMapper.selectSummaryListByAreaId();
  36 + }
  37 +
  38 + @Override
  39 + public Integer getOrderUserCount(LocalDateTime beginTime, LocalDateTime endTime) {
  40 + return tradeOrderStatisticsMapper.selectUserCountByCreateTimeBetween(beginTime, endTime);
  41 + }
  42 +
  43 + @Override
  44 + public Integer getPayUserCount(LocalDateTime beginTime, LocalDateTime endTime) {
  45 + return tradeOrderStatisticsMapper.selectUserCountByPayTimeBetween(beginTime, endTime);
  46 + }
  47 +
  48 + @Override
  49 + public Integer getOrderPayPrice(LocalDateTime beginTime, LocalDateTime endTime) {
  50 + return tradeOrderStatisticsMapper.selectSummaryPriceByPayTimeBetween(beginTime, endTime);
  51 + }
  52 +
  53 +}
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/TradeStatisticsService.java
@@ -22,7 +22,7 @@ public interface TradeStatisticsService { @@ -22,7 +22,7 @@ public interface TradeStatisticsService {
22 TradeStatisticsComparisonRespVO<TradeSummaryRespVO> getTradeSummaryComparison(); 22 TradeStatisticsComparisonRespVO<TradeSummaryRespVO> getTradeSummaryComparison();
23 23
24 /** 24 /**
25 - * 获得交易状况统计 25 + * 获得交易状况统计对照
26 * 26 *
27 * @return 统计数据对照 27 * @return 统计数据对照
28 */ 28 */
@@ -30,6 +30,13 @@ public interface TradeStatisticsService { @@ -30,6 +30,13 @@ public interface TradeStatisticsService {
30 LocalDateTime beginTime, LocalDateTime endTime); 30 LocalDateTime beginTime, LocalDateTime endTime);
31 31
32 /** 32 /**
  33 + * 获得交易状况统计
  34 + *
  35 + * @return 统计数据对照
  36 + */
  37 + Integer getExpensePrice(LocalDateTime beginTime, LocalDateTime endTime);
  38 +
  39 + /**
33 * 获得交易状况明细 40 * 获得交易状况明细
34 * 41 *
35 * @return 统计数据列表 42 * @return 统计数据列表
yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/TradeStatisticsServiceImpl.java
@@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.statistics.service.trade; @@ -2,8 +2,8 @@ package cn.iocoder.yudao.module.statistics.service.trade;
2 2
3 import cn.hutool.core.date.LocalDateTimeUtil; 3 import cn.hutool.core.date.LocalDateTimeUtil;
4 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; 4 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
5 -import cn.iocoder.yudao.module.pay.api.wallet.PayWalletApi;  
6 -import cn.iocoder.yudao.module.pay.api.wallet.dto.WalletSummaryRespDTO; 5 +import cn.iocoder.yudao.module.statistics.service.pay.PayWalletStatisticsService;
  6 +import cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO;
7 import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeStatisticsComparisonRespVO; 7 import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeStatisticsComparisonRespVO;
8 import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeSummaryRespVO; 8 import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeSummaryRespVO;
9 import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryRespVO; 9 import cn.iocoder.yudao.module.statistics.controller.admin.trade.vo.TradeTrendSummaryRespVO;
@@ -11,11 +11,8 @@ import cn.iocoder.yudao.module.statistics.convert.trade.TradeStatisticsConvert; @@ -11,11 +11,8 @@ import cn.iocoder.yudao.module.statistics.convert.trade.TradeStatisticsConvert;
11 import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO; 11 import cn.iocoder.yudao.module.statistics.dal.dataobject.trade.TradeStatisticsDO;
12 import cn.iocoder.yudao.module.statistics.dal.mysql.trade.TradeStatisticsMapper; 12 import cn.iocoder.yudao.module.statistics.dal.mysql.trade.TradeStatisticsMapper;
13 import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO; 13 import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeSummaryRespBO;
14 -import cn.iocoder.yudao.module.trade.api.aftersale.TradeAfterSaleApi;  
15 -import cn.iocoder.yudao.module.trade.api.aftersale.dto.AfterSaleSummaryRespDTO;  
16 -import cn.iocoder.yudao.module.trade.api.brokerage.TradeBrokerageApi;  
17 -import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;  
18 -import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderSummaryRespDTO; 14 +import cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO;
  15 +import cn.iocoder.yudao.module.statistics.service.trade.bo.TradeOrderSummaryRespBO;
19 import org.springframework.stereotype.Service; 16 import org.springframework.stereotype.Service;
20 import org.springframework.util.StopWatch; 17 import org.springframework.util.StopWatch;
21 import org.springframework.validation.annotation.Validated; 18 import org.springframework.validation.annotation.Validated;
@@ -37,31 +34,26 @@ public class TradeStatisticsServiceImpl implements TradeStatisticsService { @@ -37,31 +34,26 @@ public class TradeStatisticsServiceImpl implements TradeStatisticsService {
37 @Resource 34 @Resource
38 private TradeStatisticsMapper tradeStatisticsMapper; 35 private TradeStatisticsMapper tradeStatisticsMapper;
39 36
40 - // TODO @疯狂:统计逻辑,自己服务 mapper 去统计,不要调用其它服务 API;  
41 - // 主要的考虑点,其它服务是在线的业务,统计是离线业务,尽量不占用他们的 db 资源;  
42 - // 统计服务,从建议使用从库,或者从 mysql 抽取到单独的 clickhouse 或者其它的大数据组件;  
43 -  
44 @Resource 37 @Resource
45 - private TradeOrderApi tradeOrderApi; 38 + private TradeOrderStatisticsService tradeOrderStatisticsService;
46 @Resource 39 @Resource
47 - private TradeAfterSaleApi tradeAfterSaleApi; 40 + private AfterSaleStatisticsService afterSaleStatisticsService;
48 @Resource 41 @Resource
49 - private TradeBrokerageApi tradeBrokerageApi; 42 + private BrokerageStatisticsService brokerageStatisticsService;
50 @Resource 43 @Resource
51 - private PayWalletApi payWalletApi; 44 + private PayWalletStatisticsService payWalletStatisticsService;
52 45
53 @Override 46 @Override
54 public TradeStatisticsComparisonRespVO<TradeSummaryRespVO> getTradeSummaryComparison() { 47 public TradeStatisticsComparisonRespVO<TradeSummaryRespVO> getTradeSummaryComparison() {
55 - // 昨天的数据 48 + // 1.1 昨天的数据
56 TradeSummaryRespBO yesterdayData = getTradeSummaryByDays(-1); 49 TradeSummaryRespBO yesterdayData = getTradeSummaryByDays(-1);
57 - // 前天的数据(用于对照昨天的数据) 50 + // 1.2 前天的数据(用于对照昨天的数据)
58 TradeSummaryRespBO beforeYesterdayData = getTradeSummaryByDays(-2); 51 TradeSummaryRespBO beforeYesterdayData = getTradeSummaryByDays(-2);
59 -  
60 - // 本月数据; 52 + // 2.1 本月数据
61 TradeSummaryRespBO monthData = getTradeSummaryByMonths(0); 53 TradeSummaryRespBO monthData = getTradeSummaryByMonths(0);
62 - // 上月数据(用于对照本月的数据) 54 + // 2.2 上月数据(用于对照本月的数据)
63 TradeSummaryRespBO lastMonthData = getTradeSummaryByMonths(-1); 55 TradeSummaryRespBO lastMonthData = getTradeSummaryByMonths(-1);
64 - 56 + // 转换返回
65 return TradeStatisticsConvert.INSTANCE.convert(yesterdayData, beforeYesterdayData, monthData, lastMonthData); 57 return TradeStatisticsConvert.INSTANCE.convert(yesterdayData, beforeYesterdayData, monthData, lastMonthData);
66 } 58 }
67 59
@@ -77,37 +69,42 @@ public class TradeStatisticsServiceImpl implements TradeStatisticsService { @@ -77,37 +69,42 @@ public class TradeStatisticsServiceImpl implements TradeStatisticsService {
77 } 69 }
78 70
79 @Override 71 @Override
  72 + public Integer getExpensePrice(LocalDateTime beginTime, LocalDateTime endTime) {
  73 + return tradeStatisticsMapper.selectExpensePriceByTimeBetween(beginTime, endTime);
  74 + }
  75 +
  76 + @Override
80 public List<TradeTrendSummaryRespVO> getTradeStatisticsList(LocalDateTime beginTime, LocalDateTime endTime) { 77 public List<TradeTrendSummaryRespVO> getTradeStatisticsList(LocalDateTime beginTime, LocalDateTime endTime) {
81 return tradeStatisticsMapper.selectListByTimeBetween(beginTime, endTime); 78 return tradeStatisticsMapper.selectListByTimeBetween(beginTime, endTime);
82 } 79 }
83 80
84 @Override 81 @Override
85 public String statisticsYesterdayTrade() { 82 public String statisticsYesterdayTrade() {
86 - // 处理统计参数  
87 LocalDateTime yesterday = LocalDateTime.now().minusDays(1); 83 LocalDateTime yesterday = LocalDateTime.now().minusDays(1);
88 LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(yesterday); 84 LocalDateTime beginTime = LocalDateTimeUtil.beginOfDay(yesterday);
89 LocalDateTime endTime = LocalDateTimeUtil.endOfDay(yesterday); 85 LocalDateTime endTime = LocalDateTimeUtil.endOfDay(yesterday);
90 - // 统计 86 + // 1.1 统计订单
91 StopWatch stopWatch = new StopWatch("交易统计"); 87 StopWatch stopWatch = new StopWatch("交易统计");
92 stopWatch.start("统计订单"); 88 stopWatch.start("统计订单");
93 - TradeOrderSummaryRespDTO orderSummary = tradeOrderApi.getOrderSummary(beginTime, endTime); 89 + TradeOrderSummaryRespBO orderSummary = tradeOrderStatisticsService.getOrderSummary(beginTime, endTime);
94 stopWatch.stop(); 90 stopWatch.stop();
95 - 91 + // 1.2 统计售后
96 stopWatch.start("统计售后"); 92 stopWatch.start("统计售后");
97 - AfterSaleSummaryRespDTO afterSaleSummary = tradeAfterSaleApi.getAfterSaleSummary(beginTime, endTime); 93 + AfterSaleSummaryRespBO afterSaleSummary = afterSaleStatisticsService.getAfterSaleSummary(beginTime, endTime);
98 stopWatch.stop(); 94 stopWatch.stop();
99 - 95 + // 1.3 统计佣金
100 stopWatch.start("统计佣金"); 96 stopWatch.start("统计佣金");
101 - Integer brokerageSettlementPrice = tradeBrokerageApi.getBrokerageSettlementPriceSummary(beginTime, endTime); 97 + Integer brokerageSettlementPrice = brokerageStatisticsService.getBrokerageSettlementPriceSummary(beginTime, endTime);
102 stopWatch.stop(); 98 stopWatch.stop();
103 - 99 + // 1.4 统计充值
104 stopWatch.start("统计充值"); 100 stopWatch.start("统计充值");
105 - WalletSummaryRespDTO walletSummary = payWalletApi.getWalletSummary(beginTime, endTime); 101 + WalletSummaryRespBO walletSummary = payWalletStatisticsService.getWalletSummary(beginTime, endTime);
106 stopWatch.stop(); 102 stopWatch.stop();
107 - // 插入数据  
108 - TradeStatisticsDO entity = TradeStatisticsConvert.INSTANCE.convert(yesterday, orderSummary, afterSaleSummary, brokerageSettlementPrice, walletSummary); 103 +
  104 + // 2. 插入数据
  105 + TradeStatisticsDO entity = TradeStatisticsConvert.INSTANCE.convert(yesterday, orderSummary, afterSaleSummary,
  106 + brokerageSettlementPrice, walletSummary);
109 tradeStatisticsMapper.insert(entity); 107 tradeStatisticsMapper.insert(entity);
110 - // 返回计时结果  
111 return stopWatch.prettyPrint(); 108 return stopWatch.prettyPrint();
112 } 109 }
113 110
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/aftersale/dto/AfterSaleSummaryRespDTO.java renamed to yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/bo/AfterSaleSummaryRespBO.java
1 -package cn.iocoder.yudao.module.trade.api.aftersale.dto; 1 +package cn.iocoder.yudao.module.statistics.service.trade.bo;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 4
@@ -8,7 +8,7 @@ import lombok.Data; @@ -8,7 +8,7 @@ import lombok.Data;
8 * @author owen 8 * @author owen
9 */ 9 */
10 @Data 10 @Data
11 -public class AfterSaleSummaryRespDTO { 11 +public class AfterSaleSummaryRespBO {
12 12
13 /** 13 /**
14 * 退款订单数 14 * 退款订单数
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/order/dto/TradeOrderSummaryRespDTO.java renamed to yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/bo/TradeOrderSummaryRespBO.java
1 -package cn.iocoder.yudao.module.trade.api.order.dto; 1 +package cn.iocoder.yudao.module.statistics.service.trade.bo;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 4
5 /** 5 /**
6 - * 订单统计 Response DTO 6 + * 订单统计 Response BO
7 * 7 *
8 * @author owen 8 * @author owen
9 */ 9 */
10 @Data 10 @Data
11 -public class TradeOrderSummaryRespDTO { 11 +public class TradeOrderSummaryRespBO {
12 12
13 /** 13 /**
14 * 创建订单数 14 * 创建订单数
15 */ 15 */
16 - private Long orderCreateCount; 16 + private Integer orderCreateCount;
17 /** 17 /**
18 * 支付订单商品数 18 * 支付订单商品数
19 */ 19 */
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/wallet/dto/WalletSummaryRespDTO.java renamed to yudao-module-mall/yudao-module-statistics-biz/src/main/java/cn/iocoder/yudao/module/statistics/service/trade/bo/WalletSummaryRespBO.java
1 -package cn.iocoder.yudao.module.pay.api.wallet.dto; 1 +package cn.iocoder.yudao.module.statistics.service.trade.bo;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 4
@@ -8,7 +8,8 @@ import lombok.Data; @@ -8,7 +8,8 @@ import lombok.Data;
8 * @author owen 8 * @author owen
9 */ 9 */
10 @Data 10 @Data
11 -public class WalletSummaryRespDTO { 11 +public class WalletSummaryRespBO {
  12 +
12 /** 13 /**
13 * 总支付金额(余额),单位:分 14 * 总支付金额(余额),单位:分
14 */ 15 */
yudao-module-mall/yudao-module-statistics-biz/src/main/resources/mapper/infra/ApiAccessLogStatisticsMapper.xml 0 → 100644
  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.statistics.dal.mysql.infra.ApiAccessLogStatisticsMapper">
  4 +
  5 + <select id="selectCountByIp" resultType="java.lang.Integer">
  6 + SELECT COUNT(1)
  7 + FROM infra_api_access_log
  8 + WHERE create_time BETWEEN #{beginTime} AND #{endTime}
  9 + AND deleted = FALSE
  10 + GROUP BY user_ip
  11 + </select>
  12 +
  13 + <select id="selectCountByUserId" resultType="java.lang.Integer">
  14 + SELECT COUNT(1)
  15 + FROM infra_api_access_log
  16 + WHERE user_id > 0
  17 + AND create_time BETWEEN #{beginTime} AND #{endTime}
  18 + AND deleted = FALSE
  19 + GROUP BY user_id
  20 + </select>
  21 +
  22 +</mapper>
yudao-module-mall/yudao-module-statistics-biz/src/main/resources/mapper/member/MemberStatisticsMapper.xml 0 → 100644
  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.statistics.dal.mysql.member.MemberStatisticsMapper">
  4 +
  5 + <select id="selectSummaryListByAreaId"
  6 + resultType="cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAreaStatisticsRespVO">
  7 + SELECT area_id, COUNT(1) AS userCount
  8 + FROM member_user
  9 + WHERE deleted = FALSE
  10 + GROUP BY area_id
  11 + <!-- TODO @疯狂:order by 是不是交给内存哈 -->
  12 + ORDER BY userCount DESC
  13 + </select>
  14 +
  15 + <select id="selectSummaryListBySex"
  16 + resultType="cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSexStatisticsRespVO">
  17 + SELECT sex, COUNT(1) AS userCount
  18 + FROM member_user
  19 + WHERE deleted = FALSE
  20 + GROUP BY sex
  21 + <!-- TODO @疯狂:order by 是不是交给内存哈 -->
  22 + ORDER BY userCount DESC
  23 + </select>
  24 +
  25 + <select id="selectUserCount" resultType="java.lang.Integer">
  26 + SELECT COUNT(1)
  27 + FROM member_user
  28 + WHERE deleted = FALSE
  29 + <if test="beginTime != null">
  30 + AND create_time >= #{beginTime}
  31 + </if>
  32 + <if test="endTime != null">
  33 + AND create_time &lt;= #{endTime}
  34 + </if>
  35 + </select>
  36 +
  37 +</mapper>
yudao-module-mall/yudao-module-statistics-biz/src/main/resources/mapper/pay/PayWalletStatisticsMapper.xml 0 → 100644
  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.statistics.dal.mysql.pay.PayWalletStatisticsMapper">
  4 + <select id="selectRechargeSummaryByPayTimeBetween"
  5 + resultType="cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO">
  6 + SELECT COUNT(1) AS rechargePayCount,
  7 + SUM(pay_price) AS rechargePayPrice
  8 + FROM pay_wallet_recharge
  9 + WHERE pay_status = #{payStatus}
  10 + AND pay_time BETWEEN #{beginTime} AND #{endTime}
  11 + AND deleted = FALSE
  12 + </select>
  13 +
  14 + <select id="selectRechargeSummaryByRefundTimeBetween"
  15 + resultType="cn.iocoder.yudao.module.statistics.service.trade.bo.WalletSummaryRespBO">
  16 + SELECT COUNT(1) AS rechargeRefundCount,
  17 + SUM(pay_price) AS rechargeRefundPrice
  18 + FROM pay_wallet_recharge
  19 + WHERE refund_status = #{refundStatus}
  20 + AND refund_time BETWEEN #{beginTime} AND #{endTime}
  21 + AND deleted = FALSE
  22 + </select>
  23 +
  24 + <select id="selectPriceSummaryByBizTypeAndCreateTimeBetween" resultType="java.lang.Integer">
  25 + SELECT SUM(price)
  26 + FROM pay_wallet_transaction
  27 + WHERE biz_type = #{bizType}
  28 + AND create_time BETWEEN #{beginTime} AND #{endTime}
  29 + AND deleted = FALSE
  30 + </select>
  31 +
  32 + <select id="selectRechargeSummaryGroupByWalletId"
  33 + resultType="cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberSummaryRespVO">
  34 + SELECT COUNT(1) AS rechargeUserCount,
  35 + SUM(pay_price) AS rechargePrice
  36 + FROM pay_wallet_recharge
  37 + WHERE pay_status = #{payStatus}
  38 + <if test="beginTime != null">
  39 + AND pay_time >= #{beginTime}
  40 + </if>
  41 + <if test="endTime != null">
  42 + AND pay_time &lt;= #{endTime}
  43 + </if>
  44 + AND deleted = FALSE
  45 + GROUP BY wallet_id
  46 + </select>
  47 +
  48 +</mapper>
yudao-module-mall/yudao-module-statistics-biz/src/main/resources/mapper/trade/AfterSaleStatisticsMapper.xml 0 → 100644
  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.statistics.dal.mysql.trade.AfterSaleStatisticsMapper">
  4 +
  5 + <select id="selectSummaryByRefundTimeBetween"
  6 + resultType="cn.iocoder.yudao.module.statistics.service.trade.bo.AfterSaleSummaryRespBO">
  7 + SELECT COUNT(1) AS afterSaleCount,
  8 + SUM(refund_price) AS afterSaleRefundPrice
  9 + FROM trade_after_sale
  10 + WHERE refund_time BETWEEN #{beginTime} AND #{endTime}
  11 + AND deleted = FALSE
  12 + </select>
  13 +
  14 +</mapper>
yudao-module-mall/yudao-module-statistics-biz/src/main/resources/mapper/trade/BrokerageStatisticsMapper.xml 0 → 100644
  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.statistics.dal.mysql.trade.BrokerageStatisticsMapper">
  4 +
  5 + <select id="selectSummaryPriceByStatusAndUnfreezeTimeBetween" resultType="java.lang.Integer">
  6 + SELECT SUM(price)
  7 + FROM trade_brokerage_record
  8 + WHERE biz_type = #{bizType}
  9 + AND status = #{status}
  10 + AND unfreeze_time BETWEEN #{beginTime} AND #{endTime}
  11 + AND deleted = FALSE
  12 + </select>
  13 +
  14 +</mapper>
yudao-module-mall/yudao-module-statistics-biz/src/main/resources/mapper/trade/TradeOrderStatisticsMapper.xml 0 → 100644
  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.statistics.dal.mysql.trade.TradeOrderStatisticsMapper">
  4 +
  5 + <select id="selectSummaryListByAreaId"
  6 + resultType="cn.iocoder.yudao.module.statistics.controller.admin.member.vo.MemberAreaStatisticsRespVO">
  7 + SELECT receiver_area_id AS areaId,
  8 + (SELECT COUNT(1)
  9 + FROM trade_order AS s
  10 + WHERE s.receiver_area_id = m.receiver_area_id) AS orderCreateCount,
  11 + (SELECT COUNT(1)
  12 + FROM trade_order AS s
  13 + WHERE s.receiver_area_id = m.receiver_area_id
  14 + AND s.pay_status = TRUE
  15 + AND s.deleted = FALSE) AS orderPayCount,
  16 + (SELECT SUM(s.pay_price)
  17 + FROM trade_order AS s
  18 + WHERE s.receiver_area_id = m.receiver_area_id
  19 + AND s.pay_status = TRUE
  20 + AND s.deleted = FALSE) AS orderPayPrice
  21 + FROM trade_order m
  22 + WHERE deleted = FALSE
  23 + GROUP BY receiver_area_id
  24 + </select>
  25 +
  26 + <select id="selectUserCountByCreateTimeBetween" resultType="java.lang.Integer">
  27 + SELECT COUNT(DISTINCT(user_id))
  28 + FROM trade_order
  29 + WHERE deleted = FALSE
  30 + AND create_time BETWEEN #{beginTime} AND #{endTime}
  31 + </select>
  32 +
  33 + <select id="selectUserCountByPayTimeBetween" resultType="java.lang.Integer">
  34 + SELECT COUNT(DISTINCT(user_id))
  35 + FROM trade_order
  36 + WHERE pay_time BETWEEN #{beginTime} AND #{endTime}
  37 + AND pay_status = TRUE
  38 + AND deleted = FALSE
  39 + </select>
  40 +
  41 + <select id="selectCountByCreateTimeBetween" resultType="java.lang.Integer">
  42 + SELECT COUNT(1)
  43 + FROM trade_order
  44 + WHERE create_time BETWEEN #{beginTime} AND #{endTime}
  45 + AND deleted = FALSE
  46 + </select>
  47 +
  48 + <select id="selectCountByPayTimeBetween" resultType="java.lang.Integer">
  49 + SELECT COUNT(1)
  50 + FROM trade_order
  51 + WHERE pay_status = TRUE
  52 + AND create_time BETWEEN #{beginTime} AND #{endTime}
  53 + AND deleted = FALSE
  54 + </select>
  55 +
  56 + <select id="selectSummaryPriceByPayTimeBetween" resultType="java.lang.Integer">
  57 + SELECT SUM(pay_price)
  58 + FROM trade_order AS s
  59 + WHERE s.pay_status = TRUE
  60 + AND deleted = FALSE
  61 + AND create_time BETWEEN #{beginTime} AND #{endTime}
  62 + </select>
  63 +
  64 +</mapper>
yudao-module-mall/yudao-module-statistics-biz/src/main/resources/mapper/trade/TradeStatisticsMapper.xml
@@ -37,4 +37,17 @@ @@ -37,4 +37,17 @@
37 GROUP BY date 37 GROUP BY date
38 </select> 38 </select>
39 39
  40 + <select id="selectExpensePriceByTimeBetween" resultType="java.lang.Integer">
  41 + SELECT -- 支出金额 = 余额支付金额 + 支付佣金金额 + 商品退款金额
  42 + SUM(order_wallet_pay_price + brokerage_settlement_price + after_sale_refund_price) AS expensePrice
  43 + FROM trade_statistics
  44 + WHERE deleted = FALSE
  45 + <if test="beginTime != null">
  46 + AND time >= #{beginTime}
  47 + </if>
  48 + <if test="endTime != null">
  49 + AND time &lt;= #{endTime}
  50 + </if>
  51 + </select>
  52 +
40 </mapper> 53 </mapper>
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApi.java
1 package cn.iocoder.yudao.module.trade.api.order; 1 package cn.iocoder.yudao.module.trade.api.order;
2 2
3 import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO; 3 import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
4 -import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderSummaryRespDTO;  
5 4
6 -import java.time.LocalDateTime;  
7 import java.util.Collection; 5 import java.util.Collection;
8 import java.util.List; 6 import java.util.List;
9 7
@@ -30,25 +28,7 @@ public interface TradeOrderApi { @@ -30,25 +28,7 @@ public interface TradeOrderApi {
30 */ 28 */
31 TradeOrderRespDTO getOrder(Long id); 29 TradeOrderRespDTO getOrder(Long id);
32 30
33 - /**  
34 - * 获取订单统计  
35 - *  
36 - * @param beginTime 起始时间  
37 - * @param endTime 截止时间  
38 - * @return 订单统计结果  
39 - */  
40 - TradeOrderSummaryRespDTO getOrderSummary(LocalDateTime beginTime, LocalDateTime endTime);  
41 -  
42 - /**  
43 - * 更新拼团相关信息到订单  
44 - *  
45 - * @param orderId 订单编号  
46 - * @param activityId 拼团活动编号  
47 - * @param combinationRecordId 拼团记录编号  
48 - * @param headId 团长编号  
49 - */  
50 - void updateOrderCombinationInfo(Long orderId, Long activityId, Long combinationRecordId, Long headId);  
51 - 31 + // TODO 芋艿:需要优化下;
52 /** 32 /**
53 * 取消支付订单 33 * 取消支付订单
54 * 34 *
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
@@ -33,7 +33,8 @@ public interface ErrorCodeConstants { @@ -33,7 +33,8 @@ public interface ErrorCodeConstants {
33 ErrorCode ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR = new ErrorCode(1_011_000_028, "支付订单调价失败,原因:调整后支付价格不能小于 0.01 元"); 33 ErrorCode ORDER_UPDATE_PRICE_FAIL_PRICE_ERROR = new ErrorCode(1_011_000_028, "支付订单调价失败,原因:调整后支付价格不能小于 0.01 元");
34 ErrorCode ORDER_DELETE_FAIL_STATUS_NOT_CANCEL = new ErrorCode(1_011_000_029, "交易订单删除失败,订单不是【已取消】状态"); 34 ErrorCode ORDER_DELETE_FAIL_STATUS_NOT_CANCEL = new ErrorCode(1_011_000_029, "交易订单删除失败,订单不是【已取消】状态");
35 ErrorCode ORDER_RECEIVE_FAIL_DELIVERY_TYPE_NOT_PICK_UP = new ErrorCode(1_011_000_030, "交易订单自提失败,收货方式不是【用户自提】"); 35 ErrorCode ORDER_RECEIVE_FAIL_DELIVERY_TYPE_NOT_PICK_UP = new ErrorCode(1_011_000_030, "交易订单自提失败,收货方式不是【用户自提】");
36 - ErrorCode ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1_011_000_031, "交易订单修改收货地址失败,原因:订单已发货"); 36 + ErrorCode ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED = new ErrorCode(1_011_000_031, "交易订单修改收货地址失败,原因:订单不是【待发货】状态");
  37 + ErrorCode ORDER_CREATE_FAIL_EXIST_UNPAID = new ErrorCode(1_011_000_032, "交易订单创建失败,原因:存在未付款订单");
37 38
38 // ========== After Sale 模块 1-011-000-100 ========== 39 // ========== After Sale 模块 1-011-000-100 ==========
39 ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在"); 40 ErrorCode AFTER_SALE_NOT_FOUND = new ErrorCode(1_011_000_100, "售后单不存在");
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/api/order/TradeOrderApiImpl.java
1 package cn.iocoder.yudao.module.trade.api.order; 1 package cn.iocoder.yudao.module.trade.api.order;
2 2
3 import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO; 3 import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderRespDTO;
4 -import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderSummaryRespDTO;  
5 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; 4 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
6 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; 5 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
7 import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService; 6 import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
@@ -9,7 +8,6 @@ import org.springframework.stereotype.Service; @@ -9,7 +8,6 @@ import org.springframework.stereotype.Service;
9 import org.springframework.validation.annotation.Validated; 8 import org.springframework.validation.annotation.Validated;
10 9
11 import javax.annotation.Resource; 10 import javax.annotation.Resource;
12 -import java.time.LocalDateTime;  
13 import java.util.Collection; 11 import java.util.Collection;
14 import java.util.List; 12 import java.util.List;
15 13
@@ -23,9 +21,9 @@ import java.util.List; @@ -23,9 +21,9 @@ import java.util.List;
23 public class TradeOrderApiImpl implements TradeOrderApi { 21 public class TradeOrderApiImpl implements TradeOrderApi {
24 22
25 @Resource 23 @Resource
26 - private TradeOrderQueryService tradeOrderQueryService;  
27 - @Resource  
28 private TradeOrderUpdateService tradeOrderUpdateService; 24 private TradeOrderUpdateService tradeOrderUpdateService;
  25 + @Resource
  26 + private TradeOrderQueryService tradeOrderQueryService;
29 27
30 @Override 28 @Override
31 public List<TradeOrderRespDTO> getOrderList(Collection<Long> ids) { 29 public List<TradeOrderRespDTO> getOrderList(Collection<Long> ids) {
@@ -38,16 +36,6 @@ public class TradeOrderApiImpl implements TradeOrderApi { @@ -38,16 +36,6 @@ public class TradeOrderApiImpl implements TradeOrderApi {
38 } 36 }
39 37
40 @Override 38 @Override
41 - public TradeOrderSummaryRespDTO getOrderSummary(LocalDateTime beginTime, LocalDateTime endTime) {  
42 - return tradeOrderQueryService.getOrderSummary(beginTime, endTime);  
43 - }  
44 -  
45 - @Override  
46 - public void updateOrderCombinationInfo(Long orderId, Long activityId, Long combinationRecordId, Long headId) {  
47 - tradeOrderUpdateService.updateOrderCombinationInfo(orderId, activityId, combinationRecordId, headId);  
48 - }  
49 -  
50 - @Override  
51 public void cancelPaidOrder(Long userId, Long orderId) { 39 public void cancelPaidOrder(Long userId, Long orderId) {
52 tradeOrderUpdateService.cancelPaidOrder(userId, orderId); 40 tradeOrderUpdateService.cancelPaidOrder(userId, orderId);
53 } 41 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/aftersale/AfterSaleMapper.java
1 package cn.iocoder.yudao.module.trade.dal.mysql.aftersale; 1 package cn.iocoder.yudao.module.trade.dal.mysql.aftersale;
2 2
3 -import cn.hutool.core.bean.BeanUtil;  
4 -import cn.hutool.core.collection.CollUtil;  
5 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
8 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
9 -import cn.iocoder.yudao.module.trade.api.aftersale.dto.AfterSaleSummaryRespDTO;  
10 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO; 7 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO;
11 import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO; 8 import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
12 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 9 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
13 -import com.github.yulichang.toolkit.MPJWrappers;  
14 import org.apache.ibatis.annotations.Mapper; 10 import org.apache.ibatis.annotations.Mapper;
15 11
16 -import java.time.LocalDateTime;  
17 import java.util.Collection; 12 import java.util.Collection;
18 13
19 @Mapper 14 @Mapper
@@ -53,12 +48,4 @@ public interface AfterSaleMapper extends BaseMapperX&lt;AfterSaleDO&gt; { @@ -53,12 +48,4 @@ public interface AfterSaleMapper extends BaseMapperX&lt;AfterSaleDO&gt; {
53 .in(AfterSaleDO::getStatus, statuses)); 48 .in(AfterSaleDO::getStatus, statuses));
54 } 49 }
55 50
56 - default AfterSaleSummaryRespDTO selectSummaryByRefundTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {  
57 - return BeanUtil.copyProperties(CollUtil.get(selectMaps(MPJWrappers.<AfterSaleDO>lambdaJoin()  
58 - .selectCount(AfterSaleDO::getId, AfterSaleSummaryRespDTO::getAfterSaleCount)  
59 - .selectSum(AfterSaleDO::getRefundPrice, AfterSaleSummaryRespDTO::getAfterSaleRefundPrice)  
60 - .between(AfterSaleDO::getRefundTime, beginTime, endTime)), 0),  
61 - AfterSaleSummaryRespDTO.class);  
62 - }  
63 -  
64 } 51 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/brokerage/BrokerageRecordMapper.java
1 package cn.iocoder.yudao.module.trade.dal.mysql.brokerage; 1 package cn.iocoder.yudao.module.trade.dal.mysql.brokerage;
2 2
3 import cn.hutool.core.bean.BeanUtil; 3 import cn.hutool.core.bean.BeanUtil;
4 -import cn.hutool.core.collection.CollUtil;  
5 -import cn.hutool.core.convert.Convert;  
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
8 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
@@ -111,13 +109,4 @@ public interface BrokerageRecordMapper extends BaseMapperX&lt;BrokerageRecordDO&gt; { @@ -111,13 +109,4 @@ public interface BrokerageRecordMapper extends BaseMapperX&lt;BrokerageRecordDO&gt; {
111 @Param("beginTime") LocalDateTime beginTime, 109 @Param("beginTime") LocalDateTime beginTime,
112 @Param("endTime") LocalDateTime endTime); 110 @Param("endTime") LocalDateTime endTime);
113 111
114 - default Integer selectSummaryPriceByStatusAndUnfreezeTimeBetween(Integer bizType, Integer status,  
115 - LocalDateTime beginTime, LocalDateTime endTime) {  
116 - return Convert.toInt(CollUtil.getFirst(selectObjs(MPJWrappers.<BrokerageRecordDO>lambdaJoin()  
117 - .selectSum(BrokerageRecordDO::getPrice)  
118 - .eq(BrokerageRecordDO::getBizType, bizType)  
119 - .eq(BrokerageRecordDO::getStatus, status)  
120 - .between(BrokerageRecordDO::getUnfreezeTime, beginTime, endTime))), 0);  
121 - }  
122 -  
123 } 112 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/mysql/order/TradeOrderMapper.java
1 package cn.iocoder.yudao.module.trade.dal.mysql.order; 1 package cn.iocoder.yudao.module.trade.dal.mysql.order;
2 2
3 -import cn.hutool.core.bean.BeanUtil;  
4 -import cn.hutool.core.collection.CollUtil;  
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
8 -import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderSummaryRespDTO;  
9 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO; 6 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
10 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO; 7 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
11 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 8 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
12 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 9 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
13 -import com.github.yulichang.toolkit.MPJWrappers;  
14 import org.apache.ibatis.annotations.Mapper; 10 import org.apache.ibatis.annotations.Mapper;
15 11
16 import java.time.LocalDateTime; 12 import java.time.LocalDateTime;
@@ -93,21 +89,21 @@ public interface TradeOrderMapper extends BaseMapperX&lt;TradeOrderDO&gt; { @@ -93,21 +89,21 @@ public interface TradeOrderMapper extends BaseMapperX&lt;TradeOrderDO&gt; {
93 .eq(TradeOrderDO::getSeckillActivityId, seckillActivityId)); 89 .eq(TradeOrderDO::getSeckillActivityId, seckillActivityId));
94 } 90 }
95 91
96 - default TradeOrderSummaryRespDTO selectSummaryByPayTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {  
97 - return BeanUtil.copyProperties(CollUtil.get(selectMaps(MPJWrappers.<TradeOrderDO>lambdaJoin()  
98 - .selectCount(TradeOrderDO::getId, TradeOrderSummaryRespDTO::getOrderPayCount)  
99 - .selectSum(TradeOrderDO::getPayPrice, TradeOrderSummaryRespDTO::getOrderPayPrice)  
100 - .between(TradeOrderDO::getPayTime, beginTime, endTime)), 0),  
101 - TradeOrderSummaryRespDTO.class);  
102 - }  
103 -  
104 - default Long selectCountByCreateTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {  
105 - return selectCount(new LambdaQueryWrapperX<TradeOrderDO>()  
106 - .between(TradeOrderDO::getCreateTime, beginTime, endTime));  
107 - }  
108 -  
109 default TradeOrderDO selectOneByPickUpVerifyCode(String pickUpVerifyCode) { 92 default TradeOrderDO selectOneByPickUpVerifyCode(String pickUpVerifyCode) {
110 return selectOne(TradeOrderDO::getPickUpVerifyCode, pickUpVerifyCode); 93 return selectOne(TradeOrderDO::getPickUpVerifyCode, pickUpVerifyCode);
111 } 94 }
112 95
  96 + // TODO @hui999:是不是只针对 combinationActivityId 的查询呀?
  97 + default TradeOrderDO selectByUserIdAndActivityIdAndStatus(Long userId, Long activityId, Integer status) {
  98 + return selectOne(new LambdaQueryWrapperX<TradeOrderDO>()
  99 + .and(q -> q.eq(TradeOrderDO::getUserId, userId)
  100 + .eq(TradeOrderDO::getStatus, status))
  101 + .and(q -> q.eq(TradeOrderDO::getCombinationActivityId, activityId)
  102 + .or()
  103 + .eq(TradeOrderDO::getSeckillActivityId, activityId)
  104 + .or()
  105 + .eq(TradeOrderDO::getBargainActivityId, activityId))
  106 + );
  107 + }
  108 +
113 } 109 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleService.java
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.trade.service.aftersale; @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.trade.service.aftersale;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 -import cn.iocoder.yudao.module.trade.api.aftersale.dto.AfterSaleSummaryRespDTO;  
6 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleDisagreeReqVO; 5 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleDisagreeReqVO;
7 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO; 6 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO;
8 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO; 7 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO;
@@ -10,8 +9,6 @@ import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCre @@ -10,8 +9,6 @@ import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleCre
10 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO; 9 import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppAfterSaleDeliveryReqVO;
11 import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO; 10 import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
12 11
13 -import java.time.LocalDateTime;  
14 -  
15 /** 12 /**
16 * 售后订单 Service 接口 13 * 售后订单 Service 接口
17 * 14 *
@@ -127,13 +124,4 @@ public interface AfterSaleService { @@ -127,13 +124,4 @@ public interface AfterSaleService {
127 */ 124 */
128 Long getApplyingAfterSaleCount(Long userId); 125 Long getApplyingAfterSaleCount(Long userId);
129 126
130 - /**  
131 - * 获取售后单统计  
132 - *  
133 - * @param beginTime 起始时间  
134 - * @param endTime 截止时间  
135 - * @return 售后统计结果  
136 - */  
137 - AfterSaleSummaryRespDTO getAfterSaleSummary(LocalDateTime beginTime, LocalDateTime endTime);  
138 -  
139 } 127 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/aftersale/AfterSaleServiceImpl.java
@@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 7 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
8 import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; 8 import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi;
9 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; 9 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
10 -import cn.iocoder.yudao.module.trade.api.aftersale.dto.AfterSaleSummaryRespDTO;  
11 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleDisagreeReqVO; 10 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleDisagreeReqVO;
12 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO; 11 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSalePageReqVO;
13 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO; 12 import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.AfterSaleRefuseReqVO;
@@ -411,9 +410,4 @@ public class AfterSaleServiceImpl implements AfterSaleService { @@ -411,9 +410,4 @@ public class AfterSaleServiceImpl implements AfterSaleService {
411 return tradeAfterSaleMapper.selectCountByUserIdAndStatus(userId, AfterSaleStatusEnum.APPLYING_STATUSES); 410 return tradeAfterSaleMapper.selectCountByUserIdAndStatus(userId, AfterSaleStatusEnum.APPLYING_STATUSES);
412 } 411 }
413 412
414 - @Override  
415 - public AfterSaleSummaryRespDTO getAfterSaleSummary(LocalDateTime beginTime, LocalDateTime endTime) {  
416 - return tradeAfterSaleMapper.selectSummaryByRefundTimeBetween(beginTime,endTime);  
417 - }  
418 -  
419 } 413 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordService.java
@@ -156,13 +156,4 @@ public interface BrokerageRecordService { @@ -156,13 +156,4 @@ public interface BrokerageRecordService {
156 */ 156 */
157 AppBrokerageProductPriceRespVO calculateProductBrokeragePrice(Long userId, Long spuId); 157 AppBrokerageProductPriceRespVO calculateProductBrokeragePrice(Long userId, Long spuId);
158 158
159 - /**  
160 - * 获取已结算的佣金金额  
161 - *  
162 - * @param beginTime 起始时间  
163 - * @param endTime 截止时间  
164 - * @return 已结算的佣金金额  
165 - */  
166 - Integer getBrokerageSettlementPriceSummary(LocalDateTime beginTime, LocalDateTime endTime);  
167 -  
168 } 159 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordServiceImpl.java
@@ -356,13 +356,6 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService { @@ -356,13 +356,6 @@ public class BrokerageRecordServiceImpl implements BrokerageRecordService {
356 return respVO; 356 return respVO;
357 } 357 }
358 358
359 - @Override  
360 - public Integer getBrokerageSettlementPriceSummary(LocalDateTime beginTime, LocalDateTime endTime) {  
361 - return brokerageRecordMapper.selectSummaryPriceByStatusAndUnfreezeTimeBetween(  
362 - BrokerageRecordBizTypeEnum.ORDER.getType(), BrokerageRecordStatusEnum.SETTLEMENT.getStatus(),  
363 - beginTime, endTime);  
364 - }  
365 -  
366 /** 359 /**
367 * 获得自身的代理对象,解决 AOP 生效问题 360 * 获得自身的代理对象,解决 AOP 生效问题
368 * 361 *
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryService.java
1 package cn.iocoder.yudao.module.trade.service.order; 1 package cn.iocoder.yudao.module.trade.service.order;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 -import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderSummaryRespDTO;  
5 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO; 4 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
6 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO; 5 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
7 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 6 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
8 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 7 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
9 import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO; 8 import cn.iocoder.yudao.module.trade.framework.delivery.core.client.dto.ExpressTrackRespDTO;
10 9
11 -import java.time.LocalDateTime;  
12 import java.util.Collection; 10 import java.util.Collection;
13 import java.util.List; 11 import java.util.List;
14 12
@@ -41,6 +39,16 @@ public interface TradeOrderQueryService { @@ -41,6 +39,16 @@ public interface TradeOrderQueryService {
41 TradeOrderDO getOrder(Long userId, Long id); 39 TradeOrderDO getOrder(Long userId, Long id);
42 40
43 /** 41 /**
  42 + * 获得指定用户,指定活动,指定状态的交易订单
  43 + *
  44 + * @param userId 用户编号
  45 + * @param activityId 活动编号
  46 + * @param status 订单状态
  47 + * @return 交易订单
  48 + */
  49 + TradeOrderDO getActivityOrderByUserIdAndActivityIdAndStatus(Long userId, Long activityId, Integer status);
  50 +
  51 + /**
44 * 获得订单列表 52 * 获得订单列表
45 * 53 *
46 * @param ids 订单编号数组 54 * @param ids 订单编号数组
@@ -95,7 +103,7 @@ public interface TradeOrderQueryService { @@ -95,7 +103,7 @@ public interface TradeOrderQueryService {
95 /** 103 /**
96 * 【会员】在指定秒杀活动下,用户购买的商品数量 104 * 【会员】在指定秒杀活动下,用户购买的商品数量
97 * 105 *
98 - * @param userId 用户编号 106 + * @param userId 用户编号
99 * @param activityId 活动编号 107 * @param activityId 活动编号
100 * @return 秒杀商品数量 108 * @return 秒杀商品数量
101 */ 109 */
@@ -138,13 +146,4 @@ public interface TradeOrderQueryService { @@ -138,13 +146,4 @@ public interface TradeOrderQueryService {
138 */ 146 */
139 List<TradeOrderItemDO> getOrderItemListByOrderId(Collection<Long> orderIds); 147 List<TradeOrderItemDO> getOrderItemListByOrderId(Collection<Long> orderIds);
140 148
141 - /**  
142 - * 获取订单统计  
143 - *  
144 - * @param beginTime 起始时间  
145 - * @param endTime 截止时间  
146 - * @return 订单统计结果  
147 - */  
148 - TradeOrderSummaryRespDTO getOrderSummary(LocalDateTime beginTime, LocalDateTime endTime);  
149 -  
150 } 149 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderQueryServiceImpl.java
@@ -7,7 +7,6 @@ import cn.hutool.extra.spring.SpringUtil; @@ -7,7 +7,6 @@ import cn.hutool.extra.spring.SpringUtil;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 import cn.iocoder.yudao.module.member.api.user.MemberUserApi; 8 import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
9 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; 9 import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
10 -import cn.iocoder.yudao.module.trade.api.order.dto.TradeOrderSummaryRespDTO;  
11 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO; 10 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageReqVO;
12 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO; 11 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageReqVO;
13 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; 12 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
@@ -25,7 +24,6 @@ import org.springframework.cache.annotation.Cacheable; @@ -25,7 +24,6 @@ import org.springframework.cache.annotation.Cacheable;
25 import org.springframework.stereotype.Service; 24 import org.springframework.stereotype.Service;
26 25
27 import javax.annotation.Resource; 26 import javax.annotation.Resource;
28 -import java.time.LocalDateTime;  
29 import java.util.*; 27 import java.util.*;
30 28
31 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -73,6 +71,11 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { @@ -73,6 +71,11 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
73 } 71 }
74 72
75 @Override 73 @Override
  74 + public TradeOrderDO getActivityOrderByUserIdAndActivityIdAndStatus(Long userId, Long activityId, Integer status) {
  75 + return tradeOrderMapper.selectByUserIdAndActivityIdAndStatus(userId, activityId, status);
  76 + }
  77 +
  78 + @Override
76 public List<TradeOrderDO> getOrderList(Collection<Long> ids) { 79 public List<TradeOrderDO> getOrderList(Collection<Long> ids) {
77 if (CollUtil.isEmpty(ids)) { 80 if (CollUtil.isEmpty(ids)) {
78 return Collections.emptyList(); 81 return Collections.emptyList();
@@ -183,13 +186,6 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService { @@ -183,13 +186,6 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
183 .setPhone(receiverMobile)); 186 .setPhone(receiverMobile));
184 } 187 }
185 188
186 - @Override  
187 - public TradeOrderSummaryRespDTO getOrderSummary(LocalDateTime beginTime, LocalDateTime endTime) {  
188 - TradeOrderSummaryRespDTO dto = tradeOrderMapper.selectSummaryByPayTimeBetween(beginTime, endTime);  
189 - dto.setOrderCreateCount(tradeOrderMapper.selectCountByCreateTimeBetween(beginTime, endTime));  
190 - return dto;  
191 - }  
192 -  
193 189
194 // =================== Order Item =================== 190 // =================== Order Item ===================
195 191
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
@@ -14,23 +14,12 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -14,23 +14,12 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
14 import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; 14 import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
15 import cn.iocoder.yudao.module.member.api.address.AddressApi; 15 import cn.iocoder.yudao.module.member.api.address.AddressApi;
16 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; 16 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
17 -import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;  
18 -import cn.iocoder.yudao.module.member.api.point.MemberPointApi;  
19 -import cn.iocoder.yudao.module.member.api.user.MemberUserApi;  
20 -import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;  
21 -import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;  
22 -import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;  
23 import cn.iocoder.yudao.module.pay.api.order.PayOrderApi; 17 import cn.iocoder.yudao.module.pay.api.order.PayOrderApi;
24 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; 18 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
25 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; 19 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO;
26 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; 20 import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
27 import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi; 21 import cn.iocoder.yudao.module.product.api.comment.ProductCommentApi;
28 import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; 22 import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
29 -import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;  
30 -import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;  
31 -import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi;  
32 -import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;  
33 -import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;  
34 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO; 23 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDeliveryReqVO;
35 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderRemarkReqVO; 24 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderRemarkReqVO;
36 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdateAddressReqVO; 25 import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderUpdateAddressReqVO;
@@ -40,7 +29,6 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettle @@ -40,7 +29,6 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettle
40 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO; 29 import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO;
41 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO; 30 import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemCommentCreateReqVO;
42 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; 31 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
43 -import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;  
44 import cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO; 32 import cn.iocoder.yudao.module.trade.dal.dataobject.cart.CartDO;
45 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; 33 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
46 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 34 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
@@ -48,15 +36,11 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; @@ -48,15 +36,11 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
48 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper; 36 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderItemMapper;
49 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper; 37 import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
50 import cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO; 38 import cn.iocoder.yudao.module.trade.dal.redis.no.TradeNoRedisDAO;
51 -import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;  
52 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; 39 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
53 import cn.iocoder.yudao.module.trade.enums.order.*; 40 import cn.iocoder.yudao.module.trade.enums.order.*;
54 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; 41 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
55 import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrderLog; 42 import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrderLog;
56 import cn.iocoder.yudao.module.trade.framework.order.core.utils.TradeOrderLogUtils; 43 import cn.iocoder.yudao.module.trade.framework.order.core.utils.TradeOrderLogUtils;
57 -import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;  
58 -import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageUserService;  
59 -import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;  
60 import cn.iocoder.yudao.module.trade.service.cart.CartService; 44 import cn.iocoder.yudao.module.trade.service.cart.CartService;
61 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; 45 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
62 import cn.iocoder.yudao.module.trade.service.message.TradeMessageService; 46 import cn.iocoder.yudao.module.trade.service.message.TradeMessageService;
@@ -68,13 +52,15 @@ import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; @@ -68,13 +52,15 @@ import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
68 import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper; 52 import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper;
69 import lombok.extern.slf4j.Slf4j; 53 import lombok.extern.slf4j.Slf4j;
70 import org.jetbrains.annotations.NotNull; 54 import org.jetbrains.annotations.NotNull;
71 -import org.springframework.scheduling.annotation.Async;  
72 import org.springframework.stereotype.Service; 55 import org.springframework.stereotype.Service;
73 import org.springframework.transaction.annotation.Transactional; 56 import org.springframework.transaction.annotation.Transactional;
74 57
75 import javax.annotation.Resource; 58 import javax.annotation.Resource;
76 import java.time.LocalDateTime; 59 import java.time.LocalDateTime;
77 -import java.util.*; 60 +import java.util.ArrayList;
  61 +import java.util.List;
  62 +import java.util.Objects;
  63 +import java.util.Set;
78 64
79 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 65 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
80 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 66 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
@@ -109,30 +95,12 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -109,30 +95,12 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
109 private DeliveryExpressService deliveryExpressService; 95 private DeliveryExpressService deliveryExpressService;
110 @Resource 96 @Resource
111 private TradeMessageService tradeMessageService; 97 private TradeMessageService tradeMessageService;
112 - @Resource  
113 - private BrokerageUserService brokerageUserService;  
114 - @Resource  
115 - private BrokerageRecordService brokerageRecordService;  
116 98
117 @Resource 99 @Resource
118 - private ProductSpuApi productSpuApi;  
119 - @Resource  
120 - private ProductSkuApi productSkuApi;  
121 - @Resource  
122 private PayOrderApi payOrderApi; 100 private PayOrderApi payOrderApi;
123 @Resource 101 @Resource
124 private AddressApi addressApi; 102 private AddressApi addressApi;
125 @Resource 103 @Resource
126 - private CouponApi couponApi;  
127 - @Resource  
128 - private CombinationRecordApi combinationRecordApi;  
129 - @Resource  
130 - private MemberUserApi memberUserApi;  
131 - @Resource  
132 - private MemberLevelApi memberLevelApi;  
133 - @Resource  
134 - private MemberPointApi memberPointApi;  
135 - @Resource  
136 private ProductCommentApi productCommentApi; 104 private ProductCommentApi productCommentApi;
137 105
138 @Resource 106 @Resource
@@ -199,7 +167,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -199,7 +167,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
199 List<TradeOrderItemDO> orderItems = buildTradeOrderItems(order, calculateRespBO); 167 List<TradeOrderItemDO> orderItems = buildTradeOrderItems(order, calculateRespBO);
200 168
201 // 2. 订单创建前的逻辑 169 // 2. 订单创建前的逻辑
202 - beforeCreateTradeOrder(order, orderItems); 170 + tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(order, orderItems));
203 171
204 // 3. 保存订单 172 // 3. 保存订单
205 tradeOrderMapper.insert(order); 173 tradeOrderMapper.insert(order);
@@ -234,11 +202,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -234,11 +202,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
234 order.setReceiverName(createReqVO.getReceiverName()).setReceiverMobile(createReqVO.getReceiverMobile()); 202 order.setReceiverName(createReqVO.getReceiverName()).setReceiverMobile(createReqVO.getReceiverMobile());
235 order.setPickUpVerifyCode(RandomUtil.randomNumbers(8)); // 随机一个核销码,长度为 8 位 203 order.setPickUpVerifyCode(RandomUtil.randomNumbers(8)); // 随机一个核销码,长度为 8 位
236 } 204 }
237 - // 设置订单推广人  
238 - BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(order.getUserId());  
239 - if (brokerageUser != null && brokerageUser.getBindUserId() != null) {  
240 - order.setBrokerageUserId(brokerageUser.getBindUserId());  
241 - }  
242 return order; 205 return order;
243 } 206 }
244 207
@@ -248,21 +211,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -248,21 +211,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
248 } 211 }
249 212
250 /** 213 /**
251 - * 订单创建前,执行前置逻辑  
252 - *  
253 - * @param order 订单  
254 - * @param orderItems 订单项  
255 - */  
256 - private void beforeCreateTradeOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {  
257 - // 1. 执行订单创建前置处理器  
258 - // TODO @puhui999:这里有个纠结点;handler 的定义是只处理指定类型的订单的拓展逻辑;还是通用的 handler,类似可以处理优惠劵等等  
259 - tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(order, orderItems));  
260 -  
261 - // 2. 下单时扣减商品库存  
262 - productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convertNegative(orderItems));  
263 - }  
264 -  
265 - /**  
266 * 订单创建后,执行后置逻辑 214 * 订单创建后,执行后置逻辑
267 * <p> 215 * <p>
268 * 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等 216 * 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等
@@ -276,27 +224,16 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -276,27 +224,16 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
276 // 1. 执行订单创建后置处理器 224 // 1. 执行订单创建后置处理器
277 tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(order, orderItems)); 225 tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(order, orderItems));
278 226
279 - // 2. 有使用优惠券时更新  
280 - // 不在前置扣减的原因,是因为优惠劵要记录使用的订单号  
281 - if (order.getCouponId() != null) {  
282 - couponApi.useCoupon(new CouponUseReqDTO().setId(order.getCouponId()).setUserId(order.getUserId())  
283 - .setOrderId(order.getId()));  
284 - }  
285 -  
286 - // 3. 扣减积分(抵扣)  
287 - // 不在前置扣减的原因,是因为积分扣减时,需要记录关联业务  
288 - reduceUserPoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_USE, order.getId());  
289 -  
290 - // 4. 删除购物车商品 227 + // 2. 删除购物车商品
291 Set<Long> cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId); 228 Set<Long> cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId);
292 if (CollUtil.isNotEmpty(cartIds)) { 229 if (CollUtil.isNotEmpty(cartIds)) {
293 cartService.deleteCart(order.getUserId(), cartIds); 230 cartService.deleteCart(order.getUserId(), cartIds);
294 } 231 }
295 232
296 - // 5. 生成预支付 233 + // 3. 生成预支付
297 createPayOrder(order, orderItems); 234 createPayOrder(order, orderItems);
298 235
299 - // 6. 插入订单日志 236 + // 4. 插入订单日志
300 TradeOrderLogUtils.setOrderInfo(order.getId(), null, order.getStatus()); 237 TradeOrderLogUtils.setOrderInfo(order.getId(), null, order.getStatus());
301 238
302 // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来! 239 // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
@@ -330,18 +267,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -330,18 +267,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
330 throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID); 267 throw exception(ORDER_UPDATE_PAID_STATUS_NOT_UNPAID);
331 } 268 }
332 269
333 - // 3、订单支付成功后 270 + // 3. 执行 TradeOrderHandler 的后置处理
334 List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id); 271 List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
335 tradeOrderHandlers.forEach(handler -> handler.afterPayOrder(order, orderItems)); 272 tradeOrderHandlers.forEach(handler -> handler.afterPayOrder(order, orderItems));
336 273
337 - // 4.1 增加用户积分(赠送)  
338 - addUserPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_GIVE, order.getId());  
339 - // 4.2 增加用户经验  
340 - getSelf().addUserExperienceAsync(order.getUserId(), order.getPayPrice(), order.getId());  
341 - // 4.3 增加用户佣金  
342 - getSelf().addBrokerageAsync(order.getUserId(), order.getId());  
343 -  
344 - // 5. 记录订单日志 274 + // 4. 记录订单日志
345 TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.UNDELIVERED.getStatus()); 275 TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.UNDELIVERED.getStatus());
346 TradeOrderLogUtils.setUserInfo(order.getUserId(), UserTypeEnum.MEMBER.getValue()); 276 TradeOrderLogUtils.setUserInfo(order.getUserId(), UserTypeEnum.MEMBER.getValue());
347 } 277 }
@@ -434,8 +364,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -434,8 +364,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
434 .put("logisticsNo", express != null ? deliveryReqVO.getLogisticsNo() : "").build()); 364 .put("logisticsNo", express != null ? deliveryReqVO.getLogisticsNo() : "").build());
435 365
436 // 4. 发送站内信 366 // 4. 发送站内信
437 - tradeMessageService.sendMessageWhenDeliveryOrder(new TradeOrderMessageWhenDeliveryOrderReqBO().setOrderId(order.getId())  
438 - .setUserId(order.getUserId()).setMessage(null)); 367 + tradeMessageService.sendMessageWhenDeliveryOrder(new TradeOrderMessageWhenDeliveryOrderReqBO()
  368 + .setOrderId(order.getId()).setUserId(order.getUserId()).setMessage(null));
439 } 369 }
440 370
441 /** 371 /**
@@ -448,18 +378,13 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -448,18 +378,13 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
448 */ 378 */
449 private TradeOrderDO validateOrderDeliverable(Long id) { 379 private TradeOrderDO validateOrderDeliverable(Long id) {
450 TradeOrderDO order = validateOrderExists(id); 380 TradeOrderDO order = validateOrderExists(id);
451 - // 校验订单是否退款 381 + // 1. 校验订单是否未发货
452 if (ObjectUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) { 382 if (ObjectUtil.notEqual(TradeOrderRefundStatusEnum.NONE.getStatus(), order.getRefundStatus())) {
453 throw exception(ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE); 383 throw exception(ORDER_DELIVERY_FAIL_REFUND_STATUS_NOT_NONE);
454 } 384 }
455 - // 订单类型:拼团  
456 - if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {  
457 - // 校验订单拼团是否成功  
458 - if (!combinationRecordApi.isCombinationRecordSuccess(order.getUserId(), order.getId())) {  
459 - throw exception(ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS);  
460 - }  
461 - }  
462 385
  386 + // 2. 执行 TradeOrderHandler 前置处理
  387 + tradeOrderHandlers.forEach(handler -> handler.beforeDeliveryOrder(order));
463 return order; 388 return order;
464 } 389 }
465 390
@@ -616,30 +541,19 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -616,30 +541,19 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
616 * @param cancelType 取消类型 541 * @param cancelType 取消类型
617 */ 542 */
618 private void cancelOrder0(TradeOrderDO order, TradeOrderCancelTypeEnum cancelType) { 543 private void cancelOrder0(TradeOrderDO order, TradeOrderCancelTypeEnum cancelType) {
619 - Long id = order.getId();  
620 // 1. 更新 TradeOrderDO 状态为已取消 544 // 1. 更新 TradeOrderDO 状态为已取消
621 - int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(), 545 + int updateCount = tradeOrderMapper.updateByIdAndStatus(order.getId(), order.getStatus(),
622 new TradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus()) 546 new TradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
623 .setCancelType(cancelType.getType()).setCancelTime(LocalDateTime.now())); 547 .setCancelType(cancelType.getType()).setCancelTime(LocalDateTime.now()));
624 if (updateCount == 0) { 548 if (updateCount == 0) {
625 throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID); 549 throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
626 } 550 }
627 551
628 - List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);  
629 - // 3. 回滚库存  
630 - productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems));  
631 - // 3.1、 活动相关的回滚  
632 - tradeOrderHandlers.forEach(handler -> handler.cancelOrder(order, orderItems));  
633 -  
634 - // 4. 回滚优惠券  
635 - if (order.getCouponId() != null && order.getCouponId() > 0) {  
636 - couponApi.returnUsedCoupon(order.getCouponId());  
637 - }  
638 -  
639 - // 5. 回滚积分(抵扣的)  
640 - addUserPoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_CANCEL, order.getId()); 552 + // 2. 执行 TradeOrderHandler 的后置处理
  553 + List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
  554 + tradeOrderHandlers.forEach(handler -> handler.afterCancelOrder(order, orderItems));
641 555
642 - // 6. 增加订单日志 556 + // 3. 增加订单日志
643 TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus()); 557 TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus());
644 } 558 }
645 559
@@ -660,8 +574,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -660,8 +574,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
660 .setStatus(TradeOrderStatusEnum.CANCELED.getStatus()) 574 .setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
661 .setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now())); 575 .setCancelType(TradeOrderCancelTypeEnum.AFTER_SALE_CLOSE.getType()).setCancelTime(LocalDateTime.now()));
662 576
663 - // 2. 退还优惠券  
664 - couponApi.returnUsedCoupon(order.getCouponId()); 577 + // 2. 执行 TradeOrderHandler 的后置处理
  578 + List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
  579 + tradeOrderHandlers.forEach(handler -> handler.afterCancelOrder(order, orderItems));
665 } 580 }
666 581
667 @Override 582 @Override
@@ -742,9 +657,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -742,9 +657,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
742 public void updateOrderAddress(TradeOrderUpdateAddressReqVO reqVO) { 657 public void updateOrderAddress(TradeOrderUpdateAddressReqVO reqVO) {
743 // 校验交易订单 658 // 校验交易订单
744 TradeOrderDO order = validateOrderExists(reqVO.getId()); 659 TradeOrderDO order = validateOrderExists(reqVO.getId());
745 - // 发货后,不允许修改;  
746 - // TODO @puhui999:只有待发货,可以执行 update  
747 - if (TradeOrderStatusEnum.isDelivered(order.getStatus())) { 660 + // 只有待发货状态,才可以修改订单收货地址;
  661 + if (!TradeOrderStatusEnum.isUndelivered(order.getStatus())) {
748 throw exception(ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED); 662 throw exception(ORDER_UPDATE_ADDRESS_FAIL_STATUS_NOT_DELIVERED);
749 } 663 }
750 664
@@ -790,13 +704,15 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -790,13 +704,15 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
790 @Override 704 @Override
791 @Transactional(rollbackFor = Exception.class) 705 @Transactional(rollbackFor = Exception.class)
792 public void updateOrderItemWhenAfterSaleSuccess(Long id, Integer refundPrice) { 706 public void updateOrderItemWhenAfterSaleSuccess(Long id, Integer refundPrice) {
793 - // 1. 更新订单项 707 + // 1.1 更新订单项
794 updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(), 708 updateOrderItemAfterSaleStatus(id, TradeOrderItemAfterSaleStatusEnum.APPLY.getStatus(),
795 TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(), null); 709 TradeOrderItemAfterSaleStatusEnum.SUCCESS.getStatus(), null);
796 -  
797 - // 2.1 更新订单的退款金额、积分 710 + // 1.2 执行 TradeOrderHandler 的后置处理
798 TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id); 711 TradeOrderItemDO orderItem = tradeOrderItemMapper.selectById(id);
799 TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId()); 712 TradeOrderDO order = tradeOrderMapper.selectById(orderItem.getOrderId());
  713 + tradeOrderHandlers.forEach(handler -> handler.afterCancelOrderItem(order, orderItem));
  714 +
  715 + // 2.1 更新订单的退款金额、积分
800 Integer orderRefundPrice = order.getRefundPrice() + refundPrice; 716 Integer orderRefundPrice = order.getRefundPrice() + refundPrice;
801 Integer orderRefundPoint = order.getRefundPoint() + orderItem.getUsePoint(); 717 Integer orderRefundPoint = order.getRefundPoint() + orderItem.getUsePoint();
802 Integer refundStatus = isAllOrderItemAfterSaleSuccess(order.getId()) ? 718 Integer refundStatus = isAllOrderItemAfterSaleSuccess(order.getId()) ?
@@ -807,23 +723,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -807,23 +723,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
807 .setRefundPrice(orderRefundPrice).setRefundPoint(orderRefundPoint)); 723 .setRefundPrice(orderRefundPrice).setRefundPoint(orderRefundPoint));
808 // 2.2 如果全部退款,则进行取消订单 724 // 2.2 如果全部退款,则进行取消订单
809 getSelf().cancelOrderByAfterSale(order, orderRefundPrice); 725 getSelf().cancelOrderByAfterSale(order, orderRefundPrice);
810 -  
811 -  
812 - // 3. 回滚库存  
813 - productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(Collections.singletonList(orderItem)));  
814 - // 3.1、 活动相关的回滚  
815 - tradeOrderHandlers.forEach(handler -> handler.cancelOrder(order, Collections.singletonList(orderItem)));  
816 -  
817 - // 4.1 回滚积分:扣减用户积分(赠送的)  
818 - reduceUserPoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.AFTER_SALE_DEDUCT_GIVE, orderItem.getAfterSaleId());  
819 - // 4.2 回滚积分:增加用户积分(返还抵扣)  
820 - addUserPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.AFTER_SALE_REFUND_USED, orderItem.getAfterSaleId());  
821 -  
822 - // 5. 回滚经验:扣减用户经验  
823 - getSelf().reduceUserExperienceAsync(order.getUserId(), refundPrice, orderItem.getAfterSaleId());  
824 -  
825 - // 6. 回滚佣金:更新分佣记录为已失效  
826 - getSelf().cancelBrokerageAsync(order.getUserId(), id);  
827 } 726 }
828 727
829 @Override 728 @Override
@@ -840,6 +739,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -840,6 +739,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
840 if (updateCount <= 0) { 739 if (updateCount <= 0) {
841 throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL); 740 throw exception(ORDER_ITEM_UPDATE_AFTER_SALE_STATUS_FAIL);
842 } 741 }
  742 +
843 } 743 }
844 744
845 /** 745 /**
@@ -923,11 +823,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -923,11 +823,11 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
923 @Override 823 @Override
924 @Transactional(rollbackFor = Exception.class) 824 @Transactional(rollbackFor = Exception.class)
925 public void cancelPaidOrder(Long userId, Long orderId) { 825 public void cancelPaidOrder(Long userId, Long orderId) {
  826 + // TODO 芋艿:这里实现要优化下;
926 TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderId, userId); 827 TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(orderId, userId);
927 if (order == null) { 828 if (order == null) {
928 throw exception(ORDER_NOT_FOUND); 829 throw exception(ORDER_NOT_FOUND);
929 } 830 }
930 -  
931 cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL); 831 cancelOrder0(order, TradeOrderCancelTypeEnum.MEMBER_CANCEL);
932 } 832 }
933 833
@@ -940,7 +840,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -940,7 +840,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
940 @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_COMMENT) 840 @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.SYSTEM_COMMENT)
941 public void createOrderItemCommentBySystemBySystem(TradeOrderDO order) { 841 public void createOrderItemCommentBySystemBySystem(TradeOrderDO order) {
942 // 1. 查询未评论的订单项 842 // 1. 查询未评论的订单项
943 - List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderIdAndCommentStatus(order.getId(), Boolean.FALSE); 843 + List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderIdAndCommentStatus(
  844 + order.getId(), Boolean.FALSE);
944 if (CollUtil.isEmpty(orderItems)) { 845 if (CollUtil.isEmpty(orderItems)) {
945 return; 846 return;
946 } 847 }
@@ -983,73 +884,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -983,73 +884,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
983 884
984 // =================== 营销相关的操作 =================== 885 // =================== 营销相关的操作 ===================
985 886
986 - @Async  
987 - protected void addUserExperienceAsync(Long userId, Integer payPrice, Long orderId) {  
988 - int bizType = MemberExperienceBizTypeEnum.ORDER.getType();  
989 - memberLevelApi.addExperience(userId, payPrice, bizType, String.valueOf(orderId));  
990 - }  
991 -  
992 - @Async  
993 - protected void reduceUserExperienceAsync(Long userId, Integer refundPrice, Long afterSaleId) {  
994 - int bizType = MemberExperienceBizTypeEnum.REFUND.getType();  
995 - memberLevelApi.addExperience(userId, -refundPrice, bizType, String.valueOf(afterSaleId));  
996 - }  
997 -  
998 - /**  
999 - * 添加用户积分  
1000 - * <p>  
1001 - * 目前是支付成功后,就会创建积分记录。  
1002 - * <p>  
1003 - * 业内还有两种做法,可以根据自己的业务调整:  
1004 - * 1. 确认收货后,才创建积分记录  
1005 - * 2. 支付 or 下单成功时,创建积分记录(冻结),确认收货解冻或者 n 天后解冻  
1006 - *  
1007 - * @param userId 用户编号  
1008 - * @param point 增加积分数量  
1009 - * @param bizType 业务编号  
1010 - * @param bizId 业务编号  
1011 - */  
1012 - protected void addUserPoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) {  
1013 - if (point != null && point > 0) {  
1014 - memberPointApi.addPoint(userId, point, bizType.getType(), String.valueOf(bizId));  
1015 - }  
1016 - }  
1017 -  
1018 - protected void reduceUserPoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) {  
1019 - if (point != null && point > 0) {  
1020 - memberPointApi.reducePoint(userId, point, bizType.getType(), String.valueOf(bizId));  
1021 - }  
1022 - }  
1023 -  
1024 - /**  
1025 - * 创建分销记录  
1026 - * <p>  
1027 - * 目前是支付成功后,就会创建分销记录。  
1028 - * <p>  
1029 - * 业内还有两种做法,可以根据自己的业务调整:  
1030 - * 1. 确认收货后,才创建分销记录  
1031 - * 2. 支付 or 下单成功时,创建分销记录(冻结),确认收货解冻或者 n 天后解冻  
1032 - *  
1033 - * @param userId 用户编号  
1034 - * @param orderId 订单编号  
1035 - */  
1036 - @Async  
1037 - protected void addBrokerageAsync(Long userId, Long orderId) {  
1038 - MemberUserRespDTO user = memberUserApi.getUser(userId);  
1039 - Assert.notNull(user);  
1040 - // 每一个订单项,都会去生成分销记录  
1041 - List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(orderId);  
1042 - List<BrokerageAddReqBO> addList = convertList(orderItems,  
1043 - item -> TradeOrderConvert.INSTANCE.convert(user, item,  
1044 - productSpuApi.getSpu(item.getSpuId()), productSkuApi.getSku(item.getSkuId())));  
1045 - brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, addList);  
1046 - }  
1047 -  
1048 - @Async  
1049 - protected void cancelBrokerageAsync(Long userId, Long orderItemId) {  
1050 - brokerageRecordService.cancelBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, String.valueOf(orderItemId));  
1051 - }  
1052 -  
1053 /** 887 /**
1054 * 获得自身的代理对象,解决 AOP 生效问题 888 * 获得自身的代理对象,解决 AOP 生效问题
1055 * 889 *
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeBargainHandler.java renamed to yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeBargainOrderHandler.java
1 package cn.iocoder.yudao.module.trade.service.order.handler; 1 package cn.iocoder.yudao.module.trade.service.order.handler;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
3 import cn.iocoder.yudao.module.promotion.api.bargain.BargainActivityApi; 5 import cn.iocoder.yudao.module.promotion.api.bargain.BargainActivityApi;
4 import cn.iocoder.yudao.module.promotion.api.bargain.BargainRecordApi; 6 import cn.iocoder.yudao.module.promotion.api.bargain.BargainRecordApi;
5 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 7 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
@@ -11,12 +13,12 @@ import javax.annotation.Resource; @@ -11,12 +13,12 @@ import javax.annotation.Resource;
11 import java.util.List; 13 import java.util.List;
12 14
13 /** 15 /**
14 - * 砍价订单 handler 实现类 16 + * 砍价订单的 {@link TradeOrderHandler} 实现类
15 * 17 *
16 * @author HUIHUI 18 * @author HUIHUI
17 */ 19 */
18 @Component 20 @Component
19 -public class TradeBargainHandler implements TradeOrderHandler { 21 +public class TradeBargainOrderHandler implements TradeOrderHandler {
20 22
21 @Resource 23 @Resource
22 private BargainActivityApi bargainActivityApi; 24 private BargainActivityApi bargainActivityApi;
@@ -25,9 +27,11 @@ public class TradeBargainHandler implements TradeOrderHandler { @@ -25,9 +27,11 @@ public class TradeBargainHandler implements TradeOrderHandler {
25 27
26 @Override 28 @Override
27 public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { 29 public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
28 - if (TradeOrderTypeEnum.isBargain(order.getType())) { 30 + if (!TradeOrderTypeEnum.isBargain(order.getType())) {
29 return; 31 return;
30 } 32 }
  33 + // 明确校验一下
  34 + Assert.isTrue(orderItems.size() == 1, "砍价时,只允许选择一个商品");
31 35
32 // 扣减砍价活动的库存 36 // 扣减砍价活动的库存
33 bargainActivityApi.updateBargainActivityStock(order.getBargainActivityId(), 37 bargainActivityApi.updateBargainActivityStock(order.getBargainActivityId(),
@@ -36,20 +40,39 @@ public class TradeBargainHandler implements TradeOrderHandler { @@ -36,20 +40,39 @@ public class TradeBargainHandler implements TradeOrderHandler {
36 40
37 @Override 41 @Override
38 public void afterOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { 42 public void afterOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
39 - if (TradeOrderTypeEnum.isBargain(order.getType())) { 43 + if (!TradeOrderTypeEnum.isBargain(order.getType())) {
40 return; 44 return;
41 } 45 }
  46 + // 明确校验一下
  47 + Assert.isTrue(orderItems.size() == 1, "砍价时,只允许选择一个商品");
42 48
43 // 记录砍价记录对应的订单编号 49 // 记录砍价记录对应的订单编号
44 bargainRecordApi.updateBargainRecordOrderId(order.getBargainRecordId(), order.getId()); 50 bargainRecordApi.updateBargainRecordOrderId(order.getBargainRecordId(), order.getId());
45 } 51 }
46 52
47 @Override 53 @Override
48 - public void cancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {  
49 - if (TradeOrderTypeEnum.isBargain(order.getType())) { 54 + public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  55 + if (!TradeOrderTypeEnum.isBargain(order.getType())) {
50 return; 56 return;
51 } 57 }
52 - // TODO 芋艿:取消订单时,需要增加库存 58 + // 明确校验一下
  59 + Assert.isTrue(orderItems.size() == 1, "砍价时,只允许选择一个商品");
  60 +
  61 + // 售后的订单项,已经在 afterCancelOrderItem 回滚库存,所以这里不需要重复回滚
  62 + orderItems = filterOrderItemListByNoneAfterSale(orderItems);
  63 + if (CollUtil.isEmpty(orderItems)) {
  64 + return;
  65 + }
  66 + afterCancelOrderItem(order, orderItems.get(0));
  67 + }
  68 +
  69 + @Override
  70 + public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {
  71 + if (!TradeOrderTypeEnum.isBargain(order.getType())) {
  72 + return;
  73 + }
  74 + // 恢复(增加)砍价活动的库存
  75 + bargainActivityApi.updateBargainActivityStock(order.getBargainActivityId(), orderItem.getCount());
53 } 76 }
54 77
55 } 78 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeBrokerageOrderHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.order.handler;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
  5 +import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
  6 +import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
  7 +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
  8 +import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
  9 +import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
  10 +import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
  11 +import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
  12 +import cn.iocoder.yudao.module.trade.dal.dataobject.brokerage.BrokerageUserDO;
  13 +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
  14 +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
  15 +import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
  16 +import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;
  17 +import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageUserService;
  18 +import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
  19 +import org.springframework.stereotype.Component;
  20 +
  21 +import javax.annotation.Resource;
  22 +import java.util.List;
  23 +
  24 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  25 +
  26 +/**
  27 + * 订单分销的 {@link TradeOrderHandler} 实现类
  28 + *
  29 + * @author 芋道源码
  30 + */
  31 +@Component
  32 +public class TradeBrokerageOrderHandler implements TradeOrderHandler {
  33 +
  34 + @Resource
  35 + private MemberUserApi memberUserApi;
  36 + @Resource
  37 + private ProductSpuApi productSpuApi;
  38 + @Resource
  39 + private ProductSkuApi productSkuApi;
  40 +
  41 + @Resource
  42 + private BrokerageRecordService brokerageRecordService;
  43 + @Resource
  44 + private BrokerageUserService brokerageUserService;
  45 +
  46 + @Override
  47 + public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  48 + // 设置订单推广人
  49 + BrokerageUserDO brokerageUser = brokerageUserService.getBrokerageUser(order.getUserId());
  50 + if (brokerageUser != null && brokerageUser.getBindUserId() != null) {
  51 + order.setBrokerageUserId(brokerageUser.getBindUserId());
  52 + }
  53 + }
  54 +
  55 + @Override
  56 + public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  57 + if (order.getBrokerageUserId() == null) {
  58 + return;
  59 + }
  60 + addBrokerage(order.getUserId(), orderItems);
  61 + }
  62 +
  63 + @Override
  64 + public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  65 + // 如果是未支付的订单,不会产生分销结果,所以直接 return
  66 + if (!order.getPayStatus()) {
  67 + return;
  68 + }
  69 + if (order.getBrokerageUserId() == null) {
  70 + return;
  71 + }
  72 +
  73 + // 售后的订单项,已经在 afterCancelOrderItem 回滚库存,所以这里不需要重复回滚
  74 + orderItems = filterOrderItemListByNoneAfterSale(orderItems);
  75 + if (CollUtil.isEmpty(orderItems)) {
  76 + return;
  77 + }
  78 + orderItems.forEach(orderItem -> afterCancelOrderItem(order, orderItem));
  79 + }
  80 +
  81 + @Override
  82 + public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {
  83 + if (order.getBrokerageUserId() == null) {
  84 + return;
  85 + }
  86 + cancelBrokerage(order.getId(), orderItem.getOrderId());
  87 + }
  88 +
  89 + /**
  90 + * 创建分销记录
  91 + * <p>
  92 + * 目前是支付成功后,就会创建分销记录。
  93 + * <p>
  94 + * 业内还有两种做法,可以根据自己的业务调整:
  95 + * 1. 确认收货后,才创建分销记录
  96 + * 2. 支付 or 下单成功时,创建分销记录(冻结),确认收货解冻或者 n 天后解冻
  97 + *
  98 + * @param userId 用户编号
  99 + * @param orderItems 订单项
  100 + */
  101 + protected void addBrokerage(Long userId, List<TradeOrderItemDO> orderItems) {
  102 + MemberUserRespDTO user = memberUserApi.getUser(userId);
  103 + Assert.notNull(user);
  104 + ProductSpuRespDTO spu = productSpuApi.getSpu(orderItems.get(0).getSpuId());
  105 + Assert.notNull(spu);
  106 + ProductSkuRespDTO sku = productSkuApi.getSku(orderItems.get(0).getSkuId());
  107 +
  108 + // 每一个订单项,都会去生成分销记录
  109 + List<BrokerageAddReqBO> addList = convertList(orderItems,
  110 + item -> TradeOrderConvert.INSTANCE.convert(user, item, spu, sku));
  111 + brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, addList);
  112 + }
  113 +
  114 + protected void cancelBrokerage(Long userId, Long orderItemId) {
  115 + brokerageRecordService.cancelBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, String.valueOf(orderItemId));
  116 + }
  117 +
  118 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeCombinationHandler.java renamed to yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeCombinationOrderHandler.java
1 package cn.iocoder.yudao.module.trade.service.order.handler; 1 package cn.iocoder.yudao.module.trade.service.order.handler;
2 2
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
  4 +import cn.iocoder.yudao.framework.common.core.KeyValue;
4 import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi; 5 import cn.iocoder.yudao.module.promotion.api.combination.CombinationRecordApi;
5 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; 6 import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
6 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 7 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
7 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 8 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
  9 +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
8 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; 10 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
  11 +import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
  12 +import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
9 import org.springframework.stereotype.Component; 13 import org.springframework.stereotype.Component;
10 14
11 import javax.annotation.Resource; 15 import javax.annotation.Resource;
12 import java.util.List; 16 import java.util.List;
13 17
  18 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  19 +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_CREATE_FAIL_EXIST_UNPAID;
  20 +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS;
  21 +
14 /** 22 /**
15 - * 拼团订单 handler 接口实现类 23 + * 拼团订单的 {@link TradeOrderHandler} 实现类
16 * 24 *
17 * @author HUIHUI 25 * @author HUIHUI
18 */ 26 */
19 @Component 27 @Component
20 -public class TradeCombinationHandler implements TradeOrderHandler { 28 +public class TradeCombinationOrderHandler implements TradeOrderHandler {
  29 +
  30 + @Resource
  31 + private TradeOrderUpdateService orderUpdateService;
  32 + @Resource
  33 + private TradeOrderQueryService orderQueryService;
21 34
22 @Resource 35 @Resource
23 private CombinationRecordApi combinationRecordApi; 36 private CombinationRecordApi combinationRecordApi;
@@ -25,37 +38,54 @@ public class TradeCombinationHandler implements TradeOrderHandler { @@ -25,37 +38,54 @@ public class TradeCombinationHandler implements TradeOrderHandler {
25 @Override 38 @Override
26 public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { 39 public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
27 // 如果不是拼团订单则结束 40 // 如果不是拼团订单则结束
28 - if (TradeOrderTypeEnum.isCombination(order.getType())) { 41 + if (!TradeOrderTypeEnum.isCombination(order.getType())) {
29 return; 42 return;
30 } 43 }
31 Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品"); 44 Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品");
32 45
33 - // 校验是否满足拼团活动相关限制 46 + // 1. 校验是否满足拼团活动相关限制
34 TradeOrderItemDO item = orderItems.get(0); 47 TradeOrderItemDO item = orderItems.get(0);
35 combinationRecordApi.validateCombinationRecord(order.getUserId(), order.getCombinationActivityId(), 48 combinationRecordApi.validateCombinationRecord(order.getUserId(), order.getCombinationActivityId(),
36 order.getCombinationHeadId(), item.getSkuId(), item.getCount()); 49 order.getCombinationHeadId(), item.getSkuId(), item.getCount());
37 - // TODO @puhui999:这里还要限制下,是不是已经 createOrder;就是还没支付的时候,重复下单了;需要校验下;不然的话,一个拼团可以下多个单子了; 50 +
  51 + // 2. 校验该用户是否存在未支付的拼团活动订单,避免一个拼团可以下多个单子了
  52 + // TODO @puhui999:只校验未支付的拼团订单噢
  53 + TradeOrderDO activityOrder = orderQueryService.getActivityOrderByUserIdAndActivityIdAndStatus(
  54 + order.getUserId(), order.getCombinationActivityId(), TradeOrderStatusEnum.UNPAID.getStatus());
  55 + if (activityOrder != null) {
  56 + throw exception(ORDER_CREATE_FAIL_EXIST_UNPAID);
  57 + }
38 } 58 }
39 59
40 @Override 60 @Override
41 public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { 61 public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
42 - // 如果不是拼团订单则结束  
43 - if (TradeOrderTypeEnum.isCombination(order.getType())) { 62 + // 1.如果不是拼团订单则结束
  63 + if (!TradeOrderTypeEnum.isCombination(order.getType())) {
44 return; 64 return;
45 } 65 }
46 Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品"); 66 Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品");
47 67
48 - // 获取商品信息 68 + // 2. 创建拼团记录
49 TradeOrderItemDO item = orderItems.get(0); 69 TradeOrderItemDO item = orderItems.get(0);
50 - // 创建拼团记录  
51 - combinationRecordApi.createCombinationRecord(TradeOrderConvert.INSTANCE.convert(order, item)); 70 + KeyValue<Long, Long> recordIdAndHeadId = combinationRecordApi.createCombinationRecord(
  71 + TradeOrderConvert.INSTANCE.convert(order, item));
  72 +
  73 + // 3. 更新拼团相关信息到订单
  74 + // TODO 芋艿,只需要更新 record
  75 + orderUpdateService.updateOrderCombinationInfo(order.getId(), order.getCombinationActivityId(),
  76 + recordIdAndHeadId.getKey(), recordIdAndHeadId.getValue());
52 } 77 }
53 78
54 @Override 79 @Override
55 - public void cancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {  
56 - if (TradeOrderTypeEnum.isCombination(order.getType())) { 80 + public void beforeDeliveryOrder(TradeOrderDO order) {
  81 + if (!TradeOrderTypeEnum.isCombination(order.getType())) {
57 return; 82 return;
58 } 83 }
  84 + // 校验订单拼团是否成功
  85 + if (!combinationRecordApi.isCombinationRecordSuccess(order.getUserId(), order.getId())) {
  86 + throw exception(ORDER_DELIVERY_FAIL_COMBINATION_RECORD_STATUS_NOT_SUCCESS);
  87 + }
59 } 88 }
60 89
61 } 90 }
  91 +
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeCouponOrderHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.order.handler;
  2 +
  3 +import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
  4 +import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponUseReqDTO;
  5 +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
  6 +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import javax.annotation.Resource;
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * 优惠劵的 {@link TradeOrderHandler} 实现类
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Component
  18 +public class TradeCouponOrderHandler implements TradeOrderHandler {
  19 +
  20 + @Resource
  21 + private CouponApi couponApi;
  22 +
  23 + @Override
  24 + public void afterOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  25 + if (order.getCouponId() == null || order.getCouponId() <= 0) {
  26 + return;
  27 + }
  28 + // 不在前置扣减的原因,是因为优惠劵要记录使用的订单号
  29 + couponApi.useCoupon(new CouponUseReqDTO().setId(order.getCouponId()).setUserId(order.getUserId())
  30 + .setOrderId(order.getId()));
  31 + }
  32 +
  33 + @Override
  34 + public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  35 + if (order.getCouponId() == null || order.getCouponId() <= 0) {
  36 + return;
  37 + }
  38 + // 退回优惠劵
  39 + couponApi.returnUsedCoupon(order.getCouponId());
  40 + }
  41 +
  42 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeMemberPointOrderHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.order.handler;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
  5 +import cn.iocoder.yudao.module.member.api.point.MemberPointApi;
  6 +import cn.iocoder.yudao.module.member.enums.MemberExperienceBizTypeEnum;
  7 +import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;
  8 +import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.AfterSaleDO;
  9 +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
  10 +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
  11 +import cn.iocoder.yudao.module.trade.service.aftersale.AfterSaleService;
  12 +import org.springframework.stereotype.Component;
  13 +
  14 +import javax.annotation.Resource;
  15 +import java.util.List;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
  18 +
  19 +/**
  20 + * 会员积分、等级的 {@link TradeOrderHandler} 实现类
  21 + *
  22 + * @author owen
  23 + */
  24 +@Component
  25 +public class TradeMemberPointOrderHandler implements TradeOrderHandler {
  26 +
  27 + @Resource
  28 + private MemberPointApi memberPointApi;
  29 + @Resource
  30 + private MemberLevelApi memberLevelApi;
  31 +
  32 + @Resource
  33 + private AfterSaleService afterSaleService;
  34 +
  35 + @Override
  36 + public void afterOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  37 + // 扣减用户积分(订单抵扣)。不在前置扣减的原因,是因为积分扣减时,需要记录关联业务
  38 + reducePoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_USE, order.getId());
  39 + }
  40 +
  41 + @Override
  42 + public void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  43 + // 增加用户积分(订单赠送)
  44 + addPoint(order.getUserId(), order.getGivePoint(), MemberPointBizTypeEnum.ORDER_GIVE,
  45 + order.getId());
  46 +
  47 + // 增加用户经验
  48 + memberLevelApi.addExperience(order.getUserId(), order.getPayPrice(),
  49 + MemberExperienceBizTypeEnum.ORDER_GIVE.getType(), String.valueOf(order.getId()));
  50 + }
  51 +
  52 + @Override
  53 + public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  54 + // 售后的订单项,已经在 afterCancelOrderItem 回滚库存,所以这里不需要重复回滚
  55 + orderItems = filterOrderItemListByNoneAfterSale(orderItems);
  56 + if (CollUtil.isEmpty(orderItems)) {
  57 + return;
  58 + }
  59 +
  60 + // 增加(回滚)用户积分(订单抵扣)
  61 + Integer usePoint = getSumValue(orderItems, TradeOrderItemDO::getUsePoint, Integer::sum);
  62 + addPoint(order.getUserId(), usePoint, MemberPointBizTypeEnum.ORDER_USE_CANCEL,
  63 + order.getId());
  64 +
  65 + // 如下的返还,需要经过支持,也就是经历 afterPayOrder 流程
  66 + if (!order.getPayStatus()) {
  67 + return;
  68 + }
  69 + // 扣减(回滚)积分(订单赠送)
  70 + Integer givePoint = getSumValue(orderItems, TradeOrderItemDO::getGivePoint, Integer::sum);
  71 + reducePoint(order.getUserId(), givePoint, MemberPointBizTypeEnum.ORDER_GIVE_CANCEL,
  72 + order.getId());
  73 + // 扣减(回滚)用户经验
  74 + int payPrice = order.getPayPrice() - order.getRefundPrice();
  75 + memberLevelApi.addExperience(order.getUserId(), payPrice,
  76 + MemberExperienceBizTypeEnum.ORDER_GIVE_CANCEL.getType(), String.valueOf(order.getId()));
  77 + }
  78 +
  79 + @Override
  80 + public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {
  81 + // 扣减(回滚)积分(订单赠送)
  82 + reducePoint(order.getUserId(), orderItem.getGivePoint(), MemberPointBizTypeEnum.ORDER_GIVE_CANCEL_ITEM,
  83 + orderItem.getId());
  84 + // 增加(回滚)积分(订单抵扣)
  85 + addPoint(order.getUserId(), orderItem.getUsePoint(), MemberPointBizTypeEnum.ORDER_USE_CANCEL_ITEM,
  86 + orderItem.getId());
  87 +
  88 + // 扣减(回滚)用户经验
  89 + AfterSaleDO afterSale = afterSaleService.getAfterSale(orderItem.getAfterSaleId());
  90 + memberLevelApi.reduceExperience(order.getUserId(), afterSale.getRefundPrice(),
  91 + MemberExperienceBizTypeEnum.ORDER_GIVE_CANCEL_ITEM.getType(), String.valueOf(orderItem.getId()));
  92 + }
  93 +
  94 + /**
  95 + * 添加用户积分
  96 + * <p>
  97 + * 目前是支付成功后,就会创建积分记录。
  98 + * <p>
  99 + * 业内还有两种做法,可以根据自己的业务调整:
  100 + * 1. 确认收货后,才创建积分记录
  101 + * 2. 支付 or 下单成功时,创建积分记录(冻结),确认收货解冻或者 n 天后解冻
  102 + *
  103 + * @param userId 用户编号
  104 + * @param point 增加积分数量
  105 + * @param bizType 业务编号
  106 + * @param bizId 业务编号
  107 + */
  108 + protected void addPoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) {
  109 + if (point != null && point > 0) {
  110 + memberPointApi.addPoint(userId, point, bizType.getType(), String.valueOf(bizId));
  111 + }
  112 + }
  113 +
  114 + protected void reducePoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) {
  115 + if (point != null && point > 0) {
  116 + memberPointApi.reducePoint(userId, point, bizType.getType(), String.valueOf(bizId));
  117 + }
  118 + }
  119 +
  120 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeOrderHandler.java
1 package cn.iocoder.yudao.module.trade.service.order.handler; 1 package cn.iocoder.yudao.module.trade.service.order.handler;
2 2
  3 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
3 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 4 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
4 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 5 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
  6 +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
5 7
6 import java.util.List; 8 import java.util.List;
7 9
@@ -35,16 +37,42 @@ public interface TradeOrderHandler { @@ -35,16 +37,42 @@ public interface TradeOrderHandler {
35 * @param order 订单 37 * @param order 订单
36 * @param orderItems 订单项 38 * @param orderItems 订单项
37 */ 39 */
38 - default void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {  
39 - } 40 + default void afterPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {}
40 41
41 /** 42 /**
42 - * 订单取消 43 + * 订单取消后
43 * 44 *
44 * @param order 订单 45 * @param order 订单
45 * @param orderItems 订单项 46 * @param orderItems 订单项
46 */ 47 */
47 - default void cancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { 48 + default void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {}
  49 +
  50 + /**
  51 + * 订单项取消后
  52 + *
  53 + * @param order 订单
  54 + * @param orderItem 订单项
  55 + */
  56 + default void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {}
  57 +
  58 + /**
  59 + * 订单发货前
  60 + *
  61 + * @param order 订单
  62 + */
  63 + default void beforeDeliveryOrder(TradeOrderDO order) {}
  64 +
  65 + // ========== 公用方法 ==========
  66 +
  67 + /**
  68 + * 过滤“未售后”的订单项列表
  69 + *
  70 + * @param orderItems 订单项列表
  71 + * @return 过滤后的订单项列表
  72 + */
  73 + default List<TradeOrderItemDO> filterOrderItemListByNoneAfterSale(List<TradeOrderItemDO> orderItems) {
  74 + return CollectionUtils.filterList(orderItems,
  75 + item -> TradeOrderItemAfterSaleStatusEnum.isNone(item.getAfterSaleStatus()));
48 } 76 }
49 77
50 } 78 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeProductSkuOrderHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.order.handler;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
  5 +import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
  6 +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
  7 +import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
  8 +import org.springframework.stereotype.Component;
  9 +
  10 +import javax.annotation.Resource;
  11 +import java.util.List;
  12 +
  13 +import static java.util.Collections.singletonList;
  14 +
  15 +/**
  16 + * 商品 SKU 库存的 {@link TradeOrderHandler} 实现类
  17 + *
  18 + * @author 芋道源码
  19 + */
  20 +@Component
  21 +public class TradeProductSkuOrderHandler implements TradeOrderHandler {
  22 +
  23 + @Resource
  24 + private ProductSkuApi productSkuApi;
  25 +
  26 + @Override
  27 + public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  28 + productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convertNegative(orderItems));
  29 + }
  30 +
  31 + @Override
  32 + public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  33 + // 售后的订单项,已经在 afterCancelOrderItem 回滚库存,所以这里不需要重复回滚
  34 + orderItems = filterOrderItemListByNoneAfterSale(orderItems);
  35 + if (CollUtil.isEmpty(orderItems)) {
  36 + return;
  37 + }
  38 + productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems));
  39 + }
  40 +
  41 + @Override
  42 + public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {
  43 + productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(singletonList(orderItem)));
  44 + }
  45 +
  46 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeSeckillHandler.java renamed to yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeSeckillOrderHandler.java
1 package cn.iocoder.yudao.module.trade.service.order.handler; 1 package cn.iocoder.yudao.module.trade.service.order.handler;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
3 import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi; 5 import cn.iocoder.yudao.module.promotion.api.seckill.SeckillActivityApi;
4 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 6 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
5 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 7 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
@@ -10,33 +12,53 @@ import javax.annotation.Resource; @@ -10,33 +12,53 @@ import javax.annotation.Resource;
10 import java.util.List; 12 import java.util.List;
11 13
12 /** 14 /**
13 - * 秒杀订单 handler 实现类 15 + * 秒杀订单的 {@link TradeOrderHandler} 实现类
14 * 16 *
15 * @author HUIHUI 17 * @author HUIHUI
16 */ 18 */
17 @Component 19 @Component
18 -public class TradeSeckillHandler implements TradeOrderHandler { 20 +public class TradeSeckillOrderHandler implements TradeOrderHandler {
19 21
20 @Resource 22 @Resource
21 private SeckillActivityApi seckillActivityApi; 23 private SeckillActivityApi seckillActivityApi;
22 24
23 @Override 25 @Override
24 public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) { 26 public void beforeOrderCreate(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
25 - if (TradeOrderTypeEnum.isSeckill(order.getType())) { 27 + if (!TradeOrderTypeEnum.isSeckill(order.getType())) {
26 return; 28 return;
27 } 29 }
  30 + // 明确校验一下
  31 + Assert.isTrue(orderItems.size() == 1, "秒杀时,只允许选择一个商品");
28 32
29 // 扣减秒杀活动的库存 33 // 扣减秒杀活动的库存
30 - seckillActivityApi.updateSeckillStock(order.getSeckillActivityId(), 34 + seckillActivityApi.updateSeckillStockDecr(order.getSeckillActivityId(),
31 orderItems.get(0).getSkuId(), orderItems.get(0).getCount()); 35 orderItems.get(0).getSkuId(), orderItems.get(0).getCount());
32 } 36 }
33 37
34 @Override 38 @Override
35 - public void cancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {  
36 - if (TradeOrderTypeEnum.isSeckill(order.getType())) { 39 + public void afterCancelOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems) {
  40 + if (!TradeOrderTypeEnum.isSeckill(order.getType())) {
37 return; 41 return;
38 } 42 }
  43 + // 明确校验一下
  44 + Assert.isTrue(orderItems.size() == 1, "秒杀时,只允许选择一个商品");
39 45
  46 + // 售后的订单项,已经在 afterCancelOrderItem 回滚库存,所以这里不需要重复回滚
  47 + orderItems = filterOrderItemListByNoneAfterSale(orderItems);
  48 + if (CollUtil.isEmpty(orderItems)) {
  49 + return;
  50 + }
  51 + afterCancelOrderItem(order, orderItems.get(0));
  52 + }
  53 +
  54 + @Override
  55 + public void afterCancelOrderItem(TradeOrderDO order, TradeOrderItemDO orderItem) {
  56 + if (!TradeOrderTypeEnum.isSeckill(order.getType())) {
  57 + return;
  58 + }
  59 + // 恢复秒杀活动的库存
  60 + seckillActivityApi.updateSeckillStockIncr(order.getSeckillActivityId(),
  61 + orderItem.getSkuId(), orderItem.getCount());
40 } 62 }
41 63
42 } 64 }
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/level/MemberLevelApi.java
@@ -28,4 +28,14 @@ public interface MemberLevelApi { @@ -28,4 +28,14 @@ public interface MemberLevelApi {
28 */ 28 */
29 void addExperience(Long userId, Integer experience, Integer bizType, String bizId); 29 void addExperience(Long userId, Integer experience, Integer bizType, String bizId);
30 30
  31 + /**
  32 + * 扣减会员经验
  33 + *
  34 + * @param userId 会员ID
  35 + * @param experience 经验
  36 + * @param bizType 业务类型 {@link MemberExperienceBizTypeEnum}
  37 + * @param bizId 业务编号
  38 + */
  39 + void reduceExperience(Long userId, Integer experience, Integer bizType, String bizId);
  40 +
31 } 41 }
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/MemberExperienceBizTypeEnum.java
@@ -20,10 +20,11 @@ public enum MemberExperienceBizTypeEnum { @@ -20,10 +20,11 @@ public enum MemberExperienceBizTypeEnum {
20 */ 20 */
21 ADMIN(0, "管理员调整", "管理员调整获得 {} 经验", true), 21 ADMIN(0, "管理员调整", "管理员调整获得 {} 经验", true),
22 INVITE_REGISTER(1, "邀新奖励", "邀请好友获得 {} 经验", true), 22 INVITE_REGISTER(1, "邀新奖励", "邀请好友获得 {} 经验", true),
23 - ORDER(2, "下单奖励", "下单获得 {} 经验", true),  
24 - REFUND(3, "退单扣除", "退单获得 {} 经验", false),  
25 SIGN_IN(4, "签到奖励", "签到获得 {} 经验", true), 23 SIGN_IN(4, "签到奖励", "签到获得 {} 经验", true),
26 LOTTERY(5, "抽奖奖励", "抽奖获得 {} 经验", true), 24 LOTTERY(5, "抽奖奖励", "抽奖获得 {} 经验", true),
  25 + ORDER_GIVE(11, "下单奖励", "下单获得 {} 经验", true),
  26 + ORDER_GIVE_CANCEL(12, "下单奖励(整单取消)", "取消订单获得 {} 经验", false), // ORDER_GIVE 的取消
  27 + ORDER_GIVE_CANCEL_ITEM(13, "下单奖励(单个退款)", "退款订单获得 {} 经验", false), // ORDER_GIVE 的取消
27 ; 28 ;
28 29
29 /** 30 /**
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/point/MemberPointBizTypeEnum.java
@@ -18,11 +18,14 @@ public enum MemberPointBizTypeEnum implements IntArrayValuable { @@ -18,11 +18,14 @@ public enum MemberPointBizTypeEnum implements IntArrayValuable {
18 18
19 SIGN(1, "签到", "签到获得 {} 积分", true), 19 SIGN(1, "签到", "签到获得 {} 积分", true),
20 ADMIN(2, "管理员修改", "管理员修改 {} 积分", true), 20 ADMIN(2, "管理员修改", "管理员修改 {} 积分", true),
21 - ORDER_GIVE(10, "订单奖励", "下单获得 {} 积分", true), // 支付订单时,赠送积分  
22 - ORDER_CANCEL(11, "订单取消", "订单取消,退还 {} 积分", true), // 取消订单时,退回积分  
23 - ORDER_USE(12, "订单使用", "下单使用 {} 积分", false), // 下单时,扣减积分  
24 - AFTER_SALE_REFUND_USED(13, "订单退款", "订单退款,退还 {} 积分", true), // 售后订单成功时,退回积分(对应 ORDER_USE 操作)  
25 - AFTER_SALE_DEDUCT_GIVE(14, "订单退款", "订单退款,扣除赠送的 {} 积分", false), // 售后订单成功时,扣减积分(对应 ORDER_GIVE 操作) 21 +
  22 + ORDER_USE(11, "订单积分抵扣", "下单使用 {} 积分", false), // 下单时,扣减积分
  23 + ORDER_USE_CANCEL(12, "订单积分抵扣(整单取消)", "订单取消,退还 {} 积分", true), // ORDER_USE 的取消
  24 + ORDER_USE_CANCEL_ITEM(13, "订单积分抵扣(单个退款)", "订单退款,退还 {} 积分", true), // ORDER_USE 的取消
  25 +
  26 + ORDER_GIVE(21, "订单积分奖励", "下单获得 {} 积分", true), // 支付订单时,赠送积分
  27 + ORDER_GIVE_CANCEL(22, "订单积分奖励(整单取消)", "订单取消,退还 {} 积分", false), // ORDER_GIVE 的取消
  28 + ORDER_GIVE_CANCEL_ITEM(23, "订单积分奖励(单个退款)", "订单退款,扣除赠送的 {} 积分", false) // ORDER_GIVE 的取消
26 ; 29 ;
27 30
28 /** 31 /**
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/level/MemberLevelApiImpl.java
@@ -38,4 +38,9 @@ public class MemberLevelApiImpl implements MemberLevelApi { @@ -38,4 +38,9 @@ public class MemberLevelApiImpl implements MemberLevelApi {
38 memberLevelService.addExperience(userId, experience, bizTypeEnum, bizId); 38 memberLevelService.addExperience(userId, experience, bizTypeEnum, bizId);
39 } 39 }
40 40
  41 + @Override
  42 + public void reduceExperience(Long userId, Integer experience, Integer bizType, String bizId) {
  43 + addExperience(userId, -experience, bizType, bizId);
  44 + }
  45 +
41 } 46 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/wallet/PayWalletApiImpl.java deleted
1 -package cn.iocoder.yudao.module.pay.api.wallet;  
2 -  
3 -import cn.iocoder.yudao.module.pay.api.wallet.dto.WalletSummaryRespDTO;  
4 -import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;  
5 -import cn.iocoder.yudao.module.pay.service.wallet.PayWalletRechargeService;  
6 -import cn.iocoder.yudao.module.pay.service.wallet.PayWalletTransactionService;  
7 -import org.springframework.stereotype.Service;  
8 -import org.springframework.validation.annotation.Validated;  
9 -  
10 -import javax.annotation.Resource;  
11 -import java.time.LocalDateTime;  
12 -  
13 -/**  
14 - * 钱包 API 接口实现类  
15 - *  
16 - * @author owen  
17 - */  
18 -@Service  
19 -@Validated  
20 -public class PayWalletApiImpl implements PayWalletApi {  
21 -  
22 - @Resource  
23 - private PayWalletRechargeService payWalletRechargeService;  
24 - @Resource  
25 - private PayWalletTransactionService payWalletTransactionService;  
26 -  
27 - @Override  
28 - public WalletSummaryRespDTO getWalletSummary(LocalDateTime beginTime, LocalDateTime endTime) {  
29 - WalletSummaryRespDTO walletSummary = payWalletRechargeService.getWalletSummary(beginTime, endTime);  
30 - walletSummary.setOrderWalletPayPrice(payWalletTransactionService.getPriceSummary(PayWalletBizTypeEnum.PAYMENT, beginTime, endTime));  
31 - return walletSummary;  
32 - }  
33 -  
34 -}  
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletRechargeMapper.java
1 package cn.iocoder.yudao.module.pay.dal.mysql.wallet; 1 package cn.iocoder.yudao.module.pay.dal.mysql.wallet;
2 2
3 -import cn.hutool.core.bean.BeanUtil;  
4 -import cn.hutool.core.collection.CollUtil;  
5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 4 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7 -import cn.iocoder.yudao.module.pay.api.wallet.dto.WalletSummaryRespDTO;  
8 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO; 5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO;
9 -import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;  
10 -import com.github.yulichang.toolkit.MPJWrappers;  
11 import org.apache.ibatis.annotations.Mapper; 6 import org.apache.ibatis.annotations.Mapper;
12 7
13 -import java.time.LocalDateTime;  
14 -  
15 @Mapper 8 @Mapper
16 public interface PayWalletRechargeMapper extends BaseMapperX<PayWalletRechargeDO> { 9 public interface PayWalletRechargeMapper extends BaseMapperX<PayWalletRechargeDO> {
17 10
@@ -25,24 +18,6 @@ public interface PayWalletRechargeMapper extends BaseMapperX&lt;PayWalletRechargeDO @@ -25,24 +18,6 @@ public interface PayWalletRechargeMapper extends BaseMapperX&lt;PayWalletRechargeDO
25 .eq(PayWalletRechargeDO::getId, id).eq(PayWalletRechargeDO::getRefundStatus, whereRefundStatus)); 18 .eq(PayWalletRechargeDO::getId, id).eq(PayWalletRechargeDO::getRefundStatus, whereRefundStatus));
26 } 19 }
27 20
28 - default WalletSummaryRespDTO selectRechargeSummaryByPayTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {  
29 - return BeanUtil.copyProperties(CollUtil.get(selectMaps(MPJWrappers.<PayWalletRechargeDO>lambdaJoin()  
30 - .selectCount(PayWalletRechargeDO::getId, WalletSummaryRespDTO::getRechargePayCount)  
31 - .selectSum(PayWalletRechargeDO::getPayPrice, WalletSummaryRespDTO::getRechargePayPrice)  
32 - .eq(PayWalletRechargeDO::getPayStatus, true)  
33 - .between(PayWalletRechargeDO::getPayTime, beginTime, endTime)), 0),  
34 - WalletSummaryRespDTO.class);  
35 - }  
36 -  
37 - default WalletSummaryRespDTO selectRechargeSummaryByRefundTimeBetween(LocalDateTime beginTime, LocalDateTime endTime) {  
38 - return BeanUtil.copyProperties(CollUtil.get(selectMaps(MPJWrappers.<PayWalletRechargeDO>lambdaJoin()  
39 - .selectCount(PayWalletRechargeDO::getId, WalletSummaryRespDTO::getRechargeRefundCount)  
40 - .selectSum(PayWalletRechargeDO::getRefundPayPrice, WalletSummaryRespDTO::getRechargeRefundPrice)  
41 - .eq(PayWalletRechargeDO::getRefundStatus, PayRefundStatusEnum.SUCCESS.getStatus())  
42 - .between(PayWalletRechargeDO::getRefundTime, beginTime, endTime)), 0),  
43 - WalletSummaryRespDTO.class);  
44 - }  
45 -  
46 } 21 }
47 22
48 23
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/wallet/PayWalletTransactionMapper.java
@@ -6,12 +6,9 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; @@ -6,12 +6,9 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO; 7 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.transaction.AppPayWalletTransactionPageReqVO;
8 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; 8 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
9 -import com.github.yulichang.toolkit.MPJWrappers;  
10 import org.apache.ibatis.annotations.Mapper; 9 import org.apache.ibatis.annotations.Mapper;
11 10
12 -import java.time.LocalDateTime;  
13 import java.util.Objects; 11 import java.util.Objects;
14 -import java.util.Optional;  
15 12
16 @Mapper 13 @Mapper
17 public interface PayWalletTransactionMapper extends BaseMapperX<PayWalletTransactionDO> { 14 public interface PayWalletTransactionMapper extends BaseMapperX<PayWalletTransactionDO> {
@@ -38,15 +35,6 @@ public interface PayWalletTransactionMapper extends BaseMapperX&lt;PayWalletTransac @@ -38,15 +35,6 @@ public interface PayWalletTransactionMapper extends BaseMapperX&lt;PayWalletTransac
38 PayWalletTransactionDO::getBizType, bizType); 35 PayWalletTransactionDO::getBizType, bizType);
39 } 36 }
40 37
41 - default Integer selectSummaryByBizTypeAndCreateTimeBetween(Integer type, LocalDateTime beginTime, LocalDateTime endTime) {  
42 - return Optional.ofNullable(selectOne(MPJWrappers.<PayWalletTransactionDO>lambdaJoin()  
43 - .selectSum(PayWalletTransactionDO::getPrice)  
44 - .eq(PayWalletTransactionDO::getBizType, type)  
45 - .between(PayWalletTransactionDO::getCreateTime, beginTime, endTime)))  
46 - .map(PayWalletTransactionDO::getPrice)  
47 - .orElse(0);  
48 - }  
49 -  
50 } 38 }
51 39
52 40
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeService.java
1 package cn.iocoder.yudao.module.pay.service.wallet; 1 package cn.iocoder.yudao.module.pay.service.wallet;
2 2
3 -import cn.iocoder.yudao.module.pay.api.wallet.dto.WalletSummaryRespDTO;  
4 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO; 3 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO;
5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO; 4 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletRechargeDO;
6 5
7 -import java.time.LocalDateTime;  
8 -  
9 /** 6 /**
10 * 钱包充值 Service 接口 7 * 钱包充值 Service 接口
11 * 8 *
@@ -16,8 +13,8 @@ public interface PayWalletRechargeService { @@ -16,8 +13,8 @@ public interface PayWalletRechargeService {
16 /** 13 /**
17 * 创建钱包充值记录(发起充值) 14 * 创建钱包充值记录(发起充值)
18 * 15 *
19 - * @param userId 用户 id  
20 - * @param userType 用户类型 16 + * @param userId 用户 id
  17 + * @param userType 用户类型
21 * @param createReqVO 钱包充值请求 VO 18 * @param createReqVO 钱包充值请求 VO
22 * @return 钱包充值记录 19 * @return 钱包充值记录
23 */ 20 */
@@ -27,32 +24,25 @@ public interface PayWalletRechargeService { @@ -27,32 +24,25 @@ public interface PayWalletRechargeService {
27 /** 24 /**
28 * 更新钱包充值成功 25 * 更新钱包充值成功
29 * 26 *
30 - * @param id 钱包充值记录 id 27 + * @param id 钱包充值记录 id
31 * @param payOrderId 支付订单 id 28 * @param payOrderId 支付订单 id
32 */ 29 */
33 void updateWalletRechargerPaid(Long id, Long payOrderId); 30 void updateWalletRechargerPaid(Long id, Long payOrderId);
34 31
35 /** 32 /**
36 * 发起钱包充值退款 33 * 发起钱包充值退款
37 - * @param id 钱包充值编号 34 + *
  35 + * @param id 钱包充值编号
38 * @param userIp 用户 ip 地址 36 * @param userIp 用户 ip 地址
39 */ 37 */
40 void refundWalletRecharge(Long id, String userIp); 38 void refundWalletRecharge(Long id, String userIp);
41 39
42 /** 40 /**
43 * 更新钱包充值记录为已退款 41 * 更新钱包充值记录为已退款
44 - * @param id 钱包充值 id 42 + *
  43 + * @param id 钱包充值 id
45 * @param payRefundId 退款单id 44 * @param payRefundId 退款单id
46 */ 45 */
47 void updateWalletRechargeRefunded(Long id, Long payRefundId); 46 void updateWalletRechargeRefunded(Long id, Long payRefundId);
48 47
49 - /**  
50 - * 获取钱包统计  
51 - *  
52 - * @param beginTime 起始时间  
53 - * @param endTime 截止时间  
54 - * @return 钱包统计  
55 - */  
56 - WalletSummaryRespDTO getWalletSummary(LocalDateTime beginTime, LocalDateTime endTime);  
57 -  
58 } 48 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletRechargeServiceImpl.java
@@ -4,7 +4,6 @@ import cn.hutool.core.lang.Assert; @@ -4,7 +4,6 @@ import cn.hutool.core.lang.Assert;
4 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; 4 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
5 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; 5 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
6 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; 6 import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
7 -import cn.iocoder.yudao.module.pay.api.wallet.dto.WalletSummaryRespDTO;  
8 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO; 7 import cn.iocoder.yudao.module.pay.controller.app.wallet.vo.recharge.AppPayWalletRechargeCreateReqVO;
9 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; 8 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
10 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; 9 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
@@ -279,15 +278,4 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService { @@ -279,15 +278,4 @@ public class PayWalletRechargeServiceImpl implements PayWalletRechargeService {
279 return payOrder; 278 return payOrder;
280 } 279 }
281 280
282 - @Override  
283 - public WalletSummaryRespDTO getWalletSummary(LocalDateTime beginTime, LocalDateTime endTime) {  
284 - WalletSummaryRespDTO paySummary = walletRechargeMapper.selectRechargeSummaryByPayTimeBetween(beginTime, endTime);  
285 - WalletSummaryRespDTO refundSummary = walletRechargeMapper.selectRechargeSummaryByRefundTimeBetween(beginTime, endTime);  
286 -  
287 - paySummary.setRechargeRefundCount(refundSummary.getRechargeRefundCount());  
288 - paySummary.setRechargeRefundPrice(refundSummary.getRechargeRefundPrice());  
289 -  
290 - return paySummary;  
291 - }  
292 -  
293 } 281 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletService.java
1 package cn.iocoder.yudao.module.pay.service.wallet; 1 package cn.iocoder.yudao.module.pay.service.wallet;
2 2
3 -import cn.iocoder.yudao.module.pay.api.wallet.dto.WalletSummaryRespDTO;  
4 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; 3 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
5 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; 4 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO;
6 import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum; 5 import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
7 6
8 -import java.time.LocalDateTime;  
9 -  
10 /** 7 /**
11 * 钱包 Service 接口 8 * 钱包 Service 接口
12 * 9 *
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletServiceImpl.java
1 package cn.iocoder.yudao.module.pay.service.wallet; 1 package cn.iocoder.yudao.module.pay.service.wallet;
2 2
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
4 -import cn.iocoder.yudao.module.pay.api.wallet.dto.WalletSummaryRespDTO;  
5 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; 4 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
6 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; 5 import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
7 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO; 6 import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletDO;
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletTransactionService.java
@@ -7,7 +7,6 @@ import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum; @@ -7,7 +7,6 @@ import cn.iocoder.yudao.module.pay.enums.member.PayWalletBizTypeEnum;
7 import cn.iocoder.yudao.module.pay.service.wallet.bo.WalletTransactionCreateReqBO; 7 import cn.iocoder.yudao.module.pay.service.wallet.bo.WalletTransactionCreateReqBO;
8 8
9 import javax.validation.Valid; 9 import javax.validation.Valid;
10 -import java.time.LocalDateTime;  
11 10
12 /** 11 /**
13 * 钱包余额流水 Service 接口 12 * 钱包余额流水 Service 接口
@@ -50,14 +49,4 @@ public interface PayWalletTransactionService { @@ -50,14 +49,4 @@ public interface PayWalletTransactionService {
50 */ 49 */
51 PayWalletTransactionDO getWalletTransaction(String bizId, PayWalletBizTypeEnum type); 50 PayWalletTransactionDO getWalletTransaction(String bizId, PayWalletBizTypeEnum type);
52 51
53 - /**  
54 - * 获取支付金额合计  
55 - *  
56 - * @param bizType 业务类型  
57 - * @param beginTime 开始时间  
58 - * @param endTime 结束时间  
59 - * @return 支付金额合计  
60 - */  
61 - Integer getPriceSummary(PayWalletBizTypeEnum bizType, LocalDateTime beginTime, LocalDateTime endTime);  
62 -  
63 } 52 }
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/wallet/PayWalletTransactionServiceImpl.java
@@ -13,7 +13,6 @@ import lombok.extern.slf4j.Slf4j; @@ -13,7 +13,6 @@ import lombok.extern.slf4j.Slf4j;
13 import org.springframework.stereotype.Service; 13 import org.springframework.stereotype.Service;
14 14
15 import javax.annotation.Resource; 15 import javax.annotation.Resource;
16 -import java.time.LocalDateTime;  
17 16
18 /** 17 /**
19 * 钱包流水 Service 实现类 18 * 钱包流水 Service 实现类
@@ -61,9 +60,4 @@ public class PayWalletTransactionServiceImpl implements PayWalletTransactionServ @@ -61,9 +60,4 @@ public class PayWalletTransactionServiceImpl implements PayWalletTransactionServ
61 return payWalletTransactionMapper.selectByBiz(bizId, type.getType()); 60 return payWalletTransactionMapper.selectByBiz(bizId, type.getType());
62 } 61 }
63 62
64 - @Override  
65 - public Integer getPriceSummary(PayWalletBizTypeEnum bizType, LocalDateTime beginTime, LocalDateTime endTime) {  
66 - return payWalletTransactionMapper.selectSummaryByBizTypeAndCreateTimeBetween(bizType.getType(), beginTime, endTime);  
67 - }  
68 -  
69 } 63 }
yudao-server/src/main/resources/application-local.yaml
@@ -167,6 +167,7 @@ logging: @@ -167,6 +167,7 @@ logging:
167 cn.iocoder.yudao.module.member.dal.mysql: debug 167 cn.iocoder.yudao.module.member.dal.mysql: debug
168 cn.iocoder.yudao.module.trade.dal.mysql: debug 168 cn.iocoder.yudao.module.trade.dal.mysql: debug
169 cn.iocoder.yudao.module.promotion.dal.mysql: debug 169 cn.iocoder.yudao.module.promotion.dal.mysql: debug
  170 + cn.iocoder.yudao.module.statistics.dal.mysql: debug
170 171
171 debug: false 172 debug: false
172 173