Commit 2e559ebe062a02f131929f9828e84d6a58536f98

Authored by puhui999
2 parents c71bdd15 8a11ca81

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

# Conflicts:
#	yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
@@ -232,7 +232,8 @@ public class CollectionUtils { @@ -232,7 +232,8 @@ public class CollectionUtils {
232 return valueFunc.apply(t); 232 return valueFunc.apply(t);
233 } 233 }
234 234
235 - public static <T, V extends Comparable<? super V>> V getSumValue(List<T> from, Function<T, V> valueFunc, BinaryOperator<V> accumulator) { 235 + public static <T, V extends Comparable<? super V>> V getSumValue(List<T> from, Function<T, V> valueFunc,
  236 + BinaryOperator<V> accumulator) {
236 if (CollUtil.isEmpty(from)) { 237 if (CollUtil.isEmpty(from)) {
237 return null; 238 return null;
238 } 239 }
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/combination/CombinationRecordStatusEnum.java
@@ -44,4 +44,8 @@ public enum CombinationRecordStatusEnum implements IntArrayValuable { @@ -44,4 +44,8 @@ public enum CombinationRecordStatusEnum implements IntArrayValuable {
44 return ObjectUtil.equal(status, IN_PROGRESS.getStatus()); 44 return ObjectUtil.equal(status, IN_PROGRESS.getStatus());
45 } 45 }
46 46
  47 + public static boolean isFailed(Integer status) {
  48 + return ObjectUtil.equal(status, FAILED.getStatus());
  49 + }
  50 +
47 } 51 }
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationRecordMapper.java
@@ -35,7 +35,7 @@ public interface CombinationRecordMapper extends BaseMapperX&lt;CombinationRecordDO @@ -35,7 +35,7 @@ public interface CombinationRecordMapper extends BaseMapperX&lt;CombinationRecordDO
35 * @param headId 团长编号 35 * @param headId 团长编号
36 * @return 拼团记录 36 * @return 拼团记录
37 */ 37 */
38 - default CombinationRecordDO selectOneByHeadId(Long headId, Integer status) { 38 + default CombinationRecordDO selectByHeadId(Long headId, Integer status) {
39 return selectOne(new LambdaQueryWrapperX<CombinationRecordDO>() 39 return selectOne(new LambdaQueryWrapperX<CombinationRecordDO>()
40 .eq(CombinationRecordDO::getId, headId) 40 .eq(CombinationRecordDO::getId, headId)
41 .eq(CombinationRecordDO::getStatus, status)); 41 .eq(CombinationRecordDO::getStatus, status));
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordService.java
@@ -42,7 +42,8 @@ public interface CombinationRecordService { @@ -42,7 +42,8 @@ public interface CombinationRecordService {
42 * @param count 数量 42 * @param count 数量
43 * @return 拼团信息 43 * @return 拼团信息
44 */ 44 */
45 - KeyValue<CombinationActivityDO, CombinationProductDO> validateCombinationRecord(Long userId, Long activityId, Long headId, Long skuId, Integer count); 45 + KeyValue<CombinationActivityDO, CombinationProductDO> validateCombinationRecord(Long userId, Long activityId, Long headId,
  46 + Long skuId, Integer count);
46 47
47 /** 48 /**
48 * 创建拼团记录 49 * 创建拼团记录
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java
@@ -89,13 +89,13 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -89,13 +89,13 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
89 @Override 89 @Override
90 public KeyValue<CombinationActivityDO, CombinationProductDO> validateCombinationRecord( 90 public KeyValue<CombinationActivityDO, CombinationProductDO> validateCombinationRecord(
91 Long userId, Long activityId, Long headId, Long skuId, Integer count) { 91 Long userId, Long activityId, Long headId, Long skuId, Integer count) {
92 - // 1 校验拼团活动是否存在 92 + // 1. 校验拼团活动是否存在
93 CombinationActivityDO activity = combinationActivityService.validateCombinationActivityExists(activityId); 93 CombinationActivityDO activity = combinationActivityService.validateCombinationActivityExists(activityId);
94 // 1.1 校验活动是否开启 94 // 1.1 校验活动是否开启
95 if (ObjUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) { 95 if (ObjUtil.equal(activity.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
96 throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE); 96 throw exception(COMBINATION_ACTIVITY_STATUS_DISABLE);
97 } 97 }
98 - // 1.2、校验活动开始时间 98 + // 1.2. 校验活动开始时间
99 if (afterNow(activity.getStartTime())) { 99 if (afterNow(activity.getStartTime())) {
100 throw exception(COMBINATION_RECORD_FAILED_TIME_NOT_START); 100 throw exception(COMBINATION_RECORD_FAILED_TIME_NOT_START);
101 } 101 }
@@ -104,69 +104,67 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -104,69 +104,67 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
104 throw exception(COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED); 104 throw exception(COMBINATION_RECORD_FAILED_SINGLE_LIMIT_COUNT_EXCEED);
105 } 105 }
106 106
107 - // 2、父拼团是否存在,是否已经满了 107 + // 2. 父拼团是否存在,是否已经满了
108 if (headId != null) { 108 if (headId != null) {
109 - // 2.1、查询进行中的父拼团  
110 - CombinationRecordDO record = recordMapper.selectOneByHeadId(headId, CombinationRecordStatusEnum.IN_PROGRESS.getStatus()); 109 + // 2.1. 查询进行中的父拼团
  110 + CombinationRecordDO record = recordMapper.selectByHeadId(headId, CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
111 if (record == null) { 111 if (record == null) {
112 throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS); 112 throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS);
113 } 113 }
114 - // 2.2、校验拼团是否满足要求 114 + // 2.2. 校验拼团是否已满
115 if (ObjUtil.equal(record.getUserCount(), record.getUserSize())) { 115 if (ObjUtil.equal(record.getUserCount(), record.getUserSize())) {
116 throw exception(COMBINATION_RECORD_USER_FULL); 116 throw exception(COMBINATION_RECORD_USER_FULL);
117 } 117 }
118 - // 2.3、校验拼团是否过期(有父拼团的时候只校验父拼团的过期时间) 118 + // 2.3 校验拼团是否过期(有父拼团的时候只校验父拼团的过期时间)
119 if (beforeNow(record.getExpireTime())) { 119 if (beforeNow(record.getExpireTime())) {
120 throw exception(COMBINATION_RECORD_FAILED_TIME_END); 120 throw exception(COMBINATION_RECORD_FAILED_TIME_END);
121 } 121 }
122 } else { 122 } else {
123 - // 3、校验当前活动是否结束(自己是父拼团的时候才校验活动是否结束) 123 + // 3. 校验当前活动是否结束(自己是父拼团的时候才校验活动是否结束)
124 if (beforeNow(activity.getEndTime())) { 124 if (beforeNow(activity.getEndTime())) {
125 throw exception(COMBINATION_RECORD_FAILED_TIME_END); 125 throw exception(COMBINATION_RECORD_FAILED_TIME_END);
126 } 126 }
127 } 127 }
128 128
129 - // 4、校验活动商品是否存在 129 + // 4.1 校验活动商品是否存在
130 CombinationProductDO product = combinationActivityService.selectByActivityIdAndSkuId(activityId, skuId); 130 CombinationProductDO product = combinationActivityService.selectByActivityIdAndSkuId(activityId, skuId);
131 if (product == null) { 131 if (product == null) {
132 throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS); 132 throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);
133 } 133 }
134 -  
135 - // 5、校验 sku 是否存在 134 + // 4.2 校验 sku 是否存在
136 ProductSkuRespDTO sku = productSkuApi.getSku(skuId); 135 ProductSkuRespDTO sku = productSkuApi.getSku(skuId);
137 if (sku == null) { 136 if (sku == null) {
138 throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS); 137 throw exception(COMBINATION_JOIN_ACTIVITY_PRODUCT_NOT_EXISTS);
139 } 138 }
140 - // 5.1、校验库存是否充足 139 + // 4.3 校验库存是否充足
141 if (count > sku.getStock()) { 140 if (count > sku.getStock()) {
142 throw exception(COMBINATION_ACTIVITY_UPDATE_STOCK_FAIL); 141 throw exception(COMBINATION_ACTIVITY_UPDATE_STOCK_FAIL);
143 } 142 }
144 143
145 - // 6、校验是否有拼团记录  
146 - List<CombinationRecordDO> recordList = getCombinationRecordListByUserIdAndActivityId(userId, activityId);  
147 - if (CollUtil.isEmpty(recordList)) { 144 + // 6.1 校验是否有拼团记录
  145 + List<CombinationRecordDO> recordList = recordMapper.selectListByUserIdAndActivityId(userId, activityId);
  146 + recordList.removeIf(record -> CombinationRecordStatusEnum.isFailed(record.getStatus())); // 取消的订单,不算数
  147 + if (CollUtil.isEmpty(recordList)) { // 如果为空,说明可以参与,直接返回
148 return new KeyValue<>(activity, product); 148 return new KeyValue<>(activity, product);
149 } 149 }
150 - // 6.1、校验用户是否有该活动正在进行的拼团  
151 - List<CombinationRecordDO> filtered = filterList(recordList, record -> CombinationRecordStatusEnum.isInProgress(record.getStatus()));  
152 - if (CollUtil.isNotEmpty(filtered)) { 150 + // 6.2 校验用户是否有该活动正在进行的拼团
  151 + CombinationRecordDO inProgressRecord = findFirst(recordList,
  152 + record -> CombinationRecordStatusEnum.isInProgress(record.getStatus()));
  153 + if (inProgressRecord != null) {
153 throw exception(COMBINATION_RECORD_FAILED_HAVE_JOINED); 154 throw exception(COMBINATION_RECORD_FAILED_HAVE_JOINED);
154 } 155 }
155 - // 6.2、校验是否超出总限购数量  
156 - Integer sumValue = getSumValue(convertList(recordList, CombinationRecordDO::getCount,  
157 - item -> CombinationRecordStatusEnum.isSuccess(item.getStatus())), i -> i, Integer::sum);  
158 - if ((sumValue + count) > activity.getTotalLimitCount()) { 156 + // 6.3 校验是否超出总限购数量
  157 + Integer sumValue = getSumValue(recordList, CombinationRecordDO::getCount, Integer::sum);
  158 + if (sumValue != null && sumValue + count > activity.getTotalLimitCount()) {
159 throw exception(COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED); 159 throw exception(COMBINATION_RECORD_FAILED_TOTAL_LIMIT_COUNT_EXCEED);
160 } 160 }
161 -  
162 return new KeyValue<>(activity, product); 161 return new KeyValue<>(activity, product);
163 } 162 }
164 163
165 - // TODO 芋艿:在详细 review 下;  
166 @Override 164 @Override
167 @Transactional(rollbackFor = Exception.class) 165 @Transactional(rollbackFor = Exception.class)
168 public void createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) { 166 public void createCombinationRecord(CombinationRecordCreateReqDTO reqDTO) {
169 - // 1、校验拼团活动 167 + // 1. 校验拼团活动
170 KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(reqDTO.getUserId(), 168 KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(reqDTO.getUserId(),
171 reqDTO.getActivityId(), reqDTO.getHeadId(), reqDTO.getSkuId(), reqDTO.getCount()); 169 reqDTO.getActivityId(), reqDTO.getHeadId(), reqDTO.getSkuId(), reqDTO.getCount());
172 170
@@ -174,23 +172,25 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -174,23 +172,25 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
174 MemberUserRespDTO user = memberUserApi.getUser(reqDTO.getUserId()); 172 MemberUserRespDTO user = memberUserApi.getUser(reqDTO.getUserId());
175 ProductSpuRespDTO spu = productSpuApi.getSpu(reqDTO.getSpuId()); 173 ProductSpuRespDTO spu = productSpuApi.getSpu(reqDTO.getSpuId());
176 ProductSkuRespDTO sku = productSkuApi.getSku(reqDTO.getSkuId()); 174 ProductSkuRespDTO sku = productSkuApi.getSku(reqDTO.getSkuId());
177 - CombinationRecordDO recordDO = CombinationActivityConvert.INSTANCE.convert(reqDTO, keyValue.getKey(), user, spu, sku);  
178 - recordMapper.insert(recordDO);  
179 -  
180 - // 3、如果是团长(CombinationRecordCreateReqDTO#headId 为 null 时)需要设置 headId 为 CombinationRecordDO#HEAD_ID_GROUP  
181 - if (reqDTO.getHeadId() == null) {  
182 - recordMapper.updateById(new CombinationRecordDO().setId(recordDO.getId()).setHeadId(CombinationRecordDO.HEAD_ID_GROUP)); 175 + CombinationRecordDO record = CombinationActivityConvert.INSTANCE.convert(reqDTO, keyValue.getKey(), user, spu, sku);
  176 + // TODO @puhui:有 head 的情况下,以 head 结束为准
  177 + recordMapper.insert(record);
  178 +
  179 + // 3. 如果是团长需要设置 headId 为 CombinationRecordDO#HEAD_ID_GROUP
  180 + // TODO @puhui999:是不是只要是团长,record 设置了就好啦,不用 update。。。。
  181 + if (ObjUtil.equal(CombinationRecordDO.HEAD_ID_GROUP, reqDTO.getHeadId())) {
  182 + recordMapper.updateById(new CombinationRecordDO().setId(record.getId()).setHeadId(CombinationRecordDO.HEAD_ID_GROUP));
183 return; 183 return;
184 } 184 }
185 185
186 - // TODO 这里要不要弄成异步的 186 + // TODO 这里要不要弄成异步的;不用异步哈,就是事务好了;
187 // 4、更新拼团相关信息到订单 187 // 4、更新拼团相关信息到订单
188 - updateOrderCombinationInfo(recordDO.getOrderId(), recordDO.getActivityId(), recordDO.getId(), recordDO.getHeadId()); 188 + updateOrderCombinationInfo(record.getOrderId(), record.getActivityId(), record.getId(), record.getHeadId());
189 // 4、更新拼团记录 189 // 4、更新拼团记录
190 - updateCombinationRecords(keyValue.getKey(), reqDTO.getHeadId());  
191 - 190 + updateCombinationRecordWhenCreate(reqDTO.getHeadId(), keyValue.getKey());
192 } 191 }
193 192
  193 + // TODO @puhui999:这个更新,放到 trade 那就好了;createCombinationRecord 返回一个 recordId;
194 /** 194 /**
195 * 更新拼团相关信息到订单 195 * 更新拼团相关信息到订单
196 * 196 *
@@ -204,35 +204,31 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -204,35 +204,31 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
204 } 204 }
205 205
206 /** 206 /**
207 - * 更新拼团记录 207 + * 当新增拼团时,更新拼团记录的进展
208 * 208 *
209 - * @param activity 活动  
210 * @param headId 团长编号 209 * @param headId 团长编号
  210 + * @param activity 活动
211 */ 211 */
212 - private void updateCombinationRecords(CombinationActivityDO activity, Long headId) {  
213 - // 团长  
214 - CombinationRecordDO recordHead = recordMapper.selectById(headId);  
215 - // 团员 212 + private void updateCombinationRecordWhenCreate(Long headId, CombinationActivityDO activity) {
  213 + // 1. 团长 + 团员
216 List<CombinationRecordDO> records = getCombinationRecordListByHeadId(headId); 214 List<CombinationRecordDO> records = getCombinationRecordListByHeadId(headId);
217 - // 需要更新的记录  
218 - List<CombinationRecordDO> updateRecords = new ArrayList<>();  
219 -  
220 if (CollUtil.isEmpty(records)) { 215 if (CollUtil.isEmpty(records)) {
221 return; 216 return;
222 } 217 }
223 - records.add(recordHead); // 加入团长,团长也需要更新  
224 - boolean isEqual = ObjUtil.equal(records.size(), activity.getUserSize()); 218 + CombinationRecordDO headRecord = recordMapper.selectById(headId);
  219 +
  220 + // 2. 批量更新记录
  221 + List<CombinationRecordDO> updateRecords = new ArrayList<>();
  222 + records.add(headRecord); // 加入团长,团长也需要更新
  223 + boolean isFull = records.size() >= activity.getUserSize();
225 records.forEach(item -> { 224 records.forEach(item -> {
226 - CombinationRecordDO recordDO = new CombinationRecordDO();  
227 - recordDO.setId(item.getId());  
228 - recordDO.setUserCount(records.size());  
229 - // 校验拼团是否满足要求  
230 - if (isEqual) {  
231 - recordDO.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus()); 225 + CombinationRecordDO updateRecord = new CombinationRecordDO();
  226 + updateRecord.setId(item.getId()).setUserCount(records.size());
  227 + if (isFull) {
  228 + updateRecord.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus());
232 } 229 }
233 - updateRecords.add(recordDO); 230 + updateRecords.add(updateRecord);
234 }); 231 });
235 -  
236 recordMapper.updateBatch(updateRecords); 232 recordMapper.updateBatch(updateRecords);
237 } 233 }
238 234
@@ -247,12 +243,12 @@ public class CombinationRecordServiceImpl implements CombinationRecordService { @@ -247,12 +243,12 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
247 } 243 }
248 244
249 @Override 245 @Override
250 - public CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId, Long skuId, Integer count) {  
251 - KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(userId, activityId, headId, skuId, count);  
252 - return new CombinationValidateJoinRespDTO()  
253 - .setActivityId(keyValue.getKey().getId())  
254 - .setName(keyValue.getKey().getName())  
255 - .setCombinationPrice(keyValue.getValue().getCombinationPrice()); 246 + public CombinationValidateJoinRespDTO validateJoinCombination(Long userId, Long activityId, Long headId,
  247 + Long skuId, Integer count) {
  248 + KeyValue<CombinationActivityDO, CombinationProductDO> keyValue = validateCombinationRecord(userId, activityId,
  249 + headId, skuId, count);
  250 + return new CombinationValidateJoinRespDTO().setActivityId(keyValue.getKey().getId())
  251 + .setName(keyValue.getKey().getName()).setCombinationPrice(keyValue.getValue().getCombinationPrice());
256 } 252 }
257 253
258 @Override 254 @Override
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeCombinationHandler.java
@@ -30,11 +30,11 @@ public class TradeCombinationHandler implements TradeOrderHandler { @@ -30,11 +30,11 @@ public class TradeCombinationHandler implements TradeOrderHandler {
30 } 30 }
31 Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品"); 31 Assert.isTrue(orderItems.size() == 1, "拼团时,只允许选择一个商品");
32 32
33 - // 获取商品信息  
34 - TradeOrderItemDO item = orderItems.get(0);  
35 // 校验是否满足拼团活动相关限制 33 // 校验是否满足拼团活动相关限制
  34 + TradeOrderItemDO item = orderItems.get(0);
36 combinationRecordApi.validateCombinationRecord(order.getUserId(), order.getCombinationActivityId(), 35 combinationRecordApi.validateCombinationRecord(order.getUserId(), order.getCombinationActivityId(),
37 order.getCombinationHeadId(), item.getSkuId(), item.getCount()); 36 order.getCombinationHeadId(), item.getSkuId(), item.getCount());
  37 + // TODO @puhui999:这里还要限制下,是不是已经 createOrder;就是还没支付的时候,重复下单了;需要校验下;不然的话,一个拼团可以下多个单子了;
38 } 38 }
39 39
40 @Override 40 @Override