Răsfoiți Sursa

1.订单超时未支付 没有进入悔拍状态
3.资产·珍品 "status":1 竞拍中,查询不到数据
4.后台订单详情的接口缺少两个字段 支付时间和订单状态
5.订单详情接口,已发货,缺少物流信息
6.成交订单支付 需要一个地址id

hr~ 1 lună în urmă
părinte
comite
d9fa6f6fa9

+ 85 - 0
bid/src/main/java/cn/hobbystocks/auc/app/AppClient.java

@@ -5,6 +5,7 @@ import cn.hobbystocks.auc.domain.Bid;
 import cn.hobbystocks.auc.domain.Lot;
 import cn.hobbystocks.auc.domain.LotFans;
 import cn.hobbystocks.auc.domain.LotFansPushRecord;
+import cn.hobbystocks.auc.domain.Order;
 import cn.hobbystocks.auc.event.SoldEvent;
 import cn.hobbystocks.auc.forest.CommonForestClient;
 import cn.hobbystocks.auc.handle.context.Live;
@@ -36,6 +37,12 @@ public class AppClient {
 
     @Value("${hobbystocks.host.orderUrl}")
     private String orderUrl;
+    @Value("${hobbystocks.host.expireOrderUrl:}")
+    private String expireOrderUrl;
+    @Value("${hobbystocks.host.timeoutUnpaidOrdersUrl:}")
+    private String timeoutUnpaidOrdersUrl;
+    @Value("${hobbystocks.host.paidOrdersLastThreeDaysUrl:}")
+    private String paidOrdersLastThreeDaysUrl;
 
     @Value("${hobbystocks.host.noticeUrl}")
     private String noticeUrl;
@@ -182,6 +189,84 @@ public class AppClient {
         }
     }
 
+    public List<Order> queryExpireLotOrders() {
+        return queryLotOrders(getExpireOrderUrl(), "query expire lot orders");
+    }
+
+    public int batchTimeoutUnpaidOrders() {
+        ForestResponse<CommonForestClient.Response<Map<String, Object>>> response = null;
+        String url = getTimeoutUnpaidOrdersUrl();
+        try {
+            response = client.sendGet(url);
+        } catch (Exception e) {
+            log.error("batch timeout unpaid orders fail {}", url, e);
+            return 0;
+        }
+        if (Objects.isNull(response)
+                || !Objects.equals(response.getStatusCode(), 200)
+                || Objects.isNull(response.getResult())
+                || !isSuccessCode(response.getResult().getCode())) {
+            log.error("batch timeout unpaid orders fail {}", url);
+            return 0;
+        }
+        Map<String, Object> data = response.getResult().getData();
+        if (CollectionUtils.isEmpty(data) || Objects.isNull(data.get("count"))) {
+            return 0;
+        }
+        return Integer.parseInt(data.get("count").toString());
+    }
+
+    public List<Order> queryPaidLotOrdersLastThreeDays() {
+        return queryLotOrders(getPaidOrdersLastThreeDaysUrl(), "query paid lot orders last three days");
+    }
+
+    private List<Order> queryLotOrders(String url, String operation) {
+        ForestResponse<CommonForestClient.Response<Map<String, Object>>> response = null;
+        try {
+            response = client.sendGet(url);
+        } catch (Exception e) {
+            log.error("{} fail {}", operation, url, e);
+            return Lists.newArrayList();
+        }
+        if (Objects.isNull(response)
+                || !Objects.equals(response.getStatusCode(), 200)
+                || Objects.isNull(response.getResult())
+                || !isSuccessCode(response.getResult().getCode())) {
+            log.error("{} fail {}", operation, url);
+            return Lists.newArrayList();
+        }
+        Map<String, Object> data = response.getResult().getData();
+        if (CollectionUtils.isEmpty(data) || Objects.isNull(data.get("lotOrders"))) {
+            return Lists.newArrayList();
+        }
+        return JSON.parseArray(JSON.toJSONString(data.get("lotOrders")), Order.class);
+    }
+
+    private String getExpireOrderUrl() {
+        if (Objects.nonNull(expireOrderUrl) && !expireOrderUrl.trim().isEmpty()) {
+            return expireOrderUrl;
+        }
+        return orderUrl.replaceFirst("/create$", "/queryExpireOrder");
+    }
+
+    private String getTimeoutUnpaidOrdersUrl() {
+        if (Objects.nonNull(timeoutUnpaidOrdersUrl) && !timeoutUnpaidOrdersUrl.trim().isEmpty()) {
+            return timeoutUnpaidOrdersUrl;
+        }
+        return orderUrl.replaceFirst("/create$", "/batchTimeoutUnpaidOrders");
+    }
+
+    private String getPaidOrdersLastThreeDaysUrl() {
+        if (Objects.nonNull(paidOrdersLastThreeDaysUrl) && !paidOrdersLastThreeDaysUrl.trim().isEmpty()) {
+            return paidOrdersLastThreeDaysUrl;
+        }
+        return orderUrl.replaceFirst("/create$", "/queryPaidOrdersLastThreeDays");
+    }
+
+    private boolean isSuccessCode(Integer code) {
+        return Objects.equals(code, 0) || Objects.equals(code, 200);
+    }
+
     public boolean notice(String lotName, Bid bid, String url) {
         ForestResponse<CommonForestClient.Response<Object>> response;
         try {

+ 6 - 1
bid/src/main/java/cn/hobbystocks/auc/config/JacksonConfig.java

@@ -9,6 +9,7 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
 
 import java.text.SimpleDateFormat;
+import java.util.TimeZone;
 
 @Configuration
 public class JacksonConfig {
@@ -18,7 +19,11 @@ public class JacksonConfig {
         ObjectMapper objectMapper = builder.createXmlMapper(false).build();
 
         // 配置日期格式
-        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
+        TimeZone timeZone = TimeZone.getTimeZone("GMT+8");
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        dateFormat.setTimeZone(timeZone);
+        objectMapper.setDateFormat(dateFormat);
+        objectMapper.setTimeZone(timeZone);
 
         // 序列化时,忽略值为 NULL 的字段
         objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

+ 8 - 8
bid/src/main/java/cn/hobbystocks/auc/handle/SkuSoldHandler.java

@@ -63,14 +63,14 @@ public class SkuSoldHandler implements SoldHandler {
             soldEvent.getLot().setPaid(-1L);
         }
 
-        Set<String> bidAccount = Sets.newHashSet();
-        soldEvent.getBids().forEach(bid -> {
-            if (!bidAccount.contains(bid.getAccountId())) {
-                //发送站内信
-                appClient.notice(soldEvent.getName(), bid, String.format(noticeUrl, bid.getLotId()));
-                bidAccount.add(bid.getAccountId());
-            }
-        });
+//        Set<String> bidAccount = Sets.newHashSet();
+//        soldEvent.getBids().forEach(bid -> {
+//            if (!bidAccount.contains(bid.getAccountId())) {
+//                //发送站内信
+//                appClient.notice(soldEvent.getName(), bid, String.format(noticeUrl, bid.getLotId()));
+//                bidAccount.add(bid.getAccountId());
+//            }
+//        });
         return true;
 
     }

+ 52 - 0
bid/src/main/java/cn/hobbystocks/auc/task/BidTask.java

@@ -219,6 +219,58 @@ public class BidTask implements CacheMap {
 
     /**
      * 每1秒检查一次正在竞拍的拍品,并更新拍卖状态
+     */
+    /**
+     * 每5秒检查拍品订单是否到期未支付,到期后将拍品置为流拍。
+     */
+    @Scheduled(fixedRate = 5000)
+    public void expireLotOrder() {
+        List<Order> expireOrders = appClient.queryExpireLotOrders();
+        if (CollectionUtils.isEmpty(expireOrders)) {
+            return;
+        }
+        expireOrders.forEach(this::markLotPassByExpireOrder);
+        int count = appClient.batchTimeoutUnpaidOrders();
+        log.info("batch timeout unpaid lot orders count={}", count);
+    }
+
+    private void markLotPassByExpireOrder(Order order) {
+        if (Objects.isNull(order) || Objects.isNull(order.getLotId())) {
+            return;
+        }
+        Lot lot = lotMapper.selectLotById(order.getLotId());
+        if (Objects.isNull(lot) || Constants.LOT_STATUS_PASS.equals(lot.getStatus())) {
+            return;
+        }
+        lotMapper.updateLot(Lot.builder()
+                .id(order.getLotId())
+                .status(Constants.LOT_STATUS_PASS)
+                .build());
+//        appClient.notice(order.getLotId().toString(), "", "{}", "lot_auction", "");
+        log.info("expire lot order marked pass, lotId={}, orderNo={}", order.getLotId(), order.getOrderNo());
+    }
+
+    /**
+     * 每分钟同步近三天已支付订单,将对应拍品标记为已支付。
+     */
+    @Scheduled(fixedRate = 60 * 1000)
+    public void syncPaidLotOrders() {
+        List<Order> paidOrders = appClient.queryPaidLotOrdersLastThreeDays();
+        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);
+                });
+    }
+
+    /**
+     * Check active bidding lots and update auction status.
      */
 	@Scheduled(fixedRate = 1000)
 	public void live() {

+ 3 - 0
bid/src/main/resources/application-dev.yml

@@ -16,6 +16,9 @@ hobbystocks:
   host:
     pointUrl: http://app/api/local/v1/point/operate
     orderUrl: http://poyee-app/api/v5/lot/order/create
+    expireOrderUrl: ${EXPIRE_LOT_ORDER_URL:http://poyee-app/api/v5/lot/order/queryExpireOrder}
+    timeoutUnpaidOrdersUrl: ${TIMEOUT_UNPAID_ORDERS_URL:http://poyee-app/api/v5/lot/order/batchTimeoutUnpaidOrders}
+    paidOrdersLastThreeDaysUrl: ${PAID_ORDERS_LAST_THREE_DAYS_URL:http://poyee-app/api/v5/lot/order/queryPaidOrdersLastThreeDays}
     noticeUrl: http://app/api/local/v1/notify/mail #站内信通知
     couponUrl: http://app/api/local/v1/coupon/send
     imUrl: http://poyee-im/chat/handler

+ 3 - 0
bid/src/main/resources/application-local.yml

@@ -21,6 +21,9 @@ hobbystocks:
   host:
     pointUrl: http://app/api/local/v1/point/operate
     orderUrl: http://127.0.0.1:8082/api/v5/lot/order/create
+    expireOrderUrl: ${EXPIRE_LOT_ORDER_URL:http://127.0.0.1:8082/api/v5/lot/order/queryExpireOrder}
+    timeoutUnpaidOrdersUrl: ${TIMEOUT_UNPAID_ORDERS_URL:http://127.0.0.1:8082/api/v5/lot/order/batchTimeoutUnpaidOrders}
+    paidOrdersLastThreeDaysUrl: ${PAID_ORDERS_LAST_THREE_DAYS_URL:http://127.0.0.1:8082/api/v5/lot/order/queryPaidOrdersLastThreeDays}
     noticeUrl: http://poyee-im/chat/handler #IM服务通知
     couponUrl: http://app/api/local/v1/coupon/send
     imUrl: http://poyee-im/chat/handler

+ 3 - 0
bid/src/main/resources/application-prod.yml

@@ -16,6 +16,9 @@ hobbystocks:
   host:
     pointUrl: http://app/api/local/v1/point/operate
     orderUrl: http://order/api/local/v1/order/auction/submit #中标创建订单
+    expireOrderUrl: ${EXPIRE_LOT_ORDER_URL:http://poyee-app/api/v5/lot/order/queryExpireOrder}
+    timeoutUnpaidOrdersUrl: ${TIMEOUT_UNPAID_ORDERS_URL:http://poyee-app/api/v5/lot/order/batchTimeoutUnpaidOrders}
+    paidOrdersLastThreeDaysUrl: ${PAID_ORDERS_LAST_THREE_DAYS_URL:http://poyee-app/api/v5/lot/order/queryPaidOrdersLastThreeDays}
     noticeUrl: http://app/api/local/v1/notify/mail
     couponUrl: http://app/api/local/v1/coupon/send # 中标兑换优惠券
     imUrl: http://poyee-im/chat/handler

+ 3 - 0
lot/src/main/java/cn/hobbystocks/auc/dto/DepositRecordDTO.java

@@ -1,6 +1,7 @@
 package cn.hobbystocks.auc.dto;
 
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
@@ -41,8 +42,10 @@ public class DepositRecordDTO {
     private UserInfo userInfo;
 
     @ApiModelProperty("创建时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private Date createTime;
     @ApiModelProperty("支付时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     private Date payTime;
 
 

+ 34 - 15
lot/src/main/java/cn/hobbystocks/auc/service/impl/LotServiceImpl.java

@@ -13,6 +13,7 @@ import cn.hobbystocks.auc.common.user.UserInfo;
 import cn.hobbystocks.auc.common.user.UserUtils;
 import cn.hobbystocks.auc.common.utils.CloneUtils;
 import cn.hobbystocks.auc.common.utils.DateUtils;
+import cn.hobbystocks.auc.common.utils.PageUtils;
 import cn.hobbystocks.auc.common.utils.SensitiveDataUtils;
 import cn.hobbystocks.auc.common.utils.StringUtils;
 import cn.hobbystocks.auc.convert.LotConvert;
@@ -850,30 +851,48 @@ public class LotServiceImpl extends ServiceImpl<LotMapper,Lot> implements ILotSe
             categoryIds = spuCategoryMapper.listChildByParentId(request.getMainCategoryId());
         }
 
-        // 2. 没有分类 → 返回空
-        if (CollectionUtils.isEmpty(categoryIds)) {
-            return Lists.newArrayList();
-        }
-
-        // 3. 查询拍品ID
-        List<Long> lotIds = spuCategoryMapper.listLotIdsByCategoryIds(categoryIds);
-
-        if (CollectionUtils.isEmpty(lotIds)) {
-            return Lists.newArrayList();
-        }
-
         IPage<Lot> lotIPage =new Page<>(request.getPageNum(),request.getPageSize());
         Lot lot = new Lot();
-        lot.setIds(lotIds);
+        if (!CollectionUtils.isEmpty(categoryIds)) {
+            // 3. 查询拍品ID
+            List<Long> lotIds = spuCategoryMapper.listLotIdsByCategoryIds(categoryIds);
+            if (CollectionUtils.isEmpty(lotIds)) {
+                return emptyLotFansResponsePage(request);
+            }
+            lot.setIds(lotIds);
+        }
         lot.setName(request.getName());
         lot.setPubStatus(request.getPubStatus());
         lot.setStatus(request.getStatus());
         lot.setStartTime(request.getStartTime());
         lot.setEndTime(request.getEndTime());
         lot.setRealEndTime(request.getRealEndTime());
+        PageUtils.startPage(request);
         List<Lot> diamondPositions = baseMapper.selectLotList(lotIPage, lot);
-        List<LotFansResponse> lotFansResponses = diamondPositions.stream().map(LotConvert.INSTANCE::toLotFansResponse).collect(Collectors.toList());
-        return lotFansResponses;
+        return toLotFansResponsePage(diamondPositions);
+    }
+
+    private List<LotFansResponse> toLotFansResponsePage(List<Lot> lots) {
+        List<LotFansResponse> responses = lots.stream()
+                .map(LotConvert.INSTANCE::toLotFansResponse)
+                .collect(Collectors.toList());
+        if (lots instanceof com.github.pagehelper.Page) {
+            com.github.pagehelper.Page<Lot> lotPage = (com.github.pagehelper.Page<Lot>) lots;
+            com.github.pagehelper.Page<LotFansResponse> responsePage =
+                    new com.github.pagehelper.Page<>(lotPage.getPageNum(), lotPage.getPageSize());
+            responsePage.setTotal(lotPage.getTotal());
+            responsePage.addAll(responses);
+            return responsePage;
+        }
+        return responses;
+    }
+
+    private List<LotFansResponse> emptyLotFansResponsePage(LotQueryRequest request) {
+        int pageNum = Objects.isNull(request.getPageNum()) ? 1 : request.getPageNum();
+        int pageSize = Objects.isNull(request.getPageSize()) ? 100 : request.getPageSize();
+        com.github.pagehelper.Page<LotFansResponse> page = new com.github.pagehelper.Page<>(pageNum, pageSize);
+        page.setTotal(0);
+        return page;
     }
 
     @Override