From 13dea2f2a48fe0d4eb01d0391e2eccb7ad58ae5b Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Wed, 30 Sep 2026 23:55:07 +0800 Subject: [PATCH] =?UTF-8?q?=E9=85=92=E5=BA=97=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/SmHotelMidController.java | 7 ++ .../shangyou/ShangyouHotelClient.java | 34 ++++---- .../service/SmHotelMidService.java | 5 ++ .../service/impl/SmHotelMidServiceImpl.java | 36 ++++++++ .../service/impl/SmHotelSyncServiceImpl.java | 84 +++++++++---------- 5 files changed, 107 insertions(+), 59 deletions(-) diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmHotelMidController.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmHotelMidController.java index fec2a561..b8c808e4 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmHotelMidController.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmHotelMidController.java @@ -82,6 +82,13 @@ public class SmHotelMidController { return midService.pullFromUpstream(id); } + @Operation(summary = "仅拉取上游房型(getHotelComplexList,不拉房价)") + @PostMapping("/mid/pullRooms/{id}") + @PreAuthorize("@pms.hasPermission('supermarket_smHotel_edit')") + public R> pullRooms(@PathVariable Long id) { + return midService.pullRoomsFromUpstream(id); + } + @Operation(summary = "推送到下游销售方") @PostMapping("/mid/push/{id}") @PreAuthorize("@pms.hasPermission('supermarket_smHotel_push')") diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/integration/shangyou/ShangyouHotelClient.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/integration/shangyou/ShangyouHotelClient.java index 87c5e2b0..257ddc7d 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/integration/shangyou/ShangyouHotelClient.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/integration/shangyou/ShangyouHotelClient.java @@ -25,9 +25,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; -import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.DefaultTypedTuple; -import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.ZSetOperations; /** @@ -279,7 +277,7 @@ public class ShangyouHotelClient { * */ public HotelPage pageHotelBriefs(String keyword, long current, long size, boolean forceRefresh) { @@ -322,20 +320,24 @@ public class ShangyouHotelClient { long need = size; long matched = 0; List page = new ArrayList<>((int) size); - ScanOptions options = ScanOptions.scanOptions().count(1000).build(); - try (Cursor> cursor = redisTemplate.opsForHash() - .scan(ShangyouHotelApi.REDIS_HOTEL_HASH_KEY, options)) { - while (cursor.hasNext()) { - Map.Entry e = cursor.next(); - String hotelId = String.valueOf(e.getKey()); - String hotelName = e.getValue() == null ? "" : String.valueOf(e.getValue()); - if (!hotelId.toLowerCase().contains(kwLower) && !hotelName.toLowerCase().contains(kwLower)) { - continue; + final long batch = 500L; + try { + for (long start = 0; start < len; start += batch) { + long end = Math.min(start + batch - 1, len - 1); + Set ids = redisTemplate.opsForZSet() + .range(ShangyouHotelApi.REDIS_HOTEL_ZSET_KEY, start, end); + List briefs = loadBriefsByIds(ids); + for (HotelBrief b : briefs) { + String hotelId = b.hotelId() == null ? "" : b.hotelId(); + String hotelName = b.hotelName() == null ? "" : b.hotelName(); + if (!hotelId.toLowerCase().contains(kwLower) && !hotelName.toLowerCase().contains(kwLower)) { + continue; + } + if (matched >= skip && page.size() < need) { + page.add(b); + } + matched++; } - if (matched >= skip && page.size() < need) { - page.add(new HotelBrief(hotelId, hotelName)); - } - matched++; } } catch (Exception ex) { diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmHotelMidService.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmHotelMidService.java index d73c4136..abd968d6 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmHotelMidService.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmHotelMidService.java @@ -16,6 +16,11 @@ public interface SmHotelMidService { /** 从上游拉取房型/产品并按加价生成日历,落本地库 */ R> pullFromUpstream(Long hotelId); + /** + * 仅调用 getHotelComplexList 落房型(不拉房价),用于单独验证综合信息接口。 + */ + R> pullRoomsFromUpstream(Long hotelId); + /** * 仅拉取指定日期区间房价(含房型综合若本地无房型),用于「滚一天」;区间建议 1 天,最长按上游 31 天切片。 */ diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmHotelMidServiceImpl.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmHotelMidServiceImpl.java index 9cd462d7..1a902787 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmHotelMidServiceImpl.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmHotelMidServiceImpl.java @@ -165,6 +165,42 @@ public class SmHotelMidServiceImpl implements SmHotelMidService { return R.ok(result); } + @Override + @Transactional(rollbackFor = Exception.class) + public R> pullRoomsFromUpstream(Long hotelId) { + SmHotelEntity hotel = requireHotel(hotelId); + String hotelCode = hotel.getHotelCode(); + List upstreamTips = new ArrayList<>(); + log.info("|酒店拉取房型|开始|hotelId={}|hotelCode={}|", hotelId, hotelCode); + JSONObject complexResp = upstreamClient.getHotelComplexListRaw(Collections.singletonList(hotelCode)); + String complexMsg = complexResp.getString("message"); + String complexCode = complexResp.getString("code"); + JSONArray complexList = asContentArray(complexResp.get("content")); + JSONObject complex = complexList.isEmpty() ? new JSONObject() : complexList.getJSONObject(0); + int upstreamRoomTypeSize = complex.getJSONArray("hotelRoomTypeList") == null ? 0 + : complex.getJSONArray("hotelRoomTypeList").size(); + if (complex.isEmpty()) { + String tip = StrUtil.blankToDefault(complexMsg, "getHotelComplexList content 为空"); + upstreamTips.add(tip); + log.warn("|酒店拉取房型|无数据|hotelCode={}|code={}|message={}|", hotelCode, complexCode, complexMsg); + } + else { + log.info("|酒店拉取房型|综合信息|hotelCode={}|roomTypeSize={}|code={}|message={}|", hotelCode, + upstreamRoomTypeSize, complexCode, complexMsg); + } + applyHotelComplex(hotel, complex); + hotelMapper.updateById(hotel); + int roomCount = upsertRoomsFromComplex(hotel, complex); + Map result = new LinkedHashMap<>(8); + result.put("hotelCode", hotelCode); + result.put("upstreamCode", complexCode); + result.put("upstreamMessage", complexMsg); + result.put("upstreamRoomTypeSize", upstreamRoomTypeSize); + result.put("roomCount", roomCount); + result.put("upstreamTips", upstreamTips); + return R.ok(result); + } + @Override @Transactional(rollbackFor = Exception.class) public R> pullPriceRange(Long hotelId, LocalDate startInclusive, LocalDate endExclusive) { diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmHotelSyncServiceImpl.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmHotelSyncServiceImpl.java index 5a6c24a1..c209c45c 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmHotelSyncServiceImpl.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmHotelSyncServiceImpl.java @@ -19,8 +19,6 @@ import com.bsd.pjcloud.common.data.tenant.TenantContextHolder; import com.bsd.pjcloud.common.security.util.SecurityUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.redis.core.Cursor; -import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; @@ -31,6 +29,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -318,7 +317,7 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService { log.info("|酒店列表同步|完成|logId={}|upstream={}|new={}|offlineMarked={}|costMs={}|", logId, upstreamCount, newCnt, offlineMarked, done.getCostMs()); } - catch (Exception e) { + catch (Throwable e) { log.error("|酒店列表同步|失败|logId={}|", logId, e); failLog(logId, e.getMessage(), System.currentTimeMillis() - t0); } @@ -415,7 +414,7 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService { done.setUpdateTime(LocalDateTime.now()); syncLogMapper.updateById(done); } - catch (Exception e) { + catch (Throwable e) { log.error("|酒店滚价|失败|logId={}|", logId, e); failLog(logId, e.getMessage(), System.currentTimeMillis() - t0); } @@ -523,7 +522,7 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService { syncLogMapper.updateById(done); log.info("|全量详情|完成|logId={}|ok={}|fail={}|skip={}|costMs={}|", logId, ok, fail, skip, done.getCostMs()); } - catch (Exception e) { + catch (Throwable e) { log.error("|全量详情|失败|logId={}|", logId, e); failLog(logId, e.getMessage(), System.currentTimeMillis() - t0); } @@ -625,7 +624,7 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService { syncLogMapper.updateById(done); log.info("|失败重试|完成|logId={}|type={}|ok={}|fail={}|skip={}|", logId, syncType, ok, fail, skip); } - catch (Exception e) { + catch (Throwable e) { log.error("|失败重试|失败|logId={}|", logId, e); failLog(logId, e.getMessage(), System.currentTimeMillis() - t0); } @@ -788,19 +787,21 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService { private int markOfflineFromDiff() { int updated = 0; int batch = Math.max(50, properties.getDiffScanBatchSize()); - List codes = new ArrayList<>(batch); - ScanOptions options = ScanOptions.scanOptions().count(batch).build(); - try (Cursor cursor = redisTemplate.opsForSet().scan(ShangyouHotelApi.REDIS_DIFF_OFFLINE_SET, options)) { - while (cursor.hasNext()) { - codes.add(cursor.next()); - if (codes.size() >= batch) { - updated += updateStatusByCodes(codes, "OFFLINE"); - codes.clear(); - } - } + // 不用 SET.scan:Redisson 与 Spring Data Redis 3.3 的 Cursor 不兼容会抛 AbstractMethodError + Set all = redisTemplate.opsForSet().members(ShangyouHotelApi.REDIS_DIFF_OFFLINE_SET); + if (all == null || all.isEmpty()) { + return 0; } - catch (Exception e) { - log.warn("|酒店停用|扫描 offline 差集失败|{}", e.getMessage()); + List codes = new ArrayList<>(batch); + for (String code : all) { + if (StrUtil.isBlank(code)) { + continue; + } + codes.add(code); + if (codes.size() >= batch) { + updated += updateStatusByCodes(codes, "OFFLINE"); + codes.clear(); + } } if (!codes.isEmpty()) { updated += updateStatusByCodes(codes, "OFFLINE"); @@ -869,26 +870,20 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService { List> records = new ArrayList<>((int) size); if (totalVal > 0) { long skip = (current - 1) * size; - long matched = 0; - long need = skip + size; - ScanOptions options = ScanOptions.scanOptions().count(Math.max(100, properties.getDiffScanBatchSize())) - .build(); - try (Cursor cursor = redisTemplate.opsForSet().scan(key, options)) { - while (cursor.hasNext() && matched < need) { - String hotelId = cursor.next(); - if (matched >= skip) { - Map row = new LinkedHashMap<>(4); - row.put("hotelId", hotelId); - row.put("hotelName", upstreamClient.hotelNameOf(hotelId)); - row.put("type", "offline".equalsIgnoreCase(type) ? "offline" : "new"); - records.add(row); - } - matched++; + Set all = redisTemplate.opsForSet().members(key); + if (all != null && !all.isEmpty()) { + List ordered = new ArrayList<>(all); + int from = (int) Math.min(skip, ordered.size()); + int to = (int) Math.min(skip + size, ordered.size()); + for (int i = from; i < to; i++) { + String hotelId = ordered.get(i); + Map row = new LinkedHashMap<>(4); + row.put("hotelId", hotelId); + row.put("hotelName", upstreamClient.hotelNameOf(hotelId)); + row.put("type", "offline".equalsIgnoreCase(type) ? "offline" : "new"); + records.add(row); } } - catch (Exception e) { - log.warn("|酒店差集分页|scan 异常|{}", e.getMessage()); - } } Map result = new LinkedHashMap<>(); result.put("records", records); @@ -904,19 +899,22 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService { int max = limit == null || limit <= 0 ? properties.getAutoImportBatchSize() : limit; max = Math.min(Math.max(max, 1), 5000); List> batch = new ArrayList<>(); - ScanOptions options = ScanOptions.scanOptions().count(Math.max(100, properties.getDiffScanBatchSize())).build(); - try (Cursor cursor = redisTemplate.opsForSet().scan(ShangyouHotelApi.REDIS_DIFF_NEW_SET, options)) { - while (cursor.hasNext() && batch.size() < max) { - String hotelId = cursor.next(); + // 不用 SET.scan(Redisson/Spring Data Redis 3.3 Cursor 不兼容) + Set all = redisTemplate.opsForSet().members(ShangyouHotelApi.REDIS_DIFF_NEW_SET); + if (all != null) { + for (String hotelId : all) { + if (batch.size() >= max) { + break; + } + if (StrUtil.isBlank(hotelId)) { + continue; + } Map row = new HashMap<>(2); row.put("hotelId", hotelId); row.put("hotelName", upstreamClient.hotelNameOf(hotelId)); batch.add(row); } } - catch (Exception e) { - return R.failed("扫描新增差集失败: " + e.getMessage()); - } if (batch.isEmpty()) { Map empty = new HashMap<>(2); empty.put("insertCount", 0);