酒店任务优化
This commit is contained in:
1 parent
40384b7dc4
commit
89c0940daa
9 files changed
+275
-22
No files matched your search
+7
-1
@@ -95,9 +95,15 @@ public class SmHotelDownstreamChannelConfigDTO {
|
||||
@Schema(description = "推送流水保留天数,默认 15;≤0 不清理")
|
||||
private Integer pushLogRetainDays;
|
||||
|
||||
@Schema(description = "是否参与资源日更定时推送;false=仅手动/变更推,默认 false")
|
||||
@Schema(description = "是否参与资源定时推送;false=仅手动/变更推,默认 false")
|
||||
private Boolean resourcePushJoinSchedule;
|
||||
|
||||
@Schema(description = "资源定时推:每天最多启动次数,默认 1")
|
||||
private Integer resourcePushTimesPerDay;
|
||||
|
||||
@Schema(description = "资源定时推:两次最小间隔小时,默认 12;≤0 不校验间隔")
|
||||
private Integer resourcePushMinIntervalHours;
|
||||
|
||||
@Schema(description = "是否参与近窗详情定时;false=仅手动近窗刷新,默认 false")
|
||||
private Boolean nearDetailPullJoinSchedule;
|
||||
|
||||
|
||||
+21
@@ -82,11 +82,32 @@ public class SmHotelShangyouChannelConfigDTO {
|
||||
|
||||
private String detailPullDailyCron;
|
||||
|
||||
/** 全量详情前是否先确保今日列表同步成功;默认 true */
|
||||
private Boolean detailPullEnsureHotelListFirst;
|
||||
|
||||
/**
|
||||
* 全量+范围共用:每天最多几次(上游文档配额)。
|
||||
* 与 fullPriceSyncMaxPerDay 同义;YAML 写 detail-pull-times-per-day 即可。
|
||||
*/
|
||||
private Integer detailPullTimesPerDay;
|
||||
|
||||
/**
|
||||
* 全量+范围共用:两次最小间隔小时。
|
||||
* 与 fullPriceSyncMinIntervalHours 同义;YAML 写 detail-pull-min-interval-hours 即可。
|
||||
*/
|
||||
private Integer detailPullMinIntervalHours;
|
||||
|
||||
/** 渠道范围合集全量详情定时(各下游范围并集,跳过本地) */
|
||||
private Boolean scopeDetailPullDailyEnabled;
|
||||
|
||||
private String scopeDetailPullDailyCron;
|
||||
|
||||
/** 范围合集定时:每天最多启动次数(另受上游共享配额) */
|
||||
private Integer scopeDetailPullTimesPerDay;
|
||||
|
||||
/** 范围合集定时:两次启动最小间隔小时 */
|
||||
private Integer scopeDetailPullMinIntervalHours;
|
||||
|
||||
/** 近窗详情调度总开关(扫描 join 伙伴;不占全量配额) */
|
||||
private Boolean nearDetailPullEnabled;
|
||||
|
||||
|
||||
+23
-6
@@ -156,15 +156,29 @@ public class HotelDownstreamProperties {
|
||||
private int resourceAutoPushDebounceSeconds = 90;
|
||||
|
||||
/**
|
||||
* 是否参与资源日更定时推送(授权范围全量:可售/关房/跳过)。
|
||||
* false(默认):不进日更;确认范围与限流后再在伙伴 YAML 开 true。
|
||||
* 是否参与资源定时推送(授权范围全量:可售/关房/跳过)。
|
||||
* false(默认):不进定时推;确认范围与限流后再在伙伴 YAML 开 true。
|
||||
* 推荐与近窗拆开:近窗/范围只负责拉上游,本开关负责推下游。
|
||||
*/
|
||||
private boolean resourcePushJoinSchedule = false;
|
||||
|
||||
/**
|
||||
* 本伙伴资源定时推:每天最多启动次数。例:2=一天推两趟。
|
||||
* 仅 join-schedule=true 时生效;≤0 按 1。
|
||||
*/
|
||||
private int resourcePushTimesPerDay = 1;
|
||||
|
||||
/**
|
||||
* 本伙伴资源定时推:两次启动最小间隔小时数。例:12=至少隔 12 小时。
|
||||
* ≤0 表示不校验间隔(仍受 times-per-day 限制)。
|
||||
*/
|
||||
private int resourcePushMinIntervalHours = 12;
|
||||
|
||||
/**
|
||||
* 是否参与「近窗详情」定时刷新(只刷本伙伴授权范围内、未来 N 天房价;不占全量配额)。
|
||||
* false(默认):不进近窗定时,可 PC 手动「近窗刷新」;
|
||||
* true:全局近窗时钟到点后,按 {@link #nearDetailPullIntervalHours} 判断是否该刷这一家。
|
||||
* 推荐:只拉上游;推送用 resource-push-join-schedule / resource-auto-push。
|
||||
*/
|
||||
private boolean nearDetailPullJoinSchedule = false;
|
||||
|
||||
@@ -181,16 +195,19 @@ public class HotelDownstreamProperties {
|
||||
|
||||
/**
|
||||
* 近窗详情是否边拉边推:单店拉上游成功后立刻推本伙伴(拉一家推一家)。
|
||||
* 仅对「单伙伴近窗」任务生效;开放应用(拉模式)请保持 false。
|
||||
* 上万店时很慢(并发强制 1);推荐 false,改为「近窗只拉 + 资源定时推」。
|
||||
* false(默认):只刷本地。
|
||||
*/
|
||||
private boolean nearDetailPullThenPush = false;
|
||||
|
||||
/** 进程级:是否跑资源日更时钟;改 cron 需重启 */
|
||||
/** 进程级:是否跑资源推送扫描时钟;改 cron 需重启 */
|
||||
private boolean resourcePushScheduleEnabled = true;
|
||||
|
||||
/** 资源日更 cron(默认每天 02:30) */
|
||||
private String resourcePushCron = "0 30 2 * * ?";
|
||||
/**
|
||||
* 资源推送扫描时钟 cron(默认每小时 :30 扫一轮 join 伙伴;是否真推看各家 times/interval)。
|
||||
* 正式环境优先改 Quartz sys_job,本地 Spring 兜底才读本项。
|
||||
*/
|
||||
private String resourcePushCron = "0 30 * * * ?";
|
||||
|
||||
/**
|
||||
* 下游全量推酒店并发。1=串行。
|
||||
|
||||
+36
-10
@@ -172,28 +172,48 @@ public class ShangyouHotelProperties {
|
||||
private boolean localDailyRollEnabled = true;
|
||||
|
||||
/**
|
||||
* 上游渠道「全量详情」定时开关(拉该渠道全部启用酒店;受 full-price-sync 次数/间隔限制)。
|
||||
* 上游渠道「全量详情」定时开关(拉该渠道全部启用酒店)。
|
||||
* 真正到点是否开跑还受:先列表成功、每日次数、最小间隔、上游 full-price 配额。
|
||||
* 默认关闭,按需在渠道配置打开。
|
||||
*/
|
||||
private boolean detailPullDailyEnabled = false;
|
||||
|
||||
/**
|
||||
* 全量详情 Cron(仅 spring-schedule-enabled=true 时生效)。
|
||||
* 默认每天 04:00:0 0 4 * * ?(注意与上游 6 小时间隔、每日次数上限)
|
||||
* 全量详情调度时钟 Cron(仅本地 Spring 兜底;正式走 Quartz 同名任务)。
|
||||
* 默认每小时扫一次:到点只判断「该不该跑」,不是「一到就必跑满配额」。
|
||||
* Quartz 侧默认同样按小时扫,次数/间隔见 full-price-sync-* / detail-pull-times-*。
|
||||
*/
|
||||
private String detailPullDailyCron = "0 0 4 * * ?";
|
||||
private String detailPullDailyCron = "0 0 * * * ?";
|
||||
|
||||
/**
|
||||
* 「渠道范围合集」全量详情定时开关:只拉全部下游范围映射并集中的上游酒店(跳过本地)。
|
||||
* 全量详情定时:是否要求「今日已有成功的酒店列表同步」才开跑。
|
||||
* true(默认):没有则先启动列表同步,本轮详情跳过,下一时钟再试。
|
||||
*/
|
||||
private boolean detailPullEnsureHotelListFirst = true;
|
||||
|
||||
/**
|
||||
* 「渠道范围合集」详情定时开关:只拉全部下游范围映射并集中的上游酒店(跳过本地)。
|
||||
* 与全量详情分开开关;建议生产开这个、全量详情保持关(范围更小、更贴下游)。
|
||||
* 默认关闭。
|
||||
*/
|
||||
private boolean scopeDetailPullDailyEnabled = false;
|
||||
|
||||
/**
|
||||
* 渠道范围合集详情 Cron(仅 spring-schedule-enabled=true 时生效)。
|
||||
* 默认每天 05:00:0 0 5 * * ?
|
||||
* 范围合集详情调度时钟 Cron(仅本地 Spring 兜底;正式走 Quartz)。
|
||||
* 默认每小时 :20 扫一次;真正间隔/次数见 scope-detail-pull-times-per-day 等。
|
||||
*/
|
||||
private String scopeDetailPullDailyCron = "0 0 5 * * ?";
|
||||
private String scopeDetailPullDailyCron = "0 20 * * * ?";
|
||||
|
||||
/**
|
||||
* 范围合集定时:每天最多启动次数(另受下方上游 full-price 共享配额)。
|
||||
* 例:2=一天最多开两趟范围详情。
|
||||
*/
|
||||
private int scopeDetailPullTimesPerDay = 1;
|
||||
|
||||
/**
|
||||
* 范围合集定时:两次启动最小间隔小时数。例:6=至少隔 6 小时。
|
||||
*/
|
||||
private int scopeDetailPullMinIntervalHours = 6;
|
||||
|
||||
/**
|
||||
* 近窗详情定时总开关:到点后扫描「near-detail-pull-join-schedule=true」的下游伙伴,
|
||||
@@ -215,10 +235,16 @@ public class ShangyouHotelProperties {
|
||||
*/
|
||||
private String nearDetailPullCron = "0 */15 * * * ?";
|
||||
|
||||
/** 文档:全量房价房量每天最多次数 */
|
||||
/**
|
||||
* 上游文档:全量房价房量每天最多次数(全量详情 + 范围合集详情共用;近窗不占)。
|
||||
* YAML 也可写 detail-pull-times-per-day(同义覆盖本字段)。
|
||||
*/
|
||||
private int fullPriceSyncMaxPerDay = 3;
|
||||
|
||||
/** 文档:全量房价两次间隔最少小时数 */
|
||||
/**
|
||||
* 上游文档:全量房价两次间隔最少小时数(全量 + 范围合集共用;近窗不占)。
|
||||
* YAML 也可写 detail-pull-min-interval-hours(同义覆盖本字段)。
|
||||
*/
|
||||
private int fullPriceSyncMinIntervalHours = 6;
|
||||
|
||||
/** 全量拉详情:每家酒店间隔毫秒(防打爆上游;并行时主要靠 price-api-qps,默认 0) */
|
||||
|
||||
+7
-1
@@ -65,12 +65,18 @@ public final class ShangyouHotelApi {
|
||||
/** 列表同步/差集导入所选数据来源(字典值) */
|
||||
public static final String REDIS_IMPORT_DATA_SOURCE = "supermarket:shangyou:hotel:import:dataSource";
|
||||
|
||||
/** 今日全量房价同步次数 */
|
||||
/** 今日全量房价同步次数(全量详情+范围合集共用上游配额) */
|
||||
public static final String REDIS_FULL_PRICE_SYNC_COUNT = "supermarket:shangyou:hotel:fullprice:count";
|
||||
|
||||
/** 上次全量房价同步时间 epochMs */
|
||||
public static final String REDIS_FULL_PRICE_SYNC_LAST = "supermarket:shangyou:hotel:fullprice:last";
|
||||
|
||||
/** 范围合集定时:今日已启动次数(业务节奏,另受 fullprice 配额) */
|
||||
public static final String REDIS_SCOPE_DETAIL_SCHED_COUNT = "supermarket:shangyou:hotel:scope-detail:sched:count";
|
||||
|
||||
/** 范围合集定时:上次启动 epochMs */
|
||||
public static final String REDIS_SCOPE_DETAIL_SCHED_LAST = "supermarket:shangyou:hotel:scope-detail:sched:last";
|
||||
|
||||
/** 房价接口 QPS 秒窗计数前缀,完整 key = 前缀 + ":" + epochSecond */
|
||||
public static final String REDIS_PRICE_QPS_PREFIX = "supermarket:shangyou:hotel:price:qps";
|
||||
}
|
||||
+21
@@ -394,6 +394,12 @@ public class SmHotelChannelConfigServiceImpl extends ServiceImpl<SmHotelChannelC
|
||||
if (dto.getResourcePushJoinSchedule() != null) {
|
||||
target.setResourcePushJoinSchedule(dto.getResourcePushJoinSchedule());
|
||||
}
|
||||
if (dto.getResourcePushTimesPerDay() != null && dto.getResourcePushTimesPerDay() > 0) {
|
||||
target.setResourcePushTimesPerDay(dto.getResourcePushTimesPerDay());
|
||||
}
|
||||
if (dto.getResourcePushMinIntervalHours() != null) {
|
||||
target.setResourcePushMinIntervalHours(dto.getResourcePushMinIntervalHours());
|
||||
}
|
||||
if (dto.getNearDetailPullJoinSchedule() != null) {
|
||||
target.setNearDetailPullJoinSchedule(dto.getNearDetailPullJoinSchedule());
|
||||
}
|
||||
@@ -514,12 +520,27 @@ public class SmHotelChannelConfigServiceImpl extends ServiceImpl<SmHotelChannelC
|
||||
if (StrUtil.isNotBlank(dto.getDetailPullDailyCron())) {
|
||||
target.setDetailPullDailyCron(dto.getDetailPullDailyCron().trim());
|
||||
}
|
||||
if (dto.getDetailPullEnsureHotelListFirst() != null) {
|
||||
target.setDetailPullEnsureHotelListFirst(dto.getDetailPullEnsureHotelListFirst());
|
||||
}
|
||||
if (dto.getDetailPullTimesPerDay() != null && dto.getDetailPullTimesPerDay() > 0) {
|
||||
target.setFullPriceSyncMaxPerDay(dto.getDetailPullTimesPerDay());
|
||||
}
|
||||
if (dto.getDetailPullMinIntervalHours() != null && dto.getDetailPullMinIntervalHours() >= 0) {
|
||||
target.setFullPriceSyncMinIntervalHours(dto.getDetailPullMinIntervalHours());
|
||||
}
|
||||
if (dto.getScopeDetailPullDailyEnabled() != null) {
|
||||
target.setScopeDetailPullDailyEnabled(dto.getScopeDetailPullDailyEnabled());
|
||||
}
|
||||
if (StrUtil.isNotBlank(dto.getScopeDetailPullDailyCron())) {
|
||||
target.setScopeDetailPullDailyCron(dto.getScopeDetailPullDailyCron().trim());
|
||||
}
|
||||
if (dto.getScopeDetailPullTimesPerDay() != null && dto.getScopeDetailPullTimesPerDay() > 0) {
|
||||
target.setScopeDetailPullTimesPerDay(dto.getScopeDetailPullTimesPerDay());
|
||||
}
|
||||
if (dto.getScopeDetailPullMinIntervalHours() != null && dto.getScopeDetailPullMinIntervalHours() >= 0) {
|
||||
target.setScopeDetailPullMinIntervalHours(dto.getScopeDetailPullMinIntervalHours());
|
||||
}
|
||||
if (dto.getNearDetailPullEnabled() != null) {
|
||||
target.setNearDetailPullEnabled(dto.getNearDetailPullEnabled());
|
||||
}
|
||||
|
||||
+69
-3
@@ -25,6 +25,7 @@ import com.bsd.pjcloud.supermarket.service.SmHotelSyncService;
|
||||
import jakarta.annotation.PreDestroy;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
@@ -47,6 +48,12 @@ public class SmHotelDownstreamAutoPushServiceImpl implements SmHotelDownstreamAu
|
||||
|
||||
private static final String TAG = "下游自动推送";
|
||||
|
||||
/** 资源定时推:各伙伴上次启动 epochMs */
|
||||
private static final String RESOURCE_PUSH_LAST_KEY = "sm:hotel:resource-push:last:";
|
||||
|
||||
/** 资源定时推:各伙伴今日启动次数 */
|
||||
private static final String RESOURCE_PUSH_COUNT_KEY = "sm:hotel:resource-push:count:";
|
||||
|
||||
private final HotelDownstreamProperties yamlDefaults;
|
||||
|
||||
private final SmHotelChannelConfigService channelConfigService;
|
||||
@@ -63,10 +70,13 @@ public class SmHotelDownstreamAutoPushServiceImpl implements SmHotelDownstreamAu
|
||||
|
||||
private final SmHotelDownstreamPushLogService downstreamPushLogService;
|
||||
|
||||
private final StringRedisTemplate redisTemplate;
|
||||
|
||||
public SmHotelDownstreamAutoPushServiceImpl(HotelDownstreamProperties yamlDefaults,
|
||||
SmHotelChannelConfigService channelConfigService, SmHotelChannelDeptService channelDeptService,
|
||||
SmHotelMapper hotelMapper, SmHotelDailyMapper dailyMapper, @Lazy SmHotelMidService midService,
|
||||
@Lazy SmHotelSyncService syncService, SmHotelDownstreamPushLogService downstreamPushLogService) {
|
||||
@Lazy SmHotelSyncService syncService, SmHotelDownstreamPushLogService downstreamPushLogService,
|
||||
StringRedisTemplate redisTemplate) {
|
||||
this.yamlDefaults = yamlDefaults;
|
||||
this.channelConfigService = channelConfigService;
|
||||
this.channelDeptService = channelDeptService;
|
||||
@@ -75,6 +85,7 @@ public class SmHotelDownstreamAutoPushServiceImpl implements SmHotelDownstreamAu
|
||||
this.midService = midService;
|
||||
this.syncService = syncService;
|
||||
this.downstreamPushLogService = downstreamPushLogService;
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
private final ConcurrentHashMap<Long, ScheduledFuture<?>> pendingByHotel = new ConcurrentHashMap<>();
|
||||
@@ -272,12 +283,20 @@ public class SmHotelDownstreamAutoPushServiceImpl implements SmHotelDownstreamAu
|
||||
}
|
||||
for (String partner : partners) {
|
||||
try {
|
||||
String dueErr = checkResourcePushScheduleDue(partner);
|
||||
if (dueErr != null) {
|
||||
log.debug("|{}|定时推跳过|partner={}|reason={}|", TAG, partner, dueErr);
|
||||
continue;
|
||||
}
|
||||
var started = syncService.startDownstreamScopePushAsync(partner, "SCHEDULE");
|
||||
log.info("|{}|日更已启动|partner={}|msg={}|", TAG, partner,
|
||||
if (started != null && started.getCode() == 0) {
|
||||
markResourcePushScheduleStarted(partner);
|
||||
}
|
||||
log.info("|{}|定时推已启动|partner={}|msg={}|", TAG, partner,
|
||||
started == null ? null : started.getMsg());
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.warn("|{}|日更启动失败|partner={}|err={}|", TAG, partner, e.getMessage());
|
||||
log.warn("|{}|定时推启动失败|partner={}|err={}|", TAG, partner, e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -296,6 +315,53 @@ public class SmHotelDownstreamAutoPushServiceImpl implements SmHotelDownstreamAu
|
||||
+ "大批量补数可在该伙伴 YAML 临时设 resource-auto-push-enabled: false";
|
||||
}
|
||||
|
||||
/** 伙伴资源定时推:次数 + 间隔未到则返回原因。 */
|
||||
private String checkResourcePushScheduleDue(String partner) {
|
||||
HotelDownstreamProperties ds = channelConfigService.resolveDownstreamRuntime(partner);
|
||||
int times = ds == null ? 1 : Math.max(1, ds.getResourcePushTimesPerDay());
|
||||
int minHours = ds == null ? 12 : ds.getResourcePushMinIntervalHours();
|
||||
String last = redisTemplate.opsForValue().get(RESOURCE_PUSH_LAST_KEY + partner);
|
||||
if (StrUtil.isNotBlank(last) && minHours > 0) {
|
||||
try {
|
||||
long lastMs = Long.parseLong(last.trim());
|
||||
long gap = System.currentTimeMillis() - lastMs;
|
||||
if (gap < minHours * 3600_000L) {
|
||||
long leftMin = (minHours * 3600_000L - gap) / 60_000L + 1;
|
||||
return "not-due:距上次不足 " + minHours + "h,约再等 " + leftMin + " 分钟";
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
String countStr = redisTemplate.opsForValue().get(RESOURCE_PUSH_COUNT_KEY + partner);
|
||||
int count = 0;
|
||||
if (StrUtil.isNotBlank(countStr)) {
|
||||
try {
|
||||
count = Integer.parseInt(countStr.trim());
|
||||
}
|
||||
catch (NumberFormatException ignored) {
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
if (count >= times) {
|
||||
return "times-reached:今日已达 " + times + " 次";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void markResourcePushScheduleStarted(String partner) {
|
||||
if (StrUtil.isBlank(partner)) {
|
||||
return;
|
||||
}
|
||||
redisTemplate.opsForValue().set(RESOURCE_PUSH_LAST_KEY + partner,
|
||||
String.valueOf(System.currentTimeMillis()), 2, TimeUnit.DAYS);
|
||||
Long c = redisTemplate.opsForValue().increment(RESOURCE_PUSH_COUNT_KEY + partner);
|
||||
if (c != null && c == 1L) {
|
||||
redisTemplate.expire(RESOURCE_PUSH_COUNT_KEY + partner, 1, TimeUnit.DAYS);
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleDebounced(Long hotelId) {
|
||||
Long tenantId = TenantContextHolder.getTenantId();
|
||||
if (tenantId == null) {
|
||||
|
||||
+90
@@ -2920,6 +2920,68 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService {
|
||||
done.setStageMsg(StrUtil.maxLength("完成,成功见 successCount,失败明细见 failDetail", 500));
|
||||
}
|
||||
|
||||
private boolean hasRunningListSync() {
|
||||
Long running = syncLogMapper.selectCount(Wrappers.<SmHotelSyncLogEntity>lambdaQuery()
|
||||
.eq(SmHotelSyncLogEntity::getSyncType, TYPE_LIST)
|
||||
.eq(SmHotelSyncLogEntity::getStatus, STATUS_RUNNING));
|
||||
return running != null && running > 0;
|
||||
}
|
||||
|
||||
/** 今日是否已有成功的酒店列表同步(全量详情的前置条件)。 */
|
||||
private boolean hasSuccessfulListSyncToday() {
|
||||
LocalDateTime start = LocalDate.now().atStartOfDay();
|
||||
Long ok = syncLogMapper.selectCount(Wrappers.<SmHotelSyncLogEntity>lambdaQuery()
|
||||
.eq(SmHotelSyncLogEntity::getSyncType, TYPE_LIST)
|
||||
.eq(SmHotelSyncLogEntity::getStatus, STATUS_SUCCESS)
|
||||
.ge(SmHotelSyncLogEntity::getCreateTime, start));
|
||||
return ok != null && ok > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 范围合集业务节奏:每天次数 + 最小间隔(与上游 full-price 配额分开记;真正开跑仍走 checkFullPriceSyncQuota)。
|
||||
*/
|
||||
private String checkScopeDetailScheduleDue() {
|
||||
int maxPerDay = Math.max(1, props().getScopeDetailPullTimesPerDay());
|
||||
int minHours = Math.max(0, props().getScopeDetailPullMinIntervalHours());
|
||||
String last = redisTemplate.opsForValue().get(ShangyouHotelApi.REDIS_SCOPE_DETAIL_SCHED_LAST);
|
||||
if (StrUtil.isNotBlank(last) && minHours > 0) {
|
||||
try {
|
||||
long lastMs = Long.parseLong(last.trim());
|
||||
long gap = System.currentTimeMillis() - lastMs;
|
||||
if (gap < minHours * 3600_000L) {
|
||||
long leftMin = (minHours * 3600_000L - gap) / 60_000L + 1;
|
||||
return "scope-detail-not-due:距上次不足 " + minHours + "h,约再等 " + leftMin + " 分钟";
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException ignored) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
String countStr = redisTemplate.opsForValue().get(ShangyouHotelApi.REDIS_SCOPE_DETAIL_SCHED_COUNT);
|
||||
int count = 0;
|
||||
if (StrUtil.isNotBlank(countStr)) {
|
||||
try {
|
||||
count = Integer.parseInt(countStr.trim());
|
||||
}
|
||||
catch (NumberFormatException ignored) {
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
if (count >= maxPerDay) {
|
||||
return "scope-detail-times-reached:今日已达 " + maxPerDay + " 次";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void markScopeDetailScheduleStarted() {
|
||||
redisTemplate.opsForValue().set(ShangyouHotelApi.REDIS_SCOPE_DETAIL_SCHED_LAST,
|
||||
String.valueOf(System.currentTimeMillis()), 2, TimeUnit.DAYS);
|
||||
Long c = redisTemplate.opsForValue().increment(ShangyouHotelApi.REDIS_SCOPE_DETAIL_SCHED_COUNT);
|
||||
if (c != null && c == 1L) {
|
||||
redisTemplate.expire(ShangyouHotelApi.REDIS_SCOPE_DETAIL_SCHED_COUNT, 1, TimeUnit.DAYS);
|
||||
}
|
||||
}
|
||||
|
||||
/** 文档限制:全量房价每天次数 + 最小间隔 */
|
||||
private String checkFullPriceSyncQuota() {
|
||||
int maxPerDay = Math.max(1, props().getFullPriceSyncMaxPerDay());
|
||||
@@ -3314,6 +3376,29 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService {
|
||||
return Map.of("skipped", true, "reason", "detail-pull-daily-enabled=false");
|
||||
}
|
||||
String ds = StrUtil.blankToDefault(props().getDefaultImportDataSource(), SmHotelDataSource.SHANGYOU);
|
||||
// 全量详情前提:先有今日成功的酒店列表;否则本轮先拉列表,详情等下一时钟
|
||||
if (props().isDetailPullEnsureHotelListFirst()) {
|
||||
if (hasRunningListSync()) {
|
||||
return Map.of("skipped", true, "reason", "hotel-list-busy");
|
||||
}
|
||||
if (!hasSuccessfulListSyncToday()) {
|
||||
if (!props().isHotelListDailySyncEnabled()) {
|
||||
return Map.of("skipped", true, "reason",
|
||||
"ensure-hotel-list-first 但 hotel-list-daily-sync-enabled=false,且今日无成功列表");
|
||||
}
|
||||
R<SmHotelSyncLogEntity> listR = startListSyncAsync(props().isAutoImportNewHotels(), ds,
|
||||
StrUtil.blankToDefault(triggerType, "XXL"));
|
||||
Map<String, Object> m = new LinkedHashMap<>(8);
|
||||
m.put("skipped", true);
|
||||
m.put("reason", "ensure-hotel-list-first");
|
||||
m.put("listStarted", listR != null && listR.getCode() == 0);
|
||||
m.put("listMsg", listR == null ? "null" : listR.getMsg());
|
||||
if (listR != null && listR.getCode() == 0 && listR.getData() != null) {
|
||||
m.put("listLogId", listR.getData().getId());
|
||||
}
|
||||
return m;
|
||||
}
|
||||
}
|
||||
R<SmHotelSyncLogEntity> r = startDetailPullAsync(null, StrUtil.blankToDefault(triggerType, "XXL"), ds);
|
||||
if (r.getCode() != 0) {
|
||||
return Map.of("ok", false, "msg", r.getMsg());
|
||||
@@ -3334,10 +3419,15 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService {
|
||||
if (!props().isScopeDetailPullDailyEnabled()) {
|
||||
return Map.of("skipped", true, "reason", "scope-detail-pull-daily-enabled=false");
|
||||
}
|
||||
String dueErr = checkScopeDetailScheduleDue();
|
||||
if (dueErr != null) {
|
||||
return Map.of("skipped", true, "reason", dueErr);
|
||||
}
|
||||
R<SmHotelSyncLogEntity> r = startScopeUnionDetailPullAsync(StrUtil.blankToDefault(triggerType, "XXL"));
|
||||
if (r.getCode() != 0) {
|
||||
return Map.of("ok", false, "msg", r.getMsg());
|
||||
}
|
||||
markScopeDetailScheduleStarted();
|
||||
return Map.of("ok", true, "async", true, "logId", r.getData().getId());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ spring:
|
||||
# resource-auto-push-enabled: true # 本地/回调变更后防抖自动推
|
||||
# resource-auto-push-debounce-seconds: 90
|
||||
# resource-push-schedule-enabled: true # 日更总开关
|
||||
# resource-push-cron: 0 30 2 * * ? # 日更时钟;伙伴 YAML 另开 resource-push-join-schedule
|
||||
# resource-push-cron: 0 30 * * * ? # 资源推扫描时钟(每小时);伙伴 join + times/interval
|
||||
# # --- 推送流水(伙伴 YAML 可覆盖 mode/截断/保留天)---
|
||||
# push-log-mode: fail_only # fail_only|sample|all
|
||||
# push-log-sample-success-max: 20
|
||||
|
||||
Reference in new issue
Block a user