diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmHotelController.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmHotelController.java index 0c969d746..16055a53f 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmHotelController.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmHotelController.java @@ -47,16 +47,23 @@ public class SmHotelController { @PreAuthorize("@pms.hasPermission('supermarket_smHotel_view')") public R> page(@ParameterObject Page page, @ParameterObject SmHotelEntity query) { + String nameKw = StrUtil.trim(query.getHotelName()); + boolean nameSearch = StrUtil.isNotBlank(nameKw); var wrapper = Wrappers.lambdaQuery() - .like(StrUtil.isNotBlank(query.getHotelName()), SmHotelEntity::getHotelName, query.getHotelName()) .like(StrUtil.isNotBlank(query.getHotelCode()), SmHotelEntity::getHotelCode, query.getHotelCode()) .like(StrUtil.isNotBlank(query.getGroupCode()), SmHotelEntity::getGroupCode, query.getGroupCode()) .eq(StrUtil.isNotBlank(query.getSourceId()), SmHotelEntity::getSourceId, query.getSourceId()) .eq(StrUtil.isNotBlank(query.getSheetName()), SmHotelEntity::getSheetName, query.getSheetName()) // 归属部门:渠道映射按部门推送时,便于按部门筛酒店核查 - .eq(query.getDeptId() != null, SmHotelEntity::getDeptId, query.getDeptId()) - // 默认按主键倒序:雪花 ID 近似创建时间,可走索引;避免无索引时 create_time 全表 filesort - .orderByDesc(SmHotelEntity::getId); + .eq(query.getDeptId() != null, SmHotelEntity::getDeptId, query.getDeptId()); + // 店名:先走前缀(可用 idx_hotel_name);含匹配去掉 ORDER BY + 不做 COUNT,避免十几万酒店超时 + if (nameSearch) { + wrapper.and(w -> w.likeRight(SmHotelEntity::getHotelName, nameKw) + .or() + .eq(SmHotelEntity::getHotelCode, nameKw) + .or() + .eq(SmHotelEntity::getSHotelId, nameKw)); + } // 外部酒店ID/eid:推圣诺 getInfo 用 sHotelId,空时回落 hotelCode if (StrUtil.isNotBlank(query.getSHotelId())) { String eid = query.getSHotelId().trim(); @@ -74,7 +81,40 @@ public class SmHotelController { .ne(SmHotelEntity::getUpstreamStatus, "OFFLINE")); } hotelService.applySaleableFilter(wrapper, query.getSaleable()); - return R.ok(hotelService.pageScope(page, wrapper)); + if (!nameSearch) { + // 默认按主键倒序:雪花 ID 近似创建时间,可走索引 + wrapper.orderByDesc(SmHotelEntity::getId); + return R.ok(hotelService.pageScope(page, wrapper)); + } + // 名称搜索:先前缀(可排序);无结果再含匹配且不做 COUNT / 不 ORDER BY + wrapper.orderByDesc(SmHotelEntity::getId); + Page prefixPage = hotelService.pageScopeWithoutCount(page, wrapper); + if (page.getCurrent() > 1 || (prefixPage.getRecords() != null && !prefixPage.getRecords().isEmpty())) { + return R.ok(prefixPage); + } + var contains = Wrappers.lambdaQuery() + .like(SmHotelEntity::getHotelName, nameKw) + .like(StrUtil.isNotBlank(query.getHotelCode()), SmHotelEntity::getHotelCode, query.getHotelCode()) + .like(StrUtil.isNotBlank(query.getGroupCode()), SmHotelEntity::getGroupCode, query.getGroupCode()) + .eq(StrUtil.isNotBlank(query.getSourceId()), SmHotelEntity::getSourceId, query.getSourceId()) + .eq(StrUtil.isNotBlank(query.getSheetName()), SmHotelEntity::getSheetName, query.getSheetName()) + .eq(query.getDeptId() != null, SmHotelEntity::getDeptId, query.getDeptId()); + if (StrUtil.isNotBlank(query.getSHotelId())) { + String eid = query.getSHotelId().trim(); + contains.and(w -> w.eq(SmHotelEntity::getSHotelId, eid).or().eq(SmHotelEntity::getHotelCode, eid)); + } + HotelAreaAdcodeUtil.applyHotelListFilter(contains, query.getDistrictAdcode(), query.getDistrict()); + if ("OFFLINE".equalsIgnoreCase(query.getUpstreamStatus())) { + contains.eq(SmHotelEntity::getUpstreamStatus, "OFFLINE"); + } + else if ("ACTIVE".equalsIgnoreCase(query.getUpstreamStatus())) { + contains.and(w -> w.isNull(SmHotelEntity::getUpstreamStatus) + .or() + .ne(SmHotelEntity::getUpstreamStatus, "OFFLINE")); + } + hotelService.applySaleableFilter(contains, query.getSaleable()); + // 含匹配:禁止 ORDER BY,让 MySQL 凑够 LIMIT 即可停,避免全表 filesort + return R.ok(hotelService.pageScopeWithoutCount(page, contains)); } /** 上传文件会立刻 transferTo 临时目录,勿加 @SysLog */ diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmHotelService.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmHotelService.java index 8c506e5d0..efe0b409e 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmHotelService.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmHotelService.java @@ -12,6 +12,12 @@ public interface SmHotelService extends IService { Page pageScope(Page page, LambdaQueryWrapper wrapper); + /** + * 同 {@link #pageScope},但不做 COUNT(*)(店名模糊搜索时 COUNT+LIKE 会扫十几万行超时)。 + * total 用 hasMore 估算,便于前端翻页。 + */ + Page pageScopeWithoutCount(Page page, LambdaQueryWrapper wrapper); + /** * 管理端列表可售筛选:1/SALEABLE=近窗有开房可售库存;0/UNSALEABLE=近窗无可售。 */ diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmHotelServiceImpl.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmHotelServiceImpl.java index 2c2c17733..e9a988fe9 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmHotelServiceImpl.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmHotelServiceImpl.java @@ -76,8 +76,29 @@ public class SmHotelServiceImpl extends ServiceImpl pageScope(Page page, LambdaQueryWrapper wrapper) { + applyListSelectAndScope(wrapper); + return baseMapper.selectPage(page, wrapper); + } + + @Override + public Page pageScopeWithoutCount(Page page, + LambdaQueryWrapper wrapper) { + applyListSelectAndScope(wrapper); + long size = Math.max(1L, Math.min(page.getSize(), 100L)); + long current = Math.max(1L, page.getCurrent()); + Page q = new Page<>(current, size + 1, false); + Page raw = baseMapper.selectPage(q, wrapper); + java.util.List rows = raw.getRecords() == null ? java.util.List.of() : raw.getRecords(); + boolean hasMore = rows.size() > size; + java.util.List slice = hasMore ? rows.subList(0, (int) size) : rows; + long total = (current - 1) * size + slice.size() + (hasMore ? 1 : 0); + Page out = new Page<>(current, size, total); + out.setRecords(new java.util.ArrayList<>(slice)); + return out; + } + + private void applyListSelectAndScope(LambdaQueryWrapper wrapper) { // 不再聚合房型/产品/日历数;列表只查展示列,丢掉 intro/轮播等大字段减轻 IO - // coverImage 为短路径字符串,卡片封面需要;carouselImages/intro 仍不查 wrapper.select(SmHotelEntity::getId, SmHotelEntity::getTenantId, SmHotelEntity::getDeptId, SmHotelEntity::getSourceId, SmHotelEntity::getGroupCode, SmHotelEntity::getHotelCode, SmHotelEntity::getSGroupId, SmHotelEntity::getSHotelId, SmHotelEntity::getHotelName, @@ -88,9 +109,7 @@ public class SmHotelServiceImpl extends ServiceImpl