酒店
This commit is contained in:
1 parent
2db14465a9
commit
aadf00bc92
5 files changed
+156
-66
No files matched your search
+9
@@ -163,4 +163,13 @@ public class SmHotelOrderEntity extends Model<SmHotelOrderEntity> {
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "酒店数据来源")
|
||||
private String hotelDataSource;
|
||||
|
||||
/** 列表展示:酒店归属部门(对应商户,结算用;非表字段,来自 canteen_hotel.dept_id) */
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "酒店归属部门ID")
|
||||
private Long deptId;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "酒店归属部门名称")
|
||||
private String deptName;
|
||||
}
|
||||
+3
@@ -84,6 +84,9 @@ public class SmHotelOrderDetailVo {
|
||||
private BigDecimal latitude;
|
||||
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "酒店归属部门名称(商户)")
|
||||
private String deptName;
|
||||
}
|
||||
|
||||
@Data
|
||||
|
||||
+63
-21
@@ -41,7 +41,6 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -91,7 +90,7 @@ public class SmHotelAppController {
|
||||
private static final String CANDIDATE_CACHE_OVERFLOW = "OVERFLOW";
|
||||
|
||||
/** 列表页结果短缓存(同条件重复进入/下拉) */
|
||||
private static final String PAGE_CACHE_PREFIX = "sm:hotel:app:page:v13:";
|
||||
private static final String PAGE_CACHE_PREFIX = "sm:hotel:app:page:v14:";
|
||||
|
||||
private static final long PAGE_CACHE_SECONDS = 45L;
|
||||
|
||||
@@ -425,16 +424,32 @@ public class SmHotelAppController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 按酒店名搜索:
|
||||
* 1) 前缀匹配(idx_hotel_name + ORDER BY,快);
|
||||
* 2) 无结果再含匹配——SQL 不做 ORDER BY,只 LIMIT,再内存排序(避免超时变空列表)。
|
||||
* 有城市时尽量落到 district_adcode 区间。
|
||||
* 按酒店名搜索(只打 canteen_hotel,几万级):
|
||||
* 1) 先全国店名查出候选(前缀 → 含匹配),不带区划、不带日历,避免「有城市但酒店未落 adcode」直接变空;
|
||||
* 2) 有城市时内存把本市排前面;本市一个都没有则仍返回全国店名命中(总比超时/无语强);
|
||||
* 3) 日历最低价仍只查本页 id。
|
||||
*/
|
||||
private Page<SmHotelEntity> pageByHotelNameKeyword(Page<SmHotelEntity> page, String keyword, String city,
|
||||
String district, Long districtAdcode, List<Integer> starList) {
|
||||
long size = Math.max(1L, Math.min(page.getSize(), 50L));
|
||||
long current = Math.max(1L, page.getCurrent());
|
||||
int fetch = (int) Math.min(KEYWORD_CONTAINS_FETCH, Math.max(size + 1, size * current + 1));
|
||||
int fetch = KEYWORD_CONTAINS_FETCH;
|
||||
long tName = System.currentTimeMillis();
|
||||
List<SmHotelEntity> rows = hotelMapper.selectAppByNamePrefix(keyword, fetch);
|
||||
String hit = "prefix";
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
rows = hotelMapper.selectAppByNameContains(keyword, fetch);
|
||||
hit = "contains";
|
||||
}
|
||||
if (rows == null) {
|
||||
rows = List.of();
|
||||
}
|
||||
Set<Integer> starSet = starList == null || starList.isEmpty() ? Set.of() : new HashSet<>(starList);
|
||||
if (!starSet.isEmpty()) {
|
||||
rows = rows.stream()
|
||||
.filter(h -> h.getStarLevel() != null && starSet.contains(h.getStarLevel()))
|
||||
.toList();
|
||||
}
|
||||
Long adFrom = null;
|
||||
Long adTo = null;
|
||||
Long adcode = districtAdcode;
|
||||
@@ -449,20 +464,7 @@ public class SmHotelAppController {
|
||||
adFrom = range[0];
|
||||
adTo = range[1];
|
||||
}
|
||||
Collection<Integer> stars = starList == null || starList.isEmpty() ? null : starList;
|
||||
List<SmHotelEntity> rows = hotelMapper.selectAppByNamePrefix(keyword, adFrom, adTo, stars, fetch);
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
// 含匹配绝不带 ORDER BY;有城市时区间小,全国则最多捞 KEYWORD_CONTAINS_FETCH 条后内存排
|
||||
rows = hotelMapper.selectAppByNameContains(keyword, adFrom, adTo, stars, fetch);
|
||||
}
|
||||
if (rows == null) {
|
||||
rows = List.of();
|
||||
}
|
||||
List<SmHotelEntity> sorted = new ArrayList<>(rows);
|
||||
sorted.sort(Comparator
|
||||
.comparing((SmHotelEntity h) -> h.getStarLevel() == null ? 0 : h.getStarLevel())
|
||||
.reversed()
|
||||
.thenComparing((SmHotelEntity h) -> h.getId() == null ? 0L : h.getId(), Comparator.reverseOrder()));
|
||||
List<SmHotelEntity> sorted = rankKeywordHits(rows, adFrom, adTo);
|
||||
int from = (int) Math.min((current - 1) * size, sorted.size());
|
||||
int to = (int) Math.min(from + size, sorted.size());
|
||||
List<SmHotelEntity> slice = from >= sorted.size() ? List.of() : sorted.subList(from, to);
|
||||
@@ -470,6 +472,46 @@ public class SmHotelAppController {
|
||||
long total = (current - 1) * size + slice.size() + (hasMore ? 1 : 0);
|
||||
Page<SmHotelEntity> out = new Page<>(current, size, total);
|
||||
out.setRecords(new ArrayList<>(slice));
|
||||
log.info("|酒店App|店名搜索|kw={}|hit={}|cand={}|page={}|adcode={}|costMs={}|", keyword, hit, sorted.size(),
|
||||
slice.size(), adcode, System.currentTimeMillis() - tName);
|
||||
return out;
|
||||
}
|
||||
|
||||
/** 本市(adcode 命中)优先,再按星级、id;本市没有则保留全国店名命中 */
|
||||
private static List<SmHotelEntity> rankKeywordHits(List<SmHotelEntity> rows, Long adFrom, Long adTo) {
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<SmHotelEntity> inCity = new ArrayList<>();
|
||||
List<SmHotelEntity> other = new ArrayList<>();
|
||||
for (SmHotelEntity h : rows) {
|
||||
if (h == null) {
|
||||
continue;
|
||||
}
|
||||
if (adFrom != null && adTo != null && h.getDistrictAdcode() != null
|
||||
&& h.getDistrictAdcode() >= adFrom && h.getDistrictAdcode() < adTo) {
|
||||
inCity.add(h);
|
||||
}
|
||||
else {
|
||||
other.add(h);
|
||||
}
|
||||
}
|
||||
Comparator<SmHotelEntity> byStar = Comparator
|
||||
.comparing((SmHotelEntity h) -> h.getStarLevel() == null ? 0 : h.getStarLevel())
|
||||
.reversed()
|
||||
.thenComparing((SmHotelEntity h) -> h.getId() == null ? 0L : h.getId(), Comparator.reverseOrder());
|
||||
inCity.sort(byStar);
|
||||
other.sort(byStar);
|
||||
List<SmHotelEntity> out = new ArrayList<>(inCity.size() + other.size());
|
||||
out.addAll(inCity);
|
||||
// 本市一个都没有时,用全国店名结果,避免「库里有如家、界面无语」
|
||||
if (inCity.isEmpty()) {
|
||||
out.addAll(other);
|
||||
}
|
||||
else {
|
||||
// 本市有结果时仍可附带少量市外,便于翻页;首页优先本市
|
||||
out.addAll(other);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
+22
-45
@@ -6,7 +6,6 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@@ -15,58 +14,36 @@ public interface SmHotelMapper extends PjcloudBaseMapper<SmHotelEntity> {
|
||||
int insertBatch(@Param("list") List<SmHotelEntity> list);
|
||||
|
||||
/**
|
||||
* App 店名前缀搜索:走 idx_hotel_name,可带区划区间;带 ORDER BY 安全(前缀命中集小)。
|
||||
* 仅酒店表:店名前缀(走 idx_hotel_name)。几万级酒店 + LIMIT,秒级内。
|
||||
*/
|
||||
@Select("""
|
||||
<script>
|
||||
SELECT id, hotel_name AS hotelName, hotel_code AS hotelCode, address, district,
|
||||
district_adcode AS districtAdcode, biz_area AS bizArea, star_level AS starLevel,
|
||||
score, facility_tags AS facilityTags, cover_image AS coverImage, phone,
|
||||
sheet_name AS sheetName, upstream_status AS upstreamStatus, update_time AS updateTime
|
||||
FROM canteen_hotel FORCE INDEX (idx_hotel_name)
|
||||
WHERE del_flag = '0'
|
||||
AND (upstream_status IS NULL OR upstream_status <> 'OFFLINE')
|
||||
AND (hotel_name LIKE CONCAT(#{keyword}, '%') OR hotel_code = #{keyword})
|
||||
<if test="adFrom != null">
|
||||
AND district_adcode >= #{adFrom} AND district_adcode < #{adTo}
|
||||
</if>
|
||||
<if test="starLevels != null and starLevels.size() > 0">
|
||||
AND star_level IN
|
||||
<foreach collection="starLevels" item="s" open="(" separator="," close=")">#{s}</foreach>
|
||||
</if>
|
||||
ORDER BY IFNULL(star_level, 0) DESC, id DESC
|
||||
LIMIT #{limit}
|
||||
</script>
|
||||
""")
|
||||
List<SmHotelEntity> selectAppByNamePrefix(@Param("keyword") String keyword, @Param("adFrom") Long adFrom,
|
||||
@Param("adTo") Long adTo, @Param("starLevels") Collection<Integer> starLevels,
|
||||
@Param("limit") int limit);
|
||||
|
||||
/**
|
||||
* App 店名包含搜索:禁止 ORDER BY(含匹配 + 排序 = 全表 filesort 超时)。
|
||||
* 先 LIMIT 取候选,由调用方内存排序。有 adcode 时扫描范围极小。
|
||||
*/
|
||||
@Select("""
|
||||
<script>
|
||||
SELECT id, hotel_name AS hotelName, hotel_code AS hotelCode, address, district,
|
||||
district_adcode AS districtAdcode, biz_area AS bizArea, star_level AS starLevel,
|
||||
score, facility_tags AS facilityTags, cover_image AS coverImage, phone,
|
||||
sheet_name AS sheetName, upstream_status AS upstreamStatus, update_time AS updateTime
|
||||
FROM canteen_hotel
|
||||
WHERE del_flag = '0'
|
||||
AND (upstream_status IS NULL OR upstream_status <> 'OFFLINE')
|
||||
AND hotel_name LIKE CONCAT('%', #{keyword}, '%')
|
||||
<if test="adFrom != null">
|
||||
AND district_adcode >= #{adFrom} AND district_adcode < #{adTo}
|
||||
</if>
|
||||
<if test="starLevels != null and starLevels.size() > 0">
|
||||
AND star_level IN
|
||||
<foreach collection="starLevels" item="s" open="(" separator="," close=")">#{s}</foreach>
|
||||
</if>
|
||||
AND (upstream_status IS NULL OR upstream_status <> 'OFFLINE')
|
||||
AND (hotel_name LIKE CONCAT(#{keyword}, '%') OR hotel_code = #{keyword})
|
||||
ORDER BY star_level DESC, id DESC
|
||||
LIMIT #{limit}
|
||||
</script>
|
||||
""")
|
||||
List<SmHotelEntity> selectAppByNameContains(@Param("keyword") String keyword, @Param("adFrom") Long adFrom,
|
||||
@Param("adTo") Long adTo, @Param("starLevels") Collection<Integer> starLevels,
|
||||
@Param("limit") int limit);
|
||||
List<SmHotelEntity> selectAppByNamePrefix(@Param("keyword") String keyword, @Param("limit") int limit);
|
||||
|
||||
/**
|
||||
* 仅酒店表:店名包含。无 ORDER BY(避免 filesort);LIMIT 后内存排序。
|
||||
* 几万酒店全表扫含匹配通常远快于带日历/区划的复杂 SQL。
|
||||
*/
|
||||
@Select("""
|
||||
SELECT id, hotel_name AS hotelName, hotel_code AS hotelCode, address, district,
|
||||
district_adcode AS districtAdcode, biz_area AS bizArea, star_level AS starLevel,
|
||||
score, facility_tags AS facilityTags, cover_image AS coverImage, phone,
|
||||
sheet_name AS sheetName, upstream_status AS upstreamStatus, update_time AS updateTime
|
||||
FROM canteen_hotel
|
||||
WHERE del_flag = '0'
|
||||
AND (upstream_status IS NULL OR upstream_status <> 'OFFLINE')
|
||||
AND hotel_name LIKE CONCAT('%', #{keyword}, '%')
|
||||
LIMIT #{limit}
|
||||
""")
|
||||
List<SmHotelEntity> selectAppByNameContains(@Param("keyword") String keyword, @Param("limit") int limit);
|
||||
}
|
||||
+59
@@ -26,6 +26,8 @@ import com.bsd.pjcloud.supermarket.service.SmHotelOrderUpstreamLogService;
|
||||
import com.bsd.pjcloud.supermarket.service.SmHotelPriceConfigService;
|
||||
import com.bsd.pjcloud.supermarket.vo.SmHotelOrderDetailVo;
|
||||
import com.bsd.pjcloud.supermarket.vo.SmHotelOrderPayInfoVo;
|
||||
import com.bsd.pjcloud.admin.api.entity.SysDept;
|
||||
import com.bsd.pjcloud.admin.api.feign.RemoteDeptService;
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.common.data.tenant.TenantBroker;
|
||||
import com.bsd.pjcloud.common.data.tenant.TenantContextHolder;
|
||||
@@ -108,6 +110,8 @@ public class SmHotelOrderServiceImpl extends ServiceImpl<SmHotelOrderMapper, SmH
|
||||
|
||||
private final SmHotelRoomMapper roomMapper;
|
||||
|
||||
private final RemoteDeptService remoteDeptService;
|
||||
|
||||
@Override
|
||||
public Page<SmHotelOrderEntity> pageOrders(Page<SmHotelOrderEntity> page, SmHotelOrderEntity query) {
|
||||
Page<SmHotelOrderEntity> result = baseMapper.selectPage(page, Wrappers.<SmHotelOrderEntity>lambdaQuery()
|
||||
@@ -343,6 +347,7 @@ public class SmHotelOrderServiceImpl extends ServiceImpl<SmHotelOrderMapper, SmH
|
||||
if (hotel != null) {
|
||||
o.setHotelName(hotel.getHotelName());
|
||||
o.setHotelDataSource(upstreamGatewayRegistry.resolveChannelKey(hotel.getSheetName()));
|
||||
o.setDeptId(hotel.getDeptId());
|
||||
}
|
||||
SmHotelProductEntity product = null;
|
||||
if (StrUtil.isNotBlank(o.getProductCode())) {
|
||||
@@ -380,6 +385,39 @@ public class SmHotelOrderServiceImpl extends ServiceImpl<SmHotelOrderMapper, SmH
|
||||
}
|
||||
fillOrderSourceFields(o, app, partnerNameMap);
|
||||
}
|
||||
fillOrderDeptNames(records);
|
||||
}
|
||||
|
||||
/** 批量补全酒店归属部门名(结算商户) */
|
||||
private void fillOrderDeptNames(List<SmHotelOrderEntity> records) {
|
||||
List<Long> deptIds = records.stream()
|
||||
.map(SmHotelOrderEntity::getDeptId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
if (deptIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<Long, String> nameById = new HashMap<>();
|
||||
try {
|
||||
R<Map<Long, SysDept>> r = remoteDeptService.getDeptByDeptIds(deptIds);
|
||||
if (r != null && r.isOk() && r.getData() != null) {
|
||||
r.getData().forEach((id, d) -> {
|
||||
if (id != null && d != null && StrUtil.isNotBlank(d.getName())) {
|
||||
nameById.put(id, d.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.warn("|酒店订单|补全部门名失败|{}", e.getMessage());
|
||||
}
|
||||
for (SmHotelOrderEntity o : records) {
|
||||
if (o.getDeptId() == null) {
|
||||
continue;
|
||||
}
|
||||
o.setDeptName(nameById.getOrDefault(o.getDeptId(), String.valueOf(o.getDeptId())));
|
||||
}
|
||||
}
|
||||
|
||||
private static void indexOpenAppNames(Map<String, String> partnerNameMap,
|
||||
@@ -522,10 +560,31 @@ public class SmHotelOrderServiceImpl extends ServiceImpl<SmHotelOrderMapper, SmH
|
||||
hb.setLongitude(hotel.getLongitude());
|
||||
hb.setLatitude(hotel.getLatitude());
|
||||
hb.setDeptId(hotel.getDeptId());
|
||||
if (hotel.getDeptId() != null) {
|
||||
try {
|
||||
R<Map<Long, SysDept>> r = remoteDeptService.getDeptByDeptIds(List.of(hotel.getDeptId()));
|
||||
if (r != null && r.isOk() && r.getData() != null) {
|
||||
SysDept d = r.getData().get(hotel.getDeptId());
|
||||
if (d != null && StrUtil.isNotBlank(d.getName())) {
|
||||
hb.setDeptName(d.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.warn("|酒店订单详情|补全部门名失败|deptId={}|{}", hotel.getDeptId(), e.getMessage());
|
||||
}
|
||||
if (StrUtil.isBlank(hb.getDeptName())) {
|
||||
hb.setDeptName(String.valueOf(hotel.getDeptId()));
|
||||
}
|
||||
}
|
||||
vo.setHotel(hb);
|
||||
if (StrUtil.isBlank(order.getHotelName())) {
|
||||
order.setHotelName(hotel.getHotelName());
|
||||
}
|
||||
if (order.getDeptId() == null) {
|
||||
order.setDeptId(hotel.getDeptId());
|
||||
order.setDeptName(hb.getDeptName());
|
||||
}
|
||||
}
|
||||
|
||||
SmHotelProductEntity product = null;
|
||||
|
||||
Reference in new issue
Block a user