From 1ee75e91043c00f4187322be0218c17c66cdbcac Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Wed, 28 Oct 2026 15:44:45 +0800 Subject: [PATCH] =?UTF-8?q?=E9=85=92=E5=BA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SmHotelDownstreamPushLogEntity.java | 4 +- .../service/impl/SmHotelMidServiceImpl.java | 90 +++++++++++++++++-- 2 files changed, 86 insertions(+), 8 deletions(-) diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/SmHotelDownstreamPushLogEntity.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/SmHotelDownstreamPushLogEntity.java index 6289697a5..4624e7862 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/SmHotelDownstreamPushLogEntity.java +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/entity/SmHotelDownstreamPushLogEntity.java @@ -33,7 +33,7 @@ public class SmHotelDownstreamPushLogEntity extends Model roomTypeToNs = matchShengnuoRooms(hotelId, nsRooms); + List localRooms = roomMapper.selectList(Wrappers.lambdaQuery() + .eq(SmHotelRoomEntity::getHotelId, hotelId)); + Map roomNameByCode = new HashMap<>(); + List unmatchedRooms = new ArrayList<>(); + for (SmHotelRoomEntity room : localRooms) { + if (room == null || StrUtil.isBlank(room.getRoomTypeCode())) { + continue; + } + roomNameByCode.put(room.getRoomTypeCode(), StrUtil.blankToDefault(room.getRoomTypeName(), "")); + if (!roomTypeToNs.containsKey(room.getRoomTypeCode())) { + unmatchedRooms.add(room.getRoomTypeCode() + + (StrUtil.isNotBlank(room.getRoomTypeName()) ? "/" + room.getRoomTypeName() : "")); + } + } + List products = productMapper.selectList(Wrappers.lambdaQuery() .eq(SmHotelProductEntity::getHotelId, hotelId) .isNotNull(SmHotelProductEntity::getProductCode)); Map productToPlan = new LinkedHashMap<>(); int planOk = 0; + int productTotal = 0; List planErrors = new ArrayList<>(); for (SmHotelProductEntity p : products) { if (p == null || StrUtil.isBlank(p.getProductCode()) || StrUtil.isBlank(p.getRoomTypeCode())) { continue; } + productTotal++; + String productLabel = p.getProductCode() + + (StrUtil.isNotBlank(p.getProductName()) ? "/" + p.getProductName() : ""); + String roomLabel = p.getRoomTypeCode() + + (StrUtil.isNotBlank(roomNameByCode.get(p.getRoomTypeCode())) + ? "/" + roomNameByCode.get(p.getRoomTypeCode()) : ""); Long nsRoomId = roomTypeToNs.get(p.getRoomTypeCode()); if (nsRoomId == null) { - planErrors.add(p.getProductCode() + ":无匹配圣诺房型"); + planErrors.add(productLabel + "(房型 " + roomLabel + "):无匹配圣诺房型,计划/价格均未推"); continue; } int breakfast = p.getMealCount() == null ? 0 : Math.max(0, p.getMealCount()); @@ -688,14 +710,14 @@ public class SmHotelMidServiceImpl implements SmHotelMidService { String.valueOf(nsHotelId), body); long planId = extractShengnuoPlanId(planResp, breakfast); if (planId <= 0) { - planErrors.add(p.getProductCode() + ":未返回 plan_id"); + planErrors.add(productLabel + "(房型 " + roomLabel + "):未返回 plan_id,价格未推"); continue; } productToPlan.put(p.getProductCode(), planId); planOk++; } catch (Exception ex) { - planErrors.add(p.getProductCode() + ":" + ex.getMessage()); + planErrors.add(productLabel + "(房型 " + roomLabel + "):" + ex.getMessage()); log.warn("|下游计划推送[圣诺]|失败|hotelId={}|product={}|{}", hotelId, p.getProductCode(), ex.getMessage()); } @@ -713,22 +735,78 @@ public class SmHotelMidServiceImpl implements SmHotelMidService { int priceChunks = pushShengnuoPriceChunks(partnerCode, ds, hotelId, nsHotelId, products, roomTypeToNs, productToPlan, dailies, dailyFrom, dailyTo); - Map result = new HashMap<>(12); + Map result = new HashMap<>(16); result.put("protocol", SmHotelDownstreamProtocol.SHENGNUO); result.put("nsHotelId", nsHotelId); result.put("eid", eid); result.put("matchedRooms", roomTypeToNs.size()); + result.put("unmatchedRooms", unmatchedRooms); + result.put("productTotal", productTotal); result.put("planOk", planOk); result.put("planErrors", planErrors); result.put("priceChunks", priceChunks); result.put("dailyRowCount", dailies.size()); result.put("windowFrom", dailyFrom.toString()); result.put("windowTo", dailyTo.toString()); - log.info("|下游可售推送[圣诺]|hotelId={}|partner={}|nsHotelId={}|plans={}|chunks={}|", hotelId, partnerCode, - nsHotelId, planOk, priceChunks); + // 房型对不上 / 计划失败等不会打 HTTP 失败,单独落流水方便排查「哪些没推上」 + recordShengnuoPushGaps(partnerCode, hotel, eid, nsHotelId, unmatchedRooms, productTotal, planOk, planErrors, + priceChunks, dailyFrom, dailyTo); + log.info("|下游可售推送[圣诺]|hotelId={}|partner={}|nsHotelId={}|plans={}/{}|chunks={}|gaps={}|", hotelId, + partnerCode, nsHotelId, planOk, productTotal, priceChunks, planErrors.size()); return result; } + /** + * 圣诺推送缺口留底:房型未匹配、计划失败等会导致对应价格推不上,但整体 HTTP 可能仍成功。 + * success=2 部分缺口;全部产品都没推上则 success=0。 + */ + private void recordShengnuoPushGaps(String partnerCode, SmHotelEntity hotel, String eid, long nsHotelId, + List unmatchedRooms, int productTotal, int planOk, List planErrors, int priceChunks, + LocalDate dailyFrom, LocalDate dailyTo) { + boolean hasRoomGap = CollUtil.isNotEmpty(unmatchedRooms); + boolean hasPlanGap = CollUtil.isNotEmpty(planErrors); + boolean nothingPushed = productTotal > 0 && planOk <= 0; + if (!hasRoomGap && !hasPlanGap && !nothingPushed) { + return; + } + int gapCount = planErrors == null ? 0 : planErrors.size(); + String summary; + if (nothingPushed) { + summary = "圣诺可售推送:全部 " + productTotal + " 个产品未推上(计划成功0),请核对房型匹配/plan"; + } + else { + summary = "圣诺可售推送部分未推上:计划成功 " + planOk + "/" + productTotal + ",缺口 " + gapCount + + (hasRoomGap ? ",未匹配房型 " + unmatchedRooms.size() + " 个" : ""); + } + Map detail = new LinkedHashMap<>(12); + detail.put("eid", eid); + detail.put("nsHotelId", nsHotelId); + detail.put("windowFrom", dailyFrom == null ? null : dailyFrom.toString()); + detail.put("windowTo", dailyTo == null ? null : dailyTo.toString()); + detail.put("productTotal", productTotal); + detail.put("planOk", planOk); + detail.put("priceChunks", priceChunks); + detail.put("unmatchedRooms", unmatchedRooms == null ? List.of() : unmatchedRooms); + detail.put("planErrors", planErrors == null ? List.of() : planErrors); + String extHotelId = hotel == null ? eid + : StrUtil.blankToDefault(hotel.getSHotelId(), + StrUtil.blankToDefault(hotel.getHotelCode(), + hotel.getId() == null ? eid : String.valueOf(hotel.getId()))); + SmHotelDownstreamPushLogEntity row = new SmHotelDownstreamPushLogEntity(); + row.setPartnerCode(partnerCode); + row.setSyncLogId(DownstreamPushLogContext.syncLogId()); + row.setHotelId(hotel == null ? null : hotel.getId()); + row.setExtHotelId(extHotelId); + row.setAction("push_partial"); + row.setSuccess(nothingPushed ? "0" : "2"); + row.setErrorMsg(StrUtil.maxLength(summary, 900)); + row.setResponseBody(StrUtil.maxLength(JSONUtil.toJsonStr(detail), 4000)); + row.setCostMs(0L); + downstreamPushLogService.record(row); + log.warn("|下游推送缺口[圣诺]|hotelId={}|partner={}|{}|", hotel == null ? null : hotel.getId(), partnerCode, + summary); + } + /** 圣诺关房:pushHotelClose(整店) */ private Map closeHotelDailyToShengnuo(SmHotelEntity hotel, String partnerCode) { downstreamClient.assertPartnerReady(partnerCode);