Commit bccd270fc77176a9138ae643c9309633264d3c83

Authored by puhui999
1 parent fdbebc40

营销活动:完成砍价、秒杀库存回滚。完成拼团过期和虚拟成团处理

yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
... ... @@ -64,6 +64,13 @@ public class CollectionUtils {
64 64 return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toList());
65 65 }
66 66  
  67 + public static <K, V> List<V> mergeValuesFromMap(Map<K, List<V>> map) {
  68 + return map.values()
  69 + .stream()
  70 + .flatMap(List::stream)
  71 + .collect(Collectors.toList());
  72 + }
  73 +
67 74 public static <T, U> Set<U> convertSet(Collection<T> from, Function<T, U> func) {
68 75 if (CollUtil.isEmpty(from)) {
69 76 return new HashSet<>();
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/combination/CombinationActivityConvert.java
... ... @@ -28,6 +28,7 @@ import org.mapstruct.Mapping;
28 28 import org.mapstruct.Mappings;
29 29 import org.mapstruct.factory.Mappers;
30 30  
  31 +import java.util.ArrayList;
31 32 import java.util.List;
32 33 import java.util.Map;
33 34  
... ... @@ -196,4 +197,35 @@ public interface CombinationActivityConvert {
196 197 return respVO;
197 198 }
198 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 +
199 231 }
... ...
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/combination/CombinationRecordMapper.java
... ... @@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationR
11 11 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
12 12 import org.apache.ibatis.annotations.Mapper;
13 13  
  14 +import java.time.LocalDateTime;
14 15 import java.util.Collection;
15 16 import java.util.Collections;
16 17 import java.util.List;
... ... @@ -117,4 +118,15 @@ public interface CombinationRecordMapper extends BaseMapperX&lt;CombinationRecordDO
117 118 .eq(headId != null, "head_id", headId));
118 119 }
119 120  
  121 + default List<CombinationRecordDO> selectListByHeadIdAndStatusAndExpireTimeLt(Long headId, Integer status, LocalDateTime dateTime) {
  122 + return selectList(new LambdaQueryWrapperX<CombinationRecordDO>()
  123 + .eq(CombinationRecordDO::getHeadId, headId)
  124 + .eq(CombinationRecordDO::getStatus, status)
  125 + .lt(CombinationRecordDO::getExpireTime, dateTime));
  126 + }
  127 +
  128 + default List<CombinationRecordDO> selectListByHeadIds(Collection<Long> headIds) {
  129 + return selectList(new LambdaQueryWrapperX<CombinationRecordDO>().in(CombinationRecordDO::getHeadId, headIds));
  130 + }
  131 +
120 132 }
... ...
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) throws Exception {
  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/combination/CombinationRecordService.java
... ... @@ -168,5 +168,11 @@ public interface CombinationRecordService {
168 168 */
169 169 void cancelCombinationRecord(Long userId, Long id, Long headId);
170 170  
  171 + /**
  172 + * 处理过期拼团
  173 + *
  174 + * @return key 过期拼团数量, value 虚拟成团数量
  175 + */
  176 + KeyValue<Integer, Integer> expireCombinationRecord();
171 177  
172 178 }
... ...
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 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.util.ObjUtil;
  5 +import cn.hutool.extra.spring.SpringUtil;
5 6 import cn.iocoder.yudao.framework.common.core.KeyValue;
6 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
... ... @@ -20,7 +21,9 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationP
20 21 import cn.iocoder.yudao.module.promotion.dal.dataobject.combination.CombinationRecordDO;
21 22 import cn.iocoder.yudao.module.promotion.dal.mysql.combination.CombinationRecordMapper;
22 23 import cn.iocoder.yudao.module.promotion.enums.combination.CombinationRecordStatusEnum;
  24 +import cn.iocoder.yudao.module.trade.api.order.TradeOrderApi;
23 25 import org.springframework.context.annotation.Lazy;
  26 +import org.springframework.scheduling.annotation.Async;
24 27 import org.springframework.stereotype.Service;
25 28 import org.springframework.transaction.annotation.Transactional;
26 29 import org.springframework.validation.annotation.Validated;
... ... @@ -31,8 +34,7 @@ import java.time.LocalDateTime;
31 34 import java.util.*;
32 35  
33 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 38 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.afterNow;
37 39 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.beforeNow;
38 40 import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
... ... @@ -52,7 +54,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
52 54 @Lazy
53 55 private CombinationActivityService combinationActivityService;
54 56 @Resource
55   - private CombinationRecordMapper recordMapper;
  57 + private CombinationRecordMapper combinationRecordMapper;
56 58  
57 59 @Resource
58 60 private MemberUserApi memberUserApi;
... ... @@ -63,6 +65,9 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
63 65 @Lazy
64 66 private ProductSkuApi productSkuApi;
65 67  
  68 + @Resource
  69 + private TradeOrderApi tradeOrderApi;
  70 +
66 71 // TODO @芋艿:在详细预览下;
67 72 @Override
68 73 @Transactional(rollbackFor = Exception.class)
... ... @@ -72,12 +77,12 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
72 77  
73 78 // 更新状态
74 79 record.setStatus(status);
75   - recordMapper.updateById(record);
  80 + combinationRecordMapper.updateById(record);
76 81 }
77 82  
78 83 private CombinationRecordDO validateCombinationRecord(Long userId, Long orderId) {
79 84 // 校验拼团是否存在
80   - CombinationRecordDO recordDO = recordMapper.selectByUserIdAndOrderId(userId, orderId);
  85 + CombinationRecordDO recordDO = combinationRecordMapper.selectByUserIdAndOrderId(userId, orderId);
81 86 if (recordDO == null) {
82 87 throw exception(COMBINATION_RECORD_NOT_EXISTS);
83 88 }
... ... @@ -106,7 +111,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
106 111 // 2. 父拼团是否存在,是否已经满了
107 112 if (headId != null) {
108 113 // 2.1. 查询进行中的父拼团
109   - CombinationRecordDO record = recordMapper.selectByHeadId(headId, CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
  114 + CombinationRecordDO record = combinationRecordMapper.selectByHeadId(headId, CombinationRecordStatusEnum.IN_PROGRESS.getStatus());
110 115 if (record == null) {
111 116 throw exception(COMBINATION_RECORD_HEAD_NOT_EXISTS);
112 117 }
... ... @@ -141,7 +146,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
141 146 }
142 147  
143 148 // 6.1 校验是否有拼团记录
144   - List<CombinationRecordDO> recordList = recordMapper.selectListByUserIdAndActivityId(userId, activityId);
  149 + List<CombinationRecordDO> recordList = combinationRecordMapper.selectListByUserIdAndActivityId(userId, activityId);
145 150 recordList.removeIf(record -> CombinationRecordStatusEnum.isFailed(record.getStatus())); // 取消的订单,不算数
146 151 if (CollUtil.isEmpty(recordList)) { // 如果为空,说明可以参与,直接返回
147 152 return new KeyValue<>(activity, product);
... ... @@ -179,11 +184,11 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
179 184 .setHeadId(CombinationRecordDO.HEAD_ID_GROUP);
180 185 } else {
181 186 // 2.2.有团长的情况下需要设置开始时间和过期时间为团长的
182   - CombinationRecordDO headRecord = recordMapper.selectByHeadId(record.getHeadId(),
  187 + CombinationRecordDO headRecord = combinationRecordMapper.selectByHeadId(record.getHeadId(),
183 188 CombinationRecordStatusEnum.IN_PROGRESS.getStatus()); // 查询进行中的父拼团
184 189 record.setStartTime(headRecord.getStartTime()).setExpireTime(headRecord.getExpireTime());
185 190 }
186   - recordMapper.insert(record);
  191 + combinationRecordMapper.insert(record);
187 192  
188 193 if (ObjUtil.equal(CombinationRecordDO.HEAD_ID_GROUP, record.getHeadId())) {
189 194 return new KeyValue<>(record.getId(), record.getHeadId());
... ... @@ -206,31 +211,33 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
206 211 if (CollUtil.isEmpty(records)) {
207 212 return;
208 213 }
209   - CombinationRecordDO headRecord = recordMapper.selectById(headId);
  214 + CombinationRecordDO headRecord = combinationRecordMapper.selectById(headId);
210 215  
211 216 // 2. 批量更新记录
212 217 List<CombinationRecordDO> updateRecords = new ArrayList<>();
213 218 records.add(headRecord); // 加入团长,团长也需要更新
214 219 boolean isFull = records.size() >= activity.getUserSize();
  220 + LocalDateTime now = LocalDateTime.now();
215 221 records.forEach(item -> {
216 222 CombinationRecordDO updateRecord = new CombinationRecordDO();
217 223 updateRecord.setId(item.getId()).setUserCount(records.size());
218 224 if (isFull) {
219 225 updateRecord.setStatus(CombinationRecordStatusEnum.SUCCESS.getStatus());
  226 + updateRecord.setEndTime(now);
220 227 }
221 228 updateRecords.add(updateRecord);
222 229 });
223   - recordMapper.updateBatch(updateRecords);
  230 + combinationRecordMapper.updateBatch(updateRecords);
224 231 }
225 232  
226 233 @Override
227 234 public CombinationRecordDO getCombinationRecord(Long userId, Long orderId) {
228   - return recordMapper.selectByUserIdAndOrderId(userId, orderId);
  235 + return combinationRecordMapper.selectByUserIdAndOrderId(userId, orderId);
229 236 }
230 237  
231 238 @Override
232 239 public List<CombinationRecordDO> getCombinationRecordListByUserIdAndActivityId(Long userId, Long activityId) {
233   - return recordMapper.selectListByUserIdAndActivityId(userId, activityId);
  240 + return combinationRecordMapper.selectListByUserIdAndActivityId(userId, activityId);
234 241 }
235 242  
236 243 @Override
... ... @@ -244,51 +251,51 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
244 251  
245 252 @Override
246 253 public Long getCombinationRecordCount(@Nullable Integer status, @Nullable Boolean virtualGroup, @Nullable Long headId) {
247   - return recordMapper.selectCountByHeadAndStatusAndVirtualGroup(status, virtualGroup, headId);
  254 + return combinationRecordMapper.selectCountByHeadAndStatusAndVirtualGroup(status, virtualGroup, headId);
248 255 }
249 256  
250 257 @Override
251 258 public List<CombinationRecordDO> getLatestCombinationRecordList(int count) {
252   - return recordMapper.selectLatestList(count);
  259 + return combinationRecordMapper.selectLatestList(count);
253 260 }
254 261  
255 262 @Override
256 263 public List<CombinationRecordDO> getHeadCombinationRecordList(Long activityId, Integer status, Integer count) {
257   - return recordMapper.selectListByActivityIdAndStatusAndHeadId(activityId, status,
  264 + return combinationRecordMapper.selectListByActivityIdAndStatusAndHeadId(activityId, status,
258 265 CombinationRecordDO.HEAD_ID_GROUP, count);
259 266 }
260 267  
261 268 @Override
262 269 public CombinationRecordDO getCombinationRecordById(Long id) {
263   - return recordMapper.selectById(id);
  270 + return combinationRecordMapper.selectById(id);
264 271 }
265 272  
266 273 @Override
267 274 public List<CombinationRecordDO> getCombinationRecordListByHeadId(Long headId) {
268   - return recordMapper.selectList(CombinationRecordDO::getHeadId, headId);
  275 + return combinationRecordMapper.selectList(CombinationRecordDO::getHeadId, headId);
269 276 }
270 277  
271 278 @Override
272 279 public PageResult<CombinationRecordDO> getCombinationRecordPage(CombinationRecordReqPageVO pageVO) {
273   - return recordMapper.selectPage(pageVO);
  280 + return combinationRecordMapper.selectPage(pageVO);
274 281 }
275 282  
276 283 @Override
277 284 public Map<Long, Integer> getCombinationRecordCountMapByActivity(Collection<Long> activityIds,
278 285 @Nullable Integer status, @Nullable Long headId) {
279   - return recordMapper.selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId(activityIds, status, headId);
  286 + return combinationRecordMapper.selectCombinationRecordCountMapByActivityIdAndStatusAndHeadId(activityIds, status, headId);
280 287 }
281 288  
282 289 @Override
283 290 public CombinationRecordDO getCombinationRecordByIdAndUser(Long userId, Long id) {
284   - return recordMapper.selectOne(CombinationRecordDO::getUserId, userId, CombinationRecordDO::getId, id);
  291 + return combinationRecordMapper.selectOne(CombinationRecordDO::getUserId, userId, CombinationRecordDO::getId, id);
285 292 }
286 293  
287 294 @Override
288 295 @Transactional(rollbackFor = Exception.class)
289 296 public void cancelCombinationRecord(Long userId, Long id, Long headId) {
290 297 // 删除记录
291   - recordMapper.deleteById(id);
  298 + combinationRecordMapper.deleteById(id);
292 299  
293 300 // 需要更新的记录
294 301 List<CombinationRecordDO> updateRecords = new ArrayList<>();
... ... @@ -315,7 +322,7 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
315 322 });
316 323 } else { // 情况二:团员
317 324 // 团长
318   - CombinationRecordDO recordHead = recordMapper.selectById(headId);
  325 + CombinationRecordDO recordHead = combinationRecordMapper.selectById(headId);
319 326 // 团员
320 327 List<CombinationRecordDO> records = getCombinationRecordListByHeadId(headId);
321 328 if (CollUtil.isEmpty(records)) {
... ... @@ -331,7 +338,112 @@ public class CombinationRecordServiceImpl implements CombinationRecordService {
331 338 }
332 339  
333 340 // 更新拼团记录
334   - recordMapper.updateBatch(updateRecords);
  341 + combinationRecordMapper.updateBatch(updateRecords);
  342 + }
  343 +
  344 + @Override
  345 + public KeyValue<Integer, Integer> expireCombinationRecord() {
  346 + // 1。获取所有正在进行中的过期的父拼团
  347 + List<CombinationRecordDO> headExpireRecords = combinationRecordMapper.selectListByHeadIdAndStatusAndExpireTimeLt(
  348 + CombinationRecordDO.HEAD_ID_GROUP, CombinationRecordStatusEnum.IN_PROGRESS.getStatus(), LocalDateTime.now());
  349 + if (CollUtil.isEmpty(headExpireRecords)) {
  350 + return new KeyValue<>(0, 0);
  351 + }
  352 +
  353 + // 2.获取拼团活动
  354 + List<CombinationActivityDO> combinationActivities = combinationActivityService.getCombinationActivityListByIds(
  355 + convertSet(headExpireRecords, CombinationRecordDO::getActivityId));
  356 + Map<Long, CombinationActivityDO> activityMap = convertMap(combinationActivities, CombinationActivityDO::getId);
  357 +
  358 + // 3.校验是否虚拟成团
  359 + List<CombinationRecordDO> virtualGroupHeadRecords = new ArrayList<>(); // 虚拟成团
  360 + for (Iterator<CombinationRecordDO> iterator = headExpireRecords.iterator(); iterator.hasNext(); ) {
  361 + CombinationRecordDO record = iterator.next();
  362 + // 3.1 不匹配,则直接跳过
  363 + CombinationActivityDO activityDO = activityMap.get(record.getActivityId());
  364 + if (activityDO == null || !activityDO.getVirtualGroup()) { // 取不到活动的或者不是虚拟拼团的
  365 + continue;
  366 + }
  367 + // 3.2 匹配,则移除,添加到虚拟成团中,并结束寻找
  368 + virtualGroupHeadRecords.add(record);
  369 + iterator.remove();
  370 + break;
  371 + }
  372 +
  373 + // 4.处理过期的拼团
  374 + getSelf().handleExpireRecord(headExpireRecords);
  375 + // 5.虚拟成团
  376 + getSelf().handleVirtualGroupRecord(virtualGroupHeadRecords);
  377 +
  378 + return new KeyValue<>(headExpireRecords.size(), virtualGroupHeadRecords.size());
  379 + }
  380 +
  381 + @Async
  382 + protected void handleExpireRecord(List<CombinationRecordDO> headExpireRecords) {
  383 + if (CollUtil.isEmpty(headExpireRecords)) {
  384 + return;
  385 + }
  386 +
  387 + // 1.更新拼团记录
  388 + List<CombinationRecordDO> headsAndRecords = updateBatchCombinationRecords(headExpireRecords,
  389 + CombinationRecordStatusEnum.FAILED);
  390 + if (headsAndRecords == null) {
  391 + return;
  392 + }
  393 +
  394 + // 2.订单取消 TODO 以现在的取消回滚逻辑好像只能循环了
  395 + headsAndRecords.forEach(item -> {
  396 + tradeOrderApi.cancelPaidOrder(item.getUserId(), item.getOrderId());
  397 + });
  398 +
  399 + }
  400 +
  401 + @Async
  402 + protected void handleVirtualGroupRecord(List<CombinationRecordDO> virtualGroupHeadRecords) {
  403 + if (CollUtil.isEmpty(virtualGroupHeadRecords)) {
  404 + return;
  405 + }
  406 +
  407 + // 1.团员补齐
  408 + combinationRecordMapper.insertBatch(CombinationActivityConvert.INSTANCE.convertVirtualGroupList(virtualGroupHeadRecords));
  409 + // 2.更新拼团记录
  410 + updateBatchCombinationRecords(virtualGroupHeadRecords, CombinationRecordStatusEnum.SUCCESS);
  411 + }
  412 +
  413 + private List<CombinationRecordDO> updateBatchCombinationRecords(List<CombinationRecordDO> headRecords, CombinationRecordStatusEnum status) {
  414 + // 1. 查询团成员
  415 + List<CombinationRecordDO> records = combinationRecordMapper.selectListByHeadIds(
  416 + convertSet(headRecords, CombinationRecordDO::getId));
  417 + if (CollUtil.isEmpty(records)) {
  418 + return null;
  419 + }
  420 + Map<Long, List<CombinationRecordDO>> recordsMap = convertMultiMap(records, CombinationRecordDO::getHeadId);
  421 + headRecords.forEach(item -> {
  422 + recordsMap.get(item.getId()).add(item); // 把团长加进团里
  423 + });
  424 + // 2.批量更新拼团记录 status 和 失败/成团时间
  425 + List<CombinationRecordDO> headsAndRecords = mergeValuesFromMap(recordsMap);
  426 + List<CombinationRecordDO> updateRecords = new ArrayList<>(headsAndRecords.size());
  427 + LocalDateTime now = LocalDateTime.now();
  428 + headsAndRecords.forEach(item -> {
  429 + CombinationRecordDO record = new CombinationRecordDO().setId(item.getId())
  430 + .setStatus(status.getStatus()).setEndTime(now);
  431 + if (CombinationRecordStatusEnum.isSuccess(status.getStatus())) { // 虚拟成团完事更改状态成功后还需要把参与人数修改为成团需要人数
  432 + record.setUserCount(record.getUserSize());
  433 + }
  434 + updateRecords.add(record);
  435 + });
  436 + combinationRecordMapper.updateBatch(updateRecords);
  437 + return headsAndRecords;
  438 + }
  439 +
  440 + /**
  441 + * 获得自身的代理对象,解决 AOP 生效问题
  442 + *
  443 + * @return 自己
  444 + */
  445 + private CombinationRecordServiceImpl getSelf() {
  446 + return SpringUtil.getBean(getClass());
335 447 }
336 448  
337 449 }
... ...