|
|
@@ -22,6 +22,7 @@ import com.txz.cif.param.JoinParam;
|
|
|
import com.txz.cif.param.OpenParam;
|
|
|
import com.txz.mall.constants.Constants;
|
|
|
import com.txz.mall.core.*;
|
|
|
+import com.txz.mall.core.cache.RedisKey;
|
|
|
import com.txz.mall.dao.StoreOrderMapper;
|
|
|
import com.txz.mall.dao.StorePinkMapper;
|
|
|
import com.txz.mall.dao.StorePinkSummaryMapper;
|
|
|
@@ -58,6 +59,7 @@ import java.io.IOException;
|
|
|
import java.math.BigDecimal;
|
|
|
import java.net.URLEncoder;
|
|
|
import java.text.SimpleDateFormat;
|
|
|
+import java.time.LocalDateTime;
|
|
|
import java.util.*;
|
|
|
import java.util.concurrent.CompletableFuture;
|
|
|
import java.util.stream.Collectors;
|
|
|
@@ -88,6 +90,7 @@ public class StoreOrderServiceImpl extends AbstractService<StoreOrder> implement
|
|
|
private final OrderIndexService orderIndexService;
|
|
|
@Resource
|
|
|
private StoreOrderMapper storeOrderMapper;
|
|
|
+ private final DistributedTxRecordService distributedTxRecordService;
|
|
|
|
|
|
@Override
|
|
|
public StoreOrderCountItemVO getOrderStatusNum() {
|
|
|
@@ -1712,6 +1715,7 @@ public class StoreOrderServiceImpl extends AbstractService<StoreOrder> implement
|
|
|
orderIndex.setImage(detailVo.getImage());
|
|
|
orderIndex.setSku(detailVo.getSku());
|
|
|
orderIndex.setStatus(storeOrder.getStatus());
|
|
|
+ orderIndex.setPayPrice(storeOrder.getPayPrice());
|
|
|
orderIndex.setStorePinkStatus(StorePinkStatusEnum.NOT_DRAWN.getKey());
|
|
|
orderIndex.setDeliveryId(storeOrder.getDeliveryId());
|
|
|
orderIndex.setDeliveryName(storeOrder.getDeliveryName());
|
|
|
@@ -1794,44 +1798,49 @@ public class StoreOrderServiceImpl extends AbstractService<StoreOrder> implement
|
|
|
storeOrderInfoService.saveOrderInfos(storeOrderInfos);
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- @Transactional(propagation = Propagation.REQUIRED)
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
public com.txz.mall.core.Result goPay(GoPinkDTO dto) {
|
|
|
log.info("this is pay start");
|
|
|
if (ObjectUtils.isEmpty(dto.getType())) {
|
|
|
throw new ServiceException(I18nUtil.get("participation.type.is.empty"));
|
|
|
}
|
|
|
- StoreOrder storeOrder = findBy("orderId", dto.getOrderId());
|
|
|
- if (ObjectUtils.isEmpty(storeOrder)) {
|
|
|
+ boolean restLock = RedissonLockUtil.tryLock(RedisKey.pay_lock+dto.getOrderId(),20,60);
|
|
|
+ try {
|
|
|
+ if(!restLock){
|
|
|
+ throw new ServiceException(I18nUtil.get("order.is.empty.pay.lock"));
|
|
|
+ }
|
|
|
+ StoreOrder storeOrder = findBy("orderId", dto.getOrderId());
|
|
|
+ if (ObjectUtils.isEmpty(storeOrder)) {
|
|
|
throw new ServiceException(I18nUtil.get("order.is.empty.orderid") + dto.getOrderId());
|
|
|
- }
|
|
|
- if (storeOrder.getStatus().equals(PinkOrderStatusEnum.GROUP_ORDER_PAID.getKey())) {
|
|
|
+ }
|
|
|
+ if (storeOrder.getStatus().equals(PinkOrderStatusEnum.GROUP_ORDER_PAID.getKey())) {
|
|
|
throw new ServiceException(I18nUtil.get("order.has.been.paid.orderid") + dto.getOrderId());
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- PinkOrderStatusEnum pinkOrderStatusEnum = OrderStateMachine.handleEvent(storeOrder, OrderEventsEnum.GROUP_PURCHASE_PAY, null);
|
|
|
+ PinkOrderStatusEnum pinkOrderStatusEnum = OrderStateMachine.handleEvent(storeOrder, OrderEventsEnum.GROUP_PURCHASE_PAY, null);
|
|
|
|
|
|
- if (ObjectUtils.isEmpty(pinkOrderStatusEnum)) {
|
|
|
+ if (ObjectUtils.isEmpty(pinkOrderStatusEnum)) {
|
|
|
throw new ServiceException(I18nUtil.get("current.status.does.not.support.payment.orderid") + dto.getOrderId());
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- log.info("accountDubboServiceClient.getWalletAccount");
|
|
|
- // 检验余额够不够
|
|
|
- Result result = accountDubboServiceClient.getWalletAccount(storeOrder.getUid());
|
|
|
- log.info("accountDubboServiceClient.getWalletAccount result" + JSONObject.toJSONString(result));
|
|
|
- if (result.getCode().equals(ResultCode.SUCCESS.getCode())) {
|
|
|
+ log.info("accountDubboServiceClient.getWalletAccount");
|
|
|
+ // 检验余额够不够
|
|
|
+ Result result = accountDubboServiceClient.getWalletAccount(storeOrder.getUid());
|
|
|
+ log.info("accountDubboServiceClient.getWalletAccount result" + JSONObject.toJSONString(result));
|
|
|
+ if (result.getCode().equals(ResultCode.SUCCESS.getCode())) {
|
|
|
AccountDTO accountDTO = JSONObject.parseObject(JSONObject.toJSONString(result.getData()), AccountDTO.class);
|
|
|
if (accountDTO.getPayPrice().compareTo(storeOrder.getPayPrice()) < 0) {
|
|
|
- // throw new ServiceException(ResultCode.INSUFFICIENT_BALANCE);
|
|
|
- log.info("ResultGenerator.genFailResult" + JSONObject.toJSONString(result));
|
|
|
- throw new ServiceException(I18nUtil.get("insufficient.balance"));
|
|
|
+ // throw new ServiceException(ResultCode.INSUFFICIENT_BALANCE);
|
|
|
+ log.info("ResultGenerator.genFailResult" + JSONObject.toJSONString(result));
|
|
|
+ throw new ServiceException(I18nUtil.get("insufficient.balance"));
|
|
|
}
|
|
|
- } else {
|
|
|
+ } else {
|
|
|
throw new ServiceException(I18nUtil.get("failed.to.obtain.user.wallet.account"));
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
-
|
|
|
- if ("open".equals(dto.getType())) {
|
|
|
+ // todo 事物没有一致性,先扣款或者后扣款,其中哪个服务出现问题,数据不会一致性
|
|
|
+ if ("open".equals(dto.getType())) {
|
|
|
OpenParam openParam = new OpenParam();
|
|
|
openParam.setBizId(storeOrder.getId().toString());
|
|
|
openParam.setBizNo(storeOrder.getOrderId());
|
|
|
@@ -1839,19 +1848,27 @@ public class StoreOrderServiceImpl extends AbstractService<StoreOrder> implement
|
|
|
openParam.setAmount(storeOrder.getPayPrice());
|
|
|
openParam.setTransTime(new Date());
|
|
|
log.info("accountDubboServiceClient.openGroup openParam" + openParam);
|
|
|
- //
|
|
|
+ // 1.操作账户余额
|
|
|
Result result1 = accountDubboServiceClient.openGroup(openParam);
|
|
|
log.info("accountDubboServiceClient.openGroup result1:" + result1);
|
|
|
if (!result1.getCode().equals("200")) {
|
|
|
- throw new ServiceException(I18nUtil.get("payment.failed"));
|
|
|
+ throw new ServiceException(I18nUtil.get("payment.failed"));
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ // 2. 执行拼团逻辑
|
|
|
+ updateOrderStatus(storeOrder.getOrderId(), pinkOrderStatusEnum.getKey());
|
|
|
+ log.info("pinkSuccess storeOrder1" + storeOrder);
|
|
|
+ storePinkService.pinkSuccess(storeOrder.getOrderId(), storeOrder);
|
|
|
+ log.info("pinkSuccess storeOrder2" + storeOrder);
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 3. 执行失败,标记为“待补偿”
|
|
|
+ saveTransactionRecord(storeOrder.getId(),storeOrder.getUid(),storeOrder.getOrderId(), "NEED_RETRY", pinkOrderStatusEnum.getKey());
|
|
|
+ log.error("pinkSuccess failed, will retry later", e);
|
|
|
+ throw new ServiceException(I18nUtil.get("payment.success"));
|
|
|
}
|
|
|
- updateOrderStatus(storeOrder.getOrderId(), pinkOrderStatusEnum.getKey());
|
|
|
- log.info("pinkSuccess storeOrder1" + storeOrder);
|
|
|
- storePinkService.pinkSuccess(storeOrder.getOrderId(), storeOrder);
|
|
|
- log.info("pinkSuccess storeOrder2" + storeOrder);
|
|
|
// goOpen(dto.getOrderId());
|
|
|
- }
|
|
|
- if ("join".equals(dto.getType())) {
|
|
|
+ }
|
|
|
+ if ("join".equals(dto.getType())) {
|
|
|
log.info("join1");
|
|
|
JoinParam joinParam = new JoinParam();
|
|
|
joinParam.setBizId(storeOrder.getId().toString());
|
|
|
@@ -1860,18 +1877,46 @@ public class StoreOrderServiceImpl extends AbstractService<StoreOrder> implement
|
|
|
joinParam.setAmount(storeOrder.getPayPrice());
|
|
|
joinParam.setTransTime(new Date());
|
|
|
log.info("accountDubboServiceClient.joinGroup1:" + joinParam);
|
|
|
+ // 1.操作账户余额
|
|
|
Result result1 = accountDubboServiceClient.joinGroup(joinParam);
|
|
|
log.info("accountDubboServiceClient.joinGroup2 result1:" + result1);
|
|
|
if (!result1.getCode().equals("200")) {
|
|
|
- throw new ServiceException(I18nUtil.get("payment.failed"));
|
|
|
+ throw new ServiceException(I18nUtil.get("payment.failed"));
|
|
|
}
|
|
|
- updateOrderStatus(storeOrder.getOrderId(), pinkOrderStatusEnum.getKey());
|
|
|
- log.info("storePinkService.pinkSuccess1 :" + storeOrder);
|
|
|
- storePinkService.pinkSuccess(storeOrder.getOrderId(), storeOrder);
|
|
|
- log.info("storePinkService.pinkSuccess2 :" + storeOrder);
|
|
|
- // goPink(dto);
|
|
|
+ try {
|
|
|
+ // 2. 执行拼团逻辑
|
|
|
+ updateOrderStatus(storeOrder.getOrderId(), pinkOrderStatusEnum.getKey());
|
|
|
+ log.info("storePinkService.pinkSuccess1 :" + storeOrder);
|
|
|
+ storePinkService.pinkSuccess(storeOrder.getOrderId(), storeOrder);
|
|
|
+ log.info("storePinkService.pinkSuccess2 :" + storeOrder);
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 3. 执行失败,标记为“待补偿”
|
|
|
+ saveTransactionRecord(storeOrder.getId(),storeOrder.getUid(),storeOrder.getOrderId(), "NEED_RETRY", pinkOrderStatusEnum.getKey());
|
|
|
+ throw new ServiceException(I18nUtil.get("payment.success"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return ResultGenerator.genSuccessResult();
|
|
|
+ } finally {
|
|
|
+ RedissonLockUtil.unlock(RedisKey.pay_lock+dto.getOrderId());
|
|
|
}
|
|
|
- return ResultGenerator.genSuccessResult();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 失败补偿记录
|
|
|
+ * @param orderId
|
|
|
+ * @param status
|
|
|
+ */
|
|
|
+ private void saveTransactionRecord(Long id,Long uid,String orderId,String status,Integer orderStatus){
|
|
|
+ DistributedTxRecord distributedTxRecord = new DistributedTxRecord();
|
|
|
+ distributedTxRecord.setOrderId(id);
|
|
|
+ distributedTxRecord.setOrderNo(orderId);
|
|
|
+ distributedTxRecord.setUid(uid);
|
|
|
+ distributedTxRecord.setStatus(status);
|
|
|
+ distributedTxRecord.setOrderStatus(orderStatus);
|
|
|
+ distributedTxRecord.setBizType("OPEN_GROUP");
|
|
|
+ distributedTxRecord.setCreateTime(LocalDateTime.now());
|
|
|
+ distributedTxRecord.setRetryCount(0);
|
|
|
+ distributedTxRecordService.save(distributedTxRecord);
|
|
|
}
|
|
|
|
|
|
@Resource
|
|
|
@@ -2681,5 +2726,49 @@ public class StoreOrderServiceImpl extends AbstractService<StoreOrder> implement
|
|
|
return findByCondition(condition);
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public void retryDistributedTxRecord() {
|
|
|
+ Condition condition = new Condition(DistributedTxRecord.class);
|
|
|
+ Example.Criteria criteria = condition.createCriteria();
|
|
|
+ criteria.andEqualTo("status", "NEED_RETRY")
|
|
|
+ .andLessThan("retry_count",5);
|
|
|
+ List<DistributedTxRecord> distributedTxRecords = distributedTxRecordService.findByCondition(condition);
|
|
|
+ if(CollUtil.isNotEmpty(distributedTxRecords)){
|
|
|
+
|
|
|
+ for (DistributedTxRecord r : distributedTxRecords) {
|
|
|
+ DistributedTxRecord distributedTxRecord = new DistributedTxRecord();
|
|
|
+ distributedTxRecord.setId(r.getId());
|
|
|
+ try {
|
|
|
+ // simple backoff: 如果刚刚重试过则跳过
|
|
|
+ if (r.getLastRetryTime() != null && r.getLastRetryTime().isAfter(LocalDateTime.now().minusSeconds(30))) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // 2. 执行拼团逻辑
|
|
|
+ updateOrderStatus(r.getOrderNo(),r.getOrderStatus());
|
|
|
+ StoreOrder storeOrder = new StoreOrder();
|
|
|
+ storeOrder.setId(r.getOrderId());
|
|
|
+ storeOrder.setOrderId(r.getOrderNo());
|
|
|
+ storeOrder.setUid(r.getUid());
|
|
|
+ storePinkService.pinkSuccess(storeOrder.getOrderId(), storeOrder);
|
|
|
+ log.info("pinkSuccess storeOrder2" + storeOrder);
|
|
|
+ // 成功则更新 tx record
|
|
|
+ distributedTxRecord.setStatus("FINISH");
|
|
|
+ distributedTxRecord.setLastRetryTime(LocalDateTime.now());
|
|
|
+ distributedTxRecordService.update(distributedTxRecord);
|
|
|
+ } catch (Exception ex) {
|
|
|
+ distributedTxRecord.setRetryCount(r.getRetryCount() + 1);
|
|
|
+ distributedTxRecord.setLastRetryTime(LocalDateTime.now());
|
|
|
+ // 超过最大重试次数,标记为 FAIL (或保留 NEED_RETRY 并告警)
|
|
|
+ if (r.getRetryCount() >= 5) {
|
|
|
+ distributedTxRecord.setStatus("FAILED");
|
|
|
+ } else {
|
|
|
+ distributedTxRecord.setStatus("NEED_RETRY");
|
|
|
+ }
|
|
|
+ distributedTxRecordService.update(distributedTxRecord);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
}
|