酒店
This commit is contained in:
1 parent
739c34ca28
commit
fdd33d3e0a
8 files changed
+174
-19
No files matched your search
+9
@@ -98,6 +98,15 @@ public class SmHotelDownstreamChannelConfigDTO {
|
||||
@Schema(description = "是否参与资源日更定时推送;false=仅手动/变更推,默认 false")
|
||||
private Boolean resourcePushJoinSchedule;
|
||||
|
||||
@Schema(description = "是否参与近窗详情定时;false=仅手动近窗刷新,默认 false")
|
||||
private Boolean nearDetailPullJoinSchedule;
|
||||
|
||||
@Schema(description = "本伙伴近窗刷新间隔小时;例 2 / 24 / 1;≤0 按 2")
|
||||
private Integer nearDetailPullIntervalHours;
|
||||
|
||||
@Schema(description = "本伙伴近窗天数;≤0 用上游 near-detail-pull-days")
|
||||
private Integer nearDetailPullDays;
|
||||
|
||||
@Schema(description = "本地改价/库存/房态后是否自动推本伙伴;大批量补数可临时 false,默认 true(未配则跟进程级)")
|
||||
private Boolean resourceAutoPushEnabled;
|
||||
}
|
||||
+3
-2
@@ -87,12 +87,13 @@ public class SmHotelShangyouChannelConfigDTO {
|
||||
|
||||
private String scopeDetailPullDailyCron;
|
||||
|
||||
/** 近窗详情定时(范围合集未来 N 天;不占全量配额) */
|
||||
/** 近窗详情调度总开关(扫描 join 伙伴;不占全量配额) */
|
||||
private Boolean nearDetailPullEnabled;
|
||||
|
||||
/** 近窗天数,建议 7 或 15 */
|
||||
/** 近窗天数默认值(伙伴未配时用),建议 7 或 15 */
|
||||
private Integer nearDetailPullDays;
|
||||
|
||||
/** 近窗调度时钟;真正间隔看各下游 near-detail-pull-interval-hours */
|
||||
private String nearDetailPullCron;
|
||||
|
||||
private Boolean autoImportNewHotels;
|
||||
|
||||
+18
@@ -146,6 +146,24 @@ public class HotelDownstreamProperties {
|
||||
*/
|
||||
private boolean resourcePushJoinSchedule = false;
|
||||
|
||||
/**
|
||||
* 是否参与「近窗详情」定时刷新(只刷本伙伴授权范围内、未来 N 天房价;不占全量配额)。
|
||||
* false(默认):不进近窗定时,可 PC 手动「近窗刷新」;
|
||||
* true:全局近窗时钟到点后,按 {@link #nearDetailPullIntervalHours} 判断是否该刷这一家。
|
||||
*/
|
||||
private boolean nearDetailPullJoinSchedule = false;
|
||||
|
||||
/**
|
||||
* 本伙伴近窗刷新最小间隔(小时)。例:2=约两小时一次;24=一天一次;1=一小时一次。
|
||||
* 仅当 {@link #nearDetailPullJoinSchedule}=true 时生效;≤0 时按 2。
|
||||
*/
|
||||
private int nearDetailPullIntervalHours = 2;
|
||||
|
||||
/**
|
||||
* 本伙伴近窗天数;≤0 则用上游商游配置 near-detail-pull-days(默认 15)。
|
||||
*/
|
||||
private int nearDetailPullDays = 0;
|
||||
|
||||
/** 进程级:是否跑资源日更时钟;改 cron 需重启 */
|
||||
private boolean resourcePushScheduleEnabled = true;
|
||||
|
||||
|
||||
+8
-6
@@ -196,22 +196,24 @@ public class ShangyouHotelProperties {
|
||||
private String scopeDetailPullDailyCron = "0 0 5 * * ?";
|
||||
|
||||
/**
|
||||
* 近窗详情定时:只刷渠道范围合集「未来 N 天」房价(保持可售近窗新鲜;不占全量 6h/每日次数配额)。
|
||||
* 默认开启。
|
||||
* 近窗详情定时总开关:到点后扫描「near-detail-pull-join-schedule=true」的下游伙伴,
|
||||
* 按各家 intervalHours 决定是否刷其授权范围近窗(不占全量 6h/每日次数配额)。
|
||||
* 默认开启;无伙伴 join 时任务空跑。
|
||||
*/
|
||||
private boolean nearDetailPullEnabled = true;
|
||||
|
||||
/**
|
||||
* 近窗天数:建议 7 或 15(≤30 通常只需 1 次 getRoomPrice/店)。
|
||||
* 近窗天数默认值:伙伴 YAML 未配 near-detail-pull-days 时使用。建议 7 或 15。
|
||||
* 默认 15。
|
||||
*/
|
||||
private int nearDetailPullDays = 15;
|
||||
|
||||
/**
|
||||
* 近窗详情 Cron(仅 spring-schedule-enabled=true 时生效)。
|
||||
* 默认每 2 小时:
|
||||
* 近窗调度时钟 Cron(仅 spring-schedule-enabled=true 时生效)。
|
||||
* 到点只「扫一轮」到期伙伴,真正间隔由各下游 near-detail-pull-interval-hours 控制。
|
||||
* 默认每 15 分钟扫一次:
|
||||
*/
|
||||
private String nearDetailPullCron = "0 0 */2 * * ?";
|
||||
private String nearDetailPullCron = "0 */15 * * * ?";
|
||||
|
||||
/** 文档:全量房价房量每天最多次数 */
|
||||
private int fullPriceSyncMaxPerDay = 3;
|
||||
|
||||
+3
-3
@@ -124,7 +124,7 @@ public class SmHotelShangyouSpringSchedule {
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(cron = "${shangyou-hotel.near-detail-pull-cron:0 0 */2 * * ?}")
|
||||
@Scheduled(cron = "${shangyou-hotel.near-detail-pull-cron:0 */15 * * * ?}")
|
||||
public void scheduledNearDetailPull() {
|
||||
ShangyouHotelProperties properties = props();
|
||||
if (!properties.isSpringScheduleEnabled()) {
|
||||
@@ -134,8 +134,8 @@ public class SmHotelShangyouSpringSchedule {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
log.info("|酒店日更|Spring Cron 触发近窗详情|days={}|cron={}|", properties.getNearDetailPullDays(),
|
||||
properties.getNearDetailPullCron());
|
||||
log.info("|酒店日更|Spring Cron 扫近窗详情|defaultDays={}|cron={}|",
|
||||
properties.getNearDetailPullDays(), properties.getNearDetailPullCron());
|
||||
syncService.jobNearDetailPull("CRON");
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
||||
+3
@@ -41,6 +41,9 @@ public interface SmHotelChannelConfigService extends IService<SmHotelChannelConf
|
||||
/** 参与资源日更定时推送的启用伙伴(resource-push-join-schedule=true) */
|
||||
List<String> listResourcePushSchedulePartners();
|
||||
|
||||
/** 参与近窗详情定时的启用伙伴(near-detail-pull-join-schedule=true) */
|
||||
List<String> listNearDetailPullSchedulePartners();
|
||||
|
||||
/** 渠道是否在库表中启用(无行时回落 YAML enabled;默认按上游) */
|
||||
boolean isChannelEnabled(String channel);
|
||||
|
||||
|
||||
+17
@@ -197,6 +197,14 @@ public class SmHotelChannelConfigServiceImpl extends ServiceImpl<SmHotelChannelC
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> listNearDetailPullSchedulePartners() {
|
||||
return listEnabledDownstreamPartners().stream().filter(code -> {
|
||||
HotelDownstreamProperties props = resolveDownstreamRuntime(code);
|
||||
return props != null && props.isEnabled() && props.isNearDetailPullJoinSchedule();
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChannelEnabled(String channel) {
|
||||
return isChannelEnabled(channel, SmHotelChannelDirection.UPSTREAM);
|
||||
@@ -386,6 +394,15 @@ public class SmHotelChannelConfigServiceImpl extends ServiceImpl<SmHotelChannelC
|
||||
if (dto.getResourcePushJoinSchedule() != null) {
|
||||
target.setResourcePushJoinSchedule(dto.getResourcePushJoinSchedule());
|
||||
}
|
||||
if (dto.getNearDetailPullJoinSchedule() != null) {
|
||||
target.setNearDetailPullJoinSchedule(dto.getNearDetailPullJoinSchedule());
|
||||
}
|
||||
if (dto.getNearDetailPullIntervalHours() != null) {
|
||||
target.setNearDetailPullIntervalHours(dto.getNearDetailPullIntervalHours());
|
||||
}
|
||||
if (dto.getNearDetailPullDays() != null) {
|
||||
target.setNearDetailPullDays(dto.getNearDetailPullDays());
|
||||
}
|
||||
if (dto.getResourceAutoPushEnabled() != null) {
|
||||
target.setResourceAutoPushEnabled(dto.getResourceAutoPushEnabled());
|
||||
}
|
||||
|
||||
+113
-8
@@ -76,6 +76,9 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService {
|
||||
/** 范围合集详情任务在同步日志 dataSource 上的标记 */
|
||||
private static final String SCOPE_UNION_DS = "scope_union";
|
||||
|
||||
/** 近窗定时:各下游上次提交时间 Redis Key 前缀 */
|
||||
private static final String NEAR_LAST_KEY = "sm:hotel:near:last:";
|
||||
|
||||
/** 按下游范围映射全量推送 */
|
||||
private static final String TYPE_DOWNSTREAM_PUSH = "DOWNSTREAM_PUSH";
|
||||
|
||||
@@ -324,7 +327,7 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService {
|
||||
dsLabel = partner;
|
||||
scopeLabel = "渠道「" + partner + "」范围";
|
||||
}
|
||||
int nearDays = resolveNearDetailDays(days);
|
||||
int nearDays = resolveNearDetailDaysForPartner(dsLabel, days);
|
||||
return startNearScopeDetailPullInternal(raw, dsLabel, scopeLabel, nearDays, triggerType);
|
||||
}
|
||||
|
||||
@@ -349,7 +352,14 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService {
|
||||
data.put("rawHotelCount", rawCount);
|
||||
data.put("hotelCount", pullCount);
|
||||
data.put("localSkipped", Math.max(0, rawCount - pullCount));
|
||||
data.put("nearDetailPullDays", resolveNearDetailDays(null));
|
||||
data.put("nearDetailPullDays", resolveNearDetailDaysForPartner(label, null));
|
||||
if (StrUtil.isNotBlank(label) && !SCOPE_UNION_DS.equals(label)) {
|
||||
HotelDownstreamProperties ds = channelConfigService.resolveDownstreamRuntime(label);
|
||||
if (ds != null) {
|
||||
data.put("nearDetailPullJoinSchedule", ds.isNearDetailPullJoinSchedule());
|
||||
data.put("nearDetailPullIntervalHours", resolveNearIntervalHours(ds));
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -2248,6 +2258,50 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService {
|
||||
return Math.min(90, configured);
|
||||
}
|
||||
|
||||
/** 伙伴 YAML 近窗天数优先,否则用上游默认。 */
|
||||
private int resolveNearDetailDaysForPartner(String partnerCode, Integer daysOverride) {
|
||||
if (daysOverride != null && daysOverride > 0) {
|
||||
return Math.min(90, daysOverride);
|
||||
}
|
||||
if (StrUtil.isNotBlank(partnerCode) && !SCOPE_UNION_DS.equals(partnerCode)) {
|
||||
HotelDownstreamProperties ds = channelConfigService.resolveDownstreamRuntime(partnerCode);
|
||||
if (ds != null && ds.getNearDetailPullDays() > 0) {
|
||||
return Math.min(90, ds.getNearDetailPullDays());
|
||||
}
|
||||
}
|
||||
return resolveNearDetailDays(null);
|
||||
}
|
||||
|
||||
private int resolveNearIntervalHours(HotelDownstreamProperties ds) {
|
||||
if (ds == null || ds.getNearDetailPullIntervalHours() <= 0) {
|
||||
return 2;
|
||||
}
|
||||
return Math.min(24 * 7, ds.getNearDetailPullIntervalHours());
|
||||
}
|
||||
|
||||
private boolean isNearPartnerDue(String partnerCode, int intervalHours) {
|
||||
String raw = redisTemplate.opsForValue().get(NEAR_LAST_KEY + partnerCode);
|
||||
if (StrUtil.isBlank(raw)) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
long lastMs = Long.parseLong(raw.trim());
|
||||
long gapMs = Math.max(1, intervalHours) * 3600_000L;
|
||||
return System.currentTimeMillis() - lastMs >= gapMs;
|
||||
}
|
||||
catch (Exception e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void markNearPartnerStarted(String partnerCode) {
|
||||
if (StrUtil.isBlank(partnerCode)) {
|
||||
return;
|
||||
}
|
||||
redisTemplate.opsForValue().set(NEAR_LAST_KEY + partnerCode,
|
||||
String.valueOf(System.currentTimeMillis()), 14, TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
/** 解析范围详情任务对应的上游可拉酒店 ID(续跑用)。 */
|
||||
private List<Long> resolveScopeDetailHotelIds(String dataSourceLabel) {
|
||||
String ds = SmHotelDataSource.normalize(dataSourceLabel).toLowerCase();
|
||||
@@ -2844,12 +2898,17 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService {
|
||||
m.put("nearDetailPullEnabled", props().isNearDetailPullEnabled());
|
||||
m.put("nearDetailPullDays", props().getNearDetailPullDays());
|
||||
m.put("nearDetailPullCron", props().getNearDetailPullCron());
|
||||
List<String> nearPartners = channelConfigService.listNearDetailPullSchedulePartners();
|
||||
m.put("nearDetailPullJoinPartners", nearPartners);
|
||||
m.put("nearDetailPullJoinPartnerCount", nearPartners.size());
|
||||
m.put("fullPriceSyncMaxPerDay", props().getFullPriceSyncMaxPerDay());
|
||||
m.put("fullPriceSyncMinIntervalHours", props().getFullPriceSyncMinIntervalHours());
|
||||
m.put("autoImportNewHotels", props().isAutoImportNewHotels());
|
||||
m.put("autoMarkOfflineHotels", props().isAutoMarkOfflineHotels());
|
||||
m.put("hint", "近窗详情 near-detail-pull-* 刷渠道范围未来 "
|
||||
+ props().getNearDetailPullDays() + " 天(不占全量配额);"
|
||||
m.put("hint", "近窗详情:时钟 "
|
||||
+ props().getNearDetailPullCron() + " 扫描 join 伙伴(当前 " + nearPartners.size()
|
||||
+ " 家),各家 intervalHours 独立;默认天数 "
|
||||
+ props().getNearDetailPullDays() + "(不占全量配额);"
|
||||
+ "全量/范围详情共用上游 " + props().getFullPriceSyncMinIntervalHours() + "h 与每日"
|
||||
+ props().getFullPriceSyncMaxPerDay() + " 次;"
|
||||
+ "XXL:smHotelNearDetailPullJobHandler");
|
||||
@@ -2970,13 +3029,59 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService {
|
||||
if (!props().isNearDetailPullEnabled()) {
|
||||
return Map.of("skipped", true, "reason", "near-detail-pull-enabled=false");
|
||||
}
|
||||
R<SmHotelSyncLogEntity> r = startNearScopeDetailPullAsync(null, null,
|
||||
List<String> joinPartners = channelConfigService.listNearDetailPullSchedulePartners();
|
||||
if (CollUtil.isEmpty(joinPartners)) {
|
||||
return Map.of("skipped", true, "reason",
|
||||
"无伙伴开启 near-detail-pull-join-schedule(请在下游渠道 YAML 按家配置)");
|
||||
}
|
||||
if (hasRunningNearDetailPull()) {
|
||||
return Map.of("skipped", true, "reason", "near-detail-pull-busy");
|
||||
}
|
||||
if (hasRunningHeavyDetailPull()) {
|
||||
return Map.of("skipped", true, "reason", "heavy-detail-pull-busy");
|
||||
}
|
||||
List<String> due = new ArrayList<>();
|
||||
Map<String, Integer> intervals = new LinkedHashMap<>();
|
||||
for (String code : joinPartners) {
|
||||
HotelDownstreamProperties ds = channelConfigService.resolveDownstreamRuntime(code);
|
||||
int hours = resolveNearIntervalHours(ds);
|
||||
intervals.put(code, hours);
|
||||
if (isNearPartnerDue(code, hours)) {
|
||||
due.add(code);
|
||||
}
|
||||
}
|
||||
if (due.isEmpty()) {
|
||||
Map<String, Object> m = new LinkedHashMap<>(6);
|
||||
m.put("skipped", true);
|
||||
m.put("reason", "no-partner-due");
|
||||
m.put("joinPartners", joinPartners);
|
||||
m.put("intervals", intervals);
|
||||
return m;
|
||||
}
|
||||
// 近窗任务互斥:每轮只提交一家到期伙伴,其余等下一拍时钟
|
||||
String partner = due.get(0);
|
||||
int nearDays = resolveNearDetailDaysForPartner(partner, null);
|
||||
R<SmHotelSyncLogEntity> r = startNearScopeDetailPullAsync(partner, nearDays,
|
||||
StrUtil.blankToDefault(triggerType, "XXL"));
|
||||
if (r.getCode() != 0) {
|
||||
return Map.of("ok", false, "msg", r.getMsg());
|
||||
Map<String, Object> fail = new LinkedHashMap<>(6);
|
||||
fail.put("ok", false);
|
||||
fail.put("msg", r.getMsg());
|
||||
fail.put("partner", partner);
|
||||
fail.put("duePartners", due);
|
||||
return fail;
|
||||
}
|
||||
return Map.of("ok", true, "async", true, "logId", r.getData().getId(), "nearDays",
|
||||
resolveNearDetailDays(null));
|
||||
markNearPartnerStarted(partner);
|
||||
Map<String, Object> ok = new LinkedHashMap<>(10);
|
||||
ok.put("ok", true);
|
||||
ok.put("async", true);
|
||||
ok.put("partner", partner);
|
||||
ok.put("logId", r.getData().getId());
|
||||
ok.put("nearDays", nearDays);
|
||||
ok.put("intervalHours", intervals.get(partner));
|
||||
ok.put("duePartners", due);
|
||||
ok.put("remainDue", due.size() - 1);
|
||||
return ok;
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user