|
|
@@ -53,6 +53,12 @@ public class LifeUserStoreService {
|
|
|
|
|
|
private final StoreBusinessInfoMapper storeBusinessInfoMapper;
|
|
|
|
|
|
+ private static final List<String> BUSINESS_SECTION_STORE_TYPE_NEW_1 =
|
|
|
+ Arrays.asList("酒吧", "KTV", "洗浴汗蒸", "按摩足疗");
|
|
|
+ private static final List<String> BUSINESS_SECTION_STORE_TYPE_NEW_2 =
|
|
|
+ Arrays.asList("丽人美发", "运动健身");
|
|
|
+ private static final String BUSINESS_SECTION_STORE_TYPE_NEW_3 = "特色美食";
|
|
|
+
|
|
|
/**
|
|
|
* 门店列表
|
|
|
*
|
|
|
@@ -67,8 +73,6 @@ public class LifeUserStoreService {
|
|
|
*/
|
|
|
public List<Map<String, Object>> getStoreList(String storeType, String storeName, Integer distance, String jingdu, String weidu, int page, int size) {
|
|
|
try {
|
|
|
- // 初始化返回结果列表
|
|
|
- List<Map<String, Object>> returnMaps = new ArrayList<>();
|
|
|
// 构建查询条件对象
|
|
|
QueryWrapper<StoreInfoVo> wrapper = new QueryWrapper<>();
|
|
|
// 添加门店类型筛选条件()
|
|
|
@@ -90,7 +94,7 @@ public class LifeUserStoreService {
|
|
|
List<StoreInfoVo> storeInfoVoList = storeInfoMapper.getStoreInfoVoListNew(wrapper,jingdu + "," + weidu);
|
|
|
// 如果查询结果为空,则直接返回空列表
|
|
|
if (storeInfoVoList.isEmpty()) {
|
|
|
- return returnMaps;
|
|
|
+ return new ArrayList<>();
|
|
|
}
|
|
|
// 提取所有符合条件的门店ID
|
|
|
List<Integer> storeIds = storeInfoVoList.stream().map(StoreInfoVo::getId).collect(Collectors.toList());
|
|
|
@@ -112,133 +116,104 @@ public class LifeUserStoreService {
|
|
|
List<Map<Integer, Integer>> storeClockInCountList = storeClockInService.getStoreClockInCount();
|
|
|
// 获取所有店铺打卡次数(有图片并且设置为可见的)
|
|
|
List<Map<Integer, Integer>> storeClockInCountMapList = storeClockInService.getStoreClockInWithCanLookCount();
|
|
|
- // 遍历所有门店信息,构造返回结果
|
|
|
- for (StoreInfoVo store : storeInfoVoList) {
|
|
|
-
|
|
|
- //门店营业时间信息 Fixme 流式处理,或用 <assiociate>一次性查询
|
|
|
- StoreBusinessInfo storeBusinessInfo = storeBusinessInfoMapper.selectOne(new QueryWrapper<StoreBusinessInfo>().eq("store_id", store.getId()).eq("delete_flag", 0).last("limit 1"));
|
|
|
-
|
|
|
- Map<String, Object> storeMap = new HashMap<>();
|
|
|
- // 如果用户提供了经纬度信息,则计算并添加门店到用户的距离
|
|
|
- /*if ((jingdu != null && !jingdu.isEmpty()) && (weidu != null && !weidu.isEmpty())) {
|
|
|
- double storeJing = Double.parseDouble(store.getStorePosition().split(",")[0]);
|
|
|
- double storeWei = Double.parseDouble(store.getStorePosition().split(",")[1]);
|
|
|
- double storeDistance = DistanceUtil.haversineCalculateDistance(Double.parseDouble(jingdu), Double.parseDouble(weidu), storeJing, storeWei);
|
|
|
- storeMap.put("distance", storeDistance);
|
|
|
- } else {
|
|
|
- // 否则,返回提示信息
|
|
|
- storeMap.put("distance", "没有位置信息");
|
|
|
- }*/
|
|
|
- storeMap.put("distance", store.getDist());
|
|
|
- // 添加门店的业务状态及其描述
|
|
|
- storeMap.put("businessStatus", store.getBusinessStatus());
|
|
|
- storeMap.put("businessStatusStr", store.getBusinessStatusStr());
|
|
|
-
|
|
|
- String businessSectionName = store.getBusinessSectionName();
|
|
|
- if(Arrays.asList("酒吧", "KTV", "洗浴汗蒸", "按摩足疗").contains(businessSectionName)){
|
|
|
- storeMap.put("storeTypeNew", 1);
|
|
|
- } else if (Arrays.asList("丽人美发", "运动健身").contains(businessSectionName)){
|
|
|
- storeMap.put("storeTypeNew", 2);
|
|
|
- } else if (Arrays.asList("特色美食").contains(businessSectionName)){
|
|
|
- storeMap.put("storeTypeNew", 3);
|
|
|
- }
|
|
|
- //474bug
|
|
|
- storeMap.put("businessSection", store.getBusinessSection());
|
|
|
- storeMap.put("businessSectionName", store.getBusinessSectionName());
|
|
|
- storeMap.put("businessTypes", store.getBusinessTypes());
|
|
|
- storeMap.put("businessTypesName", store.getBusinessTypesName());
|
|
|
- storeMap.put("businessClassify", store.getBusinessClassify());
|
|
|
- storeMap.put("businessClassifyName", store.getBusinessClassifyName());
|
|
|
- storeMap.put("storeBusinessInfo", storeBusinessInfo);
|
|
|
- // 添加门店ID
|
|
|
- storeMap.put("storeId", store.getId());
|
|
|
- // 如果存在评分数据,则添加评分、平均消费及评价总数
|
|
|
- if (avgScoreMap.containsKey(String.valueOf(store.getId()))) {
|
|
|
- storeMap.put("avgScore", avgScoreMap.get(String.valueOf(store.getId())).get(0).get("avg_score"));
|
|
|
- //storeMap.put("avgPrice", avgPriceMap.get(String.valueOf(store.getId())).get(0).get("avg_price"));
|
|
|
- // 条数
|
|
|
- storeMap.put("totalNum", avgScoreMap.get(String.valueOf(store.getId())).get(0).get("total_num"));
|
|
|
- } else {
|
|
|
- // 否则,设置默认值
|
|
|
- storeMap.put("avgScore", 0);
|
|
|
- //storeMap.put("avgPrice", 0);
|
|
|
- storeMap.put("totalNum", 0);
|
|
|
- }
|
|
|
- if (avgPriceMap.containsKey(String.valueOf(store.getId()))) {
|
|
|
- //storeMap.put("avgScore", avgScoreMap.get(String.valueOf(store.getId())).get(0).get("avg_score"));
|
|
|
- storeMap.put("avgPrice", avgPriceMap.get(String.valueOf(store.getId())).get(0).get("avg_price"));
|
|
|
- // 条数
|
|
|
- //storeMap.put("totalNum", avgScoreMap.get(String.valueOf(store.getId())).get(0).get("total_num"));
|
|
|
- } else {
|
|
|
- // 否则,设置默认值
|
|
|
- //storeMap.put("avgScore", 0);
|
|
|
- storeMap.put("avgPrice", 0);
|
|
|
- //storeMap.put("totalNum", 0);
|
|
|
- }
|
|
|
- // 添加门店名称
|
|
|
- storeMap.put("storeName", store.getStoreName());
|
|
|
- // 添加门店地址
|
|
|
- storeMap.put("storeAddress", store.getStoreAddress());
|
|
|
- // 添加门店类型
|
|
|
- storeMap.put("storeType", store.getStoreType());
|
|
|
- // 添加门店入口图片
|
|
|
- storeMap.put("entranceImage", store.getEntranceImage());
|
|
|
- // 添加门店图片
|
|
|
- storeMap.put("storeImage", store.getImgUrl());
|
|
|
- // 添加门店简介
|
|
|
- storeMap.put("storeBlurb",store.getStoreBlurb());
|
|
|
- // 解析并添加门店经纬度
|
|
|
- String[] position = store.getStorePosition().split(",");
|
|
|
- storeMap.put("longitude", position.length == 2 ? position[0] : "");
|
|
|
- storeMap.put("latitude", position.length == 2 ? position[1] : "");
|
|
|
- // 构造该门店的优惠券列表
|
|
|
- List<Map<String, Object>> quanMapList = new ArrayList<>();
|
|
|
- if (!quanList.isEmpty()) {
|
|
|
- for (LifeCoupon quan : quanList) {
|
|
|
- // 如果优惠券的门店ID与当前门店ID匹配,则添加到优惠券列表中
|
|
|
- if (store.getId().toString().equals(quan.getStoreId())) {
|
|
|
- Map<String, Object> quanMap = new HashMap<>();
|
|
|
- quanMap.put("quanType", quan.getType());
|
|
|
- quanMap.put("name", quan.getName());
|
|
|
- quanMap.put("price", quan.getPrice());
|
|
|
- // 折扣价
|
|
|
- quanMap.put("offprice", quan.getOffprice());
|
|
|
- quanMap.put("quanId", quan.getId());
|
|
|
- quanMapList.add(quanMap);
|
|
|
- }
|
|
|
- }
|
|
|
- // 将优惠券列表添加到门店信息中
|
|
|
- storeMap.put("quanList", quanMapList);
|
|
|
- }
|
|
|
- // 添加门店类型的描述
|
|
|
- storeMap.put("storeTypeStr", store.getStoreTypeStr());
|
|
|
- // 遍历打卡次数列表,找到当前门店的打卡次数并添加到门店信息中
|
|
|
- storeClockInCountList.forEach(b -> {
|
|
|
- Integer storeId = b.get("storeId");
|
|
|
- if (Objects.equals(storeId, store.getId())) {
|
|
|
- storeMap.put("storeClockInCount", b.get("count"));
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- storeClockInCountMapList.forEach(b -> {
|
|
|
- Integer storeId = b.get("storeId");
|
|
|
- if (Objects.equals(storeId, store.getId())) {
|
|
|
- storeMap.put("storeClockInCountWithCanLook", b.get("count"));
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 如果未找到打卡次数,则设置为0
|
|
|
- if (null == storeMap.get("storeClockInCount")) {
|
|
|
- storeMap.put("storeClockInCount", 0);
|
|
|
- }
|
|
|
- // 如果未找到打卡次数(有图片并且设置为可见的),则设置为0
|
|
|
- if (null == storeMap.get("storeClockInCountWithCanLook")) {
|
|
|
- storeMap.put("storeClockInCountWithCanLook", 0);
|
|
|
- continue;
|
|
|
- }
|
|
|
- // 将当前门店的信息添加到返回结果列表中
|
|
|
- returnMaps.add(storeMap);
|
|
|
+
|
|
|
+ // 一次查出本页门店的营业时间,避免循环内 N+1 查询
|
|
|
+ List<StoreBusinessInfo> businessInfoRows = storeBusinessInfoMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<StoreBusinessInfo>()
|
|
|
+ .in(StoreBusinessInfo::getStoreId, storeIds)
|
|
|
+ .eq(StoreBusinessInfo::getDeleteFlag, 0));
|
|
|
+ Map<Integer, StoreBusinessInfo> businessInfoByStoreId = new HashMap<>();
|
|
|
+ for (StoreBusinessInfo row : businessInfoRows) {
|
|
|
+ businessInfoByStoreId.putIfAbsent(row.getStoreId(), row);
|
|
|
}
|
|
|
+
|
|
|
+ Map<String, List<LifeCoupon>> quanByStoreId = quanList.stream()
|
|
|
+ .filter(q -> q.getStoreId() != null)
|
|
|
+ .collect(Collectors.groupingBy(LifeCoupon::getStoreId));
|
|
|
+
|
|
|
+ Map<Integer, Integer> clockInCountByStoreId = buildStoreIdToCountMap(storeClockInCountList);
|
|
|
+ Map<Integer, Integer> clockInCanLookCountByStoreId = buildStoreIdToCountMap(storeClockInCountMapList);
|
|
|
+
|
|
|
+ final boolean hasGlobalQuan = !quanList.isEmpty();
|
|
|
+ // 流式组装配,避免对优惠券表、打卡汇总表在每家店上重复全表扫描
|
|
|
+ List<Map<String, Object>> returnMaps = storeInfoVoList.stream()
|
|
|
+ .map(store -> {
|
|
|
+ StoreBusinessInfo storeBusinessInfo = businessInfoByStoreId.get(store.getId());
|
|
|
+
|
|
|
+ Map<String, Object> storeMap = new HashMap<>();
|
|
|
+ // 如果用户提供了经纬度信息,则计算并添加门店到用户的距离
|
|
|
+ /*if ((jingdu != null && !jingdu.isEmpty()) && (weidu != null && !weidu.isEmpty())) {
|
|
|
+ double storeJing = Double.parseDouble(store.getStorePosition().split(",")[0]);
|
|
|
+ double storeWei = Double.parseDouble(store.getStorePosition().split(",")[1]);
|
|
|
+ double storeDistance = DistanceUtil.haversineCalculateDistance(Double.parseDouble(jingdu), Double.parseDouble(weidu), storeJing, storeWei);
|
|
|
+ storeMap.put("distance", storeDistance);
|
|
|
+ } else {
|
|
|
+ // 否则,返回提示信息
|
|
|
+ storeMap.put("distance", "没有位置信息");
|
|
|
+ }*/
|
|
|
+ storeMap.put("distance", store.getDist());
|
|
|
+ storeMap.put("businessStatus", store.getBusinessStatus());
|
|
|
+ storeMap.put("businessStatusStr", store.getBusinessStatusStr());
|
|
|
+
|
|
|
+ String businessSectionName = store.getBusinessSectionName();
|
|
|
+ if (BUSINESS_SECTION_STORE_TYPE_NEW_1.contains(businessSectionName)) {
|
|
|
+ storeMap.put("storeTypeNew", 1);
|
|
|
+ } else if (BUSINESS_SECTION_STORE_TYPE_NEW_2.contains(businessSectionName)) {
|
|
|
+ storeMap.put("storeTypeNew", 2);
|
|
|
+ } else if (BUSINESS_SECTION_STORE_TYPE_NEW_3.equals(businessSectionName)) {
|
|
|
+ storeMap.put("storeTypeNew", 3);
|
|
|
+ }
|
|
|
+ storeMap.put("businessSection", store.getBusinessSection());
|
|
|
+ storeMap.put("businessSectionName", store.getBusinessSectionName());
|
|
|
+ storeMap.put("businessTypes", store.getBusinessTypes());
|
|
|
+ storeMap.put("businessTypesName", store.getBusinessTypesName());
|
|
|
+ storeMap.put("businessClassify", store.getBusinessClassify());
|
|
|
+ storeMap.put("businessClassifyName", store.getBusinessClassifyName());
|
|
|
+ storeMap.put("storeBusinessInfo", storeBusinessInfo);
|
|
|
+ storeMap.put("storeId", store.getId());
|
|
|
+ if (avgScoreMap.containsKey(String.valueOf(store.getId()))) {
|
|
|
+ storeMap.put("avgScore", avgScoreMap.get(String.valueOf(store.getId())).get(0).get("avg_score"));
|
|
|
+ storeMap.put("totalNum", avgScoreMap.get(String.valueOf(store.getId())).get(0).get("total_num"));
|
|
|
+ } else {
|
|
|
+ storeMap.put("avgScore", 0);
|
|
|
+ storeMap.put("totalNum", 0);
|
|
|
+ }
|
|
|
+ if (avgPriceMap.containsKey(String.valueOf(store.getId()))) {
|
|
|
+ storeMap.put("avgPrice", avgPriceMap.get(String.valueOf(store.getId())).get(0).get("avg_price"));
|
|
|
+ } else {
|
|
|
+ storeMap.put("avgPrice", 0);
|
|
|
+ }
|
|
|
+ storeMap.put("storeName", store.getStoreName());
|
|
|
+ storeMap.put("storeAddress", store.getStoreAddress());
|
|
|
+ storeMap.put("storeType", store.getStoreType());
|
|
|
+ storeMap.put("entranceImage", store.getEntranceImage());
|
|
|
+ storeMap.put("storeImage", store.getImgUrl());
|
|
|
+ storeMap.put("storeBlurb", store.getStoreBlurb());
|
|
|
+ String[] position = store.getStorePosition().split(",");
|
|
|
+ storeMap.put("longitude", position.length == 2 ? position[0] : "");
|
|
|
+ storeMap.put("latitude", position.length == 2 ? position[1] : "");
|
|
|
+
|
|
|
+ if (hasGlobalQuan) {
|
|
|
+ String sid = String.valueOf(store.getId());
|
|
|
+ List<LifeCoupon> forStore = quanByStoreId.getOrDefault(sid, Collections.emptyList());
|
|
|
+ List<Map<String, Object>> quanMapList = forStore.stream().map(quan -> {
|
|
|
+ Map<String, Object> quanMap = new HashMap<>();
|
|
|
+ quanMap.put("quanType", quan.getType());
|
|
|
+ quanMap.put("name", quan.getName());
|
|
|
+ quanMap.put("price", quan.getPrice());
|
|
|
+ quanMap.put("offprice", quan.getOffprice());
|
|
|
+ quanMap.put("quanId", quan.getId());
|
|
|
+ return quanMap;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+ storeMap.put("quanList", quanMapList);
|
|
|
+ }
|
|
|
+
|
|
|
+ storeMap.put("storeTypeStr", store.getStoreTypeStr());
|
|
|
+ storeMap.put("storeClockInCount", clockInCountByStoreId.getOrDefault(store.getId(), 0));
|
|
|
+ storeMap.put("storeClockInCountWithCanLook", clockInCanLookCountByStoreId.getOrDefault(store.getId(), 0));
|
|
|
+ return storeMap;
|
|
|
+ })
|
|
|
+ .collect(Collectors.toList());
|
|
|
// 如果用户提供了距离阈值,则根据距离过滤结果,并按距离升序排序
|
|
|
if (distance != null) {
|
|
|
double maxDistance = distance;
|
|
|
@@ -286,6 +261,29 @@ public class LifeUserStoreService {
|
|
|
throw new RuntimeException(e);
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * MyBatis 将 {@code store_id as storeId} 等别名列进 Map,按行整理为 门店id 到 次数 的映射,供列表 O(1) 查询。
|
|
|
+ */
|
|
|
+ private static Map<Integer, Integer> buildStoreIdToCountMap(List<Map<Integer, Integer>> rows) {
|
|
|
+ Map<Integer, Integer> out = new HashMap<>();
|
|
|
+ if (rows == null) {
|
|
|
+ return out;
|
|
|
+ }
|
|
|
+ for (Object o : rows) {
|
|
|
+ if (!(o instanceof Map)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ Map<?, ?> row = (Map<?, ?>) o;
|
|
|
+ Object sid = row.get("storeId");
|
|
|
+ Object c = row.get("count");
|
|
|
+ if (sid != null && c != null) {
|
|
|
+ out.put(((Number) sid).intValue(), ((Number) c).intValue());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return out;
|
|
|
+ }
|
|
|
+
|
|
|
// 提取一个工具方法,统一处理距离值的转换
|
|
|
private double getDistanceValue(Object distanceObj) {
|
|
|
if (distanceObj instanceof Number) {
|