|
|
@@ -15,15 +15,15 @@ import com.github.binarywang.wxpay.service.WxPayService;
|
|
|
import com.huifu.adapay.core.exception.BaseAdaPayException;
|
|
|
import com.huifu.adapay.model.AdapayCommon;
|
|
|
import com.huifu.adapay.model.Payment;
|
|
|
-import com.izouma.nineth.config.AdapayProperties;
|
|
|
-import com.izouma.nineth.config.AlipayProperties;
|
|
|
-import com.izouma.nineth.config.GeneralProperties;
|
|
|
-import com.izouma.nineth.config.WxPayProperties;
|
|
|
+import com.huifu.adapay.model.Refund;
|
|
|
+import com.izouma.nineth.config.*;
|
|
|
import com.izouma.nineth.domain.Collection;
|
|
|
import com.izouma.nineth.domain.*;
|
|
|
import com.izouma.nineth.dto.PageQuery;
|
|
|
import com.izouma.nineth.enums.*;
|
|
|
import com.izouma.nineth.event.CreateAssetEvent;
|
|
|
+import com.izouma.nineth.event.CreateOrderEvent;
|
|
|
+import com.izouma.nineth.event.OrderNotifyEvent;
|
|
|
import com.izouma.nineth.event.TransferAssetEvent;
|
|
|
import com.izouma.nineth.exception.BusinessException;
|
|
|
import com.izouma.nineth.repo.*;
|
|
|
@@ -34,12 +34,16 @@ import lombok.AllArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.codec.EncoderException;
|
|
|
import org.apache.commons.codec.net.URLCodec;
|
|
|
-import org.apache.commons.collections.CollectionUtils;
|
|
|
import org.apache.commons.collections.MapUtils;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.apache.rocketmq.client.producer.SendResult;
|
|
|
+import org.apache.rocketmq.spring.core.RocketMQTemplate;
|
|
|
+import org.springframework.cache.annotation.Cacheable;
|
|
|
import org.springframework.context.event.EventListener;
|
|
|
import org.springframework.core.env.Environment;
|
|
|
import org.springframework.data.domain.Page;
|
|
|
+import org.springframework.data.redis.core.BoundSetOperations;
|
|
|
+import org.springframework.data.redis.core.BoundValueOperations;
|
|
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
|
import org.springframework.scheduling.annotation.Scheduled;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
@@ -51,6 +55,7 @@ import java.math.RoundingMode;
|
|
|
import java.time.LocalDateTime;
|
|
|
import java.time.format.DateTimeFormatter;
|
|
|
import java.util.*;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
@Service
|
|
|
@AllArgsConstructor
|
|
|
@@ -76,108 +81,133 @@ public class OrderService {
|
|
|
private CommissionRecordRepo commissionRecordRepo;
|
|
|
private AdapayProperties adapayProperties;
|
|
|
private GeneralProperties generalProperties;
|
|
|
+ private SnowflakeIdWorker snowflakeIdWorker;
|
|
|
+ private RocketMQTemplate rocketMQTemplate;
|
|
|
+ private ErrorOrderRepo errorOrderRepo;
|
|
|
|
|
|
public Page<Order> all(PageQuery pageQuery) {
|
|
|
return orderRepo.findAll(JpaUtils.toSpecification(pageQuery, Order.class), JpaUtils.toPageRequest(pageQuery));
|
|
|
}
|
|
|
|
|
|
+ public String mqCreate(Long userId, Long collectionId, int qty, Long addressId, Long userCouponId, Long invitor) {
|
|
|
+
|
|
|
+ Long id = snowflakeIdWorker.nextId();
|
|
|
+ SendResult result = rocketMQTemplate.syncSend(generalProperties.getCreateOrderTopic(),
|
|
|
+ new CreateOrderEvent(id, userId, collectionId, qty, addressId, userCouponId, invitor), 100000);
|
|
|
+ log.info("发送订单到队列: {}, result={}", id, result);
|
|
|
+ return String.valueOf(id);
|
|
|
+ }
|
|
|
+
|
|
|
@Transactional
|
|
|
- public Order create(Long userId, Long collectionId, int qty, Long addressId, Long userCouponId, Long invitor) {
|
|
|
- if (qty <= 0) throw new BusinessException("数量必须大于0");
|
|
|
- User user = userRepo.findByIdAndDelFalse(userId).orElseThrow(new BusinessException("用户不存在"));
|
|
|
- Collection collection = collectionRepo.findById(collectionId).orElseThrow(new BusinessException("藏品不存在"));
|
|
|
- User minter = userRepo.findById(collection.getMinterId()).orElseThrow(new BusinessException("铸造者不存在"));
|
|
|
- UserCoupon coupon = null;
|
|
|
- if (userCouponId != null) {
|
|
|
- coupon = userCouponRepo.findById(userCouponId).orElseThrow(new BusinessException("兑换券不存在"));
|
|
|
- if (coupon.isUsed()) {
|
|
|
- throw new BusinessException("该兑换券已使用");
|
|
|
+ public Order create(Long userId, Long collectionId, int qty, Long addressId, Long userCouponId, Long invitor, Long id) {
|
|
|
+ qty = 1;
|
|
|
+ long t = System.currentTimeMillis();
|
|
|
+ int stock = Optional.ofNullable(collectionService.decreaseStock(collectionId, qty))
|
|
|
+ .map(Math::toIntExact)
|
|
|
+ .orElseThrow(new BusinessException("很遗憾,藏品已售罄"));
|
|
|
+
|
|
|
+ try {
|
|
|
+ if (stock < 0) {
|
|
|
+ throw new BusinessException("很遗憾,藏品已售罄");
|
|
|
}
|
|
|
- if (coupon.isLimited() && !coupon.getCollectionIds().contains(collectionId)) {
|
|
|
- throw new BusinessException("该兑换券不可用");
|
|
|
+
|
|
|
+ User user = userRepo.findByIdAndDelFalse(userId).orElseThrow(new BusinessException("用户不存在"));
|
|
|
+ Collection collection = collectionRepo.findById(collectionId).orElseThrow(new BusinessException("藏品不存在"));
|
|
|
+ User minter = userRepo.findById(collection.getMinterId()).orElseThrow(new BusinessException("铸造者不存在"));
|
|
|
+ UserCoupon coupon = null;
|
|
|
+ if (userCouponId != null) {
|
|
|
+ coupon = userCouponRepo.findById(userCouponId).orElseThrow(new BusinessException("兑换券不存在"));
|
|
|
+ if (coupon.isUsed()) {
|
|
|
+ throw new BusinessException("该兑换券已使用");
|
|
|
+ }
|
|
|
+ if (coupon.isLimited() && !coupon.getCollectionIds().contains(collectionId)) {
|
|
|
+ throw new BusinessException("该兑换券不可用");
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- if (collection.isScheduleSale()) {
|
|
|
- if (collection.getStartTime().isAfter(LocalDateTime.now())) {
|
|
|
- throw new BusinessException("当前还未开售");
|
|
|
+ if (collection.isScheduleSale()) {
|
|
|
+ if (collection.getStartTime().isAfter(LocalDateTime.now())) {
|
|
|
+ throw new BusinessException("当前还未开售");
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- if (!collection.isOnShelf()) {
|
|
|
- throw new BusinessException("藏品已下架");
|
|
|
- }
|
|
|
- if (qty > collection.getStock()) {
|
|
|
- throw new BusinessException("库存不足");
|
|
|
- }
|
|
|
- if (!collection.isSalable()) {
|
|
|
- throw new BusinessException("该藏品当前不可购买");
|
|
|
- }
|
|
|
- if (collection.getAssetId() != null) {
|
|
|
- Asset asset = assetRepo.findById(collection.getAssetId()).orElseThrow(new BusinessException("藏品不存在"));
|
|
|
- if (asset.getStatus() != AssetStatus.NORMAL) {
|
|
|
+ if (!collection.isOnShelf()) {
|
|
|
throw new BusinessException("藏品已下架");
|
|
|
}
|
|
|
- }
|
|
|
- UserAddress userAddress = null;
|
|
|
- if (addressId != null) {
|
|
|
- userAddress = userAddressRepo.findById(addressId).orElseThrow(new BusinessException("地址信息不存在"));
|
|
|
- }
|
|
|
+ if (qty > collection.getStock()) {
|
|
|
+ throw new BusinessException("库存不足");
|
|
|
+ }
|
|
|
+ if (!collection.isSalable()) {
|
|
|
+ throw new BusinessException("该藏品当前不可购买");
|
|
|
+ }
|
|
|
+ if (collection.getAssetId() != null) {
|
|
|
+ Asset asset = assetRepo.findById(collection.getAssetId()).orElseThrow(new BusinessException("藏品不存在"));
|
|
|
+ if (asset.getStatus() != AssetStatus.NORMAL) {
|
|
|
+ throw new BusinessException("藏品已下架");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ UserAddress userAddress = null;
|
|
|
+ if (addressId != null) {
|
|
|
+ userAddress = userAddressRepo.findById(addressId).orElseThrow(new BusinessException("地址信息不存在"));
|
|
|
+ }
|
|
|
|
|
|
- collectionRepo.increaseStock(collectionId, -qty);
|
|
|
- collectionRepo.increaseSale(collectionId, qty);
|
|
|
-
|
|
|
- BigDecimal gasFee = sysConfigService.getBigDecimal("gas_fee");
|
|
|
- Order order = Order.builder()
|
|
|
- .userId(userId)
|
|
|
- .collectionId(collectionId)
|
|
|
- .name(collection.getName())
|
|
|
- .pic(collection.getPic())
|
|
|
- .detail(collection.getDetail())
|
|
|
- .properties(collection.getProperties())
|
|
|
- .category(collection.getCategory())
|
|
|
- .canResale(collection.isCanResale())
|
|
|
- .royalties(collection.getRoyalties())
|
|
|
- .serviceCharge(collection.getServiceCharge())
|
|
|
- .type(collection.getType())
|
|
|
- .source(collection.getSource())
|
|
|
- .minterId(collection.getMinterId())
|
|
|
- .minter(minter.getNickname())
|
|
|
- .minterAvatar(minter.getAvatar())
|
|
|
- .qty(qty)
|
|
|
- .price(collection.getPrice())
|
|
|
- .gasPrice(gasFee)
|
|
|
- .totalPrice(collection.getPrice().multiply(BigDecimal.valueOf(qty)).add(gasFee))
|
|
|
- .contactName(Optional.ofNullable(userAddress).map(UserAddress::getName).orElse(null))
|
|
|
- .contactPhone(Optional.ofNullable(userAddress).map(UserAddress::getPhone).orElse(null))
|
|
|
- .address(Optional.ofNullable(userAddress).map(u ->
|
|
|
- u.getProvinceId() + " " + u.getCityId() + " " + u.getDistrictId() + " " + u.getAddress())
|
|
|
- .orElse(null))
|
|
|
- .status(OrderStatus.NOT_PAID)
|
|
|
- .assetId(collection.getAssetId())
|
|
|
- .couponId(userCouponId)
|
|
|
- .invitor(invitor)
|
|
|
- .projectId(collection.getProjectId())
|
|
|
- .build();
|
|
|
- if (coupon != null) {
|
|
|
- coupon.setUsed(true);
|
|
|
- coupon.setUseTime(LocalDateTime.now());
|
|
|
- if (coupon.isNeedGas()) {
|
|
|
- order.setTotalPrice(order.getGasPrice());
|
|
|
- } else {
|
|
|
- order.setTotalPrice(BigDecimal.ZERO);
|
|
|
+ BigDecimal gasFee = sysConfigService.getBigDecimal("gas_fee");
|
|
|
+ Order order = Order.builder()
|
|
|
+ .userId(userId)
|
|
|
+ .collectionId(collectionId)
|
|
|
+ .name(collection.getName())
|
|
|
+ .pic(collection.getPic())
|
|
|
+ .detail(collection.getDetail())
|
|
|
+ .properties(collection.getProperties())
|
|
|
+ .category(collection.getCategory())
|
|
|
+ .canResale(collection.isCanResale())
|
|
|
+ .royalties(collection.getRoyalties())
|
|
|
+ .serviceCharge(collection.getServiceCharge())
|
|
|
+ .type(collection.getType())
|
|
|
+ .source(collection.getSource())
|
|
|
+ .minterId(collection.getMinterId())
|
|
|
+ .minter(minter.getNickname())
|
|
|
+ .minterAvatar(minter.getAvatar())
|
|
|
+ .qty(qty)
|
|
|
+ .price(collection.getPrice())
|
|
|
+ .gasPrice(gasFee)
|
|
|
+ .totalPrice(collection.getPrice().multiply(BigDecimal.valueOf(qty)).add(gasFee))
|
|
|
+ .contactName(Optional.ofNullable(userAddress).map(UserAddress::getName).orElse(null))
|
|
|
+ .contactPhone(Optional.ofNullable(userAddress).map(UserAddress::getPhone).orElse(null))
|
|
|
+ .address(Optional.ofNullable(userAddress).map(u ->
|
|
|
+ u.getProvinceId() + " " + u.getCityId() + " " + u.getDistrictId() + " " + u.getAddress())
|
|
|
+ .orElse(null))
|
|
|
+ .status(OrderStatus.NOT_PAID)
|
|
|
+ .assetId(collection.getAssetId())
|
|
|
+ .couponId(userCouponId)
|
|
|
+ .invitor(invitor)
|
|
|
+ .projectId(collection.getProjectId())
|
|
|
+ .build();
|
|
|
+ if (coupon != null) {
|
|
|
+ coupon.setUsed(true);
|
|
|
+ coupon.setUseTime(LocalDateTime.now());
|
|
|
+ if (coupon.isNeedGas()) {
|
|
|
+ order.setTotalPrice(order.getGasPrice());
|
|
|
+ } else {
|
|
|
+ order.setTotalPrice(BigDecimal.ZERO);
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- if (collection.getSource() == CollectionSource.TRANSFER) {
|
|
|
- Asset asset = assetRepo.findById(collection.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
|
|
|
- asset.setStatus(AssetStatus.TRADING);
|
|
|
- assetRepo.save(asset);
|
|
|
- collectionRepo.setOnShelf(collection.getId(), false);
|
|
|
- }
|
|
|
- order = orderRepo.save(order);
|
|
|
- if (order.getTotalPrice().equals(BigDecimal.ZERO)) {
|
|
|
- notifyOrder(order.getId(), PayMethod.WEIXIN, null);
|
|
|
+ if (collection.getSource() == CollectionSource.TRANSFER) {
|
|
|
+ Asset asset = assetRepo.findById(collection.getAssetId()).orElseThrow(new BusinessException("资产不存在"));
|
|
|
+ asset.setStatus(AssetStatus.TRADING);
|
|
|
+ assetRepo.save(asset);
|
|
|
+ collectionRepo.setOnShelf(collection.getId(), false);
|
|
|
+ }
|
|
|
+ order = orderRepo.save(order);
|
|
|
+ if (order.getTotalPrice().equals(BigDecimal.ZERO)) {
|
|
|
+ notifyOrder(order.getId(), PayMethod.WEIXIN, null);
|
|
|
+ }
|
|
|
+ rocketMQTemplate.syncSend(generalProperties.getUpdateStockTopic(), collectionId, 10000);
|
|
|
+ log.info("订单创建完成, id={}, {}ms", order.getId(), System.currentTimeMillis() - t);
|
|
|
+ return order;
|
|
|
+ } catch (Exception e) {
|
|
|
+ collectionService.increaseStock(collectionId, qty);
|
|
|
+ throw e;
|
|
|
}
|
|
|
- return order;
|
|
|
}
|
|
|
|
|
|
public void payOrderAlipay(Long id, Model model) {
|
|
|
@@ -191,7 +221,7 @@ public class OrderService {
|
|
|
JSONObject bizContent = new JSONObject();
|
|
|
bizContent.put("notifyUrl", alipayProperties.getNotifyUrl());
|
|
|
bizContent.put("returnUrl", alipayProperties.getReturnUrl());
|
|
|
- bizContent.put("out_trade_no", String.valueOf(new SnowflakeIdWorker(0, 0).nextId()));
|
|
|
+ bizContent.put("out_trade_no", String.valueOf(snowflakeIdWorker.nextId()));
|
|
|
bizContent.put("total_amount", order.getTotalPrice().stripTrailingZeros().toPlainString());
|
|
|
bizContent.put("disable_pay_channels", "pcredit,creditCard");
|
|
|
if (Arrays.stream(env.getActiveProfiles()).noneMatch(s -> s.equals("prod"))) {
|
|
|
@@ -228,7 +258,7 @@ public class OrderService {
|
|
|
|
|
|
WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
|
|
|
request.setBody(order.getName());
|
|
|
- request.setOutTradeNo(String.valueOf(new SnowflakeIdWorker(1, 1).nextId()));
|
|
|
+ request.setOutTradeNo(String.valueOf(snowflakeIdWorker.nextId()));
|
|
|
request.setTotalFee(order.getTotalPrice().multiply(BigDecimal.valueOf(100)).intValue());
|
|
|
if (Arrays.stream(env.getActiveProfiles()).noneMatch(s -> s.equals("prod"))) {
|
|
|
// 测试环境设为1分
|
|
|
@@ -254,6 +284,7 @@ public class OrderService {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ @Cacheable(value = "adapay", key = "#id+'_'+#payChannel")
|
|
|
public Object payAdapay(Long id, String payChannel, String openId) throws BaseAdaPayException {
|
|
|
List<String> aliChannels = Arrays.asList("alipay", "alipay_qr", "alipay_wap");
|
|
|
List<String> wxChannels = Arrays.asList("wx_pub", "wx_lite");
|
|
|
@@ -272,7 +303,7 @@ public class OrderService {
|
|
|
}
|
|
|
|
|
|
Map<String, Object> paymentParams = new HashMap<>();
|
|
|
- paymentParams.put("order_no", String.valueOf(new SnowflakeIdWorker(0, 0).nextId()));
|
|
|
+ paymentParams.put("order_no", String.valueOf(snowflakeIdWorker.nextId()));
|
|
|
paymentParams.put("pay_amt", order.getTotalPrice().setScale(2, RoundingMode.HALF_UP).toPlainString());
|
|
|
paymentParams.put("app_id", adapayProperties.getAppId());
|
|
|
paymentParams.put("pay_channel", payChannel);
|
|
|
@@ -331,6 +362,11 @@ public class OrderService {
|
|
|
response = Payment.create(paymentParams);
|
|
|
log.info("createOrderResponse {}", JSON.toJSONString(response, SerializerFeature.PrettyFormat));
|
|
|
AdapayService.checkSuccess(response);
|
|
|
+
|
|
|
+ // 保存adapay的订单id,用于后续取消订单时的查询
|
|
|
+ BoundSetOperations<String, Object> ops = redisTemplate.boundSetOps(RedisKeys.PAY_RECORD + order.getId());
|
|
|
+ ops.add(MapUtils.getString(response, "id"));
|
|
|
+ ops.expire(7, TimeUnit.DAYS);
|
|
|
}
|
|
|
|
|
|
switch (payChannel) {
|
|
|
@@ -380,44 +416,92 @@ public class OrderService {
|
|
|
|
|
|
@Transactional
|
|
|
public void notifyOrder(Long orderId, PayMethod payMethod, String transactionId) {
|
|
|
- Order order = orderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
|
|
|
- Collection collection = collectionRepo.findById(order.getCollectionId())
|
|
|
- .orElseThrow(new BusinessException("藏品不存在"));
|
|
|
- User user = userRepo.findById(order.getUserId()).orElseThrow(new BusinessException("用户不存在"));
|
|
|
- if (order.getStatus() == OrderStatus.NOT_PAID) {
|
|
|
- order.setStatus(OrderStatus.PROCESSING);
|
|
|
- order.setPayTime(LocalDateTime.now());
|
|
|
- order.setTransactionId(transactionId);
|
|
|
- order.setPayMethod(payMethod);
|
|
|
- if (order.getType() == CollectionType.BLIND_BOX) {
|
|
|
- BlindBoxItem winItem = collectionService.draw(collection.getId());
|
|
|
- order.setWinCollectionId(winItem.getCollectionId());
|
|
|
- orderRepo.save(order);
|
|
|
- assetService.createAsset(winItem, user, order.getId(), order.getPrice(), "出售",
|
|
|
- collectionService.getNextNumber(winItem.getCollectionId()));
|
|
|
- addSales(winItem.getMinterId());
|
|
|
- } else {
|
|
|
- if (collection.getSource() == CollectionSource.TRANSFER) {
|
|
|
- Asset asset = assetRepo.findById(collection.getAssetId()).orElse(null);
|
|
|
- assetService.transfer(asset, order.getPrice(), user, "转让", order.getId());
|
|
|
- List<Long> collectionIds = collectionRepo.findAllByAssetId(collection.getAssetId());
|
|
|
- log.info("删除collection {}", collectionIds);
|
|
|
- if (CollectionUtils.isNotEmpty(collectionIds)) {
|
|
|
- collectionRepo.deleteAllByIdIn(collectionIds);
|
|
|
- } else {
|
|
|
- collectionRepo.delete(collection);
|
|
|
- }
|
|
|
+ log.info("订单回调 orderId: {}, payMethod: {}, transactionId: {}", orderId, payMethod, transactionId);
|
|
|
|
|
|
- } else {
|
|
|
+ // 取消订单与订单回调不能同时进行,需要抢锁
|
|
|
+ if (!getOrderLock(orderId)) {
|
|
|
+ log.info("订单回调失败 orderId: {} redis锁定, 重新发送到队列", orderId);
|
|
|
+ rocketMQTemplate.syncSend(generalProperties.getOrderNotifyTopic(),
|
|
|
+ new OrderNotifyEvent(orderId, payMethod, transactionId, System.currentTimeMillis()));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ Order order = orderRepo.findById(orderId).orElseThrow(new BusinessException("订单不存在"));
|
|
|
+ Collection collection = collectionRepo.findById(order.getCollectionId())
|
|
|
+ .orElseThrow(new BusinessException("藏品不存在"));
|
|
|
+ User user = userRepo.findById(order.getUserId()).orElseThrow(new BusinessException("用户不存在"));
|
|
|
+ if (order.getStatus() == OrderStatus.NOT_PAID) {
|
|
|
+ order.setStatus(OrderStatus.PROCESSING);
|
|
|
+ order.setPayTime(LocalDateTime.now());
|
|
|
+ order.setTransactionId(transactionId);
|
|
|
+ order.setPayMethod(payMethod);
|
|
|
+ if (order.getType() == CollectionType.BLIND_BOX) {
|
|
|
+ log.info("开始盲盒抽卡 orderId: {}, collectionId: {}", orderId, collection.getId());
|
|
|
+ BlindBoxItem winItem = null;
|
|
|
+ try {
|
|
|
+ winItem = collectionService.draw(collection.getId());
|
|
|
+ } catch (BusinessException ignored) {
|
|
|
+ }
|
|
|
+ if (winItem == null) {
|
|
|
+ log.info("抽卡失败退款 orderId: {}", orderId);
|
|
|
+ order.setStatus(OrderStatus.CANCELLED);
|
|
|
+ order.setCancelTime(LocalDateTime.now());
|
|
|
+
|
|
|
+ Map<String, Object> refundParams = new HashMap<>();
|
|
|
+ refundParams.put("refund_amt", order.getTotalPrice().setScale(2, RoundingMode.HALF_UP)
|
|
|
+ .toPlainString());
|
|
|
+ refundParams.put("refund_order_no", String.valueOf(snowflakeIdWorker.nextId()));
|
|
|
+ try {
|
|
|
+ Map<String, Object> response = Refund.create(transactionId, refundParams);
|
|
|
+ } catch (BaseAdaPayException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ orderRepo.save(order);
|
|
|
+ throw new BusinessException("抽卡失败, 已退款 " + orderId);
|
|
|
+ }
|
|
|
+ log.info("抽卡成功 orderId: {}, collectionId: {}, winCollectionId: {}", orderId, collection.getId(), winItem.getCollectionId());
|
|
|
+ order.setWinCollectionId(winItem.getCollectionId());
|
|
|
orderRepo.save(order);
|
|
|
- assetService.createAsset(collection, user, order.getId(), order.getPrice(), "出售",
|
|
|
- collectionService.getNextNumber(order.getCollectionId()));
|
|
|
+ assetService.createAsset(winItem, user, order.getId(), order.getPrice(), "出售",
|
|
|
+ winItem.getTotal() > 1 ? collectionService.getNextNumber(winItem.getCollectionId()) : null);
|
|
|
+ } else {
|
|
|
+ if (collection.getSource() == CollectionSource.TRANSFER) {
|
|
|
+ orderRepo.save(order);
|
|
|
+ Asset asset = assetRepo.findById(collection.getAssetId()).orElse(null);
|
|
|
+ assetService.transfer(asset, order.getPrice(), user, "转让", order.getId());
|
|
|
+ collectionRepo.delete(collection);
|
|
|
+
|
|
|
+
|
|
|
+ } else {
|
|
|
+ orderRepo.save(order);
|
|
|
+ assetService.createAsset(collection, user, order.getId(), order.getPrice(), "出售",
|
|
|
+ collection.getTotal() > 1 ? collectionService.getNextNumber(order.getCollectionId()) : null);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ commission(order);
|
|
|
+ if (collection.getAssetId() == null) {
|
|
|
+ collectionService.increaseSale(order.getCollectionId(), order.getQty());
|
|
|
}
|
|
|
- addSales(collection.getMinterId());
|
|
|
+ } else {
|
|
|
+ throw new BusinessException("状态错误 " + order.getStatus());
|
|
|
}
|
|
|
- commission(order);
|
|
|
- } else if (order.getStatus() == OrderStatus.CANCELLED) {
|
|
|
+ } catch (Exception e) {
|
|
|
+ ErrorOrder errorOrder = ErrorOrder.builder()
|
|
|
+ .orderId(orderId)
|
|
|
+ .transactionId(transactionId)
|
|
|
+ .payMethod(payMethod)
|
|
|
+ .build();
|
|
|
+ if (e instanceof BusinessException) {
|
|
|
+ log.error("订单回调出错 orderId: {} {}", orderId, e.getMessage());
|
|
|
+ } else {
|
|
|
+ log.error("订单回调出错 orderId: " + orderId, e);
|
|
|
+ }
|
|
|
+ errorOrder.setErrorMessage(e.getMessage());
|
|
|
+ errorOrderRepo.save(errorOrder);
|
|
|
+
|
|
|
}
|
|
|
+ releaseOrderLock(orderId);
|
|
|
}
|
|
|
|
|
|
@EventListener
|
|
|
@@ -459,36 +543,53 @@ public class OrderService {
|
|
|
}
|
|
|
|
|
|
public void cancel(Order order) {
|
|
|
- if (order.getStatus() != OrderStatus.NOT_PAID) {
|
|
|
- throw new BusinessException("已支付订单无法取消");
|
|
|
+ if (!getOrderLock(order.getId())) {
|
|
|
+ log.error("订单取消失败 {}, redis锁了", order.getId());
|
|
|
+ return;
|
|
|
}
|
|
|
- Collection collection = collectionRepo.findById(order.getCollectionId())
|
|
|
- .orElseThrow(new BusinessException("藏品不存在"));
|
|
|
- User minter = userRepo.findById(collection.getMinterId()).orElseThrow(new BusinessException("铸造者不存在"));
|
|
|
|
|
|
- if (collection.getSource() == CollectionSource.TRANSFER) {
|
|
|
- Asset asset = assetRepo.findById(collection.getAssetId()).orElse(null);
|
|
|
- if (asset != null) {
|
|
|
- asset.setStatus(AssetStatus.NORMAL);
|
|
|
- assetRepo.save(asset);
|
|
|
+ try {
|
|
|
+ if (order.getStatus() != OrderStatus.NOT_PAID) {
|
|
|
+ throw new BusinessException("已支付订单无法取消");
|
|
|
}
|
|
|
- collectionRepo.setOnShelf(collection.getId(), true);
|
|
|
- }
|
|
|
- collectionRepo.increaseSale(collection.getId(), -order.getQty());
|
|
|
- collectionRepo.increaseStock(collection.getId(), order.getQty());
|
|
|
+ Collection collection = collectionRepo.findById(order.getCollectionId())
|
|
|
+ .orElseThrow(new BusinessException("藏品不存在"));
|
|
|
+
|
|
|
+ CollectionSource source = Optional.ofNullable(order.getSource()).orElseGet(() ->
|
|
|
+ collectionRepo.findById(order.getCollectionId()).map(Collection::getSource).orElse(null));
|
|
|
+ if (source == CollectionSource.TRANSFER) {
|
|
|
+ Asset asset = assetRepo.findById(order.getAssetId()).orElse(null);
|
|
|
+ if (asset != null) {
|
|
|
+ log.info("set normal cancelOrder {}", order.getId());
|
|
|
+ asset.setStatus(AssetStatus.NORMAL);
|
|
|
+ assetRepo.save(asset);
|
|
|
+ }
|
|
|
+ collectionRepo.setOnShelf(order.getCollectionId(), true);
|
|
|
+ }
|
|
|
+ collectionService.increaseStock(order.getCollectionId(), order.getQty());
|
|
|
|
|
|
|
|
|
- order.setStatus(OrderStatus.CANCELLED);
|
|
|
- order.setCancelTime(LocalDateTime.now());
|
|
|
- orderRepo.save(order);
|
|
|
+ order.setStatus(OrderStatus.CANCELLED);
|
|
|
+ order.setCancelTime(LocalDateTime.now());
|
|
|
+ orderRepo.save(order);
|
|
|
|
|
|
- if (order.getCouponId() != null) {
|
|
|
- userCouponRepo.findById(order.getCouponId()).ifPresent(coupon -> {
|
|
|
- coupon.setUsed(false);
|
|
|
- coupon.setUseTime(null);
|
|
|
- userCouponRepo.save(coupon);
|
|
|
- });
|
|
|
+ if (order.getCouponId() != null) {
|
|
|
+ userCouponRepo.findById(order.getCouponId()).ifPresent(coupon -> {
|
|
|
+ coupon.setUsed(false);
|
|
|
+ coupon.setUseTime(null);
|
|
|
+ userCouponRepo.save(coupon);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ rocketMQTemplate.syncSend(generalProperties.getUpdateStockTopic(), order.getCollectionId(), 10000);
|
|
|
+ log.info("取消订单{}", order.getId());
|
|
|
+ } catch (Exception e) {
|
|
|
+ if (e instanceof BusinessException) {
|
|
|
+ log.error(e.getMessage());
|
|
|
+ } else {
|
|
|
+ log.error("订单取消错误 orderId: " + order.getId(), e);
|
|
|
+ }
|
|
|
}
|
|
|
+ releaseOrderLock(order.getId());
|
|
|
}
|
|
|
|
|
|
@Scheduled(fixedRate = 30000)
|
|
|
@@ -572,7 +673,20 @@ public class OrderService {
|
|
|
request.setTransactionId(order.getTransactionId());
|
|
|
request.setTotalFee(order.getTotalPrice().multiply(BigDecimal.valueOf(100)).intValue());
|
|
|
request.setRefundFee(order.getTotalPrice().multiply(BigDecimal.valueOf(100)).intValue());
|
|
|
- request.setOutRefundNo(String.valueOf(new SnowflakeIdWorker(0, 0).nextId()));
|
|
|
+ request.setOutRefundNo(String.valueOf(snowflakeIdWorker.nextId()));
|
|
|
wxPayService.refund(request);
|
|
|
}
|
|
|
+
|
|
|
+ // 获取订单锁,有效时间1小时
|
|
|
+ public boolean getOrderLock(Long orderId) {
|
|
|
+ BoundValueOperations<String, Object> ops = redisTemplate.boundValueOps(RedisKeys.ORDER_LOCK + orderId);
|
|
|
+ Boolean flag = ops.setIfAbsent(1, 1, TimeUnit.HOURS);
|
|
|
+ return Boolean.TRUE.equals(flag);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 释放订单锁
|
|
|
+ public void releaseOrderLock(Long orderId) {
|
|
|
+ redisTemplate.delete(RedisKeys.ORDER_LOCK + orderId);
|
|
|
+ }
|
|
|
+
|
|
|
}
|