酒店优化
This commit is contained in:
1 parent
8cd0f3bc3d
commit
13dea2f2a4
5 files changed
+107
-59
No files matched your search
+7
@@ -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<Map<String, Object>> pullRooms(@PathVariable Long id) {
|
||||
return midService.pullRoomsFromUpstream(id);
|
||||
}
|
||||
|
||||
@Operation(summary = "推送到下游销售方")
|
||||
@PostMapping("/mid/push/{id}")
|
||||
@PreAuthorize("@pms.hasPermission('supermarket_smHotel_push')")
|
||||
|
||||
+18
-16
@@ -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 {
|
||||
* <ul>
|
||||
* <li>无关键字:ZRANGE + HMGET,深分页也稳定</li>
|
||||
* <li>精确 hotelId:HGET O(1)</li>
|
||||
* <li>模糊关键字:HSCAN 分批,只保留当前页(十几万时比全量装载安全,但首次统计全量匹配会稍慢)</li>
|
||||
* <li>模糊关键字:ZRANGE 分批 + HMGET 内存过滤(避开 Redisson 与 Spring Data Redis 3.3 的 HSCAN/Cursor 不兼容)</li>
|
||||
* </ul>
|
||||
*/
|
||||
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<HotelBrief> page = new ArrayList<>((int) size);
|
||||
ScanOptions options = ScanOptions.scanOptions().count(1000).build();
|
||||
try (Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash()
|
||||
.scan(ShangyouHotelApi.REDIS_HOTEL_HASH_KEY, options)) {
|
||||
while (cursor.hasNext()) {
|
||||
Map.Entry<Object, Object> 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<String> ids = redisTemplate.opsForZSet()
|
||||
.range(ShangyouHotelApi.REDIS_HOTEL_ZSET_KEY, start, end);
|
||||
List<HotelBrief> 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) {
|
||||
|
||||
+5
@@ -16,6 +16,11 @@ public interface SmHotelMidService {
|
||||
/** 从上游拉取房型/产品并按加价生成日历,落本地库 */
|
||||
R<Map<String, Object>> pullFromUpstream(Long hotelId);
|
||||
|
||||
/**
|
||||
* 仅调用 getHotelComplexList 落房型(不拉房价),用于单独验证综合信息接口。
|
||||
*/
|
||||
R<Map<String, Object>> pullRoomsFromUpstream(Long hotelId);
|
||||
|
||||
/**
|
||||
* 仅拉取指定日期区间房价(含房型综合若本地无房型),用于「滚一天」;区间建议 1 天,最长按上游 31 天切片。
|
||||
*/
|
||||
|
||||
+36
@@ -165,6 +165,42 @@ public class SmHotelMidServiceImpl implements SmHotelMidService {
|
||||
return R.ok(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<Map<String, Object>> pullRoomsFromUpstream(Long hotelId) {
|
||||
SmHotelEntity hotel = requireHotel(hotelId);
|
||||
String hotelCode = hotel.getHotelCode();
|
||||
List<String> 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<String, Object> 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<Map<String, Object>> pullPriceRange(Long hotelId, LocalDate startInclusive, LocalDate endExclusive) {
|
||||
|
||||
+41
-43
@@ -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<String> codes = new ArrayList<>(batch);
|
||||
ScanOptions options = ScanOptions.scanOptions().count(batch).build();
|
||||
try (Cursor<String> 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<String> 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<String> 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<Map<String, Object>> 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<String> cursor = redisTemplate.opsForSet().scan(key, options)) {
|
||||
while (cursor.hasNext() && matched < need) {
|
||||
String hotelId = cursor.next();
|
||||
if (matched >= skip) {
|
||||
Map<String, Object> 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<String> all = redisTemplate.opsForSet().members(key);
|
||||
if (all != null && !all.isEmpty()) {
|
||||
List<String> 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<String, Object> 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<String, Object> 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<Map<String, Object>> batch = new ArrayList<>();
|
||||
ScanOptions options = ScanOptions.scanOptions().count(Math.max(100, properties.getDiffScanBatchSize())).build();
|
||||
try (Cursor<String> 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<String> 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<String, Object> 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<String, Object> empty = new HashMap<>(2);
|
||||
empty.put("insertCount", 0);
|
||||
|
||||
Reference in new issue
Block a user