Explorar el Código

完善保证金订单退款、扣除及超时处理
- 中标订单超时未支付时,扣除对应保证金并将拍品置为流拍
- 拍品保证金在拍品结束后按中标及支付结果执行退款或扣除
- 拍卖会保证金在整场结束后按订单结果执行退款或扣除
- 新增未支付保证金订单超时状态,拍品或拍卖会结束后置为超时
- 限制保证金状态仅按有效流转更新,避免重复处理或迟到支付覆盖超时状态

hr~ hace 3 semanas
padre
commit
42186adb25

+ 154 - 64
bid/src/main/java/cn/hobbystocks/auc/task/BidTask.java

@@ -21,7 +21,6 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.google.common.collect.Lists;
 import io.micrometer.core.instrument.util.NamedThreadFactory;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.scheduling.annotation.Scheduled;
@@ -39,6 +38,11 @@ import java.util.concurrent.TimeUnit;
 @Slf4j
 public class BidTask implements CacheMap {
 
+    private static final String DEPOSIT_TYPE_LOT = "拍品";
+    private static final String DEPOSIT_TYPE_AUCTION = "拍卖会";
+    private static final Integer DEPOSIT_STATUS_WAITING_PAYMENT = 0;
+    private static final Integer DEPOSIT_STATUS_PAID = 1;
+
     // region params
 	private static final ConcurrentHashMap<String, ConcurrentHashMap<String, Live>> liveCacheMap = new ConcurrentHashMap<>();
 
@@ -239,13 +243,17 @@ public class BidTask implements CacheMap {
             return;
         }
         Lot lot = lotMapper.selectLotById(order.getLotId());
-        if (Objects.isNull(lot) || Constants.LOT_STATUS_PASS.equals(lot.getStatus())) {
+        if (Objects.isNull(lot)) {
             return;
         }
-        lotMapper.updateLot(Lot.builder()
-                .id(order.getLotId())
-                .status(Constants.LOT_STATUS_PASS)
-                .build());
+        if (!Constants.LOT_STATUS_PASS.equals(lot.getStatus())) {
+            lotMapper.updateLot(Lot.builder()
+                    .id(order.getLotId())
+                    .status(Constants.LOT_STATUS_PASS)
+                    .build());
+        }
+        selectPendingDeposit(order.getUserId(), lot)
+                .forEach(deposit -> depositOrderService.reduceDeposit(deposit.getOrderNo()));
 //        appClient.notice(order.getLotId().toString(), "", "{}", "lot_auction", "");
         log.info("expire lot order marked pass, lotId={}, orderNo={}", order.getLotId(), order.getOrderNo());
     }
@@ -259,14 +267,19 @@ public class BidTask implements CacheMap {
         if (CollectionUtils.isEmpty(paidOrders)) {
             return;
         }
-        paidOrders.stream()
-                .map(Order::getLotId)
-                .filter(Objects::nonNull)
-                .distinct()
-                .forEach(lotId -> {
-                    lotMapper.updatePay(lotId, 1);
-                    log.info("paid lot order synced, lotId={}", lotId);
-                });
+        Set<Long> processedLotIds = new HashSet<>();
+        for (Order order : paidOrders) {
+            if (Objects.isNull(order) || Objects.isNull(order.getLotId()) || !processedLotIds.add(order.getLotId())) {
+                continue;
+            }
+            lotMapper.updatePay(order.getLotId(), 1);
+            Lot lot = lotMapper.selectLotById(order.getLotId());
+            if (isIndividualDeposit(lot) && isTerminalLot(lot)) {
+                selectPendingDeposit(order.getUserId(), lot)
+                        .forEach(deposit -> depositOrderService.refundDepositOrder(deposit.getOrderNo()));
+            }
+            log.info("paid lot order synced, lotId={}", order.getLotId());
+        }
     }
 
     /**
@@ -395,61 +408,138 @@ public class BidTask implements CacheMap {
     //拍卖会结束发起退保证金,每五分钟执行一次
     @Scheduled(fixedRate = 1000*60*5)
     public void depositOrderRefund(){
-        //查询已支付待退款的保证金订单
-        DepositOrder depositOrder = new DepositOrder();
-        depositOrder.setStatus(1);
-        List<DepositOrder> depositOrderList = depositOrderService.selectDepositOrder(depositOrder);
-        for (DepositOrder order : depositOrderList) {
-            Integer userId = order.getUserId();
-            //判断拍卖会是否已结束
-            Auction auction = auctionService.selectAuctionById(order.getAuctionId());
-            if (!Constants.GROUP_STATUS_FINISH.equals(auction.getStatus())){
-                //如果拍卖会未结束,处理下一个保证金订单
-                continue;
+        DepositOrder pending = new DepositOrder();
+        pending.setStatus(DEPOSIT_STATUS_PAID);
+        List<DepositOrder> depositOrders = depositOrderService.selectDepositOrder(pending);
+        for (DepositOrder deposit : depositOrders) {
+            if (DEPOSIT_TYPE_LOT.equals(deposit.getDepositType())) {
+                settleIndividualDeposit(deposit);
+            } else if (DEPOSIT_TYPE_AUCTION.equals(deposit.getDepositType())) {
+                settleAuctionDeposit(deposit);
             }
-            if (StringUtils.equals("拍卖会",order.getDepositType())){
-
-                //判断是否有未结束的未设置单独保证金的拍品
-                List<Lot> lots = lotMapper.selectNotEndLotList(order.getAuctionId());
-                if (!CollectionUtils.isEmpty(lots))
-                    continue;
-                //拍卖会保证金,查询用户在该拍卖会下是否有非单独保证金的拍品中标未支付订单
-                List<Order> orderList = orderService.getOrderListByUserAndAuction(order.getAuctionId(), userId);
-                if (CollectionUtils.isEmpty(orderList)){
-                    //todo 没有待支付订单,或没中标,或已支付,执行退保证金操作
-                   depositOrderService.refundDepositOrder(order.getOrderNo());
-                    continue;
-                }
-                //有待支付的订单列表,判断订单是否已过期,并更新订单记录表状态,
-                for (Order order1 : orderList) {
-                  if (order1.getFlag()==2){
-                      //todo 订单待支付已过期,调用扣除保证金接口,更新订单记录表状态
-                      depositOrderService.reduceDeposit(order.getOrderNo());
-                      break;
-                  }
-                }
-            }else{
-                Long lotId = order.getLotId();
-                //查询拍品是否已结束,未结束则跳过
-                Lot lot = lotMapper.selectLotById(lotId);
-                if (StringUtils.equalsAny(lot.getStatus(), Constants.LOT_STATUS_WAITING, Constants.LOT_STATUS_STARTING, Constants.LOT_STATUS_BIDDING))
-                    continue;
-
-                //已结束,查询用户是否有该拍品的订单,
-                LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>();
-                queryWrapper.eq(Order::getLotId,lotId).eq(Order::getUserId,order.getUserId());
-                Order order1 = orderService.getOne(queryWrapper);
-                if (order1==null){
-                    //todo 未中标,退拍品保证金
-                    depositOrderService.refundDepositOrder(order.getOrderNo());
-                    continue;
+        }
+        timeoutUnpaidDepositOrders();
+    }
+
+    private void timeoutUnpaidDepositOrders() {
+        DepositOrder waiting = new DepositOrder();
+        waiting.setStatus(DEPOSIT_STATUS_WAITING_PAYMENT);
+        List<DepositOrder> depositOrders = depositOrderService.selectDepositOrder(waiting);
+        for (DepositOrder deposit : depositOrders) {
+            if (DEPOSIT_TYPE_LOT.equals(deposit.getDepositType())) {
+                Lot lot = lotMapper.selectLotById(deposit.getLotId());
+                if (isTerminalLot(lot)) {
+                    depositOrderService.timeoutDepositOrder(deposit.getOrderNo());
                 }
-                if (order1.getFlag()==2){
-                    //todo 订单已过期 调用接口扣除保证金
-                    depositOrderService.reduceDeposit(order.getOrderNo());
+            } else if (DEPOSIT_TYPE_AUCTION.equals(deposit.getDepositType())) {
+                Auction auction = auctionService.selectAuctionById(deposit.getAuctionId());
+                if (Objects.nonNull(auction)
+                        && Constants.GROUP_STATUS_FINISH.equals(auction.getStatus())
+                        && CollectionUtils.isEmpty(lotMapper.selectNotEndLotList(deposit.getAuctionId()))) {
+                    depositOrderService.timeoutDepositOrder(deposit.getOrderNo());
                 }
             }
         }
     }
+
+    private void settleIndividualDeposit(DepositOrder deposit) {
+        Lot lot = lotMapper.selectLotById(deposit.getLotId());
+        if (!isTerminalLot(lot)) {
+            return;
+        }
+        LambdaQueryWrapper<Order> query = new LambdaQueryWrapper<>();
+        query.eq(Order::getLotId, deposit.getLotId()).eq(Order::getUserId, deposit.getUserId());
+        Order winningOrder = orderService.getOne(query);
+        if (Objects.isNull(winningOrder)) {
+            if (!isWinningUser(deposit, lot)) {
+                depositOrderService.refundDepositOrder(deposit.getOrderNo());
+            }
+            return;
+        }
+        if (Objects.equals(lot.getPaid(), 1L)
+                || Constants.LOT_STATUS_CANCELLED.equals(lot.getStatus())) {
+            depositOrderService.refundDepositOrder(deposit.getOrderNo());
+        } else if (Constants.LOT_STATUS_PASS.equals(lot.getStatus())) {
+            depositOrderService.reduceDeposit(deposit.getOrderNo());
+        }
+    }
+
+    private void settleAuctionDeposit(DepositOrder deposit) {
+        Auction auction = auctionService.selectAuctionById(deposit.getAuctionId());
+        if (Objects.isNull(auction) || !Constants.GROUP_STATUS_FINISH.equals(auction.getStatus())
+                || !CollectionUtils.isEmpty(lotMapper.selectNotEndLotList(deposit.getAuctionId()))) {
+            return;
+        }
+        List<Order> winningOrders = orderService.getOrderListByUserAndAuction(deposit.getAuctionId(), deposit.getUserId());
+        if (hasWinningLotWithoutOrder(deposit, winningOrders)) {
+            return;
+        }
+        if (CollectionUtils.isEmpty(winningOrders)) {
+            depositOrderService.refundDepositOrder(deposit.getOrderNo());
+            return;
+        }
+        for (Order winningOrder : winningOrders) {
+            Lot lot = lotMapper.selectLotById(winningOrder.getLotId());
+            if (Objects.isNull(lot) || (!Constants.LOT_STATUS_PASS.equals(lot.getStatus())
+                    && !Objects.equals(lot.getPaid(), 1L))) {
+                return;
+            }
+            if (Constants.LOT_STATUS_PASS.equals(lot.getStatus())) {
+                depositOrderService.reduceDeposit(deposit.getOrderNo());
+                return;
+            }
+        }
+        depositOrderService.refundDepositOrder(deposit.getOrderNo());
+    }
+
+    private boolean hasWinningLotWithoutOrder(DepositOrder deposit, List<Order> winningOrders) {
+        List<Lot> auctionLots = lotMapper.selectLotByAucId(deposit.getAuctionId());
+        if (CollectionUtils.isEmpty(auctionLots)) {
+            return false;
+        }
+        Set<Long> orderLotIds = new HashSet<>();
+        if (!CollectionUtils.isEmpty(winningOrders)) {
+            winningOrders.stream()
+                    .map(Order::getLotId)
+                    .filter(Objects::nonNull)
+                    .forEach(orderLotIds::add);
+        }
+        return auctionLots.stream()
+                .filter(lot -> !isIndividualDeposit(lot))
+                .anyMatch(lot -> isWinningUser(deposit, lot) && !orderLotIds.contains(lot.getId()));
+    }
+
+    private boolean isWinningUser(DepositOrder deposit, Lot lot) {
+        return Objects.nonNull(deposit.getUserId()) && Objects.nonNull(lot)
+                && String.valueOf(deposit.getUserId()).equals(lot.getDealAccountId());
+    }
+
+    private List<DepositOrder> selectPendingDeposit(Long userId, Lot lot) {
+        if (Objects.isNull(userId) || Objects.isNull(lot)) {
+            return Collections.emptyList();
+        }
+        DepositOrder condition = new DepositOrder();
+        condition.setUserId(userId.intValue());
+        condition.setStatus(DEPOSIT_STATUS_PAID);
+        if (isIndividualDeposit(lot)) {
+            condition.setDepositType(DEPOSIT_TYPE_LOT);
+            condition.setLotId(lot.getId());
+        } else {
+            condition.setDepositType(DEPOSIT_TYPE_AUCTION);
+            condition.setAuctionId(lot.getAuctionId());
+        }
+        return depositOrderService.selectDepositOrder(condition);
+    }
+
+    private boolean isIndividualDeposit(Lot lot) {
+        return Objects.nonNull(lot) && Objects.nonNull(lot.getDeposit());
+    }
+
+    private boolean isTerminalLot(Lot lot) {
+        return Objects.nonNull(lot)
+                && (Constants.LOT_STATUS_SOLD.equals(lot.getStatus())
+                || Constants.LOT_STATUS_PASS.equals(lot.getStatus())
+                || Constants.LOT_STATUS_CANCELLED.equals(lot.getStatus()));
+    }
     // endregion
 }

+ 2 - 0
bid/src/main/java/cn/hobbystocks/auc/web/AddressController.java

@@ -31,6 +31,8 @@ public class AddressController extends BaseController {
         }
         LambdaQueryWrapper<ShippingAddressDto> lambdaQueryWrapper = new LambdaQueryWrapper<>();
         lambdaQueryWrapper.eq(ShippingAddressDto::getUserId,addressDto.getUserId());
+        lambdaQueryWrapper.eq(ShippingAddressDto::getDelFlag, 0);
+        lambdaQueryWrapper.orderByDesc(ShippingAddressDto::getDefult).orderByDesc(ShippingAddressDto::getCreateTime);
         List<ShippingAddressDto> list = addressService.list(lambdaQueryWrapper);
         return AjaxResult.success(list);
     }

+ 1 - 0
bid/src/main/java/cn/hobbystocks/auc/web/DepositOrderController.java

@@ -110,6 +110,7 @@ public class DepositOrderController {
     @ApiOperation("修改保证金状态")
     public AjaxResult updateStatue(@ApiParam("保证金单号") @PathVariable String depositOrderNo) {
         orderService.lambdaUpdate().eq(DepositOrder::getOrderNo, depositOrderNo)
+            .eq(DepositOrder::getStatus, 0)
             .set(DepositOrder::getPayTime, new Date())
             .set(DepositOrder::getPayType, 1)
             .set(DepositOrder::getStatus, 1).update();

+ 1 - 1
lot/src/main/java/cn/hobbystocks/auc/domain/DepositOrder.java

@@ -20,7 +20,7 @@ public class DepositOrder extends BaseEntity {
     private Long auctionId;
     private String name;
     private String depositType;
-    //保证金订单状态,0、待支付,1、已支付待退款,2、已退款,3、已扣款
+    //保证金订单状态,0、待支付,1、已支付待退款,2、已退款,3、已扣款,4、超时
     private Integer status;
     private Long amount;//订单金额
     private String orderNo;

+ 1 - 1
lot/src/main/java/cn/hobbystocks/auc/dto/DepositOrderDTO.java

@@ -33,6 +33,6 @@ public class DepositOrderDTO extends BaseEntity {
     //商家id
     @ApiModelProperty("商家id")
     private Long merchantId;
-    @ApiModelProperty("保证金订单状态:0、待支付,1、已支付待退款,2、已退款,3、已扣款")
+    @ApiModelProperty("保证金订单状态:0、待支付,1、已支付待退款,2、已退款,3、已扣款,4、超时")
     private Integer status;
 }

+ 4 - 0
lot/src/main/java/cn/hobbystocks/auc/dto/ShippingAddressDto.java

@@ -61,6 +61,10 @@ public class ShippingAddressDto extends BaseEntity implements Serializable {
     @ApiModelProperty("县")
     private String county;
 
+    @TableField("del_flag")
+    @ApiModelProperty("删除标志")
+    private Integer delFlag;
+
 
 
 }

+ 6 - 0
lot/src/main/java/cn/hobbystocks/auc/service/DepositOrderService.java

@@ -29,6 +29,12 @@ public interface DepositOrderService extends IService<DepositOrder> {
      */
     void reduceDeposit(String orderNo);
 
+    /**
+     * 将超过参与时限仍未支付的保证金订单置为超时。
+     * @param orderNo 保证金订单编号
+     */
+    void timeoutDepositOrder(String orderNo);
+
     List<DepositOrder> selectDepositOrder(DepositOrder depositOrder);
 
     List<DepositRecordDTO> selectDepositRecordPage(DepositRecordRequest request);

+ 20 - 2
lot/src/main/java/cn/hobbystocks/auc/service/impl/DepositOrderServiceImpl.java

@@ -41,6 +41,7 @@ public class DepositOrderServiceImpl extends ServiceImpl<DepositOrderMapper, Dep
     private static final String DEPOSIT_TYPE_AUCTION = "拍卖会";
     private static final Integer DEPOSIT_STATUS_WAITING_PAYMENT = 0;
     private static final Integer DEPOSIT_STATUS_PAID = 1;
+    private static final Integer DEPOSIT_STATUS_TIMEOUT = 4;
 
     @Override
     @Transactional
@@ -96,7 +97,11 @@ public class DepositOrderServiceImpl extends ServiceImpl<DepositOrderMapper, Dep
 //        orderVO.setOrderNo(orderNo);
 //        //TODO 执行保证金退款逻辑
 //        return orderApi.refundDepositOrder(orderVO);
-        this.lambdaUpdate().eq(DepositOrder::getOrderNo,orderNo).set(DepositOrder::getStatus,2);
+        this.lambdaUpdate()
+                .eq(DepositOrder::getOrderNo, orderNo)
+                .eq(DepositOrder::getStatus, DEPOSIT_STATUS_PAID)
+                .set(DepositOrder::getStatus, 2)
+                .update();
     }
 
     @Override
@@ -106,7 +111,20 @@ public class DepositOrderServiceImpl extends ServiceImpl<DepositOrderMapper, Dep
 //        //TODO 执行扣减保证金
 //
 //        return orderApi.reduceDeposit(orderVO);
-        this.lambdaUpdate().eq(DepositOrder::getOrderNo,orderNo).set(DepositOrder::getStatus,3);
+        this.lambdaUpdate()
+                .eq(DepositOrder::getOrderNo, orderNo)
+                .eq(DepositOrder::getStatus, DEPOSIT_STATUS_PAID)
+                .set(DepositOrder::getStatus, 3)
+                .update();
+    }
+
+    @Override
+    public void timeoutDepositOrder(String orderNo) {
+        this.lambdaUpdate()
+                .eq(DepositOrder::getOrderNo, orderNo)
+                .eq(DepositOrder::getStatus, DEPOSIT_STATUS_WAITING_PAYMENT)
+                .set(DepositOrder::getStatus, DEPOSIT_STATUS_TIMEOUT)
+                .update();
     }
 
     @Override

+ 1 - 0
lot/src/main/resources/mapper/DepositOrderMapper.xml

@@ -29,6 +29,7 @@
             <if test="userId!=null">and user_id=#{userId}</if>
             <if test="lotId!=null">and lot_id=#{lotId}</if>
             <if test="auctionId!=null"> and auction_id=#{auctionId}</if>
+            <if test="depositType!=null and depositType!=''">and deposit_type=#{depositType}</if>
             <if test="status!=null">and status=#{status}</if>
         </where>
     </select>

+ 1 - 1
lot/src/main/resources/mapper/OrderMapper.xml

@@ -53,6 +53,6 @@
     </select>
     <select id="selectOrderByAuctionId" resultMap="orderResultMap">
         select o.* from order_info o left JOIN lot l on o.lot_id=l."id"
-        where l.auction_id=#{auctionId} and l.deposit is null and o.user_id=#{userId} and o.status=102
+        where l.auction_id=#{auctionId} and l.deposit is null and o.user_id=#{userId}
     </select>
 </mapper>